зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1401610 - Add "Remove Extension" context menu item to browserAction. r=dao
Differential Revision: https://phabricator.services.mozilla.com/D3565 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
03af62563c
Коммит
29fc9939ad
|
@ -16,6 +16,7 @@ const {WebExtensionPolicy} = Cu.getGlobalForObject(Services);
|
|||
// lazy module getters
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||
BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
|
||||
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
|
||||
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
|
||||
|
@ -6278,44 +6279,66 @@ function UpdateCurrentCharset(target) {
|
|||
}
|
||||
}
|
||||
|
||||
function UpdateDownloadsAutoHide(popup) {
|
||||
let checkbox = popup.querySelector(".customize-context-autoHide");
|
||||
let isDownloads = popup.triggerNode && ["downloads-button", "wrapper-downloads-button"].includes(popup.triggerNode.id);
|
||||
checkbox.hidden = !isDownloads;
|
||||
if (this.window.DownloadsButton.autoHideDownloadsButton) {
|
||||
checkbox.setAttribute("checked", "true");
|
||||
} else {
|
||||
checkbox.removeAttribute("checked");
|
||||
}
|
||||
}
|
||||
var ToolbarContextMenu = {
|
||||
updateDownloadsAutoHide(popup) {
|
||||
let checkbox = popup.querySelector(".customize-context-autoHide");
|
||||
let isDownloads = popup.triggerNode && ["downloads-button", "wrapper-downloads-button"].includes(popup.triggerNode.id);
|
||||
checkbox.hidden = !isDownloads;
|
||||
if (DownloadsButton.autoHideDownloadsButton) {
|
||||
checkbox.setAttribute("checked", "true");
|
||||
} else {
|
||||
checkbox.removeAttribute("checked");
|
||||
}
|
||||
},
|
||||
|
||||
function onDownloadsAutoHideChange(event) {
|
||||
let autoHide = event.target.getAttribute("checked") == "true";
|
||||
Services.prefs.setBoolPref("browser.download.autohideButton", autoHide);
|
||||
}
|
||||
onDownloadsAutoHideChange(event) {
|
||||
let autoHide = event.target.getAttribute("checked") == "true";
|
||||
Services.prefs.setBoolPref("browser.download.autohideButton", autoHide);
|
||||
},
|
||||
|
||||
function getUnwrappedTriggerNode(popup) {
|
||||
// Toolbar buttons are wrapped in customize mode. Unwrap if necessary.
|
||||
let {triggerNode} = popup;
|
||||
if (triggerNode && gCustomizeMode.isWrappedToolbarItem(triggerNode)) {
|
||||
return triggerNode.firstElementChild;
|
||||
}
|
||||
return triggerNode;
|
||||
}
|
||||
_getUnwrappedTriggerNode(popup) {
|
||||
// Toolbar buttons are wrapped in customize mode. Unwrap if necessary.
|
||||
let {triggerNode} = popup;
|
||||
if (triggerNode && gCustomizeMode.isWrappedToolbarItem(triggerNode)) {
|
||||
return triggerNode.firstElementChild;
|
||||
}
|
||||
return triggerNode;
|
||||
},
|
||||
|
||||
function UpdateManageExtension(popup) {
|
||||
let checkbox = popup.querySelector(".customize-context-manageExtension");
|
||||
let separator = checkbox.nextElementSibling;
|
||||
let node = getUnwrappedTriggerNode(popup);
|
||||
let isWebExt = node && node.hasAttribute("data-extensionid");
|
||||
checkbox.hidden = separator.hidden = !isWebExt;
|
||||
}
|
||||
updateExtension(popup) {
|
||||
let removeExtension = popup.querySelector(".customize-context-removeExtension");
|
||||
let manageExtension = removeExtension.nextElementSibling;
|
||||
let separator = manageExtension.nextElementSibling;
|
||||
let node = this._getUnwrappedTriggerNode(popup);
|
||||
let isWebExt = node && node.hasAttribute("data-extensionid");
|
||||
removeExtension.hidden = manageExtension.hidden = separator.hidden = !isWebExt;
|
||||
},
|
||||
|
||||
function openAboutAddonsForContextAction(popup) {
|
||||
let id = getUnwrappedTriggerNode(popup).getAttribute("data-extensionid");
|
||||
let viewID = "addons://detail/" + encodeURIComponent(id);
|
||||
BrowserOpenAddonsMgr(viewID);
|
||||
}
|
||||
async removeExtensionForContextAction(popup) {
|
||||
let id = this._getUnwrappedTriggerNode(popup).getAttribute("data-extensionid");
|
||||
let addon = await AddonManager.getAddonByID(id);
|
||||
let {name} = addon;
|
||||
let brand = document.getElementById("bundle_brand").getString("brandShorterName");
|
||||
let {getFormattedString, getString} = gNavigatorBundle;
|
||||
let title = getFormattedString("webext.remove.confirmation.title", [name]);
|
||||
let message = getFormattedString("webext.remove.confirmation.message", [name, brand]);
|
||||
let btnTitle = getString("webext.remove.confirmation.button");
|
||||
let {BUTTON_TITLE_IS_STRING: titleString, BUTTON_TITLE_CANCEL: titleCancel,
|
||||
BUTTON_POS_0, BUTTON_POS_1, confirmEx} = Services.prompt;
|
||||
let btnFlags = BUTTON_POS_0 * titleString + BUTTON_POS_1 * titleCancel;
|
||||
let response = confirmEx(null, title, message, btnFlags, btnTitle, null, null, null,
|
||||
{value: 0});
|
||||
if (response == 0) {
|
||||
addon.uninstall();
|
||||
}
|
||||
},
|
||||
|
||||
openAboutAddonsForContextAction(popup) {
|
||||
let id = this._getUnwrappedTriggerNode(popup).getAttribute("data-extensionid");
|
||||
let viewID = "addons://detail/" + encodeURIComponent(id);
|
||||
BrowserOpenAddonsMgr(viewID);
|
||||
},
|
||||
};
|
||||
|
||||
var gPageStyleMenu = {
|
||||
// This maps from a <browser> element (or, more specifically, a
|
||||
|
|
|
@ -365,8 +365,13 @@ xmlns="http://www.w3.org/1999/xhtml"
|
|||
</panel>
|
||||
|
||||
<menupopup id="toolbar-context-menu"
|
||||
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator')); UpdateDownloadsAutoHide(this); UpdateManageExtension(this)">
|
||||
<menuitem oncommand="openAboutAddonsForContextAction(this.parentElement)"
|
||||
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator')); ToolbarContextMenu.updateDownloadsAutoHide(this); ToolbarContextMenu.updateExtension(this)">
|
||||
<menuitem oncommand="ToolbarContextMenu.removeExtensionForContextAction(this.parentElement)"
|
||||
accesskey="&customizeMenu.removeExtension.accesskey;"
|
||||
label="&customizeMenu.removeExtension.label;"
|
||||
contexttype="toolbaritem"
|
||||
class="customize-context-removeExtension"/>
|
||||
<menuitem oncommand="ToolbarContextMenu.openAboutAddonsForContextAction(this.parentElement)"
|
||||
accesskey="&customizeMenu.manageExtension.accesskey;"
|
||||
label="&customizeMenu.manageExtension.label;"
|
||||
contexttype="toolbaritem"
|
||||
|
@ -377,7 +382,7 @@ xmlns="http://www.w3.org/1999/xhtml"
|
|||
label="&customizeMenu.pinToOverflowMenu.label;"
|
||||
contexttype="toolbaritem"
|
||||
class="customize-context-moveToPanel"/>
|
||||
<menuitem oncommand="onDownloadsAutoHideChange(event)"
|
||||
<menuitem oncommand="ToolbarContextMenu.onDownloadsAutoHideChange(event)"
|
||||
type="checkbox"
|
||||
accesskey="&customizeMenu.autoHideDownloadsButton.accesskey;"
|
||||
label="&customizeMenu.autoHideDownloadsButton.label;"
|
||||
|
|
|
@ -29,8 +29,13 @@
|
|||
when hover styles overlap. See https://bugzilla.mozilla.org/show_bug.cgi?id=1378427 .
|
||||
-->
|
||||
<menupopup id="customizationPanelItemContextMenu"
|
||||
onpopupshowing="gCustomizeMode.onPanelContextMenuShowing(event); UpdateManageExtension(this)">
|
||||
<menuitem oncommand="openAboutAddonsForContextAction(this.parentElement)"
|
||||
onpopupshowing="gCustomizeMode.onPanelContextMenuShowing(event); ToolbarContextMenu.updateExtension(this)">
|
||||
<menuitem oncommand="ToolbarContextMenu.removeExtensionForContextAction(this.parentElement)"
|
||||
accesskey="&customizeMenu.removeExtension.accesskey;"
|
||||
label="&customizeMenu.removeExtension.label;"
|
||||
contexttype="toolbaritem"
|
||||
class="customize-context-removeExtension"/>
|
||||
<menuitem oncommand="ToolbarContextMenu.openAboutAddonsForContextAction(this.parentElement)"
|
||||
accesskey="&customizeMenu.manageExtension.accesskey;"
|
||||
label="&customizeMenu.manageExtension.label;"
|
||||
contexttype="toolbaritem"
|
||||
|
|
|
@ -105,6 +105,20 @@ add_task(async function browseraction_popup_image_contextmenu() {
|
|||
await extension.unload();
|
||||
});
|
||||
|
||||
function openContextMenu(menuId, targetId) {
|
||||
return openChromeContextMenu(menuId, "#" + CSS.escape(targetId));
|
||||
}
|
||||
|
||||
function waitForElementShown(element) {
|
||||
let win = element.ownerGlobal;
|
||||
let dwu = win.windowUtils;
|
||||
return BrowserTestUtils.waitForCondition(() => {
|
||||
info("Waiting for overflow button to have non-0 size");
|
||||
let bounds = dwu.getBoundsWithoutFlushing(element);
|
||||
return bounds.width > 0 && bounds.height > 0;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function browseraction_contextmenu_manage_extension() {
|
||||
let id = "addon_id@example.com";
|
||||
let buttonId = `${makeWidgetId(id)}-browser-action`;
|
||||
|
@ -125,17 +139,16 @@ add_task(async function browseraction_contextmenu_manage_extension() {
|
|||
},
|
||||
});
|
||||
|
||||
function openContextMenu(menuId, targetId) {
|
||||
return openChromeContextMenu(menuId, "#" + CSS.escape(targetId));
|
||||
}
|
||||
|
||||
function checkVisibility(menu, visible) {
|
||||
let removeExtension = menu.querySelector(".customize-context-removeExtension");
|
||||
let manageExtension = menu.querySelector(".customize-context-manageExtension");
|
||||
let separator = manageExtension.nextElementSibling;
|
||||
|
||||
info(`Check visibility`);
|
||||
is(manageExtension.hidden, !visible, `Manage Extension should be ${visible ? "visible" : "hidden"}`);
|
||||
is(separator.hidden, !visible, `Separator after Manage Extension should be ${visible ? "visible" : "hidden"}`);
|
||||
let expected = visible ? "visible" : "hidden";
|
||||
is(removeExtension.hidden, !visible, `Remove Extension should be ${expected}`);
|
||||
is(manageExtension.hidden, !visible, `Manage Extension should be ${expected}`);
|
||||
is(separator.hidden, !visible, `Separator after Manage Extension should be ${expected}`);
|
||||
}
|
||||
|
||||
async function testContextMenu(menuId, customizing) {
|
||||
|
@ -163,16 +176,6 @@ add_task(async function browseraction_contextmenu_manage_extension() {
|
|||
return menu;
|
||||
}
|
||||
|
||||
function waitForElementShown(element) {
|
||||
let win = element.ownerGlobal;
|
||||
let dwu = win.windowUtils;
|
||||
return BrowserTestUtils.waitForCondition(() => {
|
||||
info("Waiting for overflow button to have non-0 size");
|
||||
let bounds = dwu.getBoundsWithoutFlushing(element);
|
||||
return bounds.width > 0 && bounds.height > 0;
|
||||
});
|
||||
}
|
||||
|
||||
async function main(customizing) {
|
||||
if (customizing) {
|
||||
info("Enter customize mode");
|
||||
|
@ -247,3 +250,117 @@ add_task(async function browseraction_contextmenu_manage_extension() {
|
|||
gBrowser.removeTab(dummyTab);
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function browseraction_contextmenu_remove_extension() {
|
||||
let id = "addon_id@example.com";
|
||||
let name = "Awesome Add-on";
|
||||
let buttonId = `${makeWidgetId(id)}-browser-action`;
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
name,
|
||||
"applications": {
|
||||
"gecko": {id},
|
||||
},
|
||||
"browser_action": {},
|
||||
},
|
||||
useAddonManager: "temporary",
|
||||
});
|
||||
let brand = Services.strings.createBundle("chrome://branding/locale/brand.properties")
|
||||
.GetStringFromName("brandShorterName");
|
||||
let {prompt} = Services;
|
||||
let promptService = {
|
||||
_response: 1,
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]),
|
||||
confirmEx: function(...args) {
|
||||
promptService._confirmExArgs = args;
|
||||
return promptService._response;
|
||||
},
|
||||
};
|
||||
Services.prompt = promptService;
|
||||
registerCleanupFunction(() => {
|
||||
Services.prompt = prompt;
|
||||
});
|
||||
|
||||
async function testContextMenu(menuId, customizing) {
|
||||
info(`Open browserAction context menu in ${menuId}`);
|
||||
let menu = await openContextMenu(menuId, buttonId);
|
||||
|
||||
info(`Choosing 'Remove Extension' in ${menuId} should show confirm dialog`);
|
||||
let removeExtension = menu.querySelector(".customize-context-removeExtension");
|
||||
await closeChromeContextMenu(menuId, removeExtension);
|
||||
is(promptService._confirmExArgs[1], `Remove ${name}`);
|
||||
is(promptService._confirmExArgs[2], `Remove ${name} from ${brand}?`);
|
||||
is(promptService._confirmExArgs[4], "Remove");
|
||||
return menu;
|
||||
}
|
||||
|
||||
async function main(customizing) {
|
||||
if (customizing) {
|
||||
info("Enter customize mode");
|
||||
let customizationReady = BrowserTestUtils.waitForEvent(gNavToolbox, "customizationready");
|
||||
gCustomizeMode.enter();
|
||||
await customizationReady;
|
||||
}
|
||||
|
||||
info("Test toolbar context menu in browserAction");
|
||||
await testContextMenu("toolbar-context-menu", customizing);
|
||||
|
||||
info("Pin the browserAction and another button to the overflow menu");
|
||||
CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
|
||||
info("Wait until the overflow menu is ready");
|
||||
let overflowButton = document.getElementById("nav-bar-overflow-button");
|
||||
let icon = document.getAnonymousElementByAttribute(overflowButton, "class", "toolbarbutton-icon");
|
||||
await waitForElementShown(icon);
|
||||
|
||||
if (!customizing) {
|
||||
info("Open overflow menu");
|
||||
let menu = document.getElementById("widget-overflow");
|
||||
let shown = BrowserTestUtils.waitForEvent(menu, "popupshown");
|
||||
overflowButton.click();
|
||||
await shown;
|
||||
}
|
||||
|
||||
info("Test overflow menu context menu in browserAction");
|
||||
await testContextMenu("customizationPanelItemContextMenu", customizing);
|
||||
|
||||
info("Restore initial state");
|
||||
CustomizableUI.addWidgetToArea(buttonId, CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
if (customizing) {
|
||||
info("Exit customize mode");
|
||||
let afterCustomization = BrowserTestUtils.waitForEvent(gNavToolbox, "aftercustomization");
|
||||
gCustomizeMode.exit();
|
||||
await afterCustomization;
|
||||
}
|
||||
}
|
||||
|
||||
await extension.startup();
|
||||
|
||||
info("Run tests in normal mode");
|
||||
await main(false);
|
||||
|
||||
info("Run tests in customize mode");
|
||||
await main(true);
|
||||
|
||||
let addon = await AddonManager.getAddonByID(id);
|
||||
ok(addon, "Addon is still installed");
|
||||
|
||||
promptService._response = 0;
|
||||
let uninstalled = new Promise((resolve) => {
|
||||
AddonManager.addAddonListener({
|
||||
onUninstalled(addon) {
|
||||
is(addon.id, id, "The expected add-on has been uninstalled");
|
||||
AddonManager.removeAddonListener(this);
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
await testContextMenu("toolbar-context-menu", false);
|
||||
await uninstalled;
|
||||
|
||||
addon = await AddonManager.getAddonByID(id);
|
||||
ok(!addon, "Addon has been uninstalled");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
|
|
@ -443,6 +443,8 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
|||
<!ENTITY customizeMenu.autoHideDownloadsButton.accesskey "A">
|
||||
<!ENTITY customizeMenu.manageExtension.label "Manage Extension">
|
||||
<!ENTITY customizeMenu.manageExtension.accesskey "E">
|
||||
<!ENTITY customizeMenu.removeExtension.label "Remove Extension">
|
||||
<!ENTITY customizeMenu.removeExtension.accesskey "v">
|
||||
|
||||
<!-- LOCALIZATION NOTE (moreMenu.label) This label is used in the new Photon
|
||||
app (hamburger) menu. When clicked, it opens a subview that contains
|
||||
|
|
|
@ -158,6 +158,15 @@ webext.defaultSearchYes.accessKey=Y
|
|||
webext.defaultSearchNo.label=No
|
||||
webext.defaultSearchNo.accessKey=N
|
||||
|
||||
# LOCALIZATION NOTE (webext.remove.confirmation.title)
|
||||
# %S is the name of the extension which is about to be removed.
|
||||
webext.remove.confirmation.title=Remove %S
|
||||
# LOCALIZATION NOTE (webext.remove.confirmation.message)
|
||||
# %1$S is the name of the extension which is about to be removed.
|
||||
# %2$S is brandShorterName
|
||||
webext.remove.confirmation.message=Remove %1$S from %2$S?
|
||||
webext.remove.confirmation.button=Remove
|
||||
|
||||
# LOCALIZATION NOTE (addonPostInstall.message1)
|
||||
# %1$S is replaced with the localized named of the extension that was
|
||||
# just installed.
|
||||
|
|
Загрузка…
Ссылка в новой задаче