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