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:
Oriol Brufau 2018-09-05 10:02:52 +00:00
Родитель 03af62563c
Коммит 29fc9939ad
6 изменённых файлов: 216 добавлений и 55 удалений

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

@ -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.