test_search_suggestions.js (64945B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /** 5 * Tests that search engine suggestions are returned by 6 * UrlbarProviderSearchSuggestions. 7 */ 8 9 const lazy = {}; 10 11 ChromeUtils.defineESModuleGetters(lazy, { 12 sinon: "resource://testing-common/Sinon.sys.mjs", 13 }); 14 15 const SUGGEST_PREF = "browser.urlbar.suggest.searches"; 16 const SUGGEST_ENABLED_PREF = "browser.search.suggest.enabled"; 17 const PRIVATE_ENABLED_PREF = "browser.search.suggest.enabled.private"; 18 const PRIVATE_SEARCH_PREF = "browser.search.separatePrivateDefault.ui.enabled"; 19 const TAB_TO_SEARCH_PREF = "browser.urlbar.suggest.engines"; 20 const TRENDING_PREF = "browser.urlbar.trending.featureGate"; 21 const QUICKACTIONS_PREF = "browser.urlbar.suggest.quickactions"; 22 const MAX_RICH_RESULTS_PREF = "browser.urlbar.maxRichResults"; 23 const MAX_FORM_HISTORY_PREF = "browser.urlbar.maxHistoricalSearchSuggestions"; 24 const SHOW_SEARCH_SUGGESTIONS_FIRST_PREF = 25 "browser.urlbar.showSearchSuggestionsFirst"; 26 const SEARCH_STRING = "hello"; 27 28 const MAX_RESULTS = Services.prefs.getIntPref(MAX_RICH_RESULTS_PREF, 10); 29 30 var suggestionsFn; 31 var previousSuggestionsFn; 32 let port; 33 let sandbox; 34 35 /** 36 * Set the current suggestion funciton. 37 * 38 * @param {Function} fn 39 * A function that that a search string and returns an array of strings that 40 * will be used as search suggestions. 41 * Note: `fn` should return > 0 suggestions in most cases. Otherwise, you may 42 * encounter unexpected behaviour with UrlbarProviderSuggestion's 43 * _lastLowResultsSearchSuggestion safeguard. 44 */ 45 function setSuggestionsFn(fn) { 46 previousSuggestionsFn = suggestionsFn; 47 suggestionsFn = fn; 48 } 49 50 async function cleanup() { 51 Services.prefs.clearUserPref("browser.urlbar.autoFill"); 52 Services.prefs.clearUserPref(SUGGEST_PREF); 53 Services.prefs.clearUserPref(SUGGEST_ENABLED_PREF); 54 await PlacesUtils.bookmarks.eraseEverything(); 55 await PlacesUtils.history.clear(); 56 sandbox.restore(); 57 } 58 59 async function cleanUpSuggestions() { 60 await cleanup(); 61 if (previousSuggestionsFn) { 62 suggestionsFn = previousSuggestionsFn; 63 previousSuggestionsFn = null; 64 } 65 } 66 67 function makeFormHistoryResults(context, count) { 68 let results = []; 69 for (let i = 0; i < count; i++) { 70 results.push( 71 makeFormHistoryResult(context, { 72 suggestion: `${SEARCH_STRING} world Form History ${i}`, 73 engineName: SUGGESTIONS_ENGINE_NAME, 74 }) 75 ); 76 } 77 return results; 78 } 79 80 function makeRemoteSuggestionResults( 81 context, 82 { suggestionPrefix = SEARCH_STRING, query = undefined } = {} 83 ) { 84 // The suggestions function in `setup` returns: 85 // [searchString, searchString + "foo", searchString + "bar"] 86 // But when the heuristic is a search result, the muxer discards suggestion 87 // results that match the search string, and therefore we expect only two 88 // remote suggestion results, the "foo" and "bar" ones. 89 return [ 90 makeSearchResult(context, { 91 query, 92 engineName: SUGGESTIONS_ENGINE_NAME, 93 suggestion: suggestionPrefix + " foo", 94 }), 95 makeSearchResult(context, { 96 query, 97 engineName: SUGGESTIONS_ENGINE_NAME, 98 suggestion: suggestionPrefix + " bar", 99 }), 100 ]; 101 } 102 103 function setResultGroups(groups) { 104 sandbox.restore(); 105 sandbox.stub(UrlbarPrefs, "getResultGroups").returns({ 106 children: [ 107 // heuristic 108 { 109 maxResultCount: 1, 110 children: [ 111 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }, 112 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_EXTENSION }, 113 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_SEARCH_TIP }, 114 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_OMNIBOX }, 115 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_AUTOFILL }, 116 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TOKEN_ALIAS_ENGINE }, 117 { group: UrlbarUtils.RESULT_GROUP.HEURISTIC_FALLBACK }, 118 ], 119 }, 120 // extensions using the omnibox API 121 { 122 group: UrlbarUtils.RESULT_GROUP.OMNIBOX, 123 }, 124 ...groups, 125 ], 126 }); 127 } 128 129 add_setup(async function () { 130 sandbox = lazy.sinon.createSandbox(); 131 132 let engine = await addTestSuggestionsEngine(searchStr => { 133 return suggestionsFn(searchStr); 134 }); 135 port = engine.getSubmission("").uri.port; 136 137 setSuggestionsFn(searchStr => { 138 let suffixes = ["foo", "bar"]; 139 return [searchStr].concat(suffixes.map(s => searchStr + " " + s)); 140 }); 141 142 // Install the test engine. 143 let oldDefaultEngine = await Services.search.getDefault(); 144 registerCleanupFunction(async () => { 145 Services.search.setDefault( 146 oldDefaultEngine, 147 Ci.nsISearchService.CHANGE_REASON_UNKNOWN 148 ); 149 Services.prefs.clearUserPref(PRIVATE_SEARCH_PREF); 150 Services.prefs.clearUserPref(TRENDING_PREF); 151 Services.prefs.clearUserPref(QUICKACTIONS_PREF); 152 Services.prefs.clearUserPref(TAB_TO_SEARCH_PREF); 153 sandbox.restore(); 154 }); 155 Services.search.setDefault(engine, Ci.nsISearchService.CHANGE_REASON_UNKNOWN); 156 Services.prefs.setBoolPref(PRIVATE_SEARCH_PREF, false); 157 Services.prefs.setBoolPref(TRENDING_PREF, false); 158 Services.prefs.setBoolPref(QUICKACTIONS_PREF, false); 159 // Tab-to-search engines can introduce unexpected results, espescially because 160 // they depend on real en-US engines. 161 Services.prefs.setBoolPref(TAB_TO_SEARCH_PREF, false); 162 163 // Add MAX_RESULTS form history. 164 let context = createContext(SEARCH_STRING, { isPrivate: false }); 165 let entries = makeFormHistoryResults(context, MAX_RESULTS).map(r => ({ 166 value: r.payload.suggestion, 167 source: SUGGESTIONS_ENGINE_NAME, 168 })); 169 await UrlbarTestUtils.formHistory.add(entries); 170 }); 171 172 add_task(async function disabled_urlbarSuggestions() { 173 Services.prefs.setBoolPref(SUGGEST_PREF, false); 174 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 175 let context = createContext(SEARCH_STRING, { isPrivate: false }); 176 await check_results({ 177 context, 178 matches: [ 179 makeSearchResult(context, { 180 engineName: SUGGESTIONS_ENGINE_NAME, 181 heuristic: true, 182 }), 183 ], 184 }); 185 await cleanUpSuggestions(); 186 }); 187 188 add_task(async function disabled_allSuggestions() { 189 Services.prefs.setBoolPref(SUGGEST_PREF, true); 190 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, false); 191 let context = createContext(SEARCH_STRING, { isPrivate: false }); 192 await check_results({ 193 context, 194 matches: [ 195 makeSearchResult(context, { 196 engineName: SUGGESTIONS_ENGINE_NAME, 197 heuristic: true, 198 }), 199 ], 200 }); 201 await cleanUpSuggestions(); 202 }); 203 204 add_task(async function disabled_privateWindow() { 205 Services.prefs.setBoolPref(SUGGEST_PREF, true); 206 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 207 Services.prefs.setBoolPref(PRIVATE_ENABLED_PREF, false); 208 let context = createContext(SEARCH_STRING, { isPrivate: true }); 209 await check_results({ 210 context, 211 matches: [ 212 makeSearchResult(context, { 213 engineName: SUGGESTIONS_ENGINE_NAME, 214 heuristic: true, 215 }), 216 ], 217 }); 218 await cleanUpSuggestions(); 219 }); 220 221 add_task(async function disabled_urlbarSuggestions_withRestrictionToken() { 222 Services.prefs.setBoolPref(SUGGEST_PREF, false); 223 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 224 let context = createContext( 225 `${UrlbarTokenizer.RESTRICT.SEARCH} ${SEARCH_STRING}`, 226 { isPrivate: false } 227 ); 228 await check_results({ 229 context, 230 matches: [ 231 makeSearchResult(context, { 232 query: SEARCH_STRING, 233 alias: UrlbarTokenizer.RESTRICT.SEARCH, 234 engineName: SUGGESTIONS_ENGINE_NAME, 235 heuristic: true, 236 }), 237 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 238 ...makeRemoteSuggestionResults(context, { 239 query: SEARCH_STRING, 240 }), 241 ], 242 }); 243 await cleanUpSuggestions(); 244 }); 245 246 add_task( 247 async function disabled_urlbarSuggestions_withRestrictionToken_private() { 248 Services.prefs.setBoolPref(SUGGEST_PREF, false); 249 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 250 Services.prefs.setBoolPref(PRIVATE_ENABLED_PREF, false); 251 let context = createContext( 252 `${UrlbarTokenizer.RESTRICT.SEARCH} ${SEARCH_STRING}`, 253 { isPrivate: true } 254 ); 255 await check_results({ 256 context, 257 matches: [ 258 makeSearchResult(context, { 259 query: SEARCH_STRING, 260 alias: UrlbarTokenizer.RESTRICT.SEARCH, 261 engineName: SUGGESTIONS_ENGINE_NAME, 262 heuristic: true, 263 }), 264 ], 265 }); 266 await cleanUpSuggestions(); 267 } 268 ); 269 270 add_task( 271 async function disabled_urlbarSuggestions_withRestrictionToken_private_enabled() { 272 Services.prefs.setBoolPref(SUGGEST_PREF, false); 273 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 274 Services.prefs.setBoolPref(PRIVATE_ENABLED_PREF, true); 275 let context = createContext( 276 `${UrlbarTokenizer.RESTRICT.SEARCH} ${SEARCH_STRING}`, 277 { isPrivate: true } 278 ); 279 await check_results({ 280 context, 281 matches: [ 282 makeSearchResult(context, { 283 query: SEARCH_STRING, 284 alias: UrlbarTokenizer.RESTRICT.SEARCH, 285 engineName: SUGGESTIONS_ENGINE_NAME, 286 heuristic: true, 287 }), 288 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 289 ...makeRemoteSuggestionResults(context, { 290 query: SEARCH_STRING, 291 }), 292 ], 293 }); 294 await cleanUpSuggestions(); 295 } 296 ); 297 298 add_task(async function enabled_by_pref_privateWindow() { 299 Services.prefs.setBoolPref(SUGGEST_PREF, true); 300 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 301 Services.prefs.setBoolPref(PRIVATE_ENABLED_PREF, true); 302 let context = createContext(SEARCH_STRING, { isPrivate: true }); 303 await check_results({ 304 context, 305 matches: [ 306 makeSearchResult(context, { 307 engineName: SUGGESTIONS_ENGINE_NAME, 308 heuristic: true, 309 }), 310 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 311 ...makeRemoteSuggestionResults(context), 312 ], 313 }); 314 await cleanUpSuggestions(); 315 316 Services.prefs.clearUserPref(PRIVATE_ENABLED_PREF); 317 }); 318 319 add_task(async function singleWordQuery() { 320 Services.prefs.setBoolPref(SUGGEST_PREF, true); 321 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 322 let context = createContext(SEARCH_STRING, { isPrivate: false }); 323 324 await check_results({ 325 context, 326 matches: [ 327 makeSearchResult(context, { 328 engineName: SUGGESTIONS_ENGINE_NAME, 329 heuristic: true, 330 }), 331 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 332 ...makeRemoteSuggestionResults(context), 333 ], 334 }); 335 336 await cleanUpSuggestions(); 337 }); 338 339 add_task(async function multiWordQuery() { 340 Services.prefs.setBoolPref(SUGGEST_PREF, true); 341 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 342 const query = `${SEARCH_STRING} world`; 343 let context = createContext(query, { isPrivate: false }); 344 await check_results({ 345 context, 346 matches: [ 347 makeSearchResult(context, { 348 engineName: SUGGESTIONS_ENGINE_NAME, 349 heuristic: true, 350 }), 351 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 352 ...makeRemoteSuggestionResults(context, { 353 suggestionPrefix: query, 354 }), 355 ], 356 }); 357 358 await cleanUpSuggestions(); 359 }); 360 361 add_task(async function suffixMatch() { 362 Services.prefs.setBoolPref(SUGGEST_PREF, true); 363 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 364 365 setSuggestionsFn(searchStr => { 366 let prefixes = ["baz", "quux"]; 367 return prefixes.map(p => p + " " + searchStr); 368 }); 369 370 let context = createContext(SEARCH_STRING, { isPrivate: false }); 371 372 await check_results({ 373 context, 374 matches: [ 375 makeSearchResult(context, { 376 engineName: SUGGESTIONS_ENGINE_NAME, 377 heuristic: true, 378 }), 379 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 380 makeSearchResult(context, { 381 engineName: SUGGESTIONS_ENGINE_NAME, 382 suggestion: "baz " + SEARCH_STRING, 383 }), 384 makeSearchResult(context, { 385 engineName: SUGGESTIONS_ENGINE_NAME, 386 suggestion: "quux " + SEARCH_STRING, 387 }), 388 ], 389 }); 390 391 await cleanUpSuggestions(); 392 }); 393 394 add_task(async function remoteSuggestionsDupeSearchString() { 395 Services.prefs.setIntPref(MAX_FORM_HISTORY_PREF, 0); 396 397 // Return remote suggestions with the trimmed search string, the uppercased 398 // search string, and the search string with a trailing space, plus the usual 399 // "foo" and "bar" suggestions. 400 setSuggestionsFn(searchStr => { 401 let suffixes = ["foo", "bar"]; 402 return [searchStr.trim(), searchStr.toUpperCase(), searchStr + " "].concat( 403 suffixes.map(s => searchStr + " " + s) 404 ); 405 }); 406 407 // Do a search with a trailing space. All the variations of the search string 408 // with regard to spaces and case should be discarded from the remote 409 // suggestions, leaving only the usual "foo" and "bar" suggestions. 410 let query = SEARCH_STRING + " "; 411 let context = createContext(query, { isPrivate: false }); 412 await check_results({ 413 context, 414 matches: [ 415 makeSearchResult(context, { 416 query, 417 engineName: SUGGESTIONS_ENGINE_NAME, 418 heuristic: true, 419 }), 420 ...makeRemoteSuggestionResults(context), 421 ], 422 }); 423 424 await cleanUpSuggestions(); 425 Services.prefs.clearUserPref(MAX_FORM_HISTORY_PREF); 426 }); 427 428 add_task(async function queryIsNotASubstring() { 429 Services.prefs.setBoolPref(SUGGEST_PREF, true); 430 431 setSuggestionsFn(() => { 432 return ["aaa", "bbb"]; 433 }); 434 435 let context = createContext(SEARCH_STRING, { isPrivate: false }); 436 437 await check_results({ 438 context, 439 matches: [ 440 makeSearchResult(context, { 441 engineName: SUGGESTIONS_ENGINE_NAME, 442 heuristic: true, 443 }), 444 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 445 makeSearchResult(context, { 446 engineName: SUGGESTIONS_ENGINE_NAME, 447 suggestion: "aaa", 448 }), 449 makeSearchResult(context, { 450 engineName: SUGGESTIONS_ENGINE_NAME, 451 suggestion: "bbb", 452 }), 453 ], 454 }); 455 456 await cleanUpSuggestions(); 457 }); 458 459 add_task(async function restrictToken() { 460 Services.prefs.setBoolPref(SUGGEST_PREF, true); 461 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 462 463 // Add a visit and a bookmark. Actually, make the bookmark visited too so 464 // that it's guaranteed, with its higher frecency, to appear above the search 465 // suggestions. 466 await PlacesTestUtils.addVisits([ 467 { 468 uri: Services.io.newURI(`http://example.com/${SEARCH_STRING}-visit`), 469 title: `${SEARCH_STRING} visit`, 470 }, 471 { 472 uri: Services.io.newURI(`http://example.com/${SEARCH_STRING}-bookmark`), 473 title: `${SEARCH_STRING} bookmark`, 474 }, 475 ]); 476 477 await PlacesTestUtils.addBookmarkWithDetails({ 478 uri: Services.io.newURI(`http://example.com/${SEARCH_STRING}-bookmark`), 479 title: `${SEARCH_STRING} bookmark`, 480 }); 481 482 let context = createContext(SEARCH_STRING, { isPrivate: false }); 483 484 // Do an unrestricted search to make sure everything appears in it, including 485 // the visit and bookmark. 486 await check_results({ 487 context, 488 matches: [ 489 makeSearchResult(context, { 490 engineName: SUGGESTIONS_ENGINE_NAME, 491 heuristic: true, 492 }), 493 ...makeFormHistoryResults(context, MAX_RESULTS - 5), 494 ...makeRemoteSuggestionResults(context), 495 makeBookmarkResult(context, { 496 uri: `http://example.com/${SEARCH_STRING}-bookmark`, 497 title: `${SEARCH_STRING} bookmark`, 498 }), 499 makeVisitResult(context, { 500 uri: `http://example.com/${SEARCH_STRING}-visit`, 501 title: `${SEARCH_STRING} visit`, 502 }), 503 ], 504 }); 505 506 // Now do a restricted search to make sure only suggestions appear. 507 context = createContext( 508 `${UrlbarTokenizer.RESTRICT.SEARCH} ${SEARCH_STRING}`, 509 { 510 isPrivate: false, 511 } 512 ); 513 await check_results({ 514 context, 515 matches: [ 516 makeSearchResult(context, { 517 engineName: SUGGESTIONS_ENGINE_NAME, 518 alias: UrlbarTokenizer.RESTRICT.SEARCH, 519 query: SEARCH_STRING, 520 heuristic: true, 521 }), 522 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 523 ...makeRemoteSuggestionResults(context, { 524 suggestionPrefix: SEARCH_STRING, 525 query: SEARCH_STRING, 526 }), 527 ], 528 }); 529 530 // Typing the search restriction char shows the Search Engine entry and local 531 // results. 532 context = createContext(UrlbarTokenizer.RESTRICT.SEARCH, { 533 isPrivate: false, 534 }); 535 await check_results({ 536 context, 537 matches: [ 538 makeSearchResult(context, { 539 engineName: SUGGESTIONS_ENGINE_NAME, 540 query: "", 541 heuristic: true, 542 }), 543 ...makeFormHistoryResults(context, MAX_RESULTS - 1), 544 ], 545 }); 546 547 // Also if followed by multiple spaces. 548 context = createContext(`${UrlbarTokenizer.RESTRICT.SEARCH} `, { 549 isPrivate: false, 550 }); 551 await check_results({ 552 context, 553 matches: [ 554 makeSearchResult(context, { 555 engineName: SUGGESTIONS_ENGINE_NAME, 556 alias: UrlbarTokenizer.RESTRICT.SEARCH, 557 query: "", 558 heuristic: true, 559 }), 560 ...makeFormHistoryResults(context, MAX_RESULTS - 1), 561 ], 562 }); 563 564 // If followed by any char we should fetch suggestions. 565 // Note this uses "h" to match form history. 566 context = createContext(`${UrlbarTokenizer.RESTRICT.SEARCH}h`, { 567 isPrivate: false, 568 }); 569 await check_results({ 570 context, 571 matches: [ 572 makeSearchResult(context, { 573 engineName: SUGGESTIONS_ENGINE_NAME, 574 query: "h", 575 heuristic: true, 576 }), 577 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 578 ...makeRemoteSuggestionResults(context, { 579 suggestionPrefix: "h", 580 query: "h", 581 }), 582 ], 583 }); 584 585 // Also if followed by a space and single char. 586 context = createContext(`${UrlbarTokenizer.RESTRICT.SEARCH} h`, { 587 isPrivate: false, 588 }); 589 await check_results({ 590 context, 591 matches: [ 592 makeSearchResult(context, { 593 engineName: SUGGESTIONS_ENGINE_NAME, 594 alias: UrlbarTokenizer.RESTRICT.SEARCH, 595 query: "h", 596 heuristic: true, 597 }), 598 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 599 ...makeRemoteSuggestionResults(context, { 600 suggestionPrefix: "h", 601 query: "h", 602 }), 603 ], 604 }); 605 606 // Leading search-mode restriction tokens are removed. 607 context = createContext( 608 `${UrlbarTokenizer.RESTRICT.BOOKMARK} ${SEARCH_STRING}`, 609 { isPrivate: false } 610 ); 611 await check_results({ 612 context, 613 matches: [ 614 makeSearchResult(context, { 615 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 616 heuristic: true, 617 query: SEARCH_STRING, 618 alias: UrlbarTokenizer.RESTRICT.BOOKMARK, 619 }), 620 makeBookmarkResult(context, { 621 uri: `http://example.com/${SEARCH_STRING}-bookmark`, 622 title: `${SEARCH_STRING} bookmark`, 623 }), 624 ], 625 }); 626 627 // Non-search-mode restriction tokens remain in the query and heuristic search 628 // result. 629 let token; 630 for (let t of Object.values(UrlbarTokenizer.RESTRICT)) { 631 if (!UrlbarTokenizer.SEARCH_MODE_RESTRICT.has(t)) { 632 token = t; 633 break; 634 } 635 } 636 Assert.ok( 637 token, 638 "Non-search-mode restrict token exists -- if not, you can probably remove me!" 639 ); 640 context = createContext(token, { 641 isPrivate: false, 642 }); 643 await check_results({ 644 context, 645 matches: [ 646 makeSearchResult(context, { 647 engineName: SUGGESTIONS_ENGINE_NAME, 648 heuristic: true, 649 }), 650 ], 651 }); 652 653 await cleanUpSuggestions(); 654 }); 655 656 add_task(async function mixup_frecency() { 657 Services.prefs.setBoolPref(SUGGEST_PREF, true); 658 659 // At most, we should have 22 results in this subtest. We set this to 30 to 660 // make we're not cutting off any results and we are actually getting 22. 661 Services.prefs.setIntPref(MAX_RICH_RESULTS_PREF, 30); 662 663 // Add a visit and a bookmark. Actually, make the bookmark visited too so 664 // that it's guaranteed, with its higher frecency, to appear above the search 665 // suggestions. 666 await PlacesTestUtils.addVisits([ 667 { 668 uri: Services.io.newURI("http://example.com/lo0"), 669 title: `${SEARCH_STRING} low frecency 0`, 670 }, 671 { 672 uri: Services.io.newURI("http://example.com/lo1"), 673 title: `${SEARCH_STRING} low frecency 1`, 674 }, 675 { 676 uri: Services.io.newURI("http://example.com/lo2"), 677 title: `${SEARCH_STRING} low frecency 2`, 678 }, 679 { 680 uri: Services.io.newURI("http://example.com/lo3"), 681 title: `${SEARCH_STRING} low frecency 3`, 682 }, 683 { 684 uri: Services.io.newURI("http://example.com/lo4"), 685 title: `${SEARCH_STRING} low frecency 4`, 686 }, 687 ]); 688 689 for (let i = 0; i < 5; i++) { 690 await PlacesTestUtils.addVisits([ 691 { 692 uri: Services.io.newURI("http://example.com/hi0"), 693 title: `${SEARCH_STRING} high frecency 0`, 694 transition: Ci.nsINavHistoryService.TRANSITION_TYPED, 695 }, 696 { 697 uri: Services.io.newURI("http://example.com/hi1"), 698 title: `${SEARCH_STRING} high frecency 1`, 699 transition: Ci.nsINavHistoryService.TRANSITION_TYPED, 700 }, 701 { 702 uri: Services.io.newURI("http://example.com/hi2"), 703 title: `${SEARCH_STRING} high frecency 2`, 704 transition: Ci.nsINavHistoryService.TRANSITION_TYPED, 705 }, 706 { 707 uri: Services.io.newURI("http://example.com/hi3"), 708 title: `${SEARCH_STRING} high frecency 3`, 709 transition: Ci.nsINavHistoryService.TRANSITION_TYPED, 710 }, 711 ]); 712 } 713 714 for (let i = 0; i < 4; i++) { 715 let href = `http://example.com/hi${i}`; 716 await PlacesTestUtils.addBookmarkWithDetails({ 717 uri: href, 718 title: `${SEARCH_STRING} high frecency ${i}`, 719 }); 720 } 721 722 // Do an unrestricted search to make sure everything appears in it, including 723 // the visit and bookmark. 724 let context = createContext(SEARCH_STRING, { isPrivate: false }); 725 await check_results({ 726 context, 727 matches: [ 728 makeSearchResult(context, { 729 engineName: SUGGESTIONS_ENGINE_NAME, 730 heuristic: true, 731 }), 732 ...makeFormHistoryResults(context, MAX_RESULTS), 733 ...makeRemoteSuggestionResults(context), 734 makeBookmarkResult(context, { 735 uri: "http://example.com/hi3", 736 title: `${SEARCH_STRING} high frecency 3`, 737 }), 738 makeBookmarkResult(context, { 739 uri: "http://example.com/hi2", 740 title: `${SEARCH_STRING} high frecency 2`, 741 }), 742 makeBookmarkResult(context, { 743 uri: "http://example.com/hi1", 744 title: `${SEARCH_STRING} high frecency 1`, 745 }), 746 makeBookmarkResult(context, { 747 uri: "http://example.com/hi0", 748 title: `${SEARCH_STRING} high frecency 0`, 749 }), 750 makeVisitResult(context, { 751 uri: "http://example.com/lo4", 752 title: `${SEARCH_STRING} low frecency 4`, 753 }), 754 makeVisitResult(context, { 755 uri: "http://example.com/lo3", 756 title: `${SEARCH_STRING} low frecency 3`, 757 }), 758 makeVisitResult(context, { 759 uri: "http://example.com/lo2", 760 title: `${SEARCH_STRING} low frecency 2`, 761 }), 762 makeVisitResult(context, { 763 uri: "http://example.com/lo1", 764 title: `${SEARCH_STRING} low frecency 1`, 765 }), 766 makeVisitResult(context, { 767 uri: "http://example.com/lo0", 768 title: `${SEARCH_STRING} low frecency 0`, 769 }), 770 ], 771 }); 772 773 // Change the mixup. 774 setResultGroups([ 775 // 1 suggestion 776 { 777 maxResultCount: 1, 778 children: [ 779 { group: UrlbarUtils.RESULT_GROUP.FORM_HISTORY }, 780 { group: UrlbarUtils.RESULT_GROUP.REMOTE_SUGGESTION }, 781 ], 782 }, 783 // 5 general 784 { 785 maxResultCount: 5, 786 group: UrlbarUtils.RESULT_GROUP.GENERAL, 787 }, 788 // 1 suggestion 789 { 790 maxResultCount: 1, 791 children: [ 792 { group: UrlbarUtils.RESULT_GROUP.FORM_HISTORY }, 793 { group: UrlbarUtils.RESULT_GROUP.REMOTE_SUGGESTION }, 794 ], 795 }, 796 // remaining general 797 { group: UrlbarUtils.RESULT_GROUP.GENERAL }, 798 // remaining suggestions 799 { group: UrlbarUtils.RESULT_GROUP.FORM_HISTORY }, 800 { group: UrlbarUtils.RESULT_GROUP.REMOTE_SUGGESTION }, 801 ]); 802 803 // Do an unrestricted search to make sure everything appears in it, including 804 // the visits and bookmarks. 805 context = createContext(SEARCH_STRING, { isPrivate: false }); 806 await check_results({ 807 context, 808 matches: [ 809 makeSearchResult(context, { 810 engineName: SUGGESTIONS_ENGINE_NAME, 811 heuristic: true, 812 }), 813 ...makeFormHistoryResults(context, 1), 814 makeBookmarkResult(context, { 815 uri: "http://example.com/hi3", 816 title: `${SEARCH_STRING} high frecency 3`, 817 }), 818 makeBookmarkResult(context, { 819 uri: "http://example.com/hi2", 820 title: `${SEARCH_STRING} high frecency 2`, 821 }), 822 makeBookmarkResult(context, { 823 uri: "http://example.com/hi1", 824 title: `${SEARCH_STRING} high frecency 1`, 825 }), 826 makeBookmarkResult(context, { 827 uri: "http://example.com/hi0", 828 title: `${SEARCH_STRING} high frecency 0`, 829 }), 830 makeVisitResult(context, { 831 uri: "http://example.com/lo4", 832 title: `${SEARCH_STRING} low frecency 4`, 833 }), 834 ...makeFormHistoryResults(context, 2).slice(1), 835 makeVisitResult(context, { 836 uri: "http://example.com/lo3", 837 title: `${SEARCH_STRING} low frecency 3`, 838 }), 839 makeVisitResult(context, { 840 uri: "http://example.com/lo2", 841 title: `${SEARCH_STRING} low frecency 2`, 842 }), 843 makeVisitResult(context, { 844 uri: "http://example.com/lo1", 845 title: `${SEARCH_STRING} low frecency 1`, 846 }), 847 makeVisitResult(context, { 848 uri: "http://example.com/lo0", 849 title: `${SEARCH_STRING} low frecency 0`, 850 }), 851 ...makeFormHistoryResults(context, MAX_RESULTS).slice(2), 852 ...makeRemoteSuggestionResults(context), 853 ], 854 }); 855 856 Services.prefs.clearUserPref(MAX_RICH_RESULTS_PREF); 857 await cleanUpSuggestions(); 858 }); 859 860 add_task(async function prohibit_suggestions() { 861 Services.prefs.setBoolPref(SUGGEST_PREF, true); 862 Services.prefs.setBoolPref( 863 `browser.fixup.domainwhitelist.${SEARCH_STRING}`, 864 false 865 ); 866 867 let context = createContext(SEARCH_STRING, { isPrivate: false }); 868 await check_results({ 869 context, 870 matches: [ 871 makeSearchResult(context, { 872 engineName: SUGGESTIONS_ENGINE_NAME, 873 heuristic: true, 874 }), 875 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 876 ...makeRemoteSuggestionResults(context), 877 ], 878 }); 879 880 Services.prefs.setBoolPref( 881 `browser.fixup.domainwhitelist.${SEARCH_STRING}`, 882 true 883 ); 884 registerCleanupFunction(() => { 885 Services.prefs.setBoolPref( 886 `browser.fixup.domainwhitelist.${SEARCH_STRING}`, 887 false 888 ); 889 }); 890 context = createContext(SEARCH_STRING, { isPrivate: false }); 891 await check_results({ 892 context, 893 matches: [ 894 makeVisitResult(context, { 895 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 896 uri: `http://${SEARCH_STRING}/`, 897 title: `${SEARCH_STRING}/`, 898 iconUri: "", 899 heuristic: true, 900 }), 901 ...makeFormHistoryResults(context, MAX_RESULTS - 2), 902 makeSearchResult(context, { 903 engineName: SUGGESTIONS_ENGINE_NAME, 904 heuristic: false, 905 }), 906 ], 907 }); 908 909 // When using multiple words, we should still get suggestions: 910 let query = `${SEARCH_STRING} world`; 911 context = createContext(query, { isPrivate: false }); 912 await check_results({ 913 context, 914 matches: [ 915 makeSearchResult(context, { 916 engineName: SUGGESTIONS_ENGINE_NAME, 917 heuristic: true, 918 }), 919 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 920 ...makeRemoteSuggestionResults(context, { suggestionPrefix: query }), 921 ], 922 }); 923 924 // Clear the whitelist for SEARCH_STRING and try preferring DNS for any single 925 // word instead: 926 Services.prefs.setBoolPref( 927 `browser.fixup.domainwhitelist.${SEARCH_STRING}`, 928 false 929 ); 930 Services.prefs.setBoolPref("browser.fixup.dns_first_for_single_words", true); 931 registerCleanupFunction(() => { 932 Services.prefs.clearUserPref("browser.fixup.dns_first_for_single_words"); 933 }); 934 935 context = createContext(SEARCH_STRING, { isPrivate: false }); 936 await check_results({ 937 context, 938 matches: [ 939 makeVisitResult(context, { 940 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 941 uri: `http://${SEARCH_STRING}/`, 942 title: `${SEARCH_STRING}/`, 943 iconUri: "", 944 heuristic: true, 945 }), 946 ...makeFormHistoryResults(context, MAX_RESULTS - 2), 947 makeSearchResult(context, { 948 engineName: SUGGESTIONS_ENGINE_NAME, 949 heuristic: false, 950 }), 951 ], 952 }); 953 954 context = createContext("somethingelse", { isPrivate: false }); 955 await check_results({ 956 context, 957 matches: [ 958 makeVisitResult(context, { 959 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 960 uri: "http://somethingelse/", 961 title: "somethingelse/", 962 iconUri: "", 963 heuristic: true, 964 }), 965 makeSearchResult(context, { 966 engineName: SUGGESTIONS_ENGINE_NAME, 967 heuristic: false, 968 }), 969 ], 970 }); 971 972 // When using multiple words, we should still get suggestions: 973 query = `${SEARCH_STRING} world`; 974 context = createContext(query, { isPrivate: false }); 975 await check_results({ 976 context, 977 matches: [ 978 makeSearchResult(context, { 979 engineName: SUGGESTIONS_ENGINE_NAME, 980 heuristic: true, 981 }), 982 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 983 ...makeRemoteSuggestionResults(context, { suggestionPrefix: query }), 984 ], 985 }); 986 987 Services.prefs.clearUserPref("browser.fixup.dns_first_for_single_words"); 988 989 context = createContext("http://1.2.3.4/", { isPrivate: false }); 990 await check_results({ 991 context, 992 matches: [ 993 makeVisitResult(context, { 994 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 995 uri: "http://1.2.3.4/", 996 title: "http://1.2.3.4/", 997 iconUri: "page-icon:http://1.2.3.4/", 998 heuristic: true, 999 }), 1000 ], 1001 }); 1002 1003 context = createContext("[2001::1]:30", { isPrivate: false }); 1004 await check_results({ 1005 context, 1006 matches: [ 1007 makeVisitResult(context, { 1008 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1009 uri: "http://[2001::1]:30/", 1010 title: "[2001::1]:30/", 1011 iconUri: "", 1012 heuristic: true, 1013 }), 1014 ], 1015 }); 1016 1017 context = createContext("user:pass@test", { isPrivate: false }); 1018 await check_results({ 1019 context, 1020 matches: [ 1021 makeVisitResult(context, { 1022 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1023 uri: "http://user:pass@test/", 1024 title: "user:pass@test/", 1025 iconUri: "", 1026 heuristic: true, 1027 }), 1028 ], 1029 }); 1030 1031 context = createContext("user:pass@mozilla.org", { isPrivate: false }); 1032 await check_results({ 1033 context, 1034 matches: [ 1035 makeVisitResult(context, { 1036 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1037 uri: "http://user:pass@mozilla.org/", 1038 title: "user:pass@mozilla.org/", 1039 iconUri: "", 1040 heuristic: true, 1041 }), 1042 ], 1043 }); 1044 1045 context = createContext("mozilla.org:1234", { isPrivate: false }); 1046 await check_results({ 1047 context, 1048 matches: [ 1049 makeVisitResult(context, { 1050 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1051 uri: "http://mozilla.org:1234/", 1052 title: "mozilla.org:1234/", 1053 iconUri: "", 1054 heuristic: true, 1055 }), 1056 ], 1057 }); 1058 1059 context = createContext("data:text/plain,Content", { isPrivate: false }); 1060 await check_results({ 1061 context, 1062 matches: [ 1063 makeVisitResult(context, { 1064 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1065 uri: "data:text/plain,Content", 1066 title: "data:text/plain,Content", 1067 iconUri: "", 1068 heuristic: true, 1069 }), 1070 ], 1071 }); 1072 1073 context = createContext("a", { isPrivate: false }); 1074 await check_results({ 1075 context, 1076 matches: [ 1077 makeSearchResult(context, { 1078 engineName: SUGGESTIONS_ENGINE_NAME, 1079 heuristic: true, 1080 }), 1081 ], 1082 }); 1083 1084 await cleanUpSuggestions(); 1085 }); 1086 1087 add_task(async function simple_origin_queries() { 1088 Services.prefs.setBoolPref(SUGGEST_PREF, true); 1089 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 1090 1091 // Queries that are actual simple origins should return search suggestions 1092 // only when `allowSearchSuggestionsForSimpleOrigins` is true. 1093 let queries = ["mozilla.org", "example.com", "example.co", "readme.md"]; 1094 1095 for (let allow of [false, true]) { 1096 Services.prefs.setBoolPref( 1097 "browser.urlbar.allowSearchSuggestionsForSimpleOrigins", 1098 allow 1099 ); 1100 1101 for (let query of queries) { 1102 info("Testing: " + JSON.stringify({ allow, query })); 1103 1104 let context = createContext(query, { isPrivate: false }); 1105 let expected = [ 1106 makeVisitResult(context, { 1107 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1108 title: `${query}/`, 1109 uri: `http://${query}/`, 1110 iconUri: "", 1111 heuristic: true, 1112 }), 1113 ]; 1114 if (allow) { 1115 expected.push( 1116 ...makeRemoteSuggestionResults(context, { suggestionPrefix: query }) 1117 ); 1118 } 1119 expected.push( 1120 makeSearchResult(context, { 1121 query, 1122 engineName: SUGGESTIONS_ENGINE_NAME, 1123 }) 1124 ); 1125 1126 await check_results({ 1127 context, 1128 matches: expected, 1129 }); 1130 } 1131 } 1132 1133 await cleanUpSuggestions(); 1134 Services.prefs.clearUserPref( 1135 "browser.urlbar.allowSearchSuggestionsForSimpleOrigins" 1136 ); 1137 }); 1138 1139 add_task(async function simple_origin_like_queries() { 1140 Services.prefs.setBoolPref(SUGGEST_PREF, true); 1141 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 1142 1143 // Queries that aren't simple origins but look like simple origins should 1144 // return search suggestions only when 1145 // `allowSearchSuggestionsForSimpleOrigins` is true. 1146 let queries = ["mozilla.o", "mozilla."]; 1147 1148 for (let allow of [false, true]) { 1149 Services.prefs.setBoolPref( 1150 "browser.urlbar.allowSearchSuggestionsForSimpleOrigins", 1151 allow 1152 ); 1153 1154 for (let query of queries) { 1155 info("Testing: " + JSON.stringify({ allow, query })); 1156 1157 let context = createContext(query, { isPrivate: false }); 1158 let expected = [ 1159 makeSearchResult(context, { 1160 engineName: SUGGESTIONS_ENGINE_NAME, 1161 heuristic: true, 1162 }), 1163 ]; 1164 if (allow) { 1165 expected.push( 1166 ...makeRemoteSuggestionResults(context, { suggestionPrefix: query }) 1167 ); 1168 } 1169 1170 await check_results({ 1171 context, 1172 matches: expected, 1173 }); 1174 } 1175 } 1176 1177 await cleanUpSuggestions(); 1178 Services.prefs.clearUserPref( 1179 "browser.urlbar.allowSearchSuggestionsForSimpleOrigins" 1180 ); 1181 }); 1182 1183 add_task(async function uri_like_queries() { 1184 Services.prefs.setBoolPref(SUGGEST_PREF, true); 1185 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 1186 1187 // Queries that don't look like origins but that might be confused for URLs 1188 // should always return search suggestions regardless of 1189 // `allowSearchSuggestionsForSimpleOrigins`. 1190 const uriLikeQueries = [ 1191 "mozilla.org is a great website", 1192 "I like mozilla.org", 1193 "a/b testing", 1194 "he/him", 1195 "Google vs.", 1196 "5.8 cm", 1197 ]; 1198 1199 for (let allow of [false, true]) { 1200 Services.prefs.setBoolPref( 1201 "browser.urlbar.allowSearchSuggestionsForSimpleOrigins", 1202 allow 1203 ); 1204 1205 for (let query of uriLikeQueries) { 1206 info("Testing: " + JSON.stringify({ allow, query })); 1207 1208 let context = createContext(query, { isPrivate: false }); 1209 await check_results({ 1210 context, 1211 matches: [ 1212 makeSearchResult(context, { 1213 engineName: SUGGESTIONS_ENGINE_NAME, 1214 heuristic: true, 1215 }), 1216 ...makeRemoteSuggestionResults(context, { 1217 suggestionPrefix: query, 1218 }), 1219 ], 1220 }); 1221 } 1222 } 1223 1224 await cleanUpSuggestions(); 1225 Services.prefs.clearUserPref( 1226 "browser.urlbar.allowSearchSuggestionsForSimpleOrigins" 1227 ); 1228 }); 1229 1230 add_task(async function avoid_remote_url_suggestions_1() { 1231 Services.prefs.setBoolPref(SUGGEST_PREF, true); 1232 setSuggestionsFn(searchStr => { 1233 let suffixes = [".com", "/test", ":1]", "@test", ". com"]; 1234 return suffixes.map(s => searchStr + s); 1235 }); 1236 1237 const query = "test"; 1238 1239 await UrlbarTestUtils.formHistory.add([`${query}.com`]); 1240 1241 let context = createContext(query, { isPrivate: false }); 1242 await check_results({ 1243 context, 1244 matches: [ 1245 makeSearchResult(context, { 1246 engineName: SUGGESTIONS_ENGINE_NAME, 1247 heuristic: true, 1248 }), 1249 makeFormHistoryResult(context, { 1250 engineName: SUGGESTIONS_ENGINE_NAME, 1251 suggestion: `${query}.com`, 1252 }), 1253 makeSearchResult(context, { 1254 engineName: SUGGESTIONS_ENGINE_NAME, 1255 suggestion: `${query}. com`, 1256 }), 1257 ], 1258 }); 1259 1260 await cleanUpSuggestions(); 1261 await UrlbarTestUtils.formHistory.remove([`${query}.com`]); 1262 }); 1263 1264 add_task(async function avoid_remote_url_suggestions_2() { 1265 Services.prefs.setBoolPref(SUGGEST_PREF, true); 1266 Services.prefs.setBoolPref("browser.urlbar.autoFill", false); 1267 1268 setSuggestionsFn(searchStr => { 1269 let suffixes = ["ed", "eds"]; 1270 return suffixes.map(s => searchStr + s); 1271 }); 1272 1273 let context = createContext("htt", { isPrivate: false }); 1274 await check_results({ 1275 context, 1276 matches: [ 1277 makeSearchResult(context, { 1278 engineName: SUGGESTIONS_ENGINE_NAME, 1279 heuristic: true, 1280 }), 1281 makeSearchResult(context, { 1282 engineName: SUGGESTIONS_ENGINE_NAME, 1283 suggestion: "htted", 1284 }), 1285 makeSearchResult(context, { 1286 engineName: SUGGESTIONS_ENGINE_NAME, 1287 suggestion: "htteds", 1288 }), 1289 ], 1290 }); 1291 1292 context = createContext("ftp", { isPrivate: false }); 1293 await check_results({ 1294 context, 1295 matches: [ 1296 makeSearchResult(context, { 1297 engineName: SUGGESTIONS_ENGINE_NAME, 1298 heuristic: true, 1299 }), 1300 makeSearchResult(context, { 1301 engineName: SUGGESTIONS_ENGINE_NAME, 1302 suggestion: "ftped", 1303 }), 1304 makeSearchResult(context, { 1305 engineName: SUGGESTIONS_ENGINE_NAME, 1306 suggestion: "ftpeds", 1307 }), 1308 ], 1309 }); 1310 1311 context = createContext("http", { isPrivate: false }); 1312 await check_results({ 1313 context, 1314 matches: [ 1315 makeSearchResult(context, { 1316 engineName: SUGGESTIONS_ENGINE_NAME, 1317 heuristic: true, 1318 }), 1319 makeSearchResult(context, { 1320 engineName: SUGGESTIONS_ENGINE_NAME, 1321 suggestion: "httped", 1322 }), 1323 makeSearchResult(context, { 1324 engineName: SUGGESTIONS_ENGINE_NAME, 1325 suggestion: "httpeds", 1326 }), 1327 ], 1328 }); 1329 1330 context = createContext("http:", { isPrivate: false }); 1331 await check_results({ 1332 context, 1333 matches: [ 1334 makeSearchResult(context, { 1335 engineName: SUGGESTIONS_ENGINE_NAME, 1336 heuristic: true, 1337 }), 1338 ], 1339 }); 1340 1341 context = createContext("https", { isPrivate: false }); 1342 await check_results({ 1343 context, 1344 matches: [ 1345 makeSearchResult(context, { 1346 engineName: SUGGESTIONS_ENGINE_NAME, 1347 heuristic: true, 1348 }), 1349 makeSearchResult(context, { 1350 engineName: SUGGESTIONS_ENGINE_NAME, 1351 suggestion: "httpsed", 1352 }), 1353 makeSearchResult(context, { 1354 engineName: SUGGESTIONS_ENGINE_NAME, 1355 suggestion: "httpseds", 1356 }), 1357 ], 1358 }); 1359 1360 context = createContext("https:", { isPrivate: false }); 1361 await check_results({ 1362 context, 1363 matches: [ 1364 makeSearchResult(context, { 1365 engineName: SUGGESTIONS_ENGINE_NAME, 1366 heuristic: true, 1367 }), 1368 ], 1369 }); 1370 1371 context = createContext("httpd", { isPrivate: false }); 1372 await check_results({ 1373 context, 1374 matches: [ 1375 makeSearchResult(context, { 1376 engineName: SUGGESTIONS_ENGINE_NAME, 1377 heuristic: true, 1378 }), 1379 makeSearchResult(context, { 1380 engineName: SUGGESTIONS_ENGINE_NAME, 1381 suggestion: "httpded", 1382 }), 1383 makeSearchResult(context, { 1384 engineName: SUGGESTIONS_ENGINE_NAME, 1385 suggestion: "httpdeds", 1386 }), 1387 ], 1388 }); 1389 1390 // Check FTP disabled 1391 context = createContext("ftp:", { isPrivate: false }); 1392 await check_results({ 1393 context, 1394 matches: [ 1395 makeSearchResult(context, { 1396 engineName: SUGGESTIONS_ENGINE_NAME, 1397 heuristic: true, 1398 }), 1399 ], 1400 }); 1401 1402 context = createContext("ftp:/", { isPrivate: false }); 1403 await check_results({ 1404 context, 1405 matches: [ 1406 makeSearchResult(context, { 1407 engineName: SUGGESTIONS_ENGINE_NAME, 1408 heuristic: true, 1409 }), 1410 ], 1411 }); 1412 1413 context = createContext("ftp://", { isPrivate: false }); 1414 await check_results({ 1415 context, 1416 matches: [ 1417 makeSearchResult(context, { 1418 engineName: SUGGESTIONS_ENGINE_NAME, 1419 heuristic: true, 1420 }), 1421 ], 1422 }); 1423 1424 context = createContext("ftp://test", { isPrivate: false }); 1425 await check_results({ 1426 context, 1427 matches: [ 1428 makeVisitResult(context, { 1429 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1430 uri: "ftp://test/", 1431 title: "ftp://test/", 1432 iconUri: "", 1433 heuristic: true, 1434 }), 1435 ], 1436 }); 1437 1438 context = createContext("http:/", { isPrivate: false }); 1439 await check_results({ 1440 context, 1441 matches: [ 1442 makeSearchResult(context, { 1443 engineName: SUGGESTIONS_ENGINE_NAME, 1444 heuristic: true, 1445 }), 1446 ], 1447 }); 1448 1449 context = createContext("https:/", { isPrivate: false }); 1450 await check_results({ 1451 context, 1452 matches: [ 1453 makeSearchResult(context, { 1454 engineName: SUGGESTIONS_ENGINE_NAME, 1455 heuristic: true, 1456 }), 1457 ], 1458 }); 1459 1460 context = createContext("http://", { isPrivate: false }); 1461 await check_results({ 1462 context, 1463 matches: [ 1464 makeSearchResult(context, { 1465 engineName: SUGGESTIONS_ENGINE_NAME, 1466 heuristic: true, 1467 }), 1468 ], 1469 }); 1470 1471 context = createContext("https://", { isPrivate: false }); 1472 await check_results({ 1473 context, 1474 matches: [ 1475 makeSearchResult(context, { 1476 engineName: SUGGESTIONS_ENGINE_NAME, 1477 heuristic: true, 1478 }), 1479 ], 1480 }); 1481 1482 context = createContext("http://www", { isPrivate: false }); 1483 await check_results({ 1484 context, 1485 matches: [ 1486 makeVisitResult(context, { 1487 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1488 uri: "http://www/", 1489 title: "http://www/", 1490 iconUri: "", 1491 heuristic: true, 1492 }), 1493 ], 1494 }); 1495 1496 context = createContext("https://www", { isPrivate: false }); 1497 await check_results({ 1498 context, 1499 matches: [ 1500 makeVisitResult(context, { 1501 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1502 uri: "https://www/", 1503 title: "https://www/", 1504 iconUri: "", 1505 heuristic: true, 1506 }), 1507 ], 1508 }); 1509 1510 context = createContext("http://test", { isPrivate: false }); 1511 await check_results({ 1512 context, 1513 matches: [ 1514 makeVisitResult(context, { 1515 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1516 uri: "http://test/", 1517 title: "http://test/", 1518 iconUri: "", 1519 heuristic: true, 1520 }), 1521 ], 1522 }); 1523 1524 context = createContext("https://test", { isPrivate: false }); 1525 await check_results({ 1526 context, 1527 matches: [ 1528 makeVisitResult(context, { 1529 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1530 uri: "https://test/", 1531 title: "https://test/", 1532 iconUri: "", 1533 heuristic: true, 1534 }), 1535 ], 1536 }); 1537 1538 context = createContext("http://www.test", { isPrivate: false }); 1539 await check_results({ 1540 context, 1541 matches: [ 1542 makeVisitResult(context, { 1543 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1544 uri: "http://www.test/", 1545 title: "http://www.test/", 1546 iconUri: "", 1547 heuristic: true, 1548 }), 1549 ], 1550 }); 1551 1552 context = createContext("http://www.test.com", { isPrivate: false }); 1553 await check_results({ 1554 context, 1555 matches: [ 1556 makeVisitResult(context, { 1557 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1558 uri: "http://www.test.com/", 1559 title: "http://www.test.com/", 1560 iconUri: "", 1561 heuristic: true, 1562 }), 1563 ], 1564 }); 1565 1566 context = createContext("file", { isPrivate: false }); 1567 await check_results({ 1568 context, 1569 matches: [ 1570 makeSearchResult(context, { 1571 engineName: SUGGESTIONS_ENGINE_NAME, 1572 heuristic: true, 1573 }), 1574 makeSearchResult(context, { 1575 engineName: SUGGESTIONS_ENGINE_NAME, 1576 suggestion: "fileed", 1577 }), 1578 makeSearchResult(context, { 1579 engineName: SUGGESTIONS_ENGINE_NAME, 1580 suggestion: "fileeds", 1581 }), 1582 ], 1583 }); 1584 1585 context = createContext("file:", { isPrivate: false }); 1586 await check_results({ 1587 context, 1588 matches: [ 1589 makeSearchResult(context, { 1590 engineName: SUGGESTIONS_ENGINE_NAME, 1591 heuristic: true, 1592 }), 1593 ], 1594 }); 1595 1596 context = createContext("file:///Users", { isPrivate: false }); 1597 await check_results({ 1598 context, 1599 matches: [ 1600 makeVisitResult(context, { 1601 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 1602 uri: "file:///Users", 1603 title: "file:///Users", 1604 iconUri: "", 1605 heuristic: true, 1606 }), 1607 ], 1608 }); 1609 1610 context = createContext("moz-test://", { isPrivate: false }); 1611 await check_results({ 1612 context, 1613 matches: [ 1614 makeSearchResult(context, { 1615 engineName: SUGGESTIONS_ENGINE_NAME, 1616 heuristic: true, 1617 }), 1618 ], 1619 }); 1620 1621 context = createContext("moz+test://", { isPrivate: false }); 1622 await check_results({ 1623 context, 1624 matches: [ 1625 makeSearchResult(context, { 1626 engineName: SUGGESTIONS_ENGINE_NAME, 1627 heuristic: true, 1628 }), 1629 ], 1630 }); 1631 1632 context = createContext("about", { isPrivate: false }); 1633 await check_results({ 1634 context, 1635 matches: [ 1636 makeSearchResult(context, { 1637 engineName: SUGGESTIONS_ENGINE_NAME, 1638 heuristic: true, 1639 }), 1640 makeSearchResult(context, { 1641 engineName: SUGGESTIONS_ENGINE_NAME, 1642 suggestion: "abouted", 1643 }), 1644 makeSearchResult(context, { 1645 engineName: SUGGESTIONS_ENGINE_NAME, 1646 suggestion: "abouteds", 1647 }), 1648 ], 1649 }); 1650 1651 await cleanUpSuggestions(); 1652 }); 1653 1654 add_task(async function restrict_remote_suggestions_after_no_results() { 1655 // We don't fetch remote suggestions if a query with a length over 1656 // maxCharsForSearchSuggestions returns 0 results. We set it to 4 here to 1657 // avoid constructing a 100+ character string. 1658 Services.prefs.setIntPref("browser.urlbar.maxCharsForSearchSuggestions", 4); 1659 setSuggestionsFn(() => { 1660 return []; 1661 }); 1662 1663 const query = SEARCH_STRING.substring(0, SEARCH_STRING.length - 1); 1664 let context = createContext(query, { isPrivate: false }); 1665 await check_results({ 1666 context, 1667 matches: [ 1668 makeSearchResult(context, { 1669 engineName: SUGGESTIONS_ENGINE_NAME, 1670 heuristic: true, 1671 }), 1672 ...makeFormHistoryResults(context, MAX_RESULTS - 1), 1673 ], 1674 }); 1675 1676 context = createContext(SEARCH_STRING, { isPrivate: false }); 1677 await check_results({ 1678 context, 1679 matches: [ 1680 makeSearchResult(context, { 1681 engineName: SUGGESTIONS_ENGINE_NAME, 1682 heuristic: true, 1683 }), 1684 ...makeFormHistoryResults(context, MAX_RESULTS - 1), 1685 // Because the previous search returned no suggestions, we will not fetch 1686 // remote suggestions for this query that is just a longer version of the 1687 // previous query. 1688 ], 1689 }); 1690 1691 // Do one more search before resetting maxCharsForSearchSuggestions to reset 1692 // the search suggestion provider's _lastLowResultsSearchSuggestion property. 1693 // Otherwise it will be stuck at SEARCH_STRING, which interferes with 1694 // subsequent tests. 1695 context = createContext("not the search string", { isPrivate: false }); 1696 await check_results({ 1697 context, 1698 matches: [ 1699 makeSearchResult(context, { 1700 engineName: SUGGESTIONS_ENGINE_NAME, 1701 heuristic: true, 1702 }), 1703 ], 1704 }); 1705 1706 Services.prefs.clearUserPref("browser.urlbar.maxCharsForSearchSuggestions"); 1707 1708 await cleanUpSuggestions(); 1709 }); 1710 1711 add_task(async function formHistory() { 1712 Services.prefs.setBoolPref(SUGGEST_PREF, true); 1713 Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true); 1714 1715 // `maxHistoricalSearchSuggestions` is no longer treated as a max count but as 1716 // a boolean: If it's zero, then the user has opted out of form history so we 1717 // shouldn't include any at all; if it's non-zero, then we include form 1718 // history according to the limits specified in the muxer's result groups. 1719 1720 // zero => no form history 1721 Services.prefs.setIntPref(MAX_FORM_HISTORY_PREF, 0); 1722 let context = createContext(SEARCH_STRING, { isPrivate: false }); 1723 await check_results({ 1724 context, 1725 matches: [ 1726 makeSearchResult(context, { 1727 engineName: SUGGESTIONS_ENGINE_NAME, 1728 heuristic: true, 1729 }), 1730 ...makeRemoteSuggestionResults(context), 1731 ], 1732 }); 1733 1734 // non-zero => allow form history 1735 Services.prefs.setIntPref(MAX_FORM_HISTORY_PREF, 1); 1736 context = createContext(SEARCH_STRING, { isPrivate: false }); 1737 await check_results({ 1738 context, 1739 matches: [ 1740 makeSearchResult(context, { 1741 engineName: SUGGESTIONS_ENGINE_NAME, 1742 heuristic: true, 1743 }), 1744 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 1745 ...makeRemoteSuggestionResults(context), 1746 ], 1747 }); 1748 1749 // non-zero => allow form history 1750 Services.prefs.setIntPref(MAX_FORM_HISTORY_PREF, 2); 1751 context = createContext(SEARCH_STRING, { isPrivate: false }); 1752 await check_results({ 1753 context, 1754 matches: [ 1755 makeSearchResult(context, { 1756 engineName: SUGGESTIONS_ENGINE_NAME, 1757 heuristic: true, 1758 }), 1759 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 1760 ...makeRemoteSuggestionResults(context), 1761 ], 1762 }); 1763 1764 Services.prefs.clearUserPref(MAX_FORM_HISTORY_PREF); 1765 1766 // Do a search for exactly the suggestion of the first form history result. 1767 // The heuristic's query should be the suggestion; the first form history 1768 // result should not be included since it dupes the heuristic; the other form 1769 // history results should not be included since they don't match; and both 1770 // remote suggestions should be included. 1771 let firstSuggestion = makeFormHistoryResults(context, 1)[0].payload 1772 .suggestion; 1773 context = createContext(firstSuggestion, { isPrivate: false }); 1774 await check_results({ 1775 context, 1776 matches: [ 1777 makeSearchResult(context, { 1778 engineName: SUGGESTIONS_ENGINE_NAME, 1779 heuristic: true, 1780 }), 1781 ...makeRemoteSuggestionResults(context, { 1782 suggestionPrefix: firstSuggestion, 1783 }), 1784 ], 1785 }); 1786 1787 // Do the same search but in uppercase with a trailing space. We should get 1788 // the same results, i.e., the form history result dupes the trimmed search 1789 // string so it shouldn't be included. 1790 let query = firstSuggestion.toUpperCase() + " "; 1791 context = createContext(query, { isPrivate: false }); 1792 await check_results({ 1793 context, 1794 matches: [ 1795 makeSearchResult(context, { 1796 query, 1797 engineName: SUGGESTIONS_ENGINE_NAME, 1798 heuristic: true, 1799 }), 1800 ...makeRemoteSuggestionResults(context, { 1801 suggestionPrefix: firstSuggestion.toUpperCase(), 1802 }), 1803 ], 1804 }); 1805 1806 // Add a form history entry that dupes the first remote suggestion and do a 1807 // search that triggers both. The form history should be included but the 1808 // remote suggestion should not since it dupes the form history. 1809 let suggestionPrefix = "dupe"; 1810 let dupeSuggestion = makeRemoteSuggestionResults(context, { 1811 suggestionPrefix, 1812 })[0].payload.suggestion; 1813 Assert.ok(dupeSuggestion, "Sanity check: dupeSuggestion is defined"); 1814 await UrlbarTestUtils.formHistory.add([dupeSuggestion]); 1815 1816 context = createContext(suggestionPrefix, { isPrivate: false }); 1817 await check_results({ 1818 context, 1819 matches: [ 1820 makeSearchResult(context, { 1821 engineName: SUGGESTIONS_ENGINE_NAME, 1822 heuristic: true, 1823 }), 1824 makeFormHistoryResult(context, { 1825 suggestion: dupeSuggestion, 1826 engineName: SUGGESTIONS_ENGINE_NAME, 1827 }), 1828 ...makeRemoteSuggestionResults(context, { suggestionPrefix }).slice(1), 1829 ], 1830 }); 1831 1832 await UrlbarTestUtils.formHistory.remove([dupeSuggestion]); 1833 1834 // Add these form history strings to use below. 1835 let formHistoryStrings = ["foo", "FOO ", "foobar", "fooquux"]; 1836 await UrlbarTestUtils.formHistory.add(formHistoryStrings); 1837 1838 // Search for "foo". "foo" and "FOO " shouldn't be included since they dupe 1839 // the heuristic. Both "foobar" and "fooquux" should be included even though 1840 // the max form history count is only two and there are four matching form 1841 // history results (including the discarded "foo" and "FOO "). 1842 context = createContext("foo", { isPrivate: false }); 1843 await check_results({ 1844 context, 1845 matches: [ 1846 makeSearchResult(context, { 1847 engineName: SUGGESTIONS_ENGINE_NAME, 1848 heuristic: true, 1849 }), 1850 makeFormHistoryResult(context, { 1851 suggestion: "foobar", 1852 engineName: SUGGESTIONS_ENGINE_NAME, 1853 }), 1854 makeFormHistoryResult(context, { 1855 suggestion: "fooquux", 1856 engineName: SUGGESTIONS_ENGINE_NAME, 1857 }), 1858 ...makeRemoteSuggestionResults(context, { 1859 suggestionPrefix: "foo", 1860 }), 1861 ], 1862 }); 1863 1864 // Add a visit that matches "foo" and will autofill so that the heuristic is 1865 // not a search result. Now the "foo" and "foobar" form history should be 1866 // included. The "foo" remote suggestion should not be included since it 1867 // dupes the "foo" form history. 1868 await PlacesTestUtils.addVisits("http://foo.example.com/"); 1869 context = createContext("foo", { isPrivate: false }); 1870 await check_results({ 1871 context, 1872 matches: [ 1873 makeVisitResult(context, { 1874 source: UrlbarUtils.RESULT_SOURCE.HISTORY, 1875 uri: "http://foo.example.com/", 1876 title: "test visit for http://foo.example.com/", 1877 heuristic: true, 1878 }), 1879 makeFormHistoryResult(context, { 1880 suggestion: "foo", 1881 engineName: SUGGESTIONS_ENGINE_NAME, 1882 }), 1883 makeFormHistoryResult(context, { 1884 suggestion: "foobar", 1885 engineName: SUGGESTIONS_ENGINE_NAME, 1886 }), 1887 makeFormHistoryResult(context, { 1888 suggestion: "fooquux", 1889 engineName: SUGGESTIONS_ENGINE_NAME, 1890 }), 1891 ...makeRemoteSuggestionResults(context, { 1892 suggestionPrefix: "foo", 1893 }), 1894 ], 1895 }); 1896 await PlacesUtils.history.clear(); 1897 1898 // Add SERPs for "foobar", "fooBAR ", and "food", and search for "foo". The 1899 // "foo" form history should be excluded since it dupes the heuristic; the 1900 // "foobar" and "fooquux" form history should be included; the "food" SERP 1901 // should be included since it doesn't dupe either form history result; and 1902 // the "foobar" and "fooBAR " SERPs depend on the result groups, see below. 1903 let engine = await Services.search.getDefault(); 1904 let serpURLs = ["foobar", "fooBAR ", "food"].map( 1905 term => UrlbarUtils.getSearchQueryUrl(engine, term)[0] 1906 ); 1907 await PlacesTestUtils.addVisits(serpURLs); 1908 1909 // First set showSearchSuggestionsFirst = false so that general results appear 1910 // before suggestions, which means that the muxer visits the "foobar" and 1911 // "fooBAR " SERPs before visiting the "foobar" form history, and so it 1912 // doesn't see that these two SERPs dupe the form history. They are therefore 1913 // included. 1914 Services.prefs.setBoolPref(SHOW_SEARCH_SUGGESTIONS_FIRST_PREF, false); 1915 context = createContext("foo", { isPrivate: false }); 1916 await check_results({ 1917 context, 1918 matches: [ 1919 makeSearchResult(context, { 1920 engineName: SUGGESTIONS_ENGINE_NAME, 1921 heuristic: true, 1922 }), 1923 makeVisitResult(context, { 1924 uri: `http://localhost:${port}/search?q=food`, 1925 title: `test visit for http://localhost:${port}/search?q=food`, 1926 }), 1927 makeVisitResult(context, { 1928 uri: `http://localhost:${port}/search?q=fooBAR+`, 1929 title: `test visit for http://localhost:${port}/search?q=fooBAR+`, 1930 }), 1931 makeVisitResult(context, { 1932 uri: `http://localhost:${port}/search?q=foobar`, 1933 title: `test visit for http://localhost:${port}/search?q=foobar`, 1934 }), 1935 makeFormHistoryResult(context, { 1936 suggestion: "foobar", 1937 engineName: SUGGESTIONS_ENGINE_NAME, 1938 }), 1939 makeFormHistoryResult(context, { 1940 suggestion: "fooquux", 1941 engineName: SUGGESTIONS_ENGINE_NAME, 1942 }), 1943 ...makeRemoteSuggestionResults(context, { 1944 suggestionPrefix: "foo", 1945 }), 1946 ], 1947 }); 1948 1949 // Now clear showSearchSuggestionsFirst so that suggestions appear before 1950 // general results. Now the muxer will see that the "foobar" and "fooBAR " 1951 // SERPs dupe the "foobar" form history, so it will exclude them. 1952 Services.prefs.clearUserPref(SHOW_SEARCH_SUGGESTIONS_FIRST_PREF); 1953 context = createContext("foo", { isPrivate: false }); 1954 await check_results({ 1955 context, 1956 matches: [ 1957 makeSearchResult(context, { 1958 engineName: SUGGESTIONS_ENGINE_NAME, 1959 heuristic: true, 1960 }), 1961 makeFormHistoryResult(context, { 1962 suggestion: "foobar", 1963 engineName: SUGGESTIONS_ENGINE_NAME, 1964 }), 1965 makeFormHistoryResult(context, { 1966 suggestion: "fooquux", 1967 engineName: SUGGESTIONS_ENGINE_NAME, 1968 }), 1969 ...makeRemoteSuggestionResults(context, { 1970 suggestionPrefix: "foo", 1971 }), 1972 makeVisitResult(context, { 1973 uri: `http://localhost:${port}/search?q=food`, 1974 title: `test visit for http://localhost:${port}/search?q=food`, 1975 }), 1976 ], 1977 }); 1978 1979 await UrlbarTestUtils.formHistory.remove(formHistoryStrings); 1980 1981 await cleanUpSuggestions(); 1982 await PlacesUtils.history.clear(); 1983 }); 1984 1985 // When the heuristic is hidden, search results that match the heuristic should 1986 // be included and not deduped. 1987 add_task(async function hideHeuristic() { 1988 UrlbarPrefs.set("experimental.hideHeuristic", true); 1989 UrlbarPrefs.set("browser.search.suggest.enabled", true); 1990 UrlbarPrefs.set("suggest.searches", true); 1991 let context = createContext(SEARCH_STRING, { isPrivate: false }); 1992 await check_results({ 1993 context, 1994 matches: [ 1995 makeSearchResult(context, { 1996 engineName: SUGGESTIONS_ENGINE_NAME, 1997 heuristic: true, 1998 }), 1999 ...makeFormHistoryResults(context, MAX_RESULTS - 3), 2000 makeSearchResult(context, { 2001 query: SEARCH_STRING, 2002 engineName: SUGGESTIONS_ENGINE_NAME, 2003 suggestion: SEARCH_STRING, 2004 }), 2005 ...makeRemoteSuggestionResults(context), 2006 ], 2007 }); 2008 await cleanUpSuggestions(); 2009 UrlbarPrefs.clear("experimental.hideHeuristic"); 2010 }); 2011 2012 // When the heuristic is hidden, form history results that match the heuristic 2013 // should be included and not deduped. 2014 add_task(async function hideHeuristic_formHistory() { 2015 UrlbarPrefs.set("experimental.hideHeuristic", true); 2016 UrlbarPrefs.set("browser.search.suggest.enabled", true); 2017 UrlbarPrefs.set("suggest.searches", true); 2018 2019 // Search for exactly the suggestion of the first form history result. 2020 // Expected results: 2021 // 2022 // * First form history should be included even though it dupes the heuristic 2023 // * Other form history should not be included because they don't match the 2024 // search string 2025 // * The first remote suggestion that just echoes the search string should not 2026 // be included because it dupes the first form history 2027 // * The remaining remote suggestions should be included because they don't 2028 // dupe anything 2029 let context = createContext(SEARCH_STRING, { isPrivate: false }); 2030 let firstFormHistory = makeFormHistoryResults(context, 1)[0]; 2031 context = createContext(firstFormHistory.payload.suggestion, { 2032 isPrivate: false, 2033 }); 2034 await check_results({ 2035 context, 2036 matches: [ 2037 makeSearchResult(context, { 2038 engineName: SUGGESTIONS_ENGINE_NAME, 2039 heuristic: true, 2040 }), 2041 firstFormHistory, 2042 ...makeRemoteSuggestionResults(context, { 2043 suggestionPrefix: firstFormHistory.payload.suggestion, 2044 }), 2045 ], 2046 }); 2047 2048 // Add these form history strings to use below. 2049 let formHistoryStrings = ["foo", "FOO ", "foobar", "fooquux"]; 2050 await UrlbarTestUtils.formHistory.add(formHistoryStrings); 2051 2052 // Search for "foo". Expected results: 2053 // 2054 // * "foo" form history should be included even though it dupes the heuristic 2055 // * "FOO " form history should not be included because it dupes the "foo" 2056 // form history 2057 // * "foobar" and "fooqux" form history should be included because they don't 2058 // dupe anything 2059 // * "foo" remote suggestion should not be included because it dupes the "foo" 2060 // form history 2061 // * "foo foo" and "foo bar" remote suggestions should be included because 2062 // they don't dupe anything 2063 context = createContext("foo", { isPrivate: false }); 2064 await check_results({ 2065 context, 2066 matches: [ 2067 makeSearchResult(context, { 2068 engineName: SUGGESTIONS_ENGINE_NAME, 2069 heuristic: true, 2070 }), 2071 makeFormHistoryResult(context, { 2072 suggestion: "foo", 2073 engineName: SUGGESTIONS_ENGINE_NAME, 2074 }), 2075 makeFormHistoryResult(context, { 2076 suggestion: "foobar", 2077 engineName: SUGGESTIONS_ENGINE_NAME, 2078 }), 2079 makeFormHistoryResult(context, { 2080 suggestion: "fooquux", 2081 engineName: SUGGESTIONS_ENGINE_NAME, 2082 }), 2083 ...makeRemoteSuggestionResults(context, { 2084 suggestionPrefix: "foo", 2085 }), 2086 ], 2087 }); 2088 2089 // Add SERPs for "foo" and "food", and search for "foo". Expected results: 2090 // 2091 // * "foo" form history should be included even though it dupes the heuristic 2092 // * "foobar" and "fooqux" form history should be included because they don't 2093 // dupe anything 2094 // * "foo" SERP depends on `showSearchSuggestionsFirst`, see below 2095 // * "food" SERP should be include because it doesn't dupe anything 2096 // * "foo" remote suggestion should not be included because it dupes the "foo" 2097 // form history 2098 // * "foo foo" and "foo bar" remote suggestions should be included because 2099 // they don't dupe anything 2100 let engine = await Services.search.getDefault(); 2101 let serpURLs = ["foo", "food"].map( 2102 term => UrlbarUtils.getSearchQueryUrl(engine, term)[0] 2103 ); 2104 await PlacesTestUtils.addVisits(serpURLs); 2105 2106 // With `showSearchSuggestionsFirst = false` so that general results appear 2107 // before suggestions, the muxer visits the "foo" (and "food") SERPs before 2108 // visiting the "foo" form history, and so it doesn't see that the "foo" SERP 2109 // dupes the form history. The SERP is therefore included. 2110 UrlbarPrefs.set("showSearchSuggestionsFirst", false); 2111 context = createContext("foo", { isPrivate: false }); 2112 await check_results({ 2113 context, 2114 matches: [ 2115 makeSearchResult(context, { 2116 engineName: SUGGESTIONS_ENGINE_NAME, 2117 heuristic: true, 2118 }), 2119 makeVisitResult(context, { 2120 uri: `http://localhost:${port}/search?q=food`, 2121 title: `test visit for http://localhost:${port}/search?q=food`, 2122 }), 2123 makeVisitResult(context, { 2124 uri: `http://localhost:${port}/search?q=foo`, 2125 title: `test visit for http://localhost:${port}/search?q=foo`, 2126 }), 2127 makeFormHistoryResult(context, { 2128 suggestion: "foo", 2129 engineName: SUGGESTIONS_ENGINE_NAME, 2130 }), 2131 makeFormHistoryResult(context, { 2132 suggestion: "foobar", 2133 engineName: SUGGESTIONS_ENGINE_NAME, 2134 }), 2135 makeFormHistoryResult(context, { 2136 suggestion: "fooquux", 2137 engineName: SUGGESTIONS_ENGINE_NAME, 2138 }), 2139 ...makeRemoteSuggestionResults(context, { 2140 suggestionPrefix: "foo", 2141 }), 2142 ], 2143 }); 2144 2145 // Now clear `showSearchSuggestionsFirst` so that suggestions appear before 2146 // general results. Now the muxer will see that the "foo" SERP dupes the "foo" 2147 // form history, so it will exclude it. 2148 UrlbarPrefs.clear("showSearchSuggestionsFirst"); 2149 context = createContext("foo", { isPrivate: false }); 2150 await check_results({ 2151 context, 2152 matches: [ 2153 makeSearchResult(context, { 2154 engineName: SUGGESTIONS_ENGINE_NAME, 2155 heuristic: true, 2156 }), 2157 makeFormHistoryResult(context, { 2158 suggestion: "foo", 2159 engineName: SUGGESTIONS_ENGINE_NAME, 2160 }), 2161 makeFormHistoryResult(context, { 2162 suggestion: "foobar", 2163 engineName: SUGGESTIONS_ENGINE_NAME, 2164 }), 2165 makeFormHistoryResult(context, { 2166 suggestion: "fooquux", 2167 engineName: SUGGESTIONS_ENGINE_NAME, 2168 }), 2169 ...makeRemoteSuggestionResults(context, { 2170 suggestionPrefix: "foo", 2171 }), 2172 makeVisitResult(context, { 2173 uri: `http://localhost:${port}/search?q=food`, 2174 title: `test visit for http://localhost:${port}/search?q=food`, 2175 }), 2176 ], 2177 }); 2178 2179 await UrlbarTestUtils.formHistory.remove(formHistoryStrings); 2180 2181 await cleanUpSuggestions(); 2182 UrlbarPrefs.clear("experimental.hideHeuristic"); 2183 });