browser_privacy_dnsoverhttps.js (32287B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 requestLongerTimeout(4); 7 8 const { EnterprisePolicyTesting, PoliciesPrefTracker } = 9 ChromeUtils.importESModule( 10 "resource://testing-common/EnterprisePolicyTesting.sys.mjs" 11 ); 12 13 ChromeUtils.defineESModuleGetters(this, { 14 DoHConfigController: "moz-src:///toolkit/components/doh/DoHConfig.sys.mjs", 15 DoHController: "moz-src:///toolkit/components/doh/DoHController.sys.mjs", 16 DoHTestUtils: "resource://testing-common/DoHTestUtils.sys.mjs", 17 }); 18 19 const { MockRegistrar } = ChromeUtils.importESModule( 20 "resource://testing-common/MockRegistrar.sys.mjs" 21 ); 22 const gDNSOverride = Cc[ 23 "@mozilla.org/network/native-dns-override;1" 24 ].getService(Ci.nsINativeDNSResolverOverride); 25 26 const TRR_MODE_PREF = "network.trr.mode"; 27 const TRR_URI_PREF = "network.trr.uri"; 28 const TRR_CUSTOM_URI_PREF = "network.trr.custom_uri"; 29 const ROLLOUT_ENABLED_PREF = "doh-rollout.enabled"; 30 const ROLLOUT_SELF_ENABLED_PREF = "doh-rollout.self-enabled"; 31 const HEURISTICS_DISABLED_PREF = "doh-rollout.disable-heuristics"; 32 const FIRST_RESOLVER_VALUE = DoHTestUtils.providers[0].uri; 33 const SECOND_RESOLVER_VALUE = DoHTestUtils.providers[1].uri; 34 const DEFAULT_RESOLVER_VALUE = FIRST_RESOLVER_VALUE; 35 36 const defaultPrefValues = Object.freeze({ 37 [TRR_MODE_PREF]: 0, 38 [TRR_CUSTOM_URI_PREF]: "", 39 }); 40 41 // See bug 1741554. This test should not actually try to create a connection to 42 // the real DoH endpoint. But a background request could do that while the test 43 // is in progress, before we've actually disabled TRR, and would cause a crash 44 // due to connecting to a non-local IP. 45 // To prevent that we override the IP to a local address. 46 gDNSOverride.addIPOverride("mozilla.cloudflare-dns.com", "127.0.0.1"); 47 48 async function clearEvents() { 49 Services.telemetry.clearEvents(); 50 await TestUtils.waitForCondition(() => { 51 let events = Services.telemetry.snapshotEvents( 52 Ci.nsITelemetry.DATASET_ALL_CHANNELS, 53 true 54 ).parent; 55 return !events || !events.length; 56 }); 57 } 58 59 async function getEvent(filter1, filter2) { 60 let event = await TestUtils.waitForCondition(() => { 61 let events = Services.telemetry.snapshotEvents( 62 Ci.nsITelemetry.DATASET_ALL_CHANNELS, 63 true 64 ).parent; 65 return events?.find(e => e[1] == filter1 && e[2] == filter2); 66 }, "recorded telemetry for the load"); 67 event.shift(); 68 return event; 69 } 70 71 // Mock parental controls service in order to enable it 72 let parentalControlsService = { 73 parentalControlsEnabled: true, 74 QueryInterface: ChromeUtils.generateQI(["nsIParentalControlsService"]), 75 }; 76 let mockParentalControlsServiceCid = undefined; 77 78 async function setMockParentalControlEnabled(aEnabled) { 79 parentalControlsService.parentalControlsEnabled = aEnabled; 80 } 81 82 async function resetPrefs() { 83 await DoHTestUtils.resetRemoteSettingsConfig(); 84 await DoHController._uninit(); 85 Services.prefs.clearUserPref(TRR_MODE_PREF); 86 Services.prefs.clearUserPref(TRR_URI_PREF); 87 Services.prefs.clearUserPref(TRR_CUSTOM_URI_PREF); 88 Services.prefs.getChildList("doh-rollout.").forEach(pref => { 89 Services.prefs.clearUserPref(pref); 90 }); 91 // Clear out any telemetry events generated by DoHController so that we don't 92 // confuse tests running after this one that are looking at those. 93 Services.telemetry.clearEvents(); 94 await DoHController.init(); 95 } 96 Services.prefs.setStringPref("network.trr.confirmationNS", "skip"); 97 98 registerCleanupFunction(async () => { 99 await resetPrefs(); 100 Services.prefs.clearUserPref("network.trr.confirmationNS"); 101 102 if (mockParentalControlsServiceCid != undefined) { 103 MockRegistrar.unregister(mockParentalControlsServiceCid); 104 mockParentalControlsServiceCid = undefined; 105 Services.dns.reloadParentalControlEnabled(); 106 } 107 }); 108 109 add_setup(async function setup() { 110 mockParentalControlsServiceCid = MockRegistrar.register( 111 "@mozilla.org/parental-controls-service;1", 112 parentalControlsService 113 ); 114 Services.dns.reloadParentalControlEnabled(); 115 116 await SpecialPowers.pushPrefEnv({ 117 set: [["toolkit.telemetry.testing.overrideProductsCheck", true]], 118 }); 119 120 await DoHTestUtils.resetRemoteSettingsConfig(); 121 122 gDNSOverride.addIPOverride("use-application-dns.net.", "4.1.1.1"); 123 124 setMockParentalControlEnabled(false); 125 }); 126 127 function waitForPrefObserver(name) { 128 return new Promise(resolve => { 129 const observer = { 130 observe(aSubject, aTopic, aData) { 131 if (aData == name) { 132 Services.prefs.removeObserver(name, observer); 133 resolve(); 134 } 135 }, 136 }; 137 Services.prefs.addObserver(name, observer); 138 }); 139 } 140 141 add_task(async function testParentalControls() { 142 async function withConfiguration(configuration, fn) { 143 info("testParentalControls"); 144 145 await resetPrefs(); 146 Services.prefs.setIntPref(TRR_MODE_PREF, configuration.trr_mode); 147 await setMockParentalControlEnabled(configuration.parentalControlsState); 148 149 await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); 150 let doc = gBrowser.selectedBrowser.contentDocument; 151 let statusElement = doc.getElementById("dohStatus"); 152 153 await TestUtils.waitForCondition(() => { 154 return ( 155 document.l10n.getAttributes(statusElement).args.status == 156 configuration.wait_for_doh_status 157 ); 158 }); 159 160 await fn({ 161 statusElement, 162 }); 163 164 gBrowser.removeCurrentTab(); 165 await setMockParentalControlEnabled(false); 166 } 167 168 info("Check parental controls disabled, TRR off"); 169 await withConfiguration( 170 { 171 parentalControlsState: false, 172 trr_mode: 0, 173 wait_for_doh_status: "Off", 174 }, 175 async res => { 176 is( 177 document.l10n.getAttributes(res.statusElement).args.status, 178 "Off", 179 "expecting status off" 180 ); 181 } 182 ); 183 184 info("Check parental controls enabled, TRR off"); 185 await withConfiguration( 186 { 187 parentalControlsState: true, 188 trr_mode: 0, 189 wait_for_doh_status: "Off", 190 }, 191 async res => { 192 is( 193 document.l10n.getAttributes(res.statusElement).args.status, 194 "Off", 195 "expecting status off" 196 ); 197 } 198 ); 199 200 // Enable the rollout. 201 await DoHTestUtils.loadRemoteSettingsConfig({ 202 providers: "example", 203 rolloutEnabled: true, 204 steeringEnabled: false, 205 steeringProviders: "", 206 autoDefaultEnabled: false, 207 autoDefaultProviders: "", 208 id: "global", 209 }); 210 211 info("Check parental controls disabled, TRR first"); 212 await withConfiguration( 213 { 214 parentalControlsState: false, 215 trr_mode: 2, 216 wait_for_doh_status: "Active", 217 }, 218 async res => { 219 is( 220 document.l10n.getAttributes(res.statusElement).args.status, 221 "Active", 222 "expecting status active" 223 ); 224 } 225 ); 226 227 info("Check parental controls enabled, TRR first"); 228 await withConfiguration( 229 { 230 parentalControlsState: true, 231 trr_mode: 2, 232 wait_for_doh_status: "Not active (TRR_PARENTAL_CONTROL)", 233 }, 234 async res => { 235 is( 236 document.l10n.getAttributes(res.statusElement).args.status, 237 "Not active (TRR_PARENTAL_CONTROL)", 238 "expecting status not active" 239 ); 240 } 241 ); 242 243 info("Check parental controls disabled, TRR only"); 244 await withConfiguration( 245 { 246 parentalControlsState: false, 247 trr_mode: 3, 248 wait_for_doh_status: "Active", 249 }, 250 async res => { 251 is( 252 document.l10n.getAttributes(res.statusElement).args.status, 253 "Active", 254 "expecting status active" 255 ); 256 } 257 ); 258 259 info("Check parental controls enabled, TRR only"); 260 await withConfiguration( 261 { 262 parentalControlsState: true, 263 trr_mode: 3, 264 wait_for_doh_status: "Not active (TRR_PARENTAL_CONTROL)", 265 }, 266 async res => { 267 is( 268 document.l10n.getAttributes(res.statusElement).args.status, 269 "Not active (TRR_PARENTAL_CONTROL)", 270 "expecting status not active" 271 ); 272 } 273 ); 274 275 await resetPrefs(); 276 }); 277 278 async function testWithProperties(props, startTime) { 279 info( 280 Date.now() - 281 startTime + 282 ": testWithProperties: testing with " + 283 JSON.stringify(props) 284 ); 285 286 // There are two different signals that the DoHController is ready, depending 287 // on the config being tested. If we're setting the TRR mode pref, we should 288 // expect the disable-heuristics pref to be set as the signal. Else, we can 289 // expect the self-enabled pref as the signal. 290 let rolloutReadyPromise; 291 if (props.hasOwnProperty(TRR_MODE_PREF)) { 292 if ( 293 [2, 3, 5].includes(props[TRR_MODE_PREF]) && 294 props.hasOwnProperty(ROLLOUT_ENABLED_PREF) 295 ) { 296 // Only initialize the promise if we're going to enable the rollout - 297 // otherwise we will never await it, which could cause a leak if it doesn't 298 // end up resolving. 299 rolloutReadyPromise = waitForPrefObserver(HEURISTICS_DISABLED_PREF); 300 } 301 Services.prefs.setIntPref(TRR_MODE_PREF, props[TRR_MODE_PREF]); 302 } 303 if (props.hasOwnProperty(ROLLOUT_ENABLED_PREF)) { 304 if (!rolloutReadyPromise) { 305 rolloutReadyPromise = waitForPrefObserver(ROLLOUT_SELF_ENABLED_PREF); 306 } 307 Services.prefs.setBoolPref( 308 ROLLOUT_ENABLED_PREF, 309 props[ROLLOUT_ENABLED_PREF] 310 ); 311 await rolloutReadyPromise; 312 } 313 if (props.hasOwnProperty(TRR_CUSTOM_URI_PREF)) { 314 Services.prefs.setStringPref( 315 TRR_CUSTOM_URI_PREF, 316 props[TRR_CUSTOM_URI_PREF] 317 ); 318 } 319 if (props.hasOwnProperty(TRR_URI_PREF)) { 320 Services.prefs.setStringPref(TRR_URI_PREF, props[TRR_URI_PREF]); 321 } 322 323 await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); 324 let doc = gBrowser.selectedBrowser.contentDocument; 325 326 info(Date.now() - startTime + ": testWithProperties: tab now open"); 327 let modeRadioGroup = doc.getElementById("dohCategoryRadioGroup"); 328 let uriTextbox = doc.getElementById("dohEnabledInputField"); 329 let resolverMenulist = doc.getElementById("dohStrictResolverChoices"); 330 let modePrefChangedPromise; 331 let uriPrefChangedPromise; 332 let disableHeuristicsPrefChangedPromise; 333 334 modeRadioGroup.scrollIntoView(); 335 336 if (props.hasOwnProperty("expectedSelectedIndex")) { 337 await TestUtils.waitForCondition( 338 () => modeRadioGroup.selectedIndex === props.expectedSelectedIndex 339 ); 340 is( 341 modeRadioGroup.selectedIndex, 342 props.expectedSelectedIndex, 343 "dohCategoryRadioGroup has expected selected index" 344 ); 345 } 346 if (props.hasOwnProperty("expectedUriValue")) { 347 await TestUtils.waitForCondition( 348 () => uriTextbox.value === props.expectedUriValue 349 ); 350 is( 351 uriTextbox.value, 352 props.expectedUriValue, 353 "URI textbox has expected value" 354 ); 355 } 356 if (props.hasOwnProperty("expectedResolverListValue")) { 357 await TestUtils.waitForCondition( 358 () => resolverMenulist.value === props.expectedResolverListValue 359 ); 360 is( 361 resolverMenulist.value, 362 props.expectedResolverListValue, 363 "resolver menulist has expected value" 364 ); 365 } 366 367 if (props.clickMode) { 368 await clearEvents(); 369 info( 370 Date.now() - 371 startTime + 372 ": testWithProperties: clickMode, waiting for the pref observer" 373 ); 374 modePrefChangedPromise = waitForPrefObserver(TRR_MODE_PREF); 375 if (props.hasOwnProperty("expectedDisabledHeuristics")) { 376 disableHeuristicsPrefChangedPromise = waitForPrefObserver( 377 HEURISTICS_DISABLED_PREF 378 ); 379 } 380 info( 381 Date.now() - startTime + ": testWithProperties: clickMode, pref changed" 382 ); 383 let option = doc.getElementById(props.clickMode); 384 option.scrollIntoView(); 385 let win = doc.ownerGlobal; 386 EventUtils.synthesizeMouseAtCenter(option, {}, win); 387 info( 388 `${Date.now() - startTime} : testWithProperties: clickMode=${ 389 props.clickMode 390 }, mouse click synthesized` 391 ); 392 let clickEvent = await getEvent("security.doh.settings", "mode_changed"); 393 Assert.deepEqual(clickEvent, [ 394 "security.doh.settings", 395 "mode_changed", 396 "button", 397 props.clickMode, 398 ]); 399 } 400 if (props.hasOwnProperty("selectResolver")) { 401 await clearEvents(); 402 info( 403 Date.now() - 404 startTime + 405 ": testWithProperties: selectResolver, creating change event" 406 ); 407 resolverMenulist.focus(); 408 resolverMenulist.value = props.selectResolver; 409 resolverMenulist.dispatchEvent(new Event("input", { bubbles: true })); 410 resolverMenulist.dispatchEvent(new Event("command", { bubbles: true })); 411 info( 412 Date.now() - 413 startTime + 414 ": testWithProperties: selectResolver, item value set and events dispatched" 415 ); 416 let choiceEvent = await getEvent( 417 "security.doh.settings", 418 "provider_choice" 419 ); 420 Assert.deepEqual(choiceEvent, [ 421 "security.doh.settings", 422 "provider_choice", 423 "value", 424 props.selectResolver, 425 ]); 426 } 427 if (props.hasOwnProperty("inputUriKeys")) { 428 info( 429 Date.now() - 430 startTime + 431 ": testWithProperties: inputUriKeys, waiting for the pref observer" 432 ); 433 uriPrefChangedPromise = waitForPrefObserver(TRR_CUSTOM_URI_PREF); 434 info( 435 Date.now() - 436 startTime + 437 ": testWithProperties: inputUriKeys, pref changed, now enter the new value" 438 ); 439 let win = doc.ownerGlobal; 440 uriTextbox.focus(); 441 uriTextbox.value = props.inputUriKeys; 442 uriTextbox.dispatchEvent(new win.Event("input", { bubbles: true })); 443 uriTextbox.dispatchEvent(new win.Event("change", { bubbles: true })); 444 info( 445 Date.now() - 446 startTime + 447 ": testWithProperties: inputUriKeys, input and change events dispatched" 448 ); 449 } 450 451 info( 452 Date.now() - 453 startTime + 454 ": testWithProperties: waiting for any of uri and mode prefs to change" 455 ); 456 await Promise.all([ 457 uriPrefChangedPromise, 458 modePrefChangedPromise, 459 disableHeuristicsPrefChangedPromise, 460 ]); 461 info(Date.now() - startTime + ": testWithProperties: prefs changed"); 462 463 if (props.hasOwnProperty("expectedFinalUriPref")) { 464 if (props.expectedFinalUriPref) { 465 let uriPref = Services.prefs.getStringPref(TRR_URI_PREF); 466 is( 467 uriPref, 468 props.expectedFinalUriPref, 469 "uri pref ended up with the expected value" 470 ); 471 } else { 472 ok( 473 !Services.prefs.prefHasUserValue(TRR_URI_PREF), 474 `uri pref ended up with the expected value (unset) got ${Services.prefs.getStringPref( 475 TRR_URI_PREF 476 )}` 477 ); 478 } 479 } 480 481 if (props.hasOwnProperty("expectedModePref")) { 482 let modePref = Services.prefs.getIntPref(TRR_MODE_PREF); 483 is( 484 modePref, 485 props.expectedModePref, 486 "mode pref ended up with the expected value" 487 ); 488 } 489 490 if (props.hasOwnProperty("expectedDisabledHeuristics")) { 491 let disabledHeuristicsPref = Services.prefs.getBoolPref( 492 HEURISTICS_DISABLED_PREF 493 ); 494 is( 495 disabledHeuristicsPref, 496 props.expectedDisabledHeuristics, 497 "disable-heuristics pref ended up with the expected value" 498 ); 499 } 500 501 if (props.hasOwnProperty("expectedFinalCustomUriPref")) { 502 let customUriPref = Services.prefs.getStringPref(TRR_CUSTOM_URI_PREF); 503 is( 504 customUriPref, 505 props.expectedFinalCustomUriPref, 506 "custom_uri pref ended up with the expected value" 507 ); 508 } 509 510 if (props.hasOwnProperty("expectedModeValue")) { 511 let modeValue = Services.prefs.getIntPref(TRR_MODE_PREF); 512 is(modeValue, props.expectedModeValue, "mode pref has expected value"); 513 } 514 515 if (props.hasOwnProperty(ROLLOUT_ENABLED_PREF)) { 516 Services.prefs.clearUserPref(ROLLOUT_ENABLED_PREF); 517 } 518 519 gBrowser.removeCurrentTab(); 520 info(Date.now() - startTime + ": testWithProperties: fin"); 521 } 522 523 add_task(async function default_values() { 524 let customUriPref = Services.prefs.getStringPref(TRR_CUSTOM_URI_PREF); 525 let uriPrefHasUserValue = Services.prefs.prefHasUserValue(TRR_URI_PREF); 526 let modePref = Services.prefs.getIntPref(TRR_MODE_PREF); 527 is( 528 modePref, 529 defaultPrefValues[TRR_MODE_PREF], 530 `Actual value of ${TRR_MODE_PREF} matches expected default value` 531 ); 532 ok( 533 !uriPrefHasUserValue, 534 `Actual value of ${TRR_URI_PREF} matches expected default value (unset)` 535 ); 536 is( 537 customUriPref, 538 defaultPrefValues[TRR_CUSTOM_URI_PREF], 539 `Actual value of ${TRR_CUSTOM_URI_PREF} matches expected default value` 540 ); 541 }); 542 543 const DEFAULT_OPTION_INDEX = 0; 544 const ENABLED_OPTION_INDEX = 1; 545 const STRICT_OPTION_INDEX = 2; 546 const OFF_OPTION_INDEX = 3; 547 548 let testVariations = [ 549 // verify state with defaults 550 { 551 name: "default", 552 expectedModePref: 0, 553 expectedSelectedIndex: DEFAULT_OPTION_INDEX, 554 expectedUriValue: "", 555 }, 556 557 // verify each of the modes maps to the correct checked state 558 { 559 name: "mode 0", 560 [TRR_MODE_PREF]: 0, 561 expectedSelectedIndex: DEFAULT_OPTION_INDEX, 562 }, 563 { 564 name: "mode 1", 565 [TRR_MODE_PREF]: 1, 566 expectedSelectedIndex: OFF_OPTION_INDEX, 567 }, 568 { 569 name: "mode 2", 570 [TRR_MODE_PREF]: 2, 571 expectedSelectedIndex: ENABLED_OPTION_INDEX, 572 }, 573 { 574 name: "mode 2 and match default uri", 575 [TRR_MODE_PREF]: 2, 576 [TRR_URI_PREF]: "", 577 expectedSelectedIndex: ENABLED_OPTION_INDEX, 578 expectedFinalUriPref: DEFAULT_RESOLVER_VALUE, 579 }, 580 { 581 name: "mode 3", 582 [TRR_MODE_PREF]: 3, 583 expectedSelectedIndex: STRICT_OPTION_INDEX, 584 }, 585 { 586 name: "mode 3 and match default uri", 587 [TRR_URI_PREF]: "", 588 [TRR_MODE_PREF]: 3, 589 expectedSelectedIndex: STRICT_OPTION_INDEX, 590 expectedFinalUriPref: DEFAULT_RESOLVER_VALUE, 591 }, 592 { 593 name: "mode 4", 594 [TRR_MODE_PREF]: 4, 595 expectedSelectedIndex: OFF_OPTION_INDEX, 596 }, 597 { 598 name: "mode 5", 599 [TRR_MODE_PREF]: 5, 600 expectedSelectedIndex: OFF_OPTION_INDEX, 601 }, 602 // verify an out of bounds mode value maps to the correct checked state 603 { 604 name: "mode out-of-bounds", 605 [TRR_MODE_PREF]: 77, 606 expectedSelectedIndex: OFF_OPTION_INDEX, 607 }, 608 { 609 name: "mode out-of-bounds-negative", 610 [TRR_MODE_PREF]: -1, 611 expectedSelectedIndex: OFF_OPTION_INDEX, 612 }, 613 // Changing mode 0 to mode 2 and 3 614 { 615 name: "back to mode 0", 616 [TRR_MODE_PREF]: 0, 617 expectedSelectedIndex: DEFAULT_OPTION_INDEX, 618 }, 619 { 620 name: "mode 2 after mode 0", 621 [TRR_MODE_PREF]: 2, 622 expectedSelectedIndex: ENABLED_OPTION_INDEX, 623 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 624 }, 625 { 626 name: "back to mode 0_2", 627 [TRR_MODE_PREF]: 0, 628 expectedSelectedIndex: DEFAULT_OPTION_INDEX, 629 }, 630 { 631 name: "mode 3 after mode 0", 632 [TRR_MODE_PREF]: 3, 633 expectedSelectedIndex: STRICT_OPTION_INDEX, 634 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 635 }, 636 // verify the final URI of changing mode 5 to mode 2 and 3 637 { 638 name: "back to mode 5", 639 [TRR_MODE_PREF]: 5, 640 expectedSelectedIndex: OFF_OPTION_INDEX, 641 }, 642 { 643 name: "mode 2 after mode 5", 644 [TRR_MODE_PREF]: 2, 645 expectedSelectedIndex: ENABLED_OPTION_INDEX, 646 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 647 }, 648 { 649 name: "back to mode 5_2", 650 [TRR_MODE_PREF]: 5, 651 expectedSelectedIndex: OFF_OPTION_INDEX, 652 }, 653 { 654 name: "mode 3 after mode 5", 655 [TRR_MODE_PREF]: 3, 656 expectedSelectedIndex: STRICT_OPTION_INDEX, 657 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 658 }, 659 // verify automatic heuristics states 660 { 661 name: "heuristics on and mode unset", 662 [TRR_MODE_PREF]: 0, 663 [ROLLOUT_ENABLED_PREF]: true, 664 expectedSelectedIndex: DEFAULT_OPTION_INDEX, 665 }, 666 { 667 name: "heuristics on and mode set to 2", 668 [TRR_MODE_PREF]: 2, 669 [ROLLOUT_ENABLED_PREF]: true, 670 expectedSelectedIndex: ENABLED_OPTION_INDEX, 671 }, 672 { 673 name: "heuristics on but disabled, mode unset", 674 [TRR_MODE_PREF]: 5, 675 [ROLLOUT_ENABLED_PREF]: true, 676 expectedSelectedIndex: OFF_OPTION_INDEX, 677 }, 678 { 679 name: "heuristics on but disabled, mode set to 2", 680 [TRR_MODE_PREF]: 2, 681 [ROLLOUT_ENABLED_PREF]: true, 682 expectedSelectedIndex: ENABLED_OPTION_INDEX, 683 }, 684 685 // verify picking each radio button option gives the right outcomes 686 { 687 name: "toggle mode on", 688 clickMode: "dohEnabledRadio", 689 expectedModeValue: 2, 690 expectedUriValue: "", 691 }, 692 // TRR_URI_PREF should match the expectedFinalUriPref based on the changes made in Bug 1861285 693 { 694 name: "toggle mode on and auto default uri", 695 [TRR_URI_PREF]: "https://stub.com", 696 clickMode: "dohEnabledRadio", 697 expectedModeValue: 2, 698 expectedUriValue: "", 699 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 700 }, 701 { 702 name: "toggle mode off", 703 [TRR_MODE_PREF]: 2, 704 expectedSelectedIndex: ENABLED_OPTION_INDEX, 705 clickMode: "dohOffRadio", 706 expectedModePref: 5, 707 }, 708 { 709 name: "toggle mode off when on due to heuristics", 710 [TRR_MODE_PREF]: 0, 711 [ROLLOUT_ENABLED_PREF]: true, 712 expectedSelectedIndex: DEFAULT_OPTION_INDEX, 713 clickMode: "dohOffRadio", 714 expectedModePref: 5, 715 expectedDisabledHeuristics: true, 716 }, 717 // Test selecting non-default, non-custom TRR provider, NextDNS. 718 { 719 name: "Select NextDNS as TRR provider", 720 [TRR_MODE_PREF]: 2, 721 selectResolver: SECOND_RESOLVER_VALUE, 722 expectedFinalUriPref: SECOND_RESOLVER_VALUE, 723 }, 724 // Test selecting non-default, non-custom TRR provider, NextDNS, 725 // with DoH not enabled. The provider selection should stick. 726 { 727 name: "Select NextDNS as TRR provider in mode 0", 728 [TRR_MODE_PREF]: 0, 729 selectResolver: SECOND_RESOLVER_VALUE, 730 expectedFinalUriPref: SECOND_RESOLVER_VALUE, 731 }, 732 { 733 name: "return to default from NextDNS", 734 [TRR_MODE_PREF]: 2, 735 [TRR_URI_PREF]: SECOND_RESOLVER_VALUE, 736 expectedResolverListValue: SECOND_RESOLVER_VALUE, 737 selectResolver: DEFAULT_RESOLVER_VALUE, 738 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 739 }, 740 // Attempt to select NextDNS with DoH not enabled 741 // from mode 5 to 3 742 { 743 name: "Select NextDNS as TRR provider in mode 5", 744 [TRR_MODE_PREF]: 5, 745 selectResolver: SECOND_RESOLVER_VALUE, 746 expectedFinalUriPref: SECOND_RESOLVER_VALUE, 747 }, 748 { 749 name: "return to default from NextDNS_2", 750 [TRR_MODE_PREF]: 2, 751 [TRR_URI_PREF]: SECOND_RESOLVER_VALUE, 752 expectedResolverListValue: SECOND_RESOLVER_VALUE, 753 selectResolver: DEFAULT_RESOLVER_VALUE, 754 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 755 }, 756 { 757 name: "Select NextDNS as TRR provider in mode 5_2", 758 [TRR_MODE_PREF]: 5, 759 selectResolver: SECOND_RESOLVER_VALUE, 760 expectedFinalUriPref: SECOND_RESOLVER_VALUE, 761 }, 762 { 763 name: "return to default from NextDNS_3", 764 [TRR_MODE_PREF]: 2, 765 [TRR_URI_PREF]: SECOND_RESOLVER_VALUE, 766 expectedResolverListValue: SECOND_RESOLVER_VALUE, 767 selectResolver: DEFAULT_RESOLVER_VALUE, 768 expectedFinalUriPref: FIRST_RESOLVER_VALUE, 769 }, 770 // test that selecting Custom, when we have a TRR_CUSTOM_URI_PREF subsequently changes TRR_URI_PREF 771 { 772 name: "select custom with existing custom_uri pref value", 773 [TRR_MODE_PREF]: 2, 774 [TRR_CUSTOM_URI_PREF]: "https://example.com", 775 expectedModeValue: 2, 776 expectedSelectedIndex: ENABLED_OPTION_INDEX, 777 selectResolver: "custom", 778 expectedUriValue: "https://example.com", 779 expectedFinalUriPref: "https://example.com", 780 expectedFinalCustomUriPref: "https://example.com", 781 }, 782 { 783 name: "select custom and enter new custom_uri pref value", 784 [TRR_URI_PREF]: "", 785 [TRR_CUSTOM_URI_PREF]: "", 786 clickMode: "dohEnabledRadio", 787 selectResolver: "custom", 788 inputUriKeys: "https://custom.com", 789 expectedModePref: 2, 790 expectedFinalUriPref: "https://custom.com", 791 expectedFinalCustomUriPref: "https://custom.com", 792 }, 793 794 { 795 name: "return to default from custom", 796 [TRR_MODE_PREF]: 2, 797 [TRR_URI_PREF]: "https://example.com", 798 [TRR_CUSTOM_URI_PREF]: "https://custom.com", 799 expectedUriValue: "https://example.com", 800 expectedResolverListValue: "custom", 801 selectResolver: DEFAULT_RESOLVER_VALUE, 802 expectedFinalUriPref: DEFAULT_RESOLVER_VALUE, 803 expectedFinalCustomUriPref: "https://example.com", 804 }, 805 { 806 name: "clear the custom uri", 807 [TRR_MODE_PREF]: 2, 808 [TRR_URI_PREF]: "https://example.com", 809 [TRR_CUSTOM_URI_PREF]: "https://example.com", 810 expectedUriValue: "https://example.com", 811 expectedResolverListValue: "custom", 812 inputUriKeys: "", 813 expectedFinalUriPref: " ", 814 expectedFinalCustomUriPref: "", 815 }, 816 { 817 name: "empty default resolver list", 818 [TRR_MODE_PREF]: 2, 819 [TRR_URI_PREF]: "https://example.com", 820 [TRR_CUSTOM_URI_PREF]: "", 821 expectedUriValue: "https://example.com", 822 expectedResolverListValue: "custom", 823 expectedFinalUriPref: "https://example.com", 824 expectedFinalCustomUriPref: "https://example.com", 825 }, 826 ]; 827 828 for (let props of testVariations) { 829 add_task(async function testVariation() { 830 let startTime = Date.now(); 831 info("starting test: " + props.name); 832 await testWithProperties(props, startTime); 833 await resetPrefs(); 834 }); 835 } 836 837 add_task(async function testRemoteSettingsEnable() { 838 let startTime = Date.now(); 839 // Enable the rollout. 840 await DoHTestUtils.loadRemoteSettingsConfig({ 841 providers: "example-1, example-2", 842 rolloutEnabled: true, 843 steeringEnabled: false, 844 steeringProviders: "", 845 autoDefaultEnabled: false, 846 autoDefaultProviders: "", 847 id: "global", 848 }); 849 850 await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); 851 let doc = gBrowser.selectedBrowser.contentDocument; 852 853 info(Date.now() - startTime + ": testWithProperties: tab now open"); 854 let modeRadioGroup = doc.getElementById("dohCategoryRadioGroup"); 855 856 is(modeRadioGroup.value, "0", "expecting default mode"); 857 858 let status = doc.getElementById("dohStatus"); 859 await TestUtils.waitForCondition( 860 () => document.l10n.getAttributes(status).args.status == "Active", 861 "Waiting for remote settings to be processed" 862 ); 863 is( 864 document.l10n.getAttributes(status).args.status, 865 "Active", 866 "expecting status active" 867 ); 868 869 let provider = doc.getElementById("dohResolver"); 870 is( 871 provider.hidden, 872 false, 873 "Provider should not be hidden when status is active" 874 ); 875 await TestUtils.waitForCondition( 876 () => 877 document.l10n.getAttributes(provider).args.name == 878 DoHConfigController.currentConfig.providerList[0].UIName, 879 "waiting for correct UI name" 880 ); 881 is( 882 document.l10n.getAttributes(provider).args.name, 883 DoHConfigController.currentConfig.providerList[0].UIName, 884 "expecting the right provider name" 885 ); 886 887 let option = doc.getElementById("dohEnabledRadio"); 888 option.scrollIntoView(); 889 let win = doc.ownerGlobal; 890 EventUtils.synthesizeMouseAtCenter(option, {}, win); 891 892 await TestUtils.waitForCondition( 893 () => Services.prefs.prefHasUserValue("doh-rollout.disable-heuristics"), 894 "Waiting for disable-heuristics" 895 ); 896 is(provider.hidden, false); 897 await TestUtils.waitForCondition( 898 () => 899 document.l10n.getAttributes(provider).args.name == 900 DoHConfigController.currentConfig.providerList[0].UIName, 901 "waiting for correct UI name" 902 ); 903 is( 904 document.l10n.getAttributes(provider).args.name, 905 DoHConfigController.currentConfig.providerList[0].UIName, 906 "expecting the right provider name" 907 ); 908 is( 909 Services.prefs.getIntPref("network.trr.mode"), 910 Ci.nsIDNSService.MODE_TRRFIRST 911 ); 912 913 option = doc.getElementById("dohOffRadio"); 914 option.scrollIntoView(); 915 win = doc.ownerGlobal; 916 EventUtils.synthesizeMouseAtCenter(option, {}, win); 917 await TestUtils.waitForCondition( 918 () => status.innerHTML == "Status: Off", 919 "Waiting for Status OFF" 920 ); 921 is( 922 Services.prefs.getIntPref("network.trr.mode"), 923 Ci.nsIDNSService.MODE_TRROFF 924 ); 925 is(provider.hidden, true, "Expecting provider to be hidden when DoH is off"); 926 927 gBrowser.removeCurrentTab(); 928 929 await DoHTestUtils.loadRemoteSettingsConfig({ 930 providers: "", 931 rolloutEnabled: false, 932 steeringEnabled: false, 933 steeringProviders: "", 934 autoDefaultEnabled: false, 935 autoDefaultProviders: "", 936 id: "global", 937 }); 938 }); 939 940 add_task(async function testEnterprisePolicy() { 941 async function withPolicy(policy, fn, preFn = () => {}) { 942 await resetPrefs(); 943 PoliciesPrefTracker.start(); 944 await EnterprisePolicyTesting.setupPolicyEngineWithJson(policy); 945 await preFn(); 946 947 await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); 948 let doc = gBrowser.selectedBrowser.contentDocument; 949 950 let modeRadioGroup = doc.getElementById("dohCategoryRadioGroup"); 951 let resolverMenulist = doc.getElementById("dohEnabledResolverChoices"); 952 let uriTextbox = doc.getElementById("dohEnabledInputField"); 953 954 await fn({ 955 modeRadioGroup, 956 resolverMenulist, 957 doc, 958 uriTextbox, 959 }); 960 961 gBrowser.removeCurrentTab(); 962 // Set an empty policy before stopping the policy tracker 963 await EnterprisePolicyTesting.setupPolicyEngineWithJson({}); 964 EnterprisePolicyTesting.resetRunOnceState(); 965 PoliciesPrefTracker.stop(); 966 } 967 968 info("Check that a locked policy does not allow any changes in the UI"); 969 await withPolicy( 970 { 971 policies: { 972 DNSOverHTTPS: { 973 Enabled: true, 974 ProviderURL: "https://examplelocked.com/provider", 975 ExcludedDomains: ["examplelocked.com", "example.org"], 976 Locked: true, 977 }, 978 }, 979 }, 980 async res => { 981 is(res.modeRadioGroup.disabled, true, "The mode menu should be locked."); 982 is(res.modeRadioGroup.value, "2", "Should be enabled"); 983 is(res.resolverMenulist.value, "custom", "Resolver list shows custom"); 984 is( 985 res.uriTextbox.value, 986 "https://examplelocked.com/provider", 987 "Custom URI should be set" 988 ); 989 } 990 ); 991 992 info("Check that an unlocked policy has editable fields in the dialog"); 993 await withPolicy( 994 { 995 policies: { 996 DNSOverHTTPS: { 997 Enabled: true, 998 ProviderURL: "https://example.com/provider", 999 ExcludedDomains: ["example.com", "example.org"], 1000 }, 1001 }, 1002 }, 1003 async res => { 1004 is( 1005 res.modeRadioGroup.disabled, 1006 false, 1007 "The mode menu should not be locked." 1008 ); 1009 is(res.modeRadioGroup.value, "2", "Should be enabled"); 1010 is(res.resolverMenulist.value, "custom", "Resolver list shows custom"); 1011 is( 1012 res.uriTextbox.value, 1013 "https://example.com/provider", 1014 "Expected custom resolver" 1015 ); 1016 } 1017 ); 1018 1019 info("Check that a locked disabled policy disables the buttons"); 1020 await withPolicy( 1021 { 1022 policies: { 1023 DNSOverHTTPS: { 1024 Enabled: false, 1025 ProviderURL: "https://example.com/provider", 1026 ExcludedDomains: ["example.com", "example.org"], 1027 Locked: true, 1028 }, 1029 }, 1030 }, 1031 async res => { 1032 is(res.modeRadioGroup.disabled, true, "The mode menu should be locked."); 1033 is(res.modeRadioGroup.value, "5", "Should be disabled"); 1034 } 1035 ); 1036 1037 info("Check that an unlocked disabled policy has editable fields"); 1038 await withPolicy( 1039 { 1040 policies: { 1041 DNSOverHTTPS: { 1042 Enabled: false, 1043 ProviderURL: "https://example.com/provider", 1044 ExcludedDomains: ["example.com", "example.org"], 1045 }, 1046 }, 1047 }, 1048 async res => { 1049 is( 1050 res.modeRadioGroup.disabled, 1051 false, 1052 "The mode menu should not be locked." 1053 ); 1054 is(res.modeRadioGroup.value, "5", "Should be disabled"); 1055 } 1056 ); 1057 1058 info("Check that the remote settings config doesn't override the policy"); 1059 await withPolicy( 1060 { 1061 policies: { 1062 DNSOverHTTPS: { 1063 Enabled: true, 1064 ProviderURL: "https://example.com/provider", 1065 ExcludedDomains: ["example.com", "example.org"], 1066 }, 1067 }, 1068 }, 1069 async res => { 1070 is( 1071 res.modeRadioGroup.disabled, 1072 false, 1073 "The mode menu should not be locked." 1074 ); 1075 is(res.resolverMenulist.value, "custom", "Resolver list shows custom"); 1076 is( 1077 res.uriTextbox.value, 1078 "https://example.com/provider", 1079 "Expected custom resolver" 1080 ); 1081 }, 1082 async function runAfterSettingPolicy() { 1083 await DoHTestUtils.loadRemoteSettingsConfig({ 1084 providers: "example-1, example-2", 1085 rolloutEnabled: true, 1086 steeringEnabled: false, 1087 steeringProviders: "", 1088 autoDefaultEnabled: false, 1089 autoDefaultProviders: "", 1090 id: "global", 1091 }); 1092 } 1093 ); 1094 });