head.js (23269B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 // This directory contains tests that check tips and interventions, and in 5 // particular the update-related interventions. 6 // We mock updates by using the test helpers in 7 // toolkit/mozapps/update/tests/browser. 8 9 "use strict"; 10 11 Services.scriptloader.loadSubScript( 12 "chrome://mochitests/content/browser/toolkit/mozapps/update/tests/browser/head.js", 13 this 14 ); 15 16 ChromeUtils.defineESModuleGetters(this, { 17 HttpServer: "resource://testing-common/httpd.sys.mjs", 18 ResetProfile: "resource://gre/modules/ResetProfile.sys.mjs", 19 TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs", 20 UrlbarProviderInterventions: 21 "moz-src:///browser/components/urlbar/UrlbarProviderInterventions.sys.mjs", 22 UrlbarProvidersManager: 23 "moz-src:///browser/components/urlbar/UrlbarProvidersManager.sys.mjs", 24 UrlbarResult: "moz-src:///browser/components/urlbar/UrlbarResult.sys.mjs", 25 }); 26 27 ChromeUtils.defineLazyGetter(this, "UrlbarTestUtils", () => { 28 const { UrlbarTestUtils: module } = ChromeUtils.importESModule( 29 "resource://testing-common/UrlbarTestUtils.sys.mjs" 30 ); 31 module.init(this); 32 return module; 33 }); 34 35 ChromeUtils.defineLazyGetter(this, "SearchTestUtils", () => { 36 const { SearchTestUtils: module } = ChromeUtils.importESModule( 37 "resource://testing-common/SearchTestUtils.sys.mjs" 38 ); 39 module.init(this); 40 return module; 41 }); 42 43 // For each intervention type, a search string that trigger the intervention. 44 const SEARCH_STRINGS = { 45 CLEAR: "firefox history", 46 REFRESH: "firefox slow", 47 UPDATE: "firefox update", 48 }; 49 50 registerCleanupFunction(() => { 51 // We need to reset the provider's appUpdater.status between tests so that 52 // each test doesn't interfere with the next. 53 UrlbarProviderInterventions.resetAppUpdater(); 54 }); 55 56 /** 57 * Override our binary path so that the update lock doesn't think more than one 58 * instance of this test is running. 59 * This is a heavily pared down copy of the function in xpcshellUtilsAUS.js. 60 */ 61 function adjustGeneralPaths() { 62 let dirProvider = { 63 getFile(aProp, aPersistent) { 64 // Set the value of persistent to false so when this directory provider is 65 // unregistered it will revert back to the original provider. 66 aPersistent.value = false; 67 // The sync manager only uses XRE_EXECUTABLE_FILE, so that's all we need 68 // to override, we won't bother handling anything else. 69 if (aProp == XRE_EXECUTABLE_FILE) { 70 // The temp directory that the mochitest runner creates is unique per 71 // test, so its path can serve to provide the unique key that the update 72 // sync manager requires (it doesn't need for this to be the actual 73 // path to any real file, it's only used as an opaque string). 74 let tempPath = Services.env.get("MOZ_PROCESS_LOG"); 75 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); 76 file.initWithPath(tempPath); 77 return file; 78 } 79 return null; 80 }, 81 QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]), 82 }; 83 84 let ds = Services.dirsvc.QueryInterface(Ci.nsIDirectoryService); 85 try { 86 ds.QueryInterface(Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE); 87 } catch (_ex) { 88 // We only override one property, so we have nothing to do if that fails. 89 return; 90 } 91 ds.registerProvider(dirProvider); 92 registerCleanupFunction(() => { 93 ds.unregisterProvider(dirProvider); 94 // Reset the update lock once again so that we know the lock we're 95 // interested in here will be closed properly (normally that happens during 96 // XPCOM shutdown, but that isn't consistent during tests). 97 let syncManager = Cc[ 98 "@mozilla.org/updates/update-sync-manager;1" 99 ].getService(Ci.nsIUpdateSyncManager); 100 syncManager.resetLock(); 101 }); 102 103 // Now that we've overridden the directory provider, the name of the update 104 // lock needs to be changed to match the overridden path. 105 let syncManager = Cc["@mozilla.org/updates/update-sync-manager;1"].getService( 106 Ci.nsIUpdateSyncManager 107 ); 108 syncManager.resetLock(); 109 } 110 111 /** 112 * Initializes a mock app update. Adapted from runAboutDialogUpdateTest: 113 * https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/tests/browser/head.js 114 * 115 * @param {object} params 116 * See the files in toolkit/mozapps/update/tests/browser. 117 */ 118 async function initUpdate(params) { 119 Services.env.set("MOZ_TEST_SLOW_SKIP_UPDATE_STAGE", "1"); 120 await SpecialPowers.pushPrefEnv({ 121 set: [ 122 [PREF_APP_UPDATE_DISABLEDFORTESTING, false], 123 [PREF_APP_UPDATE_URL_MANUAL, gDetailsURL], 124 ], 125 }); 126 127 adjustGeneralPaths(); 128 await setupTestUpdater(); 129 130 let queryString = params.queryString ? params.queryString : ""; 131 let updateURL = 132 URL_HTTP_UPDATE_SJS + 133 "?detailsURL=" + 134 gDetailsURL + 135 queryString + 136 getVersionParams(); 137 if (params.backgroundUpdate) { 138 setUpdateURL(updateURL); 139 await gAUS.checkForBackgroundUpdates(); 140 if (params.continueFile) { 141 await continueFileHandler(params.continueFile); 142 } 143 if (params.waitForUpdateState) { 144 let whichUpdateFn = 145 params.waitForUpdateState == STATE_DOWNLOADING 146 ? "getDownloadingUpdate" 147 : "getReadyUpdate"; 148 let update; 149 await TestUtils.waitForCondition( 150 async () => { 151 update = await gUpdateManager[whichUpdateFn](); 152 return update && update.state == params.waitForUpdateState; 153 }, 154 "Waiting for update state: " + params.waitForUpdateState, 155 undefined, 156 200 157 ).catch(e => { 158 // Instead of throwing let the check below fail the test so the panel 159 // ID and the expected panel ID is printed in the log. 160 logTestInfo(e); 161 }); 162 // Display the UI after the update state equals the expected value. 163 Assert.equal( 164 update.state, 165 params.waitForUpdateState, 166 "The update state value should equal " + params.waitForUpdateState 167 ); 168 } 169 } else { 170 updateURL += "&slowUpdateCheck=1&useSlowDownloadMar=1"; 171 setUpdateURL(updateURL); 172 } 173 } 174 175 /** 176 * Performs steps in a mock update. Adapted from runAboutDialogUpdateTest: 177 * https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/tests/browser/head.js 178 * 179 * @param {Array} steps 180 * See the files in toolkit/mozapps/update/tests/browser. 181 */ 182 async function processUpdateSteps(steps) { 183 for (let step of steps) { 184 await processUpdateStep(step); 185 } 186 } 187 188 /** 189 * Performs a step in a mock update. Adapted from runAboutDialogUpdateTest: 190 * https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/tests/browser/head.js 191 * 192 * @param {object} step 193 * See the files in toolkit/mozapps/update/tests/browser. 194 */ 195 async function processUpdateStep(step) { 196 if (typeof step == "function") { 197 step(); 198 return; 199 } 200 201 const { panelId, checkActiveUpdate, continueFile, downloadInfo } = step; 202 203 if ( 204 panelId == "downloading" && 205 gAUS.currentState == Ci.nsIApplicationUpdateService.STATE_IDLE 206 ) { 207 // Now that `AUS.downloadUpdate` is async, we start showing the 208 // downloading panel while `AUS.downloadUpdate` is still resolving. 209 // But the below checks assume that this resolution has already 210 // happened. So we need to wait for things to actually resolve. 211 await gAUS.stateTransition; 212 } 213 214 if (checkActiveUpdate) { 215 let whichUpdateFn = 216 checkActiveUpdate.state == STATE_DOWNLOADING 217 ? "getDownloadingUpdate" 218 : "getReadyUpdate"; 219 let update; 220 await TestUtils.waitForCondition(async () => { 221 update = await gUpdateManager[whichUpdateFn](); 222 return update; 223 }, "Waiting for active update"); 224 Assert.ok(!!update, "There should be an active update"); 225 Assert.equal( 226 update.state, 227 checkActiveUpdate.state, 228 "The active update state should equal " + checkActiveUpdate.state 229 ); 230 } else { 231 Assert.ok( 232 !(await gUpdateManager.getReadyUpdate()), 233 "There should not be a ready update" 234 ); 235 Assert.ok( 236 !(await gUpdateManager.getDownloadingUpdate()), 237 "There should not be a downloadingUpdate update" 238 ); 239 } 240 241 if (panelId == "downloading") { 242 for (let i = 0; i < downloadInfo.length; ++i) { 243 let data = downloadInfo[i]; 244 // The About Dialog tests always specify a continue file. 245 await continueFileHandler(continueFile); 246 let patch = getPatchOfType( 247 data.patchType, 248 await gUpdateManager.getDownloadingUpdate() 249 ); 250 // The update is removed early when the last download fails so check 251 // that there is a patch before proceeding. 252 let isLastPatch = i == downloadInfo.length - 1; 253 if (!isLastPatch || patch) { 254 let resultName = data.bitsResult ? "bitsResult" : "internalResult"; 255 patch.QueryInterface(Ci.nsIWritablePropertyBag); 256 await TestUtils.waitForCondition( 257 () => patch.getProperty(resultName) == data[resultName], 258 "Waiting for expected patch property " + 259 resultName + 260 " value: " + 261 data[resultName], 262 undefined, 263 200 264 ).catch(e => { 265 // Instead of throwing let the check below fail the test so the 266 // property value and the expected property value is printed in 267 // the log. 268 logTestInfo(e); 269 }); 270 Assert.equal( 271 patch.getProperty(resultName), 272 data[resultName], 273 "The patch property " + 274 resultName + 275 " value should equal " + 276 data[resultName] 277 ); 278 } 279 } 280 } else if (continueFile) { 281 await continueFileHandler(continueFile); 282 } 283 } 284 285 /** 286 * Checks an intervention tip. This works by starting a search that should 287 * trigger a tip, picks the tip, and waits for the tip's action to happen. 288 * 289 * @param {object} options 290 * Options for the test 291 * @param {string} options.searchString 292 * The search string. 293 * @param {string} options.tip 294 * The expected tip type. 295 * @param {string | RegExp} options.title 296 * The expected tip title. 297 * @param {string | RegExp} options.button 298 * The expected button title. 299 * @param {Function} options.awaitCallback 300 * A function that checks the tip's action. Should return a promise (or be 301 * async). 302 * @returns {object} 303 * The value returned from `awaitCallback`. 304 */ 305 async function doUpdateTest({ 306 searchString, 307 tip, 308 title, 309 button, 310 awaitCallback, 311 } = {}) { 312 // Do a search that triggers the tip. 313 let [result, element] = await awaitTip(searchString); 314 Assert.strictEqual(result.payload.type, tip, "Tip type"); 315 await element.ownerDocument.l10n.translateFragment(element); 316 317 let actualTitle = element._elements.get("title").textContent; 318 if (typeof title == "string") { 319 Assert.equal(actualTitle, title, "Title string"); 320 } else { 321 // regexp 322 Assert.ok(title.test(actualTitle), "Title regexp"); 323 } 324 325 let actualButton = element._buttons.get("0").textContent; 326 if (typeof button == "string") { 327 Assert.equal(actualButton, button, "Button string"); 328 } else { 329 // regexp 330 Assert.ok(button.test(actualButton), "Button regexp"); 331 } 332 333 Assert.ok(element._buttons.has("result-menu"), "Tip has a menu button"); 334 335 // Pick the tip and wait for the action. 336 let values = await Promise.all([awaitCallback(), pickTip()]); 337 338 return values[0] || null; 339 } 340 341 /** 342 * Starts a search and asserts that the second result is a tip. 343 * 344 * @param {string} searchString 345 * The search string. 346 * @param {window} win 347 * The window. 348 * @returns {(result| element)[]} 349 * The result and its element in the DOM. 350 */ 351 async function awaitTip(searchString, win = window) { 352 let context = await UrlbarTestUtils.promiseAutocompleteResultPopup({ 353 window: win, 354 value: searchString, 355 waitForFocus, 356 fireInputEvent: true, 357 }); 358 Assert.greaterOrEqual( 359 context.results.length, 360 2, 361 "Number of results is greater than or equal to 2" 362 ); 363 let result = context.results[1]; 364 Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.TIP, "Result type"); 365 let element = await UrlbarTestUtils.waitForAutocompleteResultAt(win, 1); 366 return [result, element]; 367 } 368 369 /** 370 * Picks the current tip's button. The view should be open and the second 371 * result should be a tip. 372 */ 373 async function pickTip() { 374 let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1); 375 let button = result.element.row._buttons.get("0"); 376 await UrlbarTestUtils.promisePopupClose(window, () => { 377 EventUtils.synthesizeMouseAtCenter(button, {}); 378 }); 379 } 380 381 /** 382 * Waits for the quit-application-requested notification and cancels it (so that 383 * the app isn't actually restarted). 384 */ 385 async function awaitAppRestartRequest() { 386 await TestUtils.topicObserved( 387 "quit-application-requested", 388 (cancelQuit, data) => { 389 if (data == "restart") { 390 cancelQuit.QueryInterface(Ci.nsISupportsPRBool).data = true; 391 return true; 392 } 393 return false; 394 } 395 ); 396 } 397 398 /** 399 * Sets up the profile so that it can be reset. 400 */ 401 function makeProfileResettable() { 402 // Make reset possible. 403 let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].getService( 404 Ci.nsIToolkitProfileService 405 ); 406 let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); 407 let profileName = "mochitest-test-profile-temp-" + Date.now(); 408 let tempProfile = profileService.createProfile( 409 currentProfileDir, 410 profileName 411 ); 412 Assert.ok( 413 ResetProfile.resetSupported(), 414 "Should be able to reset from mochitest's temporary profile once it's in the profile manager." 415 ); 416 417 registerCleanupFunction(() => { 418 tempProfile.remove(false); 419 Assert.ok( 420 !ResetProfile.resetSupported(), 421 "Shouldn't be able to reset from mochitest's temporary profile once removed from the profile manager." 422 ); 423 }); 424 } 425 426 /** 427 * Starts a search that should trigger a tip, picks the tip, and waits for the 428 * tip's action to happen. 429 * 430 * @param {object} options 431 * Options for the test 432 * @param {string} options.searchString 433 * The search string. 434 * @param {TIPS} options.tip 435 * The expected tip type. 436 * @param {string} options.title 437 * The expected tip title. 438 * @param {string} options.button 439 * The expected button title. 440 * @param {Function} options.awaitCallback 441 * A function that checks the tip's action. Should return a promise (or be 442 * async). 443 * @returns {*} 444 * The value returned from `awaitCallback`. 445 */ 446 function checkIntervention({ 447 searchString, 448 tip, 449 title, 450 button, 451 awaitCallback, 452 } = {}) { 453 // Opening modal dialogs confuses focus on Linux just after them, thus run 454 // these checks in separate tabs to better isolate them. 455 return BrowserTestUtils.withNewTab("about:blank", async () => { 456 // Do a search that triggers the tip. 457 let [result, element] = await awaitTip(searchString); 458 Assert.strictEqual(result.payload.type, tip); 459 await element.ownerDocument.l10n.translateFragment(element); 460 461 let actualTitle = element._elements.get("title").textContent; 462 if (typeof title == "string") { 463 Assert.equal(actualTitle, title, "Title string"); 464 } else { 465 // regexp 466 Assert.ok(title.test(actualTitle), "Title regexp"); 467 } 468 469 let actualButton = element._buttons.get("0").textContent; 470 if (typeof button == "string") { 471 Assert.equal(actualButton, button, "Button string"); 472 } else { 473 // regexp 474 Assert.ok(button.test(actualButton), "Button regexp"); 475 } 476 477 let menuButton = element._buttons.get("result-menu"); 478 Assert.ok(menuButton, "Menu button exists"); 479 Assert.ok(BrowserTestUtils.isVisible(menuButton), "Menu button is visible"); 480 481 let values = await Promise.all([awaitCallback(), pickTip()]); 482 Assert.ok(true, "Refresh dialog opened"); 483 484 // Ensure the urlbar is closed so that the engagement is ended. 485 await UrlbarTestUtils.promisePopupClose(window, () => gURLBar.blur()); 486 487 return values[0] || null; 488 }); 489 } 490 491 /** 492 * Starts a search and asserts that there are no tips. 493 * 494 * @param {string} searchString 495 * The search string. 496 * @param {Window} win 497 * The host window. 498 */ 499 async function awaitNoTip(searchString, win = window) { 500 let context = await UrlbarTestUtils.promiseAutocompleteResultPopup({ 501 window: win, 502 value: searchString, 503 waitForFocus, 504 fireInputEvent: true, 505 }); 506 for (let result of context.results) { 507 Assert.notEqual(result.type, UrlbarUtils.RESULT_TYPE.TIP); 508 } 509 } 510 511 /** 512 * Search tips helper. Asserts that a particular search tip is shown or that no 513 * search tip is shown. 514 * 515 * @param {window} win 516 * A browser window. 517 * @param {UrlbarProviderSearchTips.TIP_TYPE} expectedTip 518 * The expected search tip. Pass a falsey value (like zero) for none. 519 * @param {boolean} closeView 520 * If true, this function closes the urlbar view before returning. 521 */ 522 async function checkTip(win, expectedTip, closeView = true) { 523 if (!expectedTip) { 524 // Wait a bit for the tip to not show up. 525 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 526 await new Promise(resolve => setTimeout(resolve, 100)); 527 Assert.ok(!win.gURLBar.view.isOpen, "View is not open"); 528 return; 529 } 530 531 // Wait for the view to open, and then check the tip result. 532 await UrlbarTestUtils.promisePopupOpen(win, () => {}); 533 Assert.ok(true, "View opened"); 534 Assert.equal(UrlbarTestUtils.getResultCount(win), 1, "Number of results"); 535 let result = await UrlbarTestUtils.getDetailsOfResultAt(win, 0); 536 Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.TIP, "Result type"); 537 let heuristic; 538 let title; 539 let name = Services.search.defaultEngine.name; 540 switch (expectedTip) { 541 case UrlbarProviderSearchTips.TIP_TYPE.ONBOARD: 542 heuristic = true; 543 title = 544 `Type less, find more: Search ${name} right from your ` + 545 `address bar.`; 546 break; 547 case UrlbarProviderSearchTips.TIP_TYPE.REDIRECT: 548 heuristic = false; 549 title = 550 `Start your search in the address bar to see suggestions from ` + 551 `${name} and your browsing history.`; 552 break; 553 } 554 Assert.equal(result.heuristic, heuristic, "Result is heuristic"); 555 Assert.equal(result.displayed.title, title, "Title"); 556 Assert.equal( 557 result.element.row._buttons.get("0").textContent, 558 "Okay, Got It", 559 "Button text" 560 ); 561 Assert.ok( 562 !result.element.row._buttons.has("help"), 563 "Buttons in row does not include help" 564 ); 565 566 Assert.ok( 567 !UrlbarTestUtils.getOneOffSearchButtonsVisible(window), 568 "One-offs should be hidden when showing a search tip" 569 ); 570 571 if (closeView) { 572 await UrlbarTestUtils.promisePopupClose(win); 573 } 574 } 575 576 function makeTipResult({ 577 buttonUrl, 578 helpUrl = undefined, 579 descriptionL10n = undefined, 580 descriptionLearnMoreTopic = undefined, 581 }) { 582 return new UrlbarResult({ 583 type: UrlbarUtils.RESULT_TYPE.TIP, 584 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 585 payload: { 586 helpUrl, 587 descriptionL10n, 588 descriptionLearnMoreTopic, 589 type: "test", 590 titleL10n: { id: "urlbar-search-tips-confirm" }, 591 buttons: [ 592 { 593 url: buttonUrl, 594 l10n: { id: "urlbar-search-tips-confirm" }, 595 }, 596 ], 597 }, 598 }); 599 } 600 601 /** 602 * Search tips helper. Opens a foreground tab and asserts that a particular 603 * search tip is shown or that no search tip is shown. 604 * 605 * @param {window} win 606 * A browser window. 607 * @param {string} url 608 * The URL to load in a new foreground tab. 609 * @param {UrlbarProviderSearchTips.TIP_TYPE} expectedTip 610 * The expected search tip. Pass a falsey value (like zero) for none. 611 * @param {boolean} reset 612 * If true, the search tips provider will be reset before this function 613 * returns. See resetSearchTipsProvider. 614 */ 615 async function checkTab(win, url, expectedTip, reset = true) { 616 // BrowserTestUtils.withNewTab always waits for tab load, which hangs on 617 // about:newtab for some reason, so don't use it. 618 let shownCount; 619 if (expectedTip) { 620 shownCount = UrlbarPrefs.get(`tipShownCount.${expectedTip}`); 621 } 622 623 let tab = await BrowserTestUtils.openNewForegroundTab({ 624 gBrowser: win.gBrowser, 625 url, 626 waitForLoad: url != "about:newtab", 627 }); 628 629 await checkTip(win, expectedTip, true); 630 if (expectedTip) { 631 Assert.equal( 632 UrlbarPrefs.get(`tipShownCount.${expectedTip}`), 633 shownCount + 1, 634 "The shownCount pref should have been incremented by one." 635 ); 636 } 637 638 if (reset) { 639 resetSearchTipsProvider(); 640 } 641 642 BrowserTestUtils.removeTab(tab); 643 } 644 645 /** 646 * This lets us visit www.google.com (for example) and have it redirect to 647 * our test HTTP server instead of visiting the actual site. 648 * 649 * @param {string} domain 650 * The domain to which we are redirecting. 651 * @param {string} path 652 * The pathname on the domain. 653 * @param {Function} callback 654 * Executed when the test suite thinks `domain` is loaded. 655 */ 656 async function withDNSRedirect(domain, path, callback) { 657 // Some domains have special security requirements, like www.bing.com. We 658 // need to override them to successfully load them. This part is adapted from 659 // testing/marionette/cert.js. 660 const certOverrideService = Cc[ 661 "@mozilla.org/security/certoverride;1" 662 ].getService(Ci.nsICertOverrideService); 663 Services.prefs.setBoolPref( 664 "network.stricttransportsecurity.preloadlist", 665 false 666 ); 667 Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0); 668 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 669 true 670 ); 671 672 // Now set network.dns.localDomains to redirect the domain to localhost and 673 // set up an HTTP server. 674 Services.prefs.setCharPref("network.dns.localDomains", domain); 675 676 let server = new HttpServer(); 677 server.registerPathHandler(path, (req, resp) => { 678 resp.write(`Test! http://${domain}${path}`); 679 }); 680 server.start(-1); 681 server.identity.setPrimary("http", domain, server.identity.primaryPort); 682 let url = `http://${domain}:${server.identity.primaryPort}${path}`; 683 684 await callback(url); 685 686 // Reset network.dns.localDomains and stop the server. 687 Services.prefs.clearUserPref("network.dns.localDomains"); 688 await new Promise(resolve => server.stop(resolve)); 689 690 // Reset the security stuff. 691 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 692 false 693 ); 694 Services.prefs.clearUserPref("network.stricttransportsecurity.preloadlist"); 695 Services.prefs.clearUserPref("security.cert_pinning.enforcement_level"); 696 const sss = Cc["@mozilla.org/ssservice;1"].getService( 697 Ci.nsISiteSecurityService 698 ); 699 sss.clearAll(); 700 } 701 702 function resetSearchTipsProvider() { 703 Services.prefs.clearUserPref( 704 `browser.urlbar.tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.ONBOARD}` 705 ); 706 Services.prefs.clearUserPref( 707 `browser.urlbar.tipShownCount.${UrlbarProviderSearchTips.TIP_TYPE.REDIRECT}` 708 ); 709 UrlbarProvidersManager.getProvider( 710 "UrlbarProviderSearchTips" 711 ).disableTipsForCurrentSession = false; 712 } 713 714 async function setDefaultEngine(name) { 715 let engine = (await Services.search.getEngines()).find(e => e.name == name); 716 Assert.ok(engine); 717 await Services.search.setDefault( 718 engine, 719 Ci.nsISearchService.CHANGE_REASON_UNKNOWN 720 ); 721 }