Bug 1407999 - Support locked prefs for homepage r?jaws draft
authorMark Striemer <mstriemer@mozilla.com>
Thu, 12 Oct 2017 12:47:36 -0500
changeset 680334 a570802bbbaf3b59378f42b979947202d352a8b3
parent 679468 85839dc9517240eba72ed8ba306d412facabf179
child 735827 fc98cb7b178195d52333df3e093792d05736f2b1
push id84471
push userbmo:mstriemer@mozilla.com
push dateFri, 13 Oct 2017 21:48:49 +0000
reviewersjaws
bugs1407999
milestone58.0a1
Bug 1407999 - Support locked prefs for homepage r?jaws MozReview-Commit-ID: Fxo0jh6KbOt
browser/components/preferences/in-content/main.js
browser/components/preferences/in-content/tests/browser_extension_controlled.js
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -686,25 +686,34 @@ var gMainPane = {
    */
 
   syncFromHomePref() {
     let homePref = document.getElementById("browser.startup.homepage");
 
     // Set the "Use Current Page(s)" button's text and enabled state.
     this._updateUseCurrentButton();
 
-    // This is an async task.
-    handleControllingExtension("prefs", "homepage_override")
-      .then((isControlled) => {
-        // Disable or enable the inputs based on if this is controlled by an extension.
-        document.querySelectorAll("#browserHomePage, .homepage-button")
-          .forEach((button) => {
-            button.disabled = isControlled;
-          });
-      });
+    function setInputDisabledStates(isControlled) {
+      // Disable or enable the inputs based on if this is controlled by an extension.
+      document.querySelectorAll("#browserHomePage, .homepage-button")
+        .forEach((element) => {
+          let isLocked = document.getElementById(element.getAttribute("preference")).locked;
+          element.disabled = isLocked || isControlled;
+        });
+    }
+
+    if (homePref.locked) {
+      // An extension can't control these settings if they're locked.
+      hideControllingExtension("homepage_override");
+      setInputDisabledStates(false);
+    } else {
+      // Asynchronously update the extension controlled UI.
+      handleControllingExtension("prefs", "homepage_override")
+        .then(setInputDisabledStates);
+    }
 
     // If the pref is set to about:home or about:newtab, set the value to ""
     // to show the placeholder text (about:home title) rather than
     // exposing those URLs to users.
     let defaultBranch = Services.prefs.getDefaultBranch("");
     let defaultValue = defaultBranch.getComplexValue("browser.startup.homepage",
       Ci.nsIPrefLocalizedString).data;
     let currentValue = homePref.value.toLowerCase();
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -27,29 +27,34 @@ function installAddon(xpiName) {
         onInstallCancelled: reject,
         onInstallEnded: resolve
       });
       install.install();
     });
   });
 }
 
-function waitForMessageChange(messageId, cb) {
+function waitForMutation(target, opts, cb) {
   return new Promise((resolve) => {
-    let target = gBrowser.contentDocument.getElementById(messageId);
     let observer = new MutationObserver(() => {
-      if (cb(target)) {
+      if (!cb || cb(target)) {
         observer.disconnect();
         resolve();
       }
     });
-    observer.observe(target, { attributes: true, attributeFilter: ["hidden"] });
+    observer.observe(target, opts);
   });
 }
 
+function waitForMessageChange(messageId, cb) {
+  return waitForMutation(
+    gBrowser.contentDocument.getElementById(messageId),
+    { attributes: true, attributeFilter: ["hidden"] }, cb);
+}
+
 function waitForMessageHidden(messageId) {
   return waitForMessageChange(messageId, target => target.hidden);
 }
 
 function waitForMessageShown(messageId) {
   return waitForMessageChange(messageId, target => !target.hidden);
 }
 
@@ -85,16 +90,156 @@ add_task(async function testExtensionCon
   doc.getElementById("disableHomePageExtension").click();
 
   await waitForMessageHidden("browserHomePageExtensionContent");
 
   is(homepagePref(), originalHomepagePref, "homepage is set back to default");
   is(doc.getElementById("browserHomePage").disabled, false, "The homepage input is enabled");
   is(controlledContent.hidden, true, "The extension controlled row is hidden");
 
+  let addon = await AddonManager.getAddonByID("@set_homepage");
+  // Enable the extension so we get the UNINSTALL event, which is needed by
+  // ExtensionPreferencesManager to clean up properly.
+  // FIXME: See https://bugzilla.mozilla.org/show_bug.cgi?id=1408226.
+  addon.userDisabled = false;
+  await waitForMessageShown("browserHomePageExtensionContent");
+  // Do the uninstall now that the enable code has been run.
+  addon.uninstall();
+
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_task(async function testPrefLockedHomepage() {
+  await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+  let doc = gBrowser.contentDocument;
+  is(gBrowser.currentURI.spec, "about:preferences#general",
+     "#general should be in the URI for about:preferences");
+
+  let homePagePref = "browser.startup.homepage";
+  let buttonPrefs = [
+    "pref.browser.homepage.disable_button.current_page",
+    "pref.browser.homepage.disable_button.bookmark_page",
+    "pref.browser.homepage.disable_button.restore_default",
+  ];
+  let homePageInput = doc.getElementById("browserHomePage");
+  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))
+    .concat([waitForMutation(homePageInput, mutationOpts)]));
+  let getHomepage = () => Services.prefs.getCharPref("browser.startup.homepage");
+
+  let originalHomepage = getHomepage();
+  let extensionHomepage = "https://developer.mozilla.org/";
+  let lockedHomepage = "http://www.yahoo.com";
+
+  let lockPrefs = () => {
+    buttonPrefs.forEach(pref => {
+      prefs.setBoolPref(pref, true);
+      prefs.lockPref(pref);
+    });
+    // Do the homepage last since that's the only pref that triggers a UI update.
+    prefs.setCharPref(homePagePref, lockedHomepage);
+    prefs.lockPref(homePagePref);
+  };
+  let unlockPrefs = () => {
+    buttonPrefs.forEach(pref => {
+      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);
+  };
+
+  ok(originalHomepage != extensionHomepage, "The extension will change the homepage");
+
+  // Install an extension that sets the homepage to MDN.
+  await installAddon("set_homepage.xpi");
+  await waitForMessageShown(controlledContent.id);
+
+  // 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 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;
+
+  // 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");
+  buttonPrefs.forEach(pref => {
+    is(getButton(pref).disabled, true, `The ${pref} button is disabled when locked`);
+  });
+  is(controlledContent.hidden, true, "The extension controlled message is hidden when locked");
+
+  // Unlock the prefs, wait for the UI to update.
+  let messageShown = waitForMessageShown(controlledContent.id);
+  unlockPrefs();
+  await messageShown;
+
+  // Verify that the UI is showing the extension's settings.
+  is(homePageInput.value, extensionHomepage, "The homepage is set by the extension");
+  is(homePageInput.disabled, true, "Homepage 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 when unlocked");
+
+  // Uninstall the add-on.
+  let addon = await AddonManager.getAddonByID("@set_homepage");
+  addon.uninstall();
+  await waitForMessageHidden(controlledContent.id);
+
+  // 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(homePageInput.disabled, false, "The homepage is enabled after clearing lock");
+  buttonPrefs.forEach(pref => {
+    is(getButton(pref).disabled, false, `The ${pref} button is enabled when unlocked`);
+  });
+
+  // Lock the prefs without an extension.
+  lockPrefs();
+  await waitForAllMutations();
+
+  // 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");
+  buttonPrefs.forEach(pref => {
+    is(getButton(pref).disabled, true, `The ${pref} button is disabled when locked`);
+  });
+
+  // Unlock the prefs without an extension.
+  unlockPrefs();
+  await waitForAllMutations();
+
+  // 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");
+  is(homePageInput.disabled, false, "The homepage is enabled after clearing lock");
+  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");
+
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(async function testExtensionControlledNewTab() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
   let doc = gBrowser.contentDocument;
   is(gBrowser.currentURI.spec, "about:preferences#general",
      "#general should be in the URI for about:preferences");