summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:29 +0000
commit59203c63bb777a3bacec32fb8830fba33540e809 (patch)
tree58298e711c0ff0575818c30485b44a2f21bf28a0 /browser/components/extensions
parentAdding upstream version 126.0.1. (diff)
downloadfirefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz
firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/extensions')
-rw-r--r--browser/components/extensions/ExtensionControlledPopup.sys.mjs2
-rw-r--r--browser/components/extensions/extension.css2
-rw-r--r--browser/components/extensions/parent/ext-browser.js19
-rw-r--r--browser/components/extensions/parent/ext-menus.js14
-rw-r--r--browser/components/extensions/parent/ext-sidebarAction.js169
-rw-r--r--browser/components/extensions/test/browser/browser.toml2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js117
-rw-r--r--browser/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js236
-rw-r--r--browser/components/extensions/test/browser/browser_ext_commands_update.js8
-rw-r--r--browser/components/extensions/test/browser/browser_ext_contextMenus.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_contextMenus_bookmarks.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js18
-rw-r--r--browser/components/extensions/test/browser/browser_ext_getViews.js38
-rw-r--r--browser/components/extensions/test/browser/browser_ext_menus_replace_menu_permissions.js8
-rw-r--r--browser/components/extensions/test/browser/browser_ext_menus_targetElement.js8
-rw-r--r--browser/components/extensions/test/browser/browser_ext_mousewheel_zoom.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_openPanel.js6
-rw-r--r--browser/components/extensions/test/browser/browser_ext_originControls.js6
-rw-r--r--browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js18
-rw-r--r--browser/components/extensions/test/browser/browser_ext_popup_select_in_oopif.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_runtime_getContexts.js597
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js16
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sessions_restoreTab.js6
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction.js60
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction_context.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction_httpAuth.js2
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction_incognito.js7
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction_runtime.js9
-rw-r--r--browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js2
-rw-r--r--browser/components/extensions/test/browser/browser_unified_extensions.js3
-rw-r--r--browser/components/extensions/test/browser/browser_unified_extensions_accessibility.js6
-rw-r--r--browser/components/extensions/test/browser/browser_unified_extensions_context_menu.js108
-rw-r--r--browser/components/extensions/test/browser/head.js52
34 files changed, 1145 insertions, 406 deletions
diff --git a/browser/components/extensions/ExtensionControlledPopup.sys.mjs b/browser/components/extensions/ExtensionControlledPopup.sys.mjs
index b07a8214f3..2d9a9fb584 100644
--- a/browser/components/extensions/ExtensionControlledPopup.sys.mjs
+++ b/browser/components/extensions/ExtensionControlledPopup.sys.mjs
@@ -235,8 +235,6 @@ export class ExtensionControlledPopup {
return;
}
- win.ownerGlobal.ensureCustomElements("moz-support-link");
-
// Find the elements we need.
let doc = win.document;
let panel = ExtensionControlledPopup._getAndMaybeCreatePanel(doc);
diff --git a/browser/components/extensions/extension.css b/browser/components/extensions/extension.css
index f05e2f12a1..431f0148ae 100644
--- a/browser/components/extensions/extension.css
+++ b/browser/components/extensions/extension.css
@@ -21,7 +21,7 @@
body {
background: transparent;
box-sizing: border-box;
- color: #222426;
+ color: light-dark(#222426, CanvasText);
cursor: default;
display: flex;
flex-direction: column;
diff --git a/browser/components/extensions/parent/ext-browser.js b/browser/components/extensions/parent/ext-browser.js
index d2f72d4f46..e7a516dcd3 100644
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -82,7 +82,7 @@ global.openOptionsPage = extension => {
extension.id
)}/preferences`;
- return window.BrowserOpenAddonsMgr(viewId);
+ return window.BrowserAddonUI.openAddonsMgr(viewId);
};
global.makeWidgetId = id => {
@@ -709,6 +709,23 @@ class TabTracker extends TabTrackerBase {
};
}
+ getBrowserDataForContext(context) {
+ if (["tab", "background"].includes(context.viewType)) {
+ return this.getBrowserData(context.xulBrowser);
+ } else if (["popup", "sidebar"].includes(context.viewType)) {
+ // popups and sidebars are nested inside a browser element
+ // (with url "chrome://browser/content/webext-panels.xhtml")
+ // and so we look for the corresponding topChromeWindow to
+ // determine the windowId the panel belongs to.
+ const chromeWindow =
+ context.xulBrowser?.ownerGlobal?.browsingContext?.topChromeWindow;
+ const windowId = chromeWindow ? windowTracker.getId(chromeWindow) : -1;
+ return { tabId: -1, windowId };
+ }
+
+ return { tabId: -1, windowId: -1 };
+ }
+
get activeTab() {
let window = windowTracker.topWindow;
if (window && window.gBrowser) {
diff --git a/browser/components/extensions/parent/ext-menus.js b/browser/components/extensions/parent/ext-menus.js
index a5b27bff7d..e4ad9f4747 100644
--- a/browser/components/extensions/parent/ext-menus.js
+++ b/browser/components/extensions/parent/ext-menus.js
@@ -1100,11 +1100,11 @@ const menuTracker = {
);
sidebarHeader.addEventListener("SidebarShown", menuTracker.onSidebarShown);
- await window.SidebarUI.promiseInitialized;
+ await window.SidebarController.promiseInitialized;
if (
!window.closed &&
- window.SidebarUI.currentID === "viewBookmarksSidebar"
+ window.SidebarController.currentID === "viewBookmarksSidebar"
) {
menuTracker.onSidebarShown({ currentTarget: sidebarHeader });
}
@@ -1121,8 +1121,8 @@ const menuTracker = {
);
sidebarHeader.removeEventListener("SidebarShown", this.onSidebarShown);
- if (window.SidebarUI.currentID === "viewBookmarksSidebar") {
- let sidebarBrowser = window.SidebarUI.browser;
+ if (window.SidebarController.currentID === "viewBookmarksSidebar") {
+ let sidebarBrowser = window.SidebarController.browser;
sidebarBrowser.removeEventListener("load", this.onSidebarShown);
const menu =
sidebarBrowser.contentDocument.getElementById("placesContext");
@@ -1134,10 +1134,10 @@ const menuTracker = {
// The event target is an element in a browser window, so |window| will be
// the browser window that contains the sidebar.
const window = event.currentTarget.ownerGlobal;
- if (window.SidebarUI.currentID === "viewBookmarksSidebar") {
- let sidebarBrowser = window.SidebarUI.browser;
+ if (window.SidebarController.currentID === "viewBookmarksSidebar") {
+ let sidebarBrowser = window.SidebarController.browser;
if (sidebarBrowser.contentDocument.readyState !== "complete") {
- // SidebarUI.currentID may be updated before the bookmark sidebar's
+ // SidebarController.currentID may be updated before the bookmark sidebar's
// document has finished loading. This sometimes happens when the
// sidebar is automatically shown when a new window is opened.
sidebarBrowser.addEventListener("load", menuTracker.onSidebarShown, {
diff --git a/browser/components/extensions/parent/ext-sidebarAction.js b/browser/components/extensions/parent/ext-sidebarAction.js
index 197456abd9..b2c009014e 100644
--- a/browser/components/extensions/parent/ext-sidebarAction.js
+++ b/browser/components/extensions/parent/ext-sidebarAction.js
@@ -17,8 +17,6 @@ var { IconDetails } = ExtensionParent;
// WeakMap[Extension -> SidebarAction]
let sidebarActionMap = new WeakMap();
-const sidebarURL = "chrome://browser/content/webext-panels.xhtml";
-
/**
* Responsible for the sidebar_action section of the manifest as well
* as the associated sidebar browser.
@@ -40,7 +38,6 @@ this.sidebarAction = class extends ExtensionAPI {
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-sidebar-action`;
this.menuId = `menubar_menu_${this.id}`;
- this.switcherMenuId = `sidebarswitcher_menu_${this.id}`;
this.browserStyle = options.browser_style;
@@ -66,23 +63,6 @@ this.sidebarAction = class extends ExtensionAPI {
};
windowTracker.addOpenListener(this.windowOpenListener);
- this.updateHeader = event => {
- let window = event.target.ownerGlobal;
- let details = this.tabContext.get(window.gBrowser.selectedTab);
- let header = window.document.getElementById("sidebar-switcher-target");
- if (window.SidebarUI.currentID === this.id) {
- this.setMenuIcon(header, details);
- }
- };
-
- this.windowCloseListener = window => {
- let header = window.document.getElementById("sidebar-switcher-target");
- if (header) {
- header.removeEventListener("SidebarShown", this.updateHeader);
- }
- };
- windowTracker.addCloseListener(this.windowCloseListener);
-
sidebarActionMap.set(extension, this);
}
@@ -91,7 +71,7 @@ this.sidebarAction = class extends ExtensionAPI {
}
onShutdown(isAppShutdown) {
- sidebarActionMap.delete(this.this);
+ sidebarActionMap.delete(this.extension);
this.tabContext.shutdown();
@@ -102,26 +82,18 @@ this.sidebarAction = class extends ExtensionAPI {
}
for (let window of windowTracker.browserWindows()) {
- let { document, SidebarUI } = window;
- if (SidebarUI.currentID === this.id) {
- SidebarUI.hide();
- }
- document.getElementById(this.menuId)?.remove();
- document.getElementById(this.switcherMenuId)?.remove();
- let header = document.getElementById("sidebar-switcher-target");
- header.removeEventListener("SidebarShown", this.updateHeader);
- SidebarUI.sidebars.delete(this.id);
+ let { SidebarController } = window;
+ SidebarController.removeExtension(this.id);
}
windowTracker.removeOpenListener(this.windowOpenListener);
- windowTracker.removeCloseListener(this.windowCloseListener);
}
static onUninstall(id) {
const sidebarId = `${makeWidgetId(id)}-sidebar-action`;
for (let window of windowTracker.browserWindows()) {
- let { SidebarUI } = window;
- if (SidebarUI.lastOpenedId === sidebarId) {
- SidebarUI.lastOpenedId = null;
+ let { SidebarController } = window;
+ if (SidebarController.lastOpenedId === sidebarId) {
+ SidebarController.lastOpenedId = null;
}
}
}
@@ -135,12 +107,12 @@ this.sidebarAction = class extends ExtensionAPI {
let install = this.extension.startupReason === "ADDON_INSTALL";
for (let window of windowTracker.browserWindows()) {
this.updateWindow(window);
- let { SidebarUI } = window;
+ let { SidebarController } = window;
if (
(install && this.extension.manifest.sidebar_action.open_at_install) ||
- SidebarUI.lastOpenedId == this.id
+ SidebarController.lastOpenedId == this.id
) {
- SidebarUI.show(this.id);
+ SidebarController.show(this.id);
}
}
}
@@ -149,60 +121,29 @@ this.sidebarAction = class extends ExtensionAPI {
if (!this.extension.canAccessWindow(window)) {
return;
}
- let { document, SidebarUI } = window;
- let keyId = `ext-key-id-${this.id}`;
-
- SidebarUI.sidebars.set(this.id, {
- title: details.title,
- url: sidebarURL,
+ this.panel = details.panel;
+ let { SidebarController } = window;
+ SidebarController.registerExtension(this.id, {
+ icon: this.getMenuIcon(details),
menuId: this.menuId,
- switcherMenuId: this.switcherMenuId,
- // The following properties are specific to extensions
+ title: details.title,
extensionId: this.extension.id,
- panel: details.panel,
- browserStyle: this.browserStyle,
+ onload: () =>
+ SidebarController.browser.contentWindow.loadPanel(
+ this.extension.id,
+ this.panel,
+ this.browserStyle
+ ),
});
-
- let header = document.getElementById("sidebar-switcher-target");
- header.addEventListener("SidebarShown", this.updateHeader);
-
- // Insert a menuitem for View->Show Sidebars.
- let menuitem = document.createXULElement("menuitem");
- menuitem.setAttribute("id", this.menuId);
- menuitem.setAttribute("type", "checkbox");
- menuitem.setAttribute("label", details.title);
- menuitem.setAttribute("oncommand", `SidebarUI.toggle("${this.id}");`);
- menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
- menuitem.setAttribute("key", keyId);
- this.setMenuIcon(menuitem, details);
-
- // Insert a toolbarbutton for the sidebar dropdown selector.
- let switcherMenuitem = menuitem.cloneNode();
- switcherMenuitem.setAttribute("id", this.switcherMenuId);
- switcherMenuitem.removeAttribute("type");
-
- document.getElementById("viewSidebarMenu").appendChild(menuitem);
- let separator = document.getElementById("sidebar-extensions-separator");
- separator.parentNode.insertBefore(switcherMenuitem, separator);
-
- return menuitem;
}
- setMenuIcon(menuitem, details) {
+ getMenuIcon(details) {
let getIcon = size =>
IconDetails.escapeUrl(
IconDetails.getPreferredIcon(details.icon, this.extension, size).icon
);
- menuitem.setAttribute(
- "style",
- `
- --webextension-menuitem-image: image-set(
- url("${getIcon(16)}"),
- url("${getIcon(32)}") 2x
- );
- `
- );
+ return `image-set(url("${getIcon(16)}"), url("${getIcon(32)}") 2x)`;
}
/**
@@ -214,34 +155,26 @@ this.sidebarAction = class extends ExtensionAPI {
* Tab specific sidebar configuration.
*/
updateButton(window, tabData) {
- let { document, SidebarUI } = window;
+ let { document, SidebarController } = window;
let title = tabData.title || this.extension.name;
- let menu = document.getElementById(this.menuId);
- if (!menu) {
- menu = this.createMenuItem(window, tabData);
+ if (!document.getElementById(this.menuId)) {
+ // Menu items are added when new windows are opened, or from onReady (when
+ // an extension has fully started). The menu item may be missing at this
+ // point if the extension updates the sidebar during its startup.
+ this.createMenuItem(window, tabData);
}
-
- let urlChanged = tabData.panel !== SidebarUI.sidebars.get(this.id).panel;
+ let urlChanged = tabData.panel !== this.panel;
if (urlChanged) {
- SidebarUI.sidebars.get(this.id).panel = tabData.panel;
- }
-
- menu.setAttribute("label", title);
- this.setMenuIcon(menu, tabData);
-
- let button = document.getElementById(this.switcherMenuId);
- button.setAttribute("label", title);
- this.setMenuIcon(button, tabData);
-
- // Update the sidebar if this extension is the current sidebar.
- if (SidebarUI.currentID === this.id) {
- SidebarUI.title = title;
- let header = document.getElementById("sidebar-switcher-target");
- this.setMenuIcon(header, tabData);
- if (SidebarUI.isOpen && urlChanged) {
- SidebarUI.show(this.id);
- }
+ this.panel = tabData.panel;
}
+ SidebarController.setExtensionAttributes(
+ this.id,
+ {
+ icon: this.getMenuIcon(tabData),
+ label: title,
+ },
+ urlChanged
+ );
}
/**
@@ -382,9 +315,9 @@ this.sidebarAction = class extends ExtensionAPI {
* @param {ChromeWindow} window
*/
triggerAction(window) {
- let { SidebarUI } = window;
- if (SidebarUI && this.extension.canAccessWindow(window)) {
- SidebarUI.toggle(this.id);
+ let { SidebarController } = window;
+ if (SidebarController && this.extension.canAccessWindow(window)) {
+ SidebarController.toggle(this.id);
}
}
@@ -394,9 +327,9 @@ this.sidebarAction = class extends ExtensionAPI {
* @param {ChromeWindow} window
*/
open(window) {
- let { SidebarUI } = window;
- if (SidebarUI && this.extension.canAccessWindow(window)) {
- SidebarUI.show(this.id);
+ let { SidebarController } = window;
+ if (SidebarController && this.extension.canAccessWindow(window)) {
+ SidebarController.show(this.id);
}
}
@@ -407,7 +340,7 @@ this.sidebarAction = class extends ExtensionAPI {
*/
close(window) {
if (this.isOpen(window)) {
- window.SidebarUI.hide();
+ window.SidebarController.hide();
}
}
@@ -417,15 +350,15 @@ this.sidebarAction = class extends ExtensionAPI {
* @param {ChromeWindow} window
*/
toggle(window) {
- let { SidebarUI } = window;
- if (!SidebarUI || !this.extension.canAccessWindow(window)) {
+ let { SidebarController } = window;
+ if (!SidebarController || !this.extension.canAccessWindow(window)) {
return;
}
if (!this.isOpen(window)) {
- SidebarUI.show(this.id);
+ SidebarController.show(this.id);
} else {
- SidebarUI.hide();
+ SidebarController.hide();
}
}
@@ -436,8 +369,8 @@ this.sidebarAction = class extends ExtensionAPI {
* @returns {boolean}
*/
isOpen(window) {
- let { SidebarUI } = window;
- return SidebarUI.isOpen && this.id == SidebarUI.currentID;
+ let { SidebarController } = window;
+ return SidebarController.isOpen && this.id == SidebarController.currentID;
}
getAPI(context) {
diff --git a/browser/components/extensions/test/browser/browser.toml b/browser/components/extensions/test/browser/browser.toml
index 570a51eeb4..b5b4322ffe 100644
--- a/browser/components/extensions/test/browser/browser.toml
+++ b/browser/components/extensions/test/browser/browser.toml
@@ -392,6 +392,8 @@ run-if = ["crashreporter"]
["browser_ext_request_permissions.js"]
+["browser_ext_runtime_getContexts.js"]
+
["browser_ext_runtime_onPerformanceWarning.js"]
["browser_ext_runtime_openOptionsPage.js"]
diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js b/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js
index 26d1536de1..32de8b95f2 100644
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js
@@ -2,10 +2,6 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-ChromeUtils.defineESModuleGetters(this, {
- AbuseReporter: "resource://gre/modules/AbuseReporter.sys.mjs",
-});
-
XPCOMUtils.defineLazyPreferenceGetter(
this,
"ABUSE_REPORT_ENABLED",
@@ -546,35 +542,6 @@ async function browseraction_contextmenu_report_extension_helper() {
useAddonManager: "temporary",
});
- async function testReportDialog(viaUnifiedContextMenu) {
- const reportDialogWindow = await BrowserTestUtils.waitForCondition(
- () => AbuseReporter.getOpenDialog(),
- "Wait for the abuse report dialog to have been opened"
- );
-
- const reportDialogParams = reportDialogWindow.arguments[0].wrappedJSObject;
- is(
- reportDialogParams.report.addon.id,
- id,
- "Abuse report dialog has the expected addon id"
- );
- is(
- reportDialogParams.report.reportEntryPoint,
- viaUnifiedContextMenu ? "unified_context_menu" : "toolbar_context_menu",
- "Abuse report dialog has the expected reportEntryPoint"
- );
-
- info("Wait the report dialog to complete rendering");
- await reportDialogParams.promiseReportPanel;
- info("Close the report dialog");
- reportDialogWindow.close();
- is(
- await reportDialogParams.promiseReport,
- undefined,
- "Report resolved as user cancelled when the window is closed"
- );
- }
-
async function testContextMenu(menuId, customizing) {
info(`Open browserAction context menu in ${menuId}`);
let menu = await openContextMenu(menuId, buttonId);
@@ -591,54 +558,27 @@ async function browseraction_contextmenu_report_extension_helper() {
let aboutAddonsBrowser;
- if (AbuseReporter.amoFormEnabled) {
- const reportURL = Services.urlFormatter
- .formatURLPref("extensions.abuseReport.amoFormURL")
- .replace("%addonID%", id);
-
- const promiseReportTab = BrowserTestUtils.waitForNewTab(
- gBrowser,
- reportURL,
- /* waitForLoad */ false,
- // Expect it to be the next tab opened
- /* waitForAnyTab */ false
- );
- await closeChromeContextMenu(menuId, reportExtension);
- const reportTab = await promiseReportTab;
- // Remove the report tab and expect the selected tab
- // to become the about:addons tab.
- BrowserTestUtils.removeTab(reportTab);
- is(
- gBrowser.selectedBrowser.currentURI.spec,
- "about:blank",
- "Expect about:addons tab to not have been opened (amoFormEnabled=true)"
- );
- } else {
- // When running in customizing mode "about:addons" will load in a new tab,
- // otherwise it will replace the existing blank tab.
- const onceAboutAddonsTab = customizing
- ? BrowserTestUtils.waitForNewTab(gBrowser, "about:addons")
- : BrowserTestUtils.waitForCondition(() => {
- return gBrowser.currentURI.spec === "about:addons";
- }, "Wait an about:addons tab to be opened");
- await closeChromeContextMenu(menuId, reportExtension);
- await onceAboutAddonsTab;
- const browser = gBrowser.selectedBrowser;
- is(
- browser.currentURI.spec,
- "about:addons",
- "Got about:addons tab selected (amoFormEnabled=false)"
- );
- // Do not wait for the about:addons tab to be loaded if its
- // document is already readyState==complete.
- // This prevents intermittent timeout failures while running
- // this test in optimized builds.
- if (browser.contentDocument?.readyState != "complete") {
- await BrowserTestUtils.browserLoaded(browser);
- }
- await testReportDialog(usingUnifiedContextMenu);
- aboutAddonsBrowser = browser;
- }
+ const reportURL = Services.urlFormatter
+ .formatURLPref("extensions.abuseReport.amoFormURL")
+ .replace("%addonID%", id);
+
+ const promiseReportTab = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ reportURL,
+ /* waitForLoad */ false,
+ // Expect it to be the next tab opened
+ /* waitForAnyTab */ false
+ );
+ await closeChromeContextMenu(menuId, reportExtension);
+ const reportTab = await promiseReportTab;
+ // Remove the report tab and expect the selected tab
+ // to become the about:addons tab.
+ BrowserTestUtils.removeTab(reportTab);
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ "about:blank",
+ "Expect about:addons tab to not have been opened"
+ );
// Close the new about:addons tab when running in customize mode,
// or cancel the abuse report if the about:addons page has been
@@ -729,22 +669,7 @@ add_task(async function test_unified_extensions_ui() {
await browseraction_contextmenu_manage_extension_helper();
await browseraction_contextmenu_remove_extension_helper();
await test_no_toolbar_pinning_on_builtin_helper();
-});
-
-add_task(async function test_report_amoFormEnabled() {
- await SpecialPowers.pushPrefEnv({
- set: [["extensions.abuseReport.amoFormEnabled", true]],
- });
- await browseraction_contextmenu_report_extension_helper();
- await SpecialPowers.popPrefEnv();
-});
-
-add_task(async function test_report_amoFormDisabled() {
- await SpecialPowers.pushPrefEnv({
- set: [["extensions.abuseReport.amoFormEnabled", false]],
- });
await browseraction_contextmenu_report_extension_helper();
- await SpecialPowers.popPrefEnv();
});
/**
diff --git a/browser/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js b/browser/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js
index 526dfbbeeb..9ca2a9c666 100644
--- a/browser/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js
@@ -2,10 +2,9 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-add_task(async function testTabSwitchActionContext() {
- await SpecialPowers.pushPrefEnv({
- set: [["extensions.manifestV3.enabled", true]],
- });
+ChromeUtils.defineESModuleGetters(this, {
+ ExtensionSettingsStore:
+ "resource://gre/modules/ExtensionSettingsStore.sys.mjs",
});
async function testExecuteBrowserActionWithOptions(options = {}) {
@@ -192,3 +191,232 @@ add_task(
});
}
);
+
+add_task(async function test_fallback_to_execute_browser_action_in_mv3() {
+ // Make sure the mouse isn't hovering over the browserAction widget.
+ EventUtils.synthesizeMouseAtCenter(
+ gURLBar.textbox,
+ { type: "mouseover" },
+ window
+ );
+
+ const EXTENSION_ID = "@test-action";
+ const extMV2 = ExtensionTestUtils.loadExtension({
+ manifest: {
+ manifest_version: 2,
+ browser_action: {},
+ browser_specific_settings: { gecko: { id: EXTENSION_ID } },
+ commands: {
+ _execute_browser_action: {
+ suggested_key: {
+ default: "Alt+1",
+ },
+ },
+ },
+ },
+ async background() {
+ await browser.commands.update({
+ name: "_execute_browser_action",
+ shortcut: "Alt+Shift+2",
+ });
+ browser.test.sendMessage("command-update");
+ },
+ useAddonManager: "temporary",
+ });
+ await extMV2.startup();
+ await extMV2.awaitMessage("command-update");
+
+ let storedCommands = ExtensionSettingsStore.getAllForExtension(
+ EXTENSION_ID,
+ "commands"
+ );
+ Assert.deepEqual(
+ storedCommands,
+ ["_execute_browser_action"],
+ "expected a stored command"
+ );
+
+ const extDataMV3 = {
+ manifest: {
+ manifest_version: 3,
+ action: {},
+ browser_specific_settings: { gecko: { id: EXTENSION_ID } },
+ commands: {
+ _execute_action: {
+ suggested_key: {
+ default: "Alt+3",
+ },
+ },
+ },
+ },
+ background() {
+ browser.action.onClicked.addListener(() => {
+ browser.test.notifyPass("execute-action-on-clicked-fired");
+ });
+
+ browser.test.onMessage.addListener(async (msg, data) => {
+ switch (msg) {
+ case "verify": {
+ const commands = await browser.commands.getAll();
+ browser.test.assertDeepEq(
+ data,
+ commands,
+ "expected correct commands"
+ );
+ browser.test.sendMessage(`${msg}-done`);
+ break;
+ }
+
+ case "update":
+ await browser.commands.update(data);
+ browser.test.sendMessage(`${msg}-done`);
+ break;
+
+ case "reset":
+ await browser.commands.reset("_execute_action");
+ browser.test.sendMessage(`${msg}-done`);
+ break;
+
+ default:
+ browser.test.fail(`unexpected message: ${msg}`);
+ }
+ });
+
+ browser.test.sendMessage("ready");
+ },
+ useAddonManager: "temporary",
+ };
+ const extMV3 = ExtensionTestUtils.loadExtension(extDataMV3);
+ await extMV3.startup();
+ await extMV3.awaitMessage("ready");
+
+ // We should have the shortcut value from the previous extension.
+ extMV3.sendMessage("verify", [
+ {
+ name: "_execute_action",
+ description: null,
+ shortcut: "Alt+Shift+2",
+ },
+ ]);
+ await extMV3.awaitMessage("verify-done");
+
+ // Execute the shortcut from the MV2 extension.
+ await SimpleTest.promiseFocus(window);
+ EventUtils.synthesizeKey("2", {
+ altKey: true,
+ shiftKey: true,
+ });
+ await extMV3.awaitFinish("execute-action-on-clicked-fired");
+
+ // Update the shortcut.
+ extMV3.sendMessage("update", {
+ name: "_execute_action",
+ shortcut: "Alt+Shift+4",
+ });
+ await extMV3.awaitMessage("update-done");
+
+ extMV3.sendMessage("verify", [
+ {
+ name: "_execute_action",
+ description: null,
+ shortcut: "Alt+Shift+4",
+ },
+ ]);
+ await extMV3.awaitMessage("verify-done");
+
+ // At this point, we should have the old and new commands in storage.
+ storedCommands = ExtensionSettingsStore.getAllForExtension(
+ EXTENSION_ID,
+ "commands"
+ );
+ Assert.deepEqual(
+ storedCommands,
+ ["_execute_browser_action", "_execute_action"],
+ "expected two stored commands"
+ );
+
+ // Disarm any pending writes before we modify the JSONFile directly.
+ await ExtensionSettingsStore._reloadFile(
+ true // saveChanges
+ );
+
+ let jsonFileName = "extension-settings.json";
+ let storePath = PathUtils.join(PathUtils.profileDir, jsonFileName);
+
+ let settingsStoreData = await IOUtils.readJSON(storePath);
+ Assert.deepEqual(
+ Array.from(Object.keys(settingsStoreData.commands)),
+ ["_execute_browser_action", "_execute_action"],
+ "expected command hortcuts data to be found in extension-settings.json"
+ );
+
+ // Reverse the order of _execute_action and _execute_browser_action stored
+ // in the settings store.
+ settingsStoreData.commands = {
+ _execute_action: settingsStoreData.commands._execute_action,
+ _execute_browser_action: settingsStoreData.commands._execute_browser_action,
+ };
+
+ Assert.deepEqual(
+ Array.from(Object.keys(settingsStoreData.commands)),
+ ["_execute_action", "_execute_browser_action"],
+ "expected command shortcuts order to be reversed in extension-settings.json data"
+ );
+
+ // Write the extension-settings.json data and reload it.
+ await IOUtils.writeJSON(storePath, settingsStoreData);
+ await ExtensionSettingsStore._reloadFile(
+ false // saveChanges
+ );
+
+ // Restart the extension to verify that the loaded command is the right one.
+ const updatedExtMV3 = ExtensionTestUtils.loadExtension(extDataMV3);
+ await updatedExtMV3.startup();
+ await updatedExtMV3.awaitMessage("ready");
+
+ // We should *still* have two stored commands.
+ storedCommands = ExtensionSettingsStore.getAllForExtension(
+ EXTENSION_ID,
+ "commands"
+ );
+ Assert.deepEqual(
+ storedCommands,
+ ["_execute_action", "_execute_browser_action"],
+ "expected two stored commands"
+ );
+
+ updatedExtMV3.sendMessage("verify", [
+ {
+ name: "_execute_action",
+ description: null,
+ shortcut: "Alt+Shift+4",
+ },
+ ]);
+ await updatedExtMV3.awaitMessage("verify-done");
+
+ updatedExtMV3.sendMessage("reset");
+ await updatedExtMV3.awaitMessage("reset-done");
+
+ // Resetting the shortcut should take the default value from the latest
+ // extension version.
+ updatedExtMV3.sendMessage("verify", [
+ {
+ name: "_execute_action",
+ description: null,
+ shortcut: "Alt+3",
+ },
+ ]);
+ await updatedExtMV3.awaitMessage("verify-done");
+
+ // At this point, we should no longer have any stored commands, since we are
+ // using the default.
+ storedCommands = ExtensionSettingsStore.getAllForExtension(
+ EXTENSION_ID,
+ "commands"
+ );
+ Assert.deepEqual(storedCommands, [], "expected no stored command");
+
+ await extMV2.unload();
+ await extMV3.unload();
+ await updatedExtMV3.unload();
+});
diff --git a/browser/components/extensions/test/browser/browser_ext_commands_update.js b/browser/components/extensions/test/browser/browser_ext_commands_update.js
index 5e1f05e346..9598328d26 100644
--- a/browser/components/extensions/test/browser/browser_ext_commands_update.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_update.js
@@ -401,11 +401,11 @@ add_task(async function updateSidebarCommand() {
await extension.awaitMessage("sidebar");
// Show and hide the switcher panel to generate the initial shortcuts.
- let switcherShown = promisePopupShown(SidebarUI._switcherPanel);
- SidebarUI.showSwitcherPanel();
+ let switcherShown = promisePopupShown(SidebarController._switcherPanel);
+ SidebarController.showSwitcherPanel();
await switcherShown;
- let switcherHidden = promisePopupHidden(SidebarUI._switcherPanel);
- SidebarUI.hideSwitcherPanel();
+ let switcherHidden = promisePopupHidden(SidebarController._switcherPanel);
+ SidebarController.hideSwitcherPanel();
await switcherHidden;
let menuitemId = `sidebarswitcher_menu_${makeWidgetId(
diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus.js b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
index 77e4bf0827..0ed5d6ce9e 100644
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -724,7 +724,7 @@ add_task(async function test_bookmark_sidebar_contextmenu() {
await extension.startup();
let bookmarkGuid = await extension.awaitMessage("bookmark-created");
- let sidebar = window.SidebarUI.browser;
+ let sidebar = window.SidebarController.browser;
let menu = sidebar.contentDocument.getElementById("placesContext");
tree.selectItems([bookmarkGuid]);
let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_bookmarks.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_bookmarks.js
index 24411731f7..3c0a1269a9 100644
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_bookmarks.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_bookmarks.js
@@ -54,7 +54,7 @@ add_task(async function test_bookmark_sidebar_contextmenu() {
expectedVirtualID,
] of expected_bookmarkID_2_virtualID) {
info(`Testing context menu for Bookmark ID "${expectedBookmarkID}"`);
- let sidebar = window.SidebarUI.browser;
+ let sidebar = window.SidebarController.browser;
let menu = sidebar.contentDocument.getElementById("placesContext");
tree.selectItems([expectedBookmarkID]);
diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
index 0f353600d7..ae1ec31827 100644
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
@@ -221,7 +221,7 @@ add_task(async function privileged_are_allowed_to_use_restrictedSchemes() {
manifest: {
permissions: ["tabs", "contextMenus", "mozillaAddons"],
},
- async background() {
+ background() {
browser.contextMenus.create({
id: "privileged-extension",
title: "Privileged Extension",
@@ -229,6 +229,7 @@ add_task(async function privileged_are_allowed_to_use_restrictedSchemes() {
documentUrlPatterns: ["about:reader*"],
});
+ let articleReady = Promise.withResolvers();
browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (
changeInfo.status === "complete" &&
@@ -236,6 +237,9 @@ add_task(async function privileged_are_allowed_to_use_restrictedSchemes() {
) {
browser.test.sendMessage("readerModeEntered");
}
+ if (tab.isArticle && tab.url.includes("/readerModeArticle.html")) {
+ articleReady.resolve();
+ }
});
browser.test.onMessage.addListener(async msg => {
@@ -244,8 +248,20 @@ add_task(async function privileged_are_allowed_to_use_restrictedSchemes() {
return;
}
+ browser.test.log("Waiting for tab.isArticle to be true");
+ await articleReady.promise;
+ browser.test.log("Toggling reader mode");
browser.tabs.toggleReaderMode();
});
+
+ browser.tabs.query(
+ { url: "*://example.com/*/readerModeArticle.html" },
+ tabs => {
+ if (tabs[0].isArticle) {
+ articleReady.resolve();
+ }
+ }
+ );
},
});
diff --git a/browser/components/extensions/test/browser/browser_ext_getViews.js b/browser/components/extensions/test/browser/browser_ext_getViews.js
index 1af190e753..20db9c86c8 100644
--- a/browser/components/extensions/test/browser/browser_ext_getViews.js
+++ b/browser/components/extensions/test/browser/browser_ext_getViews.js
@@ -78,44 +78,6 @@ function genericChecker() {
browser.test.sendMessage(kind + "-ready");
}
-async function promiseBrowserContentUnloaded(browser) {
- // Wait until the content has unloaded before resuming the test, to avoid
- // calling extension.getViews too early (and having intermittent failures).
- const MSG_WINDOW_DESTROYED = "Test:BrowserContentDestroyed";
- let unloadPromise = new Promise(resolve => {
- Services.ppmm.addMessageListener(MSG_WINDOW_DESTROYED, function listener() {
- Services.ppmm.removeMessageListener(MSG_WINDOW_DESTROYED, listener);
- resolve();
- });
- });
-
- await ContentTask.spawn(
- browser,
- MSG_WINDOW_DESTROYED,
- MSG_WINDOW_DESTROYED => {
- let innerWindowId = this.content.windowGlobalChild.innerWindowId;
- let observer = subject => {
- if (
- innerWindowId === subject.QueryInterface(Ci.nsISupportsPRUint64).data
- ) {
- Services.obs.removeObserver(observer, "inner-window-destroyed");
-
- // Use process message manager to ensure that the message is delivered
- // even after the <browser>'s message manager is disconnected.
- Services.cpmm.sendAsyncMessage(MSG_WINDOW_DESTROYED);
- }
- };
- // Observe inner-window-destroyed, like ExtensionPageChild, to ensure that
- // the ExtensionPageContextChild instance has been unloaded when we resolve
- // the unloadPromise.
- Services.obs.addObserver(observer, "inner-window-destroyed");
- }
- );
-
- // Return an object so that callers can use "await".
- return { unloadPromise };
-}
-
add_task(async function () {
let win1 = await BrowserTestUtils.openNewBrowserWindow();
let win2 = await BrowserTestUtils.openNewBrowserWindow();
diff --git a/browser/components/extensions/test/browser/browser_ext_menus_replace_menu_permissions.js b/browser/components/extensions/test/browser/browser_ext_menus_replace_menu_permissions.js
index c9627c5ae9..7960dd74dc 100644
--- a/browser/components/extensions/test/browser/browser_ext_menus_replace_menu_permissions.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_replace_menu_permissions.js
@@ -189,7 +189,9 @@ add_task(async function overrideContext_permissions() {
// permissions.request requires user input, export helper.
await SpecialPowers.spawn(
- SidebarUI.browser.contentDocument.getElementById("webext-panels-browser"),
+ SidebarController.browser.contentDocument.getElementById(
+ "webext-panels-browser"
+ ),
[],
() => {
const { ExtensionCommon } = ChromeUtils.importESModule(
@@ -212,7 +214,9 @@ add_task(async function overrideContext_permissions() {
await BrowserTestUtils.synthesizeMouseAtCenter(
"a",
{ type: "contextmenu" },
- SidebarUI.browser.contentDocument.getElementById("webext-panels-browser")
+ SidebarController.browser.contentDocument.getElementById(
+ "webext-panels-browser"
+ )
);
} while (await extension.awaitMessage("continue_test"));
diff --git a/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js b/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js
index 4fc9ebba82..9035b6eecb 100644
--- a/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_targetElement.js
@@ -308,6 +308,14 @@ add_task(async function independentMenusInDifferentTabs() {
gBrowser.selectedTab = tab2;
let targetElementId2 = await extension.openAndCloseMenu("#editabletext");
+ if (targetElementId === targetElementId2) {
+ // targetElementId is only guaranteed to be unique within a tab, so odds are
+ // that by unlucky coincidence, that the discovered ID accidentally overlaps
+ // with the actual ID.
+ info(`Got same targetElementId ${targetElementId}, retrying for a new one`);
+ targetElementId2 = await extension.openAndCloseMenu("#editabletext");
+ }
+ Assert.notEqual(targetElementId, targetElementId2, "targetElementId differ");
await extension.checkIsValid(
targetElementId2,
diff --git a/browser/components/extensions/test/browser/browser_ext_mousewheel_zoom.js b/browser/components/extensions/test/browser/browser_ext_mousewheel_zoom.js
index 9c5f447a76..1c9224ca00 100644
--- a/browser/components/extensions/test/browser/browser_ext_mousewheel_zoom.js
+++ b/browser/components/extensions/test/browser/browser_ext_mousewheel_zoom.js
@@ -120,7 +120,7 @@ async function test_mousewheel_zoom(test) {
const sidebar = document.getElementById("sidebar-box");
ok(!sidebar.hidden, "Sidebar box is visible");
- browser = SidebarUI.browser.contentWindow.gBrowser.selectedBrowser;
+ browser = SidebarController.browser.contentWindow.gBrowser.selectedBrowser;
} else if (test == TESTS.BROWSER_ACTION) {
browser = await openBrowserActionPanel(extension, undefined, true);
} else if (test == TESTS.PAGE_ACTION) {
diff --git a/browser/components/extensions/test/browser/browser_ext_openPanel.js b/browser/components/extensions/test/browser/browser_ext_openPanel.js
index ed96bf2520..c56c0a31b6 100644
--- a/browser/components/extensions/test/browser/browser_ext_openPanel.js
+++ b/browser/components/extensions/test/browser/browser_ext_openPanel.js
@@ -136,16 +136,16 @@ add_task(async function test_openPopup_requires_user_interaction() {
{},
gBrowser.selectedBrowser
);
- await TestUtils.waitForCondition(() => !SidebarUI.isOpen);
+ await TestUtils.waitForCondition(() => !SidebarController.isOpen);
await click("#toggleSidebarAction");
- await TestUtils.waitForCondition(() => SidebarUI.isOpen);
+ await TestUtils.waitForCondition(() => SidebarController.isOpen);
await BrowserTestUtils.synthesizeMouseAtCenter(
"#toggleSidebarAction",
{},
gBrowser.selectedBrowser
);
- await TestUtils.waitForCondition(() => !SidebarUI.isOpen);
+ await TestUtils.waitForCondition(() => !SidebarController.isOpen);
BrowserTestUtils.removeTab(gBrowser.selectedTab);
await extension.unload();
diff --git a/browser/components/extensions/test/browser/browser_ext_originControls.js b/browser/components/extensions/test/browser/browser_ext_originControls.js
index 176eef08bc..6bcae3ef8f 100644
--- a/browser/components/extensions/test/browser/browser_ext_originControls.js
+++ b/browser/components/extensions/test/browser/browser_ext_originControls.js
@@ -20,6 +20,12 @@ const l10n = new Localization(
true
);
+add_setup(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.originControls.grantByDefault", false]],
+ });
+});
+
async function makeExtension({
useAddonManager = "temporary",
manifest_version = 3,
diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js b/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js
index fd589acdbd..71f13a4691 100644
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js
@@ -268,10 +268,14 @@ add_task(async function test_pageAction_restrictScheme_false() {
},
},
background: function () {
- browser.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
+ let articleReady = Promise.withResolvers();
+ browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (changeInfo.url && changeInfo.url.startsWith("about:reader")) {
browser.test.sendMessage("readerModeEntered");
}
+ if (tab.isArticle && tab.url.includes("/readerModeArticle.html")) {
+ articleReady.resolve();
+ }
});
browser.test.onMessage.addListener(async msg => {
@@ -280,8 +284,20 @@ add_task(async function test_pageAction_restrictScheme_false() {
return;
}
+ browser.test.log("Waiting for tab.isArticle to be true");
+ await articleReady.promise;
+ browser.test.log("Toggling reader mode");
browser.tabs.toggleReaderMode();
});
+
+ browser.tabs.query(
+ { url: "*://example.com/*/readerModeArticle.html" },
+ tabs => {
+ if (tabs[0].isArticle) {
+ articleReady.resolve();
+ }
+ }
+ );
},
});
diff --git a/browser/components/extensions/test/browser/browser_ext_popup_select_in_oopif.js b/browser/components/extensions/test/browser/browser_ext_popup_select_in_oopif.js
index fa2c414047..bdc5145dc5 100644
--- a/browser/components/extensions/test/browser/browser_ext_popup_select_in_oopif.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_select_in_oopif.js
@@ -70,6 +70,8 @@ add_task(async function testPopupSelectPopup() {
});
const selectRect = await SpecialPowers.spawn(iframe, [], async () => {
+ await SpecialPowers.contentTransformsReceived(content.window);
+
await ContentTaskUtils.waitForCondition(() => {
return content.document.querySelector("select");
});
diff --git a/browser/components/extensions/test/browser/browser_ext_runtime_getContexts.js b/browser/components/extensions/test/browser/browser_ext_runtime_getContexts.js
new file mode 100644
index 0000000000..4d28cd5f87
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_runtime_getContexts.js
@@ -0,0 +1,597 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+loadTestSubscript("head_devtools.js");
+
+async function genericChecker() {
+ const params = new URLSearchParams(window.location.search);
+ const kind = params.get("kind");
+
+ browser.test.onMessage.addListener(async (msg, ...args) => {
+ if (msg == `${kind}-get-contexts-invalid-params`) {
+ browser.test.assertThrows(
+ () => browser.runtime.getContexts({ unknownParamName: true }),
+ /Type error for parameter filter \(Unexpected property "unknownParamName"\)/,
+ "Got the expected error on unexpected filter property"
+ );
+ browser.test.sendMessage(`${msg}:done`);
+ } else if (msg == `${kind}-get-contexts`) {
+ const filter = args[0];
+ try {
+ const result = await browser.runtime.getContexts(filter);
+ browser.test.sendMessage(`${msg}:result`, result);
+ } catch (err) {
+ // In case of unexpected errors, log a failure and let the test
+ // to continue to avoid it to only fail after timing out.
+ browser.test.fail(`browser.runtime.getContexts call rejected: ${err}`);
+ browser.test.sendMessage(`${msg}:result`, []);
+ }
+ } else if (msg == `${kind}-history-push-state`) {
+ const pushStateURL = args[0];
+ window.history.pushState({}, "", pushStateURL);
+ browser.test.sendMessage(`${msg}:done`);
+ } else if (msg == `${kind}-create-iframe`) {
+ const iframeUrl = args[0];
+ const iframe = document.createElement("iframe");
+ iframe.src = iframeUrl;
+ document.body.appendChild(iframe);
+ } else if (msg == `${kind}-open-options-page`) {
+ browser.runtime.openOptionsPage();
+ }
+ });
+
+ if (kind === "devtools-page") {
+ await browser.devtools.panels.create(
+ "Test DevTool Panel",
+ "fake-icon.png",
+ "page.html?kind=devtools-panel"
+ );
+ }
+
+ browser.test.log(`${kind} extension page loaded`);
+ browser.test.sendMessage(`${kind}-loaded`);
+}
+
+async function triggerActionPopup(extension, win, callback) {
+ // Window needs focus to open popups.
+ await focusWindow(win);
+ await clickBrowserAction(extension, win);
+ let browser = await awaitExtensionPanel(extension, win);
+
+ await callback();
+
+ let { unloadPromise } = await promiseBrowserContentUnloaded(browser);
+ closeBrowserAction(extension, win);
+ await unloadPromise;
+}
+
+const byWindowId = (a, b) => a.windowId - b.windowId;
+const byTabId = (a, b) => a.tabId - b.tabId;
+const byFrameId = (a, b) => a.frameId - b.frameId;
+const byContextType = (a, b) => a.contextType.localeCompare(b.contextType);
+
+const assertValidContextId = contextId => {
+ Assert.equal(
+ typeof contextId,
+ "string",
+ "contextId should be set to a string"
+ );
+ Assert.notEqual(
+ contextId.length,
+ 0,
+ "contextId should be set to a non-zero length string"
+ );
+};
+
+const assertGetContextsResult = (
+ actual,
+ expected,
+ msg,
+ { assertContextId = false } = {}
+) => {
+ const actualCopy = assertContextId ? actual : actual.map(it => ({ ...it }));
+ if (!assertContextId) {
+ actualCopy.forEach(it => delete it.contextId);
+ }
+ for (let [idx, expectedProps] of expected.entries()) {
+ Assert.deepEqual(actualCopy[idx], expectedProps, msg);
+ }
+ Assert.equal(
+ actualCopy.length,
+ expected.length,
+ "Got the expected number of extension contexts"
+ );
+};
+
+add_task(async function test_runtime_getContexts() {
+ const EXT_ID = "runtime-getContexts@mochitest";
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary", // To automatically show sidebar on load.
+ incognitoOverride: "spanning",
+ manifest: {
+ manifest_version: 3,
+ browser_specific_settings: { gecko: { id: EXT_ID } },
+
+ action: {
+ default_popup: "page.html?kind=action",
+ default_area: "navbar",
+ },
+
+ sidebar_action: {
+ default_panel: "page.html?kind=sidebar",
+ },
+
+ options_ui: {
+ page: "page.html?kind=options",
+ },
+
+ devtools_page: "page.html?kind=devtools-page",
+
+ background: {
+ page: "page.html?kind=background",
+ },
+ },
+
+ files: {
+ "page.html": `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <script src="page.js"></script>
+ </body></html>
+ `,
+
+ "page.js": genericChecker,
+ },
+ });
+
+ const {
+ Management: {
+ global: { tabTracker, windowTracker },
+ },
+ } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs");
+
+ let firstWin = window;
+ let secondWin = await BrowserTestUtils.openNewBrowserWindow();
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+
+ await extension.startup();
+ await extension.awaitMessage("background-loaded");
+
+ // Expect 3 sidebars (2 non-private and 1 private windows).
+ await extension.awaitMessage("sidebar-loaded");
+ await extension.awaitMessage("sidebar-loaded");
+ await extension.awaitMessage("sidebar-loaded");
+
+ let firstWinId = windowTracker.getId(firstWin);
+ let secondWinId = windowTracker.getId(secondWin);
+ let privateWinId = windowTracker.getId(privateWin);
+
+ const getGetContextsResults = async ({ filter, sortBy }) => {
+ extension.sendMessage("background-get-contexts", filter);
+ let results = await extension.awaitMessage(
+ "background-get-contexts:result"
+ );
+ if (sortBy) {
+ results.sort(sortBy);
+ }
+ return results;
+ };
+
+ const resolveExtPageUrl = urlPath =>
+ WebExtensionPolicy.getByID(EXT_ID).extension.baseURI.resolve(urlPath);
+
+ const documentOrigin = resolveExtPageUrl("/").slice(0, -1);
+
+ const getExpectedExtensionContext = ({
+ contextId,
+ contextType,
+ documentUrl,
+ incognito = false,
+ frameId = 0,
+ tabId = -1,
+ windowId = -1,
+ }) => {
+ let props = {
+ contextType,
+ documentOrigin,
+ documentUrl,
+ incognito,
+ frameId,
+ tabId,
+ windowId,
+ };
+ if (contextId) {
+ props.contextId = contextId;
+ }
+ return props;
+ };
+
+ let expected = [
+ getExpectedExtensionContext({
+ contextType: "BACKGROUND",
+ documentUrl: resolveExtPageUrl("page.html?kind=background"),
+ }),
+
+ getExpectedExtensionContext({
+ contextType: "SIDE_PANEL",
+ documentUrl: resolveExtPageUrl("page.html?kind=sidebar"),
+ windowId: firstWinId,
+ }),
+
+ getExpectedExtensionContext({
+ contextType: "SIDE_PANEL",
+ documentUrl: resolveExtPageUrl("page.html?kind=sidebar"),
+ windowId: secondWinId,
+ }),
+
+ getExpectedExtensionContext({
+ contextType: "SIDE_PANEL",
+ documentUrl: resolveExtPageUrl("page.html?kind=sidebar"),
+ windowId: privateWinId,
+ incognito: true,
+ }),
+ ].sort(byWindowId);
+
+ info("Test getContexts error on unsupported getContexts filter property");
+ extension.sendMessage("background-get-contexts-invalid-params");
+ await extension.awaitMessage("background-get-contexts-invalid-params:done");
+
+ info("Test getContexts with a valid empty filter");
+ let actual = await getGetContextsResults({ filter: {}, sortBy: byWindowId });
+
+ assertGetContextsResult(
+ actual,
+ expected,
+ "Got the expected results from runtime.getContexts (with an empty filter)"
+ );
+
+ for (const ctx of actual) {
+ info(`Validate contextId for context ${ctx.contextType} ${ctx.contextId}`);
+ assertValidContextId(ctx.contextId);
+ }
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser: secondWin.gBrowser,
+ url: resolveExtPageUrl("page.html?kind=tab"),
+ },
+ async browser => {
+ info("Wait the extension page to be fully loaded in the new tab");
+ await extension.awaitMessage("tab-loaded");
+
+ const tabId = tabTracker.getBrowserData(browser).tabId;
+
+ const expectedTabContext = getExpectedExtensionContext({
+ contextType: "TAB",
+ documentUrl: resolveExtPageUrl("page.html?kind=tab"),
+ windowId: secondWinId,
+ tabId,
+ incognito: false,
+ });
+
+ info("Test getContexts with contextTypes TAB filter");
+ let actual = await getGetContextsResults({
+ filter: { contextTypes: ["TAB"] },
+ });
+ assertGetContextsResult(
+ actual,
+ [expectedTabContext],
+ "Got the expected results from runtime.getContexts (with contextTypes TAB filter)"
+ );
+ assertValidContextId(actual[0].contextId);
+ const initialTabContextId = actual[0].contextId;
+
+ info("Test getContexts with contextTypes TabIds filter");
+ actual = await getGetContextsResults({
+ filter: { tabIds: [tabId] },
+ });
+ assertGetContextsResult(
+ actual,
+ [expectedTabContext],
+ "Got the expected results from runtime.getContexts (with tabIds filter)"
+ );
+
+ info("Test getContexts with contextTypes WindowIds filter");
+ actual = await getGetContextsResults({
+ filter: { windowIds: [secondWinId] },
+ sortBy: byTabId,
+ });
+ assertGetContextsResult(
+ actual,
+ [
+ expectedTabContext,
+ expected.find(it => it.windowId === secondWinId),
+ ].sort(byTabId),
+ "Got the expected results from runtime.getContexts (with windowIds filter)"
+ );
+
+ info("Test getContexts after navigating the tab");
+ const newTabURL = resolveExtPageUrl("page.html?kind=tab&navigated=true");
+ browser.loadURI(Services.io.newURI(newTabURL), {
+ triggeringPrincipal:
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ });
+ await extension.awaitMessage("tab-loaded");
+
+ actual = await getGetContextsResults({
+ filter: {
+ contextTypes: ["TAB"],
+ windowIds: [secondWinId],
+ },
+ });
+ Assert.equal(actual.length, 1, "Expect 1 tab extension context");
+ Assert.equal(
+ actual[0].documentUrl,
+ newTabURL,
+ "Expect documentUrl to match the new loaded url"
+ );
+ Assert.equal(actual[0].frameId, 0, "Got expected frameId");
+ Assert.equal(
+ actual[0].tabId,
+ expectedTabContext.tabId,
+ "Got expected tabId"
+ );
+ Assert.notEqual(
+ actual[0].contextId,
+ initialTabContextId,
+ "Expect contextId to change on navigated tab"
+ );
+ }
+ );
+
+ await triggerActionPopup(extension, privateWin, async () => {
+ info("Wait the extension page to be fully loaded in the action popup");
+ await extension.awaitMessage("action-loaded");
+
+ const expectedPopupContext = getExpectedExtensionContext({
+ contextType: "POPUP",
+ documentUrl: resolveExtPageUrl("page.html?kind=action"),
+ windowId: privateWinId,
+ tabId: -1,
+ incognito: true,
+ });
+
+ info("Test getContexts with contextTypes POPUP filter");
+ let actual = await getGetContextsResults({
+ filter: {
+ contextTypes: ["POPUP"],
+ },
+ });
+ assertGetContextsResult(
+ actual,
+ [expectedPopupContext],
+ "Got the expected results from runtime.getContexts (with contextTypes POPUP filter)"
+ );
+
+ info("Test getContexts with incognito true filter");
+ actual = await getGetContextsResults({
+ filter: { incognito: true },
+ sortBy: byContextType,
+ });
+ assertGetContextsResult(
+ actual.sort(byContextType),
+ [expectedPopupContext, ...expected.filter(it => it.incognito)].sort(
+ byContextType
+ ),
+ "Got the expected results from runtime.getContexts (with contextTypes incognito true filter)"
+ );
+ });
+
+ info("Test getContexts with existing background iframes");
+ extension.sendMessage(
+ `background-create-iframe`,
+ resolveExtPageUrl("page.html?kind=background-subframe")
+ );
+ await extension.awaitMessage(`background-subframe-loaded`);
+
+ actual = await getGetContextsResults({
+ filter: { contextTypes: ["BACKGROUND"] },
+ });
+
+ Assert.equal(
+ actual.length,
+ 2,
+ "Expect 2 background extension contexts to be found"
+ );
+ const bgTopFrame = actual.find(
+ it => it.documentUrl === resolveExtPageUrl("page.html?kind=background")
+ );
+ const bgSubFrame = actual.find(
+ it =>
+ it.documentUrl === resolveExtPageUrl("page.html?kind=background-subframe")
+ );
+
+ assertValidContextId(bgTopFrame.contextId);
+ assertValidContextId(bgSubFrame.contextId);
+ Assert.notEqual(
+ bgTopFrame.contextId,
+ bgSubFrame.contextId,
+ "Expect background top and sub frame to have different contextIds"
+ );
+
+ Assert.equal(
+ bgTopFrame.frameId,
+ 0,
+ "Expect background top frame to have frameId 0"
+ );
+ ok(
+ typeof bgSubFrame.frameId === "number" && bgSubFrame.frameId > 0,
+ "Expect background sub frame to have a non zero frameId"
+ );
+ Assert.equal(
+ bgSubFrame.windowId,
+ bgSubFrame.windowId,
+ "Expect background top frame to have same windowId as the top frame"
+ );
+ Assert.equal(
+ bgSubFrame.tabId,
+ bgTopFrame.tabId,
+ "Expect background top frame to have same tabId as the top frame"
+ );
+
+ info("Test getContexts with existing sidebars iframes");
+ extension.sendMessage(
+ `sidebar-create-iframe`,
+ resolveExtPageUrl("page.html?kind=sidebar-subframe")
+ );
+ // Expect 3 sidebar subframe to be created.
+ await extension.awaitMessage(`sidebar-subframe-loaded`);
+ await extension.awaitMessage(`sidebar-subframe-loaded`);
+ await extension.awaitMessage(`sidebar-subframe-loaded`);
+
+ actual = await getGetContextsResults({
+ filter: { contextTypes: ["SIDE_PANEL"], windowIds: [firstWinId] },
+ sortBy: byFrameId,
+ });
+ Assert.equal(
+ actual.length,
+ 2,
+ "Expect 2 sidebar extension contexts to be found for the first window"
+ );
+ // NOTE: we have already asserted that the sidebar top level frame has frameId 0.
+ Assert.greater(
+ actual.find(
+ it =>
+ it.documentUrl == resolveExtPageUrl("page.html?kind=sidebar-subframe")
+ )?.frameId,
+ 0,
+ "Expect sidebar subframe to have the expected frameId"
+ );
+ // NOTE: we have already asserted that the top level frame have tabId -1.
+ Assert.equal(
+ actual[0].tabId,
+ actual[1].tabId,
+ "Expect iframe and top level sidebar frame to have the same tabId"
+ );
+
+ actual = await getGetContextsResults({
+ filter: { contextTypes: ["SIDE_PANEL"], windowIds: [secondWinId] },
+ sortBy: byFrameId,
+ });
+ Assert.equal(
+ actual.length,
+ 2,
+ "Expect 2 sidebar extension contexts to be found for the second window"
+ );
+
+ actual = await getGetContextsResults({
+ filter: { contextTypes: ["SIDE_PANEL"], incognito: true },
+ });
+ Assert.equal(
+ actual.length,
+ 2,
+ "Expect 2 sidebar extension contexts to be found for private windows"
+ );
+
+ info("Test getContexts after background history push state");
+ let pushStateURLPath = "/page.html?kind=background&pushedState=1";
+ extension.sendMessage("background-history-push-state", pushStateURLPath);
+ await extension.awaitMessage("background-history-push-state:done");
+
+ actual = await getGetContextsResults({
+ filter: { contextTypes: ["BACKGROUND"], frameIds: [0] },
+ });
+ Assert.equal(
+ actual.length,
+ 1,
+ "Expect 1 top level background context to be found"
+ );
+ Assert.equal(
+ actual[0].contextId,
+ bgTopFrame.contextId,
+ "Expect top level background contextId to NOT be changed"
+ );
+ Assert.equal(
+ actual[0].documentUrl,
+ resolveExtPageUrl(pushStateURLPath),
+ "Expect top level background documentUrl to change due to history.pushState"
+ );
+
+ await BrowserTestUtils.closeWindow(privateWin);
+ await BrowserTestUtils.closeWindow(secondWin);
+
+ info(
+ "Test getContexts after opening an options page embedded in an about:addons tab"
+ );
+ await BrowserTestUtils.withNewTab("about:addons", async () => {
+ extension.sendMessage("background-open-options-page");
+ await extension.awaitMessage("options-loaded");
+ const { selectedBrowser } = firstWin.gBrowser;
+ Assert.equal(
+ selectedBrowser.currentURI.spec,
+ "about:addons",
+ "Expect an about:addons tab to be current active tab"
+ );
+ let optionsTabId = tabTracker.getBrowserData(selectedBrowser).tabId;
+
+ actual = await getGetContextsResults({
+ filter: { windowIds: [firstWinId], tabIds: [optionsTabId] },
+ });
+ assertGetContextsResult(
+ actual,
+ [
+ getExpectedExtensionContext({
+ contextType: "TAB",
+ documentUrl: resolveExtPageUrl("page.html?kind=options"),
+ windowId: firstWinId,
+ tabId: optionsTabId,
+ }),
+ ],
+ "Got the expected results from runtime.getContexts for an options_page"
+ );
+ });
+
+ info("Test getContexts with an extension devtools page and devtools panel");
+ await BrowserTestUtils.withNewTab("https://example.com", async () => {
+ const tab = gBrowser.selectedTab;
+ const toolbox = await openToolboxForTab(tab);
+
+ info("Wait for the devtools page to be loaded");
+ await extension.awaitMessage("devtools-page-loaded");
+
+ Assert.equal(
+ toolbox.getAdditionalTools()?.length,
+ 1,
+ "Expecte extension devtools panel to be registered"
+ );
+ let panelId = toolbox.getAdditionalTools()[0].id;
+ await gDevTools.showToolboxForTab(tab, { toolId: panelId });
+
+ info("Wait for the devtools panel to be loaded");
+ await extension.awaitMessage("devtools-panel-loaded");
+
+ actual = await getGetContextsResults({ filter: {} });
+ // Expect the backgrond page and its subframe to still be returned.
+ Assert.equal(
+ actual.filter(ctx => ctx.contextType === "BACKGROUND").length,
+ 2,
+ "Expect the existing 2 background context types"
+ );
+ // Expect the side_panel page and its subframe to still be returned.
+ Assert.equal(
+ actual.filter(ctx => ctx.contextType === "SIDE_PANEL").length,
+ 2,
+ "Expect the existing 2 side_panel context types"
+ );
+ // Expect no other context to be listed in the getContexts results
+ // (devtools page and panel are currently expected to not be
+ // part of getContexts results).
+ Assert.deepEqual(
+ actual.filter(
+ ctx => !["BACKGROUND", "SIDE_PANEL"].includes(ctx.contextType)
+ ),
+ [],
+ "DevTools page and panel are not listed in getContexts results"
+ );
+
+ await closeToolboxForTab(gBrowser.selectedTab);
+ });
+
+ await extension.unload();
+});
diff --git a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js
index 7dd41ecfe9..715cf14458 100644
--- a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js
+++ b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js
@@ -7,7 +7,7 @@ requestLongerTimeout(2);
loadTestSubscript("head_sessions.js");
add_task(async function test_sessions_get_recently_closed() {
- async function openAndCloseWindow(url = "http://example.com", tabUrls) {
+ async function openAndCloseWindow(url = "https://example.com", tabUrls) {
let win = await BrowserTestUtils.openNewBrowserWindow();
BrowserTestUtils.startLoadingURIString(win.gBrowser.selectedBrowser, url);
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
@@ -77,13 +77,13 @@ add_task(async function test_sessions_get_recently_closed() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
- "http://example.com"
+ "https://example.com"
);
BrowserTestUtils.removeTab(tab);
tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
- "http://example.com"
+ "https://example.com"
);
BrowserTestUtils.removeTab(tab);
@@ -123,7 +123,7 @@ add_task(async function test_sessions_get_recently_closed_navigated() {
.then(recentlyClosed => {
let tab = recentlyClosed[0].window.tabs[0];
browser.test.assertEq(
- "http://example.com/",
+ "https://example.com/",
tab.url,
"Tab in closed window has the expected url."
);
@@ -144,7 +144,7 @@ add_task(async function test_sessions_get_recently_closed_navigated() {
// Test with a window with navigation history.
let win = await BrowserTestUtils.openNewBrowserWindow();
- for (let url of ["about:robots", "about:mozilla", "http://example.com/"]) {
+ for (let url of ["about:robots", "about:mozilla", "https://example.com/"]) {
BrowserTestUtils.startLoadingURIString(win.gBrowser.selectedBrowser, url);
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
}
@@ -178,7 +178,7 @@ add_task(
"The second tab with empty.xpi has no url field due to empty history."
);
browser.test.assertEq(
- "http://example.com/",
+ "https://example.com/",
win.tabs[2].url,
"The third tab is example.com."
);
@@ -195,7 +195,7 @@ add_task(
// Test with a window with empty history.
let xpi =
- "http://example.com/browser/browser/components/extensions/test/browser/empty.xpi";
+ "https://example.com/browser/browser/components/extensions/test/browser/empty.xpi";
let newWin = await BrowserTestUtils.openNewBrowserWindow();
await BrowserTestUtils.openNewForegroundTab({
gBrowser: newWin.gBrowser,
@@ -205,7 +205,7 @@ add_task(
});
await BrowserTestUtils.openNewForegroundTab({
gBrowser: newWin.gBrowser,
- url: "http://example.com/",
+ url: "https://example.com/",
});
await BrowserTestUtils.closeWindow(newWin);
diff --git a/browser/components/extensions/test/browser/browser_ext_sessions_restoreTab.js b/browser/components/extensions/test/browser/browser_ext_sessions_restoreTab.js
index c89e9d39dc..46a925039a 100644
--- a/browser/components/extensions/test/browser/browser_ext_sessions_restoreTab.js
+++ b/browser/components/extensions/test/browser/browser_ext_sessions_restoreTab.js
@@ -15,7 +15,7 @@ ChromeUtils.defineESModuleGetters(this, {
// Check that we can restore a tab modified by an extension.
add_task(async function test_restoringModifiedTab() {
function background() {
- browser.tabs.create({ url: "http://example.com/" });
+ browser.tabs.create({ url: "https://example.com/" });
browser.test.onMessage.addListener(msg => {
if (msg == "change-tab") {
browser.tabs.executeScript({ code: 'location.href += "?changedTab";' });
@@ -32,14 +32,14 @@ add_task(async function test_restoringModifiedTab() {
background,
});
- const contentScriptTabURL = "http://example.com/?changedTab";
+ const contentScriptTabURL = "https://example.com/?changedTab";
let win = await BrowserTestUtils.openNewBrowserWindow({});
// Open and close a tabs.
let tabPromise = BrowserTestUtils.waitForNewTab(
win.gBrowser,
- "http://example.com/",
+ "https://example.com/",
true
);
await extension.startup();
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
index 8498f73071..ac2d19cf23 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
@@ -7,6 +7,7 @@ requestLongerTimeout(2);
let extData = {
manifest: {
sidebar_action: {
+ default_icon: "default.png",
default_panel: "sidebar.html",
},
},
@@ -29,6 +30,9 @@ let extData = {
browser.test.sendMessage("sidebar");
};
},
+
+ "default.png": imageBuffer,
+ "1.png": imageBuffer,
},
background: function () {
@@ -44,6 +48,8 @@ let extData = {
let { arg = {}, result } = data;
let isOpen = await browser.sidebarAction.isOpen(arg);
browser.test.assertEq(result, isOpen, "expected value from isOpen");
+ } else if (msg === "set-icon") {
+ await browser.sidebarAction.setIcon({ path: data });
}
browser.test.sendMessage("done");
});
@@ -148,7 +154,7 @@ add_task(async function sidebar_isOpen() {
info("Test extension1's sidebar is opened on install");
await extension1.awaitMessage("sidebar");
await sendMessage(extension1, "isOpen", { result: true });
- let sidebar1ID = SidebarUI.currentID;
+ let sidebar1ID = SidebarController.currentID;
info("Load extension2");
let extension2 = ExtensionTestUtils.loadExtension(getExtData());
@@ -160,7 +166,7 @@ add_task(async function sidebar_isOpen() {
await sendMessage(extension2, "isOpen", { result: true });
info("Switch back to extension1's sidebar");
- SidebarUI.show(sidebar1ID);
+ SidebarController.show(sidebar1ID);
await extension1.awaitMessage("sidebar");
await sendMessage(extension1, "isOpen", { result: true });
await sendMessage(extension2, "isOpen", { result: false });
@@ -195,7 +201,7 @@ add_task(async function sidebar_isOpen() {
newWin.close();
info("Close the sidebar in the original window");
- SidebarUI.hide();
+ SidebarController.hide();
await sendMessage(extension1, "isOpen", { result: false });
await sendMessage(extension2, "isOpen", { result: false });
@@ -223,11 +229,15 @@ add_task(async function testShortcuts() {
async function toggleSwitcherPanel(win = window) {
// Open and close the switcher panel to trigger shortcut content rendering.
- let switcherPanelShown = promisePopupShown(win.SidebarUI._switcherPanel);
- win.SidebarUI.showSwitcherPanel();
+ let switcherPanelShown = promisePopupShown(
+ win.SidebarController._switcherPanel
+ );
+ win.SidebarController.showSwitcherPanel();
await switcherPanelShown;
- let switcherPanelHidden = promisePopupHidden(win.SidebarUI._switcherPanel);
- win.SidebarUI.hideSwitcherPanel();
+ let switcherPanelHidden = promisePopupHidden(
+ win.SidebarController._switcherPanel
+ );
+ win.SidebarController.hideSwitcherPanel();
await switcherPanelHidden;
}
@@ -301,3 +311,39 @@ add_task(async function testShortcuts() {
await BrowserTestUtils.closeWindow(win);
});
+
+add_task(async function sidebar_switcher_panel_icon_update() {
+ info("Load extension");
+ const extension = ExtensionTestUtils.loadExtension(getExtData());
+ await extension.startup();
+
+ info("Test extension's sidebar is opened on install");
+ await extension.awaitMessage("sidebar");
+ await sendMessage(extension, "isOpen", { result: true });
+ const sidebarID = SidebarController.currentID;
+
+ const item = SidebarController._switcherPanel.querySelector(
+ ".webextension-menuitem"
+ );
+ let iconUrl = `moz-extension://${extension.uuid}/default.png`;
+ is(
+ item.style.getPropertyValue("--webextension-menuitem-image"),
+ `image-set(url("${iconUrl}"), url("${iconUrl}") 2x)`,
+ "Extension has the correct icon."
+ );
+ SidebarController.hide();
+ await sendMessage(extension, "isOpen", { result: false });
+
+ await sendMessage(extension, "set-icon", "1.png");
+ await SidebarController.show(sidebarID);
+ await extension.awaitMessage("sidebar");
+ await sendMessage(extension, "isOpen", { result: true });
+ iconUrl = `moz-extension://${extension.uuid}/1.png`;
+ is(
+ item.style.getPropertyValue("--webextension-menuitem-image"),
+ `image-set(url("${iconUrl}"), url("${iconUrl}") 2x)`,
+ "Extension has updated icon."
+ );
+
+ await extension.unload();
+});
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js
index 621d2d1180..a82f6b8186 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_click.js
@@ -54,7 +54,7 @@ add_task(async function test_sidebar_click_isAppTab_behavior() {
await extension.awaitMessage("sidebar-ready");
// This test fails if docShell.isAppTab has not been set to true.
- let content = SidebarUI.browser.contentWindow;
+ let content = SidebarController.browser.contentWindow;
// Wait for the layout to be flushed, otherwise this test may
// fail intermittently if synthesizeMouseAtCenter is being called
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_context.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_context.js
index 7057037a5e..93d34d4487 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_context.js
@@ -124,7 +124,7 @@ async function runTests(options) {
});
// Wait for initial sidebar load.
- SidebarUI.browser.addEventListener(
+ SidebarController.browser.addEventListener(
"load",
async () => {
// Wait for the background page listeners to be ready and
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_httpAuth.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_httpAuth.js
index d50d96b822..f8ab238148 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_httpAuth.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_httpAuth.js
@@ -42,7 +42,7 @@ add_task(async function sidebar_httpAuthPrompt() {
// Wait for the http auth prompt and close it with accept button.
let promptPromise = PromptTestUtils.handleNextPrompt(
- SidebarUI.browser.contentWindow,
+ SidebarController.browser.contentWindow,
{
modalType: Services.prompt.MODAL_TYPE_WINDOW,
promptType: "promptUserAndPass",
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_incognito.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_incognito.js
index 221447cf2e..39269d9d06 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_incognito.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_incognito.js
@@ -124,11 +124,14 @@ add_task(async function test_sidebarAction_not_allowed() {
await extension.startup();
let sidebarID = `${makeWidgetId(extension.id)}-sidebar-action`;
- ok(SidebarUI.sidebars.has(sidebarID), "sidebar exists in non-private window");
+ ok(
+ SidebarController.sidebars.has(sidebarID),
+ "sidebar exists in non-private window"
+ );
let winData = await getIncognitoWindow();
- let hasSidebar = winData.win.SidebarUI.sidebars.has(sidebarID);
+ let hasSidebar = winData.win.SidebarController.sidebars.has(sidebarID);
ok(!hasSidebar, "sidebar does not exist in private window");
// Test API access to private window data.
extension.sendMessage(winData.details);
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_runtime.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_runtime.js
index 55c83ee0b1..8ac1dd76a1 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_runtime.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_runtime.js
@@ -52,9 +52,10 @@ add_task(async function test_sidebar_disconnect() {
await connected;
// Bug 1445080 fixes currentURI, test to avoid future breakage.
- let currentURI = window.SidebarUI.browser.contentDocument.getElementById(
- "webext-panels-browser"
- ).currentURI;
+ let currentURI =
+ window.SidebarController.browser.contentDocument.getElementById(
+ "webext-panels-browser"
+ ).currentURI;
is(currentURI.scheme, "moz-extension", "currentURI is set correctly");
// switching sidebar to another extension
@@ -68,7 +69,7 @@ add_task(async function test_sidebar_disconnect() {
// switching sidebar to built-in sidebar
let disconnected = extension2.awaitMessage("disconnected");
- window.SidebarUI.show("viewBookmarksSidebar");
+ window.SidebarController.show("viewBookmarksSidebar");
await disconnected;
await extension.unload();
diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
index 58f2b07797..c8f3f043ec 100644
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
@@ -49,7 +49,7 @@ add_task(async function sidebar_windows() {
let secondSidebar = extension.awaitMessage("sidebar");
- // SidebarUI relies on window.opener being set, which is normal behavior when
+ // SidebarController relies on window.opener being set, which is normal behavior when
// using menu or key commands to open a new browser window.
let win = await BrowserTestUtils.openNewBrowserWindow();
diff --git a/browser/components/extensions/test/browser/browser_unified_extensions.js b/browser/components/extensions/test/browser/browser_unified_extensions.js
index 2c65f47c4e..b83d4c2e23 100644
--- a/browser/components/extensions/test/browser/browser_unified_extensions.js
+++ b/browser/components/extensions/test/browser/browser_unified_extensions.js
@@ -44,6 +44,9 @@ add_setup(async function () {
// panel, which could happen when a previous test file resizes the current
// window.
await ensureMaximizedWindow(window);
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.originControls.grantByDefault", false]],
+ });
});
add_task(async function test_button_enabled_by_pref() {
diff --git a/browser/components/extensions/test/browser/browser_unified_extensions_accessibility.js b/browser/components/extensions/test/browser/browser_unified_extensions_accessibility.js
index 44d861d97c..8439cf30dd 100644
--- a/browser/components/extensions/test/browser/browser_unified_extensions_accessibility.js
+++ b/browser/components/extensions/test/browser/browser_unified_extensions_accessibility.js
@@ -5,6 +5,12 @@
loadTestSubscript("head_unified_extensions.js");
+add_setup(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.originControls.grantByDefault", false]],
+ });
+});
+
add_task(async function test_keyboard_navigation_activeScript() {
const extension1 = ExtensionTestUtils.loadExtension({
manifest: {
diff --git a/browser/components/extensions/test/browser/browser_unified_extensions_context_menu.js b/browser/components/extensions/test/browser/browser_unified_extensions_context_menu.js
index d04d85e535..a7bdc8273c 100644
--- a/browser/components/extensions/test/browser/browser_unified_extensions_context_menu.js
+++ b/browser/components/extensions/test/browser/browser_unified_extensions_context_menu.js
@@ -5,10 +5,6 @@
requestLongerTimeout(2);
-ChromeUtils.defineESModuleGetters(this, {
- AbuseReporter: "resource://gre/modules/AbuseReporter.sys.mjs",
-});
-
const { EnterprisePolicyTesting } = ChromeUtils.importESModule(
"resource://testing-common/EnterprisePolicyTesting.sys.mjs"
);
@@ -31,21 +27,6 @@ const promiseExtensionUninstalled = extensionId => {
});
};
-function waitClosedWindow(win) {
- return new Promise(resolve => {
- function onWindowClosed() {
- if (win && !win.closed) {
- // If a specific window reference has been passed, then check
- // that the window is closed before resolving the promise.
- return;
- }
- Services.obs.removeObserver(onWindowClosed, "xul-window-destroyed");
- resolve();
- }
- Services.obs.addObserver(onWindowClosed, "xul-window-destroyed");
- });
-}
-
function assertVisibleContextMenuItems(contextMenu, expected) {
let visibleItems = contextMenu.querySelectorAll(
":is(menuitem, menuseparator):not([hidden])"
@@ -366,90 +347,33 @@ add_task(async function test_report_extension() {
// closed and about:addons is open with the "abuse report dialog".
const hidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
- if (AbuseReporter.amoFormEnabled) {
- const reportURL = Services.urlFormatter
- .formatURLPref("extensions.abuseReport.amoFormURL")
- .replace("%addonID%", extension.id);
-
- const promiseReportTab = BrowserTestUtils.waitForNewTab(
- gBrowser,
- reportURL,
- /* waitForLoad */ false,
- // Do not expect it to be the next tab opened
- /* waitForAnyTab */ true
- );
- contextMenu.activateItem(reportButton);
- const [reportTab] = await Promise.all([promiseReportTab, hidden]);
- // Remove the report tab and expect the selected tab
- // to become the about:addons tab.
- BrowserTestUtils.removeTab(reportTab);
- if (AbuseReporter.amoFormEnabled) {
- is(
- gBrowser.selectedBrowser.currentURI.spec,
- "about:blank",
- "Expect about:addons tab to have not been opened (amoFormEnabled=true)"
- );
- } else {
- is(
- gBrowser.selectedBrowser.currentURI.spec,
- "about:addons",
- "Got about:addons tab selected (amoFormEnabled=false)"
- );
- }
- return;
- }
+ const reportURL = Services.urlFormatter
+ .formatURLPref("extensions.abuseReport.amoFormURL")
+ .replace("%addonID%", extension.id);
- const abuseReportOpen = BrowserTestUtils.waitForCondition(
- () => AbuseReporter.getOpenDialog(),
- "wait for the abuse report dialog to have been opened"
+ const promiseReportTab = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ reportURL,
+ /* waitForLoad */ false,
+ // Do not expect it to be the next tab opened
+ /* waitForAnyTab */ true
);
contextMenu.activateItem(reportButton);
- const [reportDialogWindow] = await Promise.all([abuseReportOpen, hidden]);
-
- const reportDialogParams =
- reportDialogWindow.arguments[0].wrappedJSObject;
- is(
- reportDialogParams.report.addon.id,
- extension.id,
- "abuse report dialog has the expected addon id"
- );
+ const [reportTab] = await Promise.all([promiseReportTab, hidden]);
+ // Remove the report tab and expect the selected tab
+ // to become the about:addons tab.
+ BrowserTestUtils.removeTab(reportTab);
is(
- reportDialogParams.report.reportEntryPoint,
- "unified_context_menu",
- "abuse report dialog has the expected reportEntryPoint"
+ gBrowser.selectedBrowser.currentURI.spec,
+ "about:blank",
+ "Expect about:addons tab to have not been opened"
);
-
- let promiseClosedWindow = waitClosedWindow();
- reportDialogWindow.close();
- // Wait for the report dialog window to be completely closed
- // (to prevent an intermittent failure due to a race between
- // the dialog window being closed and the test tasks that follows
- // opening the unified extensions button panel to not lose the
- // focus and be suddently closed before the task has done with
- // its assertions, see Bug 1782304).
- await promiseClosedWindow;
});
}
const [ext] = createExtensions([{ name: "an extension" }]);
await ext.startup();
-
- info("Test report with amoFormEnabled=true");
-
- await SpecialPowers.pushPrefEnv({
- set: [["extensions.abuseReport.amoFormEnabled", true]],
- });
await runReportTest(ext);
- await SpecialPowers.popPrefEnv();
-
- info("Test report with amoFormEnabled=false");
-
- await SpecialPowers.pushPrefEnv({
- set: [["extensions.abuseReport.amoFormEnabled", false]],
- });
- await runReportTest(ext);
- await SpecialPowers.popPrefEnv();
-
await ext.unload();
});
diff --git a/browser/components/extensions/test/browser/head.js b/browser/components/extensions/test/browser/head.js
index 344eb3cca7..2d6835a034 100644
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -31,6 +31,7 @@
* loadTestSubscript awaitBrowserLoaded
* getScreenAt roundCssPixcel getCssAvailRect isRectContained
* getToolboxBackgroundColor
+ * promiseBrowserContentUnloaded
*/
// There are shutdown issues for which multiple rejections are left uncaught.
@@ -167,6 +168,44 @@ function promiseAnimationFrame(win = window) {
return AppUiTestInternals.promiseAnimationFrame(win);
}
+async function promiseBrowserContentUnloaded(browser) {
+ // Wait until the content has unloaded before resuming the test, to avoid
+ // calling extension.getViews too early (and having intermittent failures).
+ const MSG_WINDOW_DESTROYED = "Test:BrowserContentDestroyed";
+ let unloadPromise = new Promise(resolve => {
+ Services.ppmm.addMessageListener(MSG_WINDOW_DESTROYED, function listener() {
+ Services.ppmm.removeMessageListener(MSG_WINDOW_DESTROYED, listener);
+ resolve();
+ });
+ });
+
+ await ContentTask.spawn(
+ browser,
+ MSG_WINDOW_DESTROYED,
+ MSG_WINDOW_DESTROYED => {
+ let innerWindowId = this.content.windowGlobalChild.innerWindowId;
+ let observer = subject => {
+ if (
+ innerWindowId === subject.QueryInterface(Ci.nsISupportsPRUint64).data
+ ) {
+ Services.obs.removeObserver(observer, "inner-window-destroyed");
+
+ // Use process message manager to ensure that the message is delivered
+ // even after the <browser>'s message manager is disconnected.
+ Services.cpmm.sendAsyncMessage(MSG_WINDOW_DESTROYED);
+ }
+ };
+ // Observe inner-window-destroyed, like ExtensionPageChild, to ensure that
+ // the ExtensionPageContextChild instance has been unloaded when we resolve
+ // the unloadPromise.
+ Services.obs.addObserver(observer, "inner-window-destroyed");
+ }
+ );
+
+ // Return an object so that callers can use "await".
+ return { unloadPromise };
+}
+
function promisePopupHidden(popup) {
return new Promise(resolve => {
let onPopupHidden = () => {
@@ -437,10 +476,11 @@ async function openContextMenuInPopup(
}
async function openContextMenuInSidebar(selector = "body") {
- let contentAreaContextMenu = SidebarUI.browser.contentDocument.getElementById(
- "contentAreaContextMenu"
- );
- let browser = SidebarUI.browser.contentDocument.getElementById(
+ let contentAreaContextMenu =
+ SidebarController.browser.contentDocument.getElementById(
+ "contentAreaContextMenu"
+ );
+ let browser = SidebarController.browser.contentDocument.getElementById(
"webext-panels-browser"
);
let popupShownPromise = BrowserTestUtils.waitForEvent(
@@ -452,7 +492,9 @@ async function openContextMenuInSidebar(selector = "body") {
// fail intermittently if synthesizeMouseAtCenter is being called
// while the sidebar is still opening and the browser window layout
// being recomputed.
- await SidebarUI.browser.contentWindow.promiseDocumentFlushed(() => {});
+ await SidebarController.browser.contentWindow.promiseDocumentFlushed(
+ () => {}
+ );
info("Opening context menu in sidebarAction panel");
await BrowserTestUtils.synthesizeMouseAtCenter(