From 6106e379c96a26897ead32a300190e2327a11f3e Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Tue, 6 Jun 2023 15:54:44 -0400 Subject: [PATCH] Plugins: Uninstall immediately but allow undo When you uninstall a plugin through the UI, XPIInstall: 1. Sets the plugin's `pendingUninstall` to true 2. Calls our onUninstalling() method 3. Waits for the Add-ons window to be closed 4. Actually uninstalls the plugin 5. Calls our onUninstalled() method If you undo the uninstallation between steps 2 and 3, the remaining steps instead look like: 3. Sets the plugin's `pendingUninstall` to false 4. Calls our onOperationCancelled() method This commit changes our implementation of the bootstrapped plugin lifecycle so that the shutdown and uninstall hooks are called from onUninstalling() (step 2). If you close the Add-ons window without undoing, nothing more happens. The plugin remains uninstalled. If you undo before closing, though, we call the plugin's lifestyle hooks just as if it had been newly installed (unless it was disabled before uninstallation, in which case we call install but not startup). This mirrors the behavior of Firefox WebExtensions and makes things work more like you'd expect: uninstalling a plugin immediately deactivates it, and undoing activates it again. --- chrome/content/zotero/xpcom/plugins.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/chrome/content/zotero/xpcom/plugins.js b/chrome/content/zotero/xpcom/plugins.js index e441e344f9..9469528ad2 100644 --- a/chrome/content/zotero/xpcom/plugins.js +++ b/chrome/content/zotero/xpcom/plugins.js @@ -26,6 +26,7 @@ Zotero.Plugins = new function () { var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); + var { XPIInstall } = ChromeUtils.import("resource://gre/modules/addons/XPIInstall.jsm"); var scopes = new Map(); var observers = new Set(); @@ -279,6 +280,8 @@ Zotero.Plugins = new function () { this._addonObserver = { initialized: false, + uninstalling: new Set(), + init() { if (!this.initialized) { AddonManager.addAddonListener(this); @@ -320,13 +323,27 @@ Zotero.Plugins = new function () { async onUninstalling(addon) { Zotero.debug("Uninstalling plugin " + addon.id); - }, - - async onUninstalled(addon) { - Zotero.debug("Uninstalled plugin " + addon.id); + this.uninstalling.add(addon.id); await _callMethod(addon, 'shutdown', REASONS.ADDON_UNINSTALL); await _callMethod(addon, 'uninstall'); clearDefaultPrefs(addon); }, + + async onUninstalled(addon) { + Zotero.debug("Uninstalled plugin " + addon.id); + }, + + async onOperationCancelled(addon) { + if (!this.uninstalling.has(addon.id) || addon.type !== "extension") { + return; + } + Zotero.debug("Cancelled uninstallation of plugin " + addon.id); + this.uninstalling.delete(addon.id); + setDefaultPrefs(addon); + await _callMethod(addon, 'install'); + if (addon.isActive) { + await _callMethod(addon, 'startup', REASONS.ADDON_INSTALL); + } + } }; };