Plugin lifecycle fixes
- Call `shutdown()`/`uninstall()` with `ADDON_UPGRADE`/`ADDON_DOWNGRADE` during plugin upgrade/downgrade - Actually call new version's bootstrap.js, not cached old version - Create new scope for new version - Don't call `shutdown()` on uninstall if not active Fixes #3159
This commit is contained in:
parent
fcb30b5e5d
commit
1766f8bb53
2 changed files with 62 additions and 12 deletions
|
@ -221,6 +221,20 @@ function modify_omni {
|
||||||
replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.jsm
|
replace_line '\} else if \(info.addon.sitePermissions\) \{' '} else if (false) {' modules/AddonManager.jsm
|
||||||
replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.jsm
|
replace_line '\} else if \(requireConfirm\) \{' '} else if (false) {' modules/AddonManager.jsm
|
||||||
|
|
||||||
|
# Make addon listener methods wait for promises, to allow calling asynchronous plugin `shutdown`
|
||||||
|
# and `uninstall` methods in our `onInstalling` handler
|
||||||
|
replace_line 'callAddonListeners\(aMethod' 'async callAddonListeners(aMethod' modules/AddonManager.jsm
|
||||||
|
# Don't need this one to be async, but we can't easily avoid modifying its `listener[aMethod].apply()` call
|
||||||
|
replace_line 'callManagerListeners\(aMethod' 'async callManagerListeners(aMethod' modules/AddonManager.jsm
|
||||||
|
replace_line 'AddonManagerInternal.callAddonListeners.apply\(AddonManagerInternal, aArgs\);' \
|
||||||
|
'return AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs);' modules/AddonManager.jsm
|
||||||
|
replace_line 'listener\[aMethod\].apply\(listener, aArgs\);' \
|
||||||
|
'let maybePromise = listener[aMethod].apply(listener, aArgs);
|
||||||
|
if (maybePromise && maybePromise.then) await maybePromise;' modules/AddonManager.jsm
|
||||||
|
replace_line 'AddonManagerPrivate.callAddonListeners' 'await AddonManagerPrivate.callAddonListeners' modules/addons/XPIInstall.jsm
|
||||||
|
replace_line 'let uninstall = \(\) => \{' 'let uninstall = async () => {' modules/addons/XPIInstall.jsm
|
||||||
|
replace_line 'cancelUninstallAddon\(aAddon\)' 'async cancelUninstallAddon(aAddon)' modules/addons/XPIInstall.jsm
|
||||||
|
|
||||||
# No idea why this is necessary, but without it initialization fails with "TypeError: "constructor" is read-only"
|
# No idea why this is necessary, but without it initialization fails with "TypeError: "constructor" is read-only"
|
||||||
replace_line 'LoginStore.prototype.constructor = LoginStore;' '\/\/LoginStore.prototype.constructor = LoginStore;' modules/LoginStore.jsm
|
replace_line 'LoginStore.prototype.constructor = LoginStore;' '\/\/LoginStore.prototype.constructor = LoginStore;' modules/LoginStore.jsm
|
||||||
#
|
#
|
||||||
|
|
|
@ -29,6 +29,7 @@ Zotero.Plugins = new function () {
|
||||||
var { XPIInstall } = ChromeUtils.import("resource://gre/modules/addons/XPIInstall.jsm");
|
var { XPIInstall } = ChromeUtils.import("resource://gre/modules/addons/XPIInstall.jsm");
|
||||||
var scopes = new Map();
|
var scopes = new Map();
|
||||||
var observers = new Set();
|
var observers = new Set();
|
||||||
|
var addonVersions = new Map();
|
||||||
|
|
||||||
const REASONS = {
|
const REASONS = {
|
||||||
APP_STARTUP: 1,
|
APP_STARTUP: 1,
|
||||||
|
@ -37,15 +38,18 @@ Zotero.Plugins = new function () {
|
||||||
ADDON_DISABLE: 4,
|
ADDON_DISABLE: 4,
|
||||||
ADDON_INSTALL: 5,
|
ADDON_INSTALL: 5,
|
||||||
ADDON_UNINSTALL: 6,
|
ADDON_UNINSTALL: 6,
|
||||||
ADDON_UPGRADE: 7, // TODO
|
ADDON_UPGRADE: 7,
|
||||||
ADDON_DOWNGRADE: 8 // TODO
|
ADDON_DOWNGRADE: 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this.init = async function () {
|
this.init = async function () {
|
||||||
this._addonObserver.init();
|
this._addonObserver.init();
|
||||||
|
|
||||||
var { addons } = await AddonManager.getActiveAddons(["extension"]);
|
var { addons } = await AddonManager.getActiveAddons(["extension"]);
|
||||||
for (let addon of addons) {
|
for (let addon of addons) {
|
||||||
|
addonVersions.set(addon.id, addon.version);
|
||||||
|
_loadScope(addon);
|
||||||
setDefaultPrefs(addon);
|
setDefaultPrefs(addon);
|
||||||
registerLocales(addon);
|
registerLocales(addon);
|
||||||
await _callMethod(addon, 'startup', REASONS.APP_STARTUP);
|
await _callMethod(addon, 'startup', REASONS.APP_STARTUP);
|
||||||
|
@ -113,7 +117,13 @@ Zotero.Plugins = new function () {
|
||||||
scopes.set(addon.id, scope);
|
scopes.set(addon.id, scope);
|
||||||
|
|
||||||
var uri = addon.getResourceURI().spec + 'bootstrap.js';
|
var uri = addon.getResourceURI().spec + 'bootstrap.js';
|
||||||
Services.scriptloader.loadSubScript(uri, scope);
|
Services.scriptloader.loadSubScriptWithOptions(
|
||||||
|
uri,
|
||||||
|
{
|
||||||
|
target: scope,
|
||||||
|
ignoreCache: true
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,10 +139,6 @@ Zotero.Plugins = new function () {
|
||||||
+ `version ${addon.version} with reason ${_getReasonName(reason)}`);
|
+ `version ${addon.version} with reason ${_getReasonName(reason)}`);
|
||||||
|
|
||||||
let scope = scopes.get(id);
|
let scope = scopes.get(id);
|
||||||
if (!scope) {
|
|
||||||
_loadScope(addon);
|
|
||||||
scope = scopes.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let func;
|
let func;
|
||||||
try {
|
try {
|
||||||
|
@ -308,6 +314,13 @@ Zotero.Plugins = new function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getVersionChangeReason(oldVersion, newVersion) {
|
||||||
|
return Zotero.Utilities.semverCompare(oldVersion, newVersion) <= 0
|
||||||
|
? REASONS.ADDON_UPGRADE
|
||||||
|
: REASONS.ADDON_DOWNGRADE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an observer to be notified of lifecycle events on all plugins.
|
* Add an observer to be notified of lifecycle events on all plugins.
|
||||||
*
|
*
|
||||||
|
@ -341,6 +354,16 @@ Zotero.Plugins = new function () {
|
||||||
|
|
||||||
async onInstalling(addon) {
|
async onInstalling(addon) {
|
||||||
Zotero.debug("Installing plugin " + addon.id);
|
Zotero.debug("Installing plugin " + addon.id);
|
||||||
|
|
||||||
|
var currentVersion = addonVersions.get(addon.id);
|
||||||
|
if (currentVersion) {
|
||||||
|
let existingAddon = await AddonManager.getAddonByID(addon.id);
|
||||||
|
let reason = getVersionChangeReason(currentVersion, addon.version);
|
||||||
|
if (existingAddon.isActive) {
|
||||||
|
await _callMethod(existingAddon, 'shutdown', reason);
|
||||||
|
}
|
||||||
|
await _callMethod(existingAddon, 'uninstall', reason);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async onInstalled(addon) {
|
async onInstalled(addon) {
|
||||||
|
@ -348,11 +371,20 @@ Zotero.Plugins = new function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Zotero.debug("Installed plugin " + addon.id);
|
Zotero.debug("Installed plugin " + addon.id);
|
||||||
|
|
||||||
|
// Determine if this is a new install, an upgrade, or a downgrade
|
||||||
|
let previousVersion = addonVersions.get(addon.id);
|
||||||
|
let reason = previousVersion
|
||||||
|
? getVersionChangeReason(previousVersion, addon.version)
|
||||||
|
: REASONS.ADDON_INSTALL;
|
||||||
|
addonVersions.set(addon.id, addon.version);
|
||||||
|
|
||||||
|
_loadScope(addon);
|
||||||
setDefaultPrefs(addon);
|
setDefaultPrefs(addon);
|
||||||
registerLocales(addon);
|
registerLocales(addon);
|
||||||
await _callMethod(addon, 'install');
|
await _callMethod(addon, 'install', reason);
|
||||||
if (addon.isActive) {
|
if (addon.isActive) {
|
||||||
await _callMethod(addon, 'startup', REASONS.ADDON_INSTALL);
|
await _callMethod(addon, 'startup', reason);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -379,14 +411,18 @@ Zotero.Plugins = new function () {
|
||||||
async onUninstalling(addon) {
|
async onUninstalling(addon) {
|
||||||
Zotero.debug("Uninstalling plugin " + addon.id);
|
Zotero.debug("Uninstalling plugin " + addon.id);
|
||||||
this.uninstalling.add(addon.id);
|
this.uninstalling.add(addon.id);
|
||||||
|
if (addon.isActive) {
|
||||||
await _callMethod(addon, 'shutdown', REASONS.ADDON_UNINSTALL);
|
await _callMethod(addon, 'shutdown', REASONS.ADDON_UNINSTALL);
|
||||||
await _callMethod(addon, 'uninstall');
|
}
|
||||||
|
await _callMethod(addon, 'uninstall', REASONS.ADDON_UNINSTALL);
|
||||||
unregisterLocales(addon);
|
unregisterLocales(addon);
|
||||||
clearDefaultPrefs(addon);
|
clearDefaultPrefs(addon);
|
||||||
},
|
},
|
||||||
|
|
||||||
async onUninstalled(addon) {
|
async onUninstalled(addon) {
|
||||||
Zotero.debug("Uninstalled plugin " + addon.id);
|
Zotero.debug("Uninstalled plugin " + addon.id);
|
||||||
|
_unloadScope(addon.id);
|
||||||
|
addonVersions.delete(addon.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
async onOperationCancelled(addon) {
|
async onOperationCancelled(addon) {
|
||||||
|
@ -395,7 +431,7 @@ Zotero.Plugins = new function () {
|
||||||
}
|
}
|
||||||
Zotero.debug("Cancelled uninstallation of plugin " + addon.id);
|
Zotero.debug("Cancelled uninstallation of plugin " + addon.id);
|
||||||
this.uninstalling.delete(addon.id);
|
this.uninstalling.delete(addon.id);
|
||||||
await _callMethod(addon, 'install');
|
await _callMethod(addon, 'install', REASONS.ADDON_INSTALL);
|
||||||
if (addon.isActive) {
|
if (addon.isActive) {
|
||||||
setDefaultPrefs(addon);
|
setDefaultPrefs(addon);
|
||||||
registerLocales(addon);
|
registerLocales(addon);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue