Bug 1797838 - Add a webExtension property to CustomizableUI widgets for browserAction buttons. r=dao,willdurand
This property, along with the CustomizableUI.isWebExtensionWidget, will make it possible to distinguish WebExtension-provided widgets without sniffing CSS classes, which is what we currently do. In the event that the widget has been destroyed or hasn't yet been created, this will fallback to looking for the `-browser-action` suffix on the ID. This is mainly for use during migrations which happen early during CustomizableUI startup, when it's unlikely that the extensions have finished initializing themselves and creating their widgets yet. Differential Revision: https://phabricator.services.mozilla.com/D160802
This commit is contained in:
@@ -2899,6 +2899,7 @@ var CustomizableUIInternal = {
|
|||||||
_introducedInVersion: -1,
|
_introducedInVersion: -1,
|
||||||
keepBroadcastAttributesWhenCustomizing: false,
|
keepBroadcastAttributesWhenCustomizing: false,
|
||||||
disallowSubView: false,
|
disallowSubView: false,
|
||||||
|
webExtension: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof aData.id != "string" || !/^[a-z0-9-_]{1,}$/i.test(aData.id)) {
|
if (typeof aData.id != "string" || !/^[a-z0-9-_]{1,}$/i.test(aData.id)) {
|
||||||
@@ -2942,6 +2943,7 @@ var CustomizableUIInternal = {
|
|||||||
"localized",
|
"localized",
|
||||||
"keepBroadcastAttributesWhenCustomizing",
|
"keepBroadcastAttributesWhenCustomizing",
|
||||||
"disallowSubView",
|
"disallowSubView",
|
||||||
|
"webExtension",
|
||||||
];
|
];
|
||||||
for (let prop of kOptBoolProps) {
|
for (let prop of kOptBoolProps) {
|
||||||
if (typeof aData[prop] == "boolean") {
|
if (typeof aData[prop] == "boolean") {
|
||||||
@@ -4016,6 +4018,8 @@ var CustomizableUI = {
|
|||||||
* - locationSpecific: If true, closes the panel if the location changes.
|
* - locationSpecific: If true, closes the panel if the location changes.
|
||||||
* This is similar to tabSpecific, but also if the location
|
* This is similar to tabSpecific, but also if the location
|
||||||
* changes in the same tab, we may want to close the panel.
|
* changes in the same tab, we may want to close the panel.
|
||||||
|
* - webExtension: Set to true if this widget is being created on behalf of an
|
||||||
|
* extension.
|
||||||
*
|
*
|
||||||
* @param aProperties the specifications for the widget.
|
* @param aProperties the specifications for the widget.
|
||||||
* @return a wrapper around the created widget (see getWidget)
|
* @return a wrapper around the created widget (see getWidget)
|
||||||
@@ -4424,6 +4428,24 @@ var CustomizableUI = {
|
|||||||
isSpecialWidget(aWidgetId) {
|
isSpecialWidget(aWidgetId) {
|
||||||
return CustomizableUIInternal.isSpecialWidget(aWidgetId);
|
return CustomizableUIInternal.isSpecialWidget(aWidgetId);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Check if a widget is provided by an extension. This effectively checks
|
||||||
|
* whether `webExtension: true` passed when the widget was being created.
|
||||||
|
*
|
||||||
|
* If the widget being referred to hasn't yet been created, or has been
|
||||||
|
* destroyed, we fallback to checking the ID for the "-browser-action"
|
||||||
|
* suffix.
|
||||||
|
*
|
||||||
|
* @param aWidgetId the widget ID to check.
|
||||||
|
* @return true if the widget was provided by an extension, false otherwise.
|
||||||
|
*/
|
||||||
|
isWebExtensionWidget(aWidgetId) {
|
||||||
|
let widget = this.getWidget(aWidgetId);
|
||||||
|
if (widget) {
|
||||||
|
return widget.webExtension;
|
||||||
|
}
|
||||||
|
return aWidgetId.endsWith("-browser-action");
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Add listeners to a panel that will close it. For use from the menu panel
|
* Add listeners to a panel that will close it. For use from the menu panel
|
||||||
* and overflowable toolbar implementations, unlikely to be useful for
|
* and overflowable toolbar implementations, unlikely to be useful for
|
||||||
@@ -4736,6 +4758,7 @@ function WidgetGroupWrapper(aWidget) {
|
|||||||
"showInPrivateBrowsing",
|
"showInPrivateBrowsing",
|
||||||
"viewId",
|
"viewId",
|
||||||
"disallowSubView",
|
"disallowSubView",
|
||||||
|
"webExtension",
|
||||||
];
|
];
|
||||||
for (let prop of kBareProps) {
|
for (let prop of kBareProps) {
|
||||||
let propertyName = prop;
|
let propertyName = prop;
|
||||||
@@ -4862,6 +4885,8 @@ function XULWidgetGroupWrapper(aWidgetId) {
|
|||||||
this.isGroup = true;
|
this.isGroup = true;
|
||||||
this.id = aWidgetId;
|
this.id = aWidgetId;
|
||||||
this.type = "custom";
|
this.type = "custom";
|
||||||
|
// XUL Widgets can never be provided by extensions.
|
||||||
|
this.webExtension = false;
|
||||||
this.provider = CustomizableUI.PROVIDER_XUL;
|
this.provider = CustomizableUI.PROVIDER_XUL;
|
||||||
|
|
||||||
this.forWindow = function XULWidgetGroupWrapper_forWindow(aWindow) {
|
this.forWindow = function XULWidgetGroupWrapper_forWindow(aWindow) {
|
||||||
@@ -5003,13 +5028,13 @@ function XULWidgetSingleWrapper(aWidgetId, aNode, aDocument) {
|
|||||||
* 1. The default items overflow panel
|
* 1. The default items overflow panel
|
||||||
* This is where built-in default toolbar items will go to.
|
* This is where built-in default toolbar items will go to.
|
||||||
* 2. The Unified Extensions panel
|
* 2. The Unified Extensions panel
|
||||||
* This is where browser_action toolbar buttons created by WebExtensions will
|
* This is where browser_action toolbar buttons created by extensions will
|
||||||
* go to if the Unified Extensions UI is enabled - otherwise, those items will
|
* go to if the Unified Extensions UI is enabled - otherwise, those items will
|
||||||
* go to the default items overflow panel.
|
* go to the default items overflow panel.
|
||||||
*
|
*
|
||||||
* Finally, OverflowableToolbar manages the showing of the default items
|
* Finally, OverflowableToolbar manages the showing of the default items
|
||||||
* overflow panel when the associated anchor is clicked or dragged over. The
|
* overflow panel when the associated anchor is clicked or dragged over. The
|
||||||
* Unified Extensions panel is managed separately by the WebExtensions code.
|
* Unified Extensions panel is managed separately by the extension code.
|
||||||
*
|
*
|
||||||
* In theory, we could have multiple overflowable toolbars, but in practice,
|
* In theory, we could have multiple overflowable toolbars, but in practice,
|
||||||
* only the nav-bar (CustomizableUI.AREA_NAVBAR) makes use of this class.
|
* only the nav-bar (CustomizableUI.AREA_NAVBAR) makes use of this class.
|
||||||
@@ -5083,7 +5108,7 @@ class OverflowableToolbar {
|
|||||||
#defaultListPanel = null;
|
#defaultListPanel = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the the element that overflowed WebExtension browser action
|
* A reference to the the element that overflowed extension browser action
|
||||||
* toolbar items will be appended to as children upon overflow if the
|
* toolbar items will be appended to as children upon overflow if the
|
||||||
* Unified Extension UI is enabled.
|
* Unified Extension UI is enabled.
|
||||||
*
|
*
|
||||||
@@ -5138,9 +5163,9 @@ class OverflowableToolbar {
|
|||||||
* The ID of the <xul:panel> that contains the default-overflowtarget.
|
* The ID of the <xul:panel> that contains the default-overflowtarget.
|
||||||
* addon-webext-overflowbutton:
|
* addon-webext-overflowbutton:
|
||||||
* The ID of the button that is used to open and anchor the Unified
|
* The ID of the button that is used to open and anchor the Unified
|
||||||
* WebExtensions panel.
|
* Extensions panel.
|
||||||
* addon-webext-overflowtarget:
|
* addon-webext-overflowtarget:
|
||||||
* The ID of the element that overflowed WebExtension toolbar buttons will
|
* The ID of the element that overflowed extension toolbar buttons will
|
||||||
* be appended to as children if the Unified Extensions UI is enabled.
|
* be appended to as children if the Unified Extensions UI is enabled.
|
||||||
* Note that the overflowed toolbar items are moved into and out of this
|
* Note that the overflowed toolbar items are moved into and out of this
|
||||||
* overflow target, so it is definitely advisable to let OverflowableToolbar
|
* overflow target, so it is definitely advisable to let OverflowableToolbar
|
||||||
@@ -5462,7 +5487,7 @@ class OverflowableToolbar {
|
|||||||
if (
|
if (
|
||||||
lazy.gUnifiedExtensionsEnabled &&
|
lazy.gUnifiedExtensionsEnabled &&
|
||||||
webExtList &&
|
webExtList &&
|
||||||
child.classList.contains("webextension-browser-action")
|
CustomizableUI.isWebExtensionWidget(child.id)
|
||||||
) {
|
) {
|
||||||
child.setAttribute("cui-anchorid", webExtButtonID);
|
child.setAttribute("cui-anchorid", webExtButtonID);
|
||||||
webExtList.insertBefore(child, webExtList.firstElementChild);
|
webExtList.insertBefore(child, webExtList.firstElementChild);
|
||||||
@@ -5769,7 +5794,7 @@ class OverflowableToolbar {
|
|||||||
* of addon-webext-overflowtarget. If a cache already exists, that's returned
|
* of addon-webext-overflowtarget. If a cache already exists, that's returned
|
||||||
* instead. If addon-webext-overflowtarget has no value, null is returned.
|
* instead. If addon-webext-overflowtarget has no value, null is returned.
|
||||||
*
|
*
|
||||||
* @returns {Element|null} the list that overflowed WebExtension toolbar
|
* @returns {Element|null} the list that overflowed extension toolbar
|
||||||
* buttons should go to if the Unified Extensions UI is enabled, or null
|
* buttons should go to if the Unified Extensions UI is enabled, or null
|
||||||
* if no such list exists.
|
* if no such list exists.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ tags = overflowable-toolbar
|
|||||||
[browser_970511_undo_restore_default.js]
|
[browser_970511_undo_restore_default.js]
|
||||||
skip-if = verify
|
skip-if = verify
|
||||||
[browser_972267_customizationchange_events.js]
|
[browser_972267_customizationchange_events.js]
|
||||||
[browser_973641_button_addon.js]
|
|
||||||
[browser_976792_insertNodeInWindow.js]
|
[browser_976792_insertNodeInWindow.js]
|
||||||
skip-if = os == "linux"
|
skip-if = os == "linux"
|
||||||
[browser_978084_dragEnd_after_move.js]
|
[browser_978084_dragEnd_after_move.js]
|
||||||
@@ -129,6 +128,7 @@ tags = overflowable-toolbar
|
|||||||
[browser_bookmarks_toolbar_collapsed_restore_default.js]
|
[browser_bookmarks_toolbar_collapsed_restore_default.js]
|
||||||
[browser_bookmarks_toolbar_shown_newtab.js]
|
[browser_bookmarks_toolbar_shown_newtab.js]
|
||||||
[browser_bootstrapped_custom_toolbar.js]
|
[browser_bootstrapped_custom_toolbar.js]
|
||||||
|
[browser_create_button_widget.js]
|
||||||
[browser_ctrl_click_panel_opening.js]
|
[browser_ctrl_click_panel_opening.js]
|
||||||
[browser_currentset_post_reset.js]
|
[browser_currentset_post_reset.js]
|
||||||
[browser_customizemode_contextmenu_menubuttonstate.js]
|
[browser_customizemode_contextmenu_menubuttonstate.js]
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const kButton = "test_button_for_addon";
|
const kButton = "test_dynamically_created_button";
|
||||||
var initialLocation = gBrowser.currentURI.spec;
|
var initialLocation = gBrowser.currentURI.spec;
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
info("Check addon button functionality");
|
info("Check dynamically created button functionality");
|
||||||
|
|
||||||
// create mocked addon button on the navigation bar
|
// Let's create a simple button that will open about:addons.
|
||||||
let widgetSpec = {
|
let widgetSpec = {
|
||||||
id: kButton,
|
id: kButton,
|
||||||
type: "button",
|
type: "button",
|
||||||
@@ -20,13 +20,17 @@ add_task(async function() {
|
|||||||
};
|
};
|
||||||
CustomizableUI.createWidget(widgetSpec);
|
CustomizableUI.createWidget(widgetSpec);
|
||||||
CustomizableUI.addWidgetToArea(kButton, CustomizableUI.AREA_NAVBAR);
|
CustomizableUI.addWidgetToArea(kButton, CustomizableUI.AREA_NAVBAR);
|
||||||
|
ok(
|
||||||
|
!CustomizableUI.isWebExtensionWidget(kButton),
|
||||||
|
"This button should not be considered an extension widget."
|
||||||
|
);
|
||||||
|
|
||||||
// check the button's functionality in navigation bar
|
// check the button's functionality in navigation bar
|
||||||
let addonButton = document.getElementById(kButton);
|
let button = document.getElementById(kButton);
|
||||||
let navBar = document.getElementById("nav-bar");
|
let navBar = document.getElementById("nav-bar");
|
||||||
ok(addonButton, "Addon button exists");
|
ok(button, "Dynamically created button exists");
|
||||||
ok(navBar.contains(addonButton), "Addon button is in the navbar");
|
ok(navBar.contains(button), "Dynamically created button is in the navbar");
|
||||||
await checkButtonFunctionality(addonButton);
|
await checkButtonFunctionality(button);
|
||||||
|
|
||||||
resetTabs();
|
resetTabs();
|
||||||
|
|
||||||
@@ -36,21 +40,21 @@ add_task(async function() {
|
|||||||
CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
|
CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
|
||||||
);
|
);
|
||||||
ok(
|
ok(
|
||||||
!navBar.contains(addonButton),
|
!navBar.contains(button),
|
||||||
"Addon button was removed from the browser bar"
|
"Dynamically created button was removed from the browser bar"
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitForOverflowButtonShown();
|
await waitForOverflowButtonShown();
|
||||||
|
|
||||||
// check the addon button's functionality in the Panel Menu
|
// check the button's functionality in the Overflow Panel.
|
||||||
await document.getElementById("nav-bar").overflowable.show();
|
await document.getElementById("nav-bar").overflowable.show();
|
||||||
var panelMenu = document.getElementById("widget-overflow-mainView");
|
var panelMenu = document.getElementById("widget-overflow-mainView");
|
||||||
let addonButtonInPanel = panelMenu.getElementsByAttribute("id", kButton);
|
let buttonInPanel = panelMenu.getElementsByAttribute("id", kButton);
|
||||||
ok(
|
ok(
|
||||||
panelMenu.contains(addonButton),
|
panelMenu.contains(button),
|
||||||
"Addon button was added to the Panel Menu"
|
"Dynamically created button was added to the Panel Menu"
|
||||||
);
|
);
|
||||||
await checkButtonFunctionality(addonButtonInPanel[0]);
|
await checkButtonFunctionality(buttonInPanel[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function asyncCleanup() {
|
add_task(async function asyncCleanup() {
|
||||||
@@ -196,6 +196,7 @@ this.browserAction = class extends ExtensionAPIPersistent {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
viewId: this.viewId,
|
viewId: this.viewId,
|
||||||
type: "view",
|
type: "view",
|
||||||
|
webExtension: true,
|
||||||
removable: true,
|
removable: true,
|
||||||
label: this.action.getProperty(null, "title"),
|
label: this.action.getProperty(null, "title"),
|
||||||
tooltiptext: this.action.getProperty(null, "title"),
|
tooltiptext: this.action.getProperty(null, "title"),
|
||||||
|
|||||||
@@ -67,11 +67,14 @@ async function testAction(manifest_version) {
|
|||||||
await extension.startup();
|
await extension.startup();
|
||||||
ExtensionTestUtils.failOnSchemaWarnings(true);
|
ExtensionTestUtils.failOnSchemaWarnings(true);
|
||||||
|
|
||||||
|
let widgetGroup = getBrowserActionWidget(extension);
|
||||||
|
ok(widgetGroup.webExtension, "The extension property was set.");
|
||||||
|
|
||||||
// Do this a few times to make sure the pop-up is reloaded each time.
|
// Do this a few times to make sure the pop-up is reloaded each time.
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
clickBrowserAction(extension);
|
clickBrowserAction(extension);
|
||||||
|
|
||||||
let widget = getBrowserActionWidget(extension).forWindow(window);
|
let widget = widgetGroup.forWindow(window);
|
||||||
let image = getComputedStyle(widget.node).listStyleImage;
|
let image = getComputedStyle(widget.node).listStyleImage;
|
||||||
|
|
||||||
ok(image.includes("/icon.png"), "The extension's icon is used");
|
ok(image.includes("/icon.png"), "The extension's icon is used");
|
||||||
|
|||||||
Reference in New Issue
Block a user