Bug 1595858 - Allow users to select or deselect addons for homepage and newtab without disabling the addons. r=preferences-reviewers,Gijs,mixedpuppy
☠☠ backed out by d70bf7b7cef9 ☠ ☠
authorErica Wright <ewright@mozilla.com>
Fri, 13 Nov 2020 02:02:47 +0000
changeset 557076 d86e4d6c2be461b9401a96192f8f3daebae036bf
parent 557075 b7bf3ef5388b6ac9abe2bafeb60b2cdb3f9596d5
child 557077 d272ab3d0fb5d102a4d9cf961195f1c3c682fa18
push id130881
push userewright@mozilla.com
push dateFri, 13 Nov 2020 02:45:00 +0000
treeherderautoland@d86e4d6c2be4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspreferences-reviewers, Gijs, mixedpuppy
bugs1595858
milestone84.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 1595858 - Allow users to select or deselect addons for homepage and newtab without disabling the addons. r=preferences-reviewers,Gijs,mixedpuppy Differential Revision: https://phabricator.services.mozilla.com/D93584
browser/components/customizableui/content/panelUI.inc.xhtml
browser/components/extensions/ExtensionControlledPopup.jsm
browser/components/extensions/parent/ext-chrome-settings-overrides.js
browser/components/extensions/parent/ext-url-overrides.js
browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
browser/components/preferences/extensionControlled.js
browser/components/preferences/home.inc.xhtml
browser/components/preferences/home.js
browser/components/preferences/tests/browser_extension_controlled.js
browser/locales/en-US/browser/appMenuNotifications.ftl
browser/locales/en-US/browser/preferences/preferences.ftl
browser/themes/shared/preferences/preferences.inc.css
toolkit/components/extensions/ExtensionSettingsStore.jsm
toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
--- a/browser/components/customizableui/content/panelUI.inc.xhtml
+++ b/browser/components/customizableui/content/panelUI.inc.xhtml
@@ -228,33 +228,35 @@
          type="arrow"
          flip="slide"
          position="bottomcenter topright"
          tabspecific="true">
     <popupnotification id="extension-new-tab-notification"
                        class="extension-controlled-notification"
                        popupid="extension-new-tab"
                        hidden="true"
-                       data-lazy-l10n-id="appmenu-new-tab-controlled"
+                       data-lazy-l10n-id="appmenu-new-tab-controlled-changes"
                        data-l10n-attrs="buttonlabel, buttonaccesskey, secondarybuttonlabel, secondarybuttonaccesskey"
                        closebuttonhidden="true"
                        dropmarkerhidden="true"
+                       buttonhighlight="true"
                        checkboxhidden="true">
       <popupnotificationcontent orient="vertical">
         <description id="extension-new-tab-notification-description"/>
       </popupnotificationcontent>
     </popupnotification>
     <popupnotification id="extension-homepage-notification"
                        class="extension-controlled-notification"
                        popupid="extension-homepage"
                        hidden="true"
-                       data-lazy-l10n-id="appmenu-homepage-controlled"
+                       data-lazy-l10n-id="appmenu-homepage-controlled-changes"
                        data-l10n-attrs="buttonlabel, buttonaccesskey, secondarybuttonlabel, secondarybuttonaccesskey"
                        closebuttonhidden="true"
                        dropmarkerhidden="true"
+                       buttonhighlight="true"
                        checkboxhidden="true">
       <popupnotificationcontent orient="vertical">
         <description id="extension-homepage-notification-description"/>
       </popupnotificationcontent>
     </popupnotification>
     <popupnotification id="extension-tab-hide-notification"
                        class="extension-controlled-notification"
                        popupid="extension-tab-hide"
--- a/browser/components/extensions/ExtensionControlledPopup.jsm
+++ b/browser/components/extensions/ExtensionControlledPopup.jsm
@@ -4,17 +4,17 @@
 /* exported ExtensionControlledPopup */
 
 "use strict";
 
 /*
  * @fileOverview
  * This module exports a class that can be used to handle displaying a popup
  * doorhanger with a primary action to not show a popup for this extension again
- * and a secondary action to disable the extension.
+ * and a secondary action disables the addon, or brings the user to their settings.
  *
  * The original purpose of the popup was to notify users of an extension that has
  * changed the New Tab or homepage. Users would see this popup the first time they
  * view those pages after a change to the setting in each session until they confirm
  * the change by triggering the primary action.
  */
 
 var EXPORTED_SYMBOLS = ["ExtensionControlledPopup"];
@@ -104,16 +104,22 @@ class ExtensionControlledPopup {
    *                 add-on's icon and name). If not provided, then the add-on's
    *                 icon and name are added to the description.
    * @param {string} opts.learnMoreMessageId
    *                 The message id to be used for the text of a "learn more" link which
    *                 will be placed after the description.
    * @param {string} opts.learnMoreLink
    *                 The name of the SUMO page to link to, this is added to
    *                 app.support.baseURL.
+   * @param optional {string} opts.preferencesLocation
+   *                 If included, the name of the preferences tab that will be opened
+   *                 by the secondary action. If not included, the secondary option will
+   *                 disable the addon.
+   * @param optional {string} opts.preferencesEntrypoint
+   *                 The entrypoint to pass to preferences telemetry.
    * @param {function} opts.onObserverAdded
    *                   A callback that is triggered when an observer is registered to
    *                   trigger the popup on the next observerTopic.
    * @param {function} opts.onObserverRemoved
    *                   A callback that is triggered when the observer is removed,
    *                   either because the popup is opening or it was explicitly
    *                   cancelled by calling removeObserver.
    * @param {function} opts.beforeDisableAddon
@@ -130,16 +136,18 @@ class ExtensionControlledPopup {
     this.popupnotificationId = opts.popupnotificationId;
     this.settingType = opts.settingType;
     this.settingKey = opts.settingKey;
     this.descriptionId = opts.descriptionId;
     this.descriptionMessageId = opts.descriptionMessageId;
     this.getLocalizedDescription = opts.getLocalizedDescription;
     this.learnMoreMessageId = opts.learnMoreMessageId;
     this.learnMoreLink = opts.learnMoreLink;
+    this.preferencesLocation = opts.preferencesLocation;
+    this.preferencesEntrypoint = opts.preferencesEntrypoint;
     this.onObserverAdded = opts.onObserverAdded;
     this.onObserverRemoved = opts.onObserverRemoved;
     this.beforeDisableAddon = opts.beforeDisableAddon;
     this.observerRegistered = false;
   }
 
   get topWindow() {
     return Services.wm.getMostRecentWindow("navigator:browser");
@@ -275,16 +283,22 @@ class ExtensionControlledPopup {
     this.populateDescription(doc, addon);
 
     // Setup the command handler.
     let handleCommand = async event => {
       panel.hidePopup();
       if (event.originalTarget == popupnotification.button) {
         // Main action is to keep changes.
         await this.setConfirmation(extensionId);
+      } else if (this.preferencesLocation) {
+        // Secondary action opens Preferences, if a preferencesLocation option is included.
+        let options = this.Entrypoint
+          ? { urlParams: { entrypoint: this.Entrypoint } }
+          : {};
+        win.openPreferences(this.preferencesLocation, options);
       } else {
         // Secondary action is to restore settings.
         if (this.beforeDisableAddon) {
           await this.beforeDisableAddon(this, win);
         }
         await addon.disable();
       }
 
--- a/browser/components/extensions/parent/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/parent/ext-chrome-settings-overrides.js
@@ -52,16 +52,18 @@ XPCOMUtils.defineLazyGetter(this, "homep
     observerTopic: "browser-open-homepage-start",
     popupnotificationId: "extension-homepage-notification",
     settingType: HOMEPAGE_SETTING_TYPE,
     settingKey: HOMEPAGE_SETTING_NAME,
     descriptionId: "extension-homepage-notification-description",
     descriptionMessageId: "homepageControlled.message",
     learnMoreMessageId: "homepageControlled.learnMore",
     learnMoreLink: "extension-home",
+    preferencesLocation: "home-homeOverride",
+    preferencesEntrypoint: "addon-manage-home-override",
     async beforeDisableAddon(popup, win) {
       // Disabling an add-on should remove the tabs that it has open, but we want
       // to open the new homepage in this tab (which might get closed).
       //   1. Replace the tab's URL with about:blank, wait for it to change
       //   2. Now that this tab isn't associated with the add-on, disable the add-on
       //   3. Trigger the browser's homepage method
       let gBrowser = win.gBrowser;
       let tab = gBrowser.selectedTab;
--- a/browser/components/extensions/parent/ext-url-overrides.js
+++ b/browser/components/extensions/parent/ext-url-overrides.js
@@ -36,16 +36,18 @@ XPCOMUtils.defineLazyGetter(this, "newTa
     observerTopic: "browser-open-newtab-start",
     popupnotificationId: "extension-new-tab-notification",
     settingType: STORE_TYPE,
     settingKey: NEW_TAB_SETTING_NAME,
     descriptionId: "extension-new-tab-notification-description",
     descriptionMessageId: "newTabControlled.message2",
     learnMoreMessageId: "newTabControlled.learnMore",
     learnMoreLink: "extension-home",
+    preferencesLocation: "home-newtabOverride",
+    preferencesEntrypoint: "addon-manage-newtab-override",
     onObserverAdded() {
       AboutNewTab.willNotifyUser = true;
     },
     onObserverRemoved() {
       AboutNewTab.willNotifyUser = false;
     },
     async beforeDisableAddon(popup, win) {
       // ExtensionControlledPopup will disable the add-on once this function completes.
--- a/browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
+++ b/browser/components/extensions/test/browser/browser_ext_chrome_settings_overrides_home.js
@@ -33,16 +33,42 @@ const getHomePageURL = () => {
   return Services.prefs.getStringPref(HOMEPAGE_URL_PREF);
 };
 
 function isConfirmed(id) {
   let item = ExtensionSettingsStore.getSetting("homepageNotification", id);
   return !!(item && item.value);
 }
 
+async function assertPreferencesShown(_spotlight) {
+  await TestUtils.waitForCondition(
+    () => gBrowser.currentURI.spec == "about:preferences#home",
+    "Should open about:preferences."
+  );
+
+  await SpecialPowers.spawn(
+    gBrowser.selectedBrowser,
+    [_spotlight],
+    async spotlight => {
+      let doc = content.document;
+      let section = await ContentTaskUtils.waitForCondition(
+        () => doc.querySelector(".spotlight"),
+        "The spotlight should appear."
+      );
+      Assert.equal(
+        section.getAttribute("data-subcategory"),
+        spotlight,
+        "The correct section is spotlighted."
+      );
+    }
+  );
+
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+}
+
 add_task(async function test_multiple_extensions_overriding_home_page() {
   let defaultHomePage = getHomePageURL();
 
   function background() {
     browser.test.onMessage.addListener(async msg => {
       switch (msg) {
         case "checkHomepage":
           let homepage = await browser.browserSettings.homepageOverride.get({});
@@ -363,46 +389,63 @@ add_task(async function test_doorhanger_
     "extension-homepage-notification"
   );
 
   await ext1.startup();
   await ext2.startup();
 
   let popupShown = promisePopupShown(panel);
   BrowserHome();
-  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, () =>
+    gURLBar.value.endsWith("ext2.html")
+  );
   await popupShown;
 
-  ok(gURLBar.value.endsWith("ext2.html"), "ext2 is in control");
-
-  // Click Restore Settings.
+  // Click Manage.
   let popupHidden = promisePopupHidden(panel);
-  let prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
+  // Ensures the preferences tab opens, checks the spotlight, and then closes it
+  let spotlightShown = assertPreferencesShown("homeOverride");
   popupnotification.secondaryButton.click();
+  await popupHidden;
+  await spotlightShown;
+
+  let prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
+  ext2.unload();
   await prefPromise;
-  await popupHidden;
 
   // Expect a new doorhanger for the next extension.
-  await promisePopupShown(panel);
-
-  ok(gURLBar.value.endsWith("ext1.html"), "ext1 is in control");
+  popupShown = promisePopupShown(panel);
+  await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+  let openHomepage = TestUtils.topicObserved("browser-open-homepage-start");
+  BrowserHome();
+  await openHomepage;
+  await popupShown;
+  await TestUtils.waitForCondition(
+    () => gURLBar.value.endsWith("ext1.html"),
+    "ext1 is in control"
+  );
 
-  // Click Restore Settings again.
+  // Click manage again.
   popupHidden = promisePopupHidden(panel);
-  prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
+  // Ensures the preferences tab opens, checks the spotlight, and then closes it
+  spotlightShown = assertPreferencesShown("homeOverride");
   popupnotification.secondaryButton.click();
   await popupHidden;
+  await spotlightShown;
+
+  prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
+  await ext1.unload();
   await prefPromise;
 
-  await BrowserTestUtils.waitForLocationChange(gBrowser, defaultHomePage);
+  openHomepage = TestUtils.topicObserved("browser-open-homepage-start");
+  BrowserHome();
+  await openHomepage;
 
   is(getHomePageURL(), defaultHomePage, "The homepage is set back to default");
-
-  await ext1.unload();
-  await ext2.unload();
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(async function test_doorhanger_new_window() {
   // These extensions are temporarily loaded so that the AddonManager can see
   // them and the extension's shutdown handlers are called.
   let ext1Id = "ext1@mochi.test";
   let ext1 = ExtensionTestUtils.loadExtension({
     manifest: {
@@ -439,50 +482,63 @@ add_task(async function test_doorhanger_
   let doc = win.document;
   let panel = ExtensionControlledPopup._getAndMaybeCreatePanel(doc);
   await promisePopupShown(panel);
 
   let description = doc.getElementById(
     "extension-homepage-notification-description"
   );
 
-  ok(win.gURLBar.value.endsWith("ext2.html"), "ext2 is in control");
+  await TestUtils.waitForCondition(
+    () => gURLBar.value.endsWith("ext2.html"),
+    "ext2 is in control"
+  );
+
   is(
     description.textContent,
     "An extension,  Ext2, changed what you see when you open your homepage and new windows.Learn more",
     "The extension name is in the popup"
   );
 
-  // Click Restore Settings.
+  // Click Manage.
   let popupHidden = promisePopupHidden(panel);
-  let prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
   let popupnotification = doc.getElementById("extension-homepage-notification");
   popupnotification.secondaryButton.click();
+  await popupHidden;
+
+  let prefPromise = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
+  ext2.unload();
   await prefPromise;
-  await popupHidden;
 
   // Expect a new doorhanger for the next extension.
-  await promisePopupShown(panel);
+  let popupShown = promisePopupShown(panel);
+  await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+  let openHomepage = TestUtils.topicObserved("browser-open-homepage-start");
+  BrowserHome();
+  await openHomepage;
+  await popupShown;
+  await TestUtils.waitForCondition(
+    () => gURLBar.value.endsWith("ext1.html"),
+    "ext1 is in control"
+  );
 
-  ok(win.gURLBar.value.endsWith("ext1.html"), "ext1 is in control");
   is(
     description.textContent,
     "An extension,  Ext1, changed what you see when you open your homepage and new windows.Learn more",
     "The extension name is in the popup"
   );
 
   // Click Keep Changes.
   popupnotification.button.click();
   await TestUtils.waitForCondition(() => isConfirmed(ext1Id));
 
   ok(getHomePageURL().endsWith("ext1.html"), "The homepage is still the set");
-
   await BrowserTestUtils.closeWindow(win);
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
   await ext1.unload();
-  await ext2.unload();
 
   ok(!isConfirmed(ext1Id), "The confirmation is cleaned up on uninstall");
 });
 
 add_task(async function test_overriding_home_page_incognito_not_allowed() {
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.allowPrivateBrowsingByDefault", false]],
   });
@@ -599,21 +655,23 @@ add_task(async function test_overriding_
   });
 
   await extension.startup();
 
   // Verify a private window does not open the extension page.
   let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
   let win = OpenBrowserWindow({ private: true });
   await windowOpenedPromise;
+  let openHomepage = TestUtils.topicObserved("browser-open-homepage-start");
   win.BrowserHome();
   await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
+  await openHomepage;
 
   is(win.gURLBar.value, "", "home page not used in private window");
   is(
-    gBrowser.selectedBrowser.currentURI.spec,
+    win.gBrowser.selectedBrowser.currentURI.spec,
     "about:home",
     "home page not used in private window"
   );
 
   await extension.unload();
   await BrowserTestUtils.closeWindow(win);
 });
--- a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
+++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
@@ -29,17 +29,17 @@ function getNewTabDoorhanger() {
   ExtensionControlledPopup._getAndMaybeCreatePanel(document);
   return document.getElementById("extension-new-tab-notification");
 }
 
 function clickKeepChanges(notification) {
   notification.button.click();
 }
 
-function clickRestoreSettings(notification) {
+function clickManage(notification) {
   notification.secondaryButton.click();
 }
 
 function waitForNewTab() {
   let eventName = "browser-open-newtab-start";
   return new Promise(resolve => {
     function observer() {
       Services.obs.removeObserver(observer, eventName);
@@ -372,77 +372,51 @@ add_task(async function test_new_tab_res
     "The notification panel is open after opening New Tab"
   );
   is(
     getNotificationSetting(extensionId),
     null,
     "The New Tab notification is not set for this extension"
   );
 
-  // Click the Restore Changes button.
-  let addonDisabled = waitForAddonDisabled(addon);
+  // Click the Manage button.
+  let preferencesShown = TestUtils.waitForCondition(
+    () => gBrowser.currentURI.spec == "about:preferences#home",
+    "Should open about:preferences."
+  );
+  // BrowserTestUtils.removeTab(gBrowser.selectedTab);
   let popupHidden = promisePopupHidden(panel);
-  let locationChanged = BrowserTestUtils.waitForLocationChange(
-    gBrowser,
-    "about:newtab"
-  );
-  clickRestoreSettings(notification);
+  clickManage(notification);
   await popupHidden;
-  await addonDisabled;
-  await locationChanged;
+  await preferencesShown;
 
   // Ensure panel is closed, settings haven't changed and add-on is disabled.
   ok(
     panel.getAttribute("panelopen") != "true",
     "The notification panel is closed after click"
   );
+
   is(
     getNotificationSetting(extensionId),
     null,
-    "The New Tab notification is not set after restoring the settings"
+    "The New Tab notification is not set after clicking manage"
   );
-  is(addon.userDisabled, true, "The extension is now disabled");
-  is(
-    gBrowser.currentURI.spec,
-    "about:newtab",
-    "The user has been redirected to about:newtab"
-  );
-
-  // Wait for the next event tick to make sure the remaining part of the test
-  // is not executed inside tabbrowser's onLocationChange.
-  // See bug 1416153 for more details.
-  await TestUtils.waitForTick();
 
   // Reopen a browser tab and verify that there's no doorhanger.
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
   let newTabOpened = waitForNewTab();
   BrowserOpenTab();
   await newTabOpened;
 
   ok(
     panel.getAttribute("panelopen") != "true",
     "The notification panel is not opened after keeping the changes"
   );
 
-  // FIXME: We need to enable the add-on so it gets cleared from the
-  // ExtensionSettingsStore for now. See bug 1408226.
-  let addonEnabled = new Promise(resolve => {
-    let listener = {
-      onEnabled(enabledAddon) {
-        if (enabledAddon.id == addon.id) {
-          AddonManager.removeAddonListener(listener);
-          resolve();
-        }
-      },
-    };
-    AddonManager.addAddonListener(listener);
-  });
-  await addon.enable();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  await addonEnabled;
   await extension.unload();
 });
 
 add_task(async function test_new_tab_restore_settings_multiple() {
   await ExtensionSettingsStore.initialize();
   let notification = getNewTabDoorhanger();
   let panel = notification.closest("panel");
   let extensionOneId = "newtabrestoreone@mochi.test";
@@ -451,19 +425,16 @@ add_task(async function test_new_tab_res
       applications: { gecko: { id: extensionOneId } },
       chrome_url_overrides: { newtab: "restore-one.html" },
     },
     files: {
       "restore-one.html": `
         <h1 id="extension-new-tab">New Tab!</h1>
         <script src="newtab.js"></script>
       `,
-      "newtab.js": function() {
-        browser.test.sendMessage("from-newtab-page", window.location.href);
-      },
     },
     useAddonManager: "temporary",
   });
   let extensionTwoId = "newtabrestoretwo@mochi.test";
   let extensionTwo = ExtensionTestUtils.loadExtension({
     manifest: {
       applications: { gecko: { id: extensionTwoId } },
       chrome_url_overrides: { newtab: "restore-two.html" },
@@ -504,33 +475,46 @@ add_task(async function test_new_tab_res
     "The notification panel is open after opening New Tab"
   );
   is(
     getNotificationSetting(extensionTwoId),
     null,
     "The New Tab notification is not set for this extension"
   );
 
-  // Click the Restore Changes button.
-  let addonDisabled = waitForAddonDisabled(addonTwo);
+  // Click the Manage button.
   let popupHidden = promisePopupHidden(panel);
-  let newTabUrlPromise = extensionOne.awaitMessage("from-newtab-page");
-  clickRestoreSettings(notification);
+  let preferencesShown = TestUtils.waitForCondition(
+    () => gBrowser.currentURI.spec == "about:preferences#home",
+    "Should open about:preferences."
+  );
+  clickManage(notification);
   await popupHidden;
+  await preferencesShown;
+
+  // Disable the second addon then refresh the new tab expect to see a new addon dropdown.
+  let addonDisabled = waitForAddonDisabled(addonTwo);
+  addonTwo.disable();
   await addonDisabled;
-  await promisePopupShown(panel);
-  let newTabUrl = await newTabUrlPromise;
 
   // Ensure the panel opens again for the next add-on.
+  popupShown = promisePopupShown(panel);
+  let newtabShown = TestUtils.waitForCondition(
+    () => gBrowser.currentURI.spec == AboutNewTab.newTabURL,
+    "Should open correct new tab url."
+  );
+  BrowserOpenTab();
+  await newtabShown;
+  await popupShown;
+
   is(
     getNotificationSetting(extensionTwoId),
     null,
     "The New Tab notification is not set after restoring the settings"
   );
-  is(addonTwo.userDisabled, true, "The extension is now disabled");
   let addonOne = await AddonManager.getAddonByID(extensionOneId);
   is(
     addonOne.userDisabled,
     false,
     "The extension is enabled before making a choice"
   );
   is(
     getNotificationSetting(extensionOneId),
@@ -539,74 +523,76 @@ add_task(async function test_new_tab_res
   );
   is(
     panel.getAttribute("panelopen"),
     "true",
     "The notification panel is open after opening New Tab"
   );
   is(
     gBrowser.currentURI.spec,
-    newTabUrl,
+    AboutNewTab.newTabURL,
     "The user is now on the next extension's New Tab page"
   );
 
-  addonDisabled = waitForAddonDisabled(addonOne);
+  preferencesShown = TestUtils.waitForCondition(
+    () => gBrowser.currentURI.spec == "about:preferences#home",
+    "Should open about:preferences."
+  );
   popupHidden = promisePopupHidden(panel);
-  let locationChanged = BrowserTestUtils.waitForLocationChange(
-    gBrowser,
-    "about:newtab"
-  );
-  clickRestoreSettings(notification);
+  clickManage(notification);
   await popupHidden;
+  await preferencesShown;
+  // remove the extra preferences tab.
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  addonDisabled = waitForAddonDisabled(addonOne);
+  addonOne.disable();
   await addonDisabled;
-  await locationChanged;
+  let newTabOpened = waitForNewTab();
+  BrowserOpenTab();
+  await newTabOpened;
 
   ok(
     panel.getAttribute("panelopen") != "true",
     "The notification panel is closed after restoring the second time"
   );
   is(
     getNotificationSetting(extensionOneId),
     null,
     "The New Tab notification is not set after restoring the settings"
   );
-  is(addonOne.userDisabled, true, "The extension is now disabled");
   is(
     gBrowser.currentURI.spec,
     "about:newtab",
     "The user is now on the original New Tab URL since all extensions are disabled"
   );
 
-  // Wait for the next event tick to make sure the remaining part of the test
-  // is not executed inside tabbrowser's onLocationChange.
-  // See bug 1416153 for more details.
-  await TestUtils.waitForTick();
-
   // Reopen a browser tab and verify that there's no doorhanger.
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  let newTabOpened = waitForNewTab();
+  newTabOpened = waitForNewTab();
   BrowserOpenTab();
   await newTabOpened;
 
   ok(
     panel.getAttribute("panelopen") != "true",
     "The notification panel is not opened after keeping the changes"
   );
 
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-
   // FIXME: We need to enable the add-on so it gets cleared from the
   // ExtensionSettingsStore for now. See bug 1408226.
   let addonsEnabled = Promise.all([
     waitForAddonEnabled(addonOne),
     waitForAddonEnabled(addonTwo),
   ]);
   await addonOne.enable();
   await addonTwo.enable();
   await addonsEnabled;
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
   await extensionOne.unload();
   await extensionTwo.unload();
 });
 
 /**
  * Ensure we don't show the extension URL in the URL bar temporarily in new tabs
  * while we're switching remoteness (when the URL we're loading and the
  * default content principal are different).
--- a/browser/components/preferences/extensionControlled.js
+++ b/browser/components/preferences/extensionControlled.js
@@ -54,32 +54,28 @@ const API_PROXY_PREFS = [
   "network.proxy.socks_remote_dns",
   "network.proxy.no_proxies_on",
   "network.proxy.autoconfig_url",
   "signon.autologin.proxy",
 ];
 
 let extensionControlledContentIds = {
   "privacy.containers": "browserContainersExtensionContent",
-  homepage_override: "browserHomePageExtensionContent",
-  newTabURL: "browserNewTabExtensionContent",
   webNotificationsDisabled: "browserNotificationsPermissionExtensionContent",
   "services.passwordSavingEnabled": "passwordManagerExtensionContent",
   "proxy.settings": "proxyExtensionContent",
   get "websites.trackingProtectionMode"() {
     return {
       button: "contentBlockingDisableTrackingProtectionExtension",
       section: "contentBlockingTrackingProtectionExtensionContentLabel",
     };
   },
 };
 
 const extensionControlledL10nKeys = {
-  homepage_override: "homepage-override",
-  newTabURL: "new-tab-url",
   webNotificationsDisabled: "web-notifications",
   "services.passwordSavingEnabled": "password-saving",
   "privacy.containers": "privacy-containers",
   "websites.trackingProtectionMode": "websites-content-blocking-all-trackers",
   "proxy.settings": "proxy-config",
 };
 
 let extensionControlledIds = {};
--- a/browser/components/preferences/home.inc.xhtml
+++ b/browser/components/preferences/home.inc.xhtml
@@ -20,24 +20,20 @@
 </hbox>
 
 <groupbox id="homepageGroup"
           data-category="paneHome"
           hidden="true">
   <label><html:h2 data-l10n-id="home-new-windows-tabs-header"/></label>
   <description data-l10n-id="home-new-windows-tabs-description2" />
 
-  <hbox id="homepageAndNewWindowsOption">
+  <hbox id="homepageAndNewWindowsOption" align="center" data-subcategory="homeOverride">
     <label control="homeMode" data-l10n-id="home-homepage-mode-label" flex="1" />
 
     <vbox class="homepageMenuItemContainer" flex="1">
-      <html:input id="homePrefHidden"
-                  type="text"
-                  preference="browser.startup.homepage"
-                  hidden="true" />
       <menulist id="homeMode"
                 class="check-home-page-controlled"
                 data-preference-related="browser.startup.homepage">
         <menupopup>
           <menuitem value="0" data-l10n-id="home-mode-choice-default" />
           <menuitem value="2" data-l10n-id="home-mode-choice-custom" />
           <menuitem value="1" data-l10n-id="home-mode-choice-blank" />
         </menupopup>
@@ -76,39 +72,24 @@
                   class="homepage-button check-home-page-controlled"
                   data-l10n-id="choose-bookmark"
                   preference="pref.browser.homepage.disable_button.bookmark_page"
                   search-l10n-ids="select-bookmark-window.title, select-bookmark-desc"/>
         </hbox>
       </vbox>
     </vbox>
   </hbox>
-  <hbox id="browserHomePageExtensionContent"
-        align="center" hidden="true" class="extension-controlled">
-    <description control="disableHomePageExtension" flex="1" />
-    <button id="disableHomePageExtension"
-            is="highlightable-button"
-            class="extension-controlled-button accessory-button"
-            data-l10n-id="disable-extension" />
-  </hbox>
-
-  <hbox id="newTabsOption">
-    <label control="newTabMode" data-l10n-id="home-newtabs-mode-label" flex="1" />
+  <vbox data-subcategory="newtabOverride">
+    <hbox id="newTabsOption" align="center">
+      <label control="newTabMode" data-l10n-id="home-newtabs-mode-label" flex="1" />
 
-    <menulist id="newTabMode"
-                flex="1"
-                preference="browser.newtabpage.enabled">
-      <menupopup>
-        <menuitem value="0" data-l10n-id="home-mode-choice-default" />
-        <menuitem value="1" data-l10n-id="home-mode-choice-blank" />
-      </menupopup>
-    </menulist>
-  </hbox>
-  <hbox id="browserNewTabExtensionContent"
-        align="center" hidden="true" class="extension-controlled">
-    <description control="disableNewTabExtension" flex="1" />
-    <button id="disableNewTabExtension"
-            is="highlightable-button"
-            class="extension-controlled-button accessory-button"
-            data-l10n-id="disable-extension" />
-  </hbox>
+      <!-- This can be set to an extension value which is managed outside of
+        Preferences so we need to handle setting the pref manually.-->
+      <menulist id="newTabMode" flex="1" data-preference-related="browser.newtabpage.enabled">
+        <menupopup>
+          <menuitem value="0" data-l10n-id="home-mode-choice-default" />
+          <menuitem value="1" data-l10n-id="home-mode-choice-blank" />
+        </menupopup>
+      </menulist>
+    </hbox>
+  </vbox>
 </groupbox>
 </html:template>
--- a/browser/components/preferences/home.js
+++ b/browser/components/preferences/home.js
@@ -23,16 +23,21 @@
 Preferences.addAll([
   { id: "browser.startup.homepage", type: "wstring" },
   { id: "pref.browser.homepage.disable_button.current_page", type: "bool" },
   { id: "pref.browser.homepage.disable_button.bookmark_page", type: "bool" },
   { id: "pref.browser.homepage.disable_button.restore_default", type: "bool" },
   { id: "browser.newtabpage.enabled", type: "bool" },
 ]);
 
+XPCOMUtils.defineLazyModuleGetters(this, {
+  ExtensionPreferencesManager:
+    "resource://gre/modules/ExtensionPreferencesManager.jsm",
+});
+
 const HOMEPAGE_OVERRIDE_KEY = "homepage_override";
 const URL_OVERRIDES_TYPE = "url_overrides";
 const NEW_TAB_KEY = "newTabURL";
 
 var gHomePane = {
   HOME_MODE_FIREFOX_HOME: "0",
   HOME_MODE_BLANK: "1",
   HOME_MODE_CUSTOM: "2",
@@ -57,45 +62,213 @@ var gHomePane = {
       } catch (e) {
         console.error("Failed to parse Discovery Stream pref.");
       }
     }
 
     return false;
   },
 
-  /**
-   * _handleNewTabOverrides: disables new tab settings UI. Called by
-   * an observer in ._watchNewTab that watches for new tab url changes
-   */
-  async _handleNewTabOverrides() {
-    const isControlled = await handleControllingExtension(
-      URL_OVERRIDES_TYPE,
-      NEW_TAB_KEY
-    );
-    const el = document.getElementById("newTabMode");
-    el.disabled = isControlled;
+  async syncToNewTabPref() {
+    let menulist = document.getElementById("newTabMode");
+
+    if (["0", "1"].includes(menulist.value)) {
+      let newtabEnabledPref = Services.prefs.getBoolPref(
+        this.NEWTAB_ENABLED_PREF,
+        true
+      );
+      let newValue = menulist.value !== this.HOME_MODE_BLANK;
+      // Only set this if the pref has changed, otherwise the pref change will trigger other listeners to repeat.
+      if (newtabEnabledPref !== newValue) {
+        Services.prefs.setBoolPref(this.NEWTAB_ENABLED_PREF, newValue);
+      }
+      let selectedAddon = ExtensionSettingsStore.getSetting(
+        URL_OVERRIDES_TYPE,
+        NEW_TAB_KEY
+      );
+      if (selectedAddon) {
+        ExtensionSettingsStore.select(null, URL_OVERRIDES_TYPE, NEW_TAB_KEY);
+      }
+    } else {
+      let addon = await AddonManager.getAddonByID(menulist.value);
+      if (addon && addon.isActive) {
+        ExtensionSettingsStore.select(
+          addon.id,
+          URL_OVERRIDES_TYPE,
+          NEW_TAB_KEY
+        );
+      }
+    }
+  },
+
+  async syncFromNewTabPref() {
+    let menulist = document.getElementById("newTabMode");
+
+    // If the new tab url was changed to about:blank or about:newtab
+    if (
+      AboutNewTab.newTabURL === "about:newtab" ||
+      AboutNewTab.newTabURL === "about:blank"
+    ) {
+      let newtabEnabledPref = Services.prefs.getBoolPref(
+        this.NEWTAB_ENABLED_PREF,
+        true
+      );
+      let newValue = newtabEnabledPref
+        ? this.HOME_MODE_FIREFOX_HOME
+        : this.HOME_MODE_BLANK;
+      if (newValue !== menulist.value) {
+        menulist.value = newValue;
+      }
+      // If change was triggered by installing an addon we need to update
+      // the value of the menulist to be that addon.
+    } else {
+      let selectedAddon = ExtensionSettingsStore.getSetting(
+        URL_OVERRIDES_TYPE,
+        NEW_TAB_KEY
+      );
+      if (selectedAddon && menulist.value !== selectedAddon.id) {
+        menulist.value = selectedAddon.id;
+      }
+    }
   },
 
   /**
-   * watchNewTab: Listen for changes to the new tab url and disable appropriate
-   * areas of the UI
+   *  _updateMenuInterface: adds items to or removes them from the menulists
+   * @param {string} selectId Optional Id of the menulist to add or remove items from.
+   *                          If not included this will update both home and newtab menus.
+   */
+  async _updateMenuInterface(selectId) {
+    let selects;
+    if (selectId) {
+      selects = [document.getElementById(selectId)];
+    } else {
+      let newTabSelect = document.getElementById("newTabMode");
+      let homeSelect = document.getElementById("homeMode");
+      selects = [homeSelect, newTabSelect];
+    }
+
+    for (let select of selects) {
+      // Remove addons from the menu popup which are no longer installed, or disabled.
+      // let menuOptions = select.menupopup.childNodes;
+      let menuOptions = Array.from(select.menupopup.childNodes);
+
+      for (let option of menuOptions) {
+        // If the value is not a number, assume it is an addon ID
+        if (!/^\d+$/.test(option.value)) {
+          let addon = await AddonManager.getAddonByID(option.value);
+          if (option && (!addon || !addon.isActive)) {
+            option.remove();
+          }
+        }
+      }
+
+      let extensionOptions;
+      if (select.id === "homeMode") {
+        extensionOptions = await ExtensionSettingsStore.getAllSettings(
+          PREF_SETTING_TYPE,
+          HOMEPAGE_OVERRIDE_KEY
+        );
+      } else {
+        extensionOptions = await ExtensionSettingsStore.getAllSettings(
+          URL_OVERRIDES_TYPE,
+          NEW_TAB_KEY
+        );
+      }
+      let addons = await AddonManager.getAddonsByIDs(
+        extensionOptions.map(a => a.id)
+      );
+
+      // Add addon options to the menu popups
+      let menupopup = select.querySelector("menupopup");
+      for (let addon of addons) {
+        if (!addon || !addon.id || !addon.isActive) {
+          continue;
+        }
+        let currentOption = select.querySelector(
+          `[value="${CSS.escape(addon.id)}"]`
+        );
+        if (!currentOption) {
+          let option = document.createXULElement("menuitem");
+          option.classList.add("addon-with-favicon");
+          option.value = addon.id;
+          option.label = addon.name;
+          menupopup.append(option);
+          option.querySelector("image").src = addon.iconURL;
+        }
+        let setting = extensionOptions.find(o => o.id == addon.id);
+        if (
+          (select.id === "homeMode" && setting.value == HomePage.get()) ||
+          (select.id === "newTabMode" && setting.value == AboutNewTab.newTabURL)
+        ) {
+          select.value = addon.id;
+        }
+      }
+    }
+  },
+
+  /**
+   * watchNewTab: Listen for changes to the new tab url and enable/disable appropriate
+   * areas of the UI.
    */
   watchNewTab() {
-    this._handleNewTabOverrides();
-    let newTabObserver = {
-      observe: this._handleNewTabOverrides.bind(this),
+    let newTabObserver = () => {
+      this.syncFromNewTabPref();
+      this._updateMenuInterface("newTabMode");
     };
     Services.obs.addObserver(newTabObserver, "newtab-url-changed");
     window.addEventListener("unload", () => {
       Services.obs.removeObserver(newTabObserver, "newtab-url-changed");
     });
   },
 
   /**
+   * watchHomePrefChange: Listen for preferences changes on the Home Tab in order to
+   * show the appropriate home menu selection.
+   */
+  watchHomePrefChange() {
+    const homePrefObserver = (subject, topic, data) => {
+      // only update this UI if it is exactly the HOMEPAGE_PREF, not other prefs with the same root.
+      if (data && data != this.HOMEPAGE_PREF) {
+        return;
+      }
+      this._updateUseCurrentButton();
+      this._renderCustomSettings();
+      this._handleHomePageOverrides();
+      this._updateMenuInterface("homeMode");
+    };
+
+    Services.prefs.addObserver(this.HOMEPAGE_PREF, homePrefObserver);
+    window.addEventListener("unload", () => {
+      Services.prefs.removeObserver(this.HOMEPAGE_PREF, homePrefObserver);
+    });
+  },
+
+  /**
+   * Listen extension changes on the New Tab and Home Tab
+   * in order to update the UI and show or hide the Restore Defaults button.
+   */
+  watchExtensionPrefChange() {
+    const extensionSettingChanged = (evt, setting) => {
+      if (setting.key == "homepage_override" && setting.type == "prefs") {
+        this._updateMenuInterface("homeMode");
+      } else if (
+        setting.key == "newTabURL" &&
+        setting.type == "url_overrides"
+      ) {
+        this._updateMenuInterface("newTabMode");
+      }
+    };
+
+    Management.on("extension-setting-changed", extensionSettingChanged);
+    window.addEventListener("unload", () => {
+      Management.off("extension-setting-changed", extensionSettingChanged);
+    });
+  },
+
+  /**
    * Listen for all preferences changes on the Home Tab in order to show or
    * hide the Restore Defaults button.
    */
   watchHomeTabPrefChange() {
     const observer = () => this.toggleRestoreDefaultsBtn();
     Services.prefs.addObserver(this.ACTIVITY_STREAM_PREF_BRANCH, observer);
     Services.prefs.addObserver(this.HOMEPAGE_PREF, observer);
     Services.prefs.addObserver(this.NEWTAB_ENABLED_PREF, observer);
@@ -114,20 +287,22 @@ var gHomePane = {
    * @param {bool} options.shouldShow Should the custom UI be shown?
    * @param {bool} options.isControlled Is an extension controlling the home page?
    */
   _renderCustomSettings(options = {}) {
     let { shouldShow, isControlled } = options;
     const customSettingsContainerEl = document.getElementById("customSettings");
     const customUrlEl = document.getElementById("homePageUrl");
     const homePage = HomePage.get();
+    const isHomePageCustom =
+      (!this._isHomePageDefaultValue() &&
+        !this.isHomePageBlank() &&
+        !isControlled) ||
+      homePage.locked;
 
-    const isHomePageCustom =
-      isControlled ||
-      (!this._isHomePageDefaultValue() && !this.isHomePageBlank());
     if (typeof shouldShow === "undefined") {
       shouldShow = isHomePageCustom;
     }
     customSettingsContainerEl.hidden = !shouldShow;
 
     // We can't use isHomePageDefaultValue and isHomePageBlank here because we want to disregard the blank
     // possibility triggered by the browser.startup.page being 0.
     // We also skip when HomePage is locked because it might be locked to a default that isn't "about:home"
@@ -163,27 +338,16 @@ var gHomePane = {
    */
   isHomePageBlank() {
     const startupPref = Preferences.get("browser.startup.page");
     return (
       ["about:blank", ""].includes(HomePage.get()) ||
       startupPref.value === gMainPane.STARTUP_PREF_BLANK
     );
   },
-  /**
-   * isHomePageControlled
-   * @resolves {bool} Is the homepage being controlled by an extension?
-   * @returns {Promise}
-   */
-  isHomePageControlled() {
-    if (HomePage.locked) {
-      return Promise.resolve(false);
-    }
-    return handleControllingExtension(PREF_SETTING_TYPE, HOMEPAGE_OVERRIDE_KEY);
-  },
 
   /**
    * _isTabAboutPreferences: Is a given tab set to about:preferences?
    * @param {Element} aTab A tab element
    * @returns {bool} Is the linkedBrowser of aElement set to about:preferences?
    */
   _isTabAboutPreferences(aTab) {
     return aTab.linkedBrowser.currentURI.spec.startsWith("about:preferences");
@@ -207,24 +371,24 @@ var gHomePane = {
       tabs = tabs.filter(tab => !this._isTabAboutPreferences(tab));
       // XXX: Bug 1441637 - Fix tabbrowser to report tab.closing before it blurs it
       tabs = tabs.filter(tab => !tab.closing);
     }
 
     return tabs;
   },
 
-  _renderHomepageMode(isControlled) {
+  _renderHomepageMode(controllingExtension) {
     const isDefault = this._isHomePageDefaultValue();
     const isBlank = this.isHomePageBlank();
     const el = document.getElementById("homeMode");
     let newValue;
 
-    if (isControlled) {
-      newValue = this.HOME_MODE_CUSTOM;
+    if (controllingExtension && controllingExtension.id) {
+      newValue = controllingExtension.id;
     } else if (isDefault) {
       newValue = this.HOME_MODE_FIREFOX_HOME;
     } else if (isBlank) {
       newValue = this.HOME_MODE_BLANK;
     } else {
       newValue = this.HOME_MODE_CUSTOM;
     }
     if (el.value !== newValue) {
@@ -245,92 +409,118 @@ var gHomePane = {
           element.getAttribute("data-preference-related");
         if (!pref) {
           throw new Error(
             `Element with id ${element.id} did not have preference or data-preference-related attribute defined.`
           );
         }
 
         if (pref === this.HOMEPAGE_PREF) {
-          isDisabled = HomePage.locked || isControlled;
+          isDisabled = HomePage.locked;
         } else {
           isDisabled = Preferences.get(pref).locked || isControlled;
         }
 
         if (pref === "pref.browser.disable_button.current_page") {
           // Special case for current_page to disable it if tabCount is 0
           isDisabled = isDisabled || tabCount < 1;
         }
 
         element.disabled = isDisabled;
       });
   },
 
   async _handleHomePageOverrides() {
+    let controllingExtension;
     if (HomePage.locked) {
-      // An extension can't control these settings if they're locked.
-      hideControllingExtension(HOMEPAGE_OVERRIDE_KEY);
+      // Disable inputs if they are locked.
+      this._renderCustomSettings();
       this._setInputDisabledStates(false);
     } else {
-      const isControlled = await this.isHomePageControlled();
-      this._setInputDisabledStates(isControlled);
-      this._renderCustomSettings({ isControlled });
-      this._renderHomepageMode(isControlled);
+      if (HomePage.get().startsWith("moz-extension:")) {
+        controllingExtension = await getControllingExtension(
+          PREF_SETTING_TYPE,
+          HOMEPAGE_OVERRIDE_KEY
+        );
+      }
+      this._setInputDisabledStates();
+      this._renderCustomSettings({
+        isControlled: !!controllingExtension,
+      });
     }
-  },
-
-  syncFromHomePref() {
-    this._updateUseCurrentButton();
-    this._renderCustomSettings();
-    this._renderHomepageMode();
-    this._handleHomePageOverrides();
-  },
-
-  syncFromNewTabPref() {
-    const newtabPref = Preferences.get(this.NEWTAB_ENABLED_PREF);
-    return newtabPref.value
-      ? this.HOME_MODE_FIREFOX_HOME
-      : this.HOME_MODE_BLANK;
-  },
-
-  syncToNewTabPref(value) {
-    return value !== this.HOME_MODE_BLANK;
+    this._renderHomepageMode(controllingExtension);
   },
 
   onMenuChange(event) {
     const { value } = event.target;
     const startupPref = Preferences.get("browser.startup.page");
+    let selectedAddon = ExtensionSettingsStore.getSetting(
+      PREF_SETTING_TYPE,
+      HOMEPAGE_OVERRIDE_KEY
+    );
 
     switch (value) {
       case this.HOME_MODE_FIREFOX_HOME:
         if (startupPref.value === gMainPane.STARTUP_PREF_BLANK) {
           startupPref.value = gMainPane.STARTUP_PREF_HOMEPAGE;
         }
         if (!HomePage.isDefault) {
           HomePage.reset();
         } else {
           this._renderCustomSettings({ shouldShow: false });
         }
+        if (selectedAddon) {
+          ExtensionSettingsStore.select(
+            null,
+            PREF_SETTING_TYPE,
+            HOMEPAGE_OVERRIDE_KEY
+          );
+        }
         break;
       case this.HOME_MODE_BLANK:
         if (HomePage.get() !== "about:blank") {
           HomePage.safeSet("about:blank");
         } else {
           this._renderCustomSettings({ shouldShow: false });
         }
+        if (selectedAddon) {
+          ExtensionSettingsStore.select(
+            null,
+            PREF_SETTING_TYPE,
+            HOMEPAGE_OVERRIDE_KEY
+          );
+        }
         break;
       case this.HOME_MODE_CUSTOM:
         if (startupPref.value === gMainPane.STARTUP_PREF_BLANK) {
           Services.prefs.clearUserPref(startupPref.id);
         }
         if (HomePage.getDefault() != HomePage.getOriginalDefault()) {
           HomePage.clear();
         }
         this._renderCustomSettings({ shouldShow: true });
+        if (selectedAddon) {
+          ExtensionSettingsStore.select(
+            null,
+            PREF_SETTING_TYPE,
+            HOMEPAGE_OVERRIDE_KEY
+          );
+        }
         break;
+      // extensions will have a variety of values as their ID, so treat it as default
+      default:
+        AddonManager.getAddonByID(value).then(addon => {
+          if (addon && addon.isActive) {
+            ExtensionPreferencesManager.selectSetting(
+              addon.id,
+              HOMEPAGE_OVERRIDE_KEY
+            );
+          }
+          this._renderCustomSettings({ shouldShow: false });
+        });
     }
   },
 
   /**
    * Switches the "Use Current Page" button between its singular and plural
    * forms.
    */
   async _updateUseCurrentButton() {
@@ -401,17 +591,19 @@ var gHomePane = {
       },
       rv
     );
     Services.telemetry.scalarAdd("preferences.use_bookmark", 1);
   },
 
   restoreDefaultHomePage() {
     HomePage.reset();
+    this._handleHomePageOverrides();
     Services.prefs.clearUserPref(this.NEWTAB_ENABLED_PREF);
+    AboutNewTab.resetNewTabURL();
   },
 
   onCustomHomePageInput(event) {
     if (this._telemetryHomePageTimer) {
       clearTimeout(this._telemetryHomePageTimer);
     }
     let browserHomePage = event.target.value;
     // The length of the home page URL string should be more then four,
@@ -438,18 +630,27 @@ var gHomePane = {
    * Check all Home Tab preferences for user set values.
    */
   _changedHomeTabDefaultPrefs() {
     // If Discovery Stream is enabled Firefox Home Content preference options are hidden
     const homeContentChanged =
       !this.isPocketNewtabEnabled &&
       this.homePanePrefs.some(pref => pref.hasUserValue);
     const newtabPref = Preferences.get(this.NEWTAB_ENABLED_PREF);
+    const extensionControlled = Preferences.get(
+      "browser.startup.homepage_override.extensionControlled"
+    );
 
-    return homeContentChanged || HomePage.overridden || newtabPref.hasUserValue;
+    return (
+      homeContentChanged ||
+      HomePage.overridden ||
+      newtabPref.hasUserValue ||
+      AboutNewTab.newTabURLOverridden ||
+      extensionControlled
+    );
   },
 
   /**
    * Show the Restore Defaults button if any preference on the Home tab was
    * changed, or hide it otherwise.
    */
   toggleRestoreDefaultsBtn() {
     const btn = document.getElementById("restoreDefaultHomePageBtn");
@@ -466,65 +667,46 @@ var gHomePane = {
     if (!this.isPocketNewtabEnabled) {
       this.homePanePrefs.forEach(pref => Services.prefs.clearUserPref(pref.id));
     }
   },
 
   init() {
     // Event Listeners
     document
-      .getElementById("homeMode")
-      .addEventListener("command", this.onMenuChange.bind(this));
-    document
       .getElementById("homePageUrl")
       .addEventListener("change", this.onCustomHomePageChange.bind(this));
     document
       .getElementById("homePageUrl")
       .addEventListener("input", this.onCustomHomePageInput.bind(this));
     document
       .getElementById("useCurrentBtn")
       .addEventListener("command", this.setHomePageToCurrent.bind(this));
     document
       .getElementById("useBookmarkBtn")
       .addEventListener("command", this.setHomePageToBookmark.bind(this));
     document
       .getElementById("restoreDefaultHomePageBtn")
       .addEventListener("command", this.restoreDefaultPrefsForHome.bind(this));
 
-    Preferences.addSyncFromPrefListener(
-      document.getElementById("homePrefHidden"),
-      () => this.syncFromHomePref()
-    );
-    Preferences.addSyncFromPrefListener(
-      document.getElementById("newTabMode"),
-      () => this.syncFromNewTabPref()
-    );
-    Preferences.addSyncToPrefListener(
-      document.getElementById("newTabMode"),
-      element => this.syncToNewTabPref(element.value)
-    );
+    // Setup the add-on options for the new tab section before registering the
+    // listener.
+    this._updateMenuInterface();
+    document
+      .getElementById("newTabMode")
+      .addEventListener("command", this.syncToNewTabPref.bind(this));
+    document
+      .getElementById("homeMode")
+      .addEventListener("command", this.onMenuChange.bind(this));
 
     this._updateUseCurrentButton();
+    this._handleHomePageOverrides();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this));
 
     // Extension/override-related events
     this.watchNewTab();
-    document
-      .getElementById("disableHomePageExtension")
-      .addEventListener(
-        "command",
-        makeDisableControllingExtension(
-          PREF_SETTING_TYPE,
-          HOMEPAGE_OVERRIDE_KEY
-        )
-      );
-    document
-      .getElementById("disableNewTabExtension")
-      .addEventListener(
-        "command",
-        makeDisableControllingExtension(URL_OVERRIDES_TYPE, NEW_TAB_KEY)
-      );
-
+    this.watchHomePrefChange();
+    this.watchExtensionPrefChange();
     this.watchHomeTabPrefChange();
     // Notify observers that the UI is now ready
     Services.obs.notifyObservers(window, "home-pane-loaded");
   },
 };
--- a/browser/components/preferences/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/tests/browser_extension_controlled.js
@@ -1,11 +1,16 @@
 /* eslint-env webextensions */
 
 const PROXY_PREF = "network.proxy.type";
+const HOMEPAGE_URL_PREF = "browser.startup.homepage";
+const HOMEPAGE_OVERRIDE_KEY = "homepage_override";
+const URL_OVERRIDES_TYPE = "url_overrides";
+const NEW_TAB_KEY = "newTabURL";
+const PREF_SETTING_TYPE = "prefs";
 
 ChromeUtils.defineModuleGetter(
   this,
   "ExtensionSettingsStore",
   "resource://gre/modules/ExtensionSettingsStore.jsm"
 );
 
 ChromeUtils.defineModuleGetter(
@@ -16,49 +21,35 @@ ChromeUtils.defineModuleGetter(
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
 
 const { AddonTestUtils } = ChromeUtils.import(
   "resource://testing-common/AddonTestUtils.jsm"
 );
 AddonTestUtils.initMochitest(this);
 
+const { ExtensionPreferencesManager } = ChromeUtils.import(
+  "resource://gre/modules/ExtensionPreferencesManager.jsm"
+);
+
 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 const CHROME_URL_ROOT = TEST_DIR + "/";
 const PERMISSIONS_URL =
   "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml";
 let sitePermissionsDialog;
 
 function getSupportsFile(path) {
   let cr = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
     Ci.nsIChromeRegistry
   );
   let uri = Services.io.newURI(CHROME_URL_ROOT + path);
   let fileurl = cr.convertChromeURL(uri);
   return fileurl.QueryInterface(Ci.nsIFileURL);
 }
 
-async function installAddon(xpiName) {
-  let filePath = getSupportsFile(`addons/${xpiName}`).file;
-  let install = await AddonManager.getInstallForFile(filePath);
-  if (!install) {
-    throw new Error(`An install was not created for ${filePath}`);
-  }
-  return new Promise((resolve, reject) => {
-    install.addListener({
-      onDownloadFailed: reject,
-      onDownloadCancelled: reject,
-      onInstallFailed: reject,
-      onInstallCancelled: reject,
-      onInstallEnded: resolve,
-    });
-    install.install();
-  });
-}
-
 function waitForMessageChange(
   element,
   cb,
   opts = { attributes: true, attributeFilter: ["hidden"] }
 ) {
   return waitForMutation(element, opts, cb);
 }
 
@@ -131,99 +122,219 @@ async function disableExtensionViaClick(
 
 async function reEnableExtension(addon, labelId) {
   let controlledMessageShown = waitForMessageShown(labelId);
   await addon.enable();
   await controlledMessageShown;
 }
 
 add_task(async function testExtensionControlledHomepage() {
+  const ADDON_ID = "@set_homepage";
+  const SECOND_ADDON_ID = "@second_set_homepage";
+
   await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
-  let doc = gBrowser.contentDocument;
+  let homepagePref = () => Services.prefs.getCharPref(HOMEPAGE_URL_PREF);
+  let originalHomepagePref = homepagePref();
   is(
     gBrowser.currentURI.spec,
     "about:preferences#home",
     "#home should be in the URI for about:preferences"
   );
-  let homepagePref = () =>
-    Services.prefs.getCharPref("browser.startup.homepage");
-  let originalHomepagePref = homepagePref();
-  let extensionHomepage = "https://developer.mozilla.org/";
-  let controlledContent = doc.getElementById("browserHomePageExtensionContent");
-
+  let doc = gBrowser.contentDocument;
   let homeModeEl = doc.getElementById("homeMode");
   let customSettingsSection = doc.getElementById("customSettings");
 
+  is(homeModeEl.itemCount, 3, "The menu list starts with 3 options");
+
+  let promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount === 4,
+    "wait for the addon option to be added as an option in the menu list"
+  );
+  let extension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "permanent",
+    manifest: {
+      version: "1.0",
+      name: "set_homepage",
+      applications: {
+        gecko: {
+          id: ADDON_ID,
+        },
+      },
+      chrome_settings_overrides: { homepage: "/home.html" },
+    },
+  });
+  await extension.startup();
+  await promise;
+
   // The homepage is set to the default and the custom settings section is hidden
-  ok(originalHomepagePref != extensionHomepage, "homepage is empty by default");
   is(homeModeEl.disabled, false, "The homepage menulist is enabled");
   is(
     customSettingsSection.hidden,
     true,
     "The custom settings element is hidden"
   );
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
+
+  let addon = await AddonManager.getAddonByID(ADDON_ID);
+  is(
+    homeModeEl.value,
+    addon.id,
+    "the home select menu's value is set to the addon"
+  );
+
+  promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
+  // Set the Menu to the default value
+  homeModeEl.value = "0";
+  homeModeEl.dispatchEvent(new Event("command"));
+  await promise;
+  is(homepagePref(), originalHomepagePref, "homepage is set back to default");
+  let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    addon.id,
+    HOMEPAGE_OVERRIDE_KEY,
+    PREF_SETTING_TYPE
+  );
+  is(
+    levelOfControl,
+    "not_controllable",
+    "getLevelOfControl returns not_controllable."
+  );
+  let setting = await ExtensionPreferencesManager.getSetting(
+    HOMEPAGE_OVERRIDE_KEY
+  );
+  ok(!setting.value, "the setting is not set.");
 
-  // Install an extension that will set the homepage.
-  let promise = waitForMessageShown("browserHomePageExtensionContent");
-  await installAddon("set_homepage.xpi");
+  promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
+  // Set the menu to the addon value
+  homeModeEl.value = ADDON_ID;
+  homeModeEl.dispatchEvent(new Event("command"));
+  await promise;
+  ok(
+    homepagePref().startsWith("moz-extension") &&
+      homepagePref().endsWith("home.html"),
+    "Home url should be provided by the extension."
+  );
+  levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    addon.id,
+    HOMEPAGE_OVERRIDE_KEY,
+    PREF_SETTING_TYPE
+  );
+  is(
+    levelOfControl,
+    "controlled_by_this_extension",
+    "getLevelOfControl returns controlled_by_this_extension."
+  );
+  setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
+  ok(
+    setting.value.startsWith("moz-extension") &&
+      setting.value.endsWith("home.html"),
+    "The setting value is the same as the extension."
+  );
+
+  // Add a second extension, ensure it is added to the menulist and selected.
+  promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount == 5,
+    "addon option is added as an option in the menu list"
+  );
+  let secondExtension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "permanent",
+    manifest: {
+      version: "1.0",
+      name: "second_set_homepage",
+      applications: {
+        gecko: {
+          id: SECOND_ADDON_ID,
+        },
+      },
+      chrome_settings_overrides: { homepage: "/home2.html" },
+    },
+  });
+  await secondExtension.startup();
   await promise;
 
-  // The homepage has been set by the extension, the user is notified and it isn't editable.
-  let controlledLabel = controlledContent.querySelector("description");
-  is(homepagePref(), extensionHomepage, "homepage is set by extension");
-  Assert.deepEqual(
-    doc.l10n.getAttributes(controlledLabel),
-    {
-      id: "extension-controlled-homepage-override",
-      args: {
-        name: "set_homepage",
-      },
-    },
-    "The user is notified that an extension is controlling the homepage"
+  let secondAddon = await AddonManager.getAddonByID(SECOND_ADDON_ID);
+  is(homeModeEl.value, SECOND_ADDON_ID, "home menulist is set to the add-on");
+
+  levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    secondAddon.id,
+    HOMEPAGE_OVERRIDE_KEY,
+    PREF_SETTING_TYPE
   );
-  is(controlledContent.hidden, false, "The extension controlled row is hidden");
-  is(homeModeEl.disabled, true, "The homepage input is disabled");
-
-  // Disable the extension.
-  let enableMessageShown = waitForEnableMessage(controlledContent.id);
-  doc.getElementById("disableHomePageExtension").click();
-  await enableMessageShown;
-
-  // The user is notified how to enable the extension.
   is(
-    doc.l10n.getAttributes(controlledLabel.querySelector("label")).id,
-    "extension-controlled-enable",
-    "The user is notified of how to enable the extension again"
+    levelOfControl,
+    "controlled_by_this_extension",
+    "getLevelOfControl returns controlled_by_this_extension."
+  );
+  setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
+  ok(
+    setting.value.startsWith("moz-extension") &&
+      setting.value.endsWith("home2.html"),
+    "The setting value is the same as the extension."
   );
 
-  // The user can dismiss the enable instructions.
-  let hidden = waitForMessageHidden("browserHomePageExtensionContent");
-  controlledLabel.querySelector("image:last-of-type").click();
-  await hidden;
+  promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount == 4,
+    "addon option is no longer an option in the menu list after disable, even if it was not selected"
+  );
+  await addon.disable();
+  await promise;
+
+  // Ensure that re-enabling an addon adds it back to the menulist
+  promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount == 5,
+    "addon option is added again to the menulist when enabled"
+  );
+  await addon.enable();
+  await promise;
+
+  promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount == 4,
+    "addon option is no longer an option in the menu list after disable"
+  );
+  await secondAddon.disable();
+  await promise;
+
+  promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount == 5,
+    "addon option is added again to the menulist when enabled"
+  );
+  await secondAddon.enable();
+  await promise;
+
+  promise = TestUtils.waitForCondition(
+    () => homeModeEl.itemCount == 3,
+    "addon options are no longer an option in the menu list after disabling all addons"
+  );
+  await secondAddon.disable();
+  await addon.disable();
+  await promise;
+
+  is(homeModeEl.value, "0", "addon option is not selected in the menu list");
+
+  levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    secondAddon.id,
+    HOMEPAGE_OVERRIDE_KEY,
+    PREF_SETTING_TYPE
+  );
+  is(
+    levelOfControl,
+    "controllable_by_this_extension",
+    "getLevelOfControl returns controllable_by_this_extension."
+  );
+  setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
+  ok(!setting.value, "The setting value is back to default.");
 
   // The homepage elements are reset to their original state.
   is(homepagePref(), originalHomepagePref, "homepage is set back to default");
   is(homeModeEl.disabled, false, "The homepage menulist is enabled");
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
-
-  // Cleanup the add-on and tab.
-  let addon = await AddonManager.getAddonByID("@set_homepage");
-  // Enable the extension so we get the UNINSTALL event, which is needed by
-  // ExtensionPreferencesManager to clean up properly.
-  // FIXME: See https://bugzilla.mozilla.org/show_bug.cgi?id=1408226.
-  promise = waitForMessageShown("browserHomePageExtensionContent");
-  await addon.enable();
-  await promise;
-  // Do the uninstall now that the enable code has been run.
-  await addon.uninstall();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  await extension.unload();
+  await secondExtension.unload();
 });
 
 add_task(async function testPrefLockedHomepage() {
+  const ADDON_ID = "@set_homepage";
   await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
   let doc = gBrowser.contentDocument;
   is(
     gBrowser.currentURI.spec,
     "about:preferences#home",
     "#home should be in the URI for about:preferences"
   );
 
@@ -232,17 +343,16 @@ add_task(async function testPrefLockedHo
     "pref.browser.homepage.disable_button.current_page",
     "pref.browser.homepage.disable_button.bookmark_page",
     "pref.browser.homepage.disable_button.restore_default",
   ];
   let homeModeEl = doc.getElementById("homeMode");
   let homePageInput = doc.getElementById("homePageUrl");
   let prefs = Services.prefs.getDefaultBranch(null);
   let mutationOpts = { attributes: true, attributeFilter: ["disabled"] };
-  let controlledContent = doc.getElementById("browserHomePageExtensionContent");
 
   // Helper functions.
   let getButton = pref =>
     doc.querySelector(`.homepage-button[preference="${pref}"`);
   let waitForAllMutations = () =>
     Promise.all(
       buttonPrefs
         .map(pref => waitForMutation(getButton(pref), mutationOpts))
@@ -272,133 +382,146 @@ add_task(async function testPrefLockedHo
       prefs.unlockPref(pref);
       prefs.setBoolPref(pref, false);
     });
     // Do the homepage last since that's the only pref that triggers a UI update.
     prefs.unlockPref(homePagePref);
     prefs.setCharPref(homePagePref, originalHomepage);
   };
 
+  // Lock or unlock prefs then wait for all mutations to finish.
+  // Expects a bool indicating if we should lock or unlock.
+  let waitForLockMutations = lock => {
+    let mutationsDone = waitForAllMutations();
+    if (lock) {
+      lockPrefs();
+    } else {
+      unlockPrefs();
+    }
+    return mutationsDone;
+  };
+
   ok(
     originalHomepage != extensionHomepage,
     "The extension will change the homepage"
   );
 
   // Install an extension that sets the homepage to MDN.
-  let promise = waitForMessageShown(controlledContent.id);
-  await installAddon("set_homepage.xpi");
+  let promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
+  let extension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "permanent",
+    manifest: {
+      version: "1.0",
+      name: "set_homepage",
+      applications: {
+        gecko: {
+          id: ADDON_ID,
+        },
+      },
+      chrome_settings_overrides: { homepage: "https://developer.mozilla.org/" },
+    },
+  });
+  await extension.startup();
   await promise;
 
   // Check that everything is still disabled, homepage didn't change.
   is(
     getHomepage(),
     extensionHomepage,
     "The reported homepage is set by the extension"
   );
   is(
     homePageInput.value,
     extensionHomepage,
     "The homepage is set by the extension"
   );
-  is(
-    homePageInput.disabled,
-    true,
-    "Homepage custom input is disabled when set by extension"
-  );
-  is(
-    homeModeEl.disabled,
-    true,
-    "Homepage menulist is disabled when set by extension"
-  );
-  buttonPrefs.forEach(pref => {
-    is(
-      getButton(pref).disabled,
-      true,
-      `${pref} is disabled when set by extension`
-    );
-  });
-  is(
-    controlledContent.hidden,
-    false,
-    "The extension controlled message is shown"
-  );
 
   // Lock all of the prefs, wait for the UI to update.
-  let messageHidden = waitForMessageHidden(controlledContent.id);
-
-  lockPrefs();
-  await messageHidden;
+  await waitForLockMutations(true);
 
   // Check that everything is now disabled.
   is(getHomepage(), lockedHomepage, "The reported homepage is set by the pref");
   is(homePageInput.value, lockedHomepage, "The homepage is set by the pref");
   is(
     homePageInput.disabled,
     true,
     "The homepage is disabed when the pref is locked"
   );
-  is(
-    homeModeEl.disabled,
-    true,
-    "Homepage menulist is disabled when the pref is locked"
-  );
 
   buttonPrefs.forEach(pref => {
     is(
       getButton(pref).disabled,
       true,
       `The ${pref} button is disabled when locked`
     );
   });
+
+  let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    ADDON_ID,
+    HOMEPAGE_OVERRIDE_KEY,
+    PREF_SETTING_TYPE
+  );
   is(
-    controlledContent.hidden,
-    true,
-    "The extension controlled message is hidden when locked"
+    levelOfControl,
+    "not_controllable",
+    "getLevelOfControl returns not_controllable, the pref is locked."
   );
 
+  // Verify that the UI is selecting the extension's Id in the menulist
+  let unlockedPromise = TestUtils.waitForCondition(
+    () => homeModeEl.value == ADDON_ID,
+    "Homepage menulist value is equal to the extension ID"
+  );
   // Unlock the prefs, wait for the UI to update.
-  let messageShown = waitForMessageShown(controlledContent.id);
   unlockPrefs();
-  await messageShown;
+  await unlockedPromise;
 
-  // Verify that the UI is showing the extension's settings.
   is(
-    homePageInput.value,
-    extensionHomepage,
-    "The homepage is set by the extension"
+    homeModeEl.disabled,
+    false,
+    "the home select element is not disabled when the pref is not locked"
   );
   is(
     homePageInput.disabled,
-    true,
-    "Homepage is disabled when set by extension"
+    false,
+    "The homepage is enabled when the pref is unlocked"
   );
   is(
-    homeModeEl.disabled,
-    true,
-    "Homepage menulist is disabled when set by extension"
+    getHomepage(),
+    extensionHomepage,
+    "The homepage is reset to extension page"
+  );
+
+  levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    ADDON_ID,
+    HOMEPAGE_OVERRIDE_KEY,
+    PREF_SETTING_TYPE
   );
-  buttonPrefs.forEach(pref => {
-    is(
-      getButton(pref).disabled,
-      true,
-      `${pref} is disabled when set by extension`
-    );
-  });
   is(
-    controlledContent.hidden,
-    false,
-    "The extension controlled message is shown when unlocked"
+    levelOfControl,
+    "controlled_by_this_extension",
+    "getLevelOfControl returns controlled_by_this_extension after prefs are unlocked."
+  );
+  let setting = await ExtensionPreferencesManager.getSetting(
+    HOMEPAGE_OVERRIDE_KEY
+  );
+  is(
+    setting.value,
+    extensionHomepage,
+    "The setting value is equal to the extensionHomepage."
   );
 
   // Uninstall the add-on.
-  let addon = await AddonManager.getAddonByID("@set_homepage");
-  promise = waitForEnableMessage(controlledContent.id);
-  await addon.uninstall();
+  promise = TestUtils.waitForPrefChange(HOMEPAGE_URL_PREF);
+  await extension.unload();
   await promise;
 
+  setting = await ExtensionPreferencesManager.getSetting(HOMEPAGE_OVERRIDE_KEY);
+  ok(!setting, "The setting is gone after the addon is uninstalled.");
+
   // Check that everything is now enabled again.
   is(
     getHomepage(),
     originalHomepage,
     "The reported homepage is reset to original value"
   );
   is(homePageInput.value, "", "The homepage is empty");
   is(
@@ -415,44 +538,41 @@ add_task(async function testPrefLockedHo
     is(
       getButton(pref).disabled,
       false,
       `The ${pref} button is enabled when unlocked`
     );
   });
 
   // Lock the prefs without an extension.
-  let mutationsDone = waitForAllMutations();
-  lockPrefs();
-  await mutationsDone;
+  await waitForLockMutations(true);
 
   // Check that everything is now disabled.
   is(getHomepage(), lockedHomepage, "The reported homepage is set by the pref");
   is(homePageInput.value, lockedHomepage, "The homepage is set by the pref");
   is(
     homePageInput.disabled,
     true,
     "The homepage is disabed when the pref is locked"
   );
   is(
     homeModeEl.disabled,
     true,
-    "Homepage menulist is disabled when prefis locked"
+    "Homepage menulist is disabled when pref is locked"
   );
   buttonPrefs.forEach(pref => {
     is(
       getButton(pref).disabled,
       true,
       `The ${pref} button is disabled when locked`
     );
   });
 
   // Unlock the prefs without an extension.
-  unlockPrefs();
-  await waitForAllMutations();
+  await waitForLockMutations(false);
 
   // Check that everything is enabled again.
   is(
     getHomepage(),
     originalHomepage,
     "The homepage is reset to the original value"
   );
   is(homePageInput.value, "", "The homepage is clear after being unlocked");
@@ -468,91 +588,202 @@ add_task(async function testPrefLockedHo
   );
   buttonPrefs.forEach(pref => {
     is(
       getButton(pref).disabled,
       false,
       `The ${pref} button is enabled when unlocked`
     );
   });
-  is(
-    controlledContent.hidden,
-    true,
-    "The extension controlled message is hidden when unlocked with no extension"
-  );
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(async function testExtensionControlledNewTab() {
+  const ADDON_ID = "@set_newtab";
+  const SECOND_ADDON_ID = "@second_set_newtab";
+  const DEFAULT_NEWTAB = "about:newtab";
+  const NEWTAB_CONTROLLED_PREF = "browser.newtab.extensionControlled";
+
   await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
-  let doc = gBrowser.contentDocument;
   is(
     gBrowser.currentURI.spec,
     "about:preferences#home",
     "#home should be in the URI for about:preferences"
   );
 
-  let controlledContent = doc.getElementById("browserNewTabExtensionContent");
+  let doc = gBrowser.contentDocument;
+  let newTabMenuList = doc.getElementById("newTabMode");
+  // The new tab page is set to the default.
+  is(AboutNewTab.newTabURL, DEFAULT_NEWTAB, "new tab is set to default");
+
+  let promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 3,
+    "addon option is added as an option in the menu list"
+  );
+  // Install an extension that will set the new tab page.
+  let extension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "permanent",
+    manifest: {
+      version: "1.0",
+      name: "set_newtab",
+      applications: {
+        gecko: {
+          id: ADDON_ID,
+        },
+      },
+      chrome_url_overrides: { newtab: "/newtab.html" },
+    },
+  });
+  await extension.startup();
+
+  await promise;
+  let addon = await AddonManager.getAddonByID(ADDON_ID);
+
+  is(newTabMenuList.value, ADDON_ID, "New tab menulist is set to the add-on");
+
+  let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    addon.id,
+    NEW_TAB_KEY,
+    URL_OVERRIDES_TYPE
+  );
+  is(
+    levelOfControl,
+    "controlled_by_this_extension",
+    "getLevelOfControl returns controlled_by_this_extension."
+  );
+  let setting = ExtensionSettingsStore.getSetting(
+    URL_OVERRIDES_TYPE,
+    NEW_TAB_KEY
+  );
+  ok(
+    setting.value.startsWith("moz-extension") &&
+      setting.value.endsWith("newtab.html"),
+    "The url_overrides is set by this extension"
+  );
+
+  promise = TestUtils.waitForPrefChange(NEWTAB_CONTROLLED_PREF);
+  // Set the menu to the default value
+  newTabMenuList.value = "0";
+  newTabMenuList.dispatchEvent(new Event("command"));
+  await promise;
+  let newTabPref = Services.prefs.getBoolPref(NEWTAB_CONTROLLED_PREF, false);
+  is(newTabPref, false, "the new tab is not controlled");
 
-  // The new tab is set to the default and message is hidden.
-  ok(!AboutNewTab.newTabURL.startsWith("moz-extension:"), "new tab is not set");
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
+  levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    addon.id,
+    NEW_TAB_KEY,
+    URL_OVERRIDES_TYPE
+  );
+  is(
+    levelOfControl,
+    "not_controllable",
+    "getLevelOfControl returns not_controllable."
+  );
+  setting = ExtensionSettingsStore.getSetting(URL_OVERRIDES_TYPE, NEW_TAB_KEY);
+  ok(!setting.value, "The url_overrides is not set by this extension");
+
+  promise = TestUtils.waitForPrefChange(NEWTAB_CONTROLLED_PREF);
+  // Set the menu to a the addon value
+  newTabMenuList.value = ADDON_ID;
+  newTabMenuList.dispatchEvent(new Event("command"));
+  await promise;
+  newTabPref = Services.prefs.getBoolPref(NEWTAB_CONTROLLED_PREF, false);
+  is(newTabPref, true, "the new tab is controlled");
 
-  // Install an extension that will set the new tab page.
-  let promise = waitForMessageShown("browserNewTabExtensionContent");
-  await installAddon("set_newtab.xpi");
+  // Add a second extension, ensure it is added to the menulist and selected.
+  promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 4,
+    "addon option is added as an option in the menu list"
+  );
+  let secondExtension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "permanent",
+    manifest: {
+      version: "1.0",
+      name: "second_set_newtab",
+      applications: {
+        gecko: {
+          id: SECOND_ADDON_ID,
+        },
+      },
+      chrome_url_overrides: { newtab: "/newtab2.html" },
+    },
+  });
+  await secondExtension.startup();
+  await promise;
+  let secondAddon = await AddonManager.getAddonByID(SECOND_ADDON_ID);
+  is(
+    newTabMenuList.value,
+    SECOND_ADDON_ID,
+    "New tab menulist is set to the add-on"
+  );
+
+  levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+    secondAddon.id,
+    NEW_TAB_KEY,
+    URL_OVERRIDES_TYPE
+  );
+  is(
+    levelOfControl,
+    "controlled_by_this_extension",
+    "getLevelOfControl returns controlled_by_this_extension."
+  );
+  setting = ExtensionSettingsStore.getSetting(URL_OVERRIDES_TYPE, NEW_TAB_KEY);
+  ok(
+    setting.value.startsWith("moz-extension") &&
+      setting.value.endsWith("newtab2.html"),
+    "The url_overrides is set by the second extension"
+  );
+
+  promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 3,
+    "addon option is no longer an option in the menu list after disable, even if it was not selected"
+  );
+  await addon.disable();
   await promise;
 
-  // The new tab page has been set by the extension and the user is notified.
-  let controlledLabel = controlledContent.querySelector("description");
-  ok(
-    AboutNewTab.newTabURL.startsWith("moz-extension:"),
-    "new tab url is set by extension"
+  // Ensure that re-enabling an addon adds it back to the menulist
+  promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 4,
+    "addon option is added again to the menulist when enabled"
+  );
+  await addon.enable();
+  await promise;
+
+  promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 3,
+    "addon option is no longer an option in the menu list after disable"
   );
-  Assert.deepEqual(
-    doc.l10n.getAttributes(controlledLabel),
-    {
-      id: "extension-controlled-new-tab-url",
-      args: {
-        name: "set_newtab",
-      },
-    },
-    "The user is notified that an extension is controlling the new tab page"
+  await secondAddon.disable();
+  await promise;
+
+  promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 4,
+    "addon option is added again to the menulist when enabled"
   );
-  is(controlledContent.hidden, false, "The extension controlled row is hidden");
-
-  // Disable the extension.
-  doc.getElementById("disableNewTabExtension").click();
+  await secondAddon.enable();
+  await promise;
 
-  // Verify the user is notified how to enable the extension.
-  await waitForEnableMessage(controlledContent.id);
+  promise = TestUtils.waitForCondition(
+    () => newTabMenuList.itemCount == 2,
+    "addon options are all removed after disabling all"
+  );
+  await addon.disable();
+  await secondAddon.disable();
+  await promise;
   is(
-    doc.l10n.getAttributes(controlledLabel.querySelector("label")).id,
-    "extension-controlled-enable",
-    "The user is notified of how to enable the extension again"
+    AboutNewTab.newTabURL,
+    DEFAULT_NEWTAB,
+    "new tab page is set back to default"
   );
 
-  // Verify the enable message can be dismissed.
-  let hidden = waitForMessageHidden(controlledContent.id);
-  let dismissButton = controlledLabel.querySelector("image:last-of-type");
-  dismissButton.click();
-  await hidden;
-
-  // Ensure the New Tab page has been reset and there is no message.
-  ok(
-    !AboutNewTab.newTabURL.startsWith("moz-extension:"),
-    "new tab page is set back to default"
-  );
-  is(controlledContent.hidden, true, "The extension controlled row is shown");
-
-  // Cleanup the tab and add-on.
+  // Cleanup the tabs and add-on.
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  let addon = await AddonManager.getAddonByID("@set_newtab");
-  await addon.uninstall();
+  await extension.unload();
+  await secondExtension.unload();
 });
 
 add_task(async function testExtensionControlledWebNotificationsPermission() {
   let manifest = {
     manifest_version: 2,
     name: "TestExtension",
     version: "1.0",
     description: "Testing WebNotificationsDisable",
@@ -643,29 +874,21 @@ add_task(async function testExtensionCon
   async function checkHomepageEnabled() {
     await openPreferencesViaOpenPreferencesAPI("paneHome", { leaveOpen: true });
     let doc = gBrowser.contentDocument;
     is(
       gBrowser.currentURI.spec,
       "about:preferences#home",
       "#home should be in the URI for about:preferences"
     );
-    let controlledContent = doc.getElementById(
-      "browserHomePageExtensionContent"
-    );
 
     // The homepage is enabled.
     let homepageInput = doc.getElementById("homePageUrl");
     is(homepageInput.disabled, false, "The homepage input is enabled");
     is(homepageInput.value, "", "The homepage input is empty");
-    is(
-      controlledContent.hidden,
-      true,
-      "The extension controlled row is hidden"
-    );
 
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 
   await ExtensionSettingsStore.initialize();
 
   // Verify the setting isn't reported as controlled and the inputs are enabled.
   is(
--- a/browser/locales/en-US/browser/appMenuNotifications.ftl
+++ b/browser/locales/en-US/browser/appMenuNotifications.ftl
@@ -35,26 +35,26 @@ appmenu-update-restart-message = After a
 appmenu-addon-private-browsing-installed =
     .buttonlabel = Okay, Got It
     .buttonaccesskey = O
 appmenu-addon-post-install-message = Manage your add-ons by clicking <image data-l10n-name='addon-install-icon'></image> in the <image data-l10n-name='addon-menu-icon'></image> menu.
 appmenu-addon-post-install-incognito-checkbox =
     .label = Allow this extension to run in Private Windows
     .accesskey = A
 
-appmenu-new-tab-controlled =
-    .label = Your New Tab has changed.
+appmenu-new-tab-controlled-changes =
+    .label = Your new tab has changed.
     .buttonlabel = Keep Changes
     .buttonaccesskey = K
-    .secondarybuttonlabel = Disable Extension
-    .secondarybuttonaccesskey = D
-appmenu-homepage-controlled =
-    .label = Your home page has changed.
+    .secondarybuttonlabel = Manage New Tabs
+    .secondarybuttonaccesskey = M
+appmenu-homepage-controlled-changes =
+    .label = Your homepage has changed.
     .buttonlabel = Keep Changes
     .buttonaccesskey = K
-    .secondarybuttonlabel = Disable Extension
-    .secondarybuttonaccesskey = D
+    .secondarybuttonlabel = Manage Homepage
+    .secondarybuttonaccesskey = M
 appmenu-tab-hide-controlled =
     .label = Access Your Hidden Tabs
     .buttonlabel = Keep Tabs Hidden
     .buttonaccesskey = K
     .secondarybuttonlabel = Disable Extension
     .secondarybuttonaccesskey = D
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -90,24 +90,16 @@ restart-later = Restart Later
 ## These strings are used to inform the user
 ## about changes made by extensions to browser settings.
 ##
 ## <img data-l10n-name="icon"/> is going to be replaced by the extension icon.
 ##
 ## Variables:
 ##   $name (String): name of the extension
 
-# This string is shown to notify the user that their home page
-# is being controlled by an extension.
-extension-controlled-homepage-override = An extension, <img data-l10n-name="icon"/> { $name }, is controlling your home page.
-
-# This string is shown to notify the user that their new tab page
-# is being controlled by an extension.
-extension-controlled-new-tab-url = An extension, <img data-l10n-name="icon"/> { $name }, is controlling your New Tab page.
-
 # This string is shown to notify the user that the password manager setting
 # is being controlled by an extension
 extension-controlled-password-saving = An extension, <img data-l10n-name="icon"/> { $name }, is controlling this setting.
 
 # This string is shown to notify the user that their notifications permission
 # is being controlled by an extension.
 extension-controlled-web-notifications= An extension, <img data-l10n-name="icon"/> { $name }, is controlling this setting.
 
--- a/browser/themes/shared/preferences/preferences.inc.css
+++ b/browser/themes/shared/preferences/preferences.inc.css
@@ -435,16 +435,20 @@ button > hbox > label {
 #homeContentsGroup [data-subcategory] .section-checkbox {
   font-weight: 600;
 }
 
 #homeContentsGroup [data-subcategory] > vbox menulist {
   margin-block: 0;
 }
 
+.addon-with-favicon .menu-iconic-icon {
+  margin-inline-start: 0;
+}
+
 /* Search Pane */
 
 #engineList {
   margin: 2px 0 5px;
 }
 
 #engineList > treechildren::-moz-tree-image(engineShown, checked),
 #blocklistsTree > treechildren::-moz-tree-image(selectionCol, checked) {
--- a/toolkit/components/extensions/ExtensionSettingsStore.jsm
+++ b/toolkit/components/extensions/ExtensionSettingsStore.jsm
@@ -177,16 +177,46 @@ function getItem(type, key, id) {
     }
   }
 
   // Nothing found in the precedenceList or the setting is user-set,
   // return the initialValue.
   return { key, initialValue: keyInfo.initialValue };
 }
 
+/**
+ * Return an array of objects with properties for key, value, id, and enabled
+ * or an empty array if no settings have been stored for that key.
+ *
+ * @param {string} type
+ *        The type of setting to be retrieved.
+ * @param {string} key
+ *        A string that uniquely identifies the setting.
+ *
+ * @returns {array} an array of objects with properties for key, value, id, and enabled
+ */
+function getAllItems(type, key) {
+  ensureType(type);
+
+  let keyInfo = _store.data[type][key];
+  if (!keyInfo) {
+    return [];
+  }
+
+  let items = keyInfo.precedenceList;
+  return items
+    ? items.map(item => ({
+        key,
+        value: item.value,
+        id: item.id,
+        enabled: item.enabled,
+      }))
+    : [];
+}
+
 // Comparator used when sorting the precedence list.
 function precedenceComparator(a, b) {
   if (a.enabled && !b.enabled) {
     return -1;
   }
   if (b.enabled && !a.enabled) {
     return 1;
   }
@@ -531,16 +561,31 @@ var ExtensionSettingsStore = {
    *
    * @returns {object} An object with properties for key, value and id.
    */
   getSetting(type, key, id) {
     return getItem(type, key, id);
   },
 
   /**
+   * Retrieves an array of objects representing extensions attempting to control the specified setting
+   * or an empty array if no settings have been stored for that key.
+   *
+   * @param {string} type
+   *        The type of setting to be retrieved.
+   * @param {string} key
+   *        A string that uniquely identifies the setting.
+   *
+   * @returns {array} an array of objects with properties for key, value, id, and enabled
+   */
+  getAllSettings(type, key) {
+    return getAllItems(type, key);
+  },
+
+  /**
    * Returns whether an extension currently has a stored setting for a given
    * key.
    *
    * @param {string} id The id of the extension which is being checked.
    * @param {string} type The type of setting to be checked.
    * @param {string} key A string that uniquely identifies the setting.
    *
    * @returns {boolean} Whether the extension currently has a stored setting.
--- a/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
@@ -1004,8 +1004,86 @@ add_task(async function test_exceptions(
       "key_not_a_function",
       "val1",
       "not a function"
     ),
     /initialValueCallback must be a function/,
     "addSetting rejects with a callback that is not a function."
   );
 });
+
+add_task(async function test_get_all_settings() {
+  await promiseStartupManager();
+
+  let testExtensions = [
+    ExtensionTestUtils.loadExtension({
+      useAddonManager: "temporary",
+      manifest: {
+        applications: { gecko: { id: "@first" } },
+      },
+    }),
+    ExtensionTestUtils.loadExtension({
+      useAddonManager: "temporary",
+      manifest: {
+        applications: { gecko: { id: "@second" } },
+      },
+    }),
+  ];
+
+  for (let extension of testExtensions) {
+    await extension.startup();
+  }
+
+  await ExtensionSettingsStore.initialize();
+
+  let items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+  equal(items.length, 0, "There are no addons controlling this setting yet");
+
+  await ExtensionSettingsStore.addSetting(
+    "@first",
+    "foo",
+    "bar",
+    "set",
+    () => "not set"
+  );
+
+  items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+  equal(items.length, 1, "The add-on setting has 1 addon trying to control it");
+
+  await ExtensionSettingsStore.addSetting(
+    "@second",
+    "foo",
+    "bar",
+    "setting",
+    () => "not set"
+  );
+
+  let item = ExtensionSettingsStore.getSetting("foo", "bar");
+  equal(item.id, "@second", "The second add-on is in control");
+  equal(item.value, "setting", "The second value is set");
+
+  items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+  equal(
+    items.length,
+    2,
+    "The add-on setting has 2 addons trying to control it"
+  );
+
+  await ExtensionSettingsStore.removeSetting("@first", "foo", "bar");
+
+  items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+  equal(items.length, 1, "There is only 1 addon controlling this setting");
+
+  await ExtensionSettingsStore.removeSetting("@second", "foo", "bar");
+
+  items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+  equal(
+    items.length,
+    0,
+    "There is no longer any addon controlling this setting"
+  );
+
+  for (let extension of testExtensions) {
+    await extension.unload();
+  }
+
+  await promiseShutdownManager();
+});