Bug 1419102 - Add hot-restart of the engine to support test, and create test infrastructure. r=Mossop
MozReview-Commit-ID: BXbwXlDh3nP
--- a/browser/components/enterprisepolicies/EnterprisePolicies.js
+++ b/browser/components/enterprisepolicies/EnterprisePolicies.js
@@ -58,16 +58,17 @@ const EnterprisePoliciesFactory = {
// Constructor
function EnterprisePoliciesManager() {
LOG("STARTING CONSTRUCTOR");
Services.obs.addObserver(this, "profile-after-change", true);
Services.obs.addObserver(this, "final-ui-startup", true);
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
+ Services.obs.addObserver(this, "EnterprisePolicies:Restart", true);
}
EnterprisePoliciesManager.prototype = {
// for XPCOM
classID: Components.ID("{ea4e1414-779b-458b-9d1f-d18e8efbc145}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference,
Ci.nsIEnterprisePolicies]),
@@ -148,30 +149,71 @@ EnterprisePoliciesManager.prototype = {
while (callbacks.length > 0) {
let callback = callbacks.shift();
try {
callback();
} catch (ex) {}
}
},
+ _restart() {
+ if (!Cu.isInAutomation) {
+ return;
+ }
+ LOG("Restarting");
+
+ DisallowedFeatures = {};
+
+ this._status = Ci.nsIEnterprisePolicies.UNINITIALIZED;
+ for (let timing of Object.keys(this._callbacks)) {
+ this._callbacks[timing] = [];
+ }
+ delete Services.ppmm.initialProcessData.policies;
+ Services.ppmm.broadcastAsyncMessage("EnterprisePolicies:Restart", null);
+
+ // Simulate the startup process. This is ugly but it tries to ensure
+ // the same behavior as of a normal startup.
+ Services.tm.idleDispatchToMainThread(() => {
+ this.observe(null, "profile-after-change", null);
+
+ Services.tm.idleDispatchToMainThread(() => {
+ this.observe(null, "final-ui-startup", null);
+
+ Services.tm.idleDispatchToMainThread(() => {
+ this.observe(null, "sessionstore-windows-restored", null);
+ });
+ });
+ });
+ },
+
+
// nsIObserver implementation
observe: function BG_observe(subject, topic, data) {
switch (topic) {
case "profile-after-change":
+ // Before the first set of policy callbacks runs, we must
+ // initialize the service.
this._initialize();
this._runPoliciesCallbacks("ProfileAfterChange");
break;
case "final-ui-startup":
this._runPoliciesCallbacks("BeforeUIStartup");
break;
case "sessionstore-windows-restored":
this._runPoliciesCallbacks("AllWindowsRestored");
+
+ // After the last set of policy callbacks ran, notify the test observer.
+ Services.obs.notifyObservers(null,
+ "EnterprisePolicies:AllPoliciesApplied");
+ break;
+
+ case "EnterprisePolicies:Restart":
+ this._restart();
break;
}
},
disallowFeature(feature, neededOnContentProcess = false) {
DisallowedFeatures[feature] = true;
// NOTE: For optimization purposes, only features marked as needed
--- a/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
+++ b/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
@@ -37,35 +37,41 @@ function EnterprisePoliciesManagerConten
if (policies) {
this._status = policies.status;
// make a copy of the array so that we can keep adding to it
// in a way that is not confusing.
this._disallowedFeatures = policies.disallowedFeatures.slice();
}
Services.cpmm.addMessageListener("EnterprisePolicies:DisallowFeature", this);
+ Services.cpmm.addMessageListener("EnterprisePolicies:Restart", this);
}
EnterprisePoliciesManagerContent.prototype = {
// for XPCOM
classID: Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
Ci.nsIEnterprisePolicies]),
// redefine the default factory for XPCOMUtils
_xpcom_factory: EnterprisePoliciesFactory,
_status: Ci.nsIEnterprisePolicies.INACTIVE,
_disallowedFeatures: [],
receiveMessage({name, data}) {
- LOG(`received message: {name}, with data.feature: ${data.feature}`);
- if (name == "EnterprisePolicies:DisallowFeature") {
- this._disallowedFeatures.push(data.feature);
+ switch (name) {
+ case "EnterprisePolicies:DisallowFeature":
+ this._disallowedFeatures.push(data.feature);
+ break;
+
+ case "EnterprisePolicies:Restart":
+ this._disallowedFeatures = [];
+ break;
}
},
get status() {
return this._status;
},
isAllowed(feature) {
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -32,10 +32,10 @@ this.Policies = {
"block_devtools": {
onProfileAfterChange(manager, param) {
if (param == true) {
manager.disallowFeature("devtools");
}
}
},
- "bookmarks_on_menu": {}
+ "bookmarks_on_menu": {},
};
--- a/browser/components/enterprisepolicies/moz.build
+++ b/browser/components/enterprisepolicies/moz.build
@@ -7,29 +7,29 @@
with Files("**"):
BUG_COMPONENT = ("Firefox", "Enterprise Policies")
DIRS += [
'helpers',
'schemas',
]
+TEST_DIRS += [
+ 'tests'
+]
+
XPIDL_SOURCES += [
'nsIEnterprisePolicies.idl',
]
XPIDL_MODULE = 'enterprisepolicies'
EXTRA_COMPONENTS += [
'EnterprisePolicies.js',
'EnterprisePolicies.manifest',
'EnterprisePoliciesContent.js',
]
EXTRA_JS_MODULES.policies += [
'Policies.jsm',
]
-#BROWSER_CHROME_MANIFESTS += [
-# 'tests/browser/browser.ini'
-#]
-
FINAL_LIBRARY = 'browsercomps'
copy from browser/components/newtab/tests/browser/.eslintrc.js
copy to browser/components/enterprisepolicies/tests/browser/.eslintrc.js
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+prefs =
+ browser.policies.enabled=true
+support-files =
+ head.js
+ config_simple_policies.json
+ config_broken_json.json
+
+[browser_policies_broken_json.js]
+[browser_policies_simple_policies.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policies_broken_json.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+add_task(async function test_clean_slate() {
+ await startWithCleanSlate();
+});
+
+add_task(async function test_broken_json() {
+ await setupPolicyEngineWithJson("config_broken_json.json");
+
+ is(Services.policies.status, Ci.nsIEnterprisePolicies.FAILED, "Engine was correctly set to the error state");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policies_simple_policies.js
@@ -0,0 +1,88 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+add_task(async function test_clean_slate() {
+ await startWithCleanSlate();
+});
+
+add_task(async function test_simple_policies() {
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+ // Initialize the service in the content process, in case it hasn't
+ // already started.
+ Services.policies;
+ });
+
+ let { Policies } = Cu.import("resource:///modules/policies/Policies.jsm", {});
+
+ let policy1Ran = false, policy2Ran = false, policy3Ran = false;
+
+ // Implement functions to handle the three simple policies that will be added
+ // to the schema.
+ Policies.simple_policy1 = {
+ onProfileAfterChange(manager, param) {
+ is(param, true, "Param matches what was passed in config file");
+ manager.disallowFeature("feature1", /* needed in content process */ true);
+ policy1Ran = true;
+ }
+ };
+
+ Policies.simple_policy2 = {
+ onBeforeUIStartup(manager, param) {
+ is(param, true, "Param matches what was passed in config file");
+ manager.disallowFeature("feature2", /* needed in content process */ false);
+ policy2Ran = true;
+ }
+ };
+
+ Policies.simple_policy3 = {
+ onAllWindowsRestored(manager, param) {
+ is(param, false, "Param matches what was passed in config file");
+ policy3Ran = true;
+ }
+ };
+
+ await setupPolicyEngineWithJson(
+ "config_simple_policies.json",
+ /* custom schema */
+ {
+ properties: {
+ "simple_policy1": {
+ "type": "boolean"
+ },
+
+ "simple_policy2": {
+ "type": "boolean"
+ },
+
+ "simple_policy3": {
+ "type": "boolean"
+ }
+
+ }
+ }
+ );
+
+ is(Services.policies.status, Ci.nsIEnterprisePolicies.ACTIVE, "Engine is active");
+ is(Services.policies.isAllowed("feature1"), false, "Dummy feature was disallowed");
+ is(Services.policies.isAllowed("feature2"), false, "Dummy feature was disallowed");
+
+ ok(policy1Ran, "Policy 1 ran correctly through onProfileAfterChange");
+ ok(policy2Ran, "Policy 2 ran correctly through onBeforeUIStartup");
+ ok(policy3Ran, "Policy 3 ran correctly through onAllWindowsRestored");
+
+ await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ is(Services.policies.isAllowed("feature1"), false, "Correctly disallowed in the content process");
+ // Feature 2 wasn't explictly marked as needed in the content process, so it is not marked
+ // as disallowed there.
+ is(Services.policies.isAllowed("feature2"), true, "Correctly missing in the content process");
+ }
+ });
+
+ delete Policies.simple_policy1;
+ delete Policies.simple_policy2;
+ delete Policies.simple_policy3;
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/config_broken_json.json
@@ -0,0 +1,3 @@
+{
+ "policies
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/config_simple_policies.json
@@ -0,0 +1,7 @@
+{
+ "policies": {
+ "simple_policy1": true,
+ "simple_policy2": true,
+ "simple_policy3": false
+ }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/head.js
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+async function setupPolicyEngineWithJson(jsonName, customSchema) {
+ let filePath = getTestFilePath(jsonName ? jsonName : "non-existing-file.json");
+ Services.prefs.setStringPref("browser.policies.alternatePath", filePath);
+
+ let resolve = null;
+ let promise = new Promise((r) => resolve = r);
+
+ Services.obs.addObserver(function observer() {
+ Services.obs.removeObserver(observer, "EnterprisePolicies:AllPoliciesApplied");
+ resolve();
+ }, "EnterprisePolicies:AllPoliciesApplied");
+
+ // Clear any previously used custom schema
+ Cu.unload("resource:///modules/policies/schema.jsm");
+
+ if (customSchema) {
+ let schemaModule = Cu.import("resource:///modules/policies/schema.jsm", {});
+ schemaModule.schema = customSchema;
+ }
+
+ Services.obs.notifyObservers(null, "EnterprisePolicies:Restart");
+ return promise;
+}
+
+async function startWithCleanSlate() {
+ await setupPolicyEngineWithJson("");
+ is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Firefox", "General")
+
+BROWSER_CHROME_MANIFESTS += [
+ 'browser/browser.ini'
+]