forked from mirrors/gecko-dev
344 lines
11 KiB
JavaScript
344 lines
11 KiB
JavaScript
const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
|
|
|
// MockAddon mimics the AddonInternal interface and MockProvider implements
|
|
// just enough of the AddonManager provider interface to make it look like
|
|
// we have sideloaded webextensions so the sideloading flow can be tested.
|
|
|
|
// MockAddon -> callback
|
|
let setCallbacks = new Map();
|
|
|
|
class MockAddon {
|
|
constructor(props) {
|
|
this._userDisabled = false;
|
|
this.pendingOperations = 0;
|
|
this.type = "extension";
|
|
|
|
for (let name in props) {
|
|
if (name == "userDisabled") {
|
|
this._userDisabled = props[name];
|
|
}
|
|
this[name] = props[name];
|
|
}
|
|
}
|
|
|
|
markAsSeen() {
|
|
this.seen = true;
|
|
}
|
|
|
|
get userDisabled() {
|
|
return this._userDisabled;
|
|
}
|
|
|
|
set userDisabled(val) {
|
|
this._userDisabled = val;
|
|
AddonManagerPrivate.callAddonListeners(val ? "onDisabled" : "onEnabled", this);
|
|
let fn = setCallbacks.get(this);
|
|
if (fn) {
|
|
setCallbacks.delete(this);
|
|
fn(val);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
get permissions() {
|
|
return this._userDisabled ? AddonManager.PERM_CAN_ENABLE : AddonManager.PERM_CAN_DISABLE;
|
|
}
|
|
}
|
|
|
|
class MockProvider {
|
|
constructor(...addons) {
|
|
this.addons = new Set(addons);
|
|
}
|
|
|
|
startup() { }
|
|
shutdown() { }
|
|
|
|
getAddonByID(id, callback) {
|
|
for (let addon of this.addons) {
|
|
if (addon.id == id) {
|
|
callback(addon);
|
|
return;
|
|
}
|
|
}
|
|
callback(null);
|
|
}
|
|
|
|
getAddonsByTypes(types, callback) {
|
|
let addons = [];
|
|
if (!types || types.includes("extension")) {
|
|
addons = [...this.addons];
|
|
}
|
|
callback(addons);
|
|
}
|
|
}
|
|
|
|
function promisePopupNotificationShown(name) {
|
|
return new Promise(resolve => {
|
|
function popupshown() {
|
|
let notification = PopupNotifications.getNotification(name);
|
|
if (!notification) {
|
|
return;
|
|
}
|
|
|
|
ok(notification, `${name} notification shown`);
|
|
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
|
|
|
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
|
|
resolve(PopupNotifications.panel.firstChild);
|
|
}
|
|
|
|
PopupNotifications.panel.addEventListener("popupshown", popupshown);
|
|
});
|
|
}
|
|
|
|
function promiseSetDisabled(addon) {
|
|
return new Promise(resolve => {
|
|
setCallbacks.set(addon, resolve);
|
|
});
|
|
}
|
|
|
|
add_task(function* () {
|
|
// XXX remove this when prompts are enabled by default
|
|
yield SpecialPowers.pushPrefEnv({set: [
|
|
["extensions.webextPermissionPrompts", true],
|
|
]});
|
|
|
|
// ICON_URL wouldn't ever appear as an actual webextension icon, but
|
|
// we're just mocking out the addon here, so all we care about is that
|
|
// that it propagates correctly to the popup.
|
|
const ICON_URL = "chrome://mozapps/skin/extensions/category-extensions.svg";
|
|
const DEFAULT_ICON_URL = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
|
|
|
const ID1 = "addon1@tests.mozilla.org";
|
|
let mock1 = new MockAddon({
|
|
id: ID1,
|
|
name: "Test 1",
|
|
userDisabled: true,
|
|
seen: false,
|
|
userPermissions: {
|
|
permissions: ["history"],
|
|
hosts: ["https://*/*"],
|
|
},
|
|
iconURL: ICON_URL,
|
|
});
|
|
|
|
const ID2 = "addon2@tests.mozilla.org";
|
|
let mock2 = new MockAddon({
|
|
id: ID2,
|
|
name: "Test 2",
|
|
userDisabled: true,
|
|
seen: false,
|
|
userPermissions: {
|
|
permissions: [],
|
|
hosts: [],
|
|
},
|
|
});
|
|
|
|
const ID3 = "addon3@tests.mozilla.org";
|
|
let mock3 = new MockAddon({
|
|
id: ID3,
|
|
name: "Test 3",
|
|
isWebExtension: true,
|
|
userDisabled: true,
|
|
seen: false,
|
|
userPermissions: {
|
|
permissions: [],
|
|
hosts: ["<all_urls>"],
|
|
}
|
|
});
|
|
|
|
const ID4 = "addon4@tests.mozilla.org";
|
|
let mock4 = new MockAddon({
|
|
id: ID4,
|
|
name: "Test 4",
|
|
isWebExtension: true,
|
|
userDisabled: true,
|
|
seen: false,
|
|
userPermissions: {
|
|
permissions: [],
|
|
hosts: ["<all_urls>"],
|
|
}
|
|
});
|
|
|
|
let provider = new MockProvider(mock1, mock2, mock3, mock4);
|
|
AddonManagerPrivate.registerProvider(provider, [{
|
|
id: "extension",
|
|
name: "Extensions",
|
|
uiPriority: 4000,
|
|
flags: AddonManager.TYPE_UI_VIEW_LIST |
|
|
AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL,
|
|
}]);
|
|
registerCleanupFunction(function*() {
|
|
AddonManagerPrivate.unregisterProvider(provider);
|
|
|
|
// clear out ExtensionsUI state about sideloaded extensions so
|
|
// subsequent tests don't get confused.
|
|
ExtensionsUI.sideloaded.clear();
|
|
ExtensionsUI.emit("change");
|
|
});
|
|
|
|
// Navigate away from the starting page to force about:addons to load
|
|
// in a new tab during the tests below.
|
|
gBrowser.selectedBrowser.loadURI("about:robots");
|
|
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
|
|
|
registerCleanupFunction(function*() {
|
|
// Return to about:blank when we're done
|
|
gBrowser.selectedBrowser.loadURI("about:blank");
|
|
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
|
});
|
|
|
|
let changePromise = new Promise(resolve => {
|
|
ExtensionsUI.on("change", function listener() {
|
|
ExtensionsUI.off("change", listener);
|
|
resolve();
|
|
});
|
|
});
|
|
ExtensionsUI._checkForSideloaded();
|
|
yield changePromise;
|
|
|
|
// Check for the addons badge on the hamburger menu
|
|
let menuButton = document.getElementById("PanelUI-menu-button");
|
|
is(menuButton.getAttribute("badge-status"), "addon-alert", "Should have addon alert badge");
|
|
|
|
// Find the menu entries for sideloaded extensions
|
|
yield PanelUI.show();
|
|
|
|
let addons = document.getElementById("PanelUI-footer-addons");
|
|
is(addons.children.length, 4, "Have 4 menu entries for sideloaded extensions");
|
|
|
|
// Click the first sideloaded extension
|
|
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
|
addons.children[0].click();
|
|
|
|
// When we get the permissions prompt, we should be at the extensions
|
|
// list in about:addons
|
|
let panel = yield popupPromise;
|
|
is(gBrowser.currentURI.spec, "about:addons", "Foreground tab is at about:addons");
|
|
|
|
const VIEW = "addons://list/extension";
|
|
let win = gBrowser.selectedBrowser.contentWindow;
|
|
ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
|
|
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
|
|
|
// Check the contents of the notification, then choose "Cancel"
|
|
let icon = panel.getAttribute("icon");
|
|
is(icon, ICON_URL, "Permissions notification has the addon icon");
|
|
|
|
let disablePromise = promiseSetDisabled(mock1);
|
|
panel.secondaryButton.click();
|
|
|
|
let value = yield disablePromise;
|
|
is(value, true, "Addon should remain disabled");
|
|
|
|
let [addon1, addon2, addon3, addon4] = yield AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
|
|
ok(addon1.seen, "Addon should be marked as seen");
|
|
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
|
is(addon2.userDisabled, true, "Addon 2 should still be disabled");
|
|
is(addon3.userDisabled, true, "Addon 3 should still be disabled");
|
|
is(addon4.userDisabled, true, "Addon 4 should still be disabled");
|
|
|
|
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
|
|
// Should still have 3 entries in the hamburger menu
|
|
yield PanelUI.show();
|
|
|
|
addons = document.getElementById("PanelUI-footer-addons");
|
|
is(addons.children.length, 3, "Have 3 menu entries for sideloaded extensions");
|
|
|
|
// Click the second sideloaded extension and wait for the notification
|
|
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
|
addons.children[0].click();
|
|
panel = yield popupPromise;
|
|
|
|
// Again we should be at the extentions list in about:addons
|
|
is(gBrowser.currentURI.spec, "about:addons", "Foreground tab is at about:addons");
|
|
|
|
win = gBrowser.selectedBrowser.contentWindow;
|
|
ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
|
|
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
|
|
|
// Check the notification contents, this time accept the install
|
|
icon = panel.getAttribute("icon");
|
|
is(icon, DEFAULT_ICON_URL, "Permissions notification has the default icon");
|
|
disablePromise = promiseSetDisabled(mock2);
|
|
panel.button.click();
|
|
|
|
value = yield disablePromise;
|
|
is(value, false, "Addon should be set to enabled");
|
|
|
|
[addon1, addon2, addon3, addon4] = yield AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
|
|
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
|
is(addon2.userDisabled, false, "Addon 2 should now be enabled");
|
|
is(addon3.userDisabled, true, "Addon 3 should still be disabled");
|
|
is(addon4.userDisabled, true, "Addon 4 should still be disabled");
|
|
|
|
// Should still have 2 entries in the hamburger menu
|
|
yield PanelUI.show();
|
|
|
|
addons = document.getElementById("PanelUI-footer-addons");
|
|
is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions");
|
|
|
|
// Close the hamburger menu and go directly to the addons manager
|
|
yield PanelUI.hide();
|
|
|
|
win = yield BrowserOpenAddonsMgr(VIEW);
|
|
|
|
let list = win.document.getElementById("addon-list");
|
|
|
|
// Make sure XBL bindings are applied
|
|
list.clientHeight;
|
|
|
|
let item = list.children.find(_item => _item.value == ID3);
|
|
ok(item, "Found entry for sideloaded extension in about:addons");
|
|
item.scrollIntoView({behavior: "instant"});
|
|
|
|
ok(is_visible(item._enableBtn), "Enable button is visible for sideloaded extension");
|
|
ok(is_hidden(item._disableBtn), "Disable button is not visible for sideloaded extension");
|
|
|
|
// When clicking enable we should see the permissions notification
|
|
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
|
BrowserTestUtils.synthesizeMouseAtCenter(item._enableBtn, {},
|
|
gBrowser.selectedBrowser);
|
|
panel = yield popupPromise;
|
|
|
|
// Accept the permissions
|
|
disablePromise = promiseSetDisabled(mock3);
|
|
panel.button.click();
|
|
value = yield disablePromise;
|
|
is(value, false, "userDisabled should be set on addon 3");
|
|
|
|
addon3 = yield AddonManager.getAddonByID(ID3);
|
|
is(addon3.userDisabled, false, "Addon 3 should be enabled");
|
|
|
|
// Should still have 1 entry in the hamburger menu
|
|
yield PanelUI.show();
|
|
|
|
addons = document.getElementById("PanelUI-footer-addons");
|
|
is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
|
|
|
|
// Close the hamburger menu and go to the detail page for this addon
|
|
yield PanelUI.hide();
|
|
|
|
win = yield BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(ID4)}`);
|
|
let button = win.document.getElementById("detail-enable-btn");
|
|
|
|
// When clicking enable we should see the permissions notification
|
|
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
|
BrowserTestUtils.synthesizeMouseAtCenter(button, {},
|
|
gBrowser.selectedBrowser);
|
|
panel = yield popupPromise;
|
|
|
|
// Accept the permissions
|
|
disablePromise = promiseSetDisabled(mock4);
|
|
panel.button.click();
|
|
value = yield disablePromise;
|
|
is(value, false, "userDisabled should be set on addon 4");
|
|
|
|
addon4 = yield AddonManager.getAddonByID(ID4);
|
|
is(addon4.userDisabled, false, "Addon 4 should be enabled");
|
|
|
|
isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
|
|
|
|
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|