Bug 1932502 - Remove the Suggest JS backend: Part 2: Remove the backend. r=daisuke
This is part 2 of 4, the main part. It removes the backend and the code in `BaseFeature` and feature subclasses that hook into it. As a consequence it also makes the other following changes: * Remove `QuickSuggest.backend` (eventually I'd like to add a Merino backend and then add a new `QuickSuggest.backends` list that has the Rust, Merino, and ML backends) * Add `QuickSuggest.config` * Replace uses of `QuickSuggest.backend.config` and `QuickSuggest.jsBackend.config` with `QuickSuggest.config` * Remove the `quickSuggestRustEnabled` Nimbus variable * Update most remaining tests so that they do not assume the JS backend exists I left the `browser.urlbar.quicksuggest.rustEnabled` pref. I would like to preserve the ability to toggle off Rust suggestions while keeping other parts of Suggest enabled even if we don't need it now. That seems like a wise thing to do and is similar to how both Merino and the ML backends can be toggled separately. Depends on D231465 Differential Revision: https://phabricator.services.mozilla.com/D231466
This commit is contained in:
@@ -28,8 +28,6 @@ const FEATURES = {
|
||||
MDNSuggestions: "resource:///modules/urlbar/private/MDNSuggestions.sys.mjs",
|
||||
PocketSuggestions:
|
||||
"resource:///modules/urlbar/private/PocketSuggestions.sys.mjs",
|
||||
SuggestBackendJs:
|
||||
"resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
|
||||
SuggestBackendMl:
|
||||
"resource:///modules/urlbar/private/SuggestBackendMl.sys.mjs",
|
||||
SuggestBackendRust:
|
||||
@@ -98,30 +96,22 @@ class _QuickSuggest {
|
||||
return ONBOARDING_URI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {SuggestBackendJs|SuggestBackendRust}
|
||||
* The currently active backend.
|
||||
*/
|
||||
get backend() {
|
||||
return lazy.UrlbarPrefs.get("quickSuggestRustEnabled")
|
||||
? this.rustBackend
|
||||
: this.jsBackend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {SuggestBackendRust}
|
||||
* The Rust backend. Not used when the JS backend is enabled.
|
||||
* The Rust backend, which manages the Rust component.
|
||||
*/
|
||||
get rustBackend() {
|
||||
return this.#features.SuggestBackendRust;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {SuggestBackendJs}
|
||||
* The JS backend. Not used when the Rust backend is enabled.
|
||||
* @returns {object}
|
||||
* Global Suggest configuration stored in remote settings and ingested by
|
||||
* the Rust component. See remote settings or the Rust component for the
|
||||
* latest schema.
|
||||
*/
|
||||
get jsBackend() {
|
||||
return this.#features.SuggestBackendJs;
|
||||
get config() {
|
||||
return this.rustBackend?.config || {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -128,9 +128,8 @@ class ProviderQuickSuggest extends UrlbarProvider {
|
||||
|
||||
// Fetch suggestions from all enabled sources.
|
||||
let promises = [];
|
||||
let { backend } = lazy.QuickSuggest;
|
||||
if (backend?.isEnabled) {
|
||||
promises.push(backend.query(searchString));
|
||||
if (lazy.QuickSuggest.rustBackend?.isEnabled) {
|
||||
promises.push(lazy.QuickSuggest.rustBackend.query(searchString));
|
||||
}
|
||||
if (
|
||||
lazy.UrlbarPrefs.get("quicksuggest.dataCollection.enabled") &&
|
||||
@@ -633,10 +632,7 @@ class ProviderQuickSuggest extends UrlbarProvider {
|
||||
*/
|
||||
cancelQuery() {
|
||||
// Cancel the Rust query.
|
||||
let backend = lazy.QuickSuggest.getFeature("SuggestBackendRust");
|
||||
if (backend?.isEnabled) {
|
||||
backend.cancelQuery();
|
||||
}
|
||||
lazy.QuickSuggest.rustBackend?.cancelQuery();
|
||||
|
||||
// Cancel the Merino timeout timer so it doesn't fire and record a timeout.
|
||||
// If it's already canceled or has fired, this is a no-op.
|
||||
|
||||
@@ -76,7 +76,6 @@ EXTRA_JS_MODULES["urlbar/private"] += [
|
||||
"private/MDNSuggestions.sys.mjs",
|
||||
"private/MLSuggest.sys.mjs",
|
||||
"private/PocketSuggestions.sys.mjs",
|
||||
"private/SuggestBackendJs.sys.mjs",
|
||||
"private/SuggestBackendMl.sys.mjs",
|
||||
"private/SuggestBackendRust.sys.mjs",
|
||||
"private/Weather.sys.mjs",
|
||||
|
||||
@@ -9,7 +9,6 @@ const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
|
||||
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
|
||||
SuggestionsMap: "resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
||||
UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
|
||||
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
|
||||
@@ -51,58 +50,6 @@ export class AddonSuggestions extends BaseFeature {
|
||||
return ["Amo"];
|
||||
}
|
||||
|
||||
enable(enabled) {
|
||||
if (enabled) {
|
||||
lazy.QuickSuggest.jsBackend.register(this);
|
||||
} else {
|
||||
lazy.QuickSuggest.jsBackend.unregister(this);
|
||||
this.#suggestionsMap?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
queryRemoteSettings(searchString) {
|
||||
const suggestions = this.#suggestionsMap?.get(searchString);
|
||||
if (!suggestions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return suggestions.map(suggestion => ({
|
||||
icon: suggestion.icon,
|
||||
url: suggestion.url,
|
||||
title: suggestion.title,
|
||||
description: suggestion.description,
|
||||
guid: suggestion.guid,
|
||||
score: suggestion.score,
|
||||
}));
|
||||
}
|
||||
|
||||
async onRemoteSettingsSync(rs) {
|
||||
const records = await rs.get({ filters: { type: "amo-suggestions" } });
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const suggestionsMap = new lazy.SuggestionsMap();
|
||||
|
||||
for (const record of records) {
|
||||
const { buffer } = await rs.attachments.download(record);
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const results = JSON.parse(new TextDecoder("utf-8").decode(buffer));
|
||||
await suggestionsMap.add(results, {
|
||||
mapKeyword:
|
||||
lazy.SuggestionsMap.MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD,
|
||||
});
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.#suggestionsMap = suggestionsMap;
|
||||
}
|
||||
|
||||
async makeResult(queryContext, suggestion, searchString) {
|
||||
if (!this.isEnabled) {
|
||||
// The feature is disabled on the client, but Merino may still return
|
||||
@@ -270,10 +217,8 @@ export class AddonSuggestions extends BaseFeature {
|
||||
get canShowLessFrequently() {
|
||||
const cap =
|
||||
lazy.UrlbarPrefs.get("addonsShowLessFrequentlyCap") ||
|
||||
lazy.QuickSuggest.backend.config?.showLessFrequentlyCap ||
|
||||
lazy.QuickSuggest.config.showLessFrequentlyCap ||
|
||||
0;
|
||||
return !cap || this.showLessFrequentlyCount < cap;
|
||||
}
|
||||
|
||||
#suggestionsMap = null;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
|
||||
SuggestionsMap: "resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
||||
UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
|
||||
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
|
||||
@@ -21,11 +20,6 @@ const NONSPONSORED_IAB_CATEGORIES = new Set(["5 - Education"]);
|
||||
* called "expanded Wikipedia") suggestions in remote settings.
|
||||
*/
|
||||
export class AdmWikipedia extends BaseFeature {
|
||||
constructor() {
|
||||
super();
|
||||
this.#suggestionsMap = new lazy.SuggestionsMap();
|
||||
}
|
||||
|
||||
get shouldEnable() {
|
||||
return (
|
||||
lazy.UrlbarPrefs.get("suggest.quicksuggest.nonsponsored") ||
|
||||
@@ -69,78 +63,6 @@ export class AdmWikipedia extends BaseFeature {
|
||||
return false;
|
||||
}
|
||||
|
||||
enable(enabled) {
|
||||
if (enabled) {
|
||||
lazy.QuickSuggest.jsBackend.register(this);
|
||||
} else {
|
||||
lazy.QuickSuggest.jsBackend.unregister(this);
|
||||
this.#suggestionsMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async queryRemoteSettings(searchString) {
|
||||
let suggestions = this.#suggestionsMap.get(searchString);
|
||||
if (!suggestions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Start each icon fetch at the same time and wait for them all to finish.
|
||||
let icons = await Promise.all(
|
||||
suggestions.map(({ icon }) => this.#fetchIcon(icon))
|
||||
);
|
||||
|
||||
return suggestions.map(suggestion => ({
|
||||
full_keyword: this.#getFullKeyword(searchString, suggestion.keywords),
|
||||
title: suggestion.title,
|
||||
url: suggestion.url,
|
||||
click_url: suggestion.click_url,
|
||||
impression_url: suggestion.impression_url,
|
||||
block_id: suggestion.id,
|
||||
advertiser: suggestion.advertiser,
|
||||
iab_category: suggestion.iab_category,
|
||||
is_sponsored: !NONSPONSORED_IAB_CATEGORIES.has(suggestion.iab_category),
|
||||
score: suggestion.score,
|
||||
position: suggestion.position,
|
||||
icon: icons.shift(),
|
||||
}));
|
||||
}
|
||||
|
||||
async onRemoteSettingsSync(rs) {
|
||||
let dataType = lazy.UrlbarPrefs.get("quickSuggestRemoteSettingsDataType");
|
||||
this.logger.debug("Loading remote settings", { dataType });
|
||||
|
||||
let [data] = await Promise.all([
|
||||
rs.get({ filters: { type: dataType } }),
|
||||
rs
|
||||
.get({ filters: { type: "icon" } })
|
||||
.then(icons =>
|
||||
Promise.all(icons.map(i => rs.attachments.downloadToDisk(i)))
|
||||
),
|
||||
]);
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let suggestionsMap = new lazy.SuggestionsMap();
|
||||
|
||||
this.logger.debug("Got remote settings data", { recordCount: data.length });
|
||||
for (let record of data) {
|
||||
let { buffer } = await rs.attachments.download(record);
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let results = JSON.parse(new TextDecoder("utf-8").decode(buffer));
|
||||
this.logger.debug("Adding results", { resultCount: results.length });
|
||||
await suggestionsMap.add(results);
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.#suggestionsMap = suggestionsMap;
|
||||
}
|
||||
|
||||
makeResult(queryContext, suggestion) {
|
||||
let originalUrl;
|
||||
if (suggestion.source == "rust") {
|
||||
@@ -241,88 +163,4 @@ export class AdmWikipedia extends BaseFeature {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "full keyword" (i.e., suggestion) for a query from a list of
|
||||
* keywords. The suggestions data doesn't include full keywords, so we make
|
||||
* our own based on the result's keyword phrases and a particular query. We
|
||||
* use two heuristics:
|
||||
*
|
||||
* (1) Find the first keyword phrase that has more words than the query. Use
|
||||
* its first `queryWords.length` words as the full keyword. e.g., if the
|
||||
* query is "moz" and `keywords` is ["moz", "mozi", "mozil", "mozill",
|
||||
* "mozilla", "mozilla firefox"], pick "mozilla firefox", pop off the
|
||||
* "firefox" and use "mozilla" as the full keyword.
|
||||
* (2) If there isn't any keyword phrase with more words, then pick the
|
||||
* longest phrase. e.g., pick "mozilla" in the previous example (assuming
|
||||
* the "mozilla firefox" phrase isn't there). That might be the query
|
||||
* itself.
|
||||
*
|
||||
* @param {string} query
|
||||
* The query string.
|
||||
* @param {Array} keywords
|
||||
* An array of suggestion keywords.
|
||||
* @returns {string}
|
||||
* The full keyword.
|
||||
*/
|
||||
#getFullKeyword(query, keywords) {
|
||||
let longerPhrase;
|
||||
let trimmedQuery = query.toLocaleLowerCase().trim();
|
||||
let queryWords = trimmedQuery.split(" ");
|
||||
|
||||
for (let phrase of keywords) {
|
||||
if (phrase.startsWith(query)) {
|
||||
let trimmedPhrase = phrase.trim();
|
||||
let phraseWords = trimmedPhrase.split(" ");
|
||||
// As an exception to (1), if the query ends with a space, then look for
|
||||
// phrases with one more word so that the suggestion includes a word
|
||||
// following the space.
|
||||
let extra = query.endsWith(" ") ? 1 : 0;
|
||||
let len = queryWords.length + extra;
|
||||
if (len < phraseWords.length) {
|
||||
// We found a phrase with more words.
|
||||
return phraseWords.slice(0, len).join(" ");
|
||||
}
|
||||
if (
|
||||
query.length < phrase.length &&
|
||||
(!longerPhrase || longerPhrase.length < trimmedPhrase.length)
|
||||
) {
|
||||
// We found a longer phrase with the same number of words.
|
||||
longerPhrase = trimmedPhrase;
|
||||
}
|
||||
}
|
||||
}
|
||||
return longerPhrase || trimmedQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the icon from RemoteSettings attachments.
|
||||
*
|
||||
* @param {string} path
|
||||
* The icon's remote settings path.
|
||||
* @returns {string}
|
||||
* The absolute file path to the downloaded attachment.
|
||||
*/
|
||||
async #fetchIcon(path) {
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let { rs } = lazy.QuickSuggest.jsBackend;
|
||||
if (!rs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let record = (
|
||||
await rs.get({
|
||||
filters: { id: `icon-${path}` },
|
||||
})
|
||||
).pop();
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
return rs.attachments.downloadToDisk(record);
|
||||
}
|
||||
|
||||
#suggestionsMap;
|
||||
}
|
||||
|
||||
@@ -27,14 +27,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
* place, without mixing it with unrelated code and cluttering up
|
||||
* `QuickSuggest`. You can also test it in isolation from `QuickSuggest`.
|
||||
*
|
||||
* - Remote settings management. You can register your feature with
|
||||
* `QuickSuggestRemoteSettings` and it will be called at appropriate times to
|
||||
* sync from remote settings.
|
||||
*
|
||||
* - If your feature also serves suggestions from remote settings, you can
|
||||
* implement one method, `queryRemoteSettings()`, to hook into
|
||||
* `UrlbarProviderQuickSuggest`.
|
||||
*
|
||||
* - Your feature will automatically get its own logger.
|
||||
*
|
||||
* To register your subclass with `QuickSuggest`, add it to the `FEATURES` const
|
||||
@@ -116,31 +108,6 @@ export class BaseFeature {
|
||||
*/
|
||||
enable(_enabled) {}
|
||||
|
||||
/**
|
||||
* If the feature manages suggestions from remote settings that should be
|
||||
* returned by UrlbarProviderQuickSuggest, the subclass should override this
|
||||
* method. It should return remote settings suggestions matching the given
|
||||
* search string.
|
||||
*
|
||||
* @param {string} _searchString
|
||||
* The search string.
|
||||
* @returns {Array}
|
||||
* An array of matching suggestions, or null if not implemented.
|
||||
*/
|
||||
async queryRemoteSettings(_searchString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the feature manages data in remote settings, the subclass should
|
||||
* override this method. It should fetch the data and build whatever data
|
||||
* structures are necessary to support the feature.
|
||||
*
|
||||
* @param {RemoteSettings} _rs
|
||||
* The `RemoteSettings` client object.
|
||||
*/
|
||||
async onRemoteSettingsSync(_rs) {}
|
||||
|
||||
/**
|
||||
* If the feature manages suggestions that either aren't served by Merino or
|
||||
* whose telemetry type is different from `merinoProvider`, the subclass
|
||||
|
||||
@@ -108,7 +108,7 @@ export class FakespotSuggestions extends BaseFeature {
|
||||
get canShowLessFrequently() {
|
||||
let cap =
|
||||
lazy.UrlbarPrefs.get("fakespotShowLessFrequentlyCap") ||
|
||||
lazy.QuickSuggest.backend.config?.showLessFrequentlyCap ||
|
||||
lazy.QuickSuggest.config.showLessFrequentlyCap ||
|
||||
0;
|
||||
return !cap || this.showLessFrequentlyCount < cap;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export class ImpressionCaps extends BaseFeature {
|
||||
this.logger.debug("Starting impression stats update", {
|
||||
type,
|
||||
currentStats: this.#stats,
|
||||
impression_caps: lazy.QuickSuggest.jsBackend.config.impression_caps,
|
||||
impression_caps: lazy.QuickSuggest.config.impression_caps,
|
||||
});
|
||||
|
||||
// Don't bother recording anything if caps are disabled.
|
||||
@@ -151,7 +151,9 @@ export class ImpressionCaps extends BaseFeature {
|
||||
|
||||
// Validate stats against any changes to the impression caps in the config.
|
||||
this._onConfigSet = () => this.#validateStats();
|
||||
lazy.QuickSuggest.jsBackend.emitter.on("config-set", this._onConfigSet);
|
||||
// TODO: If impression caps are ever enabled again, this will need to be
|
||||
// fixed.
|
||||
// lazy.QuickSuggest.jsBackend.emitter.on("config-set", this._onConfigSet);
|
||||
|
||||
// Periodically record impression counters reset telemetry.
|
||||
this.#setCountersResetInterval();
|
||||
@@ -165,7 +167,9 @@ export class ImpressionCaps extends BaseFeature {
|
||||
}
|
||||
|
||||
#uninit() {
|
||||
lazy.QuickSuggest.jsBackend.emitter.off("config-set", this._onConfigSet);
|
||||
// TODO: If impression caps are ever enabled again, this will need to be
|
||||
// fixed.
|
||||
// lazy.QuickSuggest.jsBackend.emitter.off("config-set", this._onConfigSet);
|
||||
this._onConfigSet = null;
|
||||
|
||||
lazy.clearInterval(this._impressionCountersResetInterval);
|
||||
@@ -207,7 +211,7 @@ export class ImpressionCaps extends BaseFeature {
|
||||
* corresponding to each impression cap. See the `#stats` comment for info.
|
||||
*/
|
||||
#validateStats() {
|
||||
let { impression_caps } = lazy.QuickSuggest.jsBackend.config;
|
||||
let { impression_caps } = lazy.QuickSuggest.config;
|
||||
|
||||
this.logger.debug("Validating impression stats", {
|
||||
impression_caps,
|
||||
@@ -325,7 +329,7 @@ export class ImpressionCaps extends BaseFeature {
|
||||
#resetElapsedCounters() {
|
||||
this.logger.debug("Checking for elapsed impression cap intervals", {
|
||||
currentStats: this.#stats,
|
||||
impression_caps: lazy.QuickSuggest.jsBackend.config.impression_caps,
|
||||
impression_caps: lazy.QuickSuggest.config.impression_caps,
|
||||
});
|
||||
|
||||
let now = Date.now();
|
||||
@@ -446,8 +450,7 @@ export class ImpressionCaps extends BaseFeature {
|
||||
// cap is the one with the same suggestion type (sponsored or non-sponsored)
|
||||
// and interval. See `#validateStats()` for more.
|
||||
//
|
||||
// Impression caps are stored in the remote settings config. See
|
||||
// `SuggestBackendJs.config.impression_caps`.
|
||||
// Impression caps are stored in the Suggest remote settings global config.
|
||||
#stats = {};
|
||||
|
||||
// Whether impression stats are currently being updated.
|
||||
|
||||
@@ -8,7 +8,6 @@ const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
|
||||
SuggestionsMap: "resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
||||
UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
|
||||
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
|
||||
@@ -48,49 +47,6 @@ export class MDNSuggestions extends BaseFeature {
|
||||
return ["Mdn"];
|
||||
}
|
||||
|
||||
enable(enabled) {
|
||||
if (enabled) {
|
||||
lazy.QuickSuggest.jsBackend.register(this);
|
||||
} else {
|
||||
lazy.QuickSuggest.jsBackend.unregister(this);
|
||||
this.#suggestionsMap?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
queryRemoteSettings(searchString) {
|
||||
const suggestions = this.#suggestionsMap?.get(searchString);
|
||||
return suggestions
|
||||
? suggestions.map(suggestion => ({ ...suggestion }))
|
||||
: [];
|
||||
}
|
||||
|
||||
async onRemoteSettingsSync(rs) {
|
||||
const records = await rs.get({ filters: { type: "mdn-suggestions" } });
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const suggestionsMap = new lazy.SuggestionsMap();
|
||||
|
||||
for (const record of records) {
|
||||
const { buffer } = await rs.attachments.download(record);
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const results = JSON.parse(new TextDecoder("utf-8").decode(buffer));
|
||||
await suggestionsMap.add(results, {
|
||||
mapKeyword:
|
||||
lazy.SuggestionsMap.MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD,
|
||||
});
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.#suggestionsMap = suggestionsMap;
|
||||
}
|
||||
|
||||
async makeResult(queryContext, suggestion) {
|
||||
if (!this.isEnabled) {
|
||||
// The feature is disabled on the client, but Merino may still return
|
||||
@@ -193,6 +149,4 @@ export class MDNSuggestions extends BaseFeature {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#suggestionsMap = null;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
|
||||
SuggestionsMap: "resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
||||
UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
|
||||
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
|
||||
@@ -25,12 +24,6 @@ const RESULT_MENU_COMMAND = {
|
||||
* A feature that manages Pocket suggestions in remote settings.
|
||||
*/
|
||||
export class PocketSuggestions extends BaseFeature {
|
||||
constructor() {
|
||||
super();
|
||||
this.#lowConfidenceSuggestionsMap = new lazy.SuggestionsMap();
|
||||
this.#highConfidenceSuggestionsMap = new lazy.SuggestionsMap();
|
||||
}
|
||||
|
||||
get shouldEnable() {
|
||||
return (
|
||||
lazy.UrlbarPrefs.get("pocketFeatureGate") &&
|
||||
@@ -59,94 +52,11 @@ export class PocketSuggestions extends BaseFeature {
|
||||
get canShowLessFrequently() {
|
||||
let cap =
|
||||
lazy.UrlbarPrefs.get("pocketShowLessFrequentlyCap") ||
|
||||
lazy.QuickSuggest.backend.config?.showLessFrequentlyCap ||
|
||||
lazy.QuickSuggest.config.showLessFrequentlyCap ||
|
||||
0;
|
||||
return !cap || this.showLessFrequentlyCount < cap;
|
||||
}
|
||||
|
||||
enable(enabled) {
|
||||
if (enabled) {
|
||||
lazy.QuickSuggest.jsBackend.register(this);
|
||||
} else {
|
||||
lazy.QuickSuggest.jsBackend.unregister(this);
|
||||
this.#lowConfidenceSuggestionsMap.clear();
|
||||
this.#highConfidenceSuggestionsMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async queryRemoteSettings(searchString) {
|
||||
// If the search string matches high confidence suggestions, they should be
|
||||
// treated as top picks. Otherwise try to match low confidence suggestions.
|
||||
let is_top_pick = false;
|
||||
let suggestions = this.#highConfidenceSuggestionsMap.get(searchString);
|
||||
if (suggestions.length) {
|
||||
is_top_pick = true;
|
||||
} else {
|
||||
suggestions = this.#lowConfidenceSuggestionsMap.get(searchString);
|
||||
}
|
||||
|
||||
let lowerSearchString = searchString.toLocaleLowerCase();
|
||||
return suggestions.map(suggestion => {
|
||||
// Add `full_keyword` to each matched suggestion. It should be the longest
|
||||
// keyword that starts with the user's search string.
|
||||
let full_keyword = lowerSearchString;
|
||||
let keywords = is_top_pick
|
||||
? suggestion.highConfidenceKeywords
|
||||
: suggestion.lowConfidenceKeywords;
|
||||
for (let keyword of keywords) {
|
||||
if (
|
||||
keyword.startsWith(lowerSearchString) &&
|
||||
full_keyword.length < keyword.length
|
||||
) {
|
||||
full_keyword = keyword;
|
||||
}
|
||||
}
|
||||
return { ...suggestion, is_top_pick, full_keyword };
|
||||
});
|
||||
}
|
||||
|
||||
async onRemoteSettingsSync(rs) {
|
||||
let records = await rs.get({ filters: { type: "pocket-suggestions" } });
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let lowMap = new lazy.SuggestionsMap();
|
||||
let highMap = new lazy.SuggestionsMap();
|
||||
|
||||
this.logger.debug("Got records", { recordCount: records.length });
|
||||
for (let record of records) {
|
||||
let { buffer } = await rs.attachments.download(record);
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let suggestions = JSON.parse(new TextDecoder("utf-8").decode(buffer));
|
||||
this.logger.debug("Adding suggestions", {
|
||||
suggestionsCount: suggestions.length,
|
||||
});
|
||||
|
||||
await lowMap.add(suggestions, {
|
||||
keywordsProperty: "lowConfidenceKeywords",
|
||||
mapKeyword:
|
||||
lazy.SuggestionsMap.MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD,
|
||||
});
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await highMap.add(suggestions, {
|
||||
keywordsProperty: "highConfidenceKeywords",
|
||||
});
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.#lowConfidenceSuggestionsMap = lowMap;
|
||||
this.#highConfidenceSuggestionsMap = highMap;
|
||||
}
|
||||
|
||||
makeResult(queryContext, suggestion, searchString) {
|
||||
if (!this.isEnabled) {
|
||||
// The feature is disabled on the client, but Merino may still return
|
||||
@@ -320,7 +230,4 @@ export class PocketSuggestions extends BaseFeature {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#lowConfidenceSuggestionsMap;
|
||||
#highConfidenceSuggestionsMap;
|
||||
}
|
||||
|
||||
@@ -1,442 +0,0 @@
|
||||
/* 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 { BaseFeature } from "resource:///modules/urlbar/private/BaseFeature.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
EventEmitter: "resource://gre/modules/EventEmitter.sys.mjs",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
||||
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
|
||||
});
|
||||
|
||||
const RS_COLLECTION = "quicksuggest";
|
||||
|
||||
// Entries are added to `SuggestionsMap` map in chunks, and each chunk will add
|
||||
// at most this many entries.
|
||||
const SUGGESTIONS_MAP_CHUNK_SIZE = 1000;
|
||||
|
||||
const TELEMETRY_LATENCY = "FX_URLBAR_QUICK_SUGGEST_REMOTE_SETTINGS_LATENCY_MS";
|
||||
|
||||
// See `SuggestionsMap.MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD`. When a full
|
||||
// keyword starts with one of the prefixes in this list, the user must type the
|
||||
// entire prefix to start triggering matches based on that full keyword, instead
|
||||
// of only the first word.
|
||||
const KEYWORD_PREFIXES_TO_TREAT_AS_SINGLE_WORDS = ["how to"];
|
||||
|
||||
/**
|
||||
* The Suggest JS backend. Not used when the Rust backend is enabled.
|
||||
*/
|
||||
export class SuggestBackendJs extends BaseFeature {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.#emitter = new lazy.EventEmitter();
|
||||
}
|
||||
|
||||
get shouldEnable() {
|
||||
return !lazy.UrlbarPrefs.get("quickSuggestRustEnabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {RemoteSettings}
|
||||
* The underlying `RemoteSettings` client object.
|
||||
*/
|
||||
get rs() {
|
||||
return this.#rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {EventEmitter}
|
||||
* The client will emit events on this object.
|
||||
*/
|
||||
get emitter() {
|
||||
return this.#emitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object}
|
||||
* Global quick suggest configuration stored in remote settings. When the
|
||||
* config changes the `emitter` property will emit a "config-set" event. The
|
||||
* config is an object that looks like this:
|
||||
*
|
||||
* {
|
||||
* impression_caps: {
|
||||
* nonsponsored: {
|
||||
* lifetime,
|
||||
* custom: [
|
||||
* { interval_s, max_count },
|
||||
* ],
|
||||
* },
|
||||
* sponsored: {
|
||||
* lifetime,
|
||||
* custom: [
|
||||
* { interval_s, max_count },
|
||||
* ],
|
||||
* },
|
||||
* },
|
||||
* show_less_frequently_cap,
|
||||
* }
|
||||
*/
|
||||
get config() {
|
||||
return this.#config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array}
|
||||
* Array of `BasicFeature` instances.
|
||||
*/
|
||||
get features() {
|
||||
return [...this.#features];
|
||||
}
|
||||
|
||||
enable(enabled) {
|
||||
if (!enabled) {
|
||||
this.#enableSettings(false);
|
||||
} else if (this.#features.size) {
|
||||
this.#enableSettings(true);
|
||||
this.#syncAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a quick suggest feature that uses remote settings.
|
||||
*
|
||||
* @param {BaseFeature} feature
|
||||
* An instance of a `BaseFeature` subclass. See `BaseFeature` for methods
|
||||
* that the subclass must implement.
|
||||
*/
|
||||
register(feature) {
|
||||
this.logger.debug("Registering feature: " + feature.name);
|
||||
this.#features.add(feature);
|
||||
if (this.isEnabled) {
|
||||
if (this.#features.size == 1) {
|
||||
this.#enableSettings(true);
|
||||
}
|
||||
this.#syncFeature(feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a quick suggest feature that uses remote settings.
|
||||
*
|
||||
* @param {BaseFeature} feature
|
||||
* An instance of a `BaseFeature` subclass.
|
||||
*/
|
||||
unregister(feature) {
|
||||
this.logger.debug("Unregistering feature: " + feature.name);
|
||||
this.#features.delete(feature);
|
||||
if (!this.#features.size) {
|
||||
this.#enableSettings(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries remote settings suggestions from all registered features.
|
||||
*
|
||||
* @param {string} searchString
|
||||
* The search string.
|
||||
* @returns {Array}
|
||||
* The remote settings suggestions. If there are no matches, an empty array
|
||||
* is returned.
|
||||
*/
|
||||
async query(searchString) {
|
||||
let suggestions;
|
||||
let stopwatchInstance = {};
|
||||
TelemetryStopwatch.start(TELEMETRY_LATENCY, stopwatchInstance);
|
||||
try {
|
||||
suggestions = await this.#queryHelper(searchString);
|
||||
TelemetryStopwatch.finish(TELEMETRY_LATENCY, stopwatchInstance);
|
||||
} catch (error) {
|
||||
TelemetryStopwatch.cancel(TELEMETRY_LATENCY, stopwatchInstance);
|
||||
this.logger.error("Query error: " + error);
|
||||
}
|
||||
|
||||
return suggestions || [];
|
||||
}
|
||||
|
||||
async #queryHelper(searchString) {
|
||||
this.logger.info("Handling query: " + JSON.stringify(searchString));
|
||||
|
||||
let results = await Promise.all(
|
||||
[...this.#features].map(async feature => {
|
||||
let suggestions = await feature.queryRemoteSettings(searchString);
|
||||
return [feature, suggestions ?? []];
|
||||
})
|
||||
);
|
||||
|
||||
let allSuggestions = [];
|
||||
for (let [feature, suggestions] of results) {
|
||||
for (let suggestion of suggestions) {
|
||||
// Features typically return suggestion objects straight from their
|
||||
// suggestion maps. We don't want consumers to modify those objects
|
||||
// since they are the source of truth (tests especially tend to do
|
||||
// this), so return copies to consumers.
|
||||
allSuggestions.push({
|
||||
...suggestion,
|
||||
source: "remote-settings",
|
||||
provider: feature.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return allSuggestions;
|
||||
}
|
||||
|
||||
async #enableSettings(enabled) {
|
||||
if (enabled && !this.#rs) {
|
||||
this.logger.debug("Creating RemoteSettings client");
|
||||
this.#onSettingsSync = event => this.#syncAll({ event });
|
||||
this.#rs = lazy.RemoteSettings(RS_COLLECTION);
|
||||
this.#rs.on("sync", this.#onSettingsSync);
|
||||
await this.#syncConfig();
|
||||
} else if (!enabled && this.#rs) {
|
||||
this.logger.debug("Destroying RemoteSettings client");
|
||||
this.#rs.off("sync", this.#onSettingsSync);
|
||||
this.#rs = null;
|
||||
this.#onSettingsSync = null;
|
||||
}
|
||||
}
|
||||
|
||||
async #syncConfig() {
|
||||
this.logger.debug("Syncing config");
|
||||
let rs = this.#rs;
|
||||
|
||||
let configArray = await rs.get({ filters: { type: "configuration" } });
|
||||
if (rs != this.#rs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug("Got config array: " + JSON.stringify(configArray));
|
||||
this.#setConfig(configArray?.[0]?.configuration || {});
|
||||
}
|
||||
|
||||
async #syncFeature(feature) {
|
||||
this.logger.debug("Syncing feature: " + feature.name);
|
||||
await feature.onRemoteSettingsSync(this.#rs);
|
||||
}
|
||||
|
||||
async #syncAll({ event = null } = {}) {
|
||||
this.logger.debug("Syncing all");
|
||||
let rs = this.#rs;
|
||||
|
||||
// Remove local files of deleted records
|
||||
if (event?.data?.deleted) {
|
||||
await Promise.all(
|
||||
event.data.deleted
|
||||
.filter(d => d.attachment)
|
||||
.map(entry =>
|
||||
Promise.all([
|
||||
this.#rs.attachments.deleteDownloaded(entry), // type: data
|
||||
this.#rs.attachments.deleteFromDisk(entry), // type: icon
|
||||
])
|
||||
)
|
||||
);
|
||||
if (rs != this.#rs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let promises = [this.#syncConfig()];
|
||||
for (let feature of this.#features) {
|
||||
promises.push(this.#syncFeature(feature));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quick suggest config and emits a "config-set" event.
|
||||
*
|
||||
* @param {object} config
|
||||
* The config object.
|
||||
*/
|
||||
#setConfig(config) {
|
||||
config = lazy.UrlbarUtils.copySnakeKeysToCamel(config ?? {});
|
||||
this.logger.debug("Setting config: " + JSON.stringify(config));
|
||||
this.#config = config;
|
||||
this.#emitter.emit("config-set");
|
||||
}
|
||||
|
||||
async _test_syncAll() {
|
||||
if (this.#rs) {
|
||||
// `RemoteSettingsClient` won't start another import if it's already
|
||||
// importing. Wait for it to finish before starting the new one.
|
||||
await this.#rs._importingPromise;
|
||||
await this.#syncAll();
|
||||
}
|
||||
}
|
||||
|
||||
// The `RemoteSettings` client.
|
||||
#rs = null;
|
||||
|
||||
// Registered `BaseFeature` instances.
|
||||
#features = new Set();
|
||||
|
||||
// Configuration data synced from remote settings. See the `config` getter.
|
||||
#config = {};
|
||||
|
||||
#emitter = null;
|
||||
#onSettingsSync = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around `Map` that handles quick suggest suggestions from remote
|
||||
* settings. It maps keywords to suggestions. It has two benefits over `Map`:
|
||||
*
|
||||
* - The main benefit is that map entries are added in batches on idle to avoid
|
||||
* blocking the main thread for too long, since there can be many suggestions
|
||||
* and keywords.
|
||||
* - A secondary benefit is that the interface is tailored to quick suggest
|
||||
* suggestions, which have a `keywords` property.
|
||||
*/
|
||||
export class SuggestionsMap {
|
||||
/**
|
||||
* Returns the list of suggestions for a keyword.
|
||||
*
|
||||
* @param {string} keyword
|
||||
* The keyword.
|
||||
* @returns {Array}
|
||||
* The array of suggestions for the keyword. If the keyword isn't in the
|
||||
* map, the array will be empty.
|
||||
*/
|
||||
get(keyword) {
|
||||
let object = this.#suggestionsByKeyword.get(keyword.toLocaleLowerCase());
|
||||
if (!object) {
|
||||
return [];
|
||||
}
|
||||
return Array.isArray(object) ? object : [object];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of suggestion objects to the results map. Each suggestion must
|
||||
* have a property whose value is an array of keyword strings. The
|
||||
* suggestion's keywords will be taken from this array either exactly as they
|
||||
* are specified or by generating new keywords from them; see `mapKeyword`.
|
||||
*
|
||||
* @param {Array} suggestions
|
||||
* Array of suggestion objects.
|
||||
* @param {object} options
|
||||
* Options object.
|
||||
* @param {string} options.keywordsProperty
|
||||
* The name of the keywords property in each suggestion.
|
||||
* @param {Function} options.mapKeyword
|
||||
* If null, the keywords for each suggestion will be taken from the keywords
|
||||
* array exactly as they are specified. Otherwise, this function will be
|
||||
* called for each string in the array, and it should return an array of
|
||||
* strings. The suggestion's final list of keywords will be the union of all
|
||||
* strings returned by this function. See also the `MAP_KEYWORD_*` consts.
|
||||
*/
|
||||
async add(
|
||||
suggestions,
|
||||
{ keywordsProperty = "keywords", mapKeyword = null } = {}
|
||||
) {
|
||||
// There can be many suggestions, and each suggestion can have many
|
||||
// keywords. To avoid blocking the main thread for too long, update the map
|
||||
// in chunks, and to avoid blocking the UI and other higher priority work,
|
||||
// do each chunk only when the main thread is idle. During each chunk, we'll
|
||||
// add at most `chunkSize` entries to the map.
|
||||
let suggestionIndex = 0;
|
||||
let keywordIndex = 0;
|
||||
|
||||
// Keep adding chunks until all suggestions have been fully added.
|
||||
while (suggestionIndex < suggestions.length) {
|
||||
await new Promise(resolve => {
|
||||
Services.tm.idleDispatchToMainThread(() => {
|
||||
// Keep updating the map until the current chunk is done.
|
||||
let indexInChunk = 0;
|
||||
while (
|
||||
indexInChunk < SuggestionsMap.chunkSize &&
|
||||
suggestionIndex < suggestions.length
|
||||
) {
|
||||
let suggestion = suggestions[suggestionIndex];
|
||||
let keywords = suggestion[keywordsProperty];
|
||||
if (keywordIndex == keywords.length) {
|
||||
// We've added entries for all keywords of the current suggestion.
|
||||
// Move on to the next suggestion.
|
||||
suggestionIndex++;
|
||||
keywordIndex = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// As a convenience, allow `mapKeyword` to return a string even
|
||||
// though the JSDoc says an array must be returned.
|
||||
let originalKeyword = keywords[keywordIndex];
|
||||
let mappedKeywords =
|
||||
mapKeyword?.(originalKeyword) ?? originalKeyword;
|
||||
if (typeof mappedKeywords == "string") {
|
||||
mappedKeywords = [mappedKeywords];
|
||||
}
|
||||
|
||||
for (let keyword of mappedKeywords) {
|
||||
// If the keyword's only suggestion is `suggestion`, store it
|
||||
// directly as the value. Otherwise store an array of unique
|
||||
// suggestions. See the `#suggestionsByKeyword` comment.
|
||||
let object = this.#suggestionsByKeyword.get(keyword);
|
||||
if (!object) {
|
||||
this.#suggestionsByKeyword.set(keyword, suggestion);
|
||||
} else {
|
||||
let isArray = Array.isArray(object);
|
||||
if (!isArray && object != suggestion) {
|
||||
this.#suggestionsByKeyword.set(keyword, [object, suggestion]);
|
||||
} else if (isArray && !object.includes(suggestion)) {
|
||||
object.push(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keywordIndex++;
|
||||
indexInChunk++;
|
||||
}
|
||||
|
||||
// The current chunk is done.
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.#suggestionsByKeyword.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Function}
|
||||
* A `mapKeyword` function that maps a keyword to an array containing the
|
||||
* keyword's first word plus every subsequent prefix of the keyword. The
|
||||
* strings in `KEYWORD_PREFIXES_TO_TREAT_AS_SINGLE_WORDS` will modify this
|
||||
* behavior: When a full keyword starts with one of the prefixes in that
|
||||
* list, the generated prefixes will start at that prefix instead of the
|
||||
* first word.
|
||||
*/
|
||||
static get MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD() {
|
||||
return fullKeyword => {
|
||||
let prefix = KEYWORD_PREFIXES_TO_TREAT_AS_SINGLE_WORDS.find(p =>
|
||||
fullKeyword.startsWith(p + " ")
|
||||
);
|
||||
let spaceIndex = prefix ? prefix.length : fullKeyword.indexOf(" ");
|
||||
|
||||
let keywords = [fullKeyword];
|
||||
if (spaceIndex >= 0) {
|
||||
for (let i = spaceIndex; i < fullKeyword.length; i++) {
|
||||
keywords.push(fullKeyword.substring(0, i));
|
||||
}
|
||||
}
|
||||
return keywords;
|
||||
};
|
||||
}
|
||||
|
||||
// Maps each keyword in the dataset to one or more suggestions for the
|
||||
// keyword. If only one suggestion uses a keyword, the keyword's value in the
|
||||
// map will be the suggestion object. If more than one suggestion uses the
|
||||
// keyword, the value will be an array of the suggestions. The reason for not
|
||||
// always using an array is that we expect the vast majority of keywords to be
|
||||
// used by only one suggestion, and since there are potentially very many
|
||||
// keywords and suggestions and we keep them in memory all the time, we want
|
||||
// to save as much memory as possible.
|
||||
#suggestionsByKeyword = new Map();
|
||||
|
||||
// This is only defined as a property so that tests can override it.
|
||||
static chunkSize = SUGGESTIONS_MAP_CHUNK_SIZE;
|
||||
}
|
||||
@@ -108,6 +108,14 @@ export class SuggestBackendRust extends BaseFeature {
|
||||
}
|
||||
}
|
||||
|
||||
get enablingPreferences() {
|
||||
return ["quicksuggest.rustEnabled"];
|
||||
}
|
||||
|
||||
get shouldEnable() {
|
||||
return lazy.UrlbarPrefs.get("quicksuggest.rustEnabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object}
|
||||
* The global Suggest config from the Rust component as returned from
|
||||
@@ -125,10 +133,6 @@ export class SuggestBackendRust extends BaseFeature {
|
||||
return this.#ingestQueue.emptyPromise;
|
||||
}
|
||||
|
||||
get shouldEnable() {
|
||||
return lazy.UrlbarPrefs.get("quickSuggestRustEnabled");
|
||||
}
|
||||
|
||||
enable(enabled) {
|
||||
if (enabled) {
|
||||
this.#init();
|
||||
|
||||
@@ -173,7 +173,7 @@ export class Weather extends BaseFeature {
|
||||
get canShowLessFrequently() {
|
||||
const cap =
|
||||
lazy.UrlbarPrefs.get("weatherShowLessFrequentlyCap") ||
|
||||
lazy.QuickSuggest.backend.config?.showLessFrequentlyCap ||
|
||||
lazy.QuickSuggest.config.showLessFrequentlyCap ||
|
||||
0;
|
||||
return !cap || this.showLessFrequentlyCount < cap;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class YelpSuggestions extends BaseFeature {
|
||||
get canShowLessFrequently() {
|
||||
const cap =
|
||||
lazy.UrlbarPrefs.get("yelpShowLessFrequentlyCap") ||
|
||||
lazy.QuickSuggest.backend.config?.showLessFrequentlyCap ||
|
||||
lazy.QuickSuggest.config.showLessFrequentlyCap ||
|
||||
0;
|
||||
return !cap || this.showLessFrequentlyCount < cap;
|
||||
}
|
||||
|
||||
+1
-3
@@ -215,9 +215,7 @@ add_task(async function suggest() {
|
||||
assertAbandonmentTelemetry([
|
||||
{
|
||||
groups: "heuristic,suggest",
|
||||
results: UrlbarPrefs.get("quickSuggestRustEnabled")
|
||||
? "search_engine,rust_adm_nonsponsored"
|
||||
: "search_engine,rs_adm_nonsponsored",
|
||||
results: "search_engine,rust_adm_nonsponsored",
|
||||
n_results: 2,
|
||||
},
|
||||
]),
|
||||
|
||||
+1
-3
@@ -231,9 +231,7 @@ add_task(async function suggest() {
|
||||
assertEngagementTelemetry([
|
||||
{
|
||||
groups: "heuristic,suggest",
|
||||
results: UrlbarPrefs.get("quickSuggestRustEnabled")
|
||||
? "search_engine,rust_adm_nonsponsored"
|
||||
: "search_engine,rs_adm_nonsponsored",
|
||||
results: "search_engine,rust_adm_nonsponsored",
|
||||
n_results: 2,
|
||||
},
|
||||
]),
|
||||
|
||||
@@ -68,6 +68,5 @@ async function getRowByType(type) {
|
||||
}
|
||||
|
||||
function suggestResultType(typeWithoutSource) {
|
||||
let source = UrlbarPrefs.get("quickSuggestRustEnabled") ? "rust" : "rs";
|
||||
return `${source}_${typeWithoutSource}`;
|
||||
return `rust_${typeWithoutSource}`;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-disable mozilla/valid-lazy */
|
||||
/* eslint-disable jsdoc/require-param */
|
||||
|
||||
const lazy = {};
|
||||
@@ -9,28 +8,15 @@ const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
ExperimentFakes: "resource://testing-common/NimbusTestUtils.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
|
||||
Region: "resource://gre/modules/Region.sys.mjs",
|
||||
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
|
||||
RemoteSettingsConfig: "resource://gre/modules/RustRemoteSettings.sys.mjs",
|
||||
RemoteSettingsServer:
|
||||
"resource://testing-common/RemoteSettingsServer.sys.mjs",
|
||||
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
|
||||
SuggestBackendRust:
|
||||
"resource:///modules/urlbar/private/SuggestBackendRust.sys.mjs",
|
||||
Suggestion: "resource://gre/modules/RustSuggest.sys.mjs",
|
||||
SuggestionProvider: "resource://gre/modules/RustSuggest.sys.mjs",
|
||||
SuggestStore: "resource://gre/modules/RustSuggest.sys.mjs",
|
||||
TelemetryTestUtils: "resource://testing-common/TelemetryTestUtils.sys.mjs",
|
||||
TestUtils: "resource://testing-common/TestUtils.sys.mjs",
|
||||
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
||||
UrlbarProviderQuickSuggest:
|
||||
"resource:///modules/UrlbarProviderQuickSuggest.sys.mjs",
|
||||
UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.sys.mjs",
|
||||
UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
|
||||
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
||||
sinon: "resource://testing-common/Sinon.sys.mjs",
|
||||
});
|
||||
|
||||
let gTestScope;
|
||||
@@ -42,6 +28,7 @@ let gTestScope;
|
||||
// xpcshell tests.
|
||||
Object.defineProperty(lazy, "UrlbarTestUtils", {
|
||||
get: () => {
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
if (!lazy._UrlbarTestUtils) {
|
||||
const { UrlbarTestUtils: module } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/UrlbarTestUtils.sys.mjs"
|
||||
@@ -49,10 +36,13 @@ Object.defineProperty(lazy, "UrlbarTestUtils", {
|
||||
module.init(gTestScope);
|
||||
gTestScope.registerCleanupFunction(() => {
|
||||
// Make sure the utils are re-initialized during the next test.
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
lazy._UrlbarTestUtils = null;
|
||||
});
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
lazy._UrlbarTestUtils = module;
|
||||
}
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
return lazy._UrlbarTestUtils;
|
||||
},
|
||||
});
|
||||
@@ -64,6 +54,7 @@ Object.defineProperty(lazy, "UrlbarTestUtils", {
|
||||
// xpcshell tests.
|
||||
Object.defineProperty(lazy, "MerinoTestUtils", {
|
||||
get: () => {
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
if (!lazy._MerinoTestUtils) {
|
||||
const { MerinoTestUtils: module } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/MerinoTestUtils.sys.mjs"
|
||||
@@ -71,10 +62,13 @@ Object.defineProperty(lazy, "MerinoTestUtils", {
|
||||
module.init(gTestScope);
|
||||
gTestScope.registerCleanupFunction(() => {
|
||||
// Make sure the utils are re-initialized during the next test.
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
lazy._MerinoTestUtils = null;
|
||||
});
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
lazy._MerinoTestUtils = module;
|
||||
}
|
||||
// eslint-disable-next-line mozilla/valid-lazy
|
||||
return lazy._MerinoTestUtils;
|
||||
},
|
||||
});
|
||||
@@ -222,20 +216,8 @@ class _QuickSuggestTestUtils {
|
||||
await this.#remoteSettingsServer.start();
|
||||
this.#log("ensureQuickSuggestInit", "Remote settings server started");
|
||||
|
||||
// Get the cached `RemoteSettings` client used by the JS backend and tell it
|
||||
// to ignore signatures and to always force sync. Otherwise it won't sync if
|
||||
// the previous sync was recent enough, which is incompatible with testing.
|
||||
let rs = lazy.RemoteSettings("quicksuggest");
|
||||
let { get, verifySignature } = rs;
|
||||
rs.verifySignature = false;
|
||||
rs.get = opts => get.call(rs, { forceSync: true, ...opts });
|
||||
this.#restoreRemoteSettings = () => {
|
||||
rs.verifySignature = verifySignature;
|
||||
rs.get = get;
|
||||
};
|
||||
|
||||
// Finally, init Suggest and set prefs. Do this after setting up remote
|
||||
// settings because the current backend will immediately try to sync.
|
||||
// Init Suggest and set prefs. Do this after setting up remote settings
|
||||
// because the Rust backend will immediately try to sync.
|
||||
this.#log(
|
||||
"ensureQuickSuggestInit",
|
||||
"Calling QuickSuggest.init() and setting prefs"
|
||||
@@ -251,7 +233,7 @@ class _QuickSuggestTestUtils {
|
||||
serverUrl: this.#remoteSettingsServer.url.toString(),
|
||||
});
|
||||
|
||||
// Wait for the current backend to finish syncing.
|
||||
// Wait for the Rust backend to finish syncing.
|
||||
await this.forceSync();
|
||||
|
||||
// Set up Merino. This can happen any time relative to Suggest init.
|
||||
@@ -279,8 +261,8 @@ class _QuickSuggestTestUtils {
|
||||
async #uninitQuickSuggest(prefs, clearDataCollectionEnabled) {
|
||||
this.#log("#uninitQuickSuggest", "Started");
|
||||
|
||||
// Reset prefs, which can cause the current backend to start syncing. Wait
|
||||
// for it to finish.
|
||||
// Reset prefs, which can cause the Rust backend to start syncing. Wait for
|
||||
// it to finish.
|
||||
for (let [name] of prefs) {
|
||||
lazy.UrlbarPrefs.clear(name);
|
||||
}
|
||||
@@ -288,7 +270,6 @@ class _QuickSuggestTestUtils {
|
||||
|
||||
this.#log("#uninitQuickSuggest", "Stopping remote settings server");
|
||||
await this.#remoteSettingsServer.stop();
|
||||
this.#restoreRemoteSettings();
|
||||
|
||||
if (clearDataCollectionEnabled) {
|
||||
lazy.UrlbarPrefs.clear("quicksuggest.dataCollection.enabled");
|
||||
@@ -356,11 +337,6 @@ class _QuickSuggestTestUtils {
|
||||
await lazy.QuickSuggest.rustBackend._test_ingest();
|
||||
this.#log("forceSync", "Done syncing Rust backend");
|
||||
}
|
||||
if (lazy.QuickSuggest.jsBackend.isEnabled) {
|
||||
this.#log("forceSync", "Syncing JS backend");
|
||||
await lazy.QuickSuggest.jsBackend._test_syncAll();
|
||||
this.#log("forceSync", "Done syncing JS backend");
|
||||
}
|
||||
this.#log("forceSync", "Done");
|
||||
}
|
||||
|
||||
@@ -378,7 +354,7 @@ class _QuickSuggestTestUtils {
|
||||
* @see {@link setConfig}
|
||||
*/
|
||||
async withConfig({ config, callback }) {
|
||||
let original = lazy.QuickSuggest.jsBackend.config;
|
||||
let original = lazy.QuickSuggest.config;
|
||||
await this.setConfig(config);
|
||||
await callback();
|
||||
await this.setConfig(original);
|
||||
@@ -413,15 +389,14 @@ class _QuickSuggestTestUtils {
|
||||
|
||||
/**
|
||||
* Returns an expected AMP (sponsored) result that can be passed to
|
||||
* `check_results()` in xpcshell tests regardless of whether the Rust backend
|
||||
* is enabled.
|
||||
* `check_results()` in xpcshell tests.
|
||||
*
|
||||
* @returns {object}
|
||||
* An object that can be passed to `check_results()`.
|
||||
*/
|
||||
ampResult({
|
||||
source,
|
||||
provider,
|
||||
source = "rust",
|
||||
provider = "Amp",
|
||||
keyword = "amp",
|
||||
fullKeyword = keyword,
|
||||
title = "Amp Suggestion",
|
||||
@@ -452,6 +427,8 @@ class _QuickSuggestTestUtils {
|
||||
url,
|
||||
originalUrl,
|
||||
requestId,
|
||||
source,
|
||||
provider,
|
||||
displayUrl: url.replace(/^https:\/\//, ""),
|
||||
isSponsored: true,
|
||||
qsSuggestion: fullKeyword ?? keyword,
|
||||
@@ -473,17 +450,9 @@ class _QuickSuggestTestUtils {
|
||||
result.payload.descriptionL10n = descriptionL10n;
|
||||
}
|
||||
|
||||
if (lazy.UrlbarPrefs.get("quickSuggestRustEnabled")) {
|
||||
result.payload.source = source || "rust";
|
||||
result.payload.provider = provider || "Amp";
|
||||
if (result.payload.source == "rust") {
|
||||
result.payload.iconBlob = iconBlob;
|
||||
} else {
|
||||
result.payload.icon = icon;
|
||||
}
|
||||
if (result.payload.source == "rust") {
|
||||
result.payload.iconBlob = iconBlob;
|
||||
} else {
|
||||
result.payload.source = source || "remote-settings";
|
||||
result.payload.provider = provider || "AdmWikipedia";
|
||||
result.payload.icon = icon;
|
||||
}
|
||||
|
||||
@@ -519,31 +488,24 @@ class _QuickSuggestTestUtils {
|
||||
|
||||
/**
|
||||
* Returns an expected Wikipedia (non-sponsored) result that can be passed to
|
||||
* `check_results()` in xpcshell tests regardless of whether the Rust backend
|
||||
* is enabled.
|
||||
* `check_results()` in xpcshell tests.
|
||||
*
|
||||
* @returns {object}
|
||||
* An object that can be passed to `check_results()`.
|
||||
*/
|
||||
wikipediaResult({
|
||||
source,
|
||||
provider,
|
||||
source = "rust",
|
||||
provider = "Wikipedia",
|
||||
keyword = "wikipedia",
|
||||
fullKeyword = keyword,
|
||||
title = "Wikipedia Suggestion",
|
||||
url = "https://example.com/wikipedia",
|
||||
originalUrl = url,
|
||||
icon = null,
|
||||
iconBlob = null,
|
||||
impressionUrl = "https://example.com/wikipedia-impression",
|
||||
clickUrl = "https://example.com/wikipedia-click",
|
||||
blockId = 2,
|
||||
advertiser = "Wikipedia",
|
||||
iabCategory = "5 - Education",
|
||||
suggestedIndex = -1,
|
||||
isSuggestedIndexRelativeToGroup = true,
|
||||
} = {}) {
|
||||
let result = {
|
||||
return {
|
||||
suggestedIndex,
|
||||
isSuggestedIndexRelativeToGroup,
|
||||
type: lazy.UrlbarUtils.RESULT_TYPE.URL,
|
||||
@@ -553,6 +515,9 @@ class _QuickSuggestTestUtils {
|
||||
title,
|
||||
url,
|
||||
originalUrl,
|
||||
iconBlob,
|
||||
source,
|
||||
provider,
|
||||
displayUrl: url.replace(/^https:\/\//, ""),
|
||||
isSponsored: false,
|
||||
qsSuggestion: fullKeyword ?? keyword,
|
||||
@@ -566,23 +531,6 @@ class _QuickSuggestTestUtils {
|
||||
telemetryType: "adm_nonsponsored",
|
||||
},
|
||||
};
|
||||
|
||||
if (lazy.UrlbarPrefs.get("quickSuggestRustEnabled")) {
|
||||
result.payload.source = source || "rust";
|
||||
result.payload.provider = provider || "Wikipedia";
|
||||
result.payload.iconBlob = iconBlob;
|
||||
} else {
|
||||
result.payload.source = source || "remote-settings";
|
||||
result.payload.provider = provider || "AdmWikipedia";
|
||||
result.payload.icon = icon;
|
||||
result.payload.sponsoredImpressionUrl = impressionUrl;
|
||||
result.payload.sponsoredClickUrl = clickUrl;
|
||||
result.payload.sponsoredBlockId = blockId;
|
||||
result.payload.sponsoredAdvertiser = advertiser;
|
||||
result.payload.sponsoredIabCategory = iabCategory;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -847,15 +795,14 @@ class _QuickSuggestTestUtils {
|
||||
|
||||
/**
|
||||
* Returns an expected AMO (addons) result that can be passed to
|
||||
* `check_results()` in xpcshell tests regardless of whether the Rust backend
|
||||
* is enabled.
|
||||
* `check_results()` in xpcshell tests.
|
||||
*
|
||||
* @returns {object}
|
||||
* An object that can be passed to `check_results()`.
|
||||
*/
|
||||
amoResult({
|
||||
source,
|
||||
provider,
|
||||
source = "rust",
|
||||
provider = "Amo",
|
||||
title = "Amo Suggestion",
|
||||
description = "Amo description",
|
||||
url = "https://example.com/amo",
|
||||
@@ -870,7 +817,7 @@ class _QuickSuggestTestUtils {
|
||||
url = url.href;
|
||||
}
|
||||
|
||||
let result = {
|
||||
return {
|
||||
isBestMatch: true,
|
||||
suggestedIndex: 1,
|
||||
type: lazy.UrlbarUtils.RESULT_TYPE.URL,
|
||||
@@ -891,21 +838,11 @@ class _QuickSuggestTestUtils {
|
||||
telemetryType: "amo",
|
||||
},
|
||||
};
|
||||
|
||||
if (lazy.UrlbarPrefs.get("quickSuggestRustEnabled")) {
|
||||
result.payload.source = source || "rust";
|
||||
result.payload.provider = provider || "Amo";
|
||||
} else {
|
||||
result.payload.source = source || "remote-settings";
|
||||
result.payload.provider = provider || "AddonSuggestions";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an expected MDN result that can be passed to `check_results()` in
|
||||
* xpcshell tests regardless of whether the Rust backend is enabled.
|
||||
* xpcshell tests.
|
||||
*
|
||||
* @returns {object}
|
||||
* An object that can be passed to `check_results()`.
|
||||
@@ -920,7 +857,7 @@ class _QuickSuggestTestUtils {
|
||||
);
|
||||
finalUrl.searchParams.set("utm_content", "treatment");
|
||||
|
||||
let result = {
|
||||
return {
|
||||
isBestMatch: true,
|
||||
suggestedIndex: 1,
|
||||
type: lazy.UrlbarUtils.RESULT_TYPE.URL,
|
||||
@@ -936,33 +873,24 @@ class _QuickSuggestTestUtils {
|
||||
icon: "chrome://global/skin/icons/mdn.svg",
|
||||
shouldShowUrl: true,
|
||||
bottomTextL10n: { id: "firefox-suggest-mdn-bottom-text" },
|
||||
source: "rust",
|
||||
provider: "Mdn",
|
||||
},
|
||||
};
|
||||
|
||||
if (lazy.UrlbarPrefs.get("quickSuggestRustEnabled")) {
|
||||
result.payload.source = "rust";
|
||||
result.payload.provider = "Mdn";
|
||||
} else {
|
||||
result.payload.source = "remote-settings";
|
||||
result.payload.provider = "MDNSuggestions";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an expected weather result that can be passed to `check_results()`
|
||||
* in xpcshell tests regardless of whether the Rust backend is enabled.
|
||||
* in xpcshell tests.
|
||||
*
|
||||
* @returns {object}
|
||||
* An object that can be passed to `check_results()`.
|
||||
*/
|
||||
weatherResult({
|
||||
source,
|
||||
provider,
|
||||
source = "rust",
|
||||
provider = "Weather",
|
||||
city = null,
|
||||
region = null,
|
||||
telemetryType = undefined,
|
||||
temperatureUnit = undefined,
|
||||
} = {}) {
|
||||
if (!temperatureUnit) {
|
||||
@@ -970,19 +898,19 @@ class _QuickSuggestTestUtils {
|
||||
Services.locale.regionalPrefsLocales[0] == "en-US" ? "f" : "c";
|
||||
}
|
||||
|
||||
let result = {
|
||||
return {
|
||||
type: lazy.UrlbarUtils.RESULT_TYPE.DYNAMIC,
|
||||
source: lazy.UrlbarUtils.RESULT_SOURCE.SEARCH,
|
||||
heuristic: false,
|
||||
suggestedIndex: 1,
|
||||
payload: {
|
||||
temperatureUnit,
|
||||
source,
|
||||
provider,
|
||||
url: lazy.MerinoTestUtils.WEATHER_SUGGESTION.url,
|
||||
input: lazy.MerinoTestUtils.WEATHER_SUGGESTION.url,
|
||||
iconId: "6",
|
||||
requestId: lazy.MerinoTestUtils.server.response.body.request_id,
|
||||
source: "merino",
|
||||
provider: "accuweather",
|
||||
dynamicType: "weather",
|
||||
city: city || lazy.MerinoTestUtils.WEATHER_SUGGESTION.city_name,
|
||||
region: region || lazy.MerinoTestUtils.WEATHER_SUGGESTION.region_code,
|
||||
@@ -998,20 +926,9 @@ class _QuickSuggestTestUtils {
|
||||
low: lazy.MerinoTestUtils.WEATHER_SUGGESTION.forecast.low[
|
||||
temperatureUnit
|
||||
],
|
||||
telemetryType: "weather",
|
||||
},
|
||||
};
|
||||
|
||||
if (lazy.UrlbarPrefs.get("quickSuggestRustEnabled")) {
|
||||
result.payload.source = source || "rust";
|
||||
result.payload.provider = provider || "Weather";
|
||||
if (telemetryType !== null) {
|
||||
result.payload.telemetryType = telemetryType || "weather";
|
||||
}
|
||||
} else {
|
||||
throw new Error("Weather result not supported when Rust disabled");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1437,7 +1354,6 @@ class _QuickSuggestTestUtils {
|
||||
}
|
||||
|
||||
#remoteSettingsServer;
|
||||
#restoreRemoteSettings;
|
||||
}
|
||||
|
||||
export var QuickSuggestTestUtils = new _QuickSuggestTestUtils();
|
||||
|
||||
@@ -93,10 +93,7 @@ async function doOneBasicBlockTest({ result, block }) {
|
||||
// The suggested index is -1 even for sponsored since search suggestions are
|
||||
// disabled.
|
||||
let suggested_index = -1;
|
||||
let expectedBlockId =
|
||||
UrlbarPrefs.get("quicksuggest.rustEnabled") && !isSponsored
|
||||
? null
|
||||
: result.id;
|
||||
let expectedBlockId = !isSponsored ? null : result.id;
|
||||
|
||||
let pingsSubmitted = 0;
|
||||
GleanPings.quickSuggest.testBeforeNextSubmit(() => {
|
||||
|
||||
@@ -48,10 +48,7 @@ add_task(async function basic() {
|
||||
UrlbarProviderQuickSuggest.name,
|
||||
"The result should be from the expected provider"
|
||||
);
|
||||
Assert.equal(
|
||||
result.payload.provider,
|
||||
UrlbarPrefs.get("quickSuggestRustEnabled") ? "Mdn" : "MDNSuggestions"
|
||||
);
|
||||
Assert.equal(result.payload.provider, "Mdn");
|
||||
|
||||
const onLoad = BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
|
||||
@@ -27,7 +27,6 @@ add_setup(async function () {
|
||||
await QuickSuggestTestUtils.ensureQuickSuggestInit({
|
||||
remoteSettingsRecords: REMOTE_SETTINGS_RECORDS,
|
||||
prefs: [
|
||||
["quicksuggest.rustEnabled", true],
|
||||
["suggest.quicksuggest.sponsored", true],
|
||||
["suggest.yelp", true],
|
||||
["yelp.featureGate", true],
|
||||
|
||||
+1
-10
@@ -39,10 +39,7 @@ add_task(async function nonsponsored() {
|
||||
let match_type = "firefox-suggest";
|
||||
let advertiser = REMOTE_SETTINGS_RESULT.advertiser.toLowerCase();
|
||||
let reporting_url = undefined;
|
||||
let source = UrlbarPrefs.get("quicksuggest.rustEnabled")
|
||||
? "rust"
|
||||
: "remote-settings";
|
||||
let block_id = source == "rust" ? undefined : REMOTE_SETTINGS_RESULT.id;
|
||||
let source = "rust";
|
||||
|
||||
// Make sure `improve_suggest_experience_checked` is recorded correctly
|
||||
// depending on the value of the related pref.
|
||||
@@ -66,7 +63,6 @@ add_task(async function nonsponsored() {
|
||||
source,
|
||||
match_type,
|
||||
position,
|
||||
block_id,
|
||||
advertiser,
|
||||
reporting_url,
|
||||
suggested_index: -1,
|
||||
@@ -85,7 +81,6 @@ add_task(async function nonsponsored() {
|
||||
source,
|
||||
match_type,
|
||||
position,
|
||||
block_id,
|
||||
advertiser,
|
||||
reporting_url,
|
||||
suggested_index: -1,
|
||||
@@ -100,7 +95,6 @@ add_task(async function nonsponsored() {
|
||||
source,
|
||||
match_type,
|
||||
position,
|
||||
block_id,
|
||||
advertiser,
|
||||
reporting_url,
|
||||
suggested_index: -1,
|
||||
@@ -121,7 +115,6 @@ add_task(async function nonsponsored() {
|
||||
source,
|
||||
match_type,
|
||||
position,
|
||||
block_id,
|
||||
advertiser,
|
||||
reporting_url,
|
||||
suggested_index: -1,
|
||||
@@ -136,7 +129,6 @@ add_task(async function nonsponsored() {
|
||||
source,
|
||||
match_type,
|
||||
position,
|
||||
block_id,
|
||||
advertiser,
|
||||
suggested_index: -1,
|
||||
suggested_index_relative_to_group: true,
|
||||
@@ -156,7 +148,6 @@ add_task(async function nonsponsored() {
|
||||
source,
|
||||
match_type,
|
||||
position,
|
||||
block_id,
|
||||
advertiser,
|
||||
reporting_url,
|
||||
suggested_index: -1,
|
||||
|
||||
@@ -40,9 +40,7 @@ add_setup(async function () {
|
||||
// sponsored
|
||||
add_task(async function sponsored() {
|
||||
let match_type = "firefox-suggest";
|
||||
let source = UrlbarPrefs.get("quicksuggest.rustEnabled")
|
||||
? "rust"
|
||||
: "remote-settings";
|
||||
let source = "rust";
|
||||
|
||||
// Make sure `improve_suggest_experience_checked` is recorded correctly
|
||||
// depending on the value of the related pref.
|
||||
@@ -171,9 +169,7 @@ add_task(async function sponsored() {
|
||||
// higher-placement sponsored, a.k.a sponsored priority, sponsored best match
|
||||
add_task(async function sponsoredBestMatch() {
|
||||
let match_type = "best-match";
|
||||
let source = UrlbarPrefs.get("quicksuggest.rustEnabled")
|
||||
? "rust"
|
||||
: "remote-settings";
|
||||
let source = "rust";
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.urlbar.quicksuggest.sponsoredPriority", true]],
|
||||
|
||||
@@ -502,8 +502,6 @@ async function doOneShowLessFrequentlyTest({
|
||||
* The order doesn't matter.
|
||||
*/
|
||||
async function doRustProvidersTests({ searchString, tests }) {
|
||||
UrlbarPrefs.set("quicksuggest.rustEnabled", true);
|
||||
|
||||
for (let { prefs, expectedUrls } of tests) {
|
||||
info(
|
||||
"Starting Rust providers test: " + JSON.stringify({ prefs, expectedUrls })
|
||||
@@ -531,8 +529,4 @@ async function doRustProvidersTests({ searchString, tests }) {
|
||||
}
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
}
|
||||
|
||||
info("Clearing rustEnabled pref and forcing sync");
|
||||
UrlbarPrefs.clear("quicksuggest.rustEnabled");
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
}
|
||||
|
||||
@@ -672,7 +672,7 @@ add_task(async function maxResults() {
|
||||
await QuickSuggestTestUtils.forceSync();
|
||||
|
||||
let searchString = "maxresults";
|
||||
let suggestions = await QuickSuggest.backend.query(searchString);
|
||||
let suggestions = await QuickSuggest.rustBackend.query(searchString);
|
||||
Assert.equal(
|
||||
suggestions.length,
|
||||
gMaxResultsSuggestionsCount,
|
||||
@@ -784,7 +784,7 @@ async function doManySuggestResultsTest({
|
||||
|
||||
// Make sure many Suggest suggestions match the search string.
|
||||
let searchString = "maxresults";
|
||||
let suggestions = await QuickSuggest.backend.query(searchString);
|
||||
let suggestions = await QuickSuggest.rustBackend.query(searchString);
|
||||
Assert.equal(
|
||||
suggestions.length,
|
||||
gMaxResultsSuggestionsCount,
|
||||
|
||||
@@ -75,10 +75,9 @@ add_task(async function basic() {
|
||||
providers: [UrlbarProviderQuickSuggest.name],
|
||||
isPrivate: false,
|
||||
}),
|
||||
matches:
|
||||
UrlbarPrefs.get("quickSuggestRustEnabled") && !fullKeyword.includes(" ")
|
||||
? [QuickSuggestTestUtils.mdnResult(suggestion)]
|
||||
: [],
|
||||
matches: !fullKeyword.includes(" ")
|
||||
? [QuickSuggestTestUtils.mdnResult(suggestion)]
|
||||
: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -479,17 +479,10 @@ function makeExpectedResult({
|
||||
searchString,
|
||||
fullKeyword = searchString,
|
||||
suggestion = REMOTE_SETTINGS_DATA[0].attachment[0],
|
||||
source = "remote-settings",
|
||||
source = "rust",
|
||||
isTopPick = false,
|
||||
suggestedIndex,
|
||||
} = {}) {
|
||||
if (
|
||||
source == "remote-settings" &&
|
||||
UrlbarPrefs.get("quicksuggest.rustEnabled")
|
||||
) {
|
||||
source = "rust";
|
||||
}
|
||||
|
||||
let provider;
|
||||
let keywordSubstringNotTyped = fullKeyword.substring(searchString.length);
|
||||
let description = suggestion.description;
|
||||
|
||||
@@ -277,6 +277,5 @@ add_task(async function manyExposureResults_hidden_manyMatched() {
|
||||
});
|
||||
|
||||
function suggestResultType(typeWithoutSource) {
|
||||
let source = UrlbarPrefs.get("quickSuggestRustEnabled") ? "rust" : "rs";
|
||||
return `${source}_${typeWithoutSource}`;
|
||||
return `rust_${typeWithoutSource}`;
|
||||
}
|
||||
|
||||
@@ -467,12 +467,6 @@ urlbar:
|
||||
quickSuggestRemoteSettingsDataType:
|
||||
type: string
|
||||
description: The `type` of the suggestions data in remote settings. If not specified, "data" is used.
|
||||
quickSuggestRustEnabled:
|
||||
type: boolean
|
||||
fallbackPref: browser.urlbar.quicksuggest.rustEnabled
|
||||
description: >-
|
||||
Whether Firefox Suggest will use the new Rust backend instead of the
|
||||
original JS backend.
|
||||
quickSuggestScenario:
|
||||
# IMPORTANT: This should not have a fallbackPref. See UrlbarPrefs.sys.mjs.
|
||||
type: string
|
||||
|
||||
Reference in New Issue
Block a user