Bug 1419102 - Make initialization of the Policies manager its own step with a well-defined timing, running before the add-ons manager initializes. r=Mossop draft
authorFelipe Gomes <felipc@gmail.com>
Fri, 19 Jan 2018 13:04:11 -0200
changeset 722691 f6ec31581d381cb1500fc82b984640a66d03cefb
parent 722690 f2bcb7df58e3f2caec2cf310a122c392e83b4878
child 722705 dfea414d12940980b532685b160fbca6db923d8c
push id96199
push userfelipc@gmail.com
push dateFri, 19 Jan 2018 15:05:44 +0000
reviewersMossop
bugs1419102
milestone59.0a1
Bug 1419102 - Make initialization of the Policies manager its own step with a well-defined timing, running before the add-ons manager initializes. r=Mossop MozReview-Commit-ID: 5zH0kB9WGpW
browser/components/enterprisepolicies/EnterprisePolicies.js
browser/components/enterprisepolicies/EnterprisePolicies.manifest
browser/components/enterprisepolicies/tests/browser/browser_policies_simple_policies.js
browser/components/enterprisepolicies/tests/browser/config_simple_policies.json
toolkit/xre/nsXREDirProvider.cpp
--- a/browser/components/enterprisepolicies/EnterprisePolicies.js
+++ b/browser/components/enterprisepolicies/EnterprisePolicies.js
@@ -132,16 +132,17 @@ EnterprisePoliciesManager.prototype = {
                                 this, /* the EnterprisePoliciesManager */
                                 parsedParameters));
         }
       }
     }
   },
 
   _callbacks: {
+    BeforeAddons: [],
     ProfileAfterChange: [],
     BeforeUIStartup: [],
     AllWindowsRestored: [],
   },
 
   _schedulePolicyCallback(timing, callback) {
     this._callbacks[timing].push(callback);
   },
@@ -153,70 +154,81 @@ EnterprisePoliciesManager.prototype = {
       try {
         callback();
       } catch (ex) {
         log.error("Error running ", callback, `for ${timing}:`, ex);
       }
     }
   },
 
-  _restart() {
+  async _restart() {
     if (!Cu.isInAutomation) {
       return;
     }
 
     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);
+    let { PromiseUtils } = Cu.import("resource://gre/modules/PromiseUtils.jsm",
+                                     {});
+
+    // Simulate the startup process. This step-by-step is a bit ugly but it
+    // tries to emulate the same behavior as of a normal startup.
+
+    await PromiseUtils.idleDispatch(() => {
+      this.observe(null, "policies-startup", null);
+    });
 
-      Services.tm.idleDispatchToMainThread(() => {
-        this.observe(null, "final-ui-startup", null);
+    await PromiseUtils.idleDispatch(() => {
+      this.observe(null, "profile-after-change", null);
+    });
 
-        Services.tm.idleDispatchToMainThread(() => {
-          this.observe(null, "sessionstore-windows-restored", null);
-        });
-      });
+    await PromiseUtils.idleDispatch(() => {
+      this.observe(null, "final-ui-startup", null);
+    });
+
+    await PromiseUtils.idleDispatch(() => {
+      this.observe(null, "sessionstore-windows-restored", null);
     });
   },
 
-
   // nsIObserver implementation
   observe: function BG_observe(subject, topic, data) {
     switch (topic) {
+      case "policies-startup":
+        this._initialize();
+        this._runPoliciesCallbacks("BeforeAddons");
+        break;
+
       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();
+        this._restart().then(null, Cu.reportError);
         break;
     }
   },
 
   disallowFeature(feature, neededOnContentProcess = false) {
     DisallowedFeatures[feature] = true;
 
     // NOTE: For optimization purposes, only features marked as needed
--- a/browser/components/enterprisepolicies/EnterprisePolicies.manifest
+++ b/browser/components/enterprisepolicies/EnterprisePolicies.manifest
@@ -1,6 +1,5 @@
 component {ea4e1414-779b-458b-9d1f-d18e8efbc145} EnterprisePolicies.js process=main
 contract @mozilla.org/browser/enterprisepolicies;1 {ea4e1414-779b-458b-9d1f-d18e8efbc145} process=main
-category app-startup EnterprisePoliciesManager service,@mozilla.org/browser/enterprisepolicies;1 process=main
 
 component {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} EnterprisePoliciesContent.js process=content
 contract @mozilla.org/browser/enterprisepolicies;1 {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} process=content
--- a/browser/components/enterprisepolicies/tests/browser/browser_policies_simple_policies.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policies_simple_policies.js
@@ -12,20 +12,27 @@ add_task(async function test_simple_poli
   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;
+  let policy0Ran = false, policy1Ran = false, policy2Ran = false, policy3Ran = false;
 
-  // Implement functions to handle the three simple policies that will be added
+  // Implement functions to handle the four simple policies that will be added
   // to the schema.
+  Policies.simple_policy0 = {
+    onProfileAfterChange(manager, param) {
+      is(param, true, "Param matches what was passed in config file");
+      policy0Ran = true;
+    }
+  };
+
   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;
     }
   };
 
@@ -44,16 +51,20 @@ add_task(async function test_simple_poli
     }
   };
 
   await setupPolicyEngineWithJson(
     "config_simple_policies.json",
     /* custom schema */
     {
       properties: {
+        "simple_policy0": {
+          "type": "boolean"
+        },
+
         "simple_policy1": {
           "type": "boolean"
         },
 
         "simple_policy2": {
           "type": "boolean"
         },
 
@@ -64,25 +75,27 @@ add_task(async function test_simple_poli
       }
     }
   );
 
   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(policy0Ran, "Policy 0 ran correctly through BeforeAddons");
   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_policy0;
   delete Policies.simple_policy1;
   delete Policies.simple_policy2;
   delete Policies.simple_policy3;
 });
--- a/browser/components/enterprisepolicies/tests/browser/config_simple_policies.json
+++ b/browser/components/enterprisepolicies/tests/browser/config_simple_policies.json
@@ -1,7 +1,8 @@
 {
   "policies": {
+    "simple_policy0": true,
     "simple_policy1": true,
     "simple_policy2": true,
     "simple_policy3": false
   }
 }
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -1004,16 +1004,21 @@ nsXREDirProvider::DoStartup()
         appStartup->RestartInSafeMode(nsIAppStartup::eForceQuit);
         return NS_OK;
       }
     }
 
     static const char16_t kStartup[] = {'s','t','a','r','t','u','p','\0'};
     obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup);
 
+    nsCOMPtr<nsIObserver> policies(do_GetService("@mozilla.org/browser/enterprisepolicies;1"));
+    if (policies) {
+      policies->Observe(nullptr, "policies-startup", nullptr);
+    }
+
     // Init the Extension Manager
     nsCOMPtr<nsIObserver> em = do_GetService("@mozilla.org/addons/integration;1");
     if (em) {
       em->Observe(nullptr, "addons-startup", nullptr);
     } else {
       NS_WARNING("Failed to create Addons Manager.");
     }