зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
6ffc0356ab
Коммит
d9f5739109
|
@ -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 don’t 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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче