summaryrefslogtreecommitdiffstats
path: root/browser/components/preferences/tests/browser_privacy_dnsoverhttps.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/preferences/tests/browser_privacy_dnsoverhttps.js')
-rw-r--r--browser/components/preferences/tests/browser_privacy_dnsoverhttps.js844
1 files changed, 844 insertions, 0 deletions
diff --git a/browser/components/preferences/tests/browser_privacy_dnsoverhttps.js b/browser/components/preferences/tests/browser_privacy_dnsoverhttps.js
new file mode 100644
index 0000000000..48469cfce4
--- /dev/null
+++ b/browser/components/preferences/tests/browser_privacy_dnsoverhttps.js
@@ -0,0 +1,844 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(4);
+
+const { EnterprisePolicyTesting, PoliciesPrefTracker } =
+ ChromeUtils.importESModule(
+ "resource://testing-common/EnterprisePolicyTesting.sys.mjs"
+ );
+
+ChromeUtils.defineESModuleGetters(this, {
+ DoHConfigController: "resource:///modules/DoHConfig.sys.mjs",
+ DoHController: "resource:///modules/DoHController.sys.mjs",
+ DoHTestUtils: "resource://testing-common/DoHTestUtils.sys.mjs",
+});
+
+const TRR_MODE_PREF = "network.trr.mode";
+const TRR_URI_PREF = "network.trr.uri";
+const TRR_CUSTOM_URI_PREF = "network.trr.custom_uri";
+const ROLLOUT_ENABLED_PREF = "doh-rollout.enabled";
+const ROLLOUT_SELF_ENABLED_PREF = "doh-rollout.self-enabled";
+const HEURISTICS_DISABLED_PREF = "doh-rollout.disable-heuristics";
+const FIRST_RESOLVER_VALUE = DoHTestUtils.providers[0].uri;
+const SECOND_RESOLVER_VALUE = DoHTestUtils.providers[1].uri;
+const DEFAULT_RESOLVER_VALUE = FIRST_RESOLVER_VALUE;
+
+const defaultPrefValues = Object.freeze({
+ [TRR_MODE_PREF]: 0,
+ [TRR_CUSTOM_URI_PREF]: "",
+});
+
+// See bug 1741554. This test should not actually try to create a connection to
+// the real DoH endpoint. But a background request could do that while the test
+// is in progress, before we've actually disabled TRR, and would cause a crash
+// due to connecting to a non-local IP.
+// To prevent that we override the IP to a local address.
+Cc["@mozilla.org/network/native-dns-override;1"]
+ .getService(Ci.nsINativeDNSResolverOverride)
+ .addIPOverride("mozilla.cloudflare-dns.com", "127.0.0.1");
+
+async function clearEvents() {
+ Services.telemetry.clearEvents();
+ await TestUtils.waitForCondition(() => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_ALL_CHANNELS,
+ true
+ ).parent;
+ return !events || !events.length;
+ });
+}
+
+async function getEvent(filter1, filter2) {
+ let event = await TestUtils.waitForCondition(() => {
+ let events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_ALL_CHANNELS,
+ true
+ ).parent;
+ return events?.find(e => e[1] == filter1 && e[2] == filter2);
+ }, "recorded telemetry for the load");
+ event.shift();
+ return event;
+}
+
+async function resetPrefs() {
+ await DoHTestUtils.resetRemoteSettingsConfig();
+ await DoHController._uninit();
+ Services.prefs.clearUserPref(TRR_MODE_PREF);
+ Services.prefs.clearUserPref(TRR_URI_PREF);
+ Services.prefs.clearUserPref(TRR_CUSTOM_URI_PREF);
+ Services.prefs.getChildList("doh-rollout.").forEach(pref => {
+ Services.prefs.clearUserPref(pref);
+ });
+ // Clear out any telemetry events generated by DoHController so that we don't
+ // confuse tests running after this one that are looking at those.
+ Services.telemetry.clearEvents();
+ await DoHController.init();
+}
+Services.prefs.setStringPref("network.trr.confirmationNS", "skip");
+
+registerCleanupFunction(async () => {
+ await resetPrefs();
+ Services.prefs.clearUserPref("network.trr.confirmationNS");
+});
+
+add_setup(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["toolkit.telemetry.testing.overrideProductsCheck", true]],
+ });
+
+ await DoHTestUtils.resetRemoteSettingsConfig();
+});
+
+function waitForPrefObserver(name) {
+ return new Promise(resolve => {
+ const observer = {
+ observe(aSubject, aTopic, aData) {
+ if (aData == name) {
+ Services.prefs.removeObserver(name, observer);
+ resolve();
+ }
+ },
+ };
+ Services.prefs.addObserver(name, observer);
+ });
+}
+
+async function testWithProperties(props, startTime) {
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: testing with " +
+ JSON.stringify(props)
+ );
+
+ // There are two different signals that the DoHController is ready, depending
+ // on the config being tested. If we're setting the TRR mode pref, we should
+ // expect the disable-heuristics pref to be set as the signal. Else, we can
+ // expect the self-enabled pref as the signal.
+ let rolloutReadyPromise;
+ if (props.hasOwnProperty(TRR_MODE_PREF)) {
+ if (
+ [2, 3, 5].includes(props[TRR_MODE_PREF]) &&
+ props.hasOwnProperty(ROLLOUT_ENABLED_PREF)
+ ) {
+ // Only initialize the promise if we're going to enable the rollout -
+ // otherwise we will never await it, which could cause a leak if it doesn't
+ // end up resolving.
+ rolloutReadyPromise = waitForPrefObserver(HEURISTICS_DISABLED_PREF);
+ }
+ Services.prefs.setIntPref(TRR_MODE_PREF, props[TRR_MODE_PREF]);
+ }
+ if (props.hasOwnProperty(ROLLOUT_ENABLED_PREF)) {
+ if (!rolloutReadyPromise) {
+ rolloutReadyPromise = waitForPrefObserver(ROLLOUT_SELF_ENABLED_PREF);
+ }
+ Services.prefs.setBoolPref(
+ ROLLOUT_ENABLED_PREF,
+ props[ROLLOUT_ENABLED_PREF]
+ );
+ await rolloutReadyPromise;
+ }
+ if (props.hasOwnProperty(TRR_CUSTOM_URI_PREF)) {
+ Services.prefs.setStringPref(
+ TRR_CUSTOM_URI_PREF,
+ props[TRR_CUSTOM_URI_PREF]
+ );
+ }
+ if (props.hasOwnProperty(TRR_URI_PREF)) {
+ Services.prefs.setStringPref(TRR_URI_PREF, props[TRR_URI_PREF]);
+ }
+
+ await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+ let doc = gBrowser.selectedBrowser.contentDocument;
+
+ info(Date.now() - startTime + ": testWithProperties: tab now open");
+ let modeRadioGroup = doc.getElementById("dohCategoryRadioGroup");
+ let uriTextbox = doc.getElementById("dohEnabledInputField");
+ let resolverMenulist = doc.getElementById("dohStrictResolverChoices");
+ let modePrefChangedPromise;
+ let uriPrefChangedPromise;
+ let disableHeuristicsPrefChangedPromise;
+
+ modeRadioGroup.scrollIntoView();
+
+ if (props.hasOwnProperty("expectedSelectedIndex")) {
+ await TestUtils.waitForCondition(
+ () => modeRadioGroup.selectedIndex === props.expectedSelectedIndex
+ );
+ is(
+ modeRadioGroup.selectedIndex,
+ props.expectedSelectedIndex,
+ "dohCategoryRadioGroup has expected selected index"
+ );
+ }
+ if (props.hasOwnProperty("expectedUriValue")) {
+ await TestUtils.waitForCondition(
+ () => uriTextbox.value === props.expectedUriValue
+ );
+ is(
+ uriTextbox.value,
+ props.expectedUriValue,
+ "URI textbox has expected value"
+ );
+ }
+ if (props.hasOwnProperty("expectedResolverListValue")) {
+ await TestUtils.waitForCondition(
+ () => resolverMenulist.value === props.expectedResolverListValue
+ );
+ is(
+ resolverMenulist.value,
+ props.expectedResolverListValue,
+ "resolver menulist has expected value"
+ );
+ }
+
+ if (props.clickMode) {
+ await clearEvents();
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: clickMode, waiting for the pref observer"
+ );
+ modePrefChangedPromise = waitForPrefObserver(TRR_MODE_PREF);
+ if (props.hasOwnProperty("expectedDisabledHeuristics")) {
+ disableHeuristicsPrefChangedPromise = waitForPrefObserver(
+ HEURISTICS_DISABLED_PREF
+ );
+ }
+ info(
+ Date.now() - startTime + ": testWithProperties: clickMode, pref changed"
+ );
+ let option = doc.getElementById(props.clickMode);
+ option.scrollIntoView();
+ let win = doc.ownerGlobal;
+ EventUtils.synthesizeMouseAtCenter(option, {}, win);
+ info(
+ `${Date.now() - startTime} : testWithProperties: clickMode=${
+ props.clickMode
+ }, mouse click synthesized`
+ );
+ let clickEvent = await getEvent("security.doh.settings", "mode_changed");
+ Assert.deepEqual(clickEvent, [
+ "security.doh.settings",
+ "mode_changed",
+ "button",
+ props.clickMode,
+ ]);
+ }
+ if (props.hasOwnProperty("selectResolver")) {
+ await clearEvents();
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: selectResolver, creating change event"
+ );
+ resolverMenulist.focus();
+ resolverMenulist.value = props.selectResolver;
+ resolverMenulist.dispatchEvent(new Event("input", { bubbles: true }));
+ resolverMenulist.dispatchEvent(new Event("command", { bubbles: true }));
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: selectResolver, item value set and events dispatched"
+ );
+ let choiceEvent = await getEvent(
+ "security.doh.settings",
+ "provider_choice"
+ );
+ Assert.deepEqual(choiceEvent, [
+ "security.doh.settings",
+ "provider_choice",
+ "value",
+ props.selectResolver,
+ ]);
+ }
+ if (props.hasOwnProperty("inputUriKeys")) {
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: inputUriKeys, waiting for the pref observer"
+ );
+ uriPrefChangedPromise = waitForPrefObserver(TRR_CUSTOM_URI_PREF);
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: inputUriKeys, pref changed, now enter the new value"
+ );
+ let win = doc.ownerGlobal;
+ uriTextbox.focus();
+ uriTextbox.value = props.inputUriKeys;
+ uriTextbox.dispatchEvent(new win.Event("input", { bubbles: true }));
+ uriTextbox.dispatchEvent(new win.Event("change", { bubbles: true }));
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: inputUriKeys, input and change events dispatched"
+ );
+ }
+
+ info(
+ Date.now() -
+ startTime +
+ ": testWithProperties: waiting for any of uri and mode prefs to change"
+ );
+ await Promise.all([
+ uriPrefChangedPromise,
+ modePrefChangedPromise,
+ disableHeuristicsPrefChangedPromise,
+ ]);
+ info(Date.now() - startTime + ": testWithProperties: prefs changed");
+
+ if (props.hasOwnProperty("expectedFinalUriPref")) {
+ if (props.expectedFinalUriPref) {
+ let uriPref = Services.prefs.getStringPref(TRR_URI_PREF);
+ is(
+ uriPref,
+ props.expectedFinalUriPref,
+ "uri pref ended up with the expected value"
+ );
+ } else {
+ ok(
+ !Services.prefs.prefHasUserValue(TRR_URI_PREF),
+ `uri pref ended up with the expected value (unset) got ${Services.prefs.getStringPref(
+ TRR_URI_PREF
+ )}`
+ );
+ }
+ }
+
+ if (props.hasOwnProperty("expectedModePref")) {
+ let modePref = Services.prefs.getIntPref(TRR_MODE_PREF);
+ is(
+ modePref,
+ props.expectedModePref,
+ "mode pref ended up with the expected value"
+ );
+ }
+
+ if (props.hasOwnProperty("expectedDisabledHeuristics")) {
+ let disabledHeuristicsPref = Services.prefs.getBoolPref(
+ HEURISTICS_DISABLED_PREF
+ );
+ is(
+ disabledHeuristicsPref,
+ props.expectedDisabledHeuristics,
+ "disable-heuristics pref ended up with the expected value"
+ );
+ }
+
+ if (props.hasOwnProperty("expectedFinalCustomUriPref")) {
+ let customUriPref = Services.prefs.getStringPref(TRR_CUSTOM_URI_PREF);
+ is(
+ customUriPref,
+ props.expectedFinalCustomUriPref,
+ "custom_uri pref ended up with the expected value"
+ );
+ }
+
+ if (props.hasOwnProperty("expectedModeValue")) {
+ let modeValue = Services.prefs.getIntPref(TRR_MODE_PREF);
+ is(modeValue, props.expectedModeValue, "mode pref has expected value");
+ }
+
+ gBrowser.removeCurrentTab();
+ info(Date.now() - startTime + ": testWithProperties: fin");
+}
+
+add_task(async function default_values() {
+ let customUriPref = Services.prefs.getStringPref(TRR_CUSTOM_URI_PREF);
+ let uriPrefHasUserValue = Services.prefs.prefHasUserValue(TRR_URI_PREF);
+ let modePref = Services.prefs.getIntPref(TRR_MODE_PREF);
+ is(
+ modePref,
+ defaultPrefValues[TRR_MODE_PREF],
+ `Actual value of ${TRR_MODE_PREF} matches expected default value`
+ );
+ ok(
+ !uriPrefHasUserValue,
+ `Actual value of ${TRR_URI_PREF} matches expected default value (unset)`
+ );
+ is(
+ customUriPref,
+ defaultPrefValues[TRR_CUSTOM_URI_PREF],
+ `Actual value of ${TRR_CUSTOM_URI_PREF} matches expected default value`
+ );
+});
+
+const DEFAULT_OPTION_INDEX = 0;
+const ENABLED_OPTION_INDEX = 1;
+const STRICT_OPTION_INDEX = 2;
+const OFF_OPTION_INDEX = 3;
+
+let testVariations = [
+ // verify state with defaults
+ {
+ name: "default",
+ expectedModePref: 0,
+ expectedSelectedIndex: DEFAULT_OPTION_INDEX,
+ expectedUriValue: "",
+ },
+
+ // verify each of the modes maps to the correct checked state
+ {
+ name: "mode 0",
+ [TRR_MODE_PREF]: 0,
+ expectedSelectedIndex: DEFAULT_OPTION_INDEX,
+ },
+ {
+ name: "mode 1",
+ [TRR_MODE_PREF]: 1,
+ expectedSelectedIndex: OFF_OPTION_INDEX,
+ },
+ {
+ name: "mode 2",
+ [TRR_MODE_PREF]: 2,
+ expectedSelectedIndex: ENABLED_OPTION_INDEX,
+ expectedFinalUriPref: "",
+ },
+ {
+ name: "mode 3",
+ [TRR_MODE_PREF]: 3,
+ expectedSelectedIndex: STRICT_OPTION_INDEX,
+ expectedFinalUriPref: "",
+ },
+ {
+ name: "mode 4",
+ [TRR_MODE_PREF]: 4,
+ expectedSelectedIndex: OFF_OPTION_INDEX,
+ },
+ {
+ name: "mode 5",
+ [TRR_MODE_PREF]: 5,
+ expectedSelectedIndex: OFF_OPTION_INDEX,
+ },
+ // verify an out of bounds mode value maps to the correct checked state
+ {
+ name: "mode out-of-bounds",
+ [TRR_MODE_PREF]: 77,
+ expectedSelectedIndex: OFF_OPTION_INDEX,
+ },
+
+ // verify automatic heuristics states
+ {
+ name: "heuristics on and mode unset",
+ [TRR_MODE_PREF]: 0,
+ [ROLLOUT_ENABLED_PREF]: true,
+ expectedSelectedIndex: DEFAULT_OPTION_INDEX,
+ },
+ {
+ name: "heuristics on and mode set to 2",
+ [TRR_MODE_PREF]: 2,
+ [ROLLOUT_ENABLED_PREF]: true,
+ expectedSelectedIndex: ENABLED_OPTION_INDEX,
+ },
+ {
+ name: "heuristics on but disabled, mode unset",
+ [TRR_MODE_PREF]: 5,
+ [ROLLOUT_ENABLED_PREF]: true,
+ expectedSelectedIndex: OFF_OPTION_INDEX,
+ },
+ {
+ name: "heuristics on but disabled, mode set to 2",
+ [TRR_MODE_PREF]: 2,
+ [ROLLOUT_ENABLED_PREF]: true,
+ expectedSelectedIndex: ENABLED_OPTION_INDEX,
+ },
+
+ // verify picking each radio button option gives the right outcomes
+ {
+ name: "toggle mode on",
+ clickMode: "dohEnabledRadio",
+ expectedModeValue: 2,
+ expectedUriValue: "",
+ expectedFinalUriPref: "",
+ },
+ {
+ name: "toggle mode off",
+ [TRR_MODE_PREF]: 2,
+ expectedSelectedIndex: ENABLED_OPTION_INDEX,
+ clickMode: "dohOffRadio",
+ expectedModePref: 5,
+ },
+ {
+ name: "toggle mode off when on due to heuristics",
+ [TRR_MODE_PREF]: 0,
+ [ROLLOUT_ENABLED_PREF]: true,
+ expectedSelectedIndex: DEFAULT_OPTION_INDEX,
+ clickMode: "dohOffRadio",
+ expectedModePref: 5,
+ expectedDisabledHeuristics: true,
+ },
+ // Test selecting non-default, non-custom TRR provider, NextDNS.
+ {
+ name: "Select NextDNS as TRR provider",
+ [TRR_MODE_PREF]: 2,
+ selectResolver: SECOND_RESOLVER_VALUE,
+ expectedFinalUriPref: SECOND_RESOLVER_VALUE,
+ },
+ // Test selecting non-default, non-custom TRR provider, NextDNS,
+ // with DoH not enabled. The provider selection should stick.
+ {
+ name: "Select NextDNS as TRR provider in mode 0",
+ [TRR_MODE_PREF]: 0,
+ selectResolver: SECOND_RESOLVER_VALUE,
+ expectedFinalUriPref: SECOND_RESOLVER_VALUE,
+ },
+ {
+ name: "return to default from NextDNS",
+ [TRR_MODE_PREF]: 2,
+ [TRR_URI_PREF]: SECOND_RESOLVER_VALUE,
+ expectedResolverListValue: SECOND_RESOLVER_VALUE,
+ selectResolver: DEFAULT_RESOLVER_VALUE,
+ expectedFinalUriPref: FIRST_RESOLVER_VALUE,
+ },
+ // test that selecting Custom, when we have a TRR_CUSTOM_URI_PREF subsequently changes TRR_URI_PREF
+ {
+ name: "select custom with existing custom_uri pref value",
+ [TRR_MODE_PREF]: 2,
+ [TRR_CUSTOM_URI_PREF]: "https://example.com",
+ expectedModeValue: 2,
+ expectedSelectedIndex: ENABLED_OPTION_INDEX,
+ selectResolver: "custom",
+ expectedUriValue: "https://example.com",
+ expectedFinalUriPref: "https://example.com",
+ expectedFinalCustomUriPref: "https://example.com",
+ },
+ {
+ name: "select custom and enter new custom_uri pref value",
+ [TRR_URI_PREF]: "",
+ [TRR_CUSTOM_URI_PREF]: "",
+ clickMode: "dohEnabledRadio",
+ selectResolver: "custom",
+ inputUriKeys: "https://custom.com",
+ expectedModePref: 2,
+ expectedFinalUriPref: "https://custom.com",
+ expectedFinalCustomUriPref: "https://custom.com",
+ },
+
+ {
+ name: "return to default from custom",
+ [TRR_MODE_PREF]: 2,
+ [TRR_URI_PREF]: "https://example.com",
+ [TRR_CUSTOM_URI_PREF]: "https://custom.com",
+ expectedUriValue: "https://example.com",
+ expectedResolverListValue: "custom",
+ selectResolver: DEFAULT_RESOLVER_VALUE,
+ expectedFinalUriPref: DEFAULT_RESOLVER_VALUE,
+ expectedFinalCustomUriPref: "https://example.com",
+ },
+ {
+ name: "clear the custom uri",
+ [TRR_MODE_PREF]: 2,
+ [TRR_URI_PREF]: "https://example.com",
+ [TRR_CUSTOM_URI_PREF]: "https://example.com",
+ expectedUriValue: "https://example.com",
+ expectedResolverListValue: "custom",
+ inputUriKeys: "",
+ expectedFinalUriPref: " ",
+ expectedFinalCustomUriPref: "",
+ },
+ {
+ name: "empty default resolver list",
+ [TRR_MODE_PREF]: 2,
+ [TRR_URI_PREF]: "https://example.com",
+ [TRR_CUSTOM_URI_PREF]: "",
+ expectedUriValue: "https://example.com",
+ expectedResolverListValue: "custom",
+ expectedFinalUriPref: "https://example.com",
+ expectedFinalCustomUriPref: "https://example.com",
+ },
+];
+
+for (let props of testVariations) {
+ add_task(async function testVariation() {
+ let startTime = Date.now();
+ info("starting test: " + props.name);
+ await testWithProperties(props, startTime);
+ await resetPrefs();
+ });
+}
+
+add_task(async function testRemoteSettingsEnable() {
+ let startTime = Date.now();
+ // Enable the rollout.
+ await DoHTestUtils.loadRemoteSettingsConfig({
+ providers: "example-1, example-2",
+ rolloutEnabled: true,
+ steeringEnabled: false,
+ steeringProviders: "",
+ autoDefaultEnabled: false,
+ autoDefaultProviders: "",
+ id: "global",
+ });
+
+ await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+ let doc = gBrowser.selectedBrowser.contentDocument;
+
+ info(Date.now() - startTime + ": testWithProperties: tab now open");
+ let modeRadioGroup = doc.getElementById("dohCategoryRadioGroup");
+
+ is(modeRadioGroup.value, "0", "expecting default mode");
+
+ let status = doc.getElementById("dohStatus");
+ await TestUtils.waitForCondition(
+ () => document.l10n.getAttributes(status).args.status == "Active"
+ );
+ is(
+ document.l10n.getAttributes(status).args.status,
+ "Active",
+ "expecting status active"
+ );
+
+ let provider = doc.getElementById("dohResolver");
+ is(
+ provider.hidden,
+ false,
+ "Provider should not be hidden when status is active"
+ );
+ await TestUtils.waitForCondition(
+ () =>
+ document.l10n.getAttributes(provider).args.name ==
+ DoHConfigController.currentConfig.providerList[0].UIName
+ );
+ is(
+ document.l10n.getAttributes(provider).args.name,
+ DoHConfigController.currentConfig.providerList[0].UIName,
+ "expecting the right provider name"
+ );
+
+ let option = doc.getElementById("dohEnabledRadio");
+ option.scrollIntoView();
+ let win = doc.ownerGlobal;
+ EventUtils.synthesizeMouseAtCenter(option, {}, win);
+
+ await TestUtils.waitForCondition(() =>
+ Services.prefs.prefHasUserValue("doh-rollout.disable-heuristics")
+ );
+ is(provider.hidden, false);
+ await TestUtils.waitForCondition(
+ () =>
+ document.l10n.getAttributes(provider).args.name ==
+ DoHConfigController.currentConfig.providerList[0].UIName
+ );
+ is(
+ document.l10n.getAttributes(provider).args.name,
+ DoHConfigController.currentConfig.providerList[0].UIName,
+ "expecting the right provider name"
+ );
+ is(
+ Services.prefs.getIntPref("network.trr.mode"),
+ Ci.nsIDNSService.MODE_TRRFIRST
+ );
+
+ option = doc.getElementById("dohOffRadio");
+ option.scrollIntoView();
+ win = doc.ownerGlobal;
+ EventUtils.synthesizeMouseAtCenter(option, {}, win);
+ await TestUtils.waitForCondition(() => status.innerHTML == "Status: Off");
+ is(
+ Services.prefs.getIntPref("network.trr.mode"),
+ Ci.nsIDNSService.MODE_TRROFF
+ );
+ is(provider.hidden, true, "Expecting provider to be hidden when DoH is off");
+
+ gBrowser.removeCurrentTab();
+});
+
+add_task(async function testEnterprisePolicy() {
+ async function withPolicy(policy, fn, preFn = () => {}) {
+ await resetPrefs();
+ PoliciesPrefTracker.start();
+ await EnterprisePolicyTesting.setupPolicyEngineWithJson(policy);
+ await preFn();
+
+ await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+ let doc = gBrowser.selectedBrowser.contentDocument;
+
+ let modeRadioGroup = doc.getElementById("dohCategoryRadioGroup");
+ let resolverMenulist = doc.getElementById("dohEnabledResolverChoices");
+ let uriTextbox = doc.getElementById("dohEnabledInputField");
+
+ await fn({
+ modeRadioGroup,
+ resolverMenulist,
+ doc,
+ uriTextbox,
+ });
+
+ gBrowser.removeCurrentTab();
+ EnterprisePolicyTesting.resetRunOnceState();
+ PoliciesPrefTracker.stop();
+ }
+
+ info("Check that a locked policy does not allow any changes in the UI");
+ await withPolicy(
+ {
+ policies: {
+ DNSOverHTTPS: {
+ Enabled: true,
+ ProviderURL: "https://examplelocked.com/provider",
+ ExcludedDomains: ["examplelocked.com", "example.org"],
+ Locked: true,
+ },
+ },
+ },
+ async res => {
+ is(res.modeRadioGroup.disabled, true, "The mode menu should be locked.");
+ is(res.modeRadioGroup.value, "2", "Should be enabled");
+ is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
+ is(
+ res.uriTextbox.value,
+ "https://examplelocked.com/provider",
+ "Custom URI should be set"
+ );
+ }
+ );
+
+ info("Check that an unlocked policy has editable fields in the dialog");
+ await withPolicy(
+ {
+ policies: {
+ DNSOverHTTPS: {
+ Enabled: true,
+ ProviderURL: "https://example.com/provider",
+ ExcludedDomains: ["example.com", "example.org"],
+ },
+ },
+ },
+ async res => {
+ is(
+ res.modeRadioGroup.disabled,
+ false,
+ "The mode menu should not be locked."
+ );
+ is(res.modeRadioGroup.value, "2", "Should be enabled");
+ is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
+ is(
+ res.uriTextbox.value,
+ "https://example.com/provider",
+ "Expected custom resolver"
+ );
+ }
+ );
+
+ info("Check that a locked disabled policy disables the buttons");
+ await withPolicy(
+ {
+ policies: {
+ DNSOverHTTPS: {
+ Enabled: false,
+ ProviderURL: "https://example.com/provider",
+ ExcludedDomains: ["example.com", "example.org"],
+ Locked: true,
+ },
+ },
+ },
+ async res => {
+ is(res.modeRadioGroup.disabled, true, "The mode menu should be locked.");
+ is(res.modeRadioGroup.value, "5", "Should be disabled");
+ }
+ );
+
+ info("Check that an unlocked disabled policy has editable fields");
+ await withPolicy(
+ {
+ policies: {
+ DNSOverHTTPS: {
+ Enabled: false,
+ ProviderURL: "https://example.com/provider",
+ ExcludedDomains: ["example.com", "example.org"],
+ },
+ },
+ },
+ async res => {
+ is(
+ res.modeRadioGroup.disabled,
+ false,
+ "The mode menu should not be locked."
+ );
+ is(res.modeRadioGroup.value, "5", "Should be disabled");
+ }
+ );
+
+ info("Check that the remote settings config doesn't override the policy");
+ await withPolicy(
+ {
+ policies: {
+ DNSOverHTTPS: {
+ Enabled: true,
+ ProviderURL: "https://example.com/provider",
+ ExcludedDomains: ["example.com", "example.org"],
+ },
+ },
+ },
+ async res => {
+ is(
+ res.modeRadioGroup.disabled,
+ false,
+ "The mode menu should not be locked."
+ );
+ is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
+ is(
+ res.uriTextbox.value,
+ "https://example.com/provider",
+ "Expected custom resolver"
+ );
+ },
+ async function runAfterSettingPolicy() {
+ await DoHTestUtils.loadRemoteSettingsConfig({
+ providers: "example-1, example-2",
+ rolloutEnabled: true,
+ steeringEnabled: false,
+ steeringProviders: "",
+ autoDefaultEnabled: false,
+ autoDefaultProviders: "",
+ id: "global",
+ });
+ }
+ );
+});
+
+add_task(async function clickWarnButton() {
+ Services.prefs.setBoolPref(
+ "network.trr_ui.show_fallback_warning_option",
+ true
+ );
+
+ await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+ let doc = gBrowser.selectedBrowser.contentDocument;
+
+ await clearEvents();
+ let checkbox = doc.getElementById("dohWarnCheckbox1");
+ checkbox.click();
+
+ let event = await getEvent("security.doh.settings", "warn_checkbox");
+ Assert.deepEqual(event, [
+ "security.doh.settings",
+ "warn_checkbox",
+ "checkbox",
+ "true",
+ ]);
+ Assert.equal(
+ Services.prefs.getBoolPref("network.trr.display_fallback_warning"),
+ true,
+ "Clicking the checkbox should change the pref"
+ );
+
+ checkbox.click();
+ event = await getEvent("security.doh.settings", "warn_checkbox");
+ Assert.deepEqual(event, [
+ "security.doh.settings",
+ "warn_checkbox",
+ "checkbox",
+ "false",
+ ]);
+ Assert.equal(
+ Services.prefs.getBoolPref("network.trr.display_fallback_warning"),
+ false,
+ "Clicking the checkbox should change the pref"
+ );
+ gBrowser.removeCurrentTab();
+});