Skip to content

Conversation

@1rneh
Copy link
Contributor

@1rneh 1rneh commented Jan 8, 2026

  • Refactor updating remote policies
  • Fix merging policies of a combined provider when remote policies are updated

);

if (!isValid) {
continue;
Copy link
Contributor

@gcp gcp Jan 9, 2026

Choose a reason for hiding this comment

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

This will cause the policy to be removed because it doesn't go in parsedPolicies, unclear if that is what you want.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! I didn't change that behavior. But let's re-think this :)

We have two options when encountering invalid parameters:

  • Policy is currently active: We can remove the policy or keep it active with unchanged parameters. I think I prefer the latter. It's seems more intentional. In both ways we can warn in the logs about invalid parameters.
  • Policy is currently inactive: Do nothing.

Copy link
Member

Choose a reason for hiding this comment

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

This function is only called during initialization though so the policy cannot already be active here. The question is what happens later when the policies are updated if this invalid policy isn't listed in _parsedPolicies. It looks to me like you don't want it in _parsedPolicies because that would then cause us to try to call an onRemove handler for the policy even though we haven't actually applied it.

@1rneh 1rneh requested a review from Mossop January 22, 2026 15:12
@1rneh 1rneh requested a review from gcp January 22, 2026 16:00
@1rneh
Copy link
Contributor Author

1rneh commented Jan 22, 2026

Update: This patch includes:

  • Refactor updating remote policies
  • Fix merging policies of a combined provider when remote policies are updated
  • Test merging local and remote policies

Question:
What should we do with the tests under browser/components/enterprisepolicies? Is it enough to run them with local policies?

@1rneh 1rneh requested a review from mkaply January 22, 2026 17:21
},

get status() {
return this._status;
Copy link
Contributor

Choose a reason for hiding this comment

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

Given how error prone updating _status is, maybe we can add some sanity checks here for inconsistent status?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why is _status error prone?

@lissyx
Copy link
Contributor

lissyx commented Jan 23, 2026

Question: What should we do with the tests under browser/components/enterprisepolicies? Is it enough to run them with local policies?

(I have not had a look at the PR yet so this may be invalid) From past experience I ran several times in regressions I introduced with remote fetching that broke some policies. So I think we should try and run with both, but maybe not right now, and maybe we'd like to scope them in a flagged suite, maybe enterprise subsuite for mochtest browser and xpcshell ?

@1rneh 1rneh requested review from Mossop and gcp February 9, 2026 13:52
@1rneh
Copy link
Contributor Author

1rneh commented Feb 9, 2026

What should we do with the tests under browser/components/enterprisepolicies? Is it enough to run them with local policies?

From an offline conversation with @mkaply:

Tests under /browser/components/enterprisepolicies/ cover each policy logic. Tests under /toolkit/components/enterprisepolicies/ test the policy engine. We should test whether each policy is applied correctly when provided remotely after startup (not during engine initialization).

@1rneh
Copy link
Contributor Author

1rneh commented Feb 12, 2026

This PR is growing and entails quite a lot of entangled changes and therefore difficult to split into multiple patches:

Note:
Leave out the tests in your review for now. I just noticed that I have removed Testing.servePolicyWithJson but it's still used in the /browser/components/enterprisepolicies/ tests. I will have to fix this first.

},
};

let policyValue = POLICY_PARAM_STATE.DEFAULT;
Copy link
Member

Choose a reason for hiding this comment

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

Throughout this test you verify the state of the active policies but you should sprinkle in checks that policyValue has the expected state after updates.

Comment on lines +1186 to 1195
// Combine policies with primaryProvider taking precedence.
// We only do this for top level policies.
this._policies = this._primary._policies;
for (let policyName of Object.keys(this._secondary.policies)) {
this._policies = structuredClone(this._primaryProvider.policies);
for (let [policyName, policyParams] of Object.entries(
this._secondaryProvider.policies || {}
)) {
if (!(policyName in this._policies)) {
this._policies[policyName] = this._secondary.policies[policyName];
this._policies[policyName] = policyParams;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Combine policies with primaryProvider taking precedence.
// We only do this for top level policies.
this._policies = this._primary._policies;
for (let policyName of Object.keys(this._secondary.policies)) {
this._policies = structuredClone(this._primaryProvider.policies);
for (let [policyName, policyParams] of Object.entries(
this._secondaryProvider.policies || {}
)) {
if (!(policyName in this._policies)) {
this._policies[policyName] = this._secondary.policies[policyName];
this._policies[policyName] = policyParams;
}
}
// Combine policies with primaryProvider taking precedence.
// We only do this for top level policies.
return Object.assign({}, this._secondaryProvider.policies ?? {}, this._primaryProvider.policies);

Copy link
Member

Choose a reason for hiding this comment

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

I think this is just a manual implementation of Object.assign

Comment on lines +202 to +219
_chooseProvider() {
let platformProvider = null;
if (AppConstants.platform == "win" && AppConstants.MOZ_SYSTEM_POLICIES) {
platformProvider = new WindowsGPOPoliciesProvider();
platformProvider.onPoliciesChanges(handler);
} else if (
AppConstants.platform == "macosx" &&
AppConstants.MOZ_SYSTEM_POLICIES
) {
platformProvider = new macOSPoliciesProvider();
platformProvider.onPoliciesChanges(handler);
if (AppConstants.MOZ_SYSTEM_POLICIES) {
if (AppConstants.platform == "win") {
platformProvider = new WindowsGPOPoliciesProvider();
} else if (AppConstants.platform == "macosx") {
platformProvider = new macOSPoliciesProvider();
}
}

let jsonProvider = new JSONPoliciesProvider();
jsonProvider.onPoliciesChanges(handler);
let remoteProvider = RemotePoliciesProvider.createInstance();
remoteProvider.onPoliciesChanges(handler);
if (platformProvider && platformProvider.hasPolicies) {
if (jsonProvider.hasPolicies) {
return new CombinedProvider(
new CombinedProvider(remoteProvider, platformProvider),
jsonProvider
);
return new CombinedProvider(platformProvider, jsonProvider);
}
return new CombinedProvider(remoteProvider, platformProvider);
return platformProvider;
}
if (jsonProvider.hasPolicies) {
return new CombinedProvider(remoteProvider, jsonProvider);
return jsonProvider;
},
Copy link
Member

Choose a reason for hiding this comment

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

You don't have to do it here but I think things would make a lot more sense if instead of having CombinedProvider merge two policies it could instead merge any number of policies. Then instead of a binary tree of policy providers there would just be a single CombinedProfiler holding all the real providers and it would be able to merge them all in a single operation.

);

if (!isValid) {
continue;
Copy link
Member

Choose a reason for hiding this comment

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

This function is only called during initialization though so the policy cannot already be active here. The question is what happens later when the policies are updated if this invalid policy isn't listed in _parsedPolicies. It looks to me like you don't want it in _parsedPolicies because that would then cause us to try to call an onRemove handler for the policy even though we haven't actually applied it.

);

if (!isValid) {
console.warn(`Parameters for policy ${policyName} are invalid`);
Copy link
Member

Choose a reason for hiding this comment

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

_validatePolicyParams already logs warnings in the event of invalid data so we don't need to do it here.

break;

case "EnterprisePolicies:Reset":
this._resetEngine().then(null, console.error);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
this._resetEngine().then(null, console.error);
this._resetEngine().catch(console.error);

/**
* Update engine status after parsing a new set of policies
*/
_updateStatus() {
Copy link
Member

Choose a reason for hiding this comment

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

This should set the status to FAILED if this._provider.failed

Copy link
Contributor

@mkaply mkaply left a comment

Choose a reason for hiding this comment

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

I've asked some questions and made a few suggestions, but assuming this pasts the tests (and you've addressed Mossop's questions), this seems good.

// of setAndLockPref, it wasn't locked, but calling
// unlockPref is harmless
Preferences.unlock(prefName);
if (Services.prefs.prefIsLocked(prefName)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why was this added? As said above, Preference.unlock is harmless?

isTesting: "resource:///modules/enterprise/EnterpriseCommon.sys.mjs",
FeltCommon: "chrome://felt/content/FeltCommon.sys.mjs",
FeltStorage: "resource:///modules/FeltStorage.sys.mjs",
PREF_REMOTE_POLICIES_ENABLED:
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reason the same JS is loaded into two different identifiers?

"DisableDefaultBrowserAgent",
"DisableForgetButton",
"DisableFormHistory",
"DisableRemoteImprovements",
Copy link
Contributor

Choose a reason for hiding this comment

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

This is unrelated to this patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants