Bug 1409245 Part 3 Handle update and uninstall events in WebExtensions
This patch just forwards invocations of the bootstrap update() and
uninstall() methods to events that other parts of the WebExtension
framework can listen for. We have an existing event that fires while
an extension is being uninstalled. This event was named "uninstall"
but it gets renamed here to "onUninstalling" to disambiguate the two
events.
MozReview-Commit-ID: BIpIR8n9HBM
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -46,17 +46,17 @@ const getSender = (extension, target, se
}
}
};
// Used by Extension.jsm
global.tabGetSender = getSender;
/* eslint-disable mozilla/balanced-listeners */
-extensions.on("uninstall", (msg, extension) => {
+extensions.on("uninstalling", (msg, extension) => {
if (extension.uninstallURL) {
let browser = windowTracker.topWindow.gBrowser;
browser.addTab(extension.uninstallURL, {relatedToCurrent: true});
}
});
extensions.on("page-shutdown", (type, context) => {
if (context.viewType == "tab") {
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -240,17 +240,17 @@ var UninstallObserver = {
}
},
onUninstalling(addon) {
let extension = GlobalManager.extensionMap.get(addon.id);
if (extension) {
// Let any other interested listeners respond
// (e.g., display the uninstall URL)
- Management.emit("uninstall", extension);
+ Management.emit("uninstalling", extension);
}
},
onUninstalled(addon) {
let uuid = UUIDMap.get(addon.id, false);
if (!uuid) {
return;
}
@@ -970,17 +970,23 @@ this.ExtensionData = class {
};
const PROXIED_EVENTS = new Set(["test-harness-message", "add-permissions", "remove-permissions"]);
const shutdownPromises = new Map();
class BootstrapScope {
install(data, reason) {}
- uninstall(data, reason) {}
+ uninstall(data, reason) {
+ Management.emit("uninstall", {id: data.id});
+ }
+
+ update(data, reason) {
+ Management.emit("update", {id: data.id});
+ }
startup(data, reason) {
this.extension = new Extension(data, this.BOOTSTRAP_REASON_TO_STRING_MAP[reason]);
return this.extension.startup();
}
shutdown(data, reason) {
this.extension.shutdown(this.BOOTSTRAP_REASON_TO_STRING_MAP[reason]);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_events.js
@@ -0,0 +1,91 @@
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+add_task(async function() {
+ let triggered = {};
+ const {Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+ for (let event of ["install", "uninstall", "update"]) {
+ triggered[event] = false;
+ Management.on(event, () => triggered[event] = true);
+ }
+
+ async function expectEvents(expected, fn) {
+ let events = Object.keys(expected);
+ for (let event of events) {
+ triggered[event] = false;
+ }
+
+ await fn();
+
+ for (let event of events) {
+ equal(triggered[event], expected[event],
+ `Event ${event} was${expected[event] ? "" : " not"} triggered`);
+ }
+ }
+
+ await promiseStartupManager();
+
+ const id = "webextension@tests.mozilla.org";
+
+ // Install version 1.0, shouldn't see any events
+ await expectEvents({update: false, uninstall: false}, async () => {
+ await promiseInstallWebExtension({
+ manifest: {
+ version: "1.0",
+ applications: {gecko: {id}}
+ },
+ });
+ });
+
+ // Install version 2.0, we should get an update event but not an uninstall
+ await expectEvents({update: true, uninstall: false}, async () => {
+ await promiseInstallWebExtension({
+ manifest: {
+ version: "2.0",
+ applications: {gecko: {id}}
+ },
+ });
+ });
+
+ // Install version 3.0 as a temporary addon, we should again get
+ // update but not uninstall
+ let v3 = createTempWebExtensionFile({
+ manifest: {
+ version: "3.0",
+ applications: {gecko: {id}}
+ },
+ });
+
+ await expectEvents({update: true, uninstall: false}, () =>
+ Promise.all([
+ AddonManager.installTemporaryAddon(v3),
+ promiseWebExtensionStartup(),
+ ]));
+
+ // Uninstall the temporary addon, this causes version 2.0 still installed
+ // in the profile to be revealed. Again, this results in an update event.
+ let addon = await promiseAddonByID(id);
+ await expectEvents({update: true, uninstall: false},
+ () => { addon.uninstall(); });
+
+ // Re-install version 3.0 as a temporary addon
+ await Promise.all([
+ AddonManager.installTemporaryAddon(v3),
+ promiseWebExtensionStartup(),
+ ]);
+
+ // Now shut down the addons manager, this should cause the temporary
+ // addon to be uninstalled which reveals 2.0 from the profile.
+ await expectEvents({update: true, uninstall: false},
+ () => promiseShutdownManager());
+
+ // When we start up again we should not see any events
+ await expectEvents({install: false}, () => promiseStartupManager());
+
+ addon = await promiseAddonByID(id);
+
+ // When we uninstall from the profile, the addon is now gone, we should
+ // get an uninstall events.
+ await expectEvents({update: false, uninstall: true},
+ () => { addon.uninstall(); });
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -320,16 +320,19 @@ tags = blocklist
[test_sideloads.js]
[test_sourceURI.js]
[test_webextension_icons.js]
skip-if = appname == "thunderbird"
tags = webextensions
[test_webextension.js]
skip-if = appname == "thunderbird"
tags = webextensions
+[test_webextension_events.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
[test_webextension_install.js]
skip-if = appname == "thunderbird"
tags = webextensions
[test_webextension_embedded.js]
skip-if = appname == "thunderbird"
tags = webextensions
[test_webextension_install_syntax_error.js]
skip-if = appname == "thunderbird"