Backed out changeset e65fca949600 (bug 1858994) for causing xpcshell failures in...
[gecko.git] / browser / components / urlbar / tests / unit / test_providerHeuristicFallback.js
blobb29ec0b266df8033ab2226808baf4282e25cf3ca
1 /* Any copyright is dedicated to the Public Domain.
2    http://creativecommons.org/publicdomain/zero/1.0/ */
4 /**
5  * Tests that visit-url and search engine heuristic results are returned by
6  * UrlbarProviderHeuristicFallback.
7  */
9 const QUICKACTIONS_PREF = "browser.urlbar.suggest.quickactions";
10 const SUGGEST_ENABLED_PREF = "browser.search.suggest.enabled";
11 const PRIVATE_SEARCH_PREF = "browser.search.separatePrivateDefault.ui.enabled";
13 // We make sure that restriction tokens and search terms are correctly
14 // recognized when they are separated by each of these different types of spaces
15 // and combinations of spaces.  U+3000 is the ideographic space in CJK and is
16 // commonly used by CJK speakers.
17 const TEST_SPACES = [" ", "\u3000", " \u3000", "\u3000 "];
19 testEngine_setup();
21 add_setup(async function () {
22   registerCleanupFunction(async () => {
23     Services.prefs.clearUserPref(QUICKACTIONS_PREF);
24     Services.prefs.clearUserPref(SUGGEST_ENABLED_PREF);
25     Services.prefs.clearUserPref(PRIVATE_SEARCH_PREF);
26     Services.prefs.clearUserPref("keyword.enabled");
27   });
28   Services.prefs.setBoolPref(QUICKACTIONS_PREF, false);
29   Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, false);
30   Services.prefs.setBoolPref(PRIVATE_SEARCH_PREF, false);
31 });
33 add_task(async function () {
34   info("visit url, no protocol");
35   let query = "mozilla.org";
36   let context = createContext(query, { isPrivate: false });
37   await check_results({
38     context,
39     matches: [
40       makeVisitResult(context, {
41         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
42         uri: `http://${query}/`,
43         fallbackTitle: `http://${query}/`,
44         heuristic: true,
45       }),
46       makeSearchResult(context, {
47         engineName: SUGGESTIONS_ENGINE_NAME,
48       }),
49     ],
50   });
52   info("visit url, no protocol but with 2 dots");
53   query = "www.mozilla.org";
54   context = createContext(query, { isPrivate: false });
55   await check_results({
56     context,
57     matches: [
58       makeVisitResult(context, {
59         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
60         uri: `http://${query}/`,
61         fallbackTitle: `http://${query}/`,
62         heuristic: true,
63       }),
64       makeSearchResult(context, {
65         engineName: SUGGESTIONS_ENGINE_NAME,
66       }),
67     ],
68   });
70   info("visit url, no protocol, e-mail like");
71   query = "a@b.com";
72   context = createContext(query, { isPrivate: false });
73   await check_results({
74     context,
75     matches: [
76       makeVisitResult(context, {
77         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
78         uri: `http://${query}/`,
79         fallbackTitle: `http://${query}/`,
80         heuristic: true,
81       }),
82       makeSearchResult(context, {
83         engineName: SUGGESTIONS_ENGINE_NAME,
84       }),
85     ],
86   });
88   info("visit url, with protocol but with 2 dots");
89   query = "https://www.mozilla.org";
90   context = createContext(query, { isPrivate: false });
91   await check_results({
92     context,
93     matches: [
94       makeVisitResult(context, {
95         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
96         uri: `${query}/`,
97         fallbackTitle: `${query}/`,
98         heuristic: true,
99       }),
100     ],
101   });
103   // info("visit url, with protocol but with 3 dots");
104   query = "https://www.mozilla.org.tw";
105   context = createContext(query, { isPrivate: false });
106   await check_results({
107     context,
108     matches: [
109       makeVisitResult(context, {
110         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
111         uri: `${query}/`,
112         fallbackTitle: `${query}/`,
113         heuristic: true,
114       }),
115     ],
116   });
118   info("visit url, with protocol");
119   query = "https://mozilla.org";
120   context = createContext(query, { isPrivate: false });
121   await check_results({
122     context,
123     matches: [
124       makeVisitResult(context, {
125         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
126         uri: `${query}/`,
127         fallbackTitle: `${query}/`,
128         heuristic: true,
129       }),
130     ],
131   });
133   info("visit url, about: protocol (no host)");
134   query = "about:nonexistent";
135   context = createContext(query, { isPrivate: false });
136   await check_results({
137     context,
138     matches: [
139       makeVisitResult(context, {
140         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
141         uri: query,
142         fallbackTitle: query,
143         heuristic: true,
144       }),
145     ],
146   });
148   info("visit url, with non-standard whitespace");
149   query = "https://mozilla.org";
150   context = createContext(`${query}\u2028`, { isPrivate: false });
151   await check_results({
152     context,
153     matches: [
154       makeVisitResult(context, {
155         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
156         uri: `${query}/`,
157         fallbackTitle: `${query}/`,
158         heuristic: true,
159       }),
160     ],
161   });
163   // This is distinct because of how we predict being able to url autofill via
164   // host lookups.
165   info("visit url, host matching visited host but not visited url");
166   await PlacesTestUtils.addVisits([
167     {
168       uri: Services.io.newURI("http://mozilla.org/wine/"),
169       title: "Mozilla Wine",
170       transition: PlacesUtils.history.TRANSITION_TYPED,
171     },
172   ]);
173   query = "mozilla.org/rum";
174   context = createContext(`${query}\u2028`, { isPrivate: false });
175   await check_results({
176     context,
177     matches: [
178       makeVisitResult(context, {
179         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
180         uri: `http://${query}`,
181         fallbackTitle: `http://${query}`,
182         iconUri: "page-icon:http://mozilla.org/",
183         heuristic: true,
184       }),
185     ],
186   });
187   await PlacesUtils.history.clear();
189   // And hosts with no dot in them are special, due to requiring safelisting.
190   info("unknown host");
191   query = "firefox";
192   context = createContext(query, { isPrivate: false });
193   await check_results({
194     context,
195     matches: [
196       makeSearchResult(context, {
197         engineName: SUGGESTIONS_ENGINE_NAME,
198         heuristic: true,
199       }),
200     ],
201   });
203   info("string with known host");
204   query = "firefox/get";
205   context = createContext(query, { isPrivate: false });
206   await check_results({
207     context,
208     matches: [
209       makeSearchResult(context, {
210         engineName: SUGGESTIONS_ENGINE_NAME,
211         heuristic: true,
212       }),
213     ],
214   });
216   Services.prefs.setBoolPref("browser.fixup.domainwhitelist.firefox", true);
217   registerCleanupFunction(() => {
218     Services.prefs.clearUserPref("browser.fixup.domainwhitelist.firefox");
219   });
221   info("known host");
222   query = "firefox";
223   context = createContext(query, { isPrivate: false });
224   await check_results({
225     context,
226     matches: [
227       makeVisitResult(context, {
228         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
229         uri: `http://${query}/`,
230         fallbackTitle: `http://${query}/`,
231         heuristic: true,
232       }),
233       makeSearchResult(context, {
234         engineName: SUGGESTIONS_ENGINE_NAME,
235       }),
236     ],
237   });
239   info("url with known host");
240   query = "firefox/get";
241   context = createContext(query, { isPrivate: false });
242   await check_results({
243     context,
244     matches: [
245       makeVisitResult(context, {
246         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
247         uri: `http://${query}`,
248         fallbackTitle: `http://${query}`,
249         iconUri: "page-icon:http://firefox/",
250         heuristic: true,
251       }),
252     ],
253   });
255   info("visit url, host matching visited host but not visited url, known host");
256   Services.prefs.setBoolPref("browser.fixup.domainwhitelist.mozilla", true);
257   registerCleanupFunction(() => {
258     Services.prefs.clearUserPref("browser.fixup.domainwhitelist.mozilla");
259   });
260   query = "mozilla/rum";
261   context = createContext(query, { isPrivate: false });
262   await check_results({
263     context,
264     matches: [
265       makeVisitResult(context, {
266         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
267         uri: `http://${query}`,
268         fallbackTitle: `http://${query}`,
269         iconUri: "page-icon:http://mozilla/",
270         heuristic: true,
271       }),
272     ],
273   });
275   // ipv4 and ipv6 literal addresses should offer to visit.
276   info("visit url, ipv4 literal");
277   query = "127.0.0.1";
278   context = createContext(query, { isPrivate: false });
279   await check_results({
280     context,
281     matches: [
282       makeVisitResult(context, {
283         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
284         uri: `http://${query}/`,
285         fallbackTitle: `http://${query}/`,
286         heuristic: true,
287       }),
288     ],
289   });
291   info("visit url, ipv6 literal");
292   query = "[2001:db8::1]";
293   context = createContext(query, { isPrivate: false });
294   await check_results({
295     context,
296     matches: [
297       makeVisitResult(context, {
298         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
299         uri: `http://${query}/`,
300         fallbackTitle: `http://${query}/`,
301         heuristic: true,
302       }),
303     ],
304   });
306   // Setting keyword.enabled to false should always try to visit.
307   let keywordEnabled = Services.prefs.getBoolPref("keyword.enabled");
308   Services.prefs.setBoolPref("keyword.enabled", false);
309   registerCleanupFunction(() => {
310     Services.prefs.clearUserPref("keyword.enabled");
311   });
312   info("visit url, keyword.enabled = false");
313   query = "bacon";
314   context = createContext(query, { isPrivate: false });
315   await check_results({
316     context,
317     matches: [
318       makeVisitResult(context, {
319         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
320         uri: `http://${query}/`,
321         fallbackTitle: `http://${query}/`,
322         heuristic: true,
323       }),
324     ],
325   });
327   info("visit two word query, keyword.enabled = false");
328   query = "bacon lovers";
329   context = createContext(query, { isPrivate: false });
330   await check_results({
331     context,
332     matches: [
333       makeVisitResult(context, {
334         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
335         uri: query,
336         fallbackTitle: query,
337         heuristic: true,
338       }),
339     ],
340   });
342   info("Forced search through a restriction token, keyword.enabled = false");
343   query = "?bacon";
344   context = createContext(query, { isPrivate: false });
345   await check_results({
346     context,
347     matches: [
348       makeSearchResult(context, {
349         engineName: SUGGESTIONS_ENGINE_NAME,
350         heuristic: true,
351         query: "bacon",
352       }),
353     ],
354   });
356   Services.prefs.setBoolPref("keyword.enabled", true);
357   info("visit two word query, keyword.enabled = true");
358   query = "bacon lovers";
359   context = createContext(query, { isPrivate: false });
360   await check_results({
361     context,
362     matches: [
363       makeSearchResult(context, {
364         engineName: SUGGESTIONS_ENGINE_NAME,
365         heuristic: true,
366       }),
367     ],
368   });
370   Services.prefs.setBoolPref("keyword.enabled", keywordEnabled);
372   info("visit url, scheme+host");
373   query = "http://example";
374   context = createContext(query, { isPrivate: false });
375   await check_results({
376     context,
377     matches: [
378       makeVisitResult(context, {
379         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
380         uri: `${query}/`,
381         fallbackTitle: `${query}/`,
382         heuristic: true,
383       }),
384     ],
385   });
387   info("visit url, scheme+host");
388   query = "ftp://example";
389   context = createContext(query, { isPrivate: false });
390   await check_results({
391     context,
392     matches: [
393       makeVisitResult(context, {
394         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
395         uri: `${query}/`,
396         fallbackTitle: `${query}/`,
397         heuristic: true,
398       }),
399     ],
400   });
402   info("visit url, host+port");
403   query = "example:8080";
404   context = createContext(query, { isPrivate: false });
405   await check_results({
406     context,
407     matches: [
408       makeVisitResult(context, {
409         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
410         uri: `http://${query}/`,
411         fallbackTitle: `http://${query}/`,
412         heuristic: true,
413       }),
414     ],
415   });
417   info("numerical operations that look like urls should search");
418   query = "123/12";
419   context = createContext(query, { isPrivate: false });
420   await check_results({
421     context,
422     matches: [
423       makeSearchResult(context, {
424         engineName: SUGGESTIONS_ENGINE_NAME,
425         heuristic: true,
426       }),
427     ],
428   });
430   info("numerical operations that look like urls should search");
431   query = "123.12/12.1";
432   context = createContext(query, { isPrivate: false });
433   await check_results({
434     context,
435     matches: [
436       makeSearchResult(context, {
437         engineName: SUGGESTIONS_ENGINE_NAME,
438         heuristic: true,
439       }),
440     ],
441   });
443   query = "resource:///modules";
444   context = createContext(query, { isPrivate: false });
445   await check_results({
446     context,
447     matches: [
448       makeVisitResult(context, {
449         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
450         uri: query,
451         fallbackTitle: query,
452         heuristic: true,
453       }),
454     ],
455   });
457   info("access resource://app/modules");
458   query = "resource://app/modules";
459   context = createContext(query, { isPrivate: false });
460   await check_results({
461     context,
462     matches: [
463       makeVisitResult(context, {
464         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
465         uri: query,
466         fallbackTitle: query,
467         heuristic: true,
468       }),
469     ],
470   });
472   info("protocol with an extra slash");
473   query = "http:///";
474   context = createContext(query, { isPrivate: false });
475   await check_results({
476     context,
477     matches: [
478       makeSearchResult(context, {
479         engineName: SUGGESTIONS_ENGINE_NAME,
480         heuristic: true,
481       }),
482     ],
483   });
485   info("change default engine");
486   let originalTestEngine = Services.search.getEngineByName(
487     SUGGESTIONS_ENGINE_NAME
488   );
489   await SearchTestUtils.installSearchExtension({
490     name: "AliasEngine",
491     keyword: "alias",
492   });
493   let engine2 = Services.search.getEngineByName("AliasEngine");
494   Assert.notEqual(
495     Services.search.defaultEngine,
496     engine2,
497     "New engine shouldn't be the current engine yet"
498   );
499   await Services.search.setDefault(
500     engine2,
501     Ci.nsISearchService.CHANGE_REASON_UNKNOWN
502   );
503   query = "toronto";
504   context = createContext(query, { isPrivate: false });
505   await check_results({
506     context,
507     matches: [
508       makeSearchResult(context, {
509         engineName: "AliasEngine",
510         heuristic: true,
511       }),
512     ],
513   });
514   await Services.search.setDefault(
515     originalTestEngine,
516     Ci.nsISearchService.CHANGE_REASON_UNKNOWN
517   );
519   info(
520     "Leading search-mode restriction tokens are removed from the search result."
521   );
522   for (let token of UrlbarTokenizer.SEARCH_MODE_RESTRICT) {
523     for (let spaces of TEST_SPACES) {
524       query = token + spaces + "query";
525       info("Testing: " + JSON.stringify({ query, spaces: codePoints(spaces) }));
526       let expectedQuery = query.substring(1).trimStart();
527       context = createContext(query, { isPrivate: false });
528       info(`Searching for "${query}", expecting "${expectedQuery}"`);
529       let payload = {
530         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
531         heuristic: true,
532         query: expectedQuery,
533         alias: token,
534       };
535       if (token == UrlbarTokenizer.RESTRICT.SEARCH) {
536         payload.source = UrlbarUtils.RESULT_SOURCE.SEARCH;
537         payload.engineName = SUGGESTIONS_ENGINE_NAME;
538       }
539       await check_results({
540         context,
541         matches: [makeSearchResult(context, payload)],
542       });
543     }
544   }
546   info(
547     "Leading search-mode restriction tokens are removed from the search result with keyword.enabled = false."
548   );
549   Services.prefs.setBoolPref("keyword.enabled", false);
550   for (let token of UrlbarTokenizer.SEARCH_MODE_RESTRICT) {
551     for (let spaces of TEST_SPACES) {
552       query = token + spaces + "query";
553       info("Testing: " + JSON.stringify({ query, spaces: codePoints(spaces) }));
554       let expectedQuery = query.substring(1).trimStart();
555       context = createContext(query, { isPrivate: false });
556       info(`Searching for "${query}", expecting "${expectedQuery}"`);
557       let payload = {
558         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
559         heuristic: true,
560         query: expectedQuery,
561         alias: token,
562       };
563       if (token == UrlbarTokenizer.RESTRICT.SEARCH) {
564         payload.source = UrlbarUtils.RESULT_SOURCE.SEARCH;
565         payload.engineName = SUGGESTIONS_ENGINE_NAME;
566       }
567       await check_results({
568         context,
569         matches: [makeSearchResult(context, payload)],
570       });
571     }
572   }
573   Services.prefs.clearUserPref("keyword.enabled");
575   info(
576     "Leading non-search-mode restriction tokens are not removed from the search result."
577   );
578   for (let token of Object.values(UrlbarTokenizer.RESTRICT)) {
579     if (UrlbarTokenizer.SEARCH_MODE_RESTRICT.has(token)) {
580       continue;
581     }
582     for (let spaces of TEST_SPACES) {
583       query = token + spaces + "query";
584       info("Testing: " + JSON.stringify({ query, spaces: codePoints(spaces) }));
585       let expectedQuery = query;
586       context = createContext(query, { isPrivate: false });
587       info(`Searching for "${query}", expecting "${expectedQuery}"`);
588       await check_results({
589         context,
590         matches: [
591           makeSearchResult(context, {
592             heuristic: true,
593             query: expectedQuery,
594             engineName: SUGGESTIONS_ENGINE_NAME,
595           }),
596         ],
597       });
598     }
599   }
601   info(
602     "Test the format inputed is user@host, and the host is in domainwhitelist"
603   );
604   Services.prefs.setBoolPref("browser.fixup.domainwhitelist.test-host", true);
605   registerCleanupFunction(() => {
606     Services.prefs.clearUserPref("browser.fixup.domainwhitelist.test-host");
607   });
609   query = "any@test-host";
610   context = createContext(query, { isPrivate: false });
611   await check_results({
612     context,
613     matches: [
614       makeVisitResult(context, {
615         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
616         uri: `http://${query}/`,
617         fallbackTitle: `http://${query}/`,
618         heuristic: true,
619       }),
620       makeSearchResult(context, {
621         engineName: SUGGESTIONS_ENGINE_NAME,
622       }),
623     ],
624   });
626   info(
627     "Test the format inputed is user@host, but the host is not in domainwhitelist"
628   );
629   query = "any@not-host";
630   context = createContext(query, { isPrivate: false });
631   await check_results({
632     context,
633     matches: [
634       makeSearchResult(context, {
635         heuristic: true,
636         query,
637         engineName: SUGGESTIONS_ENGINE_NAME,
638       }),
639     ],
640   });
642   info(
643     "Test if the format of user:pass@host is handled as visit even if the host is not in domainwhitelist"
644   );
645   query = "user:pass@not-host";
646   context = createContext(query, { isPrivate: false });
647   await check_results({
648     context,
649     matches: [
650       makeVisitResult(context, {
651         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
652         uri: "http://user:pass@not-host/",
653         fallbackTitle: "http://user:pass@not-host/",
654         heuristic: true,
655       }),
656     ],
657   });
659   info("Test if the format of user@ipaddress is handled as visit");
660   query = "user@192.168.0.1";
661   context = createContext(query, { isPrivate: false });
662   await check_results({
663     context,
664     matches: [
665       makeVisitResult(context, {
666         source: UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
667         uri: "http://user@192.168.0.1/",
668         fallbackTitle: "http://user@192.168.0.1/",
669         heuristic: true,
670       }),
671       makeSearchResult(context, {
672         heuristic: false,
673         query,
674         engineName: SUGGESTIONS_ENGINE_NAME,
675       }),
676     ],
677   });
681  * Returns an array of code points in the given string.  Each code point is
682  * returned as a hexidecimal string.
684  * @param {string} str
685  *   The code points of this string will be returned.
686  * @returns {Array}
687  *   Array of code points in the string, where each is a hexidecimal string.
688  */
689 function codePoints(str) {
690   return str.split("").map(s => s.charCodeAt(0).toString(16));