Bug 1461146: Replace Addon.userDisabled setter with async enable()/disable() methods. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 12 May 2018 16:49:35 -0700
changeset 794553 e41f1214f16b9fea50fff7e8c261bd564d41679e
parent 794552 2a01d9aa85cd69f7892cd12e3aa7b2dc12dfbc50
child 794556 b45e093ec694975f59253a397c747798ecb22512
child 794587 306510520201277e8c29a3af56ef56f54426fa93
push id109712
push usermaglione.k@gmail.com
push dateSat, 12 May 2018 23:50:32 +0000
reviewersaswan
bugs1461146
milestone62.0a1
Bug 1461146: Replace Addon.userDisabled setter with async enable()/disable() methods. r?aswan MozReview-Commit-ID: Gj2iCsBIdnq
browser/components/extensions/ExtensionControlledPopup.jsm
browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
browser/components/extensions/test/browser/browser_ext_commands_update.js
browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
browser/components/extensions/test/browser/browser_ext_tabs_hide_update.js
browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab.js
browser/components/preferences/in-content/extensionControlled.js
browser/components/preferences/in-content/tests/browser_extension_controlled.js
browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js
devtools/client/webide/modules/addons.js
devtools/shared/gcli/commands/addon.js
mobile/android/chrome/content/aboutAddons.js
mobile/android/components/BlocklistPrompt.js
services/sync/modules/addonutils.js
services/sync/tests/unit/test_addons_tracker.js
toolkit/components/extensions/parent/ext-management.js
toolkit/components/extensions/test/xpcshell/test_ext_privacy_disable.js
toolkit/components/extensions/test/xpcshell/test_ext_runtime_onInstalled_and_onStartup.js
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
toolkit/mozapps/extensions/LightweightThemeManager.jsm
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/internal/GMPProvider.jsm
toolkit/mozapps/extensions/internal/PluginProvider.jsm
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/test/browser/browser_bug591465.js
toolkit/mozapps/extensions/test/browser/browser_bug596336.js
toolkit/mozapps/extensions/test/browser/browser_details.js
toolkit/mozapps/extensions/test/browser/browser_list.js
toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
toolkit/mozapps/extensions/test/browser/browser_webapi_addon_listener.js
toolkit/mozapps/extensions/test/browser/head.js
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklist_severities.js
toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/test_bootstrapped_chrome_manifest.js
toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
toolkit/mozapps/extensions/test/xpcshell/test_install.js
toolkit/mozapps/extensions/test/xpcshell/test_locale.js
toolkit/mozapps/extensions/test/xpcshell/test_manifest_locales.js
toolkit/mozapps/extensions/test/xpcshell/test_moved_extension_metadata.js
toolkit/mozapps/extensions/test/xpcshell/test_nodisable_hidden.js
toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
toolkit/mozapps/extensions/test/xpcshell/test_plugins.js
toolkit/mozapps/extensions/test/xpcshell/test_safemode.js
toolkit/mozapps/extensions/test/xpcshell/test_softblocked.js
toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
toolkit/mozapps/extensions/test/xpcshell/test_update.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension_embedded.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension_langpack.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
--- a/browser/components/extensions/ExtensionControlledPopup.jsm
+++ b/browser/components/extensions/ExtensionControlledPopup.jsm
@@ -204,17 +204,17 @@ class ExtensionControlledPopup {
       if (event.originalTarget.getAttribute("anonid") == "button") {
         // Main action is to keep changes.
         await this.setConfirmation(extensionId);
       } else {
         // Secondary action is to restore settings.
         if (this.beforeDisableAddon) {
           await this.beforeDisableAddon(this, win);
         }
-        addon.userDisabled = true;
+        addon.disable();
       }
 
       // If the page this is appearing on is the New Tab page then the URL bar may
       // have been focused when the doorhanger stole focus away from it. Once an
       // action is taken the focus state should be restored to what the user was
       // expecting.
       if (urlBarWasFocused) {
         win.gURLBar.focus();
--- a/browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
+++ b/browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
@@ -233,25 +233,25 @@ add_task(async function test_disable() {
   is(getHomePageURL(), HOME_URI_1,
      "Home url should be overridden by the extension.");
 
   let addon = await AddonManager.getAddonByID(ID);
   is(addon.id, ID, "Found the correct add-on.");
 
   let disabledPromise = awaitEvent("shutdown", ID);
   prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
-  addon.userDisabled = true;
+  await addon.disable();
   await Promise.all([disabledPromise, prefPromise]);
 
   is(getHomePageURL(), defaultHomePage,
      "Home url should be the default");
 
   let enabledPromise = awaitEvent("ready", ID);
   prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
-  addon.userDisabled = false;
+  await addon.enable();
   await Promise.all([enabledPromise, prefPromise]);
 
   is(getHomePageURL(), HOME_URI_1,
      "Home url should be overridden by the extension.");
 
   prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
   await ext1.unload();
   await prefPromise;
--- a/browser/components/extensions/test/browser/browser_ext_commands_update.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_update.js
@@ -12,31 +12,31 @@ function enableAddon(addon) {
     AddonManager.addAddonListener({
       onEnabled(enabledAddon) {
         if (enabledAddon.id == addon.id) {
           resolve();
           AddonManager.removeAddonListener(this);
         }
       },
     });
-    addon.userDisabled = false;
+    addon.enable();
   });
 }
 
 function disableAddon(addon) {
   return new Promise(resolve => {
     AddonManager.addAddonListener({
       onDisabled(disabledAddon) {
         if (disabledAddon.id == addon.id) {
           resolve();
           AddonManager.removeAddonListener(this);
         }
       },
     });
-    addon.userDisabled = true;
+    addon.disable();
   });
 }
 
 add_task(async function test_update_defined_command() {
   let extension;
   let updatedExtension;
 
   registerCleanupFunction(async () => {
--- a/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
+++ b/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
@@ -189,23 +189,23 @@ add_task(async function test_user_change
 
   let engine = Services.search.getEngineByName("Twitter");
   Services.search.currentEngine = engine;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
   let addon = await AddonManager.getAddonByID(EXTENSION1_ID);
-  addon.userDisabled = true;
+  await addon.disable();
   await disabledPromise;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
 
   let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
-  addon.userDisabled = false;
+  await addon.enable();
   await enabledPromise;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
   await ext1.unload();
   restoreDefaultEngine();
 });
 
 /* This tests that when two add-ons are installed that change default
@@ -249,27 +249,27 @@ add_task(async function test_two_addons_
   });
 
   await ext1.startup();
 
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
   let addon1 = await AddonManager.getAddonByID(EXTENSION1_ID);
-  addon1.userDisabled = true;
+  await addon1.disable();
   await disabledPromise;
 
   is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
 
   await ext2.startup();
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
 
   let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
-  addon1.userDisabled = false;
+  await addon1.enable();
   await enabledPromise;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
   await ext2.unload();
 
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
   await ext1.unload();
 
@@ -321,23 +321,23 @@ add_task(async function test_two_addons_
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext2.startup();
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
   let addon1 = await AddonManager.getAddonByID(EXTENSION1_ID);
-  addon1.userDisabled = true;
+  await addon1.disable();
   await disabledPromise;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
 
   let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
-  addon1.userDisabled = false;
+  await addon1.enable();
   await enabledPromise;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
   await ext2.unload();
 
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
   await ext1.unload();
 
@@ -389,23 +389,23 @@ add_task(async function test_two_addons_
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext2.startup();
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION2_ID);
   let addon2 = await AddonManager.getAddonByID(EXTENSION2_ID);
-  addon2.userDisabled = true;
+  await addon2.disable();
   await disabledPromise;
 
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   let enabledPromise = awaitEvent("ready", EXTENSION2_ID);
-  addon2.userDisabled = false;
+  await addon2.enable();
   await enabledPromise;
 
   is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
   await ext2.unload();
 
   is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
   await ext1.unload();
 
--- a/browser/components/extensions/test/browser/browser_ext_tabs_hide_update.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide_update.js
@@ -12,17 +12,17 @@ async function updateExtension(ID, optio
     AddonTestUtils.promiseWebExtensionStartup(ID),
     AddonManager.installTemporaryAddon(xpi),
   ]);
 }
 
 async function disableExtension(ID) {
   let disabledPromise = awaitEvent("shutdown", ID);
   let addon = await AddonManager.getAddonByID(ID);
-  addon.userDisabled = true;
+  await addon.disable();
   await disabledPromise;
 }
 
 function getExtension() {
   async function background() {
     let tabs = await browser.tabs.query({url: "http://example.com/"});
     let testTab = tabs[0];
 
--- a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
@@ -345,17 +345,17 @@ add_task(async function test_new_tab_res
         if (enabledAddon.id == addon.id) {
           AddonManager.removeAddonListener(listener);
           resolve();
         }
       },
     };
     AddonManager.addAddonListener(listener);
   });
-  addon.userDisabled = false;
+  await addon.enable();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
   await addonEnabled;
   await extension.unload();
 });
 
 add_task(async function test_new_tab_restore_settings_multiple() {
   await ExtensionSettingsStore.initialize();
   let notification = getNewTabDoorhanger();
@@ -463,18 +463,18 @@ add_task(async function test_new_tab_res
      "The notification panel is not opened after keeping the changes");
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // FIXME: We need to enable the add-on so it gets cleared from the
   // ExtensionSettingsStore for now. See bug 1408226.
   let addonsEnabled = Promise.all([
     waitForAddonEnabled(addonOne), waitForAddonEnabled(addonTwo)]);
-  addonOne.userDisabled = false;
-  addonTwo.userDisabled = false;
+  await addonOne.enable();
+  await addonTwo.enable();
   await addonsEnabled;
   await extensionOne.unload();
   await extensionTwo.unload();
 });
 
 /**
  * Ensure we don't show the extension URL in the URL bar temporarily in new tabs
  * while we're switching remoteness (when the URL we're loading and the
--- a/browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab.js
@@ -120,25 +120,25 @@ add_task(async function test_multiple_ex
 
   ext2.sendMessage("tryClear");
   await ext2.awaitMessage("newTabPageCleared");
   await checkNewTabPageOverride(ext1, NEWTAB_URI_2, CONTROLLED_BY_OTHER);
 
   // Disable the second extension.
   let addon = await AddonManager.getAddonByID(EXT_2_ID);
   let disabledPromise = awaitEvent("shutdown");
-  addon.userDisabled = true;
+  await addon.disable();
   await disabledPromise;
   equal(aboutNewTabService.newTabURL, DEFAULT_NEW_TAB_URL,
         "newTabURL url is reset to the default after second extension is disabled.");
   await checkNewTabPageOverride(ext1, aboutNewTabService.newTabURL, NOT_CONTROLLABLE);
 
   // Re-enable the second extension.
   let enabledPromise = awaitEvent("ready");
-  addon.userDisabled = false;
+  await addon.enable();
   await enabledPromise;
   ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
      "newTabURL is overridden by the second extension.");
   await checkNewTabPageOverride(ext2, NEWTAB_URI_2, CONTROLLED_BY_THIS);
 
   await ext1.unload();
   ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
      "newTabURL is still overridden by the second extension.");
@@ -146,25 +146,25 @@ add_task(async function test_multiple_ex
 
   await ext3.startup();
   ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
      "newTabURL is overridden by the third extension.");
   await checkNewTabPageOverride(ext2, NEWTAB_URI_3, CONTROLLED_BY_OTHER);
 
   // Disable the second extension.
   disabledPromise = awaitEvent("shutdown");
-  addon.userDisabled = true;
+  await addon.disable();
   await disabledPromise;
   ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
      "newTabURL is still overridden by the third extension.");
   await checkNewTabPageOverride(ext3, NEWTAB_URI_3, CONTROLLED_BY_THIS);
 
   // Re-enable the second extension.
   enabledPromise = awaitEvent("ready");
-  addon.userDisabled = false;
+  await addon.enable();
   await enabledPromise;
   ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_3),
      "newTabURL is still overridden by the third extension.");
   await checkNewTabPageOverride(ext3, NEWTAB_URI_3, CONTROLLED_BY_THIS);
 
   await ext3.unload();
   ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI_2),
      "newTabURL reverts to being overridden by the second extension.");
--- a/browser/components/preferences/in-content/extensionControlled.js
+++ b/browser/components/preferences/in-content/extensionControlled.js
@@ -237,17 +237,17 @@ function showEnableExtensionMessage(sett
   });
   elements.description.appendChild(dismissButton);
 }
 
 function makeDisableControllingExtension(type, settingName) {
   return async function disableExtension() {
     let {id} = await getControllingExtensionInfo(type, settingName);
     let addon = await AddonManager.getAddonByID(id);
-    addon.userDisabled = true;
+    await addon.disable();
   };
 }
 
 function initializeProxyUI(container) {
   let deferredUpdate = new DeferredTask(() => {
     container.updateProxySettingsUI();
   }, 10);
   let proxyObserver = {
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -136,17 +136,17 @@ add_task(async function testExtensionCon
   is(homeModeEl.disabled, false, "The homepage menulist is enabled");
   is(controlledContent.hidden, true, "The extension controlled row is hidden");
 
   // Cleanup the add-on and tab.
   let addon = await AddonManager.getAddonByID("@set_homepage");
   // Enable the extension so we get the UNINSTALL event, which is needed by
   // ExtensionPreferencesManager to clean up properly.
   // FIXME: See https://bugzilla.mozilla.org/show_bug.cgi?id=1408226.
-  addon.userDisabled = false;
+  await addon.enable();
   await waitForMessageShown("browserHomePageExtensionContent");
   // Do the uninstall now that the enable code has been run.
   addon.uninstall();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(async function testPrefLockedHomepage() {
   await openPreferencesViaOpenPreferencesAPI("paneHome", {leaveOpen: true});
@@ -581,17 +581,17 @@ add_task(async function testExtensionCon
     // The user can dismiss the enable instructions.
     let hidden = waitForMessageHidden(labelId);
     controlledLabel.querySelector("image:last-of-type").click();
     await hidden;
   }
 
   async function reEnableExtension(addon) {
     let controlledMessageShown = waitForMessageShown(CONTROLLED_LABEL_ID[uiType]);
-    addon.userDisabled = false;
+    await addon.enable();
     await controlledMessageShown;
   }
 
   let uiType = "new";
 
   await openPreferencesViaOpenPreferencesAPI("panePrivacy", {leaveOpen: true});
   let doc = gBrowser.contentDocument;
 
@@ -752,17 +752,17 @@ add_task(async function testExtensionCon
     // The user can dismiss the enable instructions.
     let hidden = waitForMessageHidden(sectionId, panelDoc);
     controlledSection.querySelector("image:last-of-type").click();
     return hidden;
   }
 
   async function reEnableExtension(addon) {
     let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
-    addon.userDisabled = false;
+    await addon.enable();
     await messageChanged;
   }
 
   async function openProxyPanel() {
     let panel = await openAndLoadSubDialog(PANEL_URL);
     let closingPromise = waitForEvent(panel.document.documentElement, "dialogclosing");
     ok(panel, "Proxy panel opened.");
     return {panel, closingPromise};
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js
@@ -17,17 +17,17 @@ ChromeUtils.defineModuleGetter(this, "Te
 async function install(data, reason) {
   if (!isAppSupported()) {
     uninstallExtension(data);
     return;
   }
 
   let addon = await AddonManager.getAddonByID(data.id);
   if (addon) {
-    addon.userDisabled = false;
+    await addon.enable();
   }
 }
 
 async function startup(data, reason) {
   if (!isAppSupported()) {
     uninstallExtension(data);
     return;
   }
--- a/devtools/client/webide/modules/addons.js
+++ b/devtools/client/webide/modules/addons.js
@@ -74,17 +74,17 @@ Addon.prototype = {
   install: async function() {
     let addon = await AddonManager.getAddonByID(this.addonID);
     if (addon && !addon.userDisabled) {
       this.status = "installed";
       return;
     }
     this.status = "preparing";
     if (addon && addon.userDisabled) {
-      addon.userDisabled = false;
+      await addon.enable();
     } else {
       let install = await AddonManager.getInstallForURL(this.xpiLink, "application/x-xpinstall");
       install.addListener(this);
       install.install();
     }
   },
 
   uninstall: async function() {
@@ -109,17 +109,17 @@ Addon.prototype = {
     if (install.maxProgress == -1) {
       this.emit("progress", -1);
     } else {
       this.emit("progress", install.progress / install.maxProgress);
     }
   },
 
   onInstallEnded: function({addon}) {
-    addon.userDisabled = false;
+    addon.enable();
   },
 
   onDownloadCancelled: function(install) {
     this.installFailureHandler(install, "Download cancelled");
   },
   onDownloadFailed: function(install) {
     this.installFailureHandler(install, "Download failed");
   },
--- a/devtools/shared/gcli/commands/addon.js
+++ b/devtools/shared/gcli/commands/addon.js
@@ -246,17 +246,17 @@ var items = [
         name: "addon",
         type: "addon",
         description: l10n.lookup("addonNameDesc")
       }
     ],
     exec: function(args, context) {
       let name = (args.addon.name + " " + args.addon.version).trim();
       if (args.addon.userDisabled) {
-        args.addon.userDisabled = false;
+        args.addon.enable();
         return l10n.lookupFormat("addonEnabled", [ name ]);
       }
 
       return l10n.lookupFormat("addonAlreadyEnabled", [ name ]);
     }
   },
   {
     item: "command",
@@ -272,17 +272,17 @@ var items = [
     ],
     exec: function(args, context) {
       // If the addon is not disabled or is set to "click to play" then
       // disable it. Otherwise display the message "Add-on is already
       // disabled."
       let name = (args.addon.name + " " + args.addon.version).trim();
       if (!args.addon.userDisabled ||
           args.addon.userDisabled === AddonManager.STATE_ASK_TO_ACTIVATE) {
-        args.addon.userDisabled = true;
+        args.addon.disable();
         return l10n.lookupFormat("addonDisabled", [ name ]);
       }
 
       return l10n.lookupFormat("addonAlreadyDisabled", [ name ]);
     }
   },
   {
     item: "command",
--- a/mobile/android/chrome/content/aboutAddons.js
+++ b/mobile/android/chrome/content/aboutAddons.js
@@ -514,36 +514,43 @@ var Addons = {
   setEnabled: function setEnabled(aValue, aAddon) {
     let detailItem = document.querySelector("#addons-details > .addon-item");
     let addon = aAddon || detailItem.addon;
     if (!addon)
       return;
 
     let listItem = this._getElementForAddon(addon.id);
 
+    function setDisabled(addon, value) {
+      if (value) {
+        return addon.enable();
+      }
+      return addon.disable();
+    }
+
     let opType;
     if (addon.type == "theme") {
       if (aValue) {
         // We can have only one theme enabled, so disable the current one if any
         let list = document.getElementById("addons-list");
         let item = list.firstElementChild;
         while (item) {
           if (item.addon && (item.addon.type == "theme") && (item.addon.isActive)) {
-            item.addon.userDisabled = true;
+            item.addon.disable();
             item.setAttribute("isDisabled", true);
             break;
           }
           item = item.nextSibling;
         }
       }
-      addon.userDisabled = !aValue;
+      setDisabled(addon, !aValue);
     } else if (addon.type == "locale") {
-      addon.userDisabled = !aValue;
+      setDisabled(addon, !aValue);
     } else {
-      addon.userDisabled = !aValue;
+      setDisabled(addon, !aValue);
       opType = this._getOpTypeForOperations(addon.pendingOperations);
 
       if ((addon.pendingOperations & AddonManager.PENDING_ENABLE) ||
           (addon.pendingOperations & AddonManager.PENDING_DISABLE)) {
         this.showRestart();
       } else if (listItem && /needs-(enable|disable)/.test(listItem.getAttribute("opType"))) {
         this.hideRestart();
       }
--- a/mobile/android/components/BlocklistPrompt.js
+++ b/mobile/android/components/BlocklistPrompt.js
@@ -42,16 +42,16 @@ BlocklistPrompt.prototype = {
                                    "PRIORITY_CRITICAL_HIGH",
                                    buttons);
     }
     // Disable softblocked items automatically
     for (let i = 0; i < aAddons.length; i++) {
       if (aAddons[i].item instanceof Ci.nsIPluginTag)
         aAddons[i].item.disabled = true;
       else
-        aAddons[i].item.userDisabled = true;
+        aAddons[i].item.disable();
     }
   },
   classID: Components.ID("{4e6ea350-b09a-11df-94e2-0800200c9a66}"),
   QueryInterface: ChromeUtils.generateQI([])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BlocklistPrompt]);
--- a/services/sync/modules/addonutils.js
+++ b/services/sync/modules/addonutils.js
@@ -88,17 +88,17 @@ AddonUtilsInternal.prototype = {
               install.addon.syncGUID = options.syncGUID;
             }
 
             // We only need to change userDisabled if it is disabled because
             // enabled is the default.
             if ("enabled" in options && !options.enabled) {
               log.info("Marking add-on as disabled for install: " +
                        install.name);
-              install.addon.userDisabled = true;
+              install.addon.disable();
             }
           },
           onInstallEnded(install, addon) {
             install.removeListener(listener);
 
             res({id: addon.id, install, addon});
           },
           onInstallFailed(install) {
@@ -347,16 +347,20 @@ AddonUtilsInternal.prototype = {
    *        (bool) New value for add-on's userDisabled property.
    */
   updateUserDisabled(addon, value) {
     if (addon.userDisabled == value) {
       return;
     }
 
     this._log.info("Updating userDisabled flag: " + addon.id + " -> " + value);
-    addon.userDisabled = !!value;
+    if (value) {
+      addon.disable();
+    } else {
+      addon.enable();
+    }
   },
 
 };
 
 XPCOMUtils.defineLazyGetter(this, "AddonUtils", function() {
   return new AddonUtilsInternal();
 });
--- a/services/sync/tests/unit/test_addons_tracker.js
+++ b/services/sync/tests/unit/test_addons_tracker.js
@@ -163,17 +163,17 @@ add_task(async function test_track_user_
       onDisabling(disabling) {
         _("onDisabling add-on");
       }
     };
     AddonManager.addAddonListener(listener);
   });
 
   _("Disabling add-on");
-  addon.userDisabled = true;
+  await addon.disable();
   _("Disabling started...");
   await disabledPromise;
   await reconciler.queueCaller.promiseCallsComplete();
 
   let changed = await tracker.getChangedIDs();
   Assert.equal(1, Object.keys(changed).length);
   Assert.ok(addon.syncGUID in changed);
   Assert.equal(SCORE_INCREMENT_XLARGE, tracker.score);
@@ -183,23 +183,23 @@ add_task(async function test_track_user_
 });
 
 add_task(async function test_track_enable() {
   _("Ensure that enabling a disabled add-on notifies tracker.");
 
   reconciler.startListening();
 
   let addon = await installAddon(XPIS.test_bootstrap1_1, reconciler);
-  addon.userDisabled = true;
+  await addon.disable();
   await Async.promiseYield();
 
   Assert.equal(0, tracker.score);
 
   tracker.start();
-  addon.userDisabled = false;
+  await addon.enable();
   await Async.promiseYield();
   await reconciler.queueCaller.promiseCallsComplete();
 
   let changed = await tracker.getChangedIDs();
   Assert.equal(1, Object.keys(changed).length);
   Assert.ok(addon.syncGUID in changed);
   Assert.equal(SCORE_INCREMENT_XLARGE, tracker.score);
 
--- a/toolkit/components/extensions/parent/ext-management.js
+++ b/toolkit/components/extensions/parent/ext-management.js
@@ -222,17 +222,21 @@ this.management = class extends Extensio
             throw new ExtensionError(`No such addon ${id}`);
           }
           if (addon.type !== "theme") {
             throw new ExtensionError("setEnabled applies only to theme addons");
           }
           if (addon.isSystem) {
             throw new ExtensionError("setEnabled cannot be used with a system addon");
           }
-          addon.userDisabled = !enabled;
+          if (enabled) {
+            await extension.enable();
+          } else {
+            await extension.disable();
+          }
         },
 
         onDisabled: new EventManager({
           context,
           name: "management.onDisabled",
           register: fire => {
             let listener = (event, data) => {
               fire.async(data);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_privacy_disable.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_privacy_disable.js
@@ -140,44 +140,44 @@ add_task(async function test_disable() {
   ok(!data.value, "Value set to false for the newest extension.");
 
   // Verify the prefs have been set to match the "false" setting.
   checkPrefs(false);
 
   // Disable the newest extension.
   let disabledPromise = awaitPrefChange(PREF_TO_WATCH);
   let newAddon = await AddonManager.getAddonByID(NEW_ID);
-  newAddon.userDisabled = true;
+  await newAddon.disable();
   await disabledPromise;
 
   // Verify the prefs have been set to match the "true" setting.
   checkPrefs(true);
 
   // Disable the older extension.
   disabledPromise = awaitPrefChange(PREF_TO_WATCH);
   let oldAddon = await AddonManager.getAddonByID(OLD_ID);
-  oldAddon.userDisabled = true;
+  await oldAddon.disable();
   await disabledPromise;
 
   // Verify the prefs have reverted back to their initial values.
   for (let pref in PREFS) {
     equal(Preferences.get(pref), PREFS[pref], `${pref} reset correctly.`);
   }
 
   // Re-enable the newest extension.
   let enabledPromise = awaitEvent("ready");
-  newAddon.userDisabled = false;
+  await newAddon.enable();
   await enabledPromise;
 
   // Verify the prefs have been set to match the "false" setting.
   checkPrefs(false);
 
   // Re-enable the older extension.
   enabledPromise = awaitEvent("ready");
-  oldAddon.userDisabled = false;
+  await oldAddon.enable();
   await enabledPromise;
 
   // Verify the prefs have remained set to match the "false" setting.
   checkPrefs(false);
 
   for (let extension of testExtensions) {
     await extension.unload();
   }
--- a/toolkit/components/extensions/test/xpcshell/test_ext_runtime_onInstalled_and_onStartup.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_onInstalled_and_onStartup.js
@@ -301,19 +301,18 @@ add_task(async function test_should_not_
   await expectEvents(extension, {
     onStartupFired: false,
     onInstalledFired: true,
     onInstalledTemporary: false,
     onInstalledReason: "install",
   });
 
   let addon = await AddonManager.getAddonByID(EXTENSION_ID);
-  addon.userDisabled = true;
-
-  addon.userDisabled = false;
+  await addon.disable();
+  await addon.enable();
   await extension.awaitStartup();
 
   await expectEvents(extension, {
     onStartupFired: false,
     onInstalledFired: false,
   });
 
   await extension.markUnloaded();
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -1092,24 +1092,24 @@ add_task(async function test_addonsWatch
   let checkpointPromise = registerCheckpointPromise(1);
   await installXPIFromURL(ADDON_INSTALL_URL);
   await checkpointPromise;
   assertCheckpoint(1);
   Assert.ok(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons);
 
   checkpointPromise = registerCheckpointPromise(2);
   let addon = await AddonManager.getAddonByID(ADDON_ID);
-  addon.userDisabled = true;
+  await addon.disable();
   await checkpointPromise;
   assertCheckpoint(2);
   Assert.ok(!(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons));
 
   checkpointPromise = registerCheckpointPromise(3);
   let startupPromise = AddonTestUtils.promiseWebExtensionStartup(ADDON_ID);
-  addon.userDisabled = false;
+  await addon.enable();
   await checkpointPromise;
   assertCheckpoint(3);
   Assert.ok(ADDON_ID in TelemetryEnvironment.currentEnvironment.addons.activeAddons);
   await startupPromise;
 
   checkpointPromise = registerCheckpointPromise(4);
   (await AddonManager.getAddonByID(ADDON_ID)).uninstall();
   await checkpointPromise;
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -512,16 +512,23 @@ AddonWrapper.prototype = {
     if (val)
       LightweightThemeManager.currentTheme = null;
     else
       LightweightThemeManager.currentTheme = themeFor(this);
 
     return val;
   },
 
+  async enable() {
+    this.userDisabled = false;
+  },
+  async disable() {
+    this.userDisabled = true;
+  },
+
   // Lightweight themes are never disabled by the application
   get appDisabled() {
     return false;
   },
 
   // Lightweight themes are always compatible
   get isCompatible() {
     return true;
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1142,26 +1142,26 @@ var gViewController = {
             let subject = {
               wrappedJSObject: {
                 target: getBrowserElement(),
                 info: {
                   type: "sideload",
                   addon: aAddon,
                   icon: aAddon.iconURL,
                   permissions: perms,
-                  resolve() { aAddon.userDisabled = false; },
+                  resolve() { aAddon.enable(); },
                   reject() {},
                 },
               },
             };
             Services.obs.notifyObservers(subject, "webextension-permission-prompt");
             return;
           }
         }
-        aAddon.userDisabled = false;
+        aAddon.enable();
       },
       getTooltip(aAddon) {
         if (!aAddon)
           return "";
         return gStrings.ext.GetStringFromName("enableAddonTooltip");
       }
     },
 
@@ -1169,17 +1169,17 @@ var gViewController = {
       isEnabled(aAddon) {
         if (!aAddon)
           return false;
         let addonType = AddonManager.addonTypes[aAddon.type];
         return (!(addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
                 hasPermission(aAddon, "disable"));
       },
       doCommand(aAddon) {
-        aAddon.userDisabled = true;
+        aAddon.disable();
       },
       getTooltip(aAddon) {
         if (!aAddon)
           return "";
         return gStrings.ext.GetStringFromName("disableAddonTooltip");
       }
     },
 
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1307,17 +1307,17 @@
           // uninstall later.
           if (this.typeHasFlag("SUPPORTS_UNDO_RESTARTLESS_UNINSTALL")) {
             this.mAddon.uninstall(true);
           } else {
             this.setAttribute("wasDisabled", this.mAddon.userDisabled);
 
             // We must set userDisabled to true first, this will call
             // _updateState which will clear any pending attribute set.
-            this.mAddon.userDisabled = true;
+            this.mAddon.disable();
 
             // This won't update any other add-on manager views (bug 582002)
             this.setAttribute("pending", "uninstall");
           }
         ]]></body>
       </method>
 
       <method name="showPreferences">
@@ -1560,45 +1560,53 @@
         <body><![CDATA[
           // This assumes that disabling does not require a restart when
           // uninstalling doesn't. Things will still work if not, the add-on
           // will just still be active until finally getting uninstalled.
 
           if (this.isPending("uninstall"))
             this.mAddon.cancelUninstall();
           else if (this.getAttribute("wasDisabled") != "true")
-            this.mAddon.userDisabled = false;
+            this.mAddon.enable();
 
           this.removeAttribute("pending");
         ]]></body>
       </method>
 
       <method name="onExternalInstall">
         <parameter name="aAddon"/>
         <parameter name="aExistingAddon"/>
         <body><![CDATA[
           if (aExistingAddon.id != this.mAddon.id)
             return;
 
           // Make sure any newly installed add-on has the correct disabled state
-          if (this.hasAttribute("wasDisabled"))
-            aAddon.userDisabled = this.getAttribute("wasDisabled") == "true";
+          if (this.hasAttribute("wasDisabled")) {
+            if (this.getAttribute("wasDisabled") == "true")
+              aAddon.disable();
+            else
+              aAddon.enable();
+          }
 
           this.mAddon = aAddon;
 
           this.removeAttribute("pending");
         ]]></body>
       </method>
 
       <method name="onInstallStarted">
         <parameter name="aInstall"/>
         <body><![CDATA[
           // Make sure any newly installed add-on has the correct disabled state
-          if (this.hasAttribute("wasDisabled"))
-            aInstall.addon.userDisabled = this.getAttribute("wasDisabled") == "true";
+          if (this.hasAttribute("wasDisabled")) {
+            if (this.getAttribute("wasDisabled") == "true")
+              aInstall.addon.disable();
+            else
+              aInstall.addon.enable();
+          }
         ]]></body>
       </method>
 
       <method name="onInstallEnded">
         <parameter name="aInstall"/>
         <parameter name="aAddon"/>
         <body><![CDATA[
           this.mAddon = aAddon;
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -185,16 +185,23 @@ GMPWrapper.prototype = {
 
   get userDisabled() {
     return !GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_ENABLED, true, this._plugin.id);
   },
   set userDisabled(aVal) {
     GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_ENABLED, aVal === false, this._plugin.id);
   },
 
+  async enable() {
+    this.userDisabled = false;
+  },
+  async disable() {
+    this.userDisabled = true;
+  },
+
   get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; },
   get size() { return 0; },
   get scope() { return AddonManager.SCOPE_APPLICATION; },
   get pendingOperations() { return AddonManager.PENDING_NONE; },
 
   get operationsRequiringRestart() { return AddonManager.OP_NEEDS_RESTART_NONE; },
 
   get permissions() {
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -358,16 +358,23 @@ PluginWrapper.prototype = {
     if (previousVal == AddonManager.STATE_ASK_TO_ACTIVATE ||
         val == AddonManager.STATE_ASK_TO_ACTIVATE) {
       AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["userDisabled"]);
     }
 
     return val;
   },
 
+  async enable() {
+    this.userDisabled = false;
+  },
+  async disable() {
+    this.userDisabled = true;
+  },
+
   get blocklistState() {
     let { tags: [tag] } = pluginFor(this);
     return tag.blocklistState;
   },
 
   async getBlocklistURL() {
     let { tags: [tag] } = pluginFor(this);
     return Blocklist.getPluginBlockURL(tag);
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -385,16 +385,20 @@ class AddonInternal {
       return true;
     return this.signedState > AddonManager.SIGNEDSTATE_MISSING;
   }
 
   get isCompatible() {
     return this.isCompatibleWith();
   }
 
+  get hidden() {
+    return this._installLocation.isSystem;
+  }
+
   get disabled() {
     return (this.userDisabled || this.appDisabled || this.softDisabled);
   }
 
   get isPlatformCompatible() {
     if (this.targetPlatforms.length == 0)
       return true;
 
@@ -545,16 +549,36 @@ class AddonInternal {
         this.userDisabled = userDisabled;
       }
       if (softDisabled !== undefined) {
         this.softDisabled = softDisabled;
       }
     }
   }
 
+  async setUserDisabled(val) {
+    if (val == (this.userDisabled || this.softDisabled)) {
+      return;
+    }
+
+    if (this.inDatabase) {
+      // hidden and system add-ons should not be user disabled,
+      // as there is no UI to re-enable them.
+      if (this.hidden) {
+        throw new Error(`Cannot disable hidden add-on ${this.id}`);
+      }
+      await XPIDatabase.updateAddonDisabledState(this, val);
+    } else {
+      this.userDisabled = val;
+      // When enabling remove the softDisabled flag
+      if (!val)
+        this.softDisabled = false;
+    }
+  }
+
   applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
     let wasCompatible = this.isCompatible;
 
     for (let targetApp of this.targetApplications) {
       for (let updateTarget of aUpdate.targetApplications) {
         if (targetApp.id == updateTarget.id && (aSyncCompatibility ||
             Services.vc.compare(targetApp.maxVersion, updateTarget.maxVersion) < 0)) {
           targetApp.minVersion = updateTarget.minVersion;
@@ -916,37 +940,23 @@ AddonWrapper = class {
   updateBlocklistState(applySoftBlock = true) {
     return addonFor(this).updateBlocklistState({applySoftBlock});
   }
 
   get userDisabled() {
     let addon = addonFor(this);
     return addon.softDisabled || addon.userDisabled;
   }
-  set userDisabled(val) {
-    let addon = addonFor(this);
-    if (val == this.userDisabled) {
-      return val;
-    }
-
-    if (addon.inDatabase) {
-      // hidden and system add-ons should not be user disabled,
-      // as there is no UI to re-enable them.
-      if (this.hidden) {
-        throw new Error(`Cannot disable hidden add-on ${addon.id}`);
-      }
-      XPIDatabase.updateAddonDisabledState(addon, val);
-    } else {
-      addon.userDisabled = val;
-      // When enabling remove the softDisabled flag
-      if (!val)
-        addon.softDisabled = false;
-    }
-
-    return val;
+
+  enable() {
+    return addonFor(this).setUserDisabled(false);
+  }
+
+  disable() {
+    return addonFor(this).setUserDisabled(true);
   }
 
   set softDisabled(val) {
     let addon = addonFor(this);
     if (val == addon.softDisabled)
       return val;
 
     if (addon.inDatabase) {
@@ -961,21 +971,17 @@ AddonWrapper = class {
       // Only set softDisabled if not already disabled
       addon.softDisabled = val;
     }
 
     return val;
   }
 
   get hidden() {
-    let addon = addonFor(this);
-    if (addon._installLocation.name == KEY_APP_TEMPORARY)
-      return false;
-
-    return addon._installLocation.isSystem;
+    return addonFor(this).hidden;
   }
 
   get isSystem() {
     let addon = addonFor(this);
     return addon._installLocation.isSystem;
   }
 
   // Returns true if Firefox Sync should sync this addon. Only addons
--- a/toolkit/mozapps/extensions/test/browser/browser_bug591465.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug591465.js
@@ -137,38 +137,38 @@ add_test(function() {
   }, {once: true});
 
   info("Opening context menu on enabled extension item");
   el.parentNode.ensureElementIsVisible(el);
   EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
   EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
 });
 
-add_test(function() {
+add_task(async function() {
   var el = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
   isnot(el, null, "Should have found addon element");
-  el.mAddon.userDisabled = true;
+  await el.mAddon.disable();
 
   gContextMenu.addEventListener("popupshown", function() {
     check_contextmenu(false, false, false, false, false);
 
     gContextMenu.hidePopup();
     run_next_test();
   }, {once: true});
 
   info("Opening context menu on newly disabled extension item");
   el.parentNode.ensureElementIsVisible(el);
   EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
   EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
 });
 
-add_test(function() {
+add_task(async function() {
   var el = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
   isnot(el, null, "Should have found addon element");
-  el.mAddon.userDisabled = false;
+  await el.mAddon.enable();
 
   gContextMenu.addEventListener("popupshown", function() {
     check_contextmenu(false, true, false, false, false);
 
     gContextMenu.hidePopup();
     run_next_test();
   }, {once: true});
 
--- a/toolkit/mozapps/extensions/test/browser/browser_bug596336.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug596336.js
@@ -73,17 +73,17 @@ add_task(async function() {
   is(get_list_item_count(), 0, "Should be no items in the list");
 });
 
 // Install version 1 mark it as disabled then upgrade to version 2 with the
 // manager open
 add_task(async function() {
   await install_addon("browser_bug596336_1");
   let [aAddon] = await promiseAddonsByIDs(["bug596336-1@tests.mozilla.org"]);
-  aAddon.userDisabled = true;
+  await aAddon.disable();
   await check_addon(aAddon, "1.0");
   ok(aAddon.userDisabled, "Add-on should be disabled");
 
   await install_addon("browser_bug596336_2");
   [aAddon] = await promiseAddonsByIDs(["bug596336-1@tests.mozilla.org"]);
   await check_addon(aAddon, "2.0");
   ok(aAddon.userDisabled, "Add-on should be disabled");
 
@@ -119,17 +119,17 @@ add_task(async function() {
   is(get_list_item_count(), 0, "Should be no items in the list");
 });
 
 // Install version 1, disable it, click the remove button and then upgrade to
 // version 2 with the manager open
 add_task(async function() {
   await install_addon("browser_bug596336_1");
   let [aAddon] = await promiseAddonsByIDs(["bug596336-1@tests.mozilla.org"]);
-  aAddon.userDisabled = true;
+  await aAddon.disable();
   await check_addon(aAddon, "1.0");
   ok(aAddon.userDisabled, "Add-on should be disabled");
 
   let item = get_addon_element(gManagerWindow, "bug596336-1@tests.mozilla.org");
   EventUtils.synthesizeMouseAtCenter(get_node(item, "remove-btn"), { }, gManagerWindow);
 
   // Force XBL to apply
   item.clientTop;
--- a/toolkit/mozapps/extensions/test/browser/browser_details.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_details.js
@@ -592,17 +592,17 @@ add_test(function() {
   });
 });
 
 // Check that onPropertyChanges for appDisabled updates the UI
 add_test(async function() {
   info("Checking that onPropertyChanges for appDisabled updates the UI");
 
   let aAddon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
-  aAddon.userDisabled = true;
+  await aAddon.disable();
   aAddon.isCompatible = true;
   aAddon.appDisabled = false;
 
   open_details("addon1@tests.mozilla.org", "extension", function() {
     is(get("detail-view").getAttribute("active"), "false", "Addon should not be marked as active");
     is_element_hidden(get("detail-warning"), "Warning message should not be visible");
 
     info("Making addon incompatible and appDisabled");
--- a/toolkit/mozapps/extensions/test/browser/browser_list.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_list.js
@@ -482,17 +482,17 @@ add_task(async function() {
   aAddon.uninstall();
 });
 
 // Check that onPropertyChanges for appDisabled updates the UI
 add_task(async function() {
   info("Checking that onPropertyChanges for appDisabled updates the UI");
 
   let [aAddon] = await promiseAddonsByIDs(["addon2@tests.mozilla.org"]);
-  aAddon.userDisabled = true;
+  await aAddon.disable();
   aAddon.isCompatible = true;
   aAddon.appDisabled = false;
 
   gManagerWindow.loadView("addons://list/extension");
   await new Promise(resolve => wait_for_view_load(gManagerWindow, resolve));
   var el = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
 
   is(el.getAttribute("active"), "false", "Addon should not be marked as active");
--- a/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js
@@ -132,17 +132,17 @@ add_test(async function() {
   var ID = "addon2@tests.mozilla.org";
   var list = gDocument.getElementById("addon-list");
 
   // Select the extensions category
   await gCategoryUtilities.openType("extension");
   is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
 
   let aAddon = await AddonManager.getAddonByID(ID);
-  aAddon.userDisabled = true;
+  await aAddon.disable();
 
   ok(!aAddon.isActive, "Add-on should be inactive");
   ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
   ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
 
   var item = get_item_in_list(ID, list);
   isnot(item, null, "Should have found the add-on in the list");
 
@@ -168,17 +168,17 @@ add_test(async function() {
   // Force XBL to apply
   item.clientTop;
 
   ok(!aAddon.isActive, "Add-on should be inactive");
   button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
   isnot(button, null, "Should have a remove button");
   ok(!button.disabled, "Button should not be disabled");
 
-  aAddon.userDisabled = false;
+  await aAddon.enable();
   ok(aAddon.isActive, "Add-on should be active");
 
   run_next_test();
 });
 
 // Tests that uninstalling a restartless add-on from the details view switches
 // back to the list view and can be undone
 add_test(async function() {
@@ -243,17 +243,17 @@ add_test(async function() {
   var ID = "addon2@tests.mozilla.org";
   var list = gDocument.getElementById("addon-list");
 
   // Select the extensions category
   await gCategoryUtilities.openType("extension");
   is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension");
 
   let aAddon = await AddonManager.getAddonByID(ID);
-  aAddon.userDisabled = true;
+  await aAddon.disable();
 
   ok(!aAddon.isActive, "Add-on should be inactive");
   ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall");
   ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
 
   var item = get_item_in_list(ID, list);
   isnot(item, null, "Should have found the add-on in the list");
 
@@ -289,17 +289,17 @@ add_test(async function() {
   // Force XBL to apply
   item.clientTop;
 
   ok(!aAddon.isActive, "Add-on should be inactive");
   button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
   isnot(button, null, "Should have a remove button");
   ok(!button.disabled, "Button should not be disabled");
 
-  aAddon.userDisabled = false;
+  await aAddon.enable();
   ok(aAddon.isActive, "Add-on should be active");
 
   run_next_test();
 });
 
 // Tests that switching away from the list view finalises the uninstall of
 // multiple restartless add-ons
 add_test(async function() {
--- a/toolkit/mozapps/extensions/test/browser/browser_webapi_addon_listener.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_addon_listener.js
@@ -39,17 +39,17 @@ provider.createAddons([
 
 // Test enable of add-on requiring restart
 add_task(async function test_enable() {
   await BrowserTestUtils.withNewTab(TESTPAGE, async function(browser) {
     let addon = await promiseAddonByID(RESTART_DISABLED_ID);
     is(addon.userDisabled, true, "addon is disabled");
 
     // enable it
-    addon.userDisabled = false;
+    await addon.enable();
     is(addon.userDisabled, false, "addon was enabled successfully");
 
     let events = await getListenerEvents(browser);
 
     // Just a single onEnabling since restart is needed to complete
     let expected = [
       {id: RESTART_DISABLED_ID, needsRestart: true, event: "onEnabling"},
     ];
@@ -59,21 +59,21 @@ add_task(async function test_enable() {
 
 // Test enable/disable events for restartless
 add_task(async function test_restartless() {
   await BrowserTestUtils.withNewTab(TESTPAGE, async function(browser) {
     let addon = await promiseAddonByID(RESTARTLESS_ID);
     is(addon.userDisabled, false, "addon is enabled");
 
     // disable it
-    addon.userDisabled = true;
+    await addon.disable();
     is(addon.userDisabled, true, "addon was disabled successfully");
 
     // re-enable it
-    addon.userDisabled = false;
+    await addon.enable();
     is(addon.userDisabled, false, "addon was re-enabled successfuly");
 
     let events = await getListenerEvents(browser);
     let expected = [
       {id: RESTARTLESS_ID, needsRestart: false, event: "onDisabling"},
       {id: RESTARTLESS_ID, needsRestart: false, event: "onDisabled"},
       {id: RESTARTLESS_ID, needsRestart: false, event: "onEnabling"},
       {id: RESTARTLESS_ID, needsRestart: false, event: "onEnabled"},
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -1101,16 +1101,23 @@ MockAddon.prototype = {
     var currentActive = this.shouldBeActive;
     this._userDisabled = val;
     var newActive = this.shouldBeActive;
     this._updateActiveState(currentActive, newActive);
 
     return val;
   },
 
+  async enable() {
+    this.userDisabled = false;
+  },
+  async disable() {
+    this.userDisabled = true;
+  },
+
   get permissions() {
     let permissions = this._permissions;
     if (this.appDisabled || !this._userDisabled)
       permissions &= ~AddonManager.PERM_CAN_ENABLE;
     if (this.appDisabled || this._userDisabled)
       permissions &= ~AddonManager.PERM_CAN_DISABLE;
     return permissions;
   },
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -1624,8 +1624,16 @@ function mockPluginHost(plugins) {
       return plugins.map(p => p.pluginTag);
     },
 
     QueryInterface: ChromeUtils.generateQI(["nsIPluginHost"]),
   };
 
   MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
 }
+
+async function setInitialState(addon, initialState) {
+  if (initialState.userDisabled) {
+    await addon.disable();
+  } else if (initialState.userDisabled === false) {
+    await addon.enable();
+  }
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_XPIStates.js
@@ -85,17 +85,17 @@ async function getXSJSON() {
 add_task(async function detect_touches() {
   await promiseStartupManager();
   let [/* pe */, pd] = await promiseAddonsByIDs([
          "packed-enabled@tests.mozilla.org",
          "packed-disabled@tests.mozilla.org",
          ]);
 
   info("Disable test add-ons");
-  pd.userDisabled = true;
+  await pd.disable();
 
   let XS = getXS();
 
   // Should be no changes detected here, because everything should start out up-to-date.
   Assert.ok(!XS.getInstallState());
 
   let states = XS.getLocation("app-profile");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
@@ -254,17 +254,17 @@ add_task(async function setup() {
     }
   }
 
   await promiseStartupManager();
 
   let addons = await getAddons(IDS);
   for (let [id, addon] of Object.entries(ADDONS)) {
     if (addon.initialState) {
-      Object.assign(addons.get(id), addon.initialState);
+      await setInitialState(addons.get(id), addon.initialState);
     }
     if (addon.findUpdates) {
       await promiseUpdates(addons.get(id));
     }
   }
 });
 
 add_task(async function test_after_restart() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_severities.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_severities.js
@@ -207,18 +207,18 @@ add_task(async function test_1() {
   // Blocked and incompatible should be app disabled only
   checkAddonState(addons[5], {userDisabled: false, softDisabled: false, appDisabled: true});
   checkAddonState(addons[6], {userDisabled: false, softDisabled: false, appDisabled: true});
 
   // We've overridden the plugin host so we cannot tell what that would have
   // initialised the plugins as
 
   // Put the add-ons into the base state
-  addons[0].userDisabled = true;
-  addons[4].userDisabled = false;
+  await addons[0].disable();
+  await addons[4].enable();
 
   await promiseRestartManager();
   await checkInitialState();
 
   await loadBlocklist("bug455906_warn.xml", args => {
     dump("Checking notification pt 2\n");
     equal(args.list.length, 4);
 
@@ -271,18 +271,18 @@ add_task(async function test_1() {
   checkAddonState(addons[3], {userDisabled: true, softDisabled: true, appDisabled: false});
   checkAddonState(addons[4], {userDisabled: false, softDisabled: false, appDisabled: false});
   equal(await check_plugin_state(PLUGINS[0]), "true,false");
   equal(await check_plugin_state(PLUGINS[1]), "false,false");
   equal(await check_plugin_state(PLUGINS[3]), "true,false");
   equal(await check_plugin_state(PLUGINS[4]), "false,false");
 
   // Back to starting state
-  addons[2].userDisabled = false;
-  addons[5].userDisabled = false;
+  await addons[2].enable();
+  await addons[5].enable();
   PLUGINS[2].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
   PLUGINS[5].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
 
   await promiseRestartManager();
   await loadBlocklist("bug455906_start.xml");
 });
 
 add_task(async function test_pt3() {
@@ -365,17 +365,17 @@ add_task(async function test_pt3() {
   equal(await check_plugin_state(PLUGINS[5]), "false,true");
 
   // Back to starting state
   await loadBlocklist("bug455906_start.xml");
 });
 
 add_task(async function test_pt4() {
   let addon = await AddonManager.getAddonByID(ADDONS[4].id);
-  addon.userDisabled = false;
+  await addon.enable();
   PLUGINS[4].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
 
   await promiseRestartManager();
   await checkInitialState();
 
   await loadBlocklist("bug455906_empty.xml", args => {
     dump("Checking notification pt 4\n");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
@@ -695,17 +695,17 @@ add_task(async function init() {
   await promiseWriteInstallRDFForExtension(softblock2_1, profileDir);
   await promiseWriteInstallRDFForExtension(softblock3_1, profileDir);
   await promiseWriteInstallRDFForExtension(softblock4_1, profileDir);
   await promiseWriteInstallRDFForExtension(hardblock_1, profileDir);
   await promiseWriteInstallRDFForExtension(regexpblock_1, profileDir);
   await promiseStartupManager();
 
   let [/* s1 */, /* s2 */, /* s3 */, s4, /* h, r */] = await promiseAddonsByIDs(ADDON_IDS);
-  s4.userDisabled = true;
+  await s4.disable();
 });
 
 // Starts with add-ons unblocked and then switches application versions to
 // change add-ons to blocked and back
 add_task(async function run_app_update_test() {
   await promiseRestartManager();
   await Pload_blocklist("app_update.xml");
   await promiseRestartManager();
@@ -727,20 +727,20 @@ add_task(async function app_update_step_
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 });
 
 add_task(async function app_update_step_3() {
   await promiseRestartManager();
 
   await promiseRestartManager("2.5");
 
@@ -761,18 +761,18 @@ add_task(async function app_update_step_
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
+  await s1.enable();
+  await s2.enable();
 });
 
 // Starts with add-ons unblocked and then switches application versions to
 // change add-ons to blocked and back. A DB schema change is faked to force a
 // rebuild when the application version changes
 add_task(async function run_app_update_schema_test() {
   await promiseRestartManager();
 
@@ -797,20 +797,20 @@ add_task(async function update_schema_2(
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 });
 
 add_task(async function update_schema_3() {
   await promiseRestartManager();
 
   await promiseShutdownManager();
   await changeXPIDBVersion(100);
@@ -854,18 +854,18 @@ add_task(async function update_schema_5(
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
+  await s1.enable();
+  await s2.enable();
 });
 
 // Starts with add-ons unblocked and then loads new blocklists to change add-ons
 // to blocked and back again.
 add_task(async function run_blocklist_update_test() {
   await Pload_blocklist("blocklist_update1.xml");
   await promiseRestartManager();
 
@@ -885,20 +885,20 @@ add_task(async function run_blocklist_up
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 
   await promiseRestartManager();
 
   await Pload_blocklist("blocklist_update2.xml");
   await promiseRestartManager();
 
   [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
@@ -917,18 +917,18 @@ add_task(async function run_blocklist_up
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
+  await s1.enable();
+  await s2.enable();
 });
 
 // Starts with add-ons unblocked and then new versions are installed outside of
 // the app to change them to blocked and back again.
 add_task(async function run_addon_change_test() {
   await Pload_blocklist("addon_change.xml");
   await promiseRestartManager();
 
@@ -964,20 +964,20 @@ add_task(async function run_addon_change
 
   check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 });
 
 add_task(async function run_addon_change_3() {
   await promiseRestartManager();
 
   await promiseShutdownManager();
 
@@ -1028,18 +1028,18 @@ add_task(async function run_addon_change
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
+  await s1.enable();
+  await s2.enable();
 });
 
 // Starts with add-ons blocked and then new versions are installed outside of
 // the app to change them to unblocked.
 add_task(async function run_addon_change_2_test() {
   await promiseShutdownManager();
 
   getFileForAddon(profileDir, softblock1_1.id).remove(true);
@@ -1064,20 +1064,20 @@ add_task(async function run_addon_change
   let [s1, s2, s3, /* s4 */, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 });
 
 add_task(async function addon_change_2_test_2() {
   await promiseRestartManager();
 
   await promiseShutdownManager();
 
@@ -1126,19 +1126,19 @@ add_task(async function addon_change_2_t
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
-  s4.userDisabled = true;
+  await s1.enable();
+  await s2.enable();
+  await s4.disable();
 });
 
 // Add-ons are initially unblocked then attempts to upgrade to blocked versions
 // in the background which should fail
 add_task(async function run_background_update_test() {
   await promiseRestartManager();
 
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
@@ -1190,38 +1190,38 @@ add_task(async function run_background_u
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 
   await promiseRestartManager();
 
   await Pbackground_update();
   await promiseRestartManager();
 
   [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
-  s4.userDisabled = true;
+  await s1.enable();
+  await s2.enable();
+  await s4.disable();
 });
 
 // Starts with add-ons blocked and then simulates the user upgrading them to
 // unblocked versions.
 add_task(async function run_manual_update_test() {
   await promiseRestartManager();
   await Pload_blocklist("manual_update.xml");
   await promiseRestartManager();
@@ -1230,20 +1230,20 @@ add_task(async function run_manual_updat
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 
   await promiseRestartManager();
 
   await Pmanual_update("2");
   await promiseRestartManager();
 
   [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
@@ -1296,20 +1296,20 @@ add_task(async function run_manual_updat
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 
-  s2.userDisabled = false;
-  s2.userDisabled = true;
+  await s2.enable();
+  await s2.disable();
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
-  s3.userDisabled = false;
+  await s3.enable();
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   await promiseRestartManager();
 
   await Pmanual_update("2");
   await promiseRestartManager();
 
   [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
@@ -1328,19 +1328,19 @@ add_task(async function run_manual_updat
   [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 
-  s1.userDisabled = false;
-  s2.userDisabled = false;
-  s4.userDisabled = true;
+  await s1.enable();
+  await s2.enable();
+  await s4.disable();
 });
 
 // Uses the API to install blocked add-ons from the local filesystem
 add_task(async function run_local_install_test() {
   await promiseShutdownManager();
 
   getFileForAddon(profileDir, softblock1_1.id).remove(true);
   getFileForAddon(profileDir, softblock2_1.id).remove(true);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -286,17 +286,17 @@ add_task(async function test_2() {
     [ID1]: [
       ["onDisabling", false],
       "onDisabled"
     ]
   });
 
   equal(b1.operationsRequiringRestart &
         AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
-  b1.userDisabled = true;
+  await b1.disable();
   ensure_test_completed();
 
   notEqual(b1, null);
   equal(b1.version, "1.0");
   ok(!b1.appDisabled);
   ok(b1.userDisabled);
   ok(!b1.isActive);
   BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
@@ -351,17 +351,17 @@ add_task(async function test_4() {
     [ID1]: [
       ["onEnabling", false],
       "onEnabled"
     ]
   });
 
   equal(b1.operationsRequiringRestart &
                AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
-  b1.userDisabled = false;
+  await b1.enable();
   ensure_test_completed();
 
   notEqual(b1, null);
   equal(b1.version, "1.0");
   ok(!b1.appDisabled);
   ok(!b1.userDisabled);
   ok(b1.isActive);
   ok(!b1.isSystem);
@@ -647,17 +647,17 @@ add_task(async function test_11() {
     [ID1]: [
       ["onDisabling", false],
       "onDisabled",
       ["onUninstalling", false],
       "onUninstalled"
     ]
   });
 
-  b1.userDisabled = true;
+  await b1.disable();
 
   BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
   BootstrapMonitor.checkAddonNotStarted(ID1);
   equal(getShutdownReason(), ADDON_DISABLE);
   equal(getShutdownNewVersion(), undefined);
   do_check_not_in_crash_annotation(ID1, "1.0");
 
   b1.uninstall();
@@ -799,17 +799,17 @@ add_task(async function test_15() {
   equal(b1.version, "1.0");
   ok(!b1.appDisabled);
   ok(!b1.userDisabled);
   ok(b1.isActive);
   ok(!b1.isSystem);
   BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
   BootstrapMonitor.checkAddonStarted(ID1, "1.0");
 
-  b1.userDisabled = true;
+  await b1.disable();
   ok(!b1.isActive);
   BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
   BootstrapMonitor.checkAddonNotStarted(ID1);
 
   prepare_test({}, [
     "onNewInstall"
   ]);
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrapped_chrome_manifest.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrapped_chrome_manifest.js
@@ -43,19 +43,19 @@ function checkActive(expected) {
 add_task(async function test() {
   let {addon} = await AddonTestUtils.promiseInstallXPI(ADDON);
 
   Assert.ok(addon.isActive);
 
   // Tests that chrome.manifest is registered when the addon is installed.
   checkActive(true);
 
-  addon.userDisabled = true;
+  await addon.disable();
   checkActive(false);
 
-  addon.userDisabled = false;
+  await addon.enable();
   checkActive(true);
 
   await promiseShutdownManager();
 
   // Tests that chrome.manifest remains registered at app shutdown.
   checkActive(true);
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
@@ -221,17 +221,17 @@ add_task(async function setup() {
     }
   }
 
   await promiseStartupManager();
 
   let addons = await getAddons(IDS);
   for (let [id, addon] of Object.entries(ADDONS)) {
     if (addon.initialState) {
-      Object.assign(addons.get(id), addon.initialState);
+      await setInitialState(addons.get(id), addon.initialState);
     }
     if (addon.findUpdates) {
       await promiseUpdates(addons.get(id));
     }
   }
 });
 
 add_task(async function test_after_restart() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_dictionary.js
@@ -195,17 +195,17 @@ add_task(async function test_2() {
     [ID_DICT]: [
       ["onDisabling", false],
       "onDisabled"
     ]
   });
 
   equal(addon.operationsRequiringRestart &
                AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
-  addon.userDisabled = true;
+  await addon.disable();
   ensure_test_completed();
 
   notEqual(addon, null);
   equal(addon.version, "1.0");
   ok(!addon.appDisabled);
   ok(addon.userDisabled);
   ok(!addon.isActive);
   ok(!HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
@@ -243,17 +243,17 @@ add_task(async function test_4() {
     [ID_DICT]: [
       ["onEnabling", false],
       "onEnabled"
     ]
   });
 
   equal(addon.operationsRequiringRestart &
                AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
-  addon.userDisabled = false;
+  await addon.enable();
   ensure_test_completed();
 
   notEqual(addon, null);
   equal(addon.version, "1.0");
   ok(!addon.appDisabled);
   ok(!addon.userDisabled);
   ok(addon.isActive);
   ok(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
--- a/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_duplicateplugins.js
@@ -113,17 +113,17 @@ async function run_test_1() {
 
   run_test_2();
 }
 
 // Test that disabling a coalesced plugin disables all its tags
 async function run_test_2() {
   let p = await AddonManager.getAddonByID(gPluginIDs[0]);
   Assert.ok(!p.userDisabled);
-  p.userDisabled = true;
+  await p.disable();
   Assert.ok(PLUGINS[0].disabled);
   Assert.ok(PLUGINS[1].disabled);
 
   executeSoon(run_test_3);
 }
 
 // Test that IDs persist across restart
 async function run_test_3() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
@@ -722,17 +722,17 @@ add_task(async function test_16() {
   await promiseRestartManager();
 
   let url = "http://example.com/addons/test_install2_1.xpi";
   let aInstall = await AddonManager.getInstallForURL(url, "application/x-xpinstall");
   await new Promise(resolve => {
     aInstall.addListener({
       onInstallStarted() {
         ok(!aInstall.addon.userDisabled);
-        aInstall.addon.userDisabled = true;
+        aInstall.addon.disable();
       },
 
       onInstallEnded() {
         resolve();
       }
     });
     aInstall.install();
   });
@@ -799,17 +799,17 @@ add_task(async function test_17() {
 
   let url_2 = "http://example.com/addons/test_install2_2.xpi";
   let aInstall_2 = await AddonManager.getInstallForURL(url_2, "application/x-xpinstall");
 
   await new Promise(resolve => {
     aInstall_2.addListener({
       onInstallStarted() {
         ok(!aInstall_2.addon.userDisabled);
-        aInstall_2.addon.userDisabled = true;
+        aInstall_2.addon.disable();
       },
 
       onInstallEnded() {
         resolve();
       }
     });
     aInstall_2.install();
   });
@@ -829,17 +829,17 @@ add_task(async function test_18() {
   await promiseRestartManager();
 
   let url = "http://example.com/addons/test_install2_1.xpi";
   let aInstall = await AddonManager.getInstallForURL(url, "application/x-xpinstall");
   await new Promise(resolve => {
     aInstall.addListener({
       onInstallStarted() {
         ok(!aInstall.addon.userDisabled);
-        aInstall.addon.userDisabled = true;
+        aInstall.addon.disable();
       },
 
       onInstallEnded() {
         resolve();
       }
     });
     aInstall.install();
   });
@@ -853,17 +853,17 @@ add_task(async function test_18() {
   });
 
   let url_2 = "http://example.com/addons/test_install2_2.xpi";
   let aInstall_2 = await AddonManager.getInstallForURL(url_2, "application/x-xpinstall");
   await new Promise(resolve => {
     aInstall_2.addListener({
       onInstallStarted() {
         ok(aInstall_2.addon.userDisabled);
-        aInstall_2.addon.userDisabled = false;
+        aInstall_2.addon.enable();
       },
 
       onInstallEnded() {
         resolve();
       }
     });
     aInstall_2.install();
   });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_locale.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locale.js
@@ -37,17 +37,17 @@ async function run_test_2() {
   await promiseRestartManager();
 
   let addon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(addon, null);
 
   Assert.equal(addon.name, "fr-FR Name");
   Assert.equal(addon.description, "fr-FR Description");
 
-  addon.userDisabled = true;
+  await addon.disable();
   executeSoon(run_test_3);
 }
 
 // Test that the localized properties are still there when disabled.
 async function run_test_3() {
   await promiseRestartManager();
 
   let addon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
@@ -96,17 +96,17 @@ async function run_test_6() {
   await promiseRestartManager();
 
   let addon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(addon, null);
 
   Assert.equal(addon.name, "Fallback Name");
   Assert.equal(addon.description, "Fallback Description");
 
-  addon.userDisabled = false;
+  await addon.enable();
   executeSoon(run_test_7);
 }
 
 // Test that the prefs will override the fallbacks
 async function run_test_7() {
   await promiseRestartManager();
 
   let addon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_manifest_locales.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_manifest_locales.js
@@ -74,18 +74,17 @@ add_task(async function setup() {
 });
 
 add_task(async function test_1() {
   let addon = await AddonManager.getAddonByID(ID);
   Assert.notEqual(addon, null);
   Assert.equal(addon.name, "fr Name");
   Assert.equal(addon.description, "fr Description");
 
-  // Disable item
-  addon.userDisabled = true;
+  await addon.disable();
   await promiseRestartManager();
 
   let newAddon = await AddonManager.getAddonByID(ID);
   Assert.notEqual(newAddon, null);
   Assert.equal(newAddon.name, "fr Name");
 });
 
 add_task(async function test_2() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_moved_extension_metadata.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_moved_extension_metadata.js
@@ -137,17 +137,17 @@ add_task(async function test_1() {
 add_task(async function test_2() {
   let a2 = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
  Assert.notEqual(a2, null);
  Assert.ok(!a2.appDisabled);
  Assert.ok(a2.isActive);
  Assert.ok(isExtensionInBootstrappedList(userDir, a2.id));
  Assert.equal(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
 
- a2.userDisabled = true;
+ await a2.disable();
  Assert.equal(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
 
  await promiseShutdownManager();
 
  userDir.parent.moveTo(gProfD, "extensions4");
  userDir = gProfD.clone();
  userDir.append("extensions4");
  userDir.append(gAppInfo.ID);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_nodisable_hidden.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_nodisable_hidden.js
@@ -38,17 +38,17 @@ add_task(async function() {
   Assert.equal(addon.name, "Test disabling hidden add-ons, non-hidden add-on case.");
   Assert.ok(addon.isCompatible);
   Assert.ok(!addon.appDisabled);
   Assert.ok(!addon.userDisabled);
   Assert.ok(addon.isActive);
   Assert.equal(addon.type, "extension");
 
   // normal add-ons can be disabled by the user.
-  addon.userDisabled = true;
+  await addon.disable();
 
   Assert.notEqual(addon, null);
   Assert.equal(addon.version, "1.0");
   Assert.equal(addon.name, "Test disabling hidden add-ons, non-hidden add-on case.");
   Assert.ok(addon.isCompatible);
   Assert.ok(!addon.appDisabled);
   Assert.ok(addon.userDisabled);
   Assert.ok(!addon.isActive);
@@ -85,17 +85,17 @@ add_task(async function() {
   Assert.ok(addon.isCompatible);
   Assert.ok(!addon.appDisabled);
   Assert.ok(!addon.userDisabled);
   Assert.ok(addon.isActive);
   Assert.equal(addon.type, "extension");
 
   // system add-ons cannot be disabled by the user.
   try {
-    addon.userDisabled = true;
+    await addon.disable();
     do_throw("Expected addon.userDisabled on a hidden add-on to throw!");
   } catch (e) {
     Assert.equal(e.message, `Cannot disable hidden add-on ${SYSTEM_ID}`);
   }
 
   Assert.notEqual(addon, null);
   Assert.equal(addon.version, "1.0");
   Assert.equal(addon.name, "Test disabling hidden add-ons, hidden system add-on case.");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
@@ -21,17 +21,17 @@ async function run_test() {
   }, profileDir);
 
   await promiseStartupManager();
 
   AddonManager.strictCompatibility = false;
 
   let aAddon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(aAddon, null);
-  aAddon.userDisabled = true;
+  await aAddon.disable();
   executeSoon(run_test_1);
 }
 
 async function run_test_1() {
   await promiseRestartManager();
   let aAddon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.notEqual(aAddon, null);
   Assert.ok(aAddon.userDisabled);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_plugins.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_plugins.js
@@ -141,17 +141,17 @@ async function run_test_2(p) {
   let test = {};
   test[gID] = [
     ["onDisabling", false],
     "onDisabled",
     ["onPropertyChanged", ["userDisabled"]]
   ];
   prepare_test(test);
 
-  p.userDisabled = true;
+  await p.disable();
 
   ensure_test_completed();
 
   Assert.ok(p.userDisabled);
   Assert.ok(!p.appDisabled);
   Assert.ok(!p.isActive);
 
   let p2 = await AddonManager.getAddonByID(gID);
@@ -168,17 +168,17 @@ async function run_test_2(p) {
 async function run_test_3(p) {
   let test = {};
   test[gID] = [
     ["onEnabling", false],
     "onEnabled"
   ];
   prepare_test(test);
 
-  p.userDisabled = false;
+  await p.enable();
 
   ensure_test_completed();
 
   Assert.ok(!p.userDisabled);
   Assert.ok(!p.appDisabled);
   Assert.ok(p.isActive);
 
   let p2 = await AddonManager.getAddonByID(gID);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_safemode.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_safemode.js
@@ -66,17 +66,17 @@ async function run_test_1() {
       ["onDisabling", false],
       "onDisabled"
     ]
   });
 
   let a1 = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
   Assert.ok(!hasFlag(a1.operationsRequiringRestart,
                      AddonManager.OP_NEEDS_RESTART_DISABLE));
-  a1.userDisabled = true;
+  await a1.disable();
   Assert.ok(!a1.isActive);
   Assert.equal(a1.aboutURL, null);
   Assert.equal(a1.optionsURL, null);
   Assert.equal(a1.iconURL, gIconURL);
   Assert.ok(!hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
   Assert.ok(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
   Assert.equal(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
   do_check_not_in_crash_annotation(addon1.id, addon1.version);
@@ -91,17 +91,17 @@ async function run_test_2() {
   prepare_test({
     "addon1@tests.mozilla.org": [
       ["onEnabling", false],
       "onEnabled"
     ]
   });
 
   let a1 = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
-  a1.userDisabled = false;
+  await a1.enable();
   Assert.ok(!a1.isActive);
   Assert.equal(a1.aboutURL, null);
   Assert.equal(a1.optionsURL, null);
   Assert.equal(a1.iconURL, gIconURL);
   Assert.ok(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
   Assert.ok(!hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
   Assert.equal(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
   do_check_not_in_crash_annotation(addon1.id, addon1.version);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_softblocked.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_softblocked.js
@@ -75,17 +75,17 @@ add_task(async function() {
     }]
   }, profileDir);
 
   await promiseStartupManager();
 
   let s1 = await promiseAddonByID("softblock1@tests.mozilla.org");
 
   // Make sure to mark it as previously enabled.
-  s1.userDisabled = false;
+  await s1.enable();
 
   Assert.ok(!s1.softDisabled);
   Assert.ok(s1.appDisabled);
   Assert.ok(!s1.isActive);
 
   await load_blocklist("test_softblocked1.xml");
 
   Assert.ok(s1.softDisabled);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
@@ -623,17 +623,17 @@ add_task(async function() {
 add_task(async function() {
   await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
 
   let addon = await promiseAddonByID(ID);
 
-  addon.userDisabled = true;
+  await addon.disable();
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonNotStarted(ID);
 
   let tempdir = gTmpD.clone();
   await promiseWriteInstallRDFToDir({
     id: ID,
     version: "2.0",
@@ -678,17 +678,17 @@ add_task(async function() {
   Assert.ok(!tempAddon.appDisabled);
   Assert.ok(tempAddon.isActive);
   Assert.equal(tempAddon.type, "extension");
   Assert.equal(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   tempAddon.uninstall();
   unpacked_addon.remove(true);
 
-  addon.userDisabled = false;
+  await addon.enable();
   await new Promise(executeSoon);
   addon = await promiseAddonByID(ID);
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID);
 
   // existing add-on is back
   Assert.notEqual(addon, null);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_undouninstall.js
@@ -280,17 +280,17 @@ add_task(async function uninstallDisable
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
   Assert.equal(getStartupReason(ID), ADDON_INSTALL);
   Assert.equal(a1.pendingOperations, AddonManager.PENDING_NONE);
   Assert.ok(a1.isActive);
   Assert.ok(!a1.userDisabled);
 
-  a1.userDisabled = true;
+  await a1.disable();
   BootstrapMonitor.checkAddonNotStarted(ID);
   Assert.equal(getShutdownReason(ID), ADDON_DISABLE);
   Assert.equal(a1.pendingOperations, AddonManager.PENDING_NONE);
   Assert.ok(!a1.isActive);
   Assert.ok(a1.userDisabled);
 
   prepare_test({
     "undouninstall1@tests.mozilla.org": [
@@ -352,17 +352,17 @@ add_task(async function cancelUninstallD
   Assert.ok(!a1.userDisabled);
 
   prepare_test({
     "undouninstall1@tests.mozilla.org": [
       ["onDisabling", false],
       "onDisabled"
     ]
   });
-  a1.userDisabled = true;
+  await a1.disable();
   ensure_test_completed();
 
   BootstrapMonitor.checkAddonNotStarted(ID);
   Assert.equal(getShutdownReason(ID), ADDON_DISABLE);
   Assert.equal(a1.pendingOperations, AddonManager.PENDING_NONE);
   Assert.ok(!a1.isActive);
   Assert.ok(a1.userDisabled);
 
@@ -422,17 +422,17 @@ add_task(async function reinstallDisable
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
   Assert.equal(getInstallReason(ID), ADDON_INSTALL);
   Assert.equal(getStartupReason(ID), ADDON_INSTALL);
   Assert.equal(a1.pendingOperations, AddonManager.PENDING_NONE);
   Assert.ok(a1.isActive);
   Assert.ok(!a1.userDisabled);
 
-  a1.userDisabled = true;
+  await a1.disable();
   BootstrapMonitor.checkAddonNotStarted(ID);
   Assert.equal(getShutdownReason(ID), ADDON_DISABLE);
   Assert.equal(a1.pendingOperations, AddonManager.PENDING_NONE);
   Assert.ok(!a1.isActive);
   Assert.ok(a1.userDisabled);
 
   prepare_test({
     "undouninstall1@tests.mozilla.org": [
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -619,17 +619,17 @@ add_task(async function test_8() {
   }
   let mockBlocklist = await AddonTestUtils.overrideBlocklist(blocklistAddons);
 
   for (let [id, options] of Object.entries(PARAM_ADDONS)) {
     await promiseInstallXPI(options["install.rdf"], profileDir);
 
     if (options.initialState) {
       let addon = await AddonManager.getAddonByID(id);
-      Object.assign(addon, options.initialState);
+      await setInitialState(addon, options.initialState);
     }
   }
 
   let resultsPromise = new Promise(resolve => {
     let results = new Map();
 
     testserver.registerPathHandler("/data/param_test.json", function(request, response) {
       let params = new URLSearchParams(request.queryString);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
@@ -77,21 +77,21 @@ add_task(async function() {
   let file = getFileForAddon(profileDir, ID);
   Assert.ok(file.exists());
 
   uri = do_get_addon_root_uri(profileDir, ID);
 
   Assert.equal(addon.iconURL, uri + "icon48.png");
   Assert.equal(addon.icon64URL, uri + "icon64.png");
 
-  addon.userDisabled = true;
+  await addon.disable();
 
   equal(GlobalManager.extensionMap.size, 0);
 
-  addon.userDisabled = false;
+  await addon.enable();
   await promiseWebExtensionStartup();
 
   equal(GlobalManager.extensionMap.size, 1);
   ok(GlobalManager.extensionMap.has(ID));
 
   addon.uninstall();
 
   equal(GlobalManager.extensionMap.size, 0);
@@ -137,17 +137,17 @@ add_task(async function() {
 
 add_task(async function test_manifest_localization() {
   const extensionId = "webextension3@tests.mozilla.org";
 
   await promiseInstallAllFiles([do_get_addon("webextension_3")], true);
   await promiseWebExtensionStartup();
 
   let addon = await promiseAddonByID(extensionId);
-  addon.userDisabled = true;
+  await addon.disable();
 
   equal(addon.name, "Web Extensiøn foo ☹");
   equal(addon.description, "Descriptïon bar ☹ of add-on");
 
   Services.locale.setRequestedLocales(["fr-FR"]);
   await promiseRestartManager();
 
   addon = await promiseAddonByID(extensionId);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_embedded.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_embedded.js
@@ -247,25 +247,25 @@ add_task(async function reload_embedded_
         "Got the expected number of tracked extension instances");
 
   const embeddedWebExtension = EmbeddedExtensionManager.embeddedExtensionsByAddonId.get(ID);
 
   let startupInfo = BootstrapMonitor.started.get(ID);
   await startupInfo.data.webExtension.startup();
 
   const waitForAddonDisabled = promiseAddonEvent("onDisabled");
-  addon.userDisabled = true;
+  await addon.disable();
   await waitForAddonDisabled;
 
   // No embedded webextension should be currently around.
   equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 0,
         "No embedded extension instance should be tracked here");
 
   const waitForAddonEnabled = promiseAddonEvent("onEnabled");
-  addon.userDisabled = false;
+  await addon.enable();
   await waitForAddonEnabled;
 
   // Only one embedded extension.
   equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 1,
         "Got the expected number of tracked extension instances");
 
   const embeddedWebExtensionAfterEnabled = EmbeddedExtensionManager.embeddedExtensionsByAddonId.get(ID);
   notEqual(embeddedWebExtensionAfterEnabled, embeddedWebExtension,
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_langpack.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_langpack.js
@@ -40,27 +40,27 @@ add_task(async function() {
     promiseLangpackStartup(),
     promiseInstallFile(do_get_addon("langpack_1"), true),
   ]);
 
   // Now make sure that `und` locale is available.
   equal(L10nRegistry.getAvailableLocales().includes("und"), true);
   equal(Services.locale.getAvailableLocales().includes("und"), true);
 
-  addon.userDisabled = true;
+  await addon.disable();
 
   // It is not available after the langpack has been disabled.
   equal(L10nRegistry.getAvailableLocales().includes("und"), false);
   equal(Services.locale.getAvailableLocales().includes("und"), false);
 
   // This quirky code here allows us to handle a scenario where enabling the
   // addon is synchronous or asynchronous.
   await Promise.all([
     promiseLangpackStartup(),
-    (() => { addon.userDisabled = false; })()
+    addon.enable(),
   ]);
 
   // After re-enabling it, the `und` locale is available again.
   equal(L10nRegistry.getAvailableLocales().includes("und"), true);
   equal(Services.locale.getAvailableLocales().includes("und"), true);
 
   addon.uninstall();
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
@@ -56,17 +56,18 @@ add_task(async function setup_to_default
     accentcolor: Math.random().toString()
   };
 
   let [ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
   Assert.ok(t1, "Theme addon should exist");
   Assert.ok(t2, "Theme addon should exist");
   Assert.ok(d, "Theme addon should exist");
 
-  t1.userDisabled = t2.userDisabled = true;
+  await t1.disable();
+  await t2.disable();
   Assert.ok(!t1.isActive, "Theme should be disabled");
   Assert.ok(!t2.isActive, "Theme should be disabled");
   Assert.ok(d.isActive, "Default theme should be active");
 
   await promiseRestartManager();
 
   [ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
   Assert.ok(!t1.isActive, "Theme should still be disabled");
@@ -103,17 +104,21 @@ async function setDisabledStateAndCheck(
       [ "onEnabling", false ],
       [ "onEnabled", false ],
     ]
   };
 
   // Set the state of the theme to change.
   let theme = await promiseAddonByID(which);
   prepare_test(expectedEvents);
-  theme.userDisabled = disabled;
+  if (disabled) {
+    await theme.disable();
+  } else {
+    await theme.enable();
+  }
 
   let isDisabled;
   for (theme of await promiseAddonsByIDs(THEME_IDS)) {
     isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
     Assert.equal(theme.userDisabled, isDisabled,
       `Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
     Assert.equal(theme.pendingOperations, AddonManager.PENDING_NONE,
       "There should be no pending operations when no restart is expected");