Bug 1525175 - Part 1: Convert about:addons header to HTML r=rpl,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D43610

--HG--
rename : toolkit/mozapps/extensions/test/browser/browser_bug567127.js => toolkit/mozapps/extensions/test/browser/browser_page_options_install_addon.js
rename : toolkit/mozapps/extensions/test/browser/browser_bug570760.js => toolkit/mozapps/extensions/test/browser/browser_search_bar_focus.js
extra : moz-landing-system : lando
This commit is contained in:
Mark Striemer 2019-11-01 16:26:32 +00:00
Родитель 6ffc0356ab
Коммит d9f5739109
31 изменённых файлов: 1356 добавлений и 1231 удалений

Просмотреть файл

@ -14,11 +14,14 @@ async function installFile(filename) {
MockFilePicker.setFiles([file]);
MockFilePicker.afterOpenCallback = MockFilePicker.cleanup;
await BrowserOpenAddonsMgr("addons://list/extension");
let contentWin = gBrowser.selectedTab.linkedBrowser.contentWindow;
let managerWin = await BrowserOpenAddonsMgr("addons://list/extension");
// Do the install...
contentWin.gViewController.doCommand("cmd_installFromFile");
await BrowserTestUtils.waitForEvent(managerWin.document, "ViewChanged");
let installButton = managerWin
.getHtmlBrowser()
.contentDocument.querySelector('[action="install-from-file"]');
installButton.click();
}
add_task(async function test_install_extension_from_local_file() {

Просмотреть файл

@ -1,6 +1,6 @@
// Invoke the "Check for Updates" menu item
function checkAll(win) {
win.gViewController.doCommand("cmd_findAllUpdates");
triggerPageOptionsAction(win, "check-for-updates");
return new Promise(resolve => {
let observer = {
observe(subject, topic, data) {

Просмотреть файл

@ -35,6 +35,8 @@ async function testUpdateNoPrompt(
// Go to Extensions in about:addons
let win = await BrowserOpenAddonsMgr("addons://list/extension");
await BrowserTestUtils.waitForEvent(win.document, "ViewChanged");
let sawPopup = false;
function popupListener() {
sawPopup = true;
@ -43,7 +45,7 @@ async function testUpdateNoPrompt(
// Trigger an update check, we should see the update get applied
let updatePromise = waitForUpdate(addon);
win.gViewController.doCommand("cmd_findAllUpdates");
triggerPageOptionsAction(win, "check-for-updates");
await updatePromise;
addon = await AddonManager.getAddonByID(id);

Просмотреть файл

@ -175,6 +175,16 @@ async function waitForUpdate(addon) {
return newAddon;
}
/**
* Trigger an action from the page options menu.
*/
function triggerPageOptionsAction(win, action) {
win
.getHtmlBrowser()
.contentDocument.querySelector(`#page-options [action="${action}"]`)
.click();
}
function isDefaultIcon(icon) {
// These are basically the same icon, but code within webextensions
// generates references to the former and generic add-ons manager code
@ -474,12 +484,20 @@ async function interactiveUpdateTest(autoUpdate, checkFn) {
if (manualUpdatePromise) {
await manualUpdatePromise;
const availableUpdates = win.document.getElementById(
"updates-manualUpdatesFound-btn"
);
availableUpdates.click();
let doc = win.getHtmlBrowser().contentDocument;
let card = await BrowserTestUtils.waitForCondition(() => {
if (win.gViewController.currentViewId !== "addons://updates/available") {
let showUpdatesBtn = doc.querySelector("addon-updates-message").button;
await TestUtils.waitForCondition(() => {
return !showUpdatesBtn.hidden;
}, "Wait for show updates button");
let viewChanged = BrowserTestUtils.waitForEvent(
win.document,
"ViewChanged"
);
showUpdatesBtn.click();
await viewChanged;
}
let card = await TestUtils.waitForCondition(() => {
return doc.querySelector(`addon-card[addon-id="${ID}"]`);
}, `Wait addon card for "${ID}"`);
let updateBtn = card.querySelector('panel-item[action="install-update"]');
@ -504,6 +522,8 @@ async function interactiveUpdateTest(autoUpdate, checkFn) {
let win = await BrowserOpenAddonsMgr("addons://list/extension");
await BrowserTestUtils.waitForEvent(win.document, "ViewChanged");
// Trigger an update check
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
let { promise: checkPromise } = await triggerUpdate(win, addon);

Просмотреть файл

@ -0,0 +1,81 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
from __future__ import absolute_import
import fluent.syntax.ast as FTL
from fluent.migrate.helpers import transforms_from
from fluent.migrate import COPY_PATTERN, COPY, REPLACE
from fluent.migrate.helpers import TERM_REFERENCE
def migrate(ctx):
"""Bug 1525175 - Convert about:addons header to HTML, part {index}"""
ctx.add_transforms(
'toolkit/toolkit/about/aboutAddons.ftl',
'toolkit/toolkit/about/aboutAddons.ftl',
transforms_from(
"""
addon-updates-check-for-updates = { COPY_PATTERN(from_path, "extensions-updates-check-for-updates.label") }
.accesskey = { COPY_PATTERN(from_path, "extensions-updates-check-for-updates.accesskey") }
addon-updates-view-updates = { COPY_PATTERN(from_path, "extensions-updates-view-updates.label") }
.accesskey = { COPY_PATTERN(from_path, "extensions-updates-view-updates.accesskey") }
addon-updates-update-addons-automatically = { COPY_PATTERN(from_path, "extensions-updates-update-addons-automatically.label") }
.accesskey = { COPY_PATTERN(from_path, "extensions-updates-update-addons-automatically.accesskey") }
addon-updates-reset-updates-to-automatic = { COPY_PATTERN(from_path, "extensions-updates-reset-updates-to-automatic.label") }
.accesskey = { COPY_PATTERN(from_path, "extensions-updates-reset-updates-to-automatic.accesskey") }
addon-updates-reset-updates-to-manual = { COPY_PATTERN(from_path, "extensions-updates-reset-updates-to-manual.label") }
.accesskey = { COPY_PATTERN(from_path, "extensions-updates-reset-updates-to-manual.accesskey") }
addon-updates-updating = { COPY_PATTERN(from_path, "extensions-updates-updating.value") }
addon-updates-installed = { COPY_PATTERN(from_path, "extensions-updates-installed.value") }
addon-updates-none-found = { COPY_PATTERN(from_path, "extensions-updates-none-found.value") }
addon-updates-manual-updates-found = { COPY_PATTERN(from_path, "extensions-updates-manual-updates-found.label") }
addon-install-from-file = { COPY_PATTERN(from_path, "install-addon-from-file.label") }
.accesskey = { COPY_PATTERN(from_path, "install-addon-from-file.accesskey") }
addon-open-about-debugging = { COPY_PATTERN(from_path, "debug-addons.label") }
.accesskey = { COPY_PATTERN(from_path, "debug-addons.accesskey") }
addon-manage-extensions-shortcuts = { COPY_PATTERN(from_path, "manage-extensions-shortcuts.label") }
.accesskey = { COPY_PATTERN(from_path, "manage-extensions-shortcuts.accesskey") }
addons-heading-search-input =
.placeholder = { COPY_PATTERN(from_path, "search-header.placeholder") }
"""
, from_path='toolkit/toolkit/about/aboutAddons.ftl')
)
ctx.add_transforms(
'toolkit/toolkit/about/aboutAddons.ftl',
'toolkit/toolkit/about/aboutAddons.ftl',
transforms_from(
"""
addon-install-from-file-dialog-title = { COPY(from_path, "installFromFile.dialogTitle") }
addon-install-from-file-filter-name = { COPY(from_path, "installFromFile.filterName") }
extension-heading = { COPY(from_path, "listHeading.extension") }
theme-heading = { COPY(from_path, "listHeading.theme") }
plugin-heading = { COPY(from_path, "listHeading.plugin") }
dictionary-heading = { COPY(from_path, "listHeading.dictionary") }
locale-heading = { COPY(from_path, "listHeading.locale") }
shortcuts-heading = { COPY(from_path, "listHeading.shortcuts") }
theme-heading-search-label = { COPY(from_path, "searchLabel.theme") }
extension-heading-search-label = { COPY(from_path, "searchLabel.extension") }
"""
, from_path='toolkit/chrome/mozapps/extensions/extensions.properties')
)
ctx.add_transforms(
'toolkit/toolkit/about/aboutAddons.ftl',
'toolkit/toolkit/about/aboutAddons.ftl',
[
FTL.Message(
id=FTL.Identifier("discover-heading"),
value=REPLACE(
"toolkit/chrome/mozapps/extensions/extensions.properties",
"listHeading.discover",
{
"%1$S": TERM_REFERENCE("brand-short-name")
},
normalize_printf=True
)
),
]
)

Просмотреть файл

@ -62,9 +62,6 @@ details.notification.restartless-uninstall=%1$S will be uninstalled after you cl
#LOCALIZATION NOTE (details.notification.gmpPending) %1$S is the add-on name
details.notification.gmpPending=%1$S will be installed shortly.
installFromFile.dialogTitle=Select add-on to install
installFromFile.filterName=Add-ons
type.extension.name=Extensions
type.themes.name=Themes
type.locale.name=Languages

Просмотреть файл

@ -30,10 +30,6 @@ list-empty-find-updates =
list-empty-button =
.label = Learn more about add-ons
install-addon-from-file =
.label = Install Add-on From File…
.accesskey = I
help-button = Add-ons Support
preferences =
@ -51,10 +47,6 @@ show-unsigned-extensions-button =
show-all-extensions-button =
.label = Show all extensions
debug-addons =
.label = Debug Add-ons
.accesskey = b
cmd-show-details =
.label = Show More Information
.accesskey = S
@ -260,19 +252,15 @@ extensions-warning-update-security-enable =
## Strings connected to add-on updates
extensions-updates-check-for-updates =
.label = Check for Updates
addon-updates-check-for-updates = Check for Updates
.accesskey = C
extensions-updates-view-updates =
.label = View Recent Updates
addon-updates-view-updates = View Recent Updates
.accesskey = V
# This menu item is a checkbox that toggles the default global behavior for
# add-on update checking.
extensions-updates-update-addons-automatically =
.label = Update Add-ons Automatically
addon-updates-update-addons-automatically = Update Add-ons Automatically
.accesskey = A
## Specific add-ons can have custom update checking behaviors ("Manually",
@ -281,37 +269,33 @@ extensions-updates-update-addons-automatically =
## (which itself is either "Automatically" or "Manually", controlled by the
## extensions-updates-update-addons-automatically.label menu item).
extensions-updates-reset-updates-to-automatic =
.label = Reset All Add-ons to Update Automatically
addon-updates-reset-updates-to-automatic = Reset All Add-ons to Update Automatically
.accesskey = R
extensions-updates-reset-updates-to-manual =
.label = Reset All Add-ons to Update Manually
addon-updates-reset-updates-to-manual = Reset All Add-ons to Update Manually
.accesskey = R
## Status messages displayed when updating add-ons
extensions-updates-updating =
.value = Updating add-ons
extensions-updates-installed =
.value = Your add-ons have been updated.
extensions-updates-downloaded =
.value = Your add-on updates have been downloaded.
extensions-updates-restart =
.label = Restart now to complete installation
extensions-updates-none-found =
.value = No updates found
extensions-updates-manual-updates-found =
.label = View Available Updates
extensions-updates-update-selected =
.label = Install Updates
.tooltiptext = Install available updates in this list
addon-updates-updating = Updating add-ons
addon-updates-installed = Your add-ons have been updated.
addon-updates-none-found = No updates found
addon-updates-manual-updates-found = View Available Updates
## Add-on install/debug strings for page options menu
addon-install-from-file = Install Add-on From File…
.accesskey = I
addon-install-from-file-dialog-title = Select add-on to install
addon-install-from-file-filter-name = Add-ons
addon-open-about-debugging = Debug Add-ons
.accesskey = b
## Extension shortcut management
manage-extensions-shortcuts =
.label = Manage Extension Shortcuts
# This is displayed in the page options menu
addon-manage-extensions-shortcuts = Manage Extension Shortcuts
.accesskey = S
shortcuts-no-addons = You dont have any extensions enabled.
shortcuts-no-commands = The following extensions do not have shortcuts:
shortcuts-input =
@ -494,3 +478,19 @@ recommended-themes-heading = Recommended Themes
# A recommendation for the Firefox Color theme shown at the bottom of the theme
# list view. The "Firefox Color" name itself should not be translated.
recommended-theme-1 = Feeling creative? <a data-l10n-name="link">Build your own theme with Firefox Color.</a>
## Page headings
extension-heading = Manage Your Extensions
theme-heading = Manage Your Themes
plugin-heading = Manage Your Plugins
dictionary-heading = Manage Your Dictionaries
locale-heading = Manage Your Languages
updates-heading = Manage Your Updates
discover-heading = Personalize Your { -brand-short-name }
shortcuts-heading = Manage Extension Shortcuts
theme-heading-search-label = Find more themes
extension-heading-search-label = Find more extensions
addons-heading-search-input =
.placeholder = Search addons.mozilla.org

Просмотреть файл

@ -345,6 +345,9 @@ var BrowserUtils = {
canFindInPage(location) {
return (
!location.startsWith("about:addons") &&
!location.startsWith(
"chrome://mozapps/content/extensions/aboutaddons.html"
) &&
!location.startsWith("about:preferences")
);
},

Просмотреть файл

@ -1,6 +1,7 @@
:root {
--section-width: 664px;
--addon-icon-size: 32px;
--z-index-sticky-container: 1;
}
*|*[hidden] {
@ -15,6 +16,67 @@ body {
-moz-user-select: initial;
}
.sticky-container {
width: 100%;
position: sticky;
top: 0;
z-index: var(--z-index-sticky-container);
background: var(--in-content-page-background);
}
.main-search {
display: flex;
justify-content: flex-end;
align-items: center;
padding-inline-start: 28px;
padding-top: 20px;
padding-bottom: 30px;
width: var(--section-width);
}
search-addons > search-textbox {
margin-inline-end: 0;
width: 20em;
}
.search-label {
margin-inline-end: 8px;
}
.main-heading {
display: flex;
margin-inline-start: 28px;
padding-bottom: 16px;
width: var(--section-width);
}
.spacer {
flex-grow: 1;
}
#updates-message {
display: flex;
align-items: center;
margin-inline-end: 8px;
}
.back-button {
-moz-context-properties: fill;
fill: currentColor;
background-image: url("chrome://global/skin/icons/arrow-left.svg");
background-repeat: no-repeat;
background-position: center;
min-width: auto;
width: 32px;
margin-block: 0;
margin-inline-start: 0;
margin-inline-end: 16px;
}
.back-button:dir(rtl) {
transform: scaleX(-1);
}
#main {
margin-inline-start: 28px;
margin-bottom: 28px;
@ -190,6 +252,17 @@ addon-card:not([expanded]) .addon-description {
overflow-x: hidden;
}
.page-options-menu {
position: relative;
align-self: center;
}
.page-options-menu > .more-options-button {
background-image: url("chrome://mozapps/skin/extensions/utilities.svg");
width: 32px;
height: 32px;
}
.more-options-menu {
position: relative;
/* Add some negative margin to account for the button's padding */
@ -355,7 +428,9 @@ addon-details {
margin: 0;
-moz-context-properties: fill;
fill: currentColor;
background: url("chrome://global/skin/icons/more.svg") no-repeat center center;
background-image: url("chrome://global/skin/icons/more.svg");
background-repeat: no-repeat;
background-position: center center;
position: relative;
}
@ -444,3 +519,7 @@ addon-permissions-list > .addon-detail-row:first-of-type {
/* Pull the buttons flush with the side of the card */
margin-inline: calc(var(--card-padding) * -1);
}
panel-list {
font-size: 13px;
}

Просмотреть файл

@ -6,9 +6,11 @@
<html>
<head>
<!-- Bug 1571346 Remove 'unsafe-inline' from style-src within about:addons -->
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; script-src chrome:; style-src chrome: 'unsafe-inline'; img-src chrome: file: jar: https: http:; connect-src chrome: data: https: http:; object-src 'none'">
<!-- @CSP the 'onclick' handler for the searchButtonIcon within the file search-textbox.js, using: sha512-kSDNX67wegjpcf8CSj/L6h46a0QUKm2CyijGxC5PhSWVvPU9gdd28QVBBFq9t8N5UGKUFdDcZsjYbGSlYG0y3g== -->
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; script-src chrome: 'sha512-kSDNX67wegjpcf8CSj/L6h46a0QUKm2CyijGxC5PhSWVvPU9gdd28QVBBFq9t8N5UGKUFdDcZsjYbGSlYG0y3g=='; style-src chrome: 'unsafe-inline'; img-src chrome: file: jar: https: http:; connect-src chrome: data: https: http:; object-src 'none'">
<link rel="stylesheet" href="chrome://global/content/tabprompts.css">
<link rel="stylesheet" href="chrome://global/skin/tabprompts.css">
<link rel="stylesheet" href="chrome://global/skin/textbox.css">
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
<link rel="stylesheet" href="chrome://mozapps/content/extensions/aboutaddons.css">
@ -18,14 +20,17 @@
<link rel="localization" href="toolkit/about/aboutAddons.ftl">
<link rel="localization" href="toolkit/about/abuseReports.ftl">
<script src="chrome://mozapps/content/extensions/named-deck.js"></script>
<script src="chrome://mozapps/content/extensions/aboutaddonsCommon.js"></script>
<script src="chrome://mozapps/content/extensions/message-bar.js"></script>
<script src="chrome://mozapps/content/extensions/abuse-reports.js"></script>
<script src="chrome://mozapps/content/extensions/shortcuts.js"></script>
<script src="chrome://mozapps/content/extensions/aboutaddons.js"></script>
<!-- Defer scripts so all the templates are loaded by the time they run. -->
<script defer src="chrome://mozapps/content/extensions/named-deck.js"></script>
<script defer src="chrome://mozapps/content/extensions/aboutaddonsCommon.js"></script>
<script defer src="chrome://mozapps/content/extensions/message-bar.js"></script>
<script defer src="chrome://mozapps/content/extensions/abuse-reports.js"></script>
<script defer src="chrome://mozapps/content/extensions/shortcuts.js"></script>
<script defer src="chrome://mozapps/content/extensions/aboutaddons.js"></script>
</head>
<body>
<addon-page-header id="page-header"></addon-page-header>
<message-bar-stack id="abuse-reports-messages" reverse max-message-bar-count="3">
</message-bar-stack>
@ -35,6 +40,40 @@
<content-select-dropdown></content-select-dropdown>
<proxy-context-menu id="contentAreaContextMenu"></proxy-context-menu>
<template name="addon-page-header">
<div class="sticky-container">
<div class="main-search">
<label for="search-addons" class="search-label"></label>
<search-addons data-l10n-id="search-header-shortcut" data-l10n-attrs="key"></search-addons>
</div>
<div class="main-heading">
<button class="back-button" action="go-back" data-l10n-id="go-back-button" hidden></button>
<h1 class="header-name"></h1>
<div class="spacer"></div>
<addon-updates-message id="updates-message" hidden></addon-updates-message>
<div class="page-options-menu">
<button class="more-options-button" action="page-options"></button>
<addon-page-options id="page-options"></addon-page-options>
</div>
</div>
</div>
</template>
<template name="addon-page-options">
<panel-list>
<panel-item action="check-for-updates" data-l10n-id="addon-updates-check-for-updates" data-l10n-attrs="accesskey"></panel-item>
<panel-item action="view-recent-updates" data-l10n-id="addon-updates-view-updates" data-l10n-attrs="accesskey"></panel-item>
<panel-item-separator></panel-item-separator>
<panel-item action="install-from-file" data-l10n-id="addon-install-from-file" data-l10n-attrs="accesskey"></panel-item>
<panel-item action="debug-addons" data-l10n-id="addon-open-about-debugging" data-l10n-attrs="accesskey"></panel-item>
<panel-item-separator></panel-item-separator>
<panel-item action="set-update-automatically" data-l10n-id="addon-updates-update-addons-automatically" data-l10n-attrs="accesskey"></panel-item>
<panel-item action="reset-update-states" data-l10n-attrs="accesskey"></panel-item>
<panel-item-separator></panel-item-separator>
<panel-item action="manage-shortcuts" data-l10n-id="addon-manage-extensions-shortcuts" data-l10n-attrs="accesskey"></panel-item>
</panel-list>
</template>
<template name="addon-options">
<panel-list>
<panel-item action="toggle-disabled"></panel-item>
@ -233,11 +272,6 @@
<div class="arrow bottom" role="presentation"></div>
</template>
<template name="panel-item">
<link rel="stylesheet" href="chrome://mozapps/content/extensions/panel-item.css">
<button><slot></slot></button><slot name="support-link"></slot>
</template>
<template name="taar-notice">
<message-bar class="discopane-notice">
<div class="discopane-notice-content">

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint max-len: ["error", 80] */
/* exported initialize, hide, show */
/* exported hide, initialize, show */
/* import-globals-from aboutaddonsCommon.js */
/* import-globals-from abuse-reports.js */
/* global MozXULElement, windowRoot */
@ -59,6 +59,13 @@ XPCOMUtils.defineLazyPreferenceGetter(
val => Services.urlFormatter.formatURL(val)
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"XPINSTALL_ENABLED",
"xpinstall.enabled",
true
);
const UPDATES_RECENT_TIMESPAN = 2 * 24 * 3600000; // 2 days (in milliseconds)
XPCOMUtils.defineLazyPreferenceGetter(
@ -124,19 +131,19 @@ const AddonCardListenerHandler = {
},
delegateEvent(name, addon, args) {
let cards;
let elements;
if (this.MANAGER_EVENTS.has(name)) {
cards = document.querySelectorAll("addon-card");
elements = document.querySelectorAll("addon-card, addon-page-options");
} else {
let cardSelector = `addon-card[addon-id="${addon.id}"]`;
cards = document.querySelectorAll(
elements = document.querySelectorAll(
`${cardSelector}, ${cardSelector} addon-details`
);
}
for (let card of cards) {
for (let el of elements) {
try {
if (name in card) {
card[name](...args);
if (name in el) {
el[name](...args);
}
} catch (e) {
Cu.reportError(e);
@ -275,6 +282,55 @@ async function getAddonMessageInfo(addon) {
return {};
}
function checkForUpdate(addon) {
return new Promise(resolve => {
let listener = {
onUpdateAvailable(addon, install) {
attachUpdateHandler(install);
if (AddonManager.shouldAutoUpdate(addon)) {
let failed = () => {
install.removeListener(updateListener);
resolve({ installed: false, pending: false, found: true });
};
let updateListener = {
onDownloadFailed: failed,
onInstallCancelled: failed,
onInstallFailed: failed,
onInstallEnded: (...args) => {
install.removeListener(updateListener);
resolve({ installed: true, pending: false, found: true });
},
};
install.addListener(updateListener);
install.install();
} else {
resolve({ installed: false, pending: true, found: true });
}
},
onNoUpdateAvailable() {
resolve({ found: false });
},
};
addon.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
});
}
async function checkForUpdates() {
let addons = await AddonManager.getAddonsByTypes(null);
addons = addons.filter(addon => hasPermission(addon, "upgrade"));
let updates = await Promise.all(addons.map(addon => checkForUpdate(addon)));
Services.obs.notifyObservers(null, "EM-update-check-finished");
return updates.reduce(
(counts, update) => ({
installed: counts.installed + (update.installed ? 1 : 0),
pending: counts.pending + (update.pending ? 1 : 0),
found: counts.found + (update.found ? 1 : 0),
}),
{ installed: 0, pending: 0, found: 0 }
);
}
// Don't change how we handle this while the page is open.
const INLINE_OPTIONS_ENABLED = Services.prefs.getBoolPref(
"extensions.htmlaboutaddons.inline-options.enabled"
@ -552,6 +608,9 @@ class PanelList extends HTMLElement {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(importTemplate("panel-list"));
}
connectedCallback() {
this.setAttribute("role", "menu");
}
@ -579,8 +638,19 @@ class PanelList extends HTMLElement {
}
hide(triggeringEvent) {
let openingEvent = this.triggeringEvent;
this.triggeringEvent = triggeringEvent;
this.open = false;
// Since the previously focused element (which is inside the panel) is now
// hidden, move the focus back to the element that opened the panel if it
// was opened with the keyboard.
if (
openingEvent &&
openingEvent.target &&
openingEvent.mozInputSource === MouseEvent.MOZ_SOURCE_KEYBOARD
) {
openingEvent.target.focus();
}
}
toggle(triggeringEvent) {
@ -733,10 +803,15 @@ class PanelList extends HTMLElement {
}
break;
} else if (e.key === "Escape") {
let { triggeringEvent } = this;
this.hide();
if (triggeringEvent && triggeringEvent.target) {
triggeringEvent.target.focus();
} else if (!e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey) {
// Check if any of the children have an accesskey for this letter.
let item = this.querySelector(
`[accesskey="${e.key.toLowerCase()}"],
[accesskey="${e.key.toUpperCase()}"]`
);
if (item) {
item.click();
}
}
break;
@ -807,6 +882,7 @@ class PanelList extends HTMLElement {
async onShow() {
let { triggeringEvent } = this;
this.sendEvent("showing");
this.addHideListeners();
await this.setAlign();
@ -838,12 +914,86 @@ class PanelList extends HTMLElement {
customElements.define("panel-list", PanelList);
class PanelItem extends HTMLElement {
static get observedAttributes() {
return ["accesskey"];
}
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(importTemplate("panel-item"));
this.button = this.shadowRoot.querySelector("button");
let style = document.createElement("link");
style.rel = "stylesheet";
style.href = "chrome://mozapps/content/extensions/panel-item.css";
this.button = document.createElement("button");
this.button.setAttribute("role", "menuitem");
// Use a XUL label element to show the accesskey.
this.label = document.createXULElement("label");
this.button.appendChild(this.label);
let supportLinkSlot = document.createElement("slot");
supportLinkSlot.name = "support-link";
let defaultSlot = document.createElement("slot");
defaultSlot.style.display = "none";
this.shadowRoot.append(style, this.button, supportLinkSlot, defaultSlot);
// When our content changes, move the text into the label. It doesn't work
// with a <slot>, unfortunately.
new MutationObserver(() => {
this.label.textContent = defaultSlot
.assignedNodes()
.map(node => node.textContent)
.join("");
}).observe(this, { characterData: true, childList: true, subtree: true });
}
connectedCallback() {
this.panel = this.closest("panel-list");
if (this.panel) {
this.panel.addEventListener("hidden", this);
this.panel.addEventListener("shown", this);
}
}
disconnectedCallback() {
if (this.panel) {
this.panel.removeEventListener("hidden", this);
this.panel.removeEventListener("shown", this);
this.panel = null;
}
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === "accesskey") {
// Bug 1037709 - Accesskey doesn't work in shadow DOM.
// Ideally we'd have the accesskey set in shadow DOM, and on
// attributeChangedCallback we'd just update the shadow DOM accesskey.
// Skip this change event if we caused it.
if (this._modifyingAccessKey) {
this._modifyingAccessKey = false;
return;
}
this.label.accessKey = newVal || "";
// Bug 1588156 - Accesskey is not ignored for hidden non-input elements.
// Since the accesskey won't be ignored, we need to remove it ourselves
// when the panel is closed, and move it back when it opens.
if (!this.panel || !this.panel.open) {
// When the panel isn't open, just store the key for later.
this._accessKey = newVal || null;
this._modifyingAccessKey = true;
this.accessKey = "";
} else {
this._accessKey = null;
}
}
}
get disabled() {
@ -865,9 +1015,408 @@ class PanelItem extends HTMLElement {
focus() {
this.button.focus();
}
handleEvent(e) {
// Bug 1588156 - Accesskey is not ignored for hidden non-input elements.
// Since the accesskey won't be ignored, we need to remove it ourselves
// when the panel is closed, and move it back when it opens.
switch (e.type) {
case "shown":
if (this._accessKey) {
this.accessKey = this._accessKey;
this._accessKey = null;
}
break;
case "hidden":
if (this.accessKey) {
this._accessKey = this.accessKey;
this._modifyingAccessKey = true;
this.accessKey = "";
}
break;
}
}
}
customElements.define("panel-item", PanelItem);
class SearchAddons extends HTMLElement {
connectedCallback() {
if (this.childElementCount === 0) {
this.input = document.createXULElement("search-textbox");
this.input.setAttribute("searchbutton", true);
this.input.setAttribute("maxlength", 100);
this.input.setAttribute("data-l10n-attrs", "placeholder");
this.input.inputField.id = "search-addons";
document.l10n.setAttributes(this.input, "addons-heading-search-input");
this.append(this.input);
}
this.input.addEventListener("command", this);
document.addEventListener("keypress", this);
}
disconnectedCallback() {
this.input.removeEventListener("command", this);
document.removeEventListener("keypress", this);
}
focus() {
this.input.focus();
}
get focusKey() {
return this.getAttribute("key");
}
handleEvent(e) {
if (e.type === "command") {
this.searchAddons(this.value);
} else if (e.type === "keypress") {
if (e.key === "/" && !e.ctrlKey && !e.metaKey && !e.altKey) {
this.focus();
} else if (e.key == this.focusKey) {
if (e.altKey || e.shiftKey) {
return;
}
if (Services.appinfo.OS === "Darwin") {
if (e.metaKey && !e.ctrlKey) {
this.focus();
}
} else if (e.ctrlKey && !e.metaKey) {
this.focus();
}
}
}
}
get value() {
return this.input.value;
}
searchAddons(query) {
if (query.length === 0) {
return;
}
let url = AddonRepository.getSearchURL(query);
let browser = getBrowserElement();
let chromewin = browser.ownerGlobal;
chromewin.openLinkIn(url, "tab", {
fromChrome: true,
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
});
AMTelemetry.recordLinkEvent({
object: "aboutAddons",
value: "search",
extra: {
type: this.closest("addon-page-header").getAttribute("type"),
view: getTelemetryViewName(this),
},
});
}
}
customElements.define("search-addons", SearchAddons);
class AddonPageHeader extends HTMLElement {
connectedCallback() {
if (this.childElementCount === 0) {
this.appendChild(importTemplate("addon-page-header"));
this.heading = this.querySelector(".header-name");
this.searchLabel = this.querySelector(".search-label");
this.backButton = this.querySelector(".back-button");
this.pageOptionsMenu = this.querySelector("addon-page-options");
}
this.addEventListener("click", this);
}
disconnectedCallback() {
this.removeEventListener("click", this);
}
setViewInfo({ type, param }) {
this.setAttribute("current-view", type);
this.setAttribute("current-param", param);
let viewType = type === "list" ? param : type;
this.setAttribute("type", viewType);
this.heading.hidden = viewType === "detail";
this.backButton.hidden = viewType !== "detail" && viewType !== "shortcuts";
if (viewType !== "detail") {
document.l10n.setAttributes(this.heading, `${viewType}-heading`);
}
if (
viewType === "extension" ||
viewType === "theme" ||
viewType === "shortcuts"
) {
let labelType = viewType === "shortcuts" ? "extension" : viewType;
document.l10n.setAttributes(
this.searchLabel,
`${labelType}-heading-search-label`
);
} else {
this.searchLabel.removeAttribute("data-l10n-id");
this.searchLabel.textContent = "";
}
}
handleEvent(e) {
if (e.type === "click") {
let action = e.target.getAttribute("action");
switch (action) {
case "go-back":
window.history.back();
break;
case "page-options":
this.pageOptionsMenu.toggle(e);
break;
}
}
}
}
customElements.define("addon-page-header", AddonPageHeader);
class AddonUpdatesMessage extends HTMLElement {
static get observedAttributes() {
return ["state"];
}
constructor() {
super();
this.attachShadow({ mode: "open" });
let style = document.createElement("style");
style.textContent = `
@import "chrome://global/skin/in-content/common.css";
button {
margin: 0;
}
`;
this.message = document.createElement("span");
this.message.hidden = true;
this.button = document.createElement("button");
this.button.addEventListener("click", e => {
if (e.button === 0) {
loadViewFn("updates/available", e);
}
});
this.button.hidden = true;
this.shadowRoot.append(style, this.message, this.button);
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === "state" && oldVal !== newVal) {
let l10nId = `addon-updates-${newVal}`;
switch (newVal) {
case "updating":
case "installed":
case "none-found":
this.button.hidden = true;
this.message.hidden = false;
document.l10n.setAttributes(this.message, l10nId);
break;
case "manual-updates-found":
this.message.hidden = true;
this.button.hidden = false;
document.l10n.setAttributes(this.button, l10nId);
break;
}
}
}
set state(val) {
this.setAttribute("state", val);
}
}
customElements.define("addon-updates-message", AddonUpdatesMessage);
class AddonPageOptions extends HTMLElement {
connectedCallback() {
if (this.childElementCount === 0) {
this.render();
}
this.addEventListener("click", this);
this.panel.addEventListener("showing", this);
}
disconnectedCallback() {
this.removeEventListener("click", this);
this.panel.removeEventListener("showing", this);
}
toggle(...args) {
return this.panel.toggle(...args);
}
render() {
this.appendChild(importTemplate("addon-page-options"));
this.panel = this.querySelector("panel-list");
this.installFromFile = this.querySelector('[action="install-from-file"]');
this.toggleUpdatesEl = this.querySelector(
'[action="set-update-automatically"]'
);
this.resetUpdatesEl = this.querySelector('[action="reset-update-states"]');
this.onUpdateModeChanged();
}
async handleEvent(e) {
if (e.type === "click") {
e.target.disabled = true;
try {
await this.onClick(e);
} finally {
e.target.disabled = false;
}
} else if (e.type === "showing") {
this.installFromFile.hidden = !XPINSTALL_ENABLED;
}
}
async onClick(e) {
switch (e.target.getAttribute("action")) {
case "check-for-updates":
await this.checkForUpdates();
break;
case "view-recent-updates":
loadViewFn("updates/recent", e);
break;
case "install-from-file":
if (XPINSTALL_ENABLED) {
installAddonsFromFilePicker().then(installs => {
for (let install of installs) {
this.recordActionEvent({
action: "installFromFile",
value: install.installId,
});
}
});
}
break;
case "debug-addons":
this.openAboutDebugging();
break;
case "set-update-automatically":
await this.toggleAutomaticUpdates();
break;
case "reset-update-states":
await this.resetAutomaticUpdates();
break;
case "manage-shortcuts":
loadViewFn("shortcuts/shortcuts", e);
break;
}
}
onUpdateModeChanged() {
let updatesEnabled = this.automaticUpdatesEnabled();
this.toggleUpdatesEl.checked = updatesEnabled;
let resetType = updatesEnabled ? "automatic" : "manual";
let resetStringId = `addon-updates-reset-updates-to-${resetType}`;
document.l10n.setAttributes(this.resetUpdatesEl, resetStringId);
}
async checkForUpdates(e) {
this.recordActionEvent({ action: "checkForUpdates" });
let message = document.getElementById("updates-message");
message.state = "updating";
message.hidden = false;
let { installed, pending } = await checkForUpdates();
if (pending > 0) {
message.state = "manual-updates-found";
} else if (installed > 0) {
message.state = "installed";
} else {
message.state = "none-found";
}
}
openAboutDebugging() {
let mainWindow = window.windowRoot.ownerGlobal;
this.recordLinkEvent({ value: "about:debugging" });
if ("switchToTabHavingURI" in mainWindow) {
let principal = Services.scriptSecurityManager.getSystemPrincipal();
mainWindow.switchToTabHavingURI(
`about:debugging#/runtime/this-firefox`,
true,
{
ignoreFragment: "whenComparing",
triggeringPrincipal: principal,
}
);
}
}
automaticUpdatesEnabled() {
return AddonManager.updateEnabled && AddonManager.autoUpdateDefault;
}
toggleAutomaticUpdates() {
if (!this.automaticUpdatesEnabled()) {
// One or both of the prefs is false, i.e. the checkbox is not
// checked. Now toggle both to true. If the user wants us to
// auto-update add-ons, we also need to auto-check for updates.
AddonManager.updateEnabled = true;
AddonManager.autoUpdateDefault = true;
} else {
// Both prefs are true, i.e. the checkbox is checked.
// Toggle the auto pref to false, but don't touch the enabled check.
AddonManager.autoUpdateDefault = false;
}
// Record telemetry for changing the update policy.
let updatePolicy = [];
if (AddonManager.autoUpdateDefault) {
updatePolicy.push("default");
}
if (AddonManager.updateEnabled) {
updatePolicy.push("enabled");
}
this.recordActionEvent({
action: "setUpdatePolicy",
value: updatePolicy.join(","),
});
}
async resetAutomaticUpdates() {
let addons = await AddonManager.getAllAddons();
for (let addon of addons) {
if ("applyBackgroundUpdates" in addon) {
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
}
}
this.recordActionEvent({ action: "resetUpdatePolicy" });
}
getTelemetryViewName() {
return getTelemetryViewName(document.getElementById("page-header"));
}
recordActionEvent({ action, value }) {
AMTelemetry.recordActionEvent({
object: "aboutAddons",
view: this.getTelemetryViewName(),
action,
addon: this.addon,
value,
});
}
recordLinkEvent({ value }) {
AMTelemetry.recordLinkEvent({
object: "aboutAddons",
value,
extra: {
view: this.getTelemetryViewName(),
},
});
}
}
customElements.define("addon-page-options", AddonPageOptions);
class AddonOptions extends HTMLElement {
connectedCallback() {
if (!this.children.length) {
@ -1719,6 +2268,8 @@ class AddonCard extends HTMLElement {
let install = addon.updateInstall;
if (install && install.state == AddonManager.STATE_AVAILABLE) {
this.updateInstall = install;
} else {
this.updateInstall = null;
}
if (this.children.length) {
this.render();
@ -1760,18 +2311,14 @@ class AddonCard extends HTMLElement {
this.recordActionEvent("disable");
addon.userDisabled = true;
break;
case "update-check":
case "update-check": {
this.recordActionEvent("checkForUpdate");
let listener = {
onUpdateAvailable(addon, install) {
attachUpdateHandler(install);
},
onNoUpdateAvailable: () => {
this.sendEvent("no-update");
},
};
addon.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
let { found } = await checkForUpdate(addon);
if (!found) {
this.sendEvent("no-update");
}
break;
}
case "install-update":
this.updateInstall.install().then(
() => {
@ -3197,6 +3744,7 @@ class DiscoveryView {
// Generic view management.
let mainEl = null;
let addonPageHeader = null;
/**
* The name of the view for an element, used for telemetry.
@ -3248,6 +3796,7 @@ var ScrollOffsets = {
*/
function initialize(opts) {
mainEl = document.getElementById("main");
addonPageHeader = document.getElementById("page-header");
loadViewFn = opts.loadViewFn;
replaceWithDefaultViewFn = opts.replaceWithDefaultViewFn;
setCategoryFn = opts.setCategoryFn;
@ -3272,6 +3821,7 @@ function initialize(opts) {
async function show(type, param, { isKeyboardNavigation, historyEntryId }) {
let container = document.createElement("div");
container.setAttribute("current-view", type);
addonPageHeader.setViewInfo({ type, param });
if (type == "list") {
await new ListView({ param, root: container }).render();
} else if (type == "detail") {

Просмотреть файл

@ -5,9 +5,10 @@
"use strict";
/* exported attachUpdateHandler, gBrowser, getBrowserElement, isCorrectlySigned,
* isDisabledUnsigned, isPending, loadReleaseNotes, openOptionsInTab,
* promiseEvent, shouldShowPermissionsPrompt, showPermissionsPrompt */
/* exported attachUpdateHandler, gBrowser, getBrowserElement,
* installAddonsFromFilePicker, isCorrectlySigned, isDisabledUnsigned,
* isPending, loadReleaseNotes, openOptionsInTab, promiseEvent,
* shouldShowPermissionsPrompt, showPermissionsPrompt */
const { AddonSettings } = ChromeUtils.import(
"resource://gre/modules/addons/AddonSettings.jsm"
@ -206,3 +207,47 @@ function isPending(addon, action) {
const amAction = AddonManager["PENDING_" + action.toUpperCase()];
return !!(addon.pendingOperations & amAction);
}
async function installAddonsFromFilePicker() {
let [dialogTitle, filterName] = await document.l10n.formatMessages([
{ id: "addon-install-from-file-dialog-title" },
{ id: "addon-install-from-file-filter-name" },
]);
const nsIFilePicker = Ci.nsIFilePicker;
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, dialogTitle.value, nsIFilePicker.modeOpenMultiple);
try {
fp.appendFilter(filterName.value, "*.xpi;*.jar;*.zip");
fp.appendFilters(nsIFilePicker.filterAll);
} catch (e) {}
return new Promise(resolve => {
fp.open(async result => {
if (result != nsIFilePicker.returnOK) {
return;
}
let installTelemetryInfo = {
source: "about:addons",
method: "install-from-file",
};
let browser = getBrowserElement();
let installs = [];
for (let file of fp.files) {
let install = await AddonManager.getInstallForFile(
file,
null,
installTelemetryInfo
);
AddonManager.installAddonFromAOM(
browser,
document.documentURIObject,
install
);
installs.push(install);
}
resolve(installs);
});
});
}

Просмотреть файл

@ -5,7 +5,6 @@
"use strict";
/* import-globals-from ../../../content/customElements.js */
/* import-globals-from ../../../content/contentAreaUtils.js */
/* import-globals-from aboutaddonsCommon.js */
/* globals ProcessingInstruction */
/* exported loadView */
@ -16,9 +15,6 @@ const { DeferredTask } = ChromeUtils.import(
const { AddonManager } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
const { AddonRepository } = ChromeUtils.import(
"resource://gre/modules/addons/AddonRepository.jsm"
);
ChromeUtils.defineModuleGetter(
this,
@ -129,13 +125,15 @@ function initialize(event) {
addonPage.addEventListener("drop", function(event) {
gDragDrop.onDrop(event);
});
addonPage.addEventListener("keypress", function(event) {
gHeader.onKeyPress(event);
});
if (!isDiscoverEnabled()) {
gViewDefault = "addons://list/extension";
}
// Support focusing the search bar from the XUL document.
document.addEventListener("keypress", e => {
htmlBrowser.contentDocument.querySelector("search-addons").handleEvent(e);
});
let helpButton = document.getElementById("helpButton");
let helpUrl =
Services.urlFormatter.formatURLPref("app.support.baseURL") + "addons-help";
@ -166,15 +164,10 @@ function initialize(event) {
gViewController.initialize();
gCategories.initialize();
gHeader.initialize();
gEventManager.initialize();
Services.obs.addObserver(sendEMPong, "EM-ping");
Services.obs.notifyObservers(window, "EM-loaded");
if (!XPINSTALL_ENABLED) {
document.getElementById("cmd_installFromFile").hidden = true;
}
// If the initial view has already been selected (by a call to loadView from
// the above notifications) then bail out now
if (gViewController.initialViewSelected) {
@ -229,25 +222,9 @@ function sendEMPong(aSubject, aTopic, aData) {
function recordLinkTelemetry(target) {
let extra = { view: getCurrentViewName() };
if (target == "search") {
let searchBar = document.getElementById("header-search");
extra.type = searchBar.getAttribute("data-addon-type");
}
AMTelemetry.recordLinkEvent({ object: "aboutAddons", value: target, extra });
}
function recordActionTelemetry({ action, value, view, addon }) {
view = view || getCurrentViewName();
AMTelemetry.recordActionEvent({
// The max-length for an object is 20, which it enough to be unique.
object: "aboutAddons",
value,
view,
action,
addon,
});
}
async function recordViewTelemetry(param) {
let type;
let addon;
@ -265,21 +242,6 @@ async function recordViewTelemetry(param) {
AMTelemetry.recordViewEvent({ view: getCurrentViewName(), addon, type });
}
function recordSetUpdatePolicyTelemetry() {
// Record telemetry for changing the update policy.
let updatePolicy = [];
if (AddonManager.autoUpdateDefault) {
updatePolicy.push("default");
}
if (AddonManager.updateEnabled) {
updatePolicy.push("enabled");
}
recordActionTelemetry({
action: "setUpdatePolicy",
value: updatePolicy.join(","),
});
}
function getCurrentViewName() {
let view = gViewController.currentViewObj;
let entries = Object.entries(gViewController.viewObjects);
@ -324,27 +286,6 @@ function isDiscoverEnabled() {
return true;
}
function setSearchLabel(type) {
let searchLabel = document.getElementById("search-label");
document
.getElementById("header-search")
.setAttribute("data-addon-type", type);
let keyMap = {
extension: "extension",
shortcuts: "extension",
theme: "theme",
};
if (type in keyMap) {
searchLabel.textContent = gStrings.ext.GetStringFromName(
`searchLabel.${keyMap[type]}`
);
searchLabel.hidden = false;
} else {
searchLabel.textContent = "";
searchLabel.hidden = true;
}
}
/**
* A wrapper around the HTML5 session history service that allows the browser
* back/forward controls to work within the manager
@ -365,14 +306,10 @@ var HTML5History = {
back() {
window.history.back();
gViewController.updateCommand("cmd_back");
gViewController.updateCommand("cmd_forward");
},
forward() {
window.history.forward();
gViewController.updateCommand("cmd_back");
gViewController.updateCommand("cmd_forward");
},
pushState(aState) {
@ -393,8 +330,6 @@ var HTML5History = {
}
window.addEventListener("popstate", onStatePopped, true);
window.history.back();
gViewController.updateCommand("cmd_back");
gViewController.updateCommand("cmd_forward");
},
};
@ -424,8 +359,6 @@ var FakeHistory = {
this.pos--;
gViewController.updateState(this.states[this.pos]);
gViewController.updateCommand("cmd_back");
gViewController.updateCommand("cmd_forward");
},
forward() {
@ -435,8 +368,6 @@ var FakeHistory = {
this.pos++;
gViewController.updateState(this.states[this.pos]);
gViewController.updateCommand("cmd_back");
gViewController.updateCommand("cmd_forward");
},
pushState(aState) {
@ -458,8 +389,6 @@ var FakeHistory = {
this.pos--;
gViewController.updateState(this.states[this.pos]);
gViewController.updateCommand("cmd_back");
gViewController.updateCommand("cmd_forward");
},
};
@ -518,7 +447,6 @@ var gEventManager = {
AddonManager.addAddonListener(this);
this.refreshGlobalWarning();
this.refreshAutoUpdateDefault();
},
shutdown() {
@ -622,24 +550,6 @@ var gEventManager = {
page.removeAttribute("warning");
},
refreshAutoUpdateDefault() {
var updateEnabled = AddonManager.updateEnabled;
var autoUpdateDefault = AddonManager.autoUpdateDefault;
// The checkbox needs to reflect that both prefs need to be true
// for updates to be checked for and applied automatically
document
.getElementById("utils-autoUpdateDefault")
.setAttribute("checked", updateEnabled && autoUpdateDefault);
document.getElementById(
"utils-resetAddonUpdatesToAutomatic"
).hidden = !autoUpdateDefault;
document.getElementById(
"utils-resetAddonUpdatesToManual"
).hidden = autoUpdateDefault;
},
onCompatibilityModeChanged() {
this.refreshGlobalWarning();
},
@ -647,10 +557,6 @@ var gEventManager = {
onCheckUpdateSecurityChanged() {
this.refreshGlobalWarning();
},
onUpdateModeChanged() {
this.refreshAutoUpdateDefault();
},
};
var gViewController = {
@ -667,13 +573,11 @@ var gViewController = {
viewChangeCallback: null,
initialViewSelected: false,
lastHistoryIndex: -1,
backButton: null,
initialize() {
this.viewPort = document.getElementById("view-port");
this.headeredViews = document.getElementById("headered-views");
this.headeredViewsDeck = document.getElementById("headered-views-content");
this.backButton = document.getElementById("go-back");
this.viewObjects.shortcuts = htmlView("shortcuts");
this.viewObjects.list = htmlView("list");
@ -867,25 +771,6 @@ var gViewController = {
recordViewTelemetry(view.param);
let headingName = document.getElementById("heading-name");
let headingLabel;
if (view.type == "discover") {
headingLabel = gStrings.ext.formatStringFromName("listHeading.discover", [
gStrings.brandShortName,
]);
} else {
try {
headingLabel = gStrings.ext.GetStringFromName(
`listHeading.${view.param}`
);
} catch (e) {
// Some views don't have a label, like the updates view.
headingLabel = "";
}
}
headingName.textContent = headingLabel;
setSearchLabel(view.param);
if (aViewId == aPreviousView) {
this.currentViewObj.refresh(
view.param,
@ -896,8 +781,6 @@ var gViewController = {
this.currentViewObj.show(view.param, ++this.currentViewRequest, aState);
}
this.backButton.hidden = this.currentViewObj.isRoot || !gHistory.canGoBack;
this.initialViewSelected = true;
},
@ -921,31 +804,6 @@ var gViewController = {
},
commands: {
cmd_back: {
isEnabled() {
return gHistory.canGoBack;
},
doCommand() {
gHistory.back();
},
},
cmd_forward: {
isEnabled() {
return gHistory.canGoForward;
},
doCommand() {
gHistory.forward();
},
},
cmd_focusSearch: {
isEnabled: () => true,
doCommand() {
gHeader.focusSearchBox();
},
},
cmd_enableCheckCompatibility: {
isEnabled() {
return true;
@ -963,258 +821,6 @@ var gViewController = {
AddonManager.checkUpdateSecurity = true;
},
},
cmd_toggleAutoUpdateDefault: {
isEnabled() {
return true;
},
doCommand() {
if (!AddonManager.updateEnabled || !AddonManager.autoUpdateDefault) {
// One or both of the prefs is false, i.e. the checkbox is not checked.
// Now toggle both to true. If the user wants us to auto-update
// add-ons, we also need to auto-check for updates.
AddonManager.updateEnabled = true;
AddonManager.autoUpdateDefault = true;
} else {
// Both prefs are true, i.e. the checkbox is checked.
// Toggle the auto pref to false, but don't touch the enabled check.
AddonManager.autoUpdateDefault = false;
}
recordSetUpdatePolicyTelemetry();
},
},
cmd_resetAddonAutoUpdate: {
isEnabled() {
return true;
},
async doCommand() {
let aAddonList = await AddonManager.getAllAddons();
for (let addon of aAddonList) {
if ("applyBackgroundUpdates" in addon) {
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
}
}
recordActionTelemetry({ action: "resetUpdatePolicy" });
},
},
cmd_goToDiscoverPane: {
isEnabled() {
return gDiscoverView.enabled;
},
doCommand() {
gViewController.loadView("addons://discover/");
},
},
cmd_goToRecentUpdates: {
isEnabled() {
return true;
},
doCommand() {
gViewController.loadView("addons://updates/recent");
},
},
cmd_goToAvailableUpdates: {
isEnabled() {
return true;
},
doCommand() {
gViewController.loadView("addons://updates/available");
},
},
cmd_findAllUpdates: {
inProgress: false,
isEnabled() {
return !this.inProgress;
},
async doCommand() {
this.inProgress = true;
gViewController.updateCommand("cmd_findAllUpdates");
document.getElementById("updates-noneFound").hidden = true;
document.getElementById("updates-progress").hidden = false;
document.getElementById("updates-manualUpdatesFound-btn").hidden = true;
var pendingChecks = 0;
var numUpdated = 0;
var numManualUpdates = 0;
let updateStatus = () => {
if (pendingChecks > 0) {
return;
}
this.inProgress = false;
gViewController.updateCommand("cmd_findAllUpdates");
document.getElementById("updates-progress").hidden = true;
gUpdatesView.maybeRefresh();
Services.obs.notifyObservers(null, "EM-update-check-finished");
if (numManualUpdates > 0 && numUpdated == 0) {
document.getElementById(
"updates-manualUpdatesFound-btn"
).hidden = false;
return;
}
if (numUpdated == 0) {
document.getElementById("updates-noneFound").hidden = false;
return;
}
document.getElementById("updates-installed").hidden = false;
};
var updateInstallListener = {
onDownloadFailed() {
pendingChecks--;
updateStatus();
},
onInstallCancelled() {
pendingChecks--;
updateStatus();
},
onInstallFailed() {
pendingChecks--;
updateStatus();
},
onInstallEnded(aInstall, aAddon) {
pendingChecks--;
numUpdated++;
updateStatus();
},
};
var updateCheckListener = {
onUpdateAvailable(aAddon, aInstall) {
gEventManager.delegateAddonEvent("onUpdateAvailable", [
aAddon,
aInstall,
]);
attachUpdateHandler(aInstall);
if (AddonManager.shouldAutoUpdate(aAddon)) {
aInstall.addListener(updateInstallListener);
aInstall.install();
} else {
pendingChecks--;
numManualUpdates++;
updateStatus();
}
},
onNoUpdateAvailable(aAddon) {
pendingChecks--;
updateStatus();
},
onUpdateFinished(aAddon, aError) {
gEventManager.delegateAddonEvent("onUpdateFinished", [
aAddon,
aError,
]);
},
};
let aAddonList = await AddonManager.getAddonsByTypes(null);
for (let addon of aAddonList) {
if (addon.permissions & AddonManager.PERM_CAN_UPGRADE) {
pendingChecks++;
addon.findUpdates(
updateCheckListener,
AddonManager.UPDATE_WHEN_USER_REQUESTED
);
}
}
recordActionTelemetry({ action: "checkForUpdates" });
if (pendingChecks == 0) {
updateStatus();
}
},
},
cmd_installFromFile: {
isEnabled() {
return XPINSTALL_ENABLED;
},
doCommand() {
const nsIFilePicker = Ci.nsIFilePicker;
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(
window,
gStrings.ext.GetStringFromName("installFromFile.dialogTitle"),
nsIFilePicker.modeOpenMultiple
);
try {
fp.appendFilter(
gStrings.ext.GetStringFromName("installFromFile.filterName"),
"*.xpi;*.jar;*.zip"
);
fp.appendFilters(nsIFilePicker.filterAll);
} catch (e) {}
fp.open(async result => {
if (result != nsIFilePicker.returnOK) {
return;
}
let installTelemetryInfo = {
source: "about:addons",
method: "install-from-file",
};
let browser = getBrowserElement();
for (let file of fp.files) {
let install = await AddonManager.getInstallForFile(
file,
null,
installTelemetryInfo
);
AddonManager.installAddonFromAOM(
browser,
document.documentURIObject,
install
);
recordActionTelemetry({
action: "installFromFile",
value: install.installId,
});
}
});
},
},
cmd_debugAddons: {
isEnabled() {
return true;
},
doCommand() {
let mainWindow = window.windowRoot.ownerGlobal;
recordLinkTelemetry("about:debugging");
if ("switchToTabHavingURI" in mainWindow) {
mainWindow.switchToTabHavingURI(
`about:debugging#/runtime/this-firefox`,
true,
{
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
}
);
}
},
},
cmd_showShortcuts: {
isEnabled() {
return true;
},
doCommand() {
gViewController.loadView("addons://shortcuts/shortcuts");
},
},
},
supportsCommand(aCommand) {
@ -1581,85 +1187,6 @@ var gCategories = {
// categories are in the XUL markup.
gCategories._defineCustomElement();
var gHeader = {
_search: null,
_dest: "",
initialize() {
this._search = document.getElementById("header-search");
this._search.addEventListener("command", function(aEvent) {
var query = aEvent.target.value;
if (!query.length) {
return;
}
let url = AddonRepository.getSearchURL(query);
let browser = getBrowserElement();
let chromewin = browser.ownerGlobal;
chromewin.openLinkIn(url, "tab", {
fromChrome: true,
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
});
recordLinkTelemetry("search");
});
},
focusSearchBox() {
this._search.focus();
},
onKeyPress(aEvent) {
if (String.fromCharCode(aEvent.charCode) == "/") {
this.focusSearchBox();
}
},
get shouldShowNavButtons() {
var docshellItem = window.docShell;
// If there is no outer frame then make the buttons visible
if (docshellItem.rootTreeItem == docshellItem) {
return true;
}
var outerWin = docshellItem.rootTreeItem.domWindow;
var outerDoc = outerWin.document;
var node = outerDoc.getElementById("back-button");
// If the outer frame has no back-button then make the buttons visible
if (!node) {
return true;
}
// If the back-button or any of its parents are hidden then make the buttons
// visible
while (node != outerDoc) {
var style = outerWin.getComputedStyle(node);
if (style.display == "none") {
return true;
}
if (style.visibility != "visible") {
return true;
}
node = node.parentNode;
}
return false;
},
get searchQuery() {
return this._search.value;
},
set searchQuery(aQuery) {
this._search.value = aQuery;
},
};
var gDiscoverView = {
node: null,
enabled: true,

Просмотреть файл

@ -13,15 +13,11 @@
<!-- @CSP: Soon, about:addons will not be implemented using XUL anymore (see Bug 1558982).
- Until then we have to whitelist the following things:
- *) the 'oncommand' handler for cmd_focusSearch within extensions.xul, using
- sha512-4o5Uf4E4EG+90Mb820FH2YFDf4IuX4bfUwQC7reK1ZhgcXWJBKMK2330XIELaFJJ8HiPffS9mP60MPjuXMIrHA==
- *) the 'onclick' handler for the searchButtonIcon within the file search-textbox.js, using
- sha512-kSDNX67wegjpcf8CSj/L6h46a0QUKm2CyijGxC5PhSWVvPU9gdd28QVBBFq9t8N5UGKUFdDcZsjYbGSlYG0y3g==
- *) the extensions.webservice.discoverURL using data:, http:, and https:
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
csp="default-src chrome:; frame-src chrome: data: http: https:; script-src chrome: 'sha512-4o5Uf4E4EG+90Mb820FH2YFDf4IuX4bfUwQC7reK1ZhgcXWJBKMK2330XIELaFJJ8HiPffS9mP60MPjuXMIrHA==' 'sha512-kSDNX67wegjpcf8CSj/L6h46a0QUKm2CyijGxC5PhSWVvPU9gdd28QVBBFq9t8N5UGKUFdDcZsjYbGSlYG0y3g=='; object-src 'none'"
csp="default-src chrome:; frame-src chrome: data: http: https:; script-src chrome:; object-src 'none'"
id="addons-page" data-l10n-id="addons-window"
role="application" windowtype="Addons:Manager">
@ -32,7 +28,6 @@
<xhtml:link rel="localization" href="toolkit/about/aboutAddons.ftl"/>
</linkset>
<script src="chrome://global/content/contentAreaUtils.js"/>
<script src="chrome://mozapps/content/extensions/aboutaddonsCommon.js"/>
<script src="chrome://mozapps/content/extensions/extensions.js"/>
<script src="chrome://mozapps/content/extensions/abuse-report-frame.js"/>
@ -65,30 +60,10 @@
<!-- global commands - these act on all addons, or affect the addons manager
in some other way -->
<commandset id="globalCommandSet">
<!-- XXXsw remove useless oncommand attribute once bug 371900 is fixed -->
<!-- If you remove/update the oncommand handler for cmd_focusSearch please
also remove/update the sha512 hash in the CSP above -->
<command id="cmd_focusSearch" oncommand=";"/>
<command id="cmd_findAllUpdates"/>
<command id="cmd_goToDiscoverPane"/>
<command id="cmd_goToRecentUpdates"/>
<command id="cmd_goToAvailableUpdates"/>
<command id="cmd_installFromFile"/>
<command id="cmd_debugAddons"/>
<command id="cmd_back"/>
<command id="cmd_forward"/>
<command id="cmd_enableCheckCompatibility"/>
<command id="cmd_enableUpdateSecurity"/>
<command id="cmd_toggleAutoUpdateDefault"/>
<command id="cmd_resetAddonAutoUpdate"/>
<command id="cmd_showShortcuts"/>
</commandset>
<keyset>
<key id="focusSearch" data-l10n-id="search-header-shortcut"
modifiers="accel" command="cmd_focusSearch"/>
</keyset>
<stack id="main-page-stack" flex="1">
<hbox id="main-page-content" flex="1">
<vbox id="category-box">
@ -172,75 +147,6 @@
<!-- container for views with the search/tools header -->
<vbox id="headered-views" flex="1">
<!-- main header -->
<hbox id="header">
<hbox id="header-inner" align="center" pack="end">
<label id="search-label" control="header-search"/>
<search-textbox id="header-search" searchbutton="true"
data-l10n-id="search-header"
data-l10n-attrs="placeholder, searchbuttonlabel" maxlength="100"/>
</hbox>
</hbox>
<hbox id="heading">
<hbox class="heading-inner" align="stretch">
<hbox id="go-back-wrapper">
<button id="go-back" command="cmd_back" data-l10n-id="go-back-button" hidden="true"/>
</hbox>
<label id="heading-name" class="header-name" flex="1"/>
<hbox id="updates-container" align="center">
<image class="spinner"/>
<label id="updates-noneFound" hidden="true"
data-l10n-id="extensions-updates-none-found"/>
<button id="updates-manualUpdatesFound-btn" class="button-link"
hidden="true"
data-l10n-id="extensions-updates-manual-updates-found"
command="cmd_goToAvailableUpdates"/>
<label id="updates-progress" hidden="true"
data-l10n-id="extensions-updates-updating"/>
<label id="updates-installed" hidden="true"
data-l10n-id="extensions-updates-installed"/>
<label id="updates-downloaded" hidden="true"
data-l10n-id="extensions-updates-downloaded"/>
</hbox>
<button id="header-utils-btn" type="menu" data-l10n-id="tools-menu">
<menupopup id="utils-menu">
<menuitem id="utils-updateNow"
data-l10n-id="extensions-updates-check-for-updates"
command="cmd_findAllUpdates"/>
<menuitem id="utils-viewUpdates"
data-l10n-id="extensions-updates-view-updates"
command="cmd_goToRecentUpdates"/>
<menuseparator id="utils-installFromFile-separator"/>
<menuitem id="utils-installFromFile"
data-l10n-id="install-addon-from-file"
command="cmd_installFromFile"/>
<menuitem id="utils-debugAddons"
data-l10n-id="debug-addons"
command="cmd_debugAddons"/>
<menuseparator/>
<menuitem id="utils-autoUpdateDefault"
data-l10n-id="extensions-updates-update-addons-automatically"
type="checkbox" autocheck="false"
command="cmd_toggleAutoUpdateDefault"/>
<menuitem id="utils-resetAddonUpdatesToAutomatic"
data-l10n-id="extensions-updates-reset-updates-to-automatic"
command="cmd_resetAddonAutoUpdate"/>
<menuitem id="utils-resetAddonUpdatesToManual"
data-l10n-id="extensions-updates-reset-updates-to-manual"
command="cmd_resetAddonAutoUpdate"/>
<menuseparator/>
<menuitem id="manage-shortcuts"
data-l10n-id="manage-extensions-shortcuts"
command="cmd_showShortcuts"/>
</menupopup>
</button>
</hbox>
</hbox>
<deck id="headered-views-content" flex="1" selectedIndex="0">
<vbox id="html-view" flex="1">
<vbox class="alert-container html-alert-container" align="start">

Просмотреть файл

@ -13,6 +13,10 @@
fill: currentColor;
}
:host([checked]) button {
background-size: 1em;
}
button {
background-color: transparent;
color: inherit;

Просмотреть файл

@ -48,14 +48,10 @@ generated-files =
tags = blocklist
[browser_about_debugging_link.js]
[browser_bug523784.js]
[browser_bug567127.js]
skip-if = (!debug && os == 'win') #Bug 1489496
[browser_bug567137.js]
[browser_bug570760.js]
skip-if = verify
[browser_bug572561.js]
[browser_bug577990.js]
[browser_bug586574.js]
[browser_bug591465.js]
skip-if = os == "linux" && !debug # Bug 1395539 - fails on multi-core
[browser_bug679604.js]
@ -96,9 +92,12 @@ skip-if = verify
[browser_interaction_telemetry.js]
[browser_manage_shortcuts.js]
[browser_manage_shortcuts_hidden.js]
[browser_page_options_install_addon.js]
[browser_page_options_updates.js]
[browser_panel_item_accesskey.js]
[browser_pluginprefs.js]
[browser_recentupdates.js]
[browser_reinstall.js]
[browser_search_bar_focus.js]
[browser_shortcuts_duplicate_check.js]
[browser_task_next_test.js]
[browser_updateid.js]

Просмотреть файл

@ -68,12 +68,12 @@ function waitForRequestsSuccess(store) {
}
add_task(async function testAboutDebugging() {
let win = await open_manager(null);
let categoryUtilities = new CategoryUtilities(win);
await categoryUtilities.openType("extension");
let win = await loadInitialView("extension");
let aboutAddonsTab = gBrowser.selectedTab;
let debugAddonsBtn = win.document.getElementById("utils-debugAddons");
let debugAddonsBtn = win.document.querySelector(
'#page-options [action="debug-addons"]'
);
// Verify the about:debugging is loaded.
info(`Check about:debugging loads`);
@ -82,7 +82,7 @@ add_task(async function testAboutDebugging() {
"about:debugging#/runtime/this-firefox",
true
);
debugAddonsBtn.doCommand();
debugAddonsBtn.click();
await loaded;
let aboutDebuggingTab = gBrowser.selectedTab;
const { AboutDebugging } = aboutDebuggingTab.linkedBrowser.contentWindow;
@ -99,7 +99,21 @@ add_task(async function testAboutDebugging() {
let switched = TestUtils.waitForCondition(
() => gBrowser.selectedTab == aboutDebuggingTab
);
debugAddonsBtn.doCommand();
debugAddonsBtn.click();
await switched;
info("Force about:debugging to a different hash URL");
aboutDebuggingTab.linkedBrowser.contentWindow.location.hash = "/setup";
info("Switch back to about:addons again");
await BrowserTestUtils.switchTab(gBrowser, aboutAddonsTab);
is(gBrowser.selectedTab, aboutAddonsTab, "Back to about:addons");
info("Re-open about:debugging a second time");
switched = TestUtils.waitForCondition(
() => gBrowser.selectedTab == aboutDebuggingTab
);
debugAddonsBtn.click();
await switched;
info("Wait until any new about:debugging request did settle");
@ -110,5 +124,5 @@ add_task(async function testAboutDebugging() {
info("Remove the about:debugging tab");
BrowserTestUtils.removeTab(aboutDebuggingTab);
await close_manager(win);
await closeView(win);
});

Просмотреть файл

@ -1,42 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Bug 570760 - Make ctrl-f and / focus the search box in the add-ons manager
var gManagerWindow;
var focusCount = 0;
async function test() {
waitForExplicitFinish();
// The discovery pane does not display the about:addons searchbox,
// open the extensions pane instead.
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
var searchBox = gManagerWindow.document.getElementById("header-search");
function focusHandler() {
searchBox.blur();
focusCount++;
}
searchBox.inputField.addEventListener("focus", focusHandler);
f_key_test();
slash_key_test();
searchBox.inputField.removeEventListener("focus", focusHandler);
end_test();
}
function end_test() {
close_manager(gManagerWindow, finish);
}
function f_key_test() {
EventUtils.synthesizeKey("f", { accelKey: true }, gManagerWindow);
is(focusCount, 1, "Search box should have been focused due to the f key");
}
function slash_key_test() {
EventUtils.synthesizeKey("/", {}, gManagerWindow);
is(focusCount, 2, "Search box should have been focused due to the / key");
}

Просмотреть файл

@ -1,368 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Bug 586574 - Provide way to set a default for automatic updates
// Bug 710064 - Make the "Update Add-ons Automatically" checkbox state
// also depend on the extensions.update.enabled pref
// TEST_PATH=toolkit/mozapps/extensions/test/browser/browser_bug586574.js make -C obj-ff mochitest-browser-chrome
const PREF_UPDATE_ENABLED = "extensions.update.enabled";
const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault";
var gManagerWindow;
var gProvider;
var gUtilsBtn;
var gUtilsMenu;
var gSetDefault;
var gResetToAutomatic;
var gResetToManual;
// Make sure we don't accidentally start a background update while the prefs
// are enabled.
disableBackgroundUpdateTimer();
registerCleanupFunction(() => {
enableBackgroundUpdateTimer();
});
async function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
gUtilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
gUtilsMenu = gManagerWindow.document.getElementById("utils-menu");
run_next_test();
}
function end_test() {
close_manager(gManagerWindow, finish);
}
function wait_for_popup(aCallback) {
if (gUtilsMenu.state == "open") {
aCallback();
return;
}
gUtilsMenu.addEventListener(
"popupshown",
function() {
info("Utilities menu shown");
aCallback();
},
{ once: true }
);
}
function wait_for_hide(aCallback) {
if (gUtilsMenu.state == "closed") {
aCallback();
return;
}
gUtilsMenu.addEventListener(
"popuphidden",
function() {
info("Utilities menu hidden");
aCallback();
},
{ once: true }
);
}
add_test(function() {
gSetDefault = gManagerWindow.document.getElementById(
"utils-autoUpdateDefault"
);
gResetToAutomatic = gManagerWindow.document.getElementById(
"utils-resetAddonUpdatesToAutomatic"
);
gResetToManual = gManagerWindow.document.getElementById(
"utils-resetAddonUpdatesToManual"
);
info("Ensuring default prefs are set to true");
Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, true);
Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, true);
wait_for_popup(function() {
is(
gSetDefault.getAttribute("checked"),
"true",
"#1 Set Default menuitem should be checked"
);
is_element_visible(
gResetToAutomatic,
"#1 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#1 Reset to Manual menuitem should be hidden"
);
var listener = {
onPropertyChanged(aAddon, aProperties) {
AddonManager.removeAddonListener(listener);
is(
aAddon.id,
gProvider.addons[0].id,
"Should get onPropertyChanged event for correct addon"
);
ok(
!("applyBackgroundUpdates" in aProperties),
"Should have gotten applyBackgroundUpdates in properties array"
);
is(
aAddon.applyBackgroundUpdates,
AddonManager.AUTOUPDATE_DEFAULT,
"Addon.applyBackgroundUpdates should have been reset to default"
);
info("Setting Addon.applyBackgroundUpdates back to disabled");
aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
wait_for_hide(run_next_test);
},
};
AddonManager.addAddonListener(listener);
info("Clicking Reset to Automatic menuitem");
EventUtils.synthesizeMouseAtCenter(gResetToAutomatic, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
info("Disabling extensions.update.enabled");
Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, false);
wait_for_popup(function() {
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#2 Set Default menuitem should not be checked"
);
is_element_visible(
gResetToAutomatic,
"#2 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#2 Reset to Manual menuitem should be hidden"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem to reenable");
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
ok(
Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test"
);
ok(
Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
"extensions.update.autoUpdateDefault should be true after the previous test"
);
info(
"Disabling both extensions.update.enabled and extensions.update.autoUpdateDefault"
);
Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, false);
Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, false);
wait_for_popup(function() {
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#3 Set Default menuitem should not be checked"
);
is_element_hidden(
gResetToAutomatic,
"#3 Reset to automatic menuitem should be hidden"
);
is_element_visible(
gResetToManual,
"#3 Reset to manual menuitem should be visible"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem to reenable");
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
ok(
Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test"
);
ok(
Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
"extensions.update.autoUpdateDefault should be true after the previous test"
);
info("clicking the button to disable extensions.update.autoUpdateDefault");
wait_for_popup(function() {
is(
gSetDefault.getAttribute("checked"),
"true",
"#4 Set Default menuitem should be checked"
);
is_element_visible(
gResetToAutomatic,
"#4 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#4 Reset to Manual menuitem should be hidden"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem to disable");
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
ok(
Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test"
);
is(
Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
false,
"extensions.update.autoUpdateDefault should be false after the previous test"
);
wait_for_popup(function() {
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#5 Set Default menuitem should not be checked"
);
is_element_hidden(
gResetToAutomatic,
"#5 Reset to automatic menuitem should be hidden"
);
is_element_visible(
gResetToManual,
"#5 Reset to manual menuitem should be visible"
);
var listener = {
onPropertyChanged(aAddon, aProperties) {
AddonManager.removeAddonListener(listener);
is(
aAddon.id,
gProvider.addons[0].id,
"Should get onPropertyChanged event for correct addon"
);
ok(
!("applyBackgroundUpdates" in aProperties),
"Should have gotten applyBackgroundUpdates in properties array"
);
is(
aAddon.applyBackgroundUpdates,
AddonManager.AUTOUPDATE_DEFAULT,
"Addon.applyBackgroundUpdates should have been reset to default"
);
info("Setting Addon.applyBackgroundUpdates back to disabled");
aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
wait_for_hide(run_next_test);
},
};
AddonManager.addAddonListener(listener);
info("Clicking Reset to Manual menuitem");
EventUtils.synthesizeMouseAtCenter(gResetToManual, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
wait_for_popup(function() {
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#6 Set Default menuitem should not be checked"
);
is_element_hidden(
gResetToAutomatic,
"#6 Reset to automatic menuitem should be hidden"
);
is_element_visible(
gResetToManual,
"#6 Reset to manual menuitem should be visible"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem");
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
wait_for_popup(function() {
is(
gSetDefault.getAttribute("checked"),
"true",
"#7 Set Default menuitem should be checked"
);
is_element_visible(
gResetToAutomatic,
"#7 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#7 Reset to Manual menuitem should be hidden"
);
wait_for_hide(run_next_test);
gUtilsMenu.hidePopup();
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});

Просмотреть файл

@ -200,7 +200,7 @@ add_task(async function testOpenDetailView() {
const goBack = async win => {
let loaded = waitForViewLoad(win);
win.managerWindow.document.getElementById("go-back").click();
win.document.querySelector(".back-button").click();
await loaded;
};
@ -1097,7 +1097,7 @@ add_task(async function testGoBackButton() {
let id = "addon1@mochi.test";
let win = await loadInitialView("extension");
let doc = win.document;
let backButton = win.managerWindow.document.getElementById("go-back");
let backButton = doc.querySelector(".back-button");
let loadDetailView = () => {
let loaded = waitForViewLoad(win);

Просмотреть файл

@ -118,7 +118,10 @@ class DiscoveryAPIHandler {
// Retrieve the list of visible action elements inside a document or container.
function getVisibleActions(documentOrElement) {
return Array.from(documentOrElement.querySelectorAll("[action]")).filter(
elem => elem.offsetWidth && elem.offsetHeight
elem =>
elem.getAttribute("action") !== "page-options" &&
elem.offsetWidth &&
elem.offsetHeight
);
}
@ -156,7 +159,7 @@ async function switchToNonDiscoView(win) {
// Listeners registered while the discopane was the active view continue to be
// active when the view switches to the extensions list, because both views
// share the same document.
win.managerWindow.gViewController.loadView("addons://list/extensions");
win.managerWindow.gViewController.loadView("addons://list/extension");
await wait_for_view_load(win.managerWindow);
ok(
win.document.querySelector("addon-list"),

Просмотреть файл

@ -74,13 +74,23 @@ add_task(async function testRecentUpdatesList() {
// Load extension view first so we can mock the startOfDay property.
let win = await loadInitialView("extension");
let doc = win.document;
let managerDoc = win.managerWindow.document;
const RECENT_URL = "addons://updates/recent";
let recentCat = win.managerWindow.gCategories.get(RECENT_URL);
ok(recentCat.hidden, "Recent updates category is initially hidden");
// Load the recent updates view.
let loaded = waitForViewLoad(win);
managerDoc.getElementById("utils-viewUpdates").doCommand();
doc.querySelector('#page-options [action="view-recent-updates"]').click();
await loaded;
is(
win.managerWindow.gCategories.selected,
RECENT_URL,
"Recent updates is selected"
);
ok(!recentCat.hidden, "Recent updates category is now shown");
// Find all the add-on ids.
let list = doc.querySelector("addon-list");
let addonsInOrder = () =>
@ -119,7 +129,7 @@ add_task(async function testRecentUpdatesList() {
info("Go back to the recent updates view");
loaded = waitForViewLoad(win);
managerDoc.getElementById("utils-viewUpdates").doCommand();
doc.querySelector('#page-options [action="view-recent-updates"]').click();
await loaded;
// Find the list again.

Просмотреть файл

@ -65,7 +65,7 @@ add_task(async function testChangeAutoUpdates() {
// Go back to the list view and check the details view again.
let loaded = waitForViewLoad(win);
win.managerWindow.document.getElementById("go-back").click();
doc.querySelector(".back-button").click();
await loaded;
// Load the detail view again.
@ -577,7 +577,6 @@ add_task(async function testAvailableUpdates() {
let win = await loadInitialView("extension");
let doc = win.document;
let managerDoc = win.managerWindow.document;
let { gCategories } = win.managerWindow;
let availableCat = gCategories.get("addons://updates/available");
@ -586,7 +585,7 @@ add_task(async function testAvailableUpdates() {
// Check for all updates.
let updatesFound = TestUtils.topicObserved("EM-update-check-finished");
managerDoc.getElementById("utils-updateNow").doCommand();
doc.querySelector('#page-options [action="check-for-updates"]').click();
await updatesFound;
// Wait for the available updates count to finalize, it's async.
@ -620,7 +619,7 @@ add_task(async function testAvailableUpdates() {
// Go back to the last view.
loaded = waitForViewLoad(win);
managerDoc.getElementById("go-back").click();
doc.querySelector(".back-button").click();
await loaded;
// We're back on the updates view.

Просмотреть файл

@ -466,48 +466,58 @@ add_task(async function testExtensionEvents() {
});
add_task(async function testGeneralActions() {
await init("extension");
let win = await loadInitialView("extension");
info("Loaded");
let doc = gManagerWindow.document;
let menu = doc.getElementById("utils-menu");
let checkForUpdates = doc.getElementById("utils-updateNow");
let recentUpdates = doc.getElementById("utils-viewUpdates");
let debugAddons = doc.getElementById("utils-debugAddons");
let updatePolicy = doc.getElementById("utils-autoUpdateDefault");
let resetUpdatePolicy = doc.getElementById(
"utils-resetAddonUpdatesToAutomatic"
);
let manageShortcuts = doc.getElementById("manage-shortcuts");
let doc = win.document;
let pageOptionsButton = doc.querySelector('[action="page-options"]');
let menu = doc.querySelector("#page-options panel-list");
let checkForUpdates = menu.querySelector('[action="check-for-updates"]');
let recentUpdates = menu.querySelector('[action="view-recent-updates"]');
let updatePolicy = menu.querySelector('[action="set-update-automatically"]');
let resetUpdatePolicy = menu.querySelector('[action="reset-update-states"]');
let debugAddons = menu.querySelector('[action="debug-addons"]');
let manageShortcuts = menu.querySelector('[action="manage-shortcuts"]');
async function clickInGearMenu(item) {
let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
menu.openPopup();
info(`Opening menu to click ${item.getAttribute("action")}`);
let shown = BrowserTestUtils.waitForEvent(menu, "shown");
// This should perform a click on the button to ensure that works. Other
// tests might just open the menu directly, or click items when it's closed.
pageOptionsButton.click();
await shown;
info(`Clicking ${item.getAttribute("action")}`);
item.click();
menu.hidePopup();
}
await clickInGearMenu(checkForUpdates);
let recentUpdatesLoaded = waitForViewLoad(win);
await clickInGearMenu(recentUpdates);
await wait_for_view_load(gManagerWindow);
await recentUpdatesLoaded;
await clickInGearMenu(updatePolicy);
await clickInGearMenu(updatePolicy);
await clickInGearMenu(resetUpdatePolicy);
// Check shortcuts view.
let shortcutsLoaded = waitForViewLoad(win);
await clickInGearMenu(manageShortcuts);
await wait_for_view_load(gManagerWindow);
await shortcutsLoaded;
await clickInGearMenu(checkForUpdates);
let waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
await clickInGearMenu(debugAddons);
BrowserTestUtils.removeTab(await waitForNewTab);
info("Waiting for about:debugging tab");
let tab = await waitForNewTab;
BrowserTestUtils.removeTab(tab);
waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
let searchBox = doc.getElementById("header-search");
let searchBox = doc.getElementById("search-addons");
searchBox.value = "something";
searchBox.doCommand();
BrowserTestUtils.removeTab(await waitForNewTab);
searchBox.focus();
EventUtils.synthesizeKey("KEY_Enter", {}, win);
info("Waiting for AMO search tab");
tab = await waitForNewTab;
BrowserTestUtils.removeTab(tab);
assertAboutAddonsTelemetryEvents(
[
@ -567,7 +577,7 @@ add_task(async function testGeneralActions() {
{ methods: TELEMETRY_METHODS }
);
await close_manager(gManagerWindow);
await closeView(win);
assertAboutAddonsTelemetryEvents([]);
});

Просмотреть файл

@ -5,6 +5,13 @@ const { PromiseTestUtils } = ChromeUtils.import(
);
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
function extensionShortcutsReady(id) {
let extension = WebExtensionPolicy.getByID(id).extension;
return BrowserTestUtils.waitForCondition(() => {
return extension.shortcuts.keysetsMap.has(window);
}, "Wait for add-on keyset to be registered");
}
async function loadShortcutsView() {
// Load the theme view initially so we can verify that the category is switched
// to "extension" when the shortcuts view is loaded.
@ -17,7 +24,9 @@ async function loadShortcutsView() {
"The theme category is selected"
);
let shortcutsLink = managerWindow.document.getElementById("manage-shortcuts");
let shortcutsLink = win.document.querySelector(
'#page-options [action="manage-shortcuts"]'
);
ok(!shortcutsLink.hidden, "The shortcuts link is visible");
let loaded = waitForViewLoad(win);
@ -63,6 +72,7 @@ add_task(async function testUpdatingCommands() {
await extension.startup();
await extension.awaitMessage("ready");
await extensionShortcutsReady(extension.id);
async function checkShortcut(name, key, modifiers) {
EventUtils.synthesizeKey(key, modifiers);
@ -107,7 +117,8 @@ add_task(async function testUpdatingCommands() {
// Wait for the shortcut attribute to change.
await BrowserTestUtils.waitForCondition(
() => input.getAttribute("shortcut") == "Alt+Shift+8"
() => input.getAttribute("shortcut") == "Alt+Shift+8",
"Wait for shortcut to update to Alt+Shift+8"
);
// Check that the change worked (but skip if browserAction).
@ -125,7 +136,8 @@ add_task(async function testUpdatingCommands() {
altKey: true,
});
await BrowserTestUtils.waitForCondition(
() => input.getAttribute("shortcut") == `Alt+Shift+${count}`
() => input.getAttribute("shortcut") == `Alt+Shift+${count}`,
`Wait for shortcut to update to Alt+Shift+${count}`
);
}
@ -191,6 +203,7 @@ async function startExtensionWithCommands(numCommands) {
await extension.startup();
await extension.awaitMessage("ready");
await extensionShortcutsReady(extension.id);
return extension;
}

Просмотреть файл

@ -7,8 +7,6 @@
var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window);
var gManagerWindow;
async function checkInstallConfirmation(...names) {
let notificationCount = 0;
let observer = {
@ -62,7 +60,7 @@ async function checkInstallConfirmation(...names) {
}
add_task(async function test_install_from_file() {
gManagerWindow = await open_manager("addons://list/extension");
let win = await loadInitialView("extension");
var filePaths = [
get_addon_file_url("browser_dragdrop1.xpi"),
@ -81,10 +79,50 @@ add_task(async function test_install_from_file() {
"Drag Drop test 2"
);
gManagerWindow.gViewController.doCommand("cmd_installFromFile");
win.document
.querySelector('#page-options [action="install-from-file"]')
.click();
await pInstallURIClosed;
MockFilePicker.cleanup();
await close_manager(gManagerWindow);
await closeView(win);
});
add_task(async function test_install_disabled() {
let win = await loadInitialView("extension");
let doc = win.document;
let pageOptionsMenu = doc.querySelector("addon-page-options panel-list");
function openPageOptions() {
let opened = BrowserTestUtils.waitForEvent(pageOptionsMenu, "shown");
pageOptionsMenu.open = true;
return opened;
}
function closePageOptions() {
let closed = BrowserTestUtils.waitForEvent(pageOptionsMenu, "hidden");
pageOptionsMenu.open = false;
return closed;
}
await openPageOptions();
let installButton = doc.querySelector('[action="install-from-file"]');
ok(!installButton.hidden, "The install button is shown");
await closePageOptions();
await SpecialPowers.pushPrefEnv({ set: [[PREF_XPI_ENABLED, false]] });
await openPageOptions();
ok(installButton.hidden, "The install button is now hidden");
await closePageOptions();
await SpecialPowers.popPrefEnv();
await openPageOptions();
ok(!installButton.hidden, "The install button is shown again");
await closePageOptions();
await closeView(win);
});

Просмотреть файл

@ -0,0 +1,163 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Make sure we don't accidentally start a background update while the prefs
// are enabled.
disableBackgroundUpdateTimer();
registerCleanupFunction(() => {
enableBackgroundUpdateTimer();
});
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm",
{}
);
const PREF_UPDATE_ENABLED = "extensions.update.enabled";
const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault";
add_task(async function testUpdateAutomaticallyButton() {
Services.telemetry.clearEvents();
SpecialPowers.pushPrefEnv({
set: [[PREF_UPDATE_ENABLED, true], [PREF_AUTOUPDATE_DEFAULT, true]],
});
let win = await loadInitialView("extension");
let toggleAutomaticButton = win.document.querySelector(
'#page-options [action="set-update-automatically"]'
);
info("Verify the checked state reflects the update state");
ok(toggleAutomaticButton.checked, "Automatic updates button is checked");
AddonManager.autoUpdateDefault = false;
ok(!toggleAutomaticButton.checked, "Automatic updates button is unchecked");
AddonManager.autoUpdateDefault = true;
ok(toggleAutomaticButton.checked, "Automatic updates button is re-checked");
info("Verify that clicking the button changes the update state");
ok(AddonManager.autoUpdateDefault, "Auto updates are default");
ok(AddonManager.updateEnabled, "Updates are enabled");
toggleAutomaticButton.click();
ok(!AddonManager.autoUpdateDefault, "Auto updates are disabled");
ok(AddonManager.updateEnabled, "Updates are enabled");
toggleAutomaticButton.click();
ok(AddonManager.autoUpdateDefault, "Auto updates are enabled again");
ok(AddonManager.updateEnabled, "Updates are enabled");
assertAboutAddonsTelemetryEvents(
[
[
"addonsManager",
"action",
"aboutAddons",
"enabled",
{ action: "setUpdatePolicy", view: "list" },
],
[
"addonsManager",
"action",
"aboutAddons",
"default,enabled",
{ action: "setUpdatePolicy", view: "list" },
],
],
{ methods: ["action"] }
);
await closeView(win);
});
add_task(async function testResetUpdateStates() {
let id = "update-state@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: { gecko: { id } },
},
useAddonManager: "permanent",
});
await extension.startup();
let win = await loadInitialView("extension");
let resetStateButton = win.document.querySelector(
'#page-options [action="reset-update-states"]'
);
info("Changing add-on update state");
let addon = await AddonManager.getAddonByID(id);
let setAddonUpdateState = async updateState => {
let changed = AddonTestUtils.promiseAddonEvent("onPropertyChanged");
addon.applyBackgroundUpdates = updateState;
await changed;
let addonState = addon.applyBackgroundUpdates;
is(addonState, updateState, `Add-on updates are ${updateState}`);
};
await setAddonUpdateState(AddonManager.AUTOUPDATE_DISABLE);
let propertyChanged = AddonTestUtils.promiseAddonEvent("onPropertyChanged");
resetStateButton.click();
await propertyChanged;
is(
addon.applyBackgroundUpdates,
AddonManager.AUTOUPDATE_DEFAULT,
"Add-on is reset to default updates"
);
await setAddonUpdateState(AddonManager.AUTOUPDATE_ENABLE);
propertyChanged = AddonTestUtils.promiseAddonEvent("onPropertyChanged");
resetStateButton.click();
await propertyChanged;
is(
addon.applyBackgroundUpdates,
AddonManager.AUTOUPDATE_DEFAULT,
"Add-on is reset to default updates again"
);
info("Check the label on the button as the global state changes");
is(
win.document.l10n.getAttributes(resetStateButton).id,
"addon-updates-reset-updates-to-automatic",
"The reset button label says it resets to automatic"
);
info("Disable auto updating globally");
AddonManager.autoUpdateDefault = false;
is(
win.document.l10n.getAttributes(resetStateButton).id,
"addon-updates-reset-updates-to-manual",
"The reset button label says it resets to manual"
);
assertAboutAddonsTelemetryEvents(
[
[
"addonsManager",
"action",
"aboutAddons",
null,
{ action: "resetUpdatePolicy", view: "list" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ action: "resetUpdatePolicy", view: "list" },
],
],
{ methods: ["action"] }
);
await closeView(win);
await extension.unload();
});

Просмотреть файл

@ -0,0 +1,100 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function testPanelItemWithAccesskey() {
let win = await loadInitialView("extension");
let doc = win.document;
let panelList = doc.createElement("panel-list");
let items = [
{ textContent: "First Item", accessKey: "F" },
{ textContent: "Second Item", accessKey: "S" },
{ textContent: "Third Item" },
];
let panelItems = items.map(details => {
let item = doc.createElement("panel-item");
for (let [attr, value] of Object.entries(details)) {
item[attr] = value;
}
panelList.appendChild(item);
return item;
});
doc.body.appendChild(panelList);
function assertAccessKeys(items, keys, { checkLabels = false } = {}) {
is(items.length, keys.length, "Got the same number of items and keys");
for (let i = 0; i < items.length; i++) {
is(items[i].accessKey, keys[i], `Item ${i} has the right key`);
if (checkLabels) {
let label = items[i].shadowRoot.querySelector("label");
is(label.accessKey, keys[i] || null, `Label ${i} has the right key`);
}
}
}
info("Accesskeys should be removed when closed");
assertAccessKeys(panelItems, ["", "", ""]);
info("Accesskeys should be set when open");
let panelShown = BrowserTestUtils.waitForEvent(panelList, "shown");
panelList.show();
await panelShown;
assertAccessKeys(panelItems, ["F", "S", ""], { checkLabels: true });
info("Changing accesskeys should happen right away");
panelItems[1].accessKey = "c";
panelItems[2].accessKey = "T";
assertAccessKeys(panelItems, ["F", "c", "T"], { checkLabels: true });
info("Accesskeys are removed again on hide");
let panelHidden = BrowserTestUtils.waitForEvent(panelList, "hidden");
panelList.hide();
await panelHidden;
assertAccessKeys(panelItems, ["", "", ""]);
info("Accesskeys are set again on show");
panelItems[0].removeAttribute("accesskey");
panelShown = BrowserTestUtils.waitForEvent(panelList, "shown");
panelList.show();
await panelShown;
assertAccessKeys(panelItems, ["", "c", "T"], { checkLabels: true });
info("Check that accesskeys can be used without the modifier when open");
let secondClickCount = 0;
let thirdClickCount = 0;
panelItems[1].addEventListener("click", () => secondClickCount++);
panelItems[2].addEventListener("click", () => thirdClickCount++);
// Make sure the focus is in the window.
panelItems[0].focus();
panelHidden = BrowserTestUtils.waitForEvent(panelList, "hidden");
EventUtils.synthesizeKey("c", {}, win);
await panelHidden;
is(secondClickCount, 1, "The accesskey worked unmodified");
is(thirdClickCount, 0, "The other listener wasn't fired");
EventUtils.synthesizeKey("c", {}, win);
EventUtils.synthesizeKey("t", {}, win);
is(secondClickCount, 1, "The key doesn't trigger when closed");
is(thirdClickCount, 0, "The key doesn't trigger when closed");
panelShown = BrowserTestUtils.waitForEvent(panelList, "shown");
panelList.show();
await panelShown;
panelHidden = BrowserTestUtils.waitForEvent(panelList, "hidden");
EventUtils.synthesizeKey("t", {}, win);
await panelHidden;
is(secondClickCount, 1, "The other listener wasn't fired");
is(thirdClickCount, 1, "The accesskey worked unmodified");
await closeView(win);
});

Просмотреть файл

@ -1,105 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests the recent updates pane
var gProvider;
var gManagerWindow;
var gCategoryUtilities;
async function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "updated 6 hours ago",
version: "1.0",
updateDate: new Date(Date.now() - 1000 * 60 * 60 * 6),
releaseNotesURI: Services.io.newURI(TESTROOT + "releaseNotes.xhtml"),
},
{
id: "addon2@tests.mozilla.org",
name: "updated 5 seconds ago",
version: "1.0",
updateDate: new Date(Date.now() - 1000 * 5),
},
{
id: "addon3@tests.mozilla.org",
name: "updated 1 month ago",
version: "1.0",
updateDate: new Date(Date.now() - 1000 * 60 * 60 * 25 * 30),
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
run_next_test();
}
async function end_test() {
await close_manager(gManagerWindow);
finish();
}
add_test(function() {
info("Checking menuitem for Recent Updates opens that pane");
var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
is(
gCategoryUtilities.isVisible(recentCat),
false,
"Recent Updates category should initially be hidden"
);
var utilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
utilsBtn.addEventListener(
"popupshown",
async function() {
Promise.resolve().then(() => {
var menuitem = gManagerWindow.document.getElementById(
"utils-viewUpdates"
);
EventUtils.synthesizeMouse(menuitem, 2, 2, {}, gManagerWindow);
});
await wait_for_view_load(gManagerWindow, null, true);
is(
gCategoryUtilities.isVisible(recentCat),
true,
"Recent Updates category should now be visible"
);
is(
gManagerWindow.document.getElementById("categories").selectedItem.value,
"addons://updates/recent",
"Recent Updates category should now be selected"
);
is(
gManagerWindow.gViewController.currentViewId,
"addons://updates/recent",
"Recent Updates view should be the current view"
);
run_next_test();
},
{ once: true }
);
EventUtils.synthesizeMouse(utilsBtn, 2, 2, {}, gManagerWindow);
});
add_test(async function() {
await close_manager(gManagerWindow);
let aWindow = await open_manager(null);
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
is(
gCategoryUtilities.isVisible(recentCat),
true,
"Recent Updates category should still be visible"
);
run_next_test();
});

Просмотреть файл

@ -0,0 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Bug 570760 - Make ctrl-f and / focus the search box in the add-ons manager
function testKeys(win, searchBox) {
let doc = win.document;
doc.firstElementChild.focus();
isnot(doc.activeElement, searchBox, "Search box is not focused");
EventUtils.synthesizeKey("f", { accelKey: true }, win);
is(
searchBox.ownerDocument.activeElement,
searchBox,
"ctrl-f focuses search box"
);
searchBox.blur();
doc.firstElementChild.focus();
isnot(doc.activeElement, searchBox, "Search box is not focused");
EventUtils.synthesizeKey("/", {}, win);
is(searchBox.ownerDocument.activeElement, searchBox, "/ focuses search box");
searchBox.blur();
}
// Get a stack frame with the expected browser type.
const testHtmlKeys = (...args) => testKeys(...args);
const testXulKeys = (...args) => testKeys(...args);
add_task(async function testSearchBarKeyboardAccess() {
let win = await loadInitialView("extension");
let doc = win.document;
let searchBox = doc.getElementById("search-addons");
testHtmlKeys(win, searchBox);
testXulKeys(win.managerWindow, searchBox);
await closeView(win);
});

Просмотреть файл

@ -9,9 +9,7 @@ async function loadShortcutsView() {
let win = await loadInitialView("extension");
// There should be a manage shortcuts link.
let shortcutsLink = win.managerWindow.document.getElementById(
"manage-shortcuts"
);
let shortcutsLink = win.document.querySelector('[action="manage-shortcuts"]');
// Open the shortcuts view.
let loaded = waitForViewLoad(win);