Bug 1525762: Part 2a - Migrate built-in LWTs to static WebExtension themes. r=aswan
authorKris Maglione <maglione.k@gmail.com>
Fri, 08 Feb 2019 14:18:52 -0800
changeset 466972 b62c3bde4bc3f7cba452ad57f8d728b175c15a62
parent 466971 f31ce378439a073fb7a54c44ffe5dc68e1c03b77
child 466973 83612982ab33e156ec51052fe86781d7e86e50ad
push id35789
push userbtara@mozilla.com
push dateSun, 31 Mar 2019 09:00:52 +0000
treeherdermozilla-central@c06dfc552c64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1525762
milestone68.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 1525762: Part 2a - Migrate built-in LWTs to static WebExtension themes. r=aswan Differential Revision: https://phabricator.services.mozilla.com/D24627
browser/base/content/defaultthemes/dark.icon.svg
browser/base/content/defaultthemes/light.icon.svg
browser/base/content/test/static/browser_all_files_referenced.js
browser/base/jar.mn
browser/components/BrowserGlue.jsm
browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
browser/components/customizableui/test/browser_970511_undo_restore_default.js
browser/locales/en-US/chrome/browser/app-extension-fields.properties
browser/locales/en-US/chrome/browser/browser.properties
browser/locales/jar.mn
browser/themes/addons/dark/icon.svg
browser/themes/addons/dark/manifest.json
browser/themes/addons/dark/moz.build
browser/themes/addons/light/icon.svg
browser/themes/addons/light/manifest.json
browser/themes/addons/light/moz.build
browser/themes/moz.build
browser/themes/shared/customizableui/customizeMode.inc.css
toolkit/components/extensions/test/browser/browser_ext_management_themes.js
toolkit/components/extensions/test/xpcshell/test_ext_management.js
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
toolkit/locales/en-US/chrome/global/extensions.properties
toolkit/locales/en-US/chrome/global/global-extension-fields.properties
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/content/default-theme-icon.svg
toolkit/mozapps/extensions/default-theme/icon.svg
toolkit/mozapps/extensions/default-theme/manifest.json
toolkit/mozapps/extensions/default-theme/moz.build
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/internal/XPIInstall.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/jar.mn
toolkit/mozapps/extensions/moz.build
toolkit/mozapps/extensions/test/browser/browser_html_list_view.js
toolkit/mozapps/extensions/test/xpcshell/test_db_path.js
toolkit/mozapps/extensions/test/xpcshell/test_startup.js
toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -304,29 +304,37 @@ function parseManifest(manifestUri) {
     }
   });
 }
 
 // If the given URI is a webextension manifest, extract the scripts
 // for any embedded APIs.  Returns the passed in URI if the manifest
 // is not a webextension manifest, null otherwise.
 async function parseJsonManifest(uri) {
+  uri = Services.io.newURI(convertToCodeURI(uri.spec));
+
   let raw = await fetchFile(uri.spec);
   let data;
   try {
     data = JSON.parse(raw);
   } catch (ex) {
     return uri;
   }
 
   // Simplistic test for whether this is a webextension manifest:
   if (data.manifest_version !== 2) {
     return uri;
   }
 
+  if (data.icons) {
+    for (let icon of Object.values(data.icons)) {
+      gReferencesFromCode.set(uri.resolve(icon), null);
+    }
+  }
+
   if (data.experiment_apis) {
     for (let api of Object.values(data.experiment_apis)) {
       if (api.parent && api.parent.script) {
         let script = uri.resolve(api.parent.script);
         gReferencesFromCode.set(script, null);
       }
     }
   }
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -70,18 +70,16 @@ browser.jar:
         content/browser/defaultthemes/3.icon.png      (content/defaultthemes/3.icon.png)
         content/browser/defaultthemes/3.preview.png   (content/defaultthemes/3.preview.png)
         content/browser/defaultthemes/4.header.png    (content/defaultthemes/4.header.png)
         content/browser/defaultthemes/4.icon.png      (content/defaultthemes/4.icon.png)
         content/browser/defaultthemes/4.preview.png   (content/defaultthemes/4.preview.png)
         content/browser/defaultthemes/5.header.png    (content/defaultthemes/5.header.png)
         content/browser/defaultthemes/5.icon.jpg      (content/defaultthemes/5.icon.jpg)
         content/browser/defaultthemes/5.preview.jpg   (content/defaultthemes/5.preview.jpg)
-        content/browser/defaultthemes/dark.icon.svg   (content/defaultthemes/dark.icon.svg)
-        content/browser/defaultthemes/light.icon.svg  (content/defaultthemes/light.icon.svg)
         content/browser/history-swipe-arrow.svg       (content/history-swipe-arrow.svg)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
         content/browser/pageinfo/security.js          (content/pageinfo/security.js)
         content/browser/content-refreshblocker.js     (content/content-refreshblocker.js)
         content/browser/robot.ico                     (content/robot.ico)
@@ -123,8 +121,9 @@ browser.jar:
         content/browser/newInstallPage.js             (content/newInstallPage.js)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/aboutNetError.xhtml
 
 # L10n resources and overrides.
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
+% override chrome://global/locale/app-extension-fields.properties chrome://browser/locale/app-extension-fields.properties
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -394,17 +394,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   Discovery: "resource:///modules/Discovery.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
   FileSource: "resource://gre/modules/L10nRegistry.jsm",
   FxAccounts: "resource://gre/modules/FxAccounts.jsm",
   HomePage: "resource:///modules/HomePage.jsm",
   HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm",
   Integration: "resource://gre/modules/Integration.jsm",
   L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
-  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   LiveBookmarkMigrator: "resource:///modules/LiveBookmarkMigrator.jsm",
   NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
   Normandy: "resource://normandy/Normandy.jsm",
   ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PageActions: "resource:///modules/PageActions.jsm",
   PageThumbs: "resource://gre/modules/PageThumbs.jsm",
   PdfJs: "resource://pdf.js/PdfJs.jsm",
@@ -1032,63 +1031,22 @@ BrowserGlue.prototype = {
 
     // handle any UI migration
     this._migrateUI();
 
     listeners.init();
 
     SessionStore.init();
 
-    let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");
-
-    LightweightThemeManager.addBuiltInTheme({
-      id: "firefox-compact-light@mozilla.org",
-      name: gBrowserBundle.GetStringFromName("lightTheme.name"),
-      description: gBrowserBundle.GetStringFromName("lightTheme.description"),
-      iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
-      textcolor: "rgb(24, 25, 26)",
-      icon_color: "rgb(24, 25, 26, 0.7)",
-      accentcolor: "#E3E4E6",
-      popup: "#fff",
-      popup_text: "#0c0c0d",
-      popup_border: "#ccc",
-      tab_line: "#0a84ff",
-      toolbarColor: "#f5f6f7",
-      toolbar_bottom_separator: "#ccc",
-      toolbar_field: "#fff",
-      toolbar_field_border: "#ccc",
-      author: vendorShortName,
-    });
-    LightweightThemeManager.addBuiltInTheme({
-      id: "firefox-compact-dark@mozilla.org",
-      name: gBrowserBundle.GetStringFromName("darkTheme.name"),
-      description: gBrowserBundle.GetStringFromName("darkTheme.description"),
-      iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
-      textcolor: "rgb(249, 249, 250)",
-      icon_color: "rgb(249, 249, 250, 0.7)",
-      accentcolor: "hsl(240, 5%, 5%)",
-      popup: "#4a4a4f",
-      popup_text: "rgb(249, 249, 250)",
-      popup_border: "#27272b",
-      tab_line: "#0a84ff",
-      toolbarColor: "hsl(240, 1%, 20%)",
-      toolbar_bottom_separator: "hsl(240, 5%, 5%)",
-      toolbar_field: "rgb(71, 71, 73)",
-      toolbar_field_border: "rgba(249, 249, 250, 0.2)",
-      toolbar_field_separator: "#5F6670",
-      toolbar_field_text: "rgb(249, 249, 250)",
-      ntp_background: "#2A2A2E",
-      ntp_text: "rgb(249, 249, 250)",
-      sidebar: "#38383D",
-      sidebar_text: "rgb(249, 249, 250)",
-      sidebar_border: "rgba(255, 255, 255, 0.1)",
-      author: vendorShortName,
-    }, {
-      useInDarkMode: true,
-    });
+    AddonManager.maybeInstallBuiltinAddon(
+        "firefox-compact-light@mozilla.org", "1.0",
+        "resource:///modules/themes/light/");
+    AddonManager.maybeInstallBuiltinAddon(
+        "firefox-compact-dark@mozilla.org", "1.0",
+        "resource:///modules/themes/dark/");
 
     Normandy.init();
 
     SaveToPocket.init();
     Services.obs.notifyObservers(null, "browser-ui-startup-complete");
   },
 
   _checkForOldBuildUpdates() {
--- a/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
+++ b/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js
@@ -55,17 +55,17 @@ add_task(async function() {
      "There should only be three themes (default, light, dark) in the 'My Themes' section by default");
   is(header.nextElementSibling.theme.id, DEFAULT_THEME_ID,
      "The first theme should be the default theme");
   is(header.nextElementSibling.nextElementSibling.theme.id, LIGHT_THEME_ID,
      "The second theme should be the light theme");
   is(header.nextElementSibling.nextElementSibling.nextElementSibling.theme.id, DARK_THEME_ID,
      "The third theme should be the dark theme");
 
-  let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
+  let themeChangedPromise = promiseObserverNotified("lightweight-theme-styling-update");
   header.nextElementSibling.nextElementSibling.doCommand(); // Select light theme
   info("Clicked on light theme");
   await themeChangedPromise;
 
   let button = document.getElementById("customization-reset-button");
   await TestUtils.waitForCondition(() => !button.disabled);
 
   // Check restore defaults button is enabled.
@@ -82,17 +82,17 @@ add_task(async function() {
   let activeThemes = popup.querySelectorAll("toolbarbutton.customization-lwtheme-menu-theme[active]");
   is(activeThemes.length, 1, "Exactly 1 theme should be selected");
   if (activeThemes.length > 0) {
     is(activeThemes[0].theme.id, LIGHT_THEME_ID, "Light theme should be selected");
   }
 
   let firstLWTheme = footer.previousElementSibling;
   let firstLWThemeId = firstLWTheme.theme.id;
-  themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
+  themeChangedPromise = promiseObserverNotified("lightweight-theme-styling-update");
   firstLWTheme.doCommand();
   info("Clicked on first theme");
   await themeChangedPromise;
 
   await new Promise(executeSoon);
 
   popupShownPromise = popupShown(popup);
   EventUtils.synthesizeMouseAtCenter(themesButton, {});
@@ -113,18 +113,16 @@ add_task(async function() {
     iterNode = iterNode.nextElementSibling;
   }
   is(themeCount, 3,
      "There should be four themes in the 'My Themes' section");
 
   let defaultTheme = header.nextElementSibling;
   defaultTheme.doCommand();
   await new Promise(SimpleTest.executeSoon);
-  is(Services.prefs.getCharPref("lightweightThemes.selectedThemeID"),
-     DEFAULT_THEME_ID, "Default theme should be selected");
 
   // ensure current theme isn't set to "Default"
   popupShownPromise = popupShown(popup);
   EventUtils.synthesizeMouseAtCenter(themesButton, {});
   info("Clicked on themes button a fourth time");
   await popupShownPromise;
 
   // check that "Restore Defaults" button resets theme
--- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js
+++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js
@@ -21,17 +21,17 @@ add_task(async function() {
   let popupShownPromise = popupShown(popup);
   EventUtils.synthesizeMouseAtCenter(themesButton, {});
   info("Clicked on themes button");
   await popupShownPromise;
 
   let header = document.getElementById("customization-lwtheme-menu-header");
   let firstLWTheme = header.nextElementSibling.nextElementSibling;
   let firstLWThemeId = firstLWTheme.theme.id;
-  let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
+  let themeChangedPromise = promiseObserverNotified("lightweight-theme-styling-update");
   firstLWTheme.doCommand();
   info("Clicked on first theme");
   await themeChangedPromise;
 
   let theme = await AddonManager.getAddonByID(firstLWThemeId);
   is(theme.isActive, true, "Theme changed to first option");
 
   await gCustomizeMode.reset();
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/app-extension-fields.properties
@@ -0,0 +1,12 @@
+# 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/.
+
+# LOCALIZATION NOTE (extension.firefox-compact-light@mozilla.org.name): This is displayed in about:addons -> Appearance
+extension.firefox-compact-light@mozilla.org.name=Light
+extension.firefox-compact-light@mozilla.org.description=A theme with a light color scheme.
+
+# LOCALIZATION NOTE (extension.firefox-compact-dark@mozilla.org.name): This is displayed in about:addons -> Appearance
+extension.firefox-compact-dark@mozilla.org.name=Dark
+extension.firefox-compact-dark@mozilla.org.description=A theme with a dark color scheme.
+
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -230,24 +230,16 @@ addonInstallErrorIncompatible=%3$S could
 
 # LOCALIZATION NOTE (addonInstallErrorBlocklisted): %S is add-on name
 addonInstallErrorBlocklisted=%S could not be installed because it has a high risk of causing stability or security problems.
 
 unsignedAddonsDisabled.message=One or more installed add-ons cannot be verified and have been disabled.
 unsignedAddonsDisabled.learnMore.label=Learn More
 unsignedAddonsDisabled.learnMore.accesskey=L
 
-# LOCALIZATION NOTE (lightTheme.name): This is displayed in about:addons -> Appearance
-lightTheme.name=Light
-lightTheme.description=A theme with a light color scheme.
-
-# LOCALIZATION NOTE (darkTheme.name): This is displayed in about:addons -> Appearance
-darkTheme.name=Dark
-darkTheme.description=A theme with a dark color scheme.
-
 # LOCALIZATION NOTE (lwthemeInstallRequest.message2): %S will be replaced with
 # the host name of the site.
 lwthemeInstallRequest.message2=This site (%S) attempted to install a theme.
 lwthemeInstallRequest.allowButton2=Allow
 lwthemeInstallRequest.allowButton.accesskey2=a
 
 # LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -11,16 +11,17 @@
   browser                                          (%browser/**/*.ftl)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
 # bookmarks.html is produced by LOCALIZED_GENERATED_FILES.
     locale/browser/bookmarks.html                  (bookmarks.html)
 
     locale/browser/accounts.properties             (%chrome/browser/accounts.properties)
+    locale/browser/app-extension-fields.properties (%chrome/browser/app-extension-fields.properties)
     locale/browser/browser.dtd                     (%chrome/browser/browser.dtd)
     locale/browser/baseMenuOverlay.dtd             (%chrome/browser/baseMenuOverlay.dtd)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
     locale/browser/customizableui/customizableWidgets.properties (%chrome/browser/customizableui/customizableWidgets.properties)
     locale/browser/uiDensity.properties            (%chrome/browser/uiDensity.properties)
     locale/browser/pocket.properties               (%chrome/browser/pocket.properties)
     locale/browser/search.properties               (%chrome/browser/search.properties)
     locale/browser/siteData.properties             (%chrome/browser/siteData.properties)
rename from browser/base/content/defaultthemes/dark.icon.svg
rename to browser/themes/addons/dark/icon.svg
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/dark/manifest.json
@@ -0,0 +1,39 @@
+{
+  "manifest_version": 2,
+
+  "applications": {
+    "gecko": {
+      "id": "firefox-compact-dark@mozilla.org"
+    }
+  },
+
+  "name": "Dark",
+  "description": "A theme with a dark color scheme.",
+  "author": "Mozilla",
+  "version": "1.0",
+
+  "icons": {"32": "icon.svg"},
+
+  "theme": {
+    "colors": {
+      "tab_background_text": "rgb(249, 249, 250)",
+      "icons": "rgb(249, 249, 250, 0.7)",
+      "frame": "hsl(240, 5%, 5%)",
+      "popup": "#4a4a4f",
+      "popup_text": "rgb(249, 249, 250)",
+      "popup_border": "#27272b",
+      "tab_line": "#0a84ff",
+      "toolbar": "hsl(240, 1%, 20%)",
+      "toolbar_bottom_separator": "hsl(240, 5%, 5%)",
+      "toolbar_field": "rgb(71, 71, 73)",
+      "toolbar_field_border": "rgba(249, 249, 250, 0.2)",
+      "toolbar_field_separator": "#5F6670",
+      "toolbar_field_text": "rgb(249, 249, 250)",
+      "ntp_background": "#2A2A2E",
+      "ntp_text": "rgb(249, 249, 250)",
+      "sidebar": "#38383D",
+      "sidebar_text": "rgb(249, 249, 250)",
+      "sidebar_border": "rgba(255, 255, 255, 0.1)"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/dark/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES.themes['dark'] += [
+    'icon.svg',
+    'manifest.json',
+]
rename from browser/base/content/defaultthemes/light.icon.svg
rename to browser/themes/addons/light/icon.svg
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/light/manifest.json
@@ -0,0 +1,32 @@
+{
+  "manifest_version": 2,
+
+  "applications": {
+    "gecko": {
+      "id": "firefox-compact-light@mozilla.org"
+    }
+  },
+
+  "name": "Light",
+  "description": "A theme with a light color scheme.",
+  "author": "Mozilla",
+  "version": "1.0",
+
+  "icons": {"32": "icon.svg"},
+
+  "theme": {
+    "colors": {
+      "tab_background_text": "rgb(24, 25, 26)",
+      "icons": "rgb(24, 25, 26, 0.7)",
+      "frame": "#E3E4E6",
+      "popup": "#fff",
+      "popup_text": "#0c0c0d",
+      "popup_border": "#ccc",
+      "tab_line": "#0a84ff",
+      "toolbar": "#f5f6f7",
+      "toolbar_bottom_separator": "#ccc",
+      "toolbar_field": "#fff",
+      "toolbar_field_border": "#ccc"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/light/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES.themes['light'] += [
+    'icon.svg',
+    'manifest.json',
+]
--- a/browser/themes/moz.build
+++ b/browser/themes/moz.build
@@ -11,16 +11,21 @@ toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
 
 if toolkit == 'cocoa':
     DIRS += ['osx']
 elif toolkit == 'gtk3':
     DIRS += ['linux']
 else:
     DIRS += ['windows']
 
+DIRS += [
+    'addons/dark',
+    'addons/light',
+]
+
 with Files('osx/**'):
     SCHEDULES.exclusive = ['macosx']
 
 with Files('linux/**'):
     SCHEDULES.exclusive = ['linux']
 
 with Files('windows/**'):
     SCHEDULES.exclusive = ['windows']
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -153,17 +153,17 @@
 #customization-lwtheme-button > .box-inherit > .box-inherit > .button-icon {
   width: 16px;
   height: 16px;
   border-radius: 2px;
   background-size: contain;
 }
 
 #customization-lwtheme-button > .box-inherit > .box-inherit > .button-icon {
-  background-image: url("chrome://mozapps/content/extensions/default-theme-icon.svg");
+  background-image: url("resource://gre/modules/themes/default/icon.svg");
 }
 
 #customization-uidensity-button > .box-inherit > .box-inherit > .button-icon {
   background-image: url("chrome://browser/skin/customizableui/density-normal.svg");
 }
 
 #widget-overflow-fixed-list > toolbarpaletteitem[place="menu-panel"],
 toolbarpaletteitem[place="toolbar"] {
--- a/toolkit/components/extensions/test/browser/browser_ext_management_themes.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_management_themes.js
@@ -104,16 +104,17 @@ add_task(async function test_management_
     },
     background: `(${background})("${TEST_ID}")`,
     useAddonManager: "temporary",
   });
   await extension.startup();
 
   await theme.startup();
   is(await extension.awaitMessage("onInstalled"), "Simple theme test", "webextension theme installed");
+  is(await extension.awaitMessage("onDisabled"), "Default", "default disabled");
 
   extension.sendMessage("test");
   is(await extension.awaitMessage("onEnabled"), "Default", "default enabled");
   is(await extension.awaitMessage("onDisabled"), "Simple theme test", "addon disabled");
   is(await extension.awaitMessage("onEnabled"), "Simple theme test", "addon enabled");
   is(await extension.awaitMessage("onDisabled"), "Default", "default disabled");
   await extension.awaitMessage("done");
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_management.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_management.js
@@ -28,17 +28,17 @@ add_task(async function test_management_
 
   async function background() {
     browser.test.onMessage.addListener(async (msg, id) => {
       let addon = await browser.management.get(id);
       browser.test.sendMessage("addon", addon);
     });
 
     let addons = await browser.management.getAll();
-    browser.test.assertEq(addons.length, 3, "management.getAll returned three add-ons.");
+    browser.test.assertEq(2, addons.length, "management.getAll returned correct number of add-ons.");
     browser.test.sendMessage("addons", addons);
   }
 
   let extension1 = ExtensionTestUtils.loadExtension({
     manifest: getManifest(id1),
     useAddonManager: "temporary",
   });
 
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -248,17 +248,17 @@ function createMockAddonProvider(aName) 
     },
 
     addAddon(aAddon) {
       this._addons.push(aAddon);
       AddonManagerPrivate.callAddonListeners("onInstalled", new MockAddonWrapper(aAddon));
     },
 
     async getAddonsByTypes(aTypes) {
-      return this._addons.map(a => new MockAddonWrapper(a));
+      return this._addons.filter(a => !aTypes || aTypes.includes(a.type)).map(a => new MockAddonWrapper(a));
     },
 
     shutdown() {
       return Promise.resolve();
     },
   };
 
   return mockProvider;
@@ -1442,16 +1442,17 @@ add_task(async function test_addonsField
 add_task(async function test_collectionWithbrokenAddonData() {
   const BROKEN_ADDON_ID = "telemetry-test2.example.com@services.mozilla.org";
   const BROKEN_MANIFEST = {
     id: "telemetry-test2.example.com@services.mozilla.org",
     name: "telemetry broken addon",
     origin: "https://telemetry-test2.example.com",
     version: 1, // This is intentionally not a string.
     signedState: AddonManager.SIGNEDSTATE_SIGNED,
+    type: "extension",
   };
 
   const ADDON_INSTALL_URL = gDataRoot + "restartless.xpi";
   const ADDON_ID = "tel-restartless-webext@tests.mozilla.org";
   const ADDON_INSTALL_DATE = truncateToDays(Date.now());
   const EXPECTED_ADDON_DATA = {
     blocklisted: false,
     description: "A restartless addon which gets enabled without a reboot.",
--- a/toolkit/locales/en-US/chrome/global/extensions.properties
+++ b/toolkit/locales/en-US/chrome/global/extensions.properties
@@ -36,13 +36,8 @@ newTabControlled.learnMore = Learn more
 
 #LOCALIZATION NOTE (homepageControlled.message) %S is the icon and name of the extension which updated the homepage.
 homepageControlled.message = An extension, %S, changed what you see when you open your homepage and new windows.
 homepageControlled.learnMore = Learn more
 
 #LOCALIZATION NOTE (tabHideControlled.message) %1$S is the icon and name of the extension which hid tabs, %2$S is the icon of the all tabs button.
 tabHideControlled.message = An extension, %1$S, is hiding some of your tabs. You can still access all of your tabs from %2$S.
 tabHideControlled.learnMore = Learn more
-
-# LOCALIZATION NOTE (defaultTheme.name, defaultTheme.description2): This is displayed in about:addons -> Appearance
-defaultTheme.name=Default
-defaultTheme.description2=A theme with the operating system color scheme.
-
--- a/toolkit/locales/en-US/chrome/global/global-extension-fields.properties
+++ b/toolkit/locales/en-US/chrome/global/global-extension-fields.properties
@@ -0,0 +1,8 @@
+# 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/.
+
+# LOCALIZATION NOTE (extension.default-theme@mozilla.org.name, extension.default-theme@mozilla.org.description): This is displayed in about:addons -> Appearance
+extension.default-theme@mozilla.org.name=Default
+extension.default-theme@mozilla.org.description=A theme with the operating system color scheme.
+
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -37,18 +37,16 @@ const PREF_WEBEXT_PERM_PROMPTS        = 
 const UPDATE_REQUEST_VERSION          = 2;
 
 const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
 
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
 const FILE_BLOCKLIST                  = "blocklist.xml";
 
-const DEFAULT_THEME_ID                = "default-theme@mozilla.org";
-
 const BRANCH_REGEXP                   = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
 const PREF_EM_CHECK_COMPATIBILITY_BASE = "extensions.checkCompatibility";
 var PREF_EM_CHECK_COMPATIBILITY = MOZ_COMPATIBILITY_NIGHTLY ?
                                   PREF_EM_CHECK_COMPATIBILITY_BASE + ".nightly" :
                                   undefined;
 
 const VALID_TYPES_REGEXP = /^[\w\-]+$/;
 
@@ -822,37 +820,16 @@ var AddonManagerInternal = {
 
       gStartupComplete = true;
       this.recordTimestamp("AMI_startup_end");
     } catch (e) {
       logger.error("startup failed", e);
       AddonManagerPrivate.recordException("AMI", "startup failed", e);
     }
 
-    let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
-    let extensionsBundle = Services.strings.createBundle(
-      "chrome://global/locale/extensions.properties");
-
-    // When running in xpcshell tests, the default theme may already
-    // exist.
-    if (!LightweightThemeManager._builtInThemes.has(DEFAULT_THEME_ID)) {
-      let author = "Mozilla";
-      try {
-        author = brandBundle.GetStringFromName("vendorShortName");
-      } catch (e) {}
-
-      LightweightThemeManager.addBuiltInTheme({
-        id: DEFAULT_THEME_ID,
-        name: extensionsBundle.GetStringFromName("defaultTheme.name"),
-        description: extensionsBundle.GetStringFromName("defaultTheme.description2"),
-        iconURL: "chrome://mozapps/content/extensions/default-theme-icon.svg",
-        author,
-      });
-    }
-
     logger.debug("Completed startup sequence");
     this.callManagerListeners("onStartup");
   },
 
   /**
    * Registers a new AddonProvider.
    *
    * @param {string} aProvider -The provider to register
rename from toolkit/mozapps/extensions/content/default-theme-icon.svg
rename to toolkit/mozapps/extensions/default-theme/icon.svg
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/default-theme/manifest.json
@@ -0,0 +1,19 @@
+{
+  "manifest_version": 2,
+
+  "applications": {
+    "gecko": {
+      "id": "default-theme@mozilla.org"
+    }
+  },
+
+  "name": "Default",
+  "description": "A theme with the operating system color scheme.",
+  "author": "Mozilla",
+  "version": "1.0",
+
+  "icons": {"32": "icon.svg"},
+
+  "theme": {
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/default-theme/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES.themes['default'] += [
+    'icon.svg',
+    'manifest.json',
+]
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -27,17 +27,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   DeferredTask: "resource://gre/modules/DeferredTask.jsm",
   ExtensionUtils: "resource://gre/modules/ExtensionUtils.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PermissionsUtils: "resource://gre/modules/PermissionsUtils.jsm",
   Services: "resource://gre/modules/Services.jsm",
 
   Blocklist: "resource://gre/modules/Blocklist.jsm",
-  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   UpdateChecker: "resource://gre/modules/addons/XPIInstall.jsm",
   XPIInstall: "resource://gre/modules/addons/XPIInstall.jsm",
   XPIInternal: "resource://gre/modules/addons/XPIProvider.jsm",
   XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm",
   verifyBundleSignedState: "resource://gre/modules/addons/XPIInstall.jsm",
 });
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "allowPrivateBrowsingByDefault",
@@ -616,17 +615,17 @@ class AddonInternal {
 
     // Add-ons that aren't installed cannot be modified in any way
     if (!(this.inDatabase))
       return permissions;
 
     if (!this.appDisabled) {
       if (this.userDisabled || this.softDisabled) {
         permissions |= AddonManager.PERM_CAN_ENABLE;
-      } else {
+      } else if (this.type != "theme" || this.id != DEFAULT_THEME_ID) {
         permissions |= AddonManager.PERM_CAN_DISABLE;
       }
     }
 
     // Add-ons that are in locked install locations, or are pending uninstall
     // cannot be upgraded or uninstalled
     if (!this.location.locked && !this.pendingUninstall) {
       // System add-on upgrades are triggered through a different mechanism (see updateSystemAddons())
@@ -1620,30 +1619,31 @@ this.XPIDatabase = {
    * @param {string} aType
    *        The type of the newly enabled add-on
    */
   async addonChanged(aId, aType) {
     // We only care about themes in this provider
     if (aType !== "theme")
       return;
 
+    let enableTheme;
+
     let addons = this.getAddonsByType("theme");
     for (let theme of addons) {
-      if (theme.visible && theme.id != aId)
-        await this.updateAddonDisabledState(theme, true, undefined, true);
+      if (theme.visible) {
+        if (!aId && theme.id == DEFAULT_THEME_ID) {
+          enableTheme = theme;
+        } else if (theme.id != aId) {
+          this.updateAddonDisabledState(theme, true, undefined, true);
+        }
+      }
     }
 
-    if (!aId && (!LightweightThemeManager.currentTheme ||
-                 LightweightThemeManager.currentTheme !== DEFAULT_THEME_ID)) {
-      let theme = LightweightThemeManager.getUsedTheme(DEFAULT_THEME_ID);
-      // This can only ever be null in tests.
-      // This can all go away once lightweight themes are gone.
-      if (theme) {
-        LightweightThemeManager.currentTheme = theme;
-      }
+    if (enableTheme) {
+      await this.updateAddonDisabledState(enableTheme, false, undefined, true);
     }
   },
 
   SIGNED_TYPES,
 
   /**
    * Asynchronously list all addons that match the filter function
    *
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -79,16 +79,18 @@ const PREF_PENDING_OPERATIONS         = 
 const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
 const PREF_XPI_ENABLED                = "xpinstall.enabled";
 const PREF_XPI_DIRECT_WHITELISTED     = "xpinstall.whitelist.directRequest";
 const PREF_XPI_FILE_WHITELISTED       = "xpinstall.whitelist.fileRequest";
 const PREF_XPI_WHITELIST_REQUIRED     = "xpinstall.whitelist.required";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
+const DEFAULT_THEME_ID = "default-theme@mozilla.org";
+
 /* globals BOOTSTRAP_REASONS, KEY_APP_SYSTEM_ADDONS, KEY_APP_SYSTEM_DEFAULTS, PREF_BRANCH_INSTALLED_ADDON, PREF_SYSTEM_ADDON_SET, TEMPORARY_ADDON_SUFFIX, XPI_PERMISSION, XPIStates, getURIForResourceInFile, iterDirectory */
 const XPI_INTERNAL_SYMBOLS = [
   "BOOTSTRAP_REASONS",
   "KEY_APP_SYSTEM_ADDONS",
   "KEY_APP_SYSTEM_DEFAULTS",
   "PREF_BRANCH_INSTALLED_ADDON",
   "PREF_SYSTEM_ADDON_SET",
   "TEMPORARY_ADDON_SUFFIX",
@@ -3672,16 +3674,27 @@ var XPIInstall = {
         } catch (e) {
           return false;
         }
       },
     };
 
     let addon = await loadManifest(pkg, XPIInternal.BuiltInLocation);
     addon.rootURI = base;
+
+    // Themes are disabled by default at install time. However, we
+    // always want one theme to be active, falling back to the default
+    // theme when the active theme is disabled. The first time we
+    // install the default theme, though, there likely aren't any other
+    // theme add-ons installed yet, in which case we want to enable it
+    // immediately.
+    if (addon.id === DEFAULT_THEME_ID &&
+        !XPIDatabase.getAddonsByType("theme").some(theme => !theme.disabled)) {
+      addon.userDisabled = false;
+    }
     await this._activateAddon(addon);
   },
 
   /**
    * Activate a newly installed addon.
    * This function handles all the bookkeeping related to a new addon
    * and invokes whatever bootstrap methods are necessary.
    * Note that this function is only used for temporary and built-in
@@ -3709,18 +3722,23 @@ var XPIInstall = {
       }
       throw new Error(message);
     }
 
     let oldAddon = await XPIDatabase.getVisibleAddonForID(addon.id);
 
     let install = () => {
       addon.visible = true;
-      addon.active = true;
-      addon.userDisabled = false;
+      // Themes are generally not enabled by default at install time,
+      // unless enabled by the front-end code. If they are meant to be
+      // enabled, they will already have been enabled by this point.
+      if (addon.type !== "theme" || addon.location.isTemporary) {
+        addon.userDisabled = false;
+      }
+      addon.active = !addon.disabled;
 
       addon = XPIDatabase.addToDatabase(addon, addon._sourceBundle ? addon._sourceBundle.path : null);
 
       XPIStates.addAddon(addon);
       XPIStates.save();
     };
 
     AddonManagerPrivate.callAddonListeners("onInstalling", addon.wrapper);
@@ -3743,17 +3761,17 @@ var XPIInstall = {
 
     AddonManagerPrivate.callInstallListeners("onExternalInstall",
                                              null, addon.wrapper,
                                              oldAddon ? oldAddon.wrapper : null,
                                              false);
     AddonManagerPrivate.callAddonListeners("onInstalled", addon.wrapper);
 
     // Notify providers that a new theme has been enabled.
-    if (addon.type === "theme")
+    if (addon.type === "theme" && !addon.userDisabled)
       AddonManagerPrivate.notifyAddonChanged(addon.id, addon.type, false);
   },
 
   /**
    * Uninstalls an add-on, immediately if possible or marks it as pending
    * uninstall if not.
    *
    * @param {DBAddonInternal} aAddon
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2261,16 +2261,20 @@ var XPIProvider = {
       Services.prefs.addObserver(PREF_ALLOW_LEGACY, this);
       Services.obs.addObserver(this, NOTIFICATION_FLUSH_PERMISSIONS);
 
 
       this.checkForChanges(aAppChanged, aOldAppVersion, aOldPlatformVersion);
 
       AddonManagerPrivate.markProviderSafe(this);
 
+      this.maybeInstallBuiltinAddon(
+          "default-theme@mozilla.org", "1.0",
+          "resource://gre/modules/themes/default/");
+
       resolveProviderReady(Promise.all(this.startupPromises));
 
       if (AppConstants.MOZ_CRASHREPORTER) {
         // Annotate the crash report with relevant add-on information.
         try {
           Services.appinfo.annotateCrashReport("EMCheckCompatibility",
                                                AddonManager.checkCompatibility);
         } catch (e) { }
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -1,15 +1,14 @@
 # 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/.
 
 toolkit.jar:
 % content mozapps %content/mozapps/
-  content/mozapps/extensions/default-theme-icon.svg             (content/default-theme-icon.svg)
   content/mozapps/extensions/shortcuts.html                     (content/shortcuts.html)
   content/mozapps/extensions/shortcuts.css                      (content/shortcuts.css)
   content/mozapps/extensions/shortcuts.js                       (content/shortcuts.js)
 #ifndef MOZ_FENNEC
 * content/mozapps/extensions/extensions.xul                     (content/extensions.xul)
   content/mozapps/extensions/extensions.css                     (content/extensions.css)
   content/mozapps/extensions/extensions.js                      (content/extensions.js)
 * content/mozapps/extensions/extensions.xml                     (content/extensions.xml)
--- a/toolkit/mozapps/extensions/moz.build
+++ b/toolkit/mozapps/extensions/moz.build
@@ -10,17 +10,20 @@ with Files('docs/**'):
     SCHEDULES.exclusive = ['docs']
 
 if CONFIG['MOZ_BUILD_APP'] == 'browser':
     DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True
 
 if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':
     DEFINES['MOZ_FENNEC'] = True
 
-DIRS += ['internal']
+DIRS += [
+    'default-theme',
+    'internal',
+]
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'amIAddonManagerStartup.idl',
     'amIWebInstallPrompt.idl',
 ]
 
 XPIDL_MODULE = 'extensions'
--- a/toolkit/mozapps/extensions/test/browser/browser_html_list_view.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_list_view.js
@@ -352,25 +352,31 @@ add_task(async function testThemeList() 
   is(cards.length, 1, "There is now one custom theme");
 
   let [card] = cards;
   is(card.addon.name, "My theme", "The card is for the test theme");
 
   let enabledSection = getSection(doc, "enabled");
   let disabledSection = getSection(doc, "disabled");
 
+  await TestUtils.waitForCondition(() =>
+      enabledSection.querySelectorAll("addon-card").length == 1);
+
   is(card.parentNode, enabledSection,
      "The new theme card is in the enabled section");
   is(enabledSection.querySelectorAll("addon-card").length,
      1, "There is one enabled theme");
 
   let themesChanged = waitForThemeChange(list);
   card.querySelector('[action="toggle-disabled"]').click();
   await themesChanged;
 
+  await TestUtils.waitForCondition(() =>
+      enabledSection.querySelectorAll("addon-card").length == 1);
+
   is(card.parentNode, disabledSection,
      "The card is now in the disabled section");
   is(enabledSection.querySelectorAll("addon-card").length,
      1, "There is one enabled theme");
 
   await theme.unload();
   await closeView(win);
 });
@@ -407,27 +413,33 @@ add_task(async function testBuiltInTheme
   is(darkButtons.toggleDisabled.hidden, false, "Enable is visible");
   is(darkButtons.remove.hidden, true, "Remove is hidden");
 
   // Enable the dark theme and check the buttons again.
   let themesChanged = waitForThemeChange(list);
   darkButtons.toggleDisabled.click();
   await themesChanged;
 
+  await TestUtils.waitForCondition(() =>
+      enabledSection.querySelectorAll("addon-card").length == 1);
+
   // Check the buttons.
   is(defaultButtons.toggleDisabled.hidden, false, "Enable is visible");
   is(defaultButtons.remove.hidden, true, "Remove is hidden");
   is(darkButtons.toggleDisabled.hidden, false, "Disable is visible");
   is(darkButtons.remove.hidden, true, "Remove is hidden");
 
   // Disable the dark theme.
   themesChanged = waitForThemeChange(list);
   darkButtons.toggleDisabled.click();
   await themesChanged;
 
+  await TestUtils.waitForCondition(() =>
+      enabledSection.querySelectorAll("addon-card").length == 1);
+
   // The themes are back to their starting posititons.
   is(defaultTheme.parentNode, enabledSection, "Default is enabled");
   is(darkTheme.parentNode, disabledSection, "Dark is disabled");
 
   await closeView(win);
 });
 
 add_task(async function testOnlyTypeIsShown() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_db_path.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_db_path.js
@@ -1,12 +1,14 @@
 
 const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
 
+const DEFAULT_THEME_ID = "default-theme@mozilla.org";
+
 let global = this;
 
 // Test that paths in the extensions database are stored properly
 // if they include non-ascii characters (see bug 1428234 for an example of
 // a past bug with such paths)
 add_task(async function test_non_ascii_path() {
   let env = Cc["@mozilla.org/process/environment;1"]
               .getService(Ci.nsIEnvironment);
@@ -39,15 +41,16 @@ add_task(async function test_non_ascii_p
   await AddonTestUtils.promiseStartupManager();
   await AddonTestUtils.promiseInstallFile(xpi2);
   await AddonTestUtils.promiseShutdownManager();
 
   let dbfile = OS.Path.join(profileDir, "extensions.json");
   let raw = new TextDecoder().decode(await OS.File.read(dbfile));
   let data = JSON.parse(raw);
 
-  Assert.ok(Array.isArray(data.addons), "extensions.json has addons array");
-  Assert.equal(2, data.addons.length, "extensions.json has 2 addons");
-  Assert.ok(data.addons[0].path.startsWith(profileDir),
+  let addons = data.addons.filter(a => a.id !== DEFAULT_THEME_ID);
+  Assert.ok(Array.isArray(addons), "extensions.json has addons array");
+  Assert.equal(2, addons.length, "extensions.json has 2 addons");
+  Assert.ok(addons[0].path.startsWith(profileDir),
             "path property for sideloaded extension has the proper profile directory");
-  Assert.ok(data.addons[1].path.startsWith(profileDir),
+  Assert.ok(addons[1].path.startsWith(profileDir),
             "path property for extension installed at runtime has the proper profile directory");
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -2,18 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies startup detection of added/removed/changed items and install
 // location priorities
 
 // Enable loading extensions from the user and system scopes
 Services.prefs.setIntPref("extensions.enabledScopes",
-                          AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
-                          AddonManager.SCOPE_SYSTEM);
+                          AddonManager.SCOPE_PROFILE | AddonManager.SCOPE_USER);
 
 function getID(n) { return `addon${n}@tests.mozilla.org`; }
 function initialVersion(n) { return `${n}.0`; }
 
 const ID1 = getID(1);
 const ID2 = getID(2);
 const ID3 = getID(3);
 const ID4 = getID(4);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
@@ -14,16 +14,19 @@ const THEME_IDS = [
   "default-theme@mozilla.org",
 ];
 const REAL_THEME_IDS = [THEME_IDS[0], THEME_IDS[2]];
 const DEFAULT_THEME = THEME_IDS[2];
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
+Services.prefs.setIntPref("extensions.enabledScopes",
+                          AddonManager.SCOPE_PROFILE | AddonManager.SCOPE_APPLICATION);
+
 // We remember the last/ currently active theme for tracking events.
 var gActiveTheme = null;
 
 add_task(async function setup_to_default_browserish_state() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   await promiseWriteWebManifestForExtension({
     author: "Some author",
@@ -86,16 +89,17 @@ async function setDisabledStateAndCheck(
       [ "onEnabling", false ],
       [ "onEnabled", false ],
     ],
   };
 
   // Set the state of the theme to change.
   let theme = await promiseAddonByID(which);
   prepare_test(expectedEvents);
+  let enabledPromise = promiseAddonEvent("onEnabled");
   if (disabled) {
     await theme.disable();
   } else {
     await theme.enable();
   }
 
   let isDisabled;
   for (theme of await promiseAddonsByIDs(REAL_THEME_IDS)) {
@@ -103,16 +107,18 @@ async function setDisabledStateAndCheck(
     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");
     Assert.equal(theme.isActive, !isDisabled,
       `Theme '${theme.id} should be ${isDisabled ? "in" : ""}active`);
   }
 
+  await enabledPromise;
+
   await promiseRestartManager();
 
   // All should still be good after a restart of the Addon Manager.
   for (theme of await promiseAddonsByIDs(REAL_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.isActive, !isDisabled,