Bug 1255041 Implement uninstall() on DOM Addon objects r?rhelmer
MozReview-Commit-ID: Ad3r78Y9IKb
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -309,20 +309,22 @@ function getLocale() {
function webAPIForAddon(addon) {
if (!addon) {
return null;
}
let result = {};
- // By default just pass through any plain property, the webidl will control
- // access.
+ // By default just pass through any plain property, the webidl will
+ // control access. Also filter out private properties, regular Addon
+ // objects are okay but MockAddon used in tests has non-serializable
+ // private properties.
for (let prop in addon) {
- if (typeof(addon[prop]) != "function") {
+ if (prop[0] != "_" && typeof(addon[prop]) != "function") {
result[prop] = addon[prop];
}
}
// A few properties are computed for a nicer API
result.isEnabled = !addon.userDisabled;
return result;
@@ -2854,16 +2856,34 @@ var AddonManagerInternal = {
let result = {id};
this.copyProps(install, result);
resolve(result);
};
AddonManager.getInstallForURL(options.url, newInstall, "application/x-xpinstall");
});
},
+ addonUninstall(target, id) {
+ return new Promise(resolve => {
+ AddonManager.getAddonByID(id, addon => {
+ if (!addon) {
+ resolve(false);
+ }
+
+ try {
+ addon.uninstall();
+ resolve(true);
+ } catch (err) {
+ Cu.reportError(err);
+ resolve(false);
+ }
+ });
+ });
+ },
+
addonInstallDoInstall(target, id) {
let state = this.installs.get(id);
if (!state) {
return Promise.reject(`invalid id ${id}`);
}
return Promise.resolve(state.install.install());
},
--- a/toolkit/mozapps/extensions/amWebAPI.js
+++ b/toolkit/mozapps/extensions/amWebAPI.js
@@ -71,26 +71,23 @@ const APIBroker = {
sendCleanup: function(ids) {
Services.cpmm.sendAsyncMessage(MSG_INSTALL_CLEANUP, { ids });
},
};
APIBroker.init();
-function Addon(win, properties) {
+function Addon(window, properties) {
+ this.window = window;
+
// We trust the webidl binding to broker access to our properties.
for (let key of Object.keys(properties)) {
this[key] = properties[key];
}
-
- this.uninstall = function() {
- let err = new win.Error("not yet implemented");
- return win.Promise.reject(err);
- };
}
function AddonInstall(window, properties) {
let id = properties.id;
APIBroker._installMap.set(id, this);
this.window = window;
this.handlers = new Map();
@@ -123,16 +120,22 @@ function WebAPITask(generator) {
return new win.Promise((resolve, reject) => {
task(...args).then(wrapForContent)
.then(resolve, reject);
});
}
}
+Addon.prototype = {
+ uninstall: WebAPITask(function*() {
+ return yield APIBroker.sendRequest("addonUninstall", this.id);
+ }),
+};
+
const INSTALL_EVENTS = [
"onDownloadStarted",
"onDownloadProgress",
"onDownloadEnded",
"onDownloadCancelled",
"onDownloadFailed",
"onInstallStarted",
"onInstallEnded",
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -62,10 +62,11 @@ skip-if = require_signing
[browser_newaddon.js]
[browser_updatessl.js]
[browser_task_next_test.js]
[browser_discovery_install.js]
[browser_update.js]
[browser_webapi.js]
[browser_webapi_access.js]
[browser_webapi_install.js]
+[browser_webapi_uninstall.js]
[include:browser-common.ini]
--- a/toolkit/mozapps/extensions/test/browser/browser_webapi_install.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_install.js
@@ -227,16 +227,16 @@ add_task(makeInstallTest(function* (brow
event: "onDownloadFailed",
props: {
state: "STATE_DOWNLOAD_FAILED",
error: "ERROR_NETWORK_FAILURE",
},
}
];
- yield testInstall(browser, XPI_URL + "bogus", steps, "a basic install works");
+ yield testInstall(browser, XPI_URL + "bogus", steps, "install of a bad url fails");
let addons = yield promiseAddonsByIDs([ID]);
is(addons[0], null, "The addon was not installed");
ok(AddonManager.webAPI.installs.size > 0, "webAPI is tracking the AddonInstall");
}));
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_uninstall.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const TESTPAGE = `${SECURE_TESTROOT}webapi_checkavailable.html`;
+
+Services.prefs.setBoolPref("extensions.webapi.testing", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("extensions.webapi.testing");
+});
+
+function testWithAPI(task) {
+ return function*() {
+ yield BrowserTestUtils.withNewTab(TESTPAGE, task);
+ }
+}
+
+function API_uninstallByID(browser, id) {
+ return ContentTask.spawn(browser, id, function*(id) {
+ let addon = yield content.navigator.mozAddonManager.getAddonByID(id);
+
+ let result = yield addon.uninstall();
+ return result;
+ });
+}
+
+add_task(testWithAPI(function*(browser) {
+ const ID1 = "addon1@tests.mozilla.org";
+ const ID2 = "addon2@tests.mozilla.org";
+
+ let provider = new MockProvider();
+
+ provider.addAddon(new MockAddon(ID1, "Test add-on 1", "extension", 0));
+ provider.addAddon(new MockAddon(ID2, "Test add-on 2", "extension", 0));
+
+ let [a1, a2] = yield promiseAddonsByIDs([ID1, ID2]);
+ isnot(a1, null, "addon1 is installed");
+ isnot(a2, null, "addon2 is installed");
+
+ let result = yield API_uninstallByID(browser, ID1);
+ is(result, true, "uninstall of addon1 succeeded");
+
+ [a1, a2] = yield promiseAddonsByIDs([ID1, ID2]);
+ is(a1, null, "addon1 is uninstalled");
+ isnot(a2, null, "addon2 is still installed");
+
+ result = yield API_uninstallByID(browser, ID2);
+ is(result, true, "uninstall of addon2 succeeded");
+ [a2] = yield promiseAddonsByIDs([ID2]);
+ is(a2, null, "addon2 is uninstalled");
+}));
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -1124,17 +1124,18 @@ function MockAddon(aId, aName, aType, aO
this.scope = AddonManager.SCOPE_PROFILE;
this.isActive = true;
this.creator = "";
this.pendingOperations = 0;
this._permissions = AddonManager.PERM_CAN_UNINSTALL |
AddonManager.PERM_CAN_ENABLE |
AddonManager.PERM_CAN_DISABLE |
AddonManager.PERM_CAN_UPGRADE;
- this.operationsRequiringRestart = aOperationsRequiringRestart ||
+ this.operationsRequiringRestart = (aOperationsRequiringRestart != undefined) ?
+ aOperationsRequiringRestart :
(AddonManager.OP_NEEDS_RESTART_INSTALL |
AddonManager.OP_NEEDS_RESTART_UNINSTALL |
AddonManager.OP_NEEDS_RESTART_ENABLE |
AddonManager.OP_NEEDS_RESTART_DISABLE);
}
MockAddon.prototype = {
get shouldBeActive() {