Bug 1378882 - Support is_default for built-in engines only. r=mixedpuppy
☠☠ backed out by 75b01708e389 ☠ ☠
authorMichael Kaply <mozilla@kaply.com>
Tue, 18 Jul 2017 11:28:33 -0500
changeset 376690 5d5c1a6c3f3a0e951a9f479ab71aef22e3ab87e4
parent 376689 bbddacd6f1322a091c77de202344a8590afe1641
child 376691 55335088ab5346ca218828fbcb4f0d3da553da65
push id94144
push userryanvm@gmail.com
push dateFri, 25 Aug 2017 00:21:50 +0000
treeherdermozilla-inbound@942d868646cf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1378882
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1378882 - Support is_default for built-in engines only. r=mixedpuppy MozReview-Commit-ID: C4iM2boQhK3
browser/components/extensions/ext-chrome-settings-overrides.js
browser/components/extensions/schemas/chrome_settings_overrides.json
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
--- a/browser/components/extensions/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/ext-chrome-settings-overrides.js
@@ -1,16 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPreferencesManager",
                                   "resource://gre/modules/ExtensionPreferencesManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
+                                  "resource://gre/modules/ExtensionSettingsStore.jsm");
+
+const DEFAULT_SEARCH_STORE_TYPE = "default_search";
+const DEFAULT_SEARCH_SETTING_NAME = "defaultSearch";
 
 const searchInitialized = () => {
   return new Promise(resolve => {
     if (Services.search.isInitialized) {
       resolve();
     }
     const SEARCH_SERVICE_TOPIC = "browser-search-service";
     Services.obs.addObserver(function observer(subject, topic, data) {
@@ -20,27 +27,90 @@ const searchInitialized = () => {
 
       Services.obs.removeObserver(observer, SEARCH_SERVICE_TOPIC);
       resolve();
     }, SEARCH_SERVICE_TOPIC);
   });
 };
 
 this.chrome_settings_overrides = class extends ExtensionAPI {
+  processDefaultSearchSetting(action) {
+    let {extension} = this;
+    let {manifest} = extension;
+    let item = ExtensionSettingsStore.getSetting(DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
+    if (!item) {
+      return;
+    }
+    if (Services.search.currentEngine.name != item.value &&
+        Services.search.currentEngine.name != item.initialValue) {
+      // The current engine is not the same as the value that the ExtensionSettingsStore has.
+      // This means that the user changed the engine, so we shouldn't control it anymore.
+      // Do nothing and remove our entry from the ExtensionSettingsStore.
+      ExtensionSettingsStore.removeSetting(extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
+      return;
+    }
+    item = ExtensionSettingsStore[action](extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
+    if (item) {
+      try {
+        let engine = Services.search.getEngineByName(item.value || item.initialValue);
+        if (engine) {
+          Services.search.currentEngine = engine;
+        }
+      } catch (e) {
+        Components.utils.reportError(e);
+      }
+    }
+  }
+
   async onManifestEntry(entryName) {
     let {extension} = this;
     let {manifest} = extension;
 
+    await ExtensionSettingsStore.initialize();
     if (manifest.chrome_settings_overrides.homepage) {
       ExtensionPreferencesManager.setSetting(extension, "homepage_override",
                                              manifest.chrome_settings_overrides.homepage);
     }
     if (manifest.chrome_settings_overrides.search_provider) {
       await searchInitialized();
       let searchProvider = manifest.chrome_settings_overrides.search_provider;
+      if (searchProvider.is_default) {
+        let engineName = searchProvider.name.trim();
+        let engine = Services.search.getEngineByName(engineName);
+        if (engine && Services.search.getDefaultEngines().includes(engine)) {
+          // Only add onclose handlers if we would definitely
+          // be setting the default engine.
+          extension.callOnClose({
+            close: () => {
+              switch (extension.shutdownReason) {
+                case "ADDON_DISABLE":
+                  this.processDefaultSearchSetting("disable");
+                  break;
+
+                case "ADDON_UNINSTALL":
+                  this.processDefaultSearchSetting("removeSetting");
+                  break;
+              }
+            },
+          });
+          if (extension.startupReason === "ADDON_INSTALL") {
+            let item = await ExtensionSettingsStore.addSetting(
+              extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME, engineName, () => {
+                return Services.search.currentEngine.name;
+              });
+            Services.search.currentEngine = Services.search.getEngineByName(item.value);
+          } else if (extension.startupReason === "ADDON_ENABLE") {
+            this.processDefaultSearchSetting("enable");
+          }
+          // If we would have set the default engine,
+          // we don't allow a search provider to be added.
+          return;
+        }
+        Components.utils.reportError("is_default can only be used for built-in engines.");
+      }
       let isCurrent = false;
       let index = -1;
       if (extension.startupReason === "ADDON_UPGRADE") {
         let engines = Services.search.getEnginesByExtensionID(extension.id);
         if (engines.length > 0) {
           // There can be only one engine right now
           isCurrent = Services.search.currentEngine == engines[0];
           // Get position of engine and store it
@@ -65,16 +135,27 @@ this.chrome_settings_overrides = class e
           if (index != -1) {
             Services.search.moveEngine(engine, index);
           }
         }
       } catch (e) {
         Components.utils.reportError(e);
       }
     }
+    // If the setting exists for the extension, but is missing from the manifest,
+    // remove it. This can happen if the extension removes is_default.
+    // There's really no good place to put this, because the entire search section
+    // could be removed.
+    // We'll never get here in the normal case because we always return early
+    // if we have an is_default value that we use.
+    if (ExtensionSettingsStore.hasSetting(
+               extension, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME)) {
+      await searchInitialized();
+      this.processDefaultSearchSetting("removeSetting");
+    }
   }
   async onShutdown(reason) {
     let {extension} = this;
     if (reason == "ADDON_DISABLE" ||
         reason == "ADDON_UNINSTALL") {
       if (extension.manifest.chrome_settings_overrides.search_provider) {
         await searchInitialized();
         let engines = Services.search.getEnginesByExtensionID(extension.id);
--- a/browser/components/extensions/schemas/chrome_settings_overrides.json
+++ b/browser/components/extensions/schemas/chrome_settings_overrides.json
@@ -93,17 +93,17 @@
                   "prepopulated_id": {
                     "type": "integer",
                     "optional": true,
                     "deprecated": "Unsupported on Firefox."
                   },
                   "is_default": {
                     "type": "boolean",
                     "optional": true,
-                    "deprecated": "Unsupported on Firefox at this time."
+                    "description": "Sets the default engine to a built-in engine only."
                   }
                 }
               }
             }
           }
         }
       }
     ]
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -101,16 +101,17 @@ skip-if = debug || asan # Bug 1354681
 [browser_ext_runtime_openOptionsPage_uninstall.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_sessions_forgetClosedTab.js]
 [browser_ext_sessions_forgetClosedWindow.js]
 [browser_ext_sessions_getRecentlyClosed.js]
 [browser_ext_sessions_getRecentlyClosed_private.js]
 [browser_ext_sessions_getRecentlyClosed_tabs.js]
 [browser_ext_sessions_restore.js]
+[browser_ext_settings_overrides_default_search.js]
 [browser_ext_settings_overrides_search.js]
 [browser_ext_sidebarAction.js]
 [browser_ext_sidebarAction_browser_style.js]
 [browser_ext_sidebarAction_context.js]
 [browser_ext_sidebarAction_contextMenu.js]
 [browser_ext_sidebarAction_tabs.js]
 [browser_ext_sidebarAction_windows.js]
 [browser_ext_simple.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
@@ -0,0 +1,507 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+XPCOMUtils.defineLazyGetter(this, "Management", () => {
+  const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
+  return Management;
+});
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
+                                  "resource://gre/modules/AddonManager.jsm");
+
+const EXTENSION1_ID = "extension1@mozilla.com";
+const EXTENSION2_ID = "extension2@mozilla.com";
+
+function awaitEvent(eventName, id) {
+  return new Promise(resolve => {
+    let listener = (_eventName, ...args) => {
+      let extension = args[0];
+      if (_eventName === eventName &&
+          extension.id == id) {
+        Management.off(eventName, listener);
+        resolve(...args);
+      }
+    };
+
+    Management.on(eventName, listener);
+  });
+}
+
+/* This tests setting a default engine. */
+add_task(async function test_extension_setting_default_engine() {
+  let defaultEngineName = Services.search.currentEngine.name;
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
+
+  await ext1.unload();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests that using an invalid engine does nothing. */
+add_task(async function test_extension_setting_invalid_name_default_engine() {
+  let defaultEngineName = Services.search.currentEngine.name;
+
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "InvalidName",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+
+  await ext1.unload();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests that uninstalling add-ons maintains the proper
+ * search default. */
+add_task(async function test_extension_setting_multiple_default_engine() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "Twitter",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
+
+  await ext2.startup();
+
+  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();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests that uninstalling add-ons in reverse order maintains the proper
+ * search default. */
+add_task(async function test_extension_setting_multiple_default_engine_reversed() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "Twitter",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
+
+  await ext2.startup();
+
+  is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
+
+  await ext1.unload();
+
+  is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
+
+  await ext2.unload();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests adding an engine with one add-on and trying to make it the
+ *default with anoth. */
+add_task(async function test_extension_setting_invalid_default_engine() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "MozSearch",
+          "keyword": "MozSearch",
+          "search_url": "https://example.com/?q={searchTerms}",
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "MozSearch",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+
+  let engine = Services.search.getEngineByName("MozSearch");
+  ok(engine, "Engine should exist.");
+
+  await ext2.startup();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+
+  await ext2.unload();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+
+  await ext1.unload();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests that when the user changes the search engine and the add-on
+ * is unistalled, search stays with the user's choice. */
+add_task(async function test_user_changing_default_engine() {
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
+
+  let engine = Services.search.getEngineByName("Twitter");
+  Services.search.currentEngine = engine;
+
+  await ext1.unload();
+
+  is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
+});
+
+/* This tests that when the user changes the search engine while it is
+ * disabled, user choice is maintained when the add-on is reenabled. */
+add_task(async function test_user_change_with_disabling() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION1_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
+
+  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 disabledPromise;
+
+  is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
+
+  let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
+  addon.userDisabled = false;
+  await enabledPromise;
+
+  is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
+  await ext1.unload();
+});
+
+/* This tests that when two add-ons are installed that change default
+ * search and the first one is disabled, before the second one is installed,
+ * when the first one is reenabled, the second add-on keeps the search. */
+add_task(async function test_two_addons_with_first_disabled_before_second() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION1_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION2_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "Twitter",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  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 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 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();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests that when two add-ons are installed that change default
+ * search and the first one is disabled, the second one maintains
+ * the search. */
+add_task(async function test_two_addons_with_first_disabled() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION1_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION2_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "Twitter",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  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 disabledPromise;
+
+  is(Services.search.currentEngine.name, "Twitter", "Default engine is Twitter");
+
+  let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
+  addon1.userDisabled = false;
+  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();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});
+
+/* This tests that when two add-ons are installed that change default
+ * search and the second one is disabled, the first one properly
+ * gets the search. */
+add_task(async function test_two_addons_with_second_disabled() {
+  let defaultEngineName = Services.search.currentEngine.name;
+  let ext1 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION1_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "DuckDuckGo",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  let ext2 = ExtensionTestUtils.loadExtension({
+    manifest: {
+      applications: {
+        gecko: {
+          id: EXTENSION2_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "search_provider": {
+          "name": "Twitter",
+          "search_url": "https://example.com/?q={searchTerms}",
+          "is_default": true,
+        },
+      },
+    },
+    useAddonManager: "temporary",
+  });
+
+  await ext1.startup();
+
+  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 disabledPromise;
+
+  is(Services.search.currentEngine.name, "DuckDuckGo", "Default engine is DuckDuckGo");
+
+  let enabledPromise = awaitEvent("ready", EXTENSION2_ID);
+  addon2.userDisabled = false;
+  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();
+
+  is(Services.search.currentEngine.name, defaultEngineName, `Default engine is ${defaultEngineName}`);
+});