Bug 1429186 - Create an enterprise policy to set the homepage and, optionally, lock it draft
authorKirk Steuber <ksteuber@mozilla.com>
Tue, 27 Feb 2018 09:13:16 -0800
changeset 762092 8905e3a6deac516f3d182d1b2188ed2c7b8d9524
parent 760371 b184be59874080e96903183176c0f88dcbfafe25
push id101084
push userksteuber@mozilla.com
push dateThu, 01 Mar 2018 20:20:39 +0000
bugs1429186
milestone60.0a1
Bug 1429186 - Create an enterprise policy to set the homepage and, optionally, lock it Also creates the runOncePerModification helper function in Policies.jsm and fixes a minor bug in the runOnce helper function also in Policies.jsm MozReview-Commit-ID: HDgMmhHI1D0
browser/components/enterprisepolicies/Policies.jsm
browser/components/enterprisepolicies/schemas/policies-schema.json
browser/components/enterprisepolicies/tests/browser/browser.ini
browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -205,16 +205,40 @@ var Policies = {
   },
 
   "FlashPlugin": {
     onBeforeUIStartup(manager, param) {
       addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
     }
   },
 
+  "Homepage": {
+    onBeforeUIStartup(manager, param) {
+      // |homepages| will be a string containing a pipe-separated ('|') list of
+      // URLs because that is what the "Home page" section of about:preferences
+      // (and therefore what the pref |browser.startup.homepage|) accepts.
+      let homepages = param.URL.spec;
+      if (param.Additional && param.Additional.length > 0) {
+        homepages += "|" + param.Additional.map(url => url.spec).join("|");
+      }
+      if (param.Locked) {
+        setAndLockPref("browser.startup.homepage", homepages);
+        setAndLockPref("browser.startup.page", 1);
+        setAndLockPref("pref.browser.homepage.disable_button.current_page", true);
+        setAndLockPref("pref.browser.homepage.disable_button.bookmark_page", true);
+        setAndLockPref("pref.browser.homepage.disable_button.restore_default", true);
+      } else {
+        runOncePerModification("setHomepage", homepages, () => {
+          Services.prefs.setStringPref("browser.startup.homepage", homepages);
+          Services.prefs.setIntPref("browser.startup.page", 1);
+        });
+      }
+    }
+  },
+
   "InstallAddons": {
     onBeforeUIStartup(manager, param) {
       addAllowDenyPermissions("install", param.Allow, null);
     }
   },
 
   "Popups": {
     onBeforeUIStartup(manager, param) {
@@ -321,11 +345,41 @@ function addAllowDenyPermissions(permiss
  *        The callback to run only once.
  */
 function runOnce(actionName, callback) {
   let prefName = `browser.policies.runonce.${actionName}`;
   if (Services.prefs.getBoolPref(prefName, false)) {
     log.debug(`Not running action ${actionName} again because it has already run.`);
     return;
   }
+  Services.prefs.setBoolPref(prefName, true);
   callback();
-  Services.prefs.setBoolPref(prefName, true);
 }
+
+/**
+ * runOncePerModification
+ *
+ * Helper function similar to runOnce. The difference is that runOnce runs the
+ * callback once when the policy is set, then never again.
+ * runOncePerModification runs the callback once each time the policy value
+ * changes from its previous value.
+ *
+ * @param {string} actionName
+ *        A given name which will be used to track if this callback has run.
+ *        This string will be part of a pref name.
+ * @param {string} policyValue
+ *        The current value of the policy. This will be compared to previous
+ *        values given to this function to determine if the policy value has
+ *        changed. Regardless of the data type of the policy, this must be a
+ *        string.
+ * @param {Function} callback
+ *        The callback to be run when the pref value changes
+ */
+function runOncePerModification(actionName, policyValue, callback) {
+  let prefName = `browser.policies.runOncePerModification.${actionName}`;
+  let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
+  if (policyValue === oldPolicyValue) {
+    log.debug(`Not running action ${actionName} again because the policy's value is unchanged`);
+    return;
+  }
+  Services.prefs.setStringPref(prefName, policyValue);
+  callback();
+}
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
@@ -194,16 +194,39 @@
           "type": "array",
           "items": {
             "type": "origin"
           }
         }
       }
     },
 
+    "Homepage": {
+      "description": "Set and optionally lock the homepage.",
+      "first_available": "60.0",
+      "enterprise_only": true,
+
+      "type": "object",
+      "properties": {
+        "URL": {
+          "type": "URL"
+        },
+        "Locked": {
+          "type": "boolean"
+        },
+        "Additional": {
+          "type": "array",
+          "items": {
+            "type": "URL"
+          }
+        }
+      },
+      "required": ["URL"]
+    },
+
     "InstallAddons": {
       "description": "Allow or deny popup websites to install webextensions.",
       "first_available": "60.0",
 
       "type": "object",
       "properties": {
         "Allow": {
           "type": "array",
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -24,8 +24,9 @@ support-files =
 [browser_policy_disable_formhistory.js]
 [browser_policy_disable_fxscreenshots.js]
 [browser_policy_disable_masterpassword.js]
 [browser_policy_disable_pocket.js]
 [browser_policy_disable_shield.js]
 [browser_policy_display_bookmarks.js]
 [browser_policy_display_menu.js]
 [browser_policy_remember_passwords.js]
+[browser_policy_set_homepage.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js
@@ -0,0 +1,170 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Prefs that will be touched by the test and need to be reset when the test
+// cleans up.
+const TOUCHED_PREFS = {
+  "browser.startup.homepage": "String",
+  "browser.startup.page": "Int",
+  "pref.browser.homepage.disable_button.current_page": "Bool",
+  "pref.browser.homepage.disable_button.bookmark_page": "Bool",
+  "pref.browser.homepage.disable_button.restore_default": "Bool",
+  "browser.policies.runOncePerModification.setHomepage": "String",
+};
+let ORIGINAL_PREF_VALUE = {};
+
+add_task(function read_original_pref_values() {
+  for (let pref in TOUCHED_PREFS) {
+    let prefType = TOUCHED_PREFS[pref];
+    ORIGINAL_PREF_VALUE[pref] =
+      Services.prefs[`get${prefType}Pref`](pref, undefined);
+  }
+});
+registerCleanupFunction(function restore_pref_values() {
+  let defaults = Services.prefs.getDefaultBranch("");
+  for (let pref in TOUCHED_PREFS) {
+    Services.prefs.unlockPref(pref);
+    Services.prefs.clearUserPref(pref);
+    let originalValue = ORIGINAL_PREF_VALUE[pref];
+    let prefType = TOUCHED_PREFS[pref];
+    if (originalValue !== undefined) {
+      defaults[`set${prefType}Pref`](pref, originalValue);
+    }
+  }
+});
+
+add_task(async function homepage_test_simple() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example1.com/"
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example1.com/", "Homepage URL should have been set");
+  is(Services.prefs.getIntPref("browser.startup.page", -1), 1,
+     "Homepage should be used instead of blank page or previous tabs");
+});
+
+add_task(async function homepage_test_repeat_same_policy_value() {
+  // Simulate homepage change after policy applied
+  Services.prefs.setStringPref("browser.startup.homepage",
+                               "http://example2.com/");
+  Services.prefs.setIntPref("browser.startup.page", 3);
+
+  // Policy should have no effect. Homepage has not been locked and policy value
+  // has not changed. We should be respecting the homepage that the user gave.
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example1.com/"
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example2.com/",
+     "Homepage URL should not have been overridden by pre-existing policy value");
+  is(Services.prefs.getIntPref("browser.startup.page", -1), 3,
+     "Start page type should not have been overridden by pre-existing policy value");
+});
+
+add_task(async function homepage_test_different_policy_value() {
+  // This policy is a change from the policy's previous value. This should
+  // override the user's homepage
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example3.com/"
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example3.com/",
+     "Homepage URL should have been overridden by the policy value");
+  is(Services.prefs.getIntPref("browser.startup.page", -1), 1,
+     "Start page type should have been overridden by setting the policy value");
+});
+
+add_task(async function homepage_test_empty_additional() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example1.com/",
+        "Additional": []
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example1.com/", "Homepage URL should have been set properly");
+});
+
+add_task(async function homepage_test_single_additional() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example1.com/",
+        "Additional": ["http://example2.com/"]
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example1.com/|http://example2.com/",
+     "Homepage URL should have been set properly");
+
+});
+
+add_task(async function homepage_test_multiple_additional() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example1.com/",
+        "Additional": ["http://example2.com/",
+                       "http://example3.com/"]
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example1.com/|http://example2.com/|http://example3.com/",
+     "Homepage URL should have been set properly");
+
+});
+
+add_task(async function homepage_test_locked() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Homepage": {
+        "URL": "http://example4.com/",
+        "Additional": ["http://example5.com/",
+                       "http://example6.com/"],
+        "Locked": true
+      }
+    }
+  });
+  is(Services.prefs.getStringPref("browser.startup.homepage", ""),
+     "http://example4.com/|http://example5.com/|http://example6.com/",
+     "Homepage URL should have been set properly");
+  is(Services.prefs.getIntPref("browser.startup.page", -1), 1,
+     "Homepage should be used instead of blank page or previous tabs");
+  is(Services.prefs.prefIsLocked("browser.startup.homepage"), true,
+     "Homepage pref should be locked");
+  is(Services.prefs.prefIsLocked("browser.startup.page"), true,
+     "Start page type pref should be locked");
+
+  // Test that UI is disabled when the Locked property is enabled
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
+  await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+    is(content.document.getElementById("browserStartupPage").disabled, true,
+       "When homepage is locked, the startup page choice controls should be locked");
+    is(content.document.getElementById("browserHomePage").disabled, true,
+       "Homepage input should be disabled");
+    is(content.document.getElementById("useCurrent").disabled, true,
+       "\"Use Current Page\" button should be disabled");
+    is(content.document.getElementById("useBookmark").disabled, true,
+       "\"Use Bookmark...\" button should be disabled");
+    is(content.document.getElementById("restoreDefaultHomePage").disabled, true,
+       "\"Restore to Default\" button should be disabled");
+  });
+  await BrowserTestUtils.removeTab(tab);
+});