Bug 1944627 - update sidebar button checked state for non-revamped sidebar cases...
[gecko.git] / browser / components / urlbar / UrlbarProviderClipboard.sys.mjs
blob12b27d1401c8eceb4e2a28996581d331ce3e6a5f
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/. */
5 import {
6   UrlbarProvider,
7   UrlbarUtils,
8 } from "resource:///modules/UrlbarUtils.sys.mjs";
10 const lazy = {};
12 ChromeUtils.defineESModuleGetters(lazy, {
13   UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
14   UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
15   UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.sys.mjs",
16 });
18 const RESULT_MENU_COMMANDS = {
19   DISMISS: "dismiss",
21 const CLIPBOARD_IMPRESSION_LIMIT = 2;
23 /**
24  * A provider that returns a suggested url to the user based
25  * on a valid URL stored in the clipboard.
26  */
27 class ProviderClipboard extends UrlbarProvider {
28   #previousClipboard = {
29     value: "",
30     impressionsLeft: CLIPBOARD_IMPRESSION_LIMIT,
31   };
33   constructor() {
34     super();
35   }
37   get name() {
38     return "UrlbarProviderClipboard";
39   }
41   get type() {
42     return UrlbarUtils.PROVIDER_TYPE.PROFILE;
43   }
45   setPreviousClipboardValue(newValue) {
46     this.#previousClipboard.value = newValue;
47   }
49   isActive(queryContext, controller) {
50     // Return clipboard results only for empty searches.
51     if (
52       !lazy.UrlbarPrefs.get("clipboard.featureGate") ||
53       !lazy.UrlbarPrefs.get("suggest.clipboard") ||
54       queryContext.searchString ||
55       queryContext.searchMode
56     ) {
57       return false;
58     }
59     const obj = {};
60     if (
61       !TelemetryStopwatch.running(
62         "FX_URLBAR_PROVIDER_CLIPBOARD_READ_TIME_MS",
63         obj
64       )
65     ) {
66       TelemetryStopwatch.start(
67         "FX_URLBAR_PROVIDER_CLIPBOARD_READ_TIME_MS",
68         obj
69       );
70     }
71     let textFromClipboard = controller.browserWindow.readFromClipboard();
72     TelemetryStopwatch.finish("FX_URLBAR_PROVIDER_CLIPBOARD_READ_TIME_MS", obj);
74     // Check for spaces in clipboard text to avoid suggesting
75     // clipboard content including both a url and the following text.
76     if (
77       !textFromClipboard ||
78       textFromClipboard.length > 2048 ||
79       lazy.UrlbarTokenizer.REGEXP_SPACES.test(textFromClipboard)
80     ) {
81       return false;
82     }
83     textFromClipboard =
84       controller.input.sanitizeTextFromClipboard(textFromClipboard);
85     const validUrl = this.#validUrl(textFromClipboard);
86     if (!validUrl) {
87       return false;
88     }
90     if (this.#previousClipboard.value === validUrl) {
91       if (this.#previousClipboard.impressionsLeft <= 0) {
92         return false;
93       }
94     } else {
95       this.#previousClipboard = {
96         value: validUrl,
97         impressionsLeft: CLIPBOARD_IMPRESSION_LIMIT,
98       };
99     }
101     return true;
102   }
104   #validUrl(clipboardVal) {
105     try {
106       let givenUrl;
107       givenUrl = new URL(clipboardVal);
108       if (givenUrl.protocol == "http:" || givenUrl.protocol == "https:") {
109         return givenUrl.href;
110       }
111     } catch (ex) {
112       // Not a valid URI.
113     }
114     return null;
115   }
117   getPriority() {
118     // Zero-prefix suggestions have the same priority as top sites.
119     return 1;
120   }
122   async startQuery(queryContext, addCallback) {
123     // If the query was started, isActive should have cached a url already.
124     let result = new lazy.UrlbarResult(
125       UrlbarUtils.RESULT_TYPE.URL,
126       UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
127       ...lazy.UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
128         fallbackTitle: [
129           UrlbarUtils.prepareUrlForDisplay(this.#previousClipboard.value, {
130             trimURL: false,
131           }),
132           UrlbarUtils.HIGHLIGHT.NONE,
133         ],
134         url: [this.#previousClipboard.value, UrlbarUtils.HIGHLIGHT.NONE],
135         icon: "chrome://global/skin/icons/clipboard.svg",
136         isBlockable: true,
137         blockL10n: {
138           id: "urlbar-result-menu-dismiss-firefox-suggest",
139         },
140       })
141     );
143     addCallback(this, result);
144   }
146   onEngagement(queryContext, controller, details) {
147     this.#previousClipboard.impressionsLeft = 0; // User has picked the suggested clipboard result
148     // Handle commands.
149     this.#handlePossibleCommand(
150       controller.view,
151       details.result,
152       details.selType
153     );
154   }
156   onImpression() {
157     this.#previousClipboard.impressionsLeft--;
158   }
160   #handlePossibleCommand(view, result, selType) {
161     switch (selType) {
162       case RESULT_MENU_COMMANDS.DISMISS:
163         view.controller.removeResult(result);
164         this.#previousClipboard.impressionsLeft = 0;
165         break;
166     }
167   }
170 const UrlbarProviderClipboard = new ProviderClipboard();
171 export { UrlbarProviderClipboard, CLIPBOARD_IMPRESSION_LIMIT };