Bug 1409245 Part 3 Handle update and uninstall events in WebExtensions draft
authorAndrew Swan <aswan@mozilla.com>
Wed, 25 Oct 2017 20:49:14 -0700
changeset 689495 a35091602cf6d93e9bea9b8dd6aaabb9e7d04a3f
parent 689494 aa76ce45d5c249685cb65345c4c4d99277b0e3f9
child 738327 0e7d3411861d437c26c7bb0024cac8a7d0f02b66
push id87036
push useraswan@mozilla.com
push dateTue, 31 Oct 2017 16:41:42 +0000
bugs1409245
milestone58.0a1
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
browser/components/extensions/ext-browser.js
toolkit/components/extensions/Extension.jsm
toolkit/mozapps/extensions/test/xpcshell/test_webextension_events.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
--- 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"