forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			798 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			798 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 SUBDIALOG_URL =
 | |
|   "chrome://browser/content/preferences/dialogs/connection.xhtml";
 | |
| 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 modeCheckboxSelector = "#networkDnsOverHttps";
 | |
| const uriTextboxSelector = "#networkCustomDnsOverHttpsInput";
 | |
| const resolverMenulistSelector = "#networkDnsOverHttpsResolverChoices";
 | |
| 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 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");
 | |
| let preferencesOpen = new Promise(res => open_preferences(res));
 | |
| 
 | |
| registerCleanupFunction(async () => {
 | |
|   await resetPrefs();
 | |
|   gBrowser.removeCurrentTab();
 | |
|   Services.prefs.clearUserPref("network.trr.confirmationNS");
 | |
| });
 | |
| 
 | |
| async function openConnectionsSubDialog() {
 | |
|   /*
 | |
|     The connection dialog has type="child", So it has to be opened as a sub dialog
 | |
|     of the main pref tab. Prefs only get updated after the subdialog is confirmed & closed
 | |
|   */
 | |
|   let dialog = await openAndLoadSubDialog(SUBDIALOG_URL);
 | |
|   ok(dialog, "connection window opened");
 | |
|   return dialog;
 | |
| }
 | |
| 
 | |
| 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]);
 | |
|   }
 | |
| 
 | |
|   let dialog = await openConnectionsSubDialog();
 | |
|   await dialog.uiReady;
 | |
|   info(
 | |
|     Date.now() - startTime + ": testWithProperties: connections dialog now open"
 | |
|   );
 | |
|   let doc = dialog.document;
 | |
|   let win = doc.ownerGlobal;
 | |
|   let dialogElement = doc.getElementById("ConnectionsDialog");
 | |
|   let dialogClosingPromise = BrowserTestUtils.waitForEvent(
 | |
|     dialogElement,
 | |
|     "dialogclosing"
 | |
|   );
 | |
|   let modeCheckbox = doc.querySelector(modeCheckboxSelector);
 | |
|   let uriTextbox = doc.querySelector(uriTextboxSelector);
 | |
|   let resolverMenulist = doc.querySelector(resolverMenulistSelector);
 | |
|   let uriPrefChangedPromise;
 | |
|   let modePrefChangedPromise;
 | |
|   let disableHeuristicsPrefChangedPromise;
 | |
| 
 | |
|   if (props.hasOwnProperty("expectedModeChecked")) {
 | |
|     await TestUtils.waitForCondition(
 | |
|       () => modeCheckbox.checked === props.expectedModeChecked
 | |
|     );
 | |
|     is(
 | |
|       modeCheckbox.checked,
 | |
|       props.expectedModeChecked,
 | |
|       "mode checkbox has expected checked state"
 | |
|     );
 | |
|   }
 | |
|   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) {
 | |
|     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"
 | |
|     );
 | |
|     modeCheckbox.scrollIntoView();
 | |
|     EventUtils.synthesizeMouseAtCenter(modeCheckbox, {}, win);
 | |
|     info(
 | |
|       Date.now() -
 | |
|         startTime +
 | |
|         ": testWithProperties: clickMode, mouse click synthesized"
 | |
|     );
 | |
|   }
 | |
|   if (props.hasOwnProperty("selectResolver")) {
 | |
|     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("change", { bubbles: true }));
 | |
|     info(
 | |
|       Date.now() -
 | |
|         startTime +
 | |
|         ": testWithProperties: selectResolver, item value set and events dispatched"
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   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"
 | |
|     );
 | |
|     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: calling acceptDialog");
 | |
|   dialogElement.acceptDialog();
 | |
| 
 | |
|   info(
 | |
|     Date.now() -
 | |
|       startTime +
 | |
|       ": testWithProperties: waiting for the dialogClosingPromise"
 | |
|   );
 | |
|   let dialogClosingEvent = await dialogClosingPromise;
 | |
|   ok(dialogClosingEvent, "connection window closed");
 | |
| 
 | |
|   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)"
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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("expectedFinalCusomUriPref")) {
 | |
|     let customUriPref = Services.prefs.getStringPref(TRR_CUSTOM_URI_PREF);
 | |
|     is(
 | |
|       customUriPref,
 | |
|       props.expectedFinalCustomUriPref,
 | |
|       "custom_uri pref ended up with the expected value"
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   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`
 | |
|   );
 | |
| });
 | |
| 
 | |
| let testVariations = [
 | |
|   // verify state with defaults
 | |
|   { name: "default", expectedModePref: 5, expectedUriValue: "" },
 | |
| 
 | |
|   // verify each of the modes maps to the correct checked state
 | |
|   { name: "mode 0", [TRR_MODE_PREF]: 0, expectedModeChecked: false },
 | |
|   {
 | |
|     name: "mode 1",
 | |
|     [TRR_MODE_PREF]: 1,
 | |
|     expectedModeChecked: false,
 | |
|   },
 | |
|   {
 | |
|     name: "mode 2",
 | |
|     [TRR_MODE_PREF]: 2,
 | |
|     expectedModeChecked: true,
 | |
|     expectedFinalUriPref: DEFAULT_RESOLVER_VALUE,
 | |
|   },
 | |
|   {
 | |
|     name: "mode 3",
 | |
|     [TRR_MODE_PREF]: 3,
 | |
|     expectedModeChecked: true,
 | |
|     expectedFinalUriPref: DEFAULT_RESOLVER_VALUE,
 | |
|   },
 | |
|   {
 | |
|     name: "mode 4",
 | |
|     [TRR_MODE_PREF]: 4,
 | |
|     expectedModeChecked: false,
 | |
|   },
 | |
|   { name: "mode 5", [TRR_MODE_PREF]: 5, expectedModeChecked: false },
 | |
|   // verify an out of bounds mode value maps to the correct checked state
 | |
|   {
 | |
|     name: "mode out-of-bounds",
 | |
|     [TRR_MODE_PREF]: 77,
 | |
|     expectedModeChecked: false,
 | |
|   },
 | |
| 
 | |
|   // verify automatic heuristics states
 | |
|   {
 | |
|     name: "heuristics on and mode unset",
 | |
|     [TRR_MODE_PREF]: 0,
 | |
|     [ROLLOUT_ENABLED_PREF]: true,
 | |
|     expectedModeChecked: true,
 | |
|   },
 | |
|   {
 | |
|     name: "heuristics on and mode set to 2",
 | |
|     [TRR_MODE_PREF]: 2,
 | |
|     [ROLLOUT_ENABLED_PREF]: true,
 | |
|     expectedModeChecked: true,
 | |
|   },
 | |
|   {
 | |
|     name: "heuristics on but disabled, mode unset",
 | |
|     [TRR_MODE_PREF]: 5,
 | |
|     [ROLLOUT_ENABLED_PREF]: true,
 | |
|     expectedModeChecked: false,
 | |
|   },
 | |
|   {
 | |
|     name: "heuristics on but disabled, mode set to 2",
 | |
|     [TRR_MODE_PREF]: 2,
 | |
|     [ROLLOUT_ENABLED_PREF]: true,
 | |
|     expectedModeChecked: true,
 | |
|   },
 | |
| 
 | |
|   // verify toggling the checkbox gives the right outcomes
 | |
|   {
 | |
|     name: "toggle mode on",
 | |
|     clickMode: true,
 | |
|     expectedModeValue: 2,
 | |
|     expectedUriValue: "",
 | |
|     expectedFinalUriPref: DEFAULT_RESOLVER_VALUE,
 | |
|   },
 | |
|   {
 | |
|     name: "toggle mode off",
 | |
|     [TRR_MODE_PREF]: 2,
 | |
|     expectedModeChecked: true,
 | |
|     clickMode: true,
 | |
|     expectedModePref: 5,
 | |
|   },
 | |
|   {
 | |
|     name: "toggle mode off when on due to heuristics",
 | |
|     [TRR_MODE_PREF]: 0,
 | |
|     [ROLLOUT_ENABLED_PREF]: true,
 | |
|     expectedModeChecked: true,
 | |
|     clickMode: true,
 | |
|     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: DEFAULT_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: true,
 | |
|     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: true,
 | |
|     selectResolver: "custom",
 | |
|     inputUriKeys: "https://example.com",
 | |
|     expectedModePref: 2,
 | |
|     expectedFinalUriPref: "https://example.com",
 | |
|     expectedFinalCustomUriPref: "https://example.com",
 | |
|   },
 | |
| 
 | |
|   {
 | |
|     name: "return to default from custom",
 | |
|     [TRR_MODE_PREF]: 2,
 | |
|     [TRR_URI_PREF]: "https://example.com",
 | |
|     [TRR_CUSTOM_URI_PREF]: "https://example.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: DEFAULT_RESOLVER_VALUE,
 | |
|     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() {
 | |
|     await preferencesOpen;
 | |
|     let startTime = Date.now();
 | |
|     info("starting test: " + props.name);
 | |
|     await testWithProperties(props, startTime);
 | |
|     await resetPrefs();
 | |
|   });
 | |
| }
 | |
| 
 | |
| add_task(async function testRemoteSettingsEnable() {
 | |
|   // Enable the rollout.
 | |
|   await DoHTestUtils.loadRemoteSettingsConfig({
 | |
|     providers: "example-1, example-2",
 | |
|     rolloutEnabled: true,
 | |
|     steeringEnabled: false,
 | |
|     steeringProviders: "",
 | |
|     autoDefaultEnabled: false,
 | |
|     autoDefaultProviders: "",
 | |
|     id: "global",
 | |
|   });
 | |
| 
 | |
|   let doTest = async (cancelOrAccept = "cancel") => {
 | |
|     let dialog = await openConnectionsSubDialog();
 | |
|     await dialog.uiReady;
 | |
|     let doc = dialog.document;
 | |
|     let dialogElement = doc.getElementById("ConnectionsDialog");
 | |
|     let modeCheckbox = doc.querySelector(modeCheckboxSelector);
 | |
|     ok(modeCheckbox.checked, "The mode checkbox should be checked.");
 | |
|     let dialogClosingPromise = BrowserTestUtils.waitForEvent(
 | |
|       dialogElement,
 | |
|       "dialogclosing"
 | |
|     );
 | |
|     if (cancelOrAccept == "cancel") {
 | |
|       dialogElement.cancelDialog();
 | |
|     } else {
 | |
|       dialogElement.acceptDialog();
 | |
|     }
 | |
|     await dialogClosingPromise;
 | |
|     if (cancelOrAccept == "cancel") {
 | |
|       try {
 | |
|         await TestUtils.waitForCondition(() =>
 | |
|           Services.prefs.prefHasUserValue("doh-rollout.disable-heuristics")
 | |
|         );
 | |
|         ok(false, "Heuristics were disabled when they shouldn't have been!");
 | |
|       } catch (e) {
 | |
|         ok(true, "Heuristics remained enabled.");
 | |
|       }
 | |
|       is(Services.prefs.getStringPref("network.trr.uri"), "");
 | |
|       ok(!Services.prefs.prefHasUserValue("network.trr.mode"));
 | |
|     } else {
 | |
|       // If accepting, the chosen provider is persisted to network.trr.uri
 | |
|       // and heuristics should get disabled.
 | |
|       await TestUtils.waitForCondition(() =>
 | |
|         Services.prefs.prefHasUserValue("doh-rollout.disable-heuristics")
 | |
|       );
 | |
|       ok(
 | |
|         Services.prefs.getBoolPref("doh-rollout.disable-heuristics"),
 | |
|         "Heurstics were disabled."
 | |
|       );
 | |
|       is(
 | |
|         Services.prefs.getStringPref("network.trr.uri"),
 | |
|         DEFAULT_RESOLVER_VALUE
 | |
|       );
 | |
|       is(Services.prefs.getIntPref("network.trr.mode"), 2);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   for (let action of ["cancel", "accept"]) {
 | |
|     await doTest(action);
 | |
|   }
 | |
| });
 | |
| 
 | |
| add_task(async function testEnterprisePolicy() {
 | |
|   async function closeDialog(dialog) {
 | |
|     let dialogClosingPromise = BrowserTestUtils.waitForEvent(
 | |
|       dialog,
 | |
|       "dialogclosing"
 | |
|     );
 | |
| 
 | |
|     dialog.cancelDialog();
 | |
|     await dialogClosingPromise;
 | |
|   }
 | |
| 
 | |
|   async function withPolicy(policy, fn, preFn = () => {}) {
 | |
|     await resetPrefs();
 | |
|     PoliciesPrefTracker.start();
 | |
|     await EnterprisePolicyTesting.setupPolicyEngineWithJson(policy);
 | |
| 
 | |
|     await preFn();
 | |
| 
 | |
|     let dialog = await openConnectionsSubDialog();
 | |
|     await dialog.uiReady;
 | |
| 
 | |
|     let doc = dialog.document;
 | |
| 
 | |
|     let dialogElement = doc.getElementById("ConnectionsDialog");
 | |
|     let modeCheckbox = doc.querySelector(modeCheckboxSelector);
 | |
|     let resolverMenulist = doc.querySelector(resolverMenulistSelector);
 | |
|     let uriTextbox = doc.querySelector(uriTextboxSelector);
 | |
| 
 | |
|     await fn({
 | |
|       dialog,
 | |
|       dialogElement,
 | |
|       modeCheckbox,
 | |
|       resolverMenulist,
 | |
|       doc,
 | |
|       uriTextbox,
 | |
|     });
 | |
| 
 | |
|     await closeDialog(dialogElement);
 | |
|     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 => {
 | |
|       ok(res.modeCheckbox.checked, "The mode checkbox should be checked.");
 | |
|       is(res.modeCheckbox.disabled, true, "The checkbox should be locked.");
 | |
| 
 | |
|       is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
 | |
|       is(
 | |
|         res.resolverMenulist.disabled,
 | |
|         true,
 | |
|         "The resolver list should be locked."
 | |
|       );
 | |
| 
 | |
|       is(res.uriTextbox.disabled, true, "The custom URI should be locked.");
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   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 => {
 | |
|       ok(res.modeCheckbox.checked, "The mode checkbox should be checked.");
 | |
|       is(
 | |
|         res.modeCheckbox.disabled,
 | |
|         false,
 | |
|         "The checkbox should not be locked."
 | |
|       );
 | |
| 
 | |
|       is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
 | |
|       is(
 | |
|         res.resolverMenulist.disabled,
 | |
|         false,
 | |
|         "The resolver list should not be locked."
 | |
|       );
 | |
| 
 | |
|       is(
 | |
|         res.uriTextbox.value,
 | |
|         "https://example.com/provider",
 | |
|         "Expected custom resolver"
 | |
|       );
 | |
|       is(
 | |
|         res.uriTextbox.disabled,
 | |
|         false,
 | |
|         "The custom URI should not be locked."
 | |
|       );
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   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 => {
 | |
|       ok(!res.modeCheckbox.checked, "The mode checkbox should be unchecked.");
 | |
|       is(res.modeCheckbox.disabled, true, "The checkbox should be locked.");
 | |
| 
 | |
|       is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
 | |
|       is(
 | |
|         res.resolverMenulist.disabled,
 | |
|         true,
 | |
|         "The resolver list should be locked."
 | |
|       );
 | |
| 
 | |
|       is(res.uriTextbox.disabled, true, "The custom URI should be locked.");
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   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 => {
 | |
|       ok(!res.modeCheckbox.checked, "The mode checkbox should be unchecked.");
 | |
|       is(
 | |
|         res.modeCheckbox.disabled,
 | |
|         false,
 | |
|         "The checkbox should not be locked."
 | |
|       );
 | |
| 
 | |
|       is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
 | |
|       is(
 | |
|         res.resolverMenulist.disabled,
 | |
|         true,
 | |
|         "The resolver list should be locked."
 | |
|       );
 | |
| 
 | |
|       is(res.uriTextbox.disabled, true, "The custom URI should be locked.");
 | |
|     }
 | |
|   );
 | |
| 
 | |
|   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 => {
 | |
|       ok(res.modeCheckbox.checked, "The mode checkbox should be checked.");
 | |
|       is(
 | |
|         res.modeCheckbox.disabled,
 | |
|         false,
 | |
|         "The checkbox should not be locked."
 | |
|       );
 | |
| 
 | |
|       is(res.resolverMenulist.value, "custom", "Resolver list shows custom");
 | |
|       is(
 | |
|         res.resolverMenulist.disabled,
 | |
|         false,
 | |
|         "The resolver list should not be locked."
 | |
|       );
 | |
| 
 | |
|       is(
 | |
|         res.uriTextbox.value,
 | |
|         "https://example.com/provider",
 | |
|         "Expected custom resolver"
 | |
|       );
 | |
|       is(
 | |
|         res.uriTextbox.disabled,
 | |
|         false,
 | |
|         "The custom URI should not be locked."
 | |
|       );
 | |
|     },
 | |
|     async function runAfterSettingPolicy() {
 | |
|       await DoHTestUtils.loadRemoteSettingsConfig({
 | |
|         providers: "example-1, example-2",
 | |
|         rolloutEnabled: true,
 | |
|         steeringEnabled: false,
 | |
|         steeringProviders: "",
 | |
|         autoDefaultEnabled: false,
 | |
|         autoDefaultProviders: "",
 | |
|         id: "global",
 | |
|       });
 | |
|     }
 | |
|   );
 | |
| });
 | 
