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
--- 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);
+});