Bug 1429186 - Create an enterprise policy to set the homepage and, optionally, lock it r=Felipe
authorKirk Steuber <ksteuber@mozilla.com>
Tue, 27 Feb 2018 09:13:16 -0800
changeset 461178 406c8d7d134fd094469ed7910a28696a8a5330af
parent 461177 413a684727a20b4c4a0e0aa06244faa14e1be401
child 461179 30b45ad3b81ada48f1924c16035e6bf7926c4474
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersFelipe
bugs1429186
milestone60.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 1429186 - Create an enterprise policy to set the homepage and, optionally, lock it r=Felipe 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
@@ -216,16 +216,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) {
@@ -332,11 +356,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
@@ -202,16 +202,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
@@ -25,8 +25,9 @@ support-files =
 [browser_policy_disable_fxscreenshots.js]
 [browser_policy_disable_masterpassword.js]
 [browser_policy_disable_pocket.js]
 [browser_policy_disable_privatebrowsing.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);
+});