test_autofill_origins.js (32124B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const HEURISTIC_FALLBACK_PROVIDERNAME = "UrlbarProviderHeuristicFallback"; 8 9 const origin = "example.com"; 10 11 async function cleanup() { 12 let suggestPrefs = ["history", "bookmark", "openpage"]; 13 for (let type of suggestPrefs) { 14 Services.prefs.clearUserPref("browser.urlbar.suggest." + type); 15 } 16 await cleanupPlaces(); 17 } 18 19 testEngine_setup(); 20 21 registerCleanupFunction(async () => { 22 Services.prefs.clearUserPref("browser.urlbar.suggest.quickactions"); 23 }); 24 Services.prefs.setBoolPref("browser.urlbar.suggest.quickactions", false); 25 26 // "example.com/" should match http://example.com/. i.e., the search string 27 // should be treated as if it didn't have the trailing slash. 28 add_task(async function trailingSlash() { 29 await PlacesTestUtils.addVisits([ 30 { 31 uri: "http://example.com/", 32 }, 33 ]); 34 35 let context = createContext(`${origin}/`, { isPrivate: false }); 36 await check_results({ 37 context, 38 autofilled: `${origin}/`, 39 completed: `http://${origin}/`, 40 matches: [ 41 makeVisitResult(context, { 42 uri: `http://${origin}/`, 43 title: `test visit for http://${origin}/`, 44 heuristic: true, 45 }), 46 ], 47 }); 48 await cleanup(); 49 }); 50 51 // "example.com/" should match http://www.example.com/. i.e., the search string 52 // should be treated as if it didn't have the trailing slash. 53 add_task(async function trailingSlashWWW() { 54 await PlacesTestUtils.addVisits([ 55 { 56 uri: "http://www.example.com/", 57 }, 58 ]); 59 let context = createContext(`${origin}/`, { isPrivate: false }); 60 await check_results({ 61 context, 62 autofilled: "example.com/", 63 completed: "http://www.example.com/", 64 matches: [ 65 makeVisitResult(context, { 66 uri: `http://www.${origin}/`, 67 title: `test visit for http://www.${origin}/`, 68 heuristic: true, 69 }), 70 ], 71 }); 72 await cleanup(); 73 }); 74 75 // "ex" should match http://example.com:8888/, and the port should be completed. 76 add_task(async function port() { 77 await PlacesTestUtils.addVisits([ 78 { 79 uri: "http://example.com:8888/", 80 }, 81 ]); 82 let context = createContext("ex", { isPrivate: false }); 83 await check_results({ 84 context, 85 autofilled: "example.com:8888/", 86 completed: "http://example.com:8888/", 87 matches: [ 88 makeVisitResult(context, { 89 uri: `http://${origin}:8888/`, 90 title: `test visit for http://${origin}:8888/`, 91 heuristic: true, 92 }), 93 ], 94 }); 95 await cleanup(); 96 }); 97 98 // "example.com:8" should match http://example.com:8888/, and the port should 99 // be completed. 100 add_task(async function portPartial() { 101 await PlacesTestUtils.addVisits([ 102 { 103 uri: "http://example.com:8888/", 104 }, 105 ]); 106 let context = createContext(`${origin}:8`, { isPrivate: false }); 107 await check_results({ 108 context, 109 autofilled: "example.com:8888/", 110 completed: "http://example.com:8888/", 111 matches: [ 112 makeVisitResult(context, { 113 uri: `http://${origin}:8888/`, 114 title: `test visit for http://${origin}:8888/`, 115 heuristic: true, 116 }), 117 ], 118 }); 119 await cleanup(); 120 }); 121 122 // "EXaM" should match http://example.com/ and the case of the search string 123 // should be preserved in the autofilled value. 124 add_task(async function preserveCase() { 125 await PlacesTestUtils.addVisits([ 126 { 127 uri: "http://example.com/", 128 }, 129 ]); 130 let context = createContext("EXaM", { isPrivate: false }); 131 await check_results({ 132 context, 133 autofilled: "EXaMple.com/", 134 completed: "http://example.com/", 135 matches: [ 136 makeVisitResult(context, { 137 uri: `http://${origin}/`, 138 title: `test visit for http://${origin}/`, 139 heuristic: true, 140 }), 141 ], 142 }); 143 await cleanup(); 144 }); 145 146 // "EXaM" should match http://example.com:8888/, the port should be completed, 147 // and the case of the search string should be preserved in the autofilled 148 // value. 149 add_task(async function preserveCasePort() { 150 await PlacesTestUtils.addVisits([ 151 { 152 uri: "http://example.com:8888/", 153 }, 154 ]); 155 let context = createContext("EXaM", { isPrivate: false }); 156 await check_results({ 157 context, 158 autofilled: "EXaMple.com:8888/", 159 completed: "http://example.com:8888/", 160 matches: [ 161 makeVisitResult(context, { 162 uri: `http://${origin}:8888/`, 163 title: `test visit for http://${origin}:8888/`, 164 heuristic: true, 165 }), 166 ], 167 }); 168 await cleanup(); 169 }); 170 171 // "example.com:89" should *not* match http://example.com:8888/. 172 add_task(async function portNoMatch1() { 173 await PlacesTestUtils.addVisits([ 174 { 175 uri: "http://example.com:8888/", 176 }, 177 ]); 178 let context = createContext(`${origin}:89`, { isPrivate: false }); 179 await check_results({ 180 context, 181 matches: [ 182 makeVisitResult(context, { 183 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 184 uri: `http://${origin}:89/`, 185 title: `${origin}:89/`, 186 iconUri: "", 187 heuristic: true, 188 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 189 }), 190 ], 191 }); 192 await cleanup(); 193 }); 194 195 // "example.com:9" should *not* match http://example.com:8888/. 196 add_task(async function portNoMatch2() { 197 await PlacesTestUtils.addVisits([ 198 { 199 uri: "http://example.com:8888/", 200 }, 201 ]); 202 let context = createContext(`${origin}:9`, { isPrivate: false }); 203 await check_results({ 204 context, 205 matches: [ 206 makeVisitResult(context, { 207 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 208 uri: `http://${origin}:9/`, 209 title: `${origin}:9/`, 210 iconUri: "", 211 heuristic: true, 212 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 213 }), 214 ], 215 }); 216 await cleanup(); 217 }); 218 219 // "example/" should *not* match http://example.com/. 220 add_task(async function trailingSlash_2() { 221 await PlacesTestUtils.addVisits([ 222 { 223 uri: "http://example.com/", 224 }, 225 ]); 226 let context = createContext("example/", { isPrivate: false }); 227 await check_results({ 228 context, 229 matches: [ 230 makeVisitResult(context, { 231 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 232 uri: "http://example/", 233 title: "example/", 234 iconUri: "page-icon:http://example/", 235 heuristic: true, 236 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 237 }), 238 ], 239 }); 240 await cleanup(); 241 }); 242 243 // multi.dotted.domain, search up to dot. 244 add_task(async function multidotted() { 245 await PlacesTestUtils.addVisits([ 246 { 247 uri: "http://www.example.co.jp:8888/", 248 }, 249 ]); 250 let context = createContext("www.example.co.", { isPrivate: false }); 251 await check_results({ 252 context, 253 autofilled: "www.example.co.jp:8888/", 254 completed: "http://www.example.co.jp:8888/", 255 matches: [ 256 makeVisitResult(context, { 257 uri: "http://www.example.co.jp:8888/", 258 title: "test visit for http://www.example.co.jp:8888/", 259 heuristic: true, 260 }), 261 ], 262 }); 263 await cleanup(); 264 }); 265 266 add_task(async function test_ip() { 267 // IP addresses have complicated rules around whether they show 268 // HeuristicFallback's backup search result. Flip this pref to disable that 269 // backup search and simplify ths subtest. 270 Services.prefs.setBoolPref("keyword.enabled", false); 271 for (let str of [ 272 "192.168.1.1/", 273 "255.255.255.255:8080/", 274 "[2001:db8::1428:57ab]/", 275 "[::c0a8:5909]/", 276 "[::1]/", 277 ]) { 278 info("testing " + str); 279 await PlacesTestUtils.addVisits("http://" + str); 280 for (let i = 1; i < str.length; ++i) { 281 let context = createContext(str.substring(0, i), { isPrivate: false }); 282 await check_results({ 283 context, 284 autofilled: str, 285 completed: "http://" + str, 286 matches: [ 287 makeVisitResult(context, { 288 uri: "http://" + str, 289 title: `test visit for http://${str}`, 290 heuristic: true, 291 }), 292 ], 293 }); 294 } 295 await cleanup(); 296 } 297 Services.prefs.clearUserPref("keyword.enabled"); 298 }); 299 300 // host starting with large number. 301 add_task(async function large_number_host() { 302 await PlacesTestUtils.addVisits([ 303 { 304 uri: "http://12345example.it:8888/", 305 }, 306 ]); 307 let context = createContext("1234", { isPrivate: false }); 308 await check_results({ 309 context, 310 autofilled: "12345example.it:8888/", 311 completed: "http://12345example.it:8888/", 312 matches: [ 313 makeVisitResult(context, { 314 uri: "http://12345example.it:8888/", 315 title: "test visit for http://12345example.it:8888/", 316 heuristic: true, 317 }), 318 ], 319 }); 320 await cleanup(); 321 }); 322 323 // When determining which origins should be autofilled, all the origins sharing 324 // a host should be added together to get their combined frecency -- i.e., 325 // prefixes should be collapsed. And then from that list, the origin with the 326 // highest frecency should be chosen. 327 add_task(async function groupByHost() { 328 // Add some visits to the same host, example.com. Add one http and two https 329 // so that https has a higher frecency and is therefore the origin that should 330 // be autofilled. Also add another origin that has a higher frecency than 331 // both so that alone, neither http nor https would be autofilled, but added 332 // together they should be. 333 await PlacesTestUtils.addVisits([ 334 { uri: "http://example.com/", visitDate: daysAgo(30) }, 335 336 // Have a higher frecency by being more recent. But not so recent that it 337 // has a higher frecency than other visits that bump the origins threshold. 338 { uri: "https://example.com/", visitDate: daysAgo(7) }, 339 340 { 341 uri: "https://mozilla.org/", 342 transition: PlacesUtils.history.TRANSITION_TYPED, 343 }, 344 { 345 uri: "https://mozilla.org/1", 346 transition: PlacesUtils.history.TRANSITION_TYPED, 347 visitDate: daysAgo(1), 348 }, 349 350 // Add more origins to make the threshold higher 351 { uri: "https://mozilla.com/" }, 352 { uri: "https://mozilla.ca/" }, 353 ]); 354 355 let httpFrec = await getOriginFrecency("http://", "example.com"); 356 let httpsFrec = await getOriginFrecency("https://", "example.com"); 357 let otherFrec = await getOriginFrecency("https://", "mozilla.org"); 358 359 Assert.less( 360 httpFrec, 361 httpsFrec, 362 "Frecency http://example.com is less than https://example.com" 363 ); 364 Assert.less( 365 httpsFrec, 366 otherFrec, 367 "Frecency of https://example.com is less than https://mozilla.org" 368 ); 369 370 // Make sure the frecencies of the three origins are as expected in relation 371 // to the threshold. 372 let threshold = await getOriginAutofillThreshold(); 373 Assert.less(httpFrec, threshold, "http origin should be < threshold"); 374 Assert.less(httpsFrec, threshold, "https origin should be < threshold"); 375 Assert.lessOrEqual( 376 threshold, 377 otherFrec, 378 "Other origin should cross threshold" 379 ); 380 381 Assert.lessOrEqual( 382 threshold, 383 httpFrec + httpsFrec, 384 "http and https origin added together should cross threshold" 385 ); 386 387 // The https origin should be autofilled. 388 let context = createContext("ex", { isPrivate: false }); 389 await check_results({ 390 context, 391 autofilled: "example.com/", 392 completed: "https://example.com/", 393 matches: [ 394 makeVisitResult(context, { 395 uri: "https://example.com/", 396 title: "test visit for https://example.com/", 397 heuristic: true, 398 }), 399 ], 400 }); 401 402 await cleanup(); 403 }); 404 405 // This is the same as the previous (groupByHost), but it changes the standard 406 // deviation multiplier by setting the corresponding pref. This makes sure that 407 // the pref is respected. 408 add_task(async function groupByHostNonDefaultStddevMultiplier() { 409 let stddevMultiplier = 1.5; 410 Services.prefs.setCharPref( 411 "browser.urlbar.autoFill.stddevMultiplier", 412 Number(stddevMultiplier).toFixed(1) 413 ); 414 415 await PlacesTestUtils.addVisits([ 416 { uri: "http://example.com/", visitDate: daysAgo(30) }, 417 418 { uri: "https://example.com/", visitDate: daysAgo(7) }, 419 420 { uri: "https://mozilla.org/" }, 421 { uri: "https://mozilla.org/1", visitDate: daysAgo(1) }, 422 { uri: "https://mozilla.org/2", visitDate: daysAgo(2) }, 423 424 // Add more origins to make the threshold higher 425 { uri: "https://mozilla.com/" }, 426 { uri: "https://mozilla.ca/" }, 427 ]); 428 429 let httpFrec = await getOriginFrecency("http://", "example.com"); 430 let httpsFrec = await getOriginFrecency("https://", "example.com"); 431 let otherFrec = await getOriginFrecency("https://", "mozilla.org"); 432 Assert.less(httpFrec, httpsFrec, "Sanity check"); 433 Assert.less(httpsFrec, otherFrec, "Sanity check"); 434 435 // Make sure the frecencies of the three origins are as expected in relation 436 // to the threshold. 437 let threshold = await getOriginAutofillThreshold(); 438 Assert.less(httpFrec, threshold, "http origin should be < threshold"); 439 Assert.less(httpsFrec, threshold, "https origin should be < threshold"); 440 Assert.lessOrEqual( 441 threshold, 442 otherFrec, 443 "Other origin should cross threshold" 444 ); 445 446 Assert.lessOrEqual( 447 threshold, 448 httpFrec + httpsFrec, 449 "http and https origin added together should cross threshold" 450 ); 451 452 // The https origin should be autofilled. 453 let context = createContext("ex", { isPrivate: false }); 454 await check_results({ 455 context, 456 autofilled: "example.com/", 457 completed: "https://example.com/", 458 matches: [ 459 makeVisitResult(context, { 460 uri: "https://example.com/", 461 title: "test visit for https://example.com/", 462 heuristic: true, 463 }), 464 ], 465 }); 466 467 Services.prefs.clearUserPref("browser.urlbar.autoFill.stddevMultiplier"); 468 469 await cleanup(); 470 }); 471 472 // This is similar to suggestHistoryFalse_bookmark_0 in test_autofill_tasks.js, 473 // but it adds unbookmarked visits for multiple URLs with the same origin. 474 add_task(async function suggestHistoryFalse_bookmark_multiple() { 475 // Force only bookmarked pages to be suggested and therefore only bookmarked 476 // pages to be completed. 477 Services.prefs.setBoolPref("browser.urlbar.suggest.history", false); 478 479 let search = "ex"; 480 let baseURL = "http://example.com/"; 481 let bookmarkedURL = baseURL + "bookmarked"; 482 483 // Add visits for three different URLs all sharing the same origin, and then 484 // bookmark the second one. After that, the origin should be autofilled. The 485 // reason for adding unbookmarked visits before and after adding the 486 // bookmarked visit is to make sure our aggregate SQL query for determining 487 // whether an origin is bookmarked is correct. 488 489 await PlacesTestUtils.addVisits([ 490 { 491 uri: baseURL + "other1", 492 }, 493 ]); 494 let context = createContext(search, { isPrivate: false }); 495 await check_results({ 496 context, 497 matches: [ 498 makeSearchResult(context, { 499 engineName: SUGGESTIONS_ENGINE_NAME, 500 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 501 heuristic: true, 502 }), 503 ], 504 }); 505 506 await PlacesTestUtils.addVisits([ 507 { 508 uri: bookmarkedURL, 509 }, 510 ]); 511 context = createContext(search, { isPrivate: false }); 512 await check_results({ 513 context, 514 matches: [ 515 makeSearchResult(context, { 516 engineName: SUGGESTIONS_ENGINE_NAME, 517 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 518 heuristic: true, 519 }), 520 ], 521 }); 522 523 await PlacesTestUtils.addVisits([ 524 { 525 uri: baseURL + "other2", 526 }, 527 ]); 528 context = createContext(search, { isPrivate: false }); 529 await check_results({ 530 context, 531 matches: [ 532 makeSearchResult(context, { 533 engineName: SUGGESTIONS_ENGINE_NAME, 534 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 535 heuristic: true, 536 }), 537 ], 538 }); 539 540 // Now bookmark the second URL. It should be suggested and completed. 541 await PlacesTestUtils.addBookmarkWithDetails({ 542 uri: bookmarkedURL, 543 }); 544 context = createContext(search, { isPrivate: false }); 545 await check_results({ 546 context, 547 autofilled: "example.com/", 548 completed: baseURL, 549 matches: [ 550 makeVisitResult(context, { 551 uri: baseURL, 552 title: UrlbarTestUtils.trimURL(baseURL), 553 heuristic: true, 554 }), 555 makeBookmarkResult(context, { 556 uri: bookmarkedURL, 557 title: "A bookmark", 558 }), 559 ], 560 }); 561 562 await cleanup(); 563 }); 564 565 // This is similar to suggestHistoryFalse_bookmark_prefix_0 in 566 // autofill_test_autofill_originsAndQueries.js, but it adds unbookmarked visits 567 // for multiple URLs with the same origin. 568 add_task(async function suggestHistoryFalse_bookmark_prefix_multiple() { 569 // Force only bookmarked pages to be suggested and therefore only bookmarked 570 // pages to be completed. 571 Services.prefs.setBoolPref("browser.urlbar.suggest.history", false); 572 573 let search = "http://ex"; 574 let baseURL = "http://example.com/"; 575 let bookmarkedURL = baseURL + "bookmarked"; 576 577 // Add visits for three different URLs all sharing the same origin, and then 578 // bookmark the second one. After that, the origin should be autofilled. The 579 // reason for adding unbookmarked visits before and after adding the 580 // bookmarked visit is to make sure our aggregate SQL query for determining 581 // whether an origin is bookmarked is correct. 582 583 await PlacesTestUtils.addVisits([ 584 { 585 uri: baseURL + "other1", 586 }, 587 ]); 588 let context = createContext(search, { isPrivate: false }); 589 await check_results({ 590 context, 591 matches: [ 592 makeVisitResult(context, { 593 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 594 uri: `${search}/`, 595 title: `${search}/`, 596 iconUri: "", 597 heuristic: true, 598 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 599 }), 600 ], 601 }); 602 603 await PlacesTestUtils.addVisits([ 604 { 605 uri: bookmarkedURL, 606 }, 607 ]); 608 context = createContext(search, { isPrivate: false }); 609 await check_results({ 610 context, 611 matches: [ 612 makeVisitResult(context, { 613 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 614 uri: `${search}/`, 615 title: `${search}/`, 616 iconUri: "", 617 heuristic: true, 618 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 619 }), 620 ], 621 }); 622 623 await PlacesTestUtils.addVisits([ 624 { 625 uri: baseURL + "other2", 626 }, 627 ]); 628 context = createContext(search, { isPrivate: false }); 629 await check_results({ 630 context, 631 matches: [ 632 makeVisitResult(context, { 633 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 634 uri: `${search}/`, 635 title: `${search}/`, 636 iconUri: "", 637 heuristic: true, 638 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 639 }), 640 ], 641 }); 642 643 // Now bookmark the second URL. It should be suggested and completed. 644 await PlacesTestUtils.addBookmarkWithDetails({ 645 uri: bookmarkedURL, 646 }); 647 context = createContext(search, { isPrivate: false }); 648 await check_results({ 649 context, 650 autofilled: "http://example.com/", 651 completed: baseURL, 652 matches: [ 653 makeVisitResult(context, { 654 uri: baseURL, 655 title: UrlbarTestUtils.trimURL(baseURL), 656 heuristic: true, 657 }), 658 makeBookmarkResult(context, { 659 uri: bookmarkedURL, 660 title: "A bookmark", 661 }), 662 ], 663 }); 664 665 await cleanup(); 666 }); 667 668 // When the autofilled URL is `example.com/`, a visit for `example.com/?` should 669 // not be included in the results since it dupes the autofill result. 670 add_task(async function searchParams() { 671 await PlacesTestUtils.addVisits([ 672 "http://example.com/", 673 "http://example.com/?", 674 "http://example.com/?foo", 675 ]); 676 677 // First, do a search with autofill disabled to make sure the visits were 678 // properly added. `example.com/?foo` has the highest frecency because it was 679 // added last; `example.com/?` has the next highest. `example.com/` dupes 680 // `example.com/?`, so it should not appear. 681 UrlbarPrefs.set("autoFill", false); 682 let context = createContext("ex", { isPrivate: false }); 683 await check_results({ 684 context, 685 matches: [ 686 makeSearchResult(context, { 687 engineName: SUGGESTIONS_ENGINE_NAME, 688 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 689 heuristic: true, 690 }), 691 makeVisitResult(context, { 692 uri: "http://example.com/?foo", 693 title: "test visit for http://example.com/?foo", 694 }), 695 makeVisitResult(context, { 696 uri: "http://example.com/?", 697 title: "test visit for http://example.com/?", 698 }), 699 ], 700 }); 701 702 // Now do a search with autofill enabled. This time `example.com/` will be 703 // autofilled, and since `example.com/?` dupes it, `example.com/?` should not 704 // appear. 705 UrlbarPrefs.clear("autoFill"); 706 context = createContext("ex", { isPrivate: false }); 707 await check_results({ 708 context, 709 autofilled: "example.com/", 710 completed: "http://example.com/", 711 matches: [ 712 makeVisitResult(context, { 713 uri: "http://example.com/", 714 title: "test visit for http://example.com/", 715 heuristic: true, 716 }), 717 makeVisitResult(context, { 718 uri: "http://example.com/?foo", 719 title: "test visit for http://example.com/?foo", 720 }), 721 ], 722 }); 723 724 await cleanup(); 725 }); 726 727 // When the autofilled URL is `example.com/`, a visit for `example.com/?` should 728 // not be included in the results since it dupes the autofill result. (Same as 729 // the previous task but with https URLs instead of http. There shouldn't be any 730 // substantive difference.) 731 add_task(async function searchParams_https() { 732 await PlacesTestUtils.addVisits([ 733 "https://example.com/", 734 "https://example.com/?", 735 "https://example.com/?foo", 736 ]); 737 738 // First, do a search with autofill disabled to make sure the visits were 739 // properly added. `example.com/?foo` has the highest frecency because it was 740 // added last; `example.com/?` has the next highest. `example.com/` dupes 741 // `example.com/?`, so it should not appear. 742 UrlbarPrefs.set("autoFill", false); 743 let context = createContext("ex", { isPrivate: false }); 744 await check_results({ 745 context, 746 matches: [ 747 makeSearchResult(context, { 748 engineName: SUGGESTIONS_ENGINE_NAME, 749 providerName: HEURISTIC_FALLBACK_PROVIDERNAME, 750 heuristic: true, 751 }), 752 makeVisitResult(context, { 753 uri: "https://example.com/?foo", 754 title: "test visit for https://example.com/?foo", 755 }), 756 makeVisitResult(context, { 757 uri: "https://example.com/?", 758 title: "test visit for https://example.com/?", 759 }), 760 ], 761 }); 762 763 // Now do a search with autofill enabled. This time `example.com/` will be 764 // autofilled, and since `example.com/?` dupes it, `example.com/?` should not 765 // appear. 766 UrlbarPrefs.clear("autoFill"); 767 context = createContext("ex", { isPrivate: false }); 768 await check_results({ 769 context, 770 autofilled: "example.com/", 771 completed: "https://example.com/", 772 matches: [ 773 makeVisitResult(context, { 774 uri: "https://example.com/", 775 title: "test visit for https://example.com/", 776 heuristic: true, 777 }), 778 makeVisitResult(context, { 779 uri: "https://example.com/?foo", 780 title: "test visit for https://example.com/?foo", 781 }), 782 ], 783 }); 784 785 await cleanup(); 786 }); 787 788 // Checks an origin that looks like a prefix: a scheme with no dots + a port. 789 add_task(async function originLooksLikePrefix() { 790 let hostAndPort = "localhost:8888"; 791 let address = `http://${hostAndPort}/`; 792 await PlacesTestUtils.addVisits([{ uri: address }]); 793 794 // addTestSuggestionsEngine adds a search engine 795 // with localhost as a server, so we have to disable the 796 // TTS result or else it will show up as a second result 797 // when searching l to localhost 798 UrlbarPrefs.set("suggest.engines", false); 799 800 for (let search of ["lo", "localhost", "localhost:", "localhost:8888"]) { 801 let context = createContext(search, { isPrivate: false }); 802 await check_results({ 803 context, 804 autofilled: hostAndPort + "/", 805 completed: address, 806 matches: [ 807 makeVisitResult(context, { 808 uri: address, 809 title: `test visit for http://${hostAndPort}/`, 810 heuristic: true, 811 }), 812 ], 813 }); 814 } 815 await cleanup(); 816 }); 817 818 // Checks an origin whose prefix is "about:". 819 add_task(async function about() { 820 const testData = [ 821 { 822 uri: "about:config", 823 input: "conf", 824 results: [ 825 context => 826 makeSearchResult(context, { 827 engineName: "Suggestions", 828 heuristic: true, 829 }), 830 context => 831 makeBookmarkResult(context, { 832 uri: "about:config", 833 title: "A bookmark", 834 }), 835 ], 836 }, 837 { 838 uri: "about:blank", 839 input: "about:blan", 840 results: [ 841 context => 842 makeVisitResult(context, { 843 uri: "about:blan", 844 title: "about:blan", 845 source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, 846 heuristic: true, 847 }), 848 context => 849 makeBookmarkResult(context, { 850 uri: "about:blank", 851 title: "A bookmark", 852 }), 853 ], 854 }, 855 ]; 856 857 for (const { uri, input, results } of testData) { 858 await PlacesTestUtils.addBookmarkWithDetails({ uri }); 859 860 const context = createContext(input, { isPrivate: false }); 861 await check_results({ 862 context, 863 matches: results.map(f => f(context)), 864 }); 865 await cleanup(); 866 } 867 }); 868 869 // Checks an origin whose prefix is "place:". 870 add_task(async function place() { 871 const testData = [ 872 { 873 uri: "place:transition=7&sort=4", 874 input: "tran", 875 }, 876 { 877 uri: "place:transition=7&sort=4", 878 input: "place:tran", 879 }, 880 ]; 881 882 for (const { uri, input } of testData) { 883 await PlacesTestUtils.addBookmarkWithDetails({ uri }); 884 885 const context = createContext(input, { isPrivate: false }); 886 await check_results({ 887 context, 888 matches: [ 889 makeSearchResult(context, { 890 engineName: "Suggestions", 891 heuristic: true, 892 }), 893 ], 894 }); 895 await cleanup(); 896 } 897 }); 898 899 add_task(async function nullTitle() { 900 await doTitleTest({ 901 visits: [ 902 { 903 uri: "http://example.com/", 904 // Set title of visits data to an empty string causes 905 // the title to be null in the database. 906 title: "", 907 frecencyBucket: "high", 908 }, 909 { 910 uri: "https://www.example.com/", 911 title: "medium frecency", 912 frecencyBucket: "medium", 913 }, 914 { 915 uri: "http://www.example.com/", 916 title: "low frecency", 917 frecencyBucket: "low", 918 }, 919 ], 920 input: "example.com", 921 expected: { 922 autofilled: "example.com/", 923 completed: "http://example.com/", 924 matches: context => [ 925 makeVisitResult(context, { 926 uri: "http://example.com/", 927 title: "medium frecency", 928 heuristic: true, 929 }), 930 makeVisitResult(context, { 931 uri: "https://www.example.com/", 932 title: "medium frecency", 933 }), 934 ], 935 }, 936 }); 937 }); 938 939 add_task(async function domainTitle() { 940 await doTitleTest({ 941 visits: [ 942 { 943 uri: "http://example.com/", 944 title: "example.com", 945 frecencyBucket: "high", 946 }, 947 { 948 uri: "https://www.example.com/", 949 title: "", 950 frecencyBucket: "medium", 951 }, 952 { 953 uri: "http://www.example.com/", 954 title: "lowest frecency but has title", 955 frecencyBucket: "low", 956 }, 957 ], 958 input: "example.com", 959 expected: { 960 autofilled: "example.com/", 961 completed: "http://example.com/", 962 matches: context => [ 963 makeVisitResult(context, { 964 uri: "http://example.com/", 965 title: "lowest frecency but has title", 966 heuristic: true, 967 }), 968 makeVisitResult(context, { 969 uri: "https://www.example.com/", 970 title: "www.example.com", 971 }), 972 ], 973 }, 974 }); 975 }); 976 977 add_task(async function exactMatchedTitle() { 978 await doTitleTest({ 979 visits: [ 980 { 981 uri: "http://example.com/", 982 title: "exact match", 983 frecencyBucket: "medium", 984 }, 985 { 986 uri: "https://www.example.com/", 987 title: "high frecency uri", 988 frecencyBucket: "high", 989 }, 990 ], 991 input: "http://example.com/", 992 expected: { 993 autofilled: "http://example.com/", 994 completed: "http://example.com/", 995 matches: context => [ 996 makeVisitResult(context, { 997 uri: "http://example.com/", 998 title: "exact match", 999 heuristic: true, 1000 }), 1001 makeVisitResult(context, { 1002 uri: "https://www.example.com/", 1003 title: "high frecency uri", 1004 }), 1005 ], 1006 }, 1007 }); 1008 }); 1009 1010 async function doTitleTest({ visits, input, expected }) { 1011 for (let visit of visits) { 1012 switch (visit.frecencyBucket) { 1013 case "high": { 1014 await PlacesTestUtils.addVisits({ 1015 title: visit.title, 1016 uri: visit.uri, 1017 transition: PlacesUtils.history.TRANSITION_TYPED, 1018 }); 1019 break; 1020 } 1021 case "medium": { 1022 await PlacesTestUtils.addVisits({ title: visit.title, uri: visit.uri }); 1023 break; 1024 } 1025 case "low": { 1026 // Non-bookmarked sponsors are categorized as low. 1027 await PlacesTestUtils.addVisits({ 1028 title: visit.title, 1029 uri: visit.uri, 1030 }); 1031 // Add visits doesn't allow you to set the visit source. 1032 await PlacesUtils.withConnectionWrapper("setVisitSource", async db => { 1033 await db.execute( 1034 ` 1035 UPDATE moz_historyvisits 1036 SET source = :source 1037 WHERE place_id = (SELECT id FROM moz_places WHERE url = :url)`, 1038 { 1039 url: visit.uri, 1040 source: PlacesUtils.history.VISIT_SOURCE_SPONSORED, 1041 } 1042 ); 1043 await db.execute( 1044 ` 1045 UPDATE moz_places 1046 SET recalc_frecency = 1 1047 WHERE id = (SELECT id FROM moz_places WHERE url = :url)`, 1048 { 1049 url: visit.uri, 1050 } 1051 ); 1052 }); 1053 await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies(); 1054 break; 1055 } 1056 } 1057 } 1058 1059 await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies(); 1060 1061 const context = createContext(input, { isPrivate: false }); 1062 await check_results({ 1063 context, 1064 autofilled: expected.autofilled, 1065 completed: expected.completed, 1066 matches: expected.matches(context), 1067 }); 1068 1069 await cleanup(); 1070 } 1071 1072 /* Tests sorting order when only unvisited bookmarks are available (e.g. in 1073 permanent private browsing mode), then the only information we have is the 1074 number of bookmarks per origin, and we're going to use that. */ 1075 add_task(async function just_multiple_unvisited_bookmarks() { 1076 // These are sorted to avoid confusion with natural sorting, so the one with 1077 // the highest score is added in the middle. 1078 let filledUrl = "https://www.tld2.com/"; 1079 let urls = [ 1080 { 1081 url: "https://tld1.com/", 1082 count: 1, 1083 }, 1084 { 1085 url: "https://tld2.com/", 1086 count: 2, 1087 }, 1088 { 1089 url: filledUrl, 1090 count: 2, 1091 }, 1092 { 1093 url: "https://tld3.com/", 1094 count: 3, 1095 }, 1096 ]; 1097 1098 await PlacesUtils.history.clear(); 1099 for (let { url, count } of urls) { 1100 while (count--) { 1101 await PlacesTestUtils.addBookmarkWithDetails({ 1102 uri: url, 1103 }); 1104 } 1105 } 1106 await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies(); 1107 1108 let context = createContext("tld", { isPrivate: false }); 1109 await check_results({ 1110 context, 1111 autofilled: "tld2.com/", 1112 completed: filledUrl, 1113 matches: [ 1114 makeVisitResult(context, { 1115 uri: filledUrl, 1116 title: "A bookmark", 1117 heuristic: true, 1118 }), 1119 makeBookmarkResult(context, { 1120 uri: "https://tld3.com/", 1121 title: "A bookmark", 1122 }), 1123 makeBookmarkResult(context, { 1124 uri: "https://tld2.com/", 1125 title: "A bookmark", 1126 }), 1127 makeBookmarkResult(context, { 1128 uri: "https://tld1.com/", 1129 title: "A bookmark", 1130 }), 1131 ], 1132 }); 1133 1134 await cleanup(); 1135 });