Bug 1532165 use prefs to support extension newtab and homepage on startup. r=kmag
authorShane Caraveo <scaraveo@mozilla.com>
Mon, 08 Apr 2019 22:36:26 +0000
changeset 468799 e8634d10ef03068a08d6d85c2ce754ff9f0714f2
parent 468798 c64199fb6020ac6a0fd093d7de961b4fafc21270
child 468800 995276110de48270e38039380ce4674cad10d93e
push id35850
push userdvarga@mozilla.com
push dateWed, 10 Apr 2019 21:52:56 +0000
treeherdermozilla-central@9d3dbe3fef26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1532165
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 1532165 use prefs to support extension newtab and homepage on startup. r=kmag Differential Revision: https://phabricator.services.mozilla.com/D25808
browser/base/content/utilityOverlay.js
browser/components/extensions/parent/ext-chrome-settings-overrides.js
browser/components/extensions/parent/ext-url-overrides.js
browser/components/extensions/test/browser/browser-private.ini
browser/components/extensions/test/browser/browser_ext_tabs_newtab_private.js
browser/components/extensions/test/xpcshell/test_ext_homepage_overrides_private.js
browser/components/extensions/test/xpcshell/xpcshell-common.ini
browser/modules/HomePage.jsm
toolkit/components/extensions/ExtensionSettingsStore.jsm
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -23,35 +23,24 @@ XPCOMUtils.defineLazyServiceGetter(this,
 Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
   enumerable: true,
   get() {
     if (PrivateBrowsingUtils.isWindowPrivate(window)) {
       if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
           !aboutNewTabService.overridden) {
         return "about:privatebrowsing";
       }
-      // If the extension does not have private browsing permission,
-      // use about:privatebrowsing.
-      let extensionInfo;
-      try {
-        extensionInfo = ExtensionSettingsStore.getSetting("url_overrides", "newTabURL");
-      } catch (e) {
-        // ExtensionSettings may not be initialized if no extensions are enabled.  If
-        // we have some indication that an extension controls the homepage, return
-        // the defaults instead.
-        if (aboutNewTabService.newTabURL.startsWith("moz-extension://")) {
-          return "about:privatebrowsing";
-        }
-      }
-
-      if (extensionInfo) {
-        let policy = WebExtensionPolicy.getByID(extensionInfo.id);
-        if (!policy || !policy.privateBrowsingAllowed) {
-          return "about:privatebrowsing";
-        }
+      // If an extension controls the setting and does not have private
+      // browsing permission, use the default setting.
+      let extensionControlled = Services.prefs.getBoolPref("browser.newtab.extensionControlled", false);
+      let privateAllowed = Services.prefs.getBoolPref("browser.newtab.privateAllowed", false);
+      // There is a potential on upgrade that the prefs are not set yet, so we double check
+      // for moz-extension.
+      if (!privateAllowed && (extensionControlled || aboutNewTabService.newTabURL.startsWith("moz-extension://"))) {
+        return "about:privatebrowsing";
       }
     }
     return aboutNewTabService.newTabURL;
   },
 });
 
 var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
 
--- a/browser/components/extensions/parent/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/parent/ext-chrome-settings-overrides.js
@@ -1,26 +1,29 @@
 /* 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";
 
 var {ExtensionPreferencesManager} = ChromeUtils.import("resource://gre/modules/ExtensionPreferencesManager.jsm");
+var {ExtensionParent} = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
 
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionControlledPopup",
                                "resource:///modules/ExtensionControlledPopup.jsm");
 
 const DEFAULT_SEARCH_STORE_TYPE = "default_search";
 const DEFAULT_SEARCH_SETTING_NAME = "defaultSearch";
 const ENGINE_ADDED_SETTING_NAME = "engineAdded";
 
 const HOMEPAGE_PREF = "browser.startup.homepage";
+const HOMEPAGE_PRIVATE_ALLOWED = "browser.startup.homepage_override.privateAllowed";
+const HOMEPAGE_EXTENSION_CONTROLLED = "browser.startup.homepage_override.extensionControlled";
 const HOMEPAGE_CONFIRMED_TYPE = "homepageNotification";
 const HOMEPAGE_SETTING_TYPE = "prefs";
 const HOMEPAGE_SETTING_NAME = "homepage_override";
 
 XPCOMUtils.defineLazyGetter(this, "homepagePopup", () => {
   return new ExtensionControlledPopup({
     confirmedType: HOMEPAGE_CONFIRMED_TYPE,
     observerTopic: "browser-open-homepage-start",
@@ -181,22 +184,46 @@ this.chrome_settings_overrides = class e
           extension.id, "homepage_override", homepageUrl);
       } else {
         let item = await ExtensionPreferencesManager.getSetting("homepage_override");
         inControl = item.id == extension.id;
       }
       // We need to add the listener here too since onPrefsChanged won't trigger on a
       // restart (the prefs are already set).
       if (inControl) {
+        Services.prefs.setBoolPref(HOMEPAGE_PRIVATE_ALLOWED, extension.privateBrowsingAllowed);
+        // Also set this now as an upgraded browser will need this.
+        Services.prefs.setBoolPref(HOMEPAGE_EXTENSION_CONTROLLED, true);
         if (extension.startupReason == "APP_STARTUP") {
           handleInitialHomepagePopup(extension.id, homepageUrl);
         } else {
           homepagePopup.addObserver(extension.id);
         }
       }
+
+      // We need to monitor permission change and update the preferences.
+      // eslint-disable-next-line mozilla/balanced-listeners
+      extension.on("add-permissions", async (ignoreEvent, permissions) => {
+        if (permissions.permissions.includes("internal:privateBrowsingAllowed")) {
+          let item = await ExtensionPreferencesManager.getSetting("homepage_override");
+          if (item.id == extension.id) {
+            Services.prefs.setBoolPref(HOMEPAGE_PRIVATE_ALLOWED, true);
+          }
+        }
+      });
+      // eslint-disable-next-line mozilla/balanced-listeners
+      extension.on("remove-permissions", async (ignoreEvent, permissions) => {
+        if (permissions.permissions.includes("internal:privateBrowsingAllowed")) {
+          let item = await ExtensionPreferencesManager.getSetting("homepage_override");
+          if (item.id == extension.id) {
+            Services.prefs.setBoolPref(HOMEPAGE_PRIVATE_ALLOWED, false);
+          }
+        }
+      });
+
       extension.callOnClose({
         close: () => {
           if (extension.shutdownReason == "ADDON_DISABLE") {
             homepagePopup.clearConfirmation(extension.id);
           }
         },
       });
     }
@@ -339,27 +366,36 @@ this.chrome_settings_overrides = class e
     }
     return true;
   }
 };
 
 ExtensionPreferencesManager.addSetting("homepage_override", {
   prefNames: [
     HOMEPAGE_PREF,
+    HOMEPAGE_EXTENSION_CONTROLLED,
   ],
   // ExtensionPreferencesManager will call onPrefsChanged when control changes
   // and it updates the preferences. We are passed the item from
   // ExtensionSettingsStore that details what is in control. If there is an id
   // then control has changed to an extension, if there is no id then control
   // has been returned to the user.
   onPrefsChanged(item) {
     if (item.id) {
       homepagePopup.addObserver(item.id);
+
+      let policy = ExtensionParent.WebExtensionPolicy.getByID(item.id);
+      Services.prefs.setBoolPref(HOMEPAGE_PRIVATE_ALLOWED, policy && policy.privateBrowsingAllowed);
+      Services.prefs.setBoolPref(HOMEPAGE_EXTENSION_CONTROLLED, true);
     } else {
       homepagePopup.removeObserver();
+
+      Services.prefs.clearUserPref(HOMEPAGE_PRIVATE_ALLOWED);
+      Services.prefs.clearUserPref(HOMEPAGE_EXTENSION_CONTROLLED);
     }
   },
   setCallback(value) {
     return {
       [HOMEPAGE_PREF]: value,
+      [HOMEPAGE_EXTENSION_CONTROLLED]: !!value,
     };
   },
 });
--- a/browser/components/extensions/parent/ext-url-overrides.js
+++ b/browser/components/extensions/parent/ext-url-overrides.js
@@ -1,26 +1,30 @@
 /* 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";
 
+var {ExtensionParent} = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
+
 ChromeUtils.defineModuleGetter(this, "ExtensionControlledPopup",
                                "resource:///modules/ExtensionControlledPopup.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 const STORE_TYPE = "url_overrides";
 const NEW_TAB_SETTING_NAME = "newTabURL";
 const NEW_TAB_CONFIRMED_TYPE = "newTabNotification";
+const NEW_TAB_PRIVATE_ALLOWED = "browser.newtab.privateAllowed";
+const NEW_TAB_EXTENSION_CONTROLLED = "browser.newtab.extensionControlled";
 
 XPCOMUtils.defineLazyGetter(this, "newTabPopup", () => {
   return new ExtensionControlledPopup({
     confirmedType: NEW_TAB_CONFIRMED_TYPE,
     observerTopic: "browser-open-newtab-start",
     popupnotificationId: "extension-new-tab-notification",
     settingType: STORE_TYPE,
     settingKey: NEW_TAB_SETTING_NAME,
@@ -55,34 +59,53 @@ XPCOMUtils.defineLazyGetter(this, "newTa
       }, "newtab-url-changed");
     },
   });
 });
 
 function setNewTabURL(extensionId, url) {
   if (extensionId) {
     newTabPopup.addObserver(extensionId);
+    let policy = ExtensionParent.WebExtensionPolicy.getByID(extensionId);
+    Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, policy && policy.privateBrowsingAllowed);
+    Services.prefs.setBoolPref(NEW_TAB_EXTENSION_CONTROLLED, true);
   } else {
     newTabPopup.removeObserver();
+    Services.prefs.clearUserPref(NEW_TAB_PRIVATE_ALLOWED);
+    Services.prefs.clearUserPref(NEW_TAB_EXTENSION_CONTROLLED);
   }
-  aboutNewTabService.newTabURL = url;
+  if (url) {
+    aboutNewTabService.newTabURL = url;
+  }
 }
 
+// eslint-disable-next-line mozilla/balanced-listeners
+ExtensionParent.apiManager.on("extension-setting-changed", async (eventName, setting) => {
+  let extensionId, url;
+  if (setting.type === STORE_TYPE && setting.key === NEW_TAB_SETTING_NAME) {
+    if (setting.action === "enable" || setting.item) {
+      let {item} = setting;
+      // If setting.item exists, it is the new value.  If it doesn't exist, and an
+      // extension is being enabled, we use the id.
+      extensionId = (item && item.id) || setting.id;
+      url = item && (item.value || item.initialValue);
+    }
+  }
+  setNewTabURL(extensionId, url);
+});
+
 this.urlOverrides = class extends ExtensionAPI {
   static onUninstall(id) {
     // TODO: This can be removed once bug 1438364 is fixed and all data is cleaned up.
     newTabPopup.clearConfirmation(id);
   }
 
   processNewTabSetting(action) {
     let {extension} = this;
-    let item = ExtensionSettingsStore[action](extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
-    if (item) {
-      setNewTabURL(item.id, item.value || item.initialValue);
-    }
+    ExtensionSettingsStore[action](extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
   }
 
   async onManifestEntry(entryName) {
     let {extension} = this;
     let {manifest} = extension;
 
     await ExtensionSettingsStore.initialize();
 
@@ -120,11 +143,31 @@ this.urlOverrides = class extends Extens
           .includes(extension.startupReason)) {
         item = ExtensionSettingsStore.enable(extension.id, STORE_TYPE, NEW_TAB_SETTING_NAME);
       }
 
       // Set the newTabURL to the current value of the setting.
       if (item) {
         setNewTabURL(item.id, item.value || item.initialValue);
       }
+
+      // We need to monitor permission change and update the preferences.
+      // eslint-disable-next-line mozilla/balanced-listeners
+      extension.on("add-permissions", async (ignoreEvent, permissions) => {
+        if (permissions.permissions.includes("internal:privateBrowsingAllowed")) {
+          let item = await ExtensionSettingsStore.getSetting(STORE_TYPE, NEW_TAB_SETTING_NAME);
+          if (item && item.id == extension.id) {
+            Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, true);
+          }
+        }
+      });
+      // eslint-disable-next-line mozilla/balanced-listeners
+      extension.on("remove-permissions", async (ignoreEvent, permissions) => {
+        if (permissions.permissions.includes("internal:privateBrowsingAllowed")) {
+          let item = await ExtensionSettingsStore.getSetting(STORE_TYPE, NEW_TAB_SETTING_NAME);
+          if (item && item.id == extension.id) {
+            Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, false);
+          }
+        }
+      });
     }
   }
 };
--- a/browser/components/extensions/test/browser/browser-private.ini
+++ b/browser/components/extensions/test/browser/browser-private.ini
@@ -3,8 +3,9 @@
 # This manifest lists tests that use permanent private browsing mode.
 #
 tags = webextensions
 prefs = browser.privatebrowsing.autostart=true
 support-files =
   head.js
 
 [browser_ext_tabs_cookieStoreId_private.js]
+[browser_ext_tabs_newtab_private.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_newtab_private.js
@@ -0,0 +1,79 @@
+"use strict";
+
+const {GlobalManager} = ChromeUtils.import("resource://gre/modules/Extension.jsm", null);
+const {ExtensionPermissions} = ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm");
+
+const NEWTAB_PRIVATE_ALLOWED = "browser.newtab.privateAllowed";
+const NEWTAB_EXTENSION_CONTROLLED = "browser.newtab.extensionControlled";
+const NEWTAB_URI = "webext-newtab-1.html";
+
+function promisePrefChange(pref) {
+  return new Promise((resolve, reject) => {
+    Services.prefs.addObserver(pref, function observer() {
+      Services.prefs.removeObserver(pref, observer);
+      resolve(arguments);
+    });
+  });
+}
+
+function verifyPrefSettings(controlled, allowed) {
+  is(Services.prefs.getBoolPref(NEWTAB_EXTENSION_CONTROLLED, false), controlled, "newtab extension controlled");
+  is(Services.prefs.getBoolPref(NEWTAB_PRIVATE_ALLOWED, false), allowed, "newtab private permission after permission change");
+
+  if (controlled) {
+    ok(aboutNewTabService.newTabURL.endsWith(NEWTAB_URI), "Newtab url is overridden by the extension.");
+  }
+  if (controlled && allowed) {
+    ok(BROWSER_NEW_TAB_URL.endsWith(NEWTAB_URI), "active newtab url is overridden by the extension.");
+  } else {
+    let expectednewTab = controlled ? "about:privatebrowsing" : "about:newtab";
+    is(BROWSER_NEW_TAB_URL, expectednewTab, "active newtab url is default.");
+  }
+}
+
+async function promiseUpdatePrivatePermission(allowed, extension) {
+  info(`update private allowed permission`);
+  let ext = GlobalManager.extensionMap.get(extension.id);
+  await Promise.all([
+    promisePrefChange(NEWTAB_PRIVATE_ALLOWED),
+    ExtensionPermissions[allowed ? "add" : "remove"](extension.id,
+                                                     {permissions: ["internal:privateBrowsingAllowed"], origins: []},
+                                                     ext),
+  ]);
+
+  verifyPrefSettings(true, allowed);
+}
+
+add_task(async function test_new_tab_private() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "applications": {
+        "gecko": {
+          "id": "@private-newtab",
+        },
+      },
+      "chrome_url_overrides": {
+        newtab: NEWTAB_URI,
+      },
+    },
+    files: {
+      NEWTAB_URI: `
+        <!DOCTYPE html>
+        <head>
+          <meta charset="utf-8"/></head>
+        <html>
+          <body>
+          </body>
+        </html>
+      `,
+    },
+    useAddonManager: "permanent",
+  });
+  await extension.startup();
+
+  verifyPrefSettings(true, false);
+
+  promiseUpdatePrivatePermission(true, extension);
+
+  await extension.unload();
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/xpcshell/test_ext_homepage_overrides_private.js
@@ -0,0 +1,117 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
+const {HomePage} = ChromeUtils.import("resource:///modules/HomePage.jsm");
+const {ExtensionPermissions} = ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm");
+
+const {
+  createAppInfo,
+  promiseShutdownManager,
+  promiseStartupManager,
+} = AddonTestUtils;
+
+const EXTENSION_ID = "test_overrides@tests.mozilla.org";
+const HOMEPAGE_EXTENSION_CONTROLLED = "browser.startup.homepage_override.extensionControlled";
+const HOMEPAGE_PRIVATE_ALLOWED = "browser.startup.homepage_override.privateAllowed";
+const HOMEPAGE_URL_PREF = "browser.startup.homepage";
+const HOMEPAGE_URI = "webext-homepage-1.html";
+
+Services.prefs.setBoolPref("browser.privatebrowsing.autostart", true);
+Services.prefs.setBoolPref("extensions.allowPrivateBrowsingByDefault", false);
+
+AddonTestUtils.init(this);
+AddonTestUtils.usePrivilegedSignatures = false;
+AddonTestUtils.overrideCertDB();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+function promisePrefChange(pref) {
+  return new Promise((resolve, reject) => {
+    Services.prefs.addObserver(pref, function observer() {
+      Services.prefs.removeObserver(pref, observer);
+      resolve(arguments);
+    });
+  });
+}
+
+let defaultHomepageURL;
+
+function verifyPrefSettings(controlled, allowed) {
+  equal(Services.prefs.getBoolPref(HOMEPAGE_EXTENSION_CONTROLLED, false), controlled, "homepage extension controlled");
+  equal(Services.prefs.getBoolPref(HOMEPAGE_PRIVATE_ALLOWED, false), allowed, "homepage private permission after permission change");
+
+  if (controlled && allowed) {
+    ok(HomePage.get().endsWith(HOMEPAGE_URI), "Home page url is overridden by the extension");
+  } else {
+    equal(HomePage.get(), defaultHomepageURL, "Home page url is default.");
+  }
+}
+
+async function promiseUpdatePrivatePermission(allowed, extension) {
+  info(`update private allowed permission`);
+  await Promise.all([
+    promisePrefChange(HOMEPAGE_PRIVATE_ALLOWED),
+    ExtensionPermissions[allowed ? "add" : "remove"](extension.id,
+                                                     {permissions: ["internal:privateBrowsingAllowed"], origins: []},
+                                                     extension),
+  ]);
+
+  verifyPrefSettings(true, allowed);
+}
+
+add_task(async function test_overrides_private() {
+  await promiseStartupManager();
+
+  let extensionInfo = {
+    useAddonManager: "permanent",
+    manifest: {
+      "version": "1.0",
+      "applications": {
+        "gecko": {
+          "id": EXTENSION_ID,
+        },
+      },
+      "chrome_settings_overrides": {
+        "homepage": HOMEPAGE_URI,
+      },
+    },
+  };
+  let extension = ExtensionTestUtils.loadExtension(extensionInfo);
+
+  defaultHomepageURL = HomePage.get();
+
+  await extension.startup();
+
+  verifyPrefSettings(true, false);
+
+  equal(HomePage.get(), defaultHomepageURL, "Home page url is default.");
+
+  info("add permission to extension");
+  await promiseUpdatePrivatePermission(true, extension.extension);
+  info("remove permission from extension");
+  await promiseUpdatePrivatePermission(false, extension.extension);
+  // set back to true to test upgrade removing extension control
+  info("add permission back to prepare for upgrade test");
+  await promiseUpdatePrivatePermission(true, extension.extension);
+
+  extensionInfo.manifest = {
+    "version": "2.0",
+    "applications": {
+      "gecko": {
+        "id": EXTENSION_ID,
+      },
+    },
+  };
+
+  await Promise.all([
+    promisePrefChange(HOMEPAGE_URL_PREF),
+    extension.upgrade(extensionInfo),
+  ]);
+
+  verifyPrefSettings(false, false);
+
+  await extension.unload();
+  await promiseShutdownManager();
+});
--- a/browser/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -7,9 +7,10 @@
 [test_ext_chrome_settings_overrides_update.js]
 [test_ext_distribution_popup.js]
 [test_ext_history.js]
 [test_ext_settings_overrides_search.js]
 [test_ext_settings_overrides_search_mozParam.js]
 [test_ext_settings_overrides_shutdown.js]
 [test_ext_url_overrides_newtab.js]
 [test_ext_url_overrides_newtab_update.js]
+[test_ext_homepage_overrides_private.js]
 
--- a/browser/modules/HomePage.jsm
+++ b/browser/modules/HomePage.jsm
@@ -7,18 +7,16 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["HomePage"];
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
-                               "resource://gre/modules/ExtensionSettingsStore.jsm");
 
 const kPrefName = "browser.startup.homepage";
 
 function getHomepagePref(useDefault) {
   let homePage;
   let prefs = Services.prefs;
   if (useDefault) {
     prefs = prefs.getDefaultBranch(null);
@@ -48,33 +46,22 @@ function getHomepagePref(useDefault) {
 
 let HomePage = {
   get(aWindow) {
     let homePages = getHomepagePref();
     if (PrivateBrowsingUtils.permanentPrivateBrowsing ||
         (aWindow && PrivateBrowsingUtils.isWindowPrivate(aWindow))) {
       // If an extension controls the setting and does not have private
       // browsing permission, use the default setting.
-      let extensionInfo;
-      try {
-        extensionInfo = ExtensionSettingsStore.getSetting("prefs", "homepage_override");
-      } catch (e) {
-        // ExtensionSettings may not be initialized if no extensions are enabled.  If
-        // we have some indication that an extension controls the homepage, return
-        // the defaults instead.
-        if (homePages.includes("moz-extension://")) {
-          return this.getDefault();
-        }
-      }
-
-      if (extensionInfo) {
-        let policy = WebExtensionPolicy.getByID(extensionInfo.id);
-        if (!policy || !policy.privateBrowsingAllowed) {
-          return this.getDefault();
-        }
+      let extensionControlled = Services.prefs.getBoolPref("browser.startup.homepage_override.extensionControlled", false);
+      let privateAllowed = Services.prefs.getBoolPref("browser.startup.homepage_override.privateAllowed", false);
+      // There is a potential on upgrade that the prefs are not set yet, so we double check
+      // for moz-extension.
+      if (!privateAllowed && (extensionControlled || homePages.includes("moz-extension://"))) {
+        return this.getDefault();
       }
     }
 
     return homePages;
   },
 
   getDefault() {
     return getHomepagePref(true);
--- a/toolkit/components/extensions/ExtensionSettingsStore.jsm
+++ b/toolkit/components/extensions/ExtensionSettingsStore.jsm
@@ -44,16 +44,18 @@ var EXPORTED_SYMBOLS = ["ExtensionSettin
 
 const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "JSONFile",
                                "resource://gre/modules/JSONFile.jsm");
+ChromeUtils.defineModuleGetter(this, "ExtensionParent",
+                               "resource://gre/modules/ExtensionParent.jsm");
 
 const JSON_FILE_NAME = "extension-settings.json";
 const JSON_FILE_VERSION = 2;
 const STORE_PATH = OS.Path.join(Services.dirsvc.get("ProfD", Ci.nsIFile).path, JSON_FILE_NAME);
 
 let _initializePromise;
 let _store = {};
 
@@ -231,17 +233,17 @@ function alterSetting(id, type, key, act
     returnItem = getItem(type, key);
   }
 
   if (action === "remove" && keyInfo.precedenceList.length === 0) {
     delete _store.data[type][key];
   }
 
   _store.saveSoon();
-
+  ExtensionParent.apiManager.emit("extension-setting-changed", {action, id, type, key, item: returnItem});
   return returnItem;
 }
 
 var ExtensionSettingsStore = {
   /**
    * Loads the JSON file for the SettingsStore into memory.
    * The promise this returns must be resolved before asking the SettingsStore
    * to perform any other operations.
@@ -394,16 +396,17 @@ var ExtensionSettingsStore = {
     if (!precedenceList) {
       // The setting for this key does not exist. Nothing to do.
       return;
     }
 
     for (let item of precedenceList) {
       item.enabled = false;
     }
+    ExtensionParent.apiManager.emit("extension-setting-changed", {action: "disable", type, key});
 
     _store.saveSoon();
   },
 
   /**
    * Retrieves all settings from the store for a given extension.
    *
    * @param {string} id