Bug 1282981 - Implement management.get and getAll, r?mixedpuppy
This updates the existing implementation of browser.management.getAll to allow both themes and extensions
to be returned. It also adds an API for browser.management.get.
MozReview-Commit-ID: C1v2FqQqNux
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -96,16 +96,17 @@ webextPerms.optionalPermsDeny.label=Deny
webextPerms.optionalPermsDeny.accessKey=D
webextPerms.description.bookmarks=Read and modify bookmarks
webextPerms.description.clipboardRead=Get data from the clipboard
webextPerms.description.clipboardWrite=Input data to the clipboard
webextPerms.description.downloads=Download files and read and modify the browser’s download history
webextPerms.description.geolocation=Access your location
webextPerms.description.history=Access browsing history
+webextPerms.description.management=Monitor extension usage and manage themes
# LOCALIZATION NOTE (webextPerms.description.nativeMessaging)
# %S will be replaced with the name of the application
webextPerms.description.nativeMessaging=Exchange messages with programs other than %S
webextPerms.description.notifications=Display notifications to you
webextPerms.description.privacy=Read and modify privacy settings
webextPerms.description.sessions=Access recently closed tabs
webextPerms.description.tabs=Access browser tabs
webextPerms.description.topSites=Access browsing history
--- a/toolkit/components/extensions/ext-management.js
+++ b/toolkit/components/extensions/ext-management.js
@@ -84,56 +84,60 @@ function getExtensionInfoForAddon(extens
if (addon.updateURL) {
extInfo.updateUrl = addon.updateURL;
}
return extInfo;
}
const listenerMap = new WeakMap();
// Some management APIs are intentionally limited.
-const allowedTypes = ["theme"];
+const allowedTypes = ["theme", "extension"];
class AddonListener {
constructor() {
AddonManager.addAddonListener(this);
EventEmitter.decorate(this);
}
release() {
AddonManager.removeAddonListener(this);
}
getExtensionInfo(addon) {
let ext = addon.isWebExtension && GlobalManager.extensionMap.get(addon.id);
return getExtensionInfoForAddon(ext, addon);
}
+ checkAllowed(addon) {
+ return !addon.isSystem && allowedTypes.includes(addon.type);
+ }
+
onEnabled(addon) {
- if (!allowedTypes.includes(addon.type)) {
+ if (!this.checkAllowed(addon)) {
return;
}
this.emit("onEnabled", this.getExtensionInfo(addon));
}
onDisabled(addon) {
- if (!allowedTypes.includes(addon.type)) {
+ if (!this.checkAllowed(addon)) {
return;
}
this.emit("onDisabled", this.getExtensionInfo(addon));
}
onInstalled(addon) {
- if (!allowedTypes.includes(addon.type)) {
+ if (!this.checkAllowed(addon)) {
return;
}
this.emit("onInstalled", this.getExtensionInfo(addon));
}
onUninstalled(addon) {
- if (!allowedTypes.includes(addon.type)) {
+ if (!this.checkAllowed(addon)) {
return;
}
this.emit("onUninstalled", this.getExtensionInfo(addon));
}
}
let addonListener;
@@ -156,19 +160,26 @@ function getListener(extension, context)
return addonListener;
}
this.management = class extends ExtensionAPI {
getAPI(context) {
let {extension} = context;
return {
management: {
+ async get(id) {
+ let addon = await AddonManager.getAddonByID(id);
+ if (!addon.isSystem) {
+ return getExtensionInfoForAddon(extension, addon);
+ }
+ },
+
async getAll() {
let addons = await AddonManager.getAddonsByTypes(allowedTypes);
- return addons.map(addon => {
+ return addons.filter(addon => !addon.isSystem).map(addon => {
// If the extension is enabled get it and use it for more data.
let ext = addon.isWebExtension && GlobalManager.extensionMap.get(addon.id);
return getExtensionInfoForAddon(ext, addon);
});
},
async getSelf() {
let addon = await AddonManager.getAddonByID(extension.id);
@@ -199,19 +210,22 @@ this.management = class extends Extensio
addon.uninstall();
},
async setEnabled(id, enabled) {
let addon = await AddonManager.getAddonByID(id);
if (!addon) {
throw new ExtensionError(`No such addon ${id}`);
}
- if (!allowedTypes.includes(addon.type)) {
+ if (addon.type !== "theme") {
throw new ExtensionError("setEnabled applies only to theme addons");
}
+ if (addon.isSystem) {
+ throw new ExtensionError("setEnabled cannot be used with a system addon");
+ }
addon.userDisabled = !enabled;
},
onDisabled: new SingletonEventManager(context, "management.onDisabled", fire => {
let listener = (event, data) => {
fire.async(data);
};
--- a/toolkit/components/extensions/schemas/management.json
+++ b/toolkit/components/extensions/schemas/management.json
@@ -170,17 +170,16 @@
]
}
]
},
{
"name": "get",
"type": "function",
"permissions": ["management"],
- "unsupported": true,
"description": "Returns information about the installed extension that has the given ID.",
"async": "callback",
"parameters": [
{
"name": "id",
"$ref": "manifest.ExtensionID",
"description": "The ID from an item of $(ref:management.ExtensionInfo)."
},
--- a/toolkit/components/extensions/test/browser/browser_ext_management_themes.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_management_themes.js
@@ -6,16 +6,18 @@ const {LightweightThemeManager} = Cu.imp
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.webextensions.themes.enabled", true]],
});
});
add_task(async function test_management_themes() {
+ const TEST_ID = "test_management_themes@tests.mozilla.com";
+
let theme = ExtensionTestUtils.loadExtension({
manifest: {
"name": "Simple theme test",
"version": "1.0",
"description": "test theme",
"theme": {
"images": {
"headerURL": "image1.png",
@@ -23,17 +25,17 @@ add_task(async function test_management_
},
},
files: {
"image1.png": BACKGROUND,
},
useAddonManager: "temporary",
});
- async function background() {
+ async function background(TEST_ID) {
browser.management.onInstalled.addListener(info => {
browser.test.log(`${info.name} was installed`);
browser.test.assertEq(info.type, "theme", "addon is theme");
browser.test.sendMessage("onInstalled", info.name);
});
browser.management.onDisabled.addListener(info => {
browser.test.log(`${info.name} was disabled`);
browser.test.assertEq(info.type, "theme", "addon is theme");
@@ -47,20 +49,25 @@ add_task(async function test_management_
browser.management.onUninstalled.addListener(info => {
browser.test.log(`${info.name} was uninstalled`);
browser.test.assertEq(info.type, "theme", "addon is theme");
browser.test.sendMessage("onUninstalled", info.name);
});
async function getAddon(type) {
let addons = await browser.management.getAll();
+ let themes = addons.filter(addon => addon.type === "theme");
// We get the 3 built-in themes plus the lwt and our addon.
- browser.test.assertEq(5, addons.length, "got expected addons");
+ browser.test.assertEq(5, themes.length, "got expected addons");
+ // We should also get our test extension.
+ let testExtension = addons.find(addon => { return addon.id === TEST_ID; });
+ browser.test.assertTrue(!!testExtension,
+ `The extension with id ${TEST_ID} was returned by getAll.`);
let found;
- for (let addon of addons) {
+ for (let addon of themes) {
browser.test.assertEq(addon.type, "theme", "addon is theme");
if (type == "theme" && addon.id.includes("temporary-addon")) {
found = addon;
} else if (type == "enabled" && addon.enabled) {
found = addon;
}
}
return found;
@@ -84,19 +91,26 @@ add_task(async function test_management_
browser.test.assertEq(theme.id, addon.id, "theme is enabled");
browser.test.sendMessage("done");
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
+ applications: {
+ gecko: {
+ id: TEST_ID,
+ },
+ },
+ name: TEST_ID,
permissions: ["management"],
},
- background,
+ background: `(${background})("${TEST_ID}")`,
+ useAddonManager: "temporary",
});
await extension.startup();
// Test LWT
LightweightThemeManager.currentTheme = {
id: "lwt@personas.mozilla.org",
version: "1",
name: "Bling",
--- a/toolkit/components/extensions/test/xpcshell/test_ext_management.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_management.js
@@ -4,27 +4,63 @@
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
add_task(async function setup() {
await ExtensionTestUtils.startAddonManager();
});
-add_task(async function test_management_schema() {
+add_task(async function test_management_getAll() {
+ const id1 = "get_all_test1@tests.mozilla.com";
+ const id2 = "get_all_test2@tests.mozilla.com";
+
+ function getManifest(id) {
+ return {
+ applications: {
+ gecko: {
+ id,
+ },
+ },
+ name: id,
+ version: "1.0",
+ permissions: ["management"],
+ };
+ }
+
async function background() {
- browser.test.assertTrue(browser.management, "browser.management API exists");
- let self = await browser.management.getSelf();
- browser.test.assertEq(browser.runtime.id, self.id, "got self");
- browser.test.notifyPass("management-schema");
+ browser.test.onMessage.addListener(async (msg, id) => {
+ let addon = await browser.management.get(id);
+ browser.test.sendMessage("addon", addon);
+ });
+
+ let addons = await browser.management.getAll();
+ browser.test.assertEq(addons.length, 2, "management.getAll returned two extensions.");
+ browser.test.sendMessage("addons", addons);
}
- let extension = ExtensionTestUtils.loadExtension({
- manifest: {
- permissions: ["management"],
- },
- background: `(${background})()`,
+ let extension1 = ExtensionTestUtils.loadExtension({
+ manifest: getManifest(id1),
+ useAddonManager: "temporary",
+ });
+
+ let extension2 = ExtensionTestUtils.loadExtension({
+ manifest: getManifest(id2),
+ background,
useAddonManager: "temporary",
});
- await extension.startup();
- await extension.awaitFinish("management-schema");
- await extension.unload();
+
+ await extension1.startup();
+ await extension2.startup();
+
+ let addons = await extension2.awaitMessage("addons");
+ for (let id of [id1, id2]) {
+ let addon = addons.find(a => { return a.id === id; });
+ equal(addon.name, id, `The extension with id ${id} was returned by getAll.`);
+ }
+
+ extension2.sendMessage("getAddon", id1);
+ let addon = await extension2.awaitMessage("addon");
+ equal(addon.name, id1, `The extension with id ${id1} was returned by get.`);
+
+ await extension2.unload();
+ await extension1.unload();
});