Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ EnterprisePoliciesManager.prototype = {
}
},

_initialize() {
async _initialize() {
this._cleanupPolicies();

const changesHandler = provider => {
Expand Down Expand Up @@ -137,7 +137,7 @@ EnterprisePoliciesManager.prototype = {
this._status = Ci.nsIEnterprisePolicies.INACTIVE;
Services.prefs.setBoolPref(PREF_POLICIES_APPLIED, false);

let provider = this._chooseProvider(changesHandler);
let provider = await this._chooseProvider(changesHandler);
if (provider.failed) {
this._status = Ci.nsIEnterprisePolicies.FAILED;
}
Expand All @@ -148,7 +148,7 @@ EnterprisePoliciesManager.prototype = {
Glean.policies.isEnterprise.set(this.isEnterprise);
},

_chooseProvider(handler) {
async _chooseProvider(handler) {
let platformProvider = null;
if (AppConstants.platform == "win" && AppConstants.MOZ_SYSTEM_POLICIES) {
platformProvider = new WindowsGPOPoliciesProvider();
Expand All @@ -163,8 +163,13 @@ EnterprisePoliciesManager.prototype = {

let jsonProvider = new JSONPoliciesProvider();
jsonProvider.onPoliciesChanges(handler);

let remoteProvider = RemotePoliciesProvider.createInstance();
// Fetch first set of remote policies during the
// initialization of the policy engine
await remoteProvider.fetchPoliciesOnStartup();
remoteProvider.onPoliciesChanges(handler);

if (platformProvider && platformProvider.hasPolicies) {
if (jsonProvider.hasPolicies) {
return new CombinedProvider(
Expand Down Expand Up @@ -415,14 +420,16 @@ EnterprisePoliciesManager.prototype = {
this.observersReceived.push(topic);

switch (topic) {
case "policies-startup":
// Before the first set of policy callbacks runs, we must
// initialize the service.
this._initialize();

case "policies-startup": {
const initializedPromise = this._initialize();
// _initialize() does async work (fetching remote policies).
// We spin a nested event loop until the promise resolves so
// this observer doesn't return before initialization completes.
// This keeps startup behavior effectively synchronous.
this.spinResolve(initializedPromise);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming/guessing that the reason this uses a spinResolve instead of await is that observe is non-async and defined in niIObserver so you can't change that, combined with needing _initialize to finish before calling _runPoliciesCallbacks. Is it worth adding a small comment here explaining the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some background is probably in the original PR #321 (comment)

this._runPoliciesCallbacks("onBeforeAddons");
break;

}
case "profile-after-change":
this._runPoliciesCallbacks("onProfileAfterChange");
break;
Expand Down Expand Up @@ -472,6 +479,44 @@ EnterprisePoliciesManager.prototype = {
}
},

/**
* Spin the event loop until the passed promise resolves.
*
* This is used to await the response when fetching remote
* policies during the initialization of the policy engine.
*
* @param {Promise} promise
* @returns {any} Result of the resolved promise
*/
spinResolve(promise) {
if (!(promise instanceof Promise)) {
return promise;
}
let done = false;
let result = null;
let error = null;
promise
.catch(e => {
error = e;
})
.then(r => {
result = r;
done = true;
});

Services.tm.spinEventLoopUntil(
"EnterprisePoliciesManager.sys.mjs:_initialize",
() => done
);
if (!done) {
throw new Error("Forcefully exited event loop.");
} else if (error) {
throw error;
} else {
return result;
}
},

messageDisallowedFeatures(neededOnContentProcess = false) {
// NOTE: For optimization purposes, only features marked as needed
// on content process will be passed onto the child processes.
Expand Down Expand Up @@ -954,6 +999,29 @@ class RemotePoliciesProvider {
this.triggerOnPoliciesChanges();
}
}

async fetchPoliciesOnStartup() {
if (!this._isPollingEnabled) {
return;
}

let res;
try {
res = await lazy.ConsoleClient.getRemotePolicies();
} catch (e) {
console.error(`Failed to fetch remote policies on startup: ${e}`);
this._failed = true;
return;
}
if (!res.policies) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous can throw I think, so you want to catch too, not just nullcheck.

console.error(
`No policies were found in the response: ${JSON.stringify(res)}.`
);
this._failed = true;
} else {
this._policies = res.policies;
Copy link
Contributor

@gcp gcp Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this need to do a call/trigger/notify to explicitly process the policies?

Copy link
Contributor

@gcp gcp Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ingestPolicies() or triggerOnPoliciesChanges() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yes. Good catch! I have to fetch the policies before calling

Let me verify, that that is correct and working.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified!

}
}
}

class WindowsGPOPoliciesProvider {
Expand Down