--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1195,31 +1195,35 @@ class MockExtension {
}
cleanupGeneratedFile() {
flushJarCache(this.file);
return OS.File.remove(this.file.path);
}
}
+let _browserUpdated = false;
+
// We create one instance of this class per extension. |addonData|
// comes directly from bootstrap.js when initializing.
this.Extension = class extends ExtensionData {
- constructor(addonData) {
+ constructor(addonData, startupReason) {
super(addonData.resourceURI);
this.uuid = UUIDMap.get(addonData.id);
if (addonData.cleanupFile) {
Services.obs.addObserver(this, "xpcom-shutdown", false);
this.cleanupFile = addonData.cleanupFile || null;
delete addonData.cleanupFile;
}
this.addonData = addonData;
+ this.startupReason = startupReason;
+
this.id = addonData.id;
this.baseURI = NetUtil.newURI(this.getURL("")).QueryInterface(Ci.nsIURL);
this.principal = this.createPrincipal();
this.onStartup = null;
this.hasShutdown = false;
this.onShutdown = new Set();
@@ -1228,16 +1232,24 @@ this.Extension = class extends Extension
this.apis = [];
this.whiteListedHosts = null;
this.webAccessibleResources = null;
this.emitter = new EventEmitter();
}
+ static set browserUpdated(updated) {
+ _browserUpdated = updated;
+ }
+
+ static get browserUpdated() {
+ return _browserUpdated;
+ }
+
/**
* This code is designed to make it easy to test a WebExtension
* without creating a bunch of files. Everything is contained in a
* single JSON blob.
*
* Properties:
* "background": "<JS code>"
* A script to be loaded as the background script.
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -15,16 +15,21 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyGetter(this, "Management", () => {
+ const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
+ return Management;
+});
+
/* exported ExtensionTestUtils */
let BASE_MANIFEST = Object.freeze({
"applications": Object.freeze({
"gecko": Object.freeze({
"id": "test@web.ext",
}),
}),
@@ -45,65 +50,69 @@ class ExtensionWrapper {
this.testResolve = null;
this.testDone = new Promise(resolve => { this.testResolve = resolve; });
this.messageHandler = new Map();
this.messageAwaiter = new Map();
this.messageQueue = new Set();
+ this.attachListeners();
+
this.testScope.do_register_cleanup(() => {
if (this.messageQueue.size) {
let names = Array.from(this.messageQueue, ([msg]) => msg);
this.testScope.equal(JSON.stringify(names), "[]", "message queue is empty");
}
if (this.messageAwaiter.size) {
let names = Array.from(this.messageAwaiter.keys());
this.testScope.equal(JSON.stringify(names), "[]", "no tasks awaiting on messages");
}
});
- /* eslint-disable mozilla/balanced-listeners */
- extension.on("test-eq", (kind, pass, msg, expected, actual) => {
- this.testScope.ok(pass, `${msg} - Expected: ${expected}, Actual: ${actual}`);
- });
- extension.on("test-log", (kind, pass, msg) => {
- this.testScope.do_print(msg);
- });
- extension.on("test-result", (kind, pass, msg) => {
- this.testScope.ok(pass, msg);
- });
- extension.on("test-done", (kind, pass, msg, expected, actual) => {
- this.testScope.ok(pass, msg);
- this.testResolve(msg);
- });
-
- extension.on("test-message", (kind, msg, ...args) => {
- let handler = this.messageHandler.get(msg);
- if (handler) {
- handler(...args);
- } else {
- this.messageQueue.add([msg, ...args]);
- this.checkMessages();
- }
- });
- /* eslint-enable mozilla/balanced-listeners */
-
this.testScope.do_register_cleanup(() => {
if (this.state == "pending" || this.state == "running") {
this.testScope.equal(this.state, "unloaded", "Extension left running at test shutdown");
return this.unload();
} else if (extension.state == "unloading") {
this.testScope.equal(this.state, "unloaded", "Extension not fully unloaded at test shutdown");
}
});
this.testScope.do_print(`Extension loaded`);
}
+ attachListeners() {
+ /* eslint-disable mozilla/balanced-listeners */
+ this.extension.on("test-eq", (kind, pass, msg, expected, actual) => {
+ this.testScope.ok(pass, `${msg} - Expected: ${expected}, Actual: ${actual}`);
+ });
+ this.extension.on("test-log", (kind, pass, msg) => {
+ this.testScope.do_print(msg);
+ });
+ this.extension.on("test-result", (kind, pass, msg) => {
+ this.testScope.ok(pass, msg);
+ });
+ this.extension.on("test-done", (kind, pass, msg, expected, actual) => {
+ this.testScope.ok(pass, msg);
+ this.testResolve(msg);
+ });
+
+ this.extension.on("test-message", (kind, msg, ...args) => {
+ let handler = this.messageHandler.get(msg);
+ if (handler) {
+ handler(...args);
+ } else {
+ this.messageQueue.add([msg, ...args]);
+ this.checkMessages();
+ }
+ });
+ /* eslint-enable mozilla/balanced-listeners */
+ }
+
startup() {
if (this.state != "uninitialized") {
throw new Error("Extension already started");
}
this.state = "pending";
return this.extension.startup().then(
result => {
@@ -195,18 +204,16 @@ class ExtensionWrapper {
this.messageHandler.set(msg, callback);
}
}
var ExtensionTestUtils = {
BASE_MANIFEST,
normalizeManifest: Task.async(function* (manifest, baseManifest = BASE_MANIFEST) {
- const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
-
yield Management.lazyInit();
let errors = [];
let context = {
url: null,
logError: error => {
errors.push(error);
--- a/toolkit/components/extensions/ext-backgroundPage.js
+++ b/toolkit/components/extensions/ext-backgroundPage.js
@@ -96,21 +96,17 @@ BackgroundPage.prototype = {
// Set the add-on's main debugger global, for use in the debugger
// console.
if (this.extension.addonData.instanceID) {
AddonManager.getAddonByInstanceID(this.extension.addonData.instanceID)
.then(addon => addon.setDebugGlobal(window));
}
- // TODO(robwu): This implementation of onStartup is wrong, see
- // https://bugzil.la/1247435#c1
- if (this.extension.onStartup) {
- this.extension.onStartup();
- }
+ this.extension.emit("startup");
}),
shutdown() {
if (this.extension.addonData.instanceID) {
AddonManager.getAddonByInstanceID(this.extension.addonData.instanceID)
.then(addon => addon.setDebugGlobal(null));
}
--- a/toolkit/components/extensions/ext-runtime.js
+++ b/toolkit/components/extensions/ext-runtime.js
@@ -3,41 +3,57 @@
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Extension",
+ "resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
"resource://gre/modules/ExtensionManagement.jsm");
var {
- EventManager,
+ ignoreEvent,
SingletonEventManager,
- ignoreEvent,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
"resource://gre/modules/NativeMessaging.jsm");
extensions.registerSchemaAPI("runtime", "addon_parent", context => {
let {extension} = context;
return {
runtime: {
- onStartup: new EventManager(context, "runtime.onStartup", fire => {
- extension.onStartup = fire;
+ onStartup: ignoreEvent(context, "runtime.onStartup"),
+
+ onInstalled: new SingletonEventManager(context, "runtime.onInstalled", fire => {
+ let listener = () => {
+ switch (extension.startupReason) {
+ case "APP_STARTUP":
+ if (Extension.browserUpdated) {
+ fire({reason: "browser_update"});
+ }
+ break;
+ case "ADDON_INSTALL":
+ fire({reason: "install"});
+ break;
+ case "ADDON_UPGRADE":
+ fire({reason: "update"});
+ break;
+ }
+ };
+ extension.on("startup", listener);
return () => {
- extension.onStartup = null;
+ extension.off("startup", listener);
};
}).api(),
- onInstalled: ignoreEvent(context, "runtime.onInstalled"),
-
onUpdateAvailable: new SingletonEventManager(context, "runtime.onUpdateAvailable", fire => {
let instanceID = extension.addonData.instanceID;
AddonManager.addUpgradeListener(instanceID, upgrade => {
extension.upgrade = upgrade;
let details = {
version: upgrade.version,
};
context.runSafe(fire, details);
--- a/toolkit/components/extensions/schemas/runtime.json
+++ b/toolkit/components/extensions/schemas/runtime.json
@@ -117,17 +117,17 @@
"type": "string",
"enum": ["throttled", "no_update", "update_available"],
"allowedContexts": ["content"],
"description": "Result of the update check."
},
{
"id": "OnInstalledReason",
"type": "string",
- "enum": ["install", "update", "chrome_update", "shared_module_update"],
+ "enum": ["install", "update", "browser_update"],
"allowedContexts": ["content"],
"description": "The reason that this event is being dispatched."
},
{
"id": "OnRestartRequiredReason",
"type": "string",
"allowedContexts": ["content"],
"description": "The reason that the event is being dispatched. 'app_update' is used when the restart is needed because the application is updated to a newer version. 'os_update' is used when the restart is needed because the browser/OS is updated to a newer version. 'periodic' is used when the system runs for more than the permitted uptime set in the enterprise policy.",
@@ -454,36 +454,37 @@
{
"name": "onStartup",
"unsupported": true,
"type": "function",
"description": "Fired when a profile that has this extension installed first starts up. This event is not fired when an incognito profile is started, even if this extension is operating in 'split' incognito mode."
},
{
"name": "onInstalled",
- "unsupported": true,
"type": "function",
"description": "Fired when the extension is first installed, when the extension is updated to a new version, and when the browser is updated to a new version.",
"parameters": [
{
"type": "object",
"name": "details",
"properties": {
"reason": {
"$ref": "OnInstalledReason",
"description": "The reason that this event is being dispatched."
},
"previousVersion": {
"type": "string",
"optional": true,
+ "unsupported": true,
"description": "Indicates the previous version of the extension, which has just been updated. This is present only if 'reason' is 'update'."
},
"id": {
"type": "string",
"optional": true,
+ "unsupported": true,
"description": "Indicates the ID of the imported shared module extension which updated. This is present only if 'reason' is 'shared_module_update'."
}
}
}
]
},
{
"name": "onSuspend",
--- a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
+++ b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
@@ -68,16 +68,17 @@ let expectedBackgroundApis = [
"management.ExtensionDisabledReason",
"management.ExtensionInstallType",
"management.ExtensionType",
"management.getSelf",
"management.uninstallSelf",
"runtime.getBackgroundPage",
"runtime.getBrowserInfo",
"runtime.getPlatformInfo",
+ "runtime.onInstalled",
"runtime.onUpdateAvailable",
"runtime.openOptionsPage",
"runtime.reload",
"runtime.setUninstallURL",
];
function sendAllApis() {
function isEvent(key, val) {
--- a/toolkit/components/extensions/test/xpcshell/head.js
+++ b/toolkit/components/extensions/test/xpcshell/head.js
@@ -2,16 +2,17 @@
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/* exported createHttpServer, promiseConsoleOutput, cleanupDir */
Components.utils.import("resource://gre/modules/Task.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Timer.jsm");
+Components.utils.import("resource://testing-common/AddonTestUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_onInstalled.js
@@ -0,0 +1,324 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyGetter(this, "Management", () => {
+ const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
+ return Management;
+});
+
+const {
+ createAppInfo,
+ createTempWebExtensionFile,
+ promiseAddonByID,
+ promiseAddonEvent,
+ promiseCompleteAllInstalls,
+ promiseFindAddonUpdates,
+ promiseRestartManager,
+ promiseShutdownManager,
+ promiseStartupManager,
+} = AddonTestUtils;
+
+AddonTestUtils.init(this);
+
+// Allow for unsigned addons.
+AddonTestUtils.overrideCertDB();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+function awaitEvent(eventName) {
+ return new Promise(resolve => {
+ let listener = (_eventName, ...args) => {
+ if (_eventName === eventName) {
+ Management.off(eventName, listener);
+ resolve(...args);
+ }
+ };
+
+ Management.on(eventName, listener);
+ });
+}
+
+add_task(function* test_should_fire_on_addon_update() {
+ const EXTENSION_ID = "test_runtime_on_installed_addon_update@tests.mozilla.org";
+
+ const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
+
+ // The test extension uses an insecure update url.
+ Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+ const testServer = createHttpServer();
+ const port = testServer.identity.primaryPort;
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": EXTENSION_ID,
+ "update_url": `http://localhost:${port}/test_update.json`,
+ },
+ },
+ },
+ background() {
+ browser.runtime.onUpdateAvailable.addListener(details => {
+ browser.test.sendMessage("reloading");
+ browser.runtime.reload();
+ });
+
+ browser.runtime.onInstalled.addListener(details => {
+ browser.test.sendMessage("installed", details);
+ });
+ },
+ });
+
+ testServer.registerPathHandler("/test_update.json", (request, response) => {
+ response.write(`{
+ "addons": {
+ "${EXTENSION_ID}": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:${port}/addons/test_runtime_on_installed-2.0.xpi"
+ }
+ ]
+ }
+ }
+ }`);
+ });
+
+ let webExtensionFile = createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ applications: {
+ gecko: {
+ id: EXTENSION_ID,
+ },
+ },
+ },
+ background() {
+ browser.runtime.onInstalled.addListener(details => {
+ browser.test.sendMessage("installed", details);
+ });
+ },
+ });
+
+ testServer.registerFile("/addons/test_runtime_on_installed-2.0.xpi", webExtensionFile);
+
+ yield promiseStartupManager();
+
+ yield extension.startup();
+ let details = yield extension.awaitMessage("installed");
+ equal(details.reason, "install", "runtime.onInstalled fired with the correct reason");
+
+ let addon = yield promiseAddonByID(EXTENSION_ID);
+ equal(addon.version, "1.0", "The installed addon has the correct version");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ let promiseInstalled = promiseAddonEvent("onInstalled");
+ yield promiseCompleteAllInstalls([install]);
+
+ yield extension.awaitMessage("reloading");
+
+ let startupPromise = awaitEvent("ready");
+
+ let [updated_addon] = yield promiseInstalled;
+ equal(updated_addon.version, "2.0", "The updated addon has the correct version");
+
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ details = yield extension.awaitMessage("installed");
+ equal(details.reason, "update", "runtime.onInstalled fired with the correct reason");
+
+ yield extension.unload();
+
+ yield updated_addon.uninstall();
+ yield promiseShutdownManager();
+});
+
+add_task(function* test_should_fire_on_browser_update() {
+ const EXTENSION_ID = "test_runtime_on_installed_browser_update@tests.mozilla.org";
+
+ yield promiseStartupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": EXTENSION_ID,
+ },
+ },
+ },
+ background() {
+ let onInstalledDetails = null;
+
+ browser.runtime.onInstalled.addListener(details => {
+ onInstalledDetails = details;
+ });
+
+ browser.test.onMessage.addListener(message => {
+ if (message == "get-on-installed-details") {
+ browser.test.sendMessage("on-installed-details", onInstalledDetails);
+ }
+ });
+ },
+ });
+
+ yield extension.startup();
+
+ extension.sendMessage("get-on-installed-details");
+ let details = yield extension.awaitMessage("on-installed-details");
+ equal(details.reason, "install", "runtime.onInstalled fired with the correct reason");
+
+ let startupPromise = awaitEvent("ready");
+ yield promiseRestartManager("1");
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get-on-installed-details");
+ details = yield extension.awaitMessage("on-installed-details");
+ equal(details, null, "runtime.onInstalled should not have fired");
+
+ // Update the browser.
+ startupPromise = awaitEvent("ready");
+ yield promiseRestartManager("2");
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get-on-installed-details");
+ details = yield extension.awaitMessage("on-installed-details");
+ equal(details.reason, "browser_update", "runtime.onInstalled fired with the correct reason");
+
+ // Restart the browser.
+ startupPromise = awaitEvent("ready");
+ yield promiseRestartManager("2");
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get-on-installed-details");
+ details = yield extension.awaitMessage("on-installed-details");
+ equal(details, null, "runtime.onInstalled should not have fired");
+
+ // Update the browser again.
+ startupPromise = awaitEvent("ready");
+ yield promiseRestartManager("3");
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get-on-installed-details");
+ details = yield extension.awaitMessage("on-installed-details");
+ equal(details.reason, "browser_update", "runtime.onInstalled fired with the correct reason");
+
+ yield extension.unload();
+
+ yield promiseShutdownManager();
+});
+
+add_task(function* test_should_not_fire_on_reload() {
+ const EXTENSION_ID = "test_runtime_on_installed_reload@tests.mozilla.org";
+
+ yield promiseStartupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": EXTENSION_ID,
+ },
+ },
+ },
+ background() {
+ let onInstalledDetails = null;
+
+ browser.runtime.onInstalled.addListener(details => {
+ onInstalledDetails = details;
+ });
+
+ browser.test.onMessage.addListener(message => {
+ if (message == "reload-extension") {
+ browser.runtime.reload();
+ } else if (message == "get-on-installed-details") {
+ browser.test.sendMessage("on-installed-details", onInstalledDetails);
+ }
+ });
+ },
+ });
+
+ yield extension.startup();
+
+ extension.sendMessage("get-on-installed-details");
+ let details = yield extension.awaitMessage("on-installed-details");
+ equal(details.reason, "install", "runtime.onInstalled fired with the correct reason");
+
+ let startupPromise = awaitEvent("ready");
+ extension.sendMessage("reload-extension");
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get-on-installed-details");
+ details = yield extension.awaitMessage("on-installed-details");
+ equal(details, null, "runtime.onInstalled should not have fired");
+
+ yield extension.unload();
+ yield promiseShutdownManager();
+});
+
+add_task(function* test_should_not_fire_on_restart() {
+ const EXTENSION_ID = "test_runtime_on_installed_restart@tests.mozilla.org";
+
+ yield promiseStartupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": EXTENSION_ID,
+ },
+ },
+ },
+ background() {
+ let onInstalledDetails = null;
+
+ browser.runtime.onInstalled.addListener(details => {
+ onInstalledDetails = details;
+ });
+
+ browser.test.onMessage.addListener(message => {
+ if (message == "get-on-installed-details") {
+ browser.test.sendMessage("on-installed-details", onInstalledDetails);
+ }
+ });
+ },
+ });
+
+ yield extension.startup();
+
+ extension.sendMessage("get-on-installed-details");
+ let details = yield extension.awaitMessage("on-installed-details");
+ equal(details.reason, "install", "runtime.onInstalled fired with the correct reason");
+
+ let addon = yield promiseAddonByID(EXTENSION_ID);
+ addon.userDisabled = true;
+
+ let startupPromise = awaitEvent("ready");
+ addon.userDisabled = false;
+ extension.extension = yield startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get-on-installed-details");
+ details = yield extension.awaitMessage("on-installed-details");
+ equal(details, null, "runtime.onInstalled should not have fired");
+
+ yield extension.markUnloaded();
+ yield promiseShutdownManager();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -41,16 +41,17 @@ skip-if = release_or_beta
[test_ext_management_uninstall_self.js]
[test_ext_manifest_content_security_policy.js]
[test_ext_manifest_incognito.js]
[test_ext_manifest_minimum_chrome_version.js]
[test_ext_onmessage_removelistener.js]
[test_ext_runtime_connect_no_receiver.js]
[test_ext_runtime_getBrowserInfo.js]
[test_ext_runtime_getPlatformInfo.js]
+[test_ext_runtime_onInstalled.js]
[test_ext_runtime_sendMessage.js]
[test_ext_runtime_sendMessage_errors.js]
[test_ext_runtime_sendMessage_no_receiver.js]
[test_ext_runtime_sendMessage_self.js]
[test_ext_schemas.js]
[test_ext_schemas_api_injection.js]
[test_ext_schemas_async.js]
[test_ext_schemas_allowed_contexts.js]
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -80,16 +80,18 @@ Cu.import("resource://gre/modules/XPCOMU
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
"resource://gre/modules/addons/AddonRepository.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Extension",
+ "resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
let certUtils = {};
Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
return certUtils;
});
@@ -845,16 +847,18 @@ var AddonManagerInternal = {
let oldAppVersion = null;
try {
oldAppVersion = Services.prefs.getCharPref(PREF_EM_LAST_APP_VERSION);
appChanged = Services.appinfo.version != oldAppVersion;
}
catch (e) { }
+ Extension.browserUpdated = appChanged;
+
let oldPlatformVersion = null;
try {
oldPlatformVersion = Services.prefs.getCharPref(PREF_EM_LAST_PLATFORM_VERSION);
}
catch (e) { }
if (appChanged !== false) {
logger.debug("Application has been upgraded");
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -1086,16 +1086,20 @@ var AddonTestUtils = {
promiseAddonByID(id) {
return new Promise(resolve => AddonManager.getAddonByID(id, resolve));
},
/**
* Returns a promise that will be resolved when an add-on update check is
* complete. The value resolved will be an AddonInstall if a new version was
* found.
+ *
+ * @param {object} addon The add-on to find updates for.
+ * @param {integer} reason The type of update to find.
+ * @return {Promise<object>} an object containing information about the update.
*/
promiseFindAddonUpdates(addon, reason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
let equal = this.testScope.equal;
return new Promise((resolve, reject) => {
let result = {};
addon.findUpdates({
onNoCompatibilityUpdateAvailable: function(addon2) {
if ("compatibilityUpdate" in result) {
--- a/toolkit/mozapps/extensions/internal/WebExtensionBootstrap.js
+++ b/toolkit/mozapps/extensions/internal/WebExtensionBootstrap.js
@@ -3,23 +3,34 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
Components.utils.import("resource://gre/modules/Extension.jsm");
var extension;
+const BOOTSTRAP_REASON_TO_STRING_MAP = {
+ 1: "APP_STARTUP",
+ 2: "APP_SHUTDOWN",
+ 3: "ADDON_ENABLE",
+ 4: "ADDON_DISABLE",
+ 5: "ADDON_INSTALL",
+ 6: "ADDON_UNINSTALL",
+ 7: "ADDON_UPGRADE",
+ 8: "ADDON_DOWNGRADE",
+}
+
function install(data, reason)
{
}
function startup(data, reason)
{
- extension = new Extension(data);
+ extension = new Extension(data, BOOTSTRAP_REASON_TO_STRING_MAP[reason]);
extension.startup();
}
function shutdown(data, reason)
{
extension.shutdown();
}