diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /toolkit/components/search | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-40a355a42d4a9444dc753c04c6608dade2f06a23.tar.xz firefox-40a355a42d4a9444dc753c04c6608dade2f06a23.zip |
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/search')
48 files changed, 2681 insertions, 497 deletions
diff --git a/toolkit/components/search/AppProvidedSearchEngine.sys.mjs b/toolkit/components/search/AppProvidedSearchEngine.sys.mjs index a8db801ac4..7401ba115c 100644 --- a/toolkit/components/search/AppProvidedSearchEngine.sys.mjs +++ b/toolkit/components/search/AppProvidedSearchEngine.sys.mjs @@ -12,10 +12,95 @@ import { const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs", }); /** + * Handles loading application provided search engine icons from remote settings. + */ +class IconHandler { + #iconList = null; + #iconCollection = null; + + /** + * Returns the icon for the record that matches the engine identifier + * and the preferred width. + * + * @param {string} engineIdentifier + * The identifier of the engine to match against. + * @param {number} preferredWidth + * The preferred with of the icon. + * @returns {string} + * An object URL that can be used to reference the contents of the specified + * source object. + */ + async getIcon(engineIdentifier, preferredWidth) { + if (!this.#iconList) { + await this.#getIconList(); + } + + let iconRecords = this.#iconList.filter(r => { + return r.engineIdentifiers.some(i => { + if (i.endsWith("*")) { + return engineIdentifier.startsWith(i.slice(0, -1)); + } + return engineIdentifier == i; + }); + }); + + if (!iconRecords.length) { + console.warn("No icon found for", engineIdentifier); + return null; + } + + // Default to the first record, in the event we don't have any records + // that match the width. + let iconRecord = iconRecords[0]; + for (let record of iconRecords) { + // TODO: Bug 1655070. We should be using the closest size, but for now use + // an exact match. + if (record.imageSize == preferredWidth) { + iconRecord = record; + break; + } + } + + let iconURL; + try { + iconURL = await this.#iconCollection.attachments.get(iconRecord); + } catch (ex) { + console.error(ex); + return null; + } + if (!iconURL) { + console.warn("Unable to find the icon for", engineIdentifier); + return null; + } + return URL.createObjectURL( + new Blob([iconURL.buffer]), + iconRecord.attachment.mimetype + ); + } + + /** + * Obtains the icon list from the remote settings collection. + */ + async #getIconList() { + this.#iconCollection = lazy.RemoteSettings("search-config-icons"); + try { + this.#iconList = await this.#iconCollection.get(); + } catch (ex) { + console.error(ex); + this.#iconList = []; + } + if (!this.#iconList.length) { + console.error("Failed to obtain search engine icon list records"); + } + } +} + +/** * AppProvidedSearchEngine represents a search engine defined by the * search configuration. */ @@ -25,6 +110,20 @@ export class AppProvidedSearchEngine extends SearchEngine { ["suggestions", lazy.SearchUtils.URL_TYPE.SUGGEST_JSON], ["trending", lazy.SearchUtils.URL_TYPE.TRENDING_JSON], ]); + static iconHandler = new IconHandler(); + + /** + * @typedef {?Promise<string>} + * A promise for the blob URL of the icon. We save the promise to avoid + * reentrancy issues. + */ + #blobURLPromise = null; + + /** + * @typedef {?string} + * The identifier from the configuration. + */ + #configurationId = null; /** * @param {object} options @@ -51,26 +150,35 @@ export class AppProvidedSearchEngine extends SearchEngine { this._extensionID = extensionId; this._locale = config.webExtension.locale; + this.#configurationId = config.identifier; this.#init(config); this._loadSettings(settings); } /** + * Used to clean up the engine when it is removed. This will revoke the blob + * URL for the icon. + */ + async cleanup() { + if (this.#blobURLPromise) { + URL.revokeObjectURL(await this.#blobURLPromise); + this.#blobURLPromise = null; + } + } + + /** * Update this engine based on new config, used during * config upgrades. * @param {object} options * The options object. * - * @param {object} options.locale - * The locale that is being used for the engine. * @param {object} options.configuration * The search engine configuration for application provided engines. */ - update({ locale, configuration } = {}) { + update({ configuration } = {}) { this._urls = []; - this._iconMapObj = null; this.#init(configuration); lazy.SearchUtils.notifyAction(this, lazy.SearchUtils.MODIFIED_TYPE.CHANGED); } @@ -89,24 +197,10 @@ export class AppProvidedSearchEngine extends SearchEngine { * Returns true if the engine was updated, false otherwise. */ async updateIfNoNameChange({ configuration, locale }) { - let newName; - if (locale != "default") { - newName = configuration.webExtension.searchProvider[locale].name; - } else if ( - locale == "default" && - configuration.webExtension.default_locale - ) { - newName = - configuration.webExtension.searchProvider[ - configuration.webExtension.default_locale - ].name; - } else { - newName = configuration.webExtension.name; - } - - if (this.name != newName.trim()) { + if (this.name != configuration.name.trim()) { return false; } + this.update({ locale, configuration }); return true; } @@ -145,6 +239,25 @@ export class AppProvidedSearchEngine extends SearchEngine { } /** + * Returns the icon URL for the search engine closest to the preferred width. + * + * @param {number} preferredWidth + * The preferred width of the image. + * @returns {Promise<string>} + * A promise that resolves to the URL of the icon. + */ + async getIconURL(preferredWidth) { + if (this.#blobURLPromise) { + return this.#blobURLPromise; + } + this.#blobURLPromise = AppProvidedSearchEngine.iconHandler.getIcon( + this.#configurationId, + preferredWidth + ); + return this.#blobURLPromise; + } + + /** * Creates a JavaScript object that represents this engine. * * @returns {object} @@ -175,38 +288,6 @@ export class AppProvidedSearchEngine extends SearchEngine { this._telemetryId += `-${engineConfig.telemetrySuffix}`; } - // Set the main icon URL for the engine. - // let iconURL = searchProvider.favicon_url; - - // if (!iconURL) { - // iconURL = - // manifest.icons && - // extensionBaseURI.resolve( - // lazy.ExtensionParent.IconDetails.getPreferredIcon(manifest.icons).icon - // ); - // } - - // // Record other icons that the WebExtension has. - // if (manifest.icons) { - // let iconList = Object.entries(manifest.icons).map(icon => { - // return { - // width: icon[0], - // height: icon[0], - // url: extensionBaseURI.resolve(icon[1]), - // }; - // }); - // for (let icon of iconList) { - // this._addIconToMap(icon.size, icon.size, icon.url); - // } - // } - - // this._initWithDetails(config); - - // this._sendAttributionRequest = config.sendAttributionRequest ?? false; // TODO check if we need to this? - // if (details.iconURL) { - // this._setIcon(details.iconURL, true); - // } - this._name = engineConfig.name.trim(); this._definedAliases = engineConfig.aliases?.map(alias => `@${alias}`) ?? []; diff --git a/toolkit/components/search/SearchEngine.sys.mjs b/toolkit/components/search/SearchEngine.sys.mjs index a043c7d4ca..832ffbe2d0 100644 --- a/toolkit/components/search/SearchEngine.sys.mjs +++ b/toolkit/components/search/SearchEngine.sys.mjs @@ -1685,9 +1685,9 @@ export class SearchEngine { * @param {number} preferredWidth * Width of the requested icon. If not specified, it is assumed that * 16x16 is desired. - * @returns {string|undefined} + * @returns {Promise<string|undefined>} */ - getIconURL(preferredWidth) { + async getIconURL(preferredWidth) { // XPCOM interfaces pass optional number parameters as 0 and can't be // handled in the same way. if (!preferredWidth) { diff --git a/toolkit/components/search/SearchService.sys.mjs b/toolkit/components/search/SearchService.sys.mjs index 4e1a99671f..b9de8e0bb3 100644 --- a/toolkit/components/search/SearchService.sys.mjs +++ b/toolkit/components/search/SearchService.sys.mjs @@ -69,6 +69,18 @@ export const NON_SPLIT_ENGINE_IDS = [ "wolnelektury-pl", "yahoo-jp", "yahoo-jp-auctions", + // below are test engines + "engine-pref", + "engine-rel-searchform-purpose", + "engine-chromeicon", + "engine-resourceicon", + "engine-resourceicon-gd", + "engine-reordered", + "engine-same-name", + "engine-same-name-gd", + "engine-purpose", + "engine-fr", + "fixup_search", ]; const TOPIC_LOCALES_CHANGE = "intl:app-locales-changed"; @@ -627,7 +639,6 @@ export class SearchService { * An Extension object containing data about the extension. */ async addEnginesFromExtension(extension) { - lazy.logConsole.debug("addEnginesFromExtension: " + extension.id); // Treat add-on upgrade and downgrades the same - either way, the search // engine gets updated, not added. Generally, we don't expect a downgrade, // but just in case... @@ -640,7 +651,7 @@ export class SearchService { // In either case, there will not be an existing engine. let existing = await this.#upgradeExtensionEngine(extension); if (existing?.length) { - return existing; + return; } } @@ -653,28 +664,32 @@ export class SearchService { let { engines } = await this._fetchEngineSelectorEngines(); let inConfig = engines.filter(el => el.webExtension.id == extension.id); if (inConfig.length) { - return this.#installExtensionEngine( + await this.#installExtensionEngine( extension, inConfig.map(el => el.webExtension.locale) ); + return; } } lazy.logConsole.debug( - "addEnginesFromExtension: Ignoring builtIn engine." + "addEnginesFromExtension: Ignoring app engine during init or reload:", + extension.id ); - return []; + return; } + lazy.logConsole.debug("addEnginesFromExtension:", extension.id); // If we havent started SearchService yet, store this extension // to install in SearchService.init(). if (!this.isInitialized) { this.#startupExtensions.add(extension); - return []; + return; } - return this.#installExtensionEngine(extension, [ - lazy.SearchUtils.DEFAULT_TAG, - ]); + await this.#createAndAddAddonEngine({ + extension, + locale: lazy.SearchUtils.DEFAULT_TAG, + }); } async addOpenSearchEngine(engineURL, iconURL) { @@ -1082,7 +1097,7 @@ export class SearchService { /** * A Set of installed search extensions reported by AddonManager * startup before SearchSevice has started. Will be installed - * during init(). + * during init(). Does not contain application provided engines. * * @type {Set<object>} */ @@ -1800,21 +1815,21 @@ export class SearchService { } lazy.logConsole.debug( - "#loadEngines: loading", + "#loadStartupEngines: loading", this.#startupExtensions.size, "engines reported by AddonManager startup" ); for (let extension of this.#startupExtensions) { try { - await this.#installExtensionEngine( + await this.#createAndAddAddonEngine({ extension, - [lazy.SearchUtils.DEFAULT_TAG], + locale: lazy.SearchUtils.DEFAULT_TAG, settings, - true - ); + initEngine: true, + }); } catch (ex) { lazy.logConsole.error( - `#installExtensionEngine failed for ${extension.id}`, + `#createAndAddAddonEngine failed for ${extension.id}`, ex ); } @@ -2196,60 +2211,7 @@ export class SearchService { // Finally, remove any engines that need removing. We do this after sorting // out the new default, as otherwise this could cause multiple notifications // and the wrong engine to be selected as default. - - for (let engine of this._engines.values()) { - if (!engine.pendingRemoval) { - continue; - } - - // If we have other engines that use the same extension ID, then - // we do not want to remove the add-on - only remove the engine itself. - let inUseEngines = [...this._engines.values()].filter( - e => e._extensionID == engine._extensionID - ); - - if (inUseEngines.length <= 1) { - if (inUseEngines.length == 1 && inUseEngines[0] == engine) { - // No other engines are using this extension ID. - - // The internal remove is done first to avoid a call to removeEngine - // which could adjust the sort order when we don't want it to. - this.#internalRemoveEngine(engine); - - // Only uninstall application provided engines. We don't want to - // remove third-party add-ons. Their search engine names might conflict, - // but we still allow the add-on to be installed. - if (engine.isAppProvided) { - let addon = await lazy.AddonManager.getAddonByID( - engine._extensionID - ); - if (addon) { - // AddonManager won't call removeEngine if an engine with the - // WebExtension id doesn't exist in the search service. - await addon.uninstall(); - } - } - } - // For the case where `inUseEngines[0] != engine`: - // This is a situation where there was an engine added earlier in this - // function with the same name. - // For example, eBay has the same name for both US and GB, but has - // a different domain and uses a different locale of the same - // WebExtension. - // The result of this is the earlier addition has already replaced - // the engine in `this._engines` (which is indexed by name), so all that - // needs to be done here is to pretend the old engine was removed - // which is notified below. - } else { - // More than one engine is using this extension ID, so we don't want to - // remove the add-on. - this.#internalRemoveEngine(engine); - } - lazy.SearchUtils.notifyAction( - engine, - lazy.SearchUtils.MODIFIED_TYPE.REMOVED - ); - } + await this.#maybeRemoveEnginesAfterReload(this._engines); // Save app default engine to the user's settings metaData incase it has // been updated @@ -2343,6 +2305,78 @@ export class SearchService { return true; } + /** + * Remove any engines that have been flagged for removal during reloadEngines. + * + * @param {SearchEngine[]} engines + * The list of engines to check. + */ + async #maybeRemoveEnginesAfterReload(engines) { + for (let engine of engines.values()) { + if (!engine.pendingRemoval) { + continue; + } + + if (lazy.SearchUtils.newSearchConfigEnabled) { + // Use the internal remove - _reloadEngines already deals with default + // engines etc, and we want to avoid adjusting the sort order unnecessarily. + this.#internalRemoveEngine(engine); + + if (engine instanceof lazy.AppProvidedSearchEngine) { + await engine.cleanup(); + } + } else { + // If we have other engines that use the same extension ID, then + // we do not want to remove the add-on - only remove the engine itself. + let inUseEngines = [...this._engines.values()].filter( + e => e._extensionID == engine._extensionID + ); + + if (inUseEngines.length <= 1) { + if (inUseEngines.length == 1 && inUseEngines[0] == engine) { + // No other engines are using this extension ID. + + // The internal remove is done first to avoid a call to removeEngine + // which could adjust the sort order when we don't want it to. + this.#internalRemoveEngine(engine); + + // Only uninstall application provided engines. We don't want to + // remove third-party add-ons. Their search engine names might conflict, + // but we still allow the add-on to be installed. + if (engine.isAppProvided) { + let addon = await lazy.AddonManager.getAddonByID( + engine._extensionID + ); + if (addon) { + // AddonManager won't call removeEngine if an engine with the + // WebExtension id doesn't exist in the search service. + await addon.uninstall(); + } + } + } + // For the case where `inUseEngines[0] != engine`: + // This is a situation where there was an engine added earlier in this + // function with the same name. + // For example, eBay has the same name for both US and GB, but has + // a different domain and uses a different locale of the same + // WebExtension. + // The result of this is the earlier addition has already replaced + // the engine in `this._engines` (which is indexed by name), so all that + // needs to be done here is to pretend the old engine was removed + // which is notified below. + } else { + // More than one engine is using this extension ID, so we don't want to + // remove the add-on. + this.#internalRemoveEngine(engine); + } + } + lazy.SearchUtils.notifyAction( + engine, + lazy.SearchUtils.MODIFIED_TYPE.REMOVED + ); + } + } + #addEngineToStore(engine, skipDuplicateCheck = false) { if (this.#engineMatchesIgnoreLists(engine)) { lazy.logConsole.debug("#addEngineToStore: Ignoring engine"); @@ -2885,7 +2919,7 @@ export class SearchService { * @param {initEngine} [options.initEngine] * Set to true if this engine is being loaded during initialization. */ - async _createAndAddEngine({ + async #createAndAddAddonEngine({ extension, locale = lazy.SearchUtils.DEFAULT_TAG, settings, @@ -2904,7 +2938,7 @@ export class SearchService { "Engine already loaded via settings, skipping due to APP_STARTUP:", extension.id ); - return engine; + return; } } @@ -2915,6 +2949,12 @@ export class SearchService { await this.init(); } + lazy.logConsole.debug( + "#createAndAddAddonEngine: installing:", + extension.id, + locale + ); + let shouldSetAsDefault = false; let changeReason = Ci.nsISearchService.CHANGE_REASON_UNKNOWN; @@ -2988,7 +3028,6 @@ export class SearchService { if (shouldSetAsDefault) { this.#setEngineDefault(false, newEngine, changeReason); } - return newEngine; } /** @@ -3046,26 +3085,14 @@ export class SearchService { ) { lazy.logConsole.debug("installExtensionEngine:", extension.id); - let installLocale = async locale => { - return this._createAndAddEngine({ + for (let locale of locales) { + await this.#createAndAddAddonEngine({ extension, locale, settings, initEngine, }); - }; - - let engines = []; - for (let locale of locales) { - lazy.logConsole.debug( - "addEnginesFromExtension: installing:", - extension.id, - ":", - locale - ); - engines.push(await installLocale(locale)); } - return engines; } #internalRemoveEngine(engine) { @@ -3953,11 +3980,7 @@ XPCOMUtils.defineLazyServiceGetter( * Handles getting and checking extensions against the allow list. */ class SearchDefaultOverrideAllowlistHandler { - /** - * @param {Function} listener - * A listener for configuration update changes. - */ - constructor(listener) { + constructor() { this._remoteConfig = lazy.RemoteSettings( lazy.SearchUtils.SETTINGS_ALLOWLIST_KEY ); diff --git a/toolkit/components/search/SearchSuggestionController.sys.mjs b/toolkit/components/search/SearchSuggestionController.sys.mjs index b528066d84..871931c280 100644 --- a/toolkit/components/search/SearchSuggestionController.sys.mjs +++ b/toolkit/components/search/SearchSuggestionController.sys.mjs @@ -525,14 +525,14 @@ export class SearchSuggestionController { this.#onRemoteLoaded(context, deferredResponse); }); - request.addEventListener("error", evt => { + request.addEventListener("error", () => { this.#reportTelemetryForEngine(context); deferredResponse.resolve("HTTP error"); }); // Reject for an abort assuming it's always from .stop() in which case we // shouldn't return local or remote results for existing searches. - request.addEventListener("abort", evt => { + request.addEventListener("abort", () => { context.timer.cancel(); this.#reportTelemetryForEngine(context); deferredResponse.reject("HTTP request aborted"); diff --git a/toolkit/components/search/SearchUtils.sys.mjs b/toolkit/components/search/SearchUtils.sys.mjs index c1a956e56e..27c8f8ad03 100644 --- a/toolkit/components/search/SearchUtils.sys.mjs +++ b/toolkit/components/search/SearchUtils.sys.mjs @@ -106,8 +106,8 @@ class LoadListener { } // nsIProgressEventSink - onProgress(request, progress, progressMax) {} - onStatus(request, status, statusArg) {} + onProgress() {} + onStatus() {} } export var SearchUtils = { diff --git a/toolkit/components/search/nsISearchService.idl b/toolkit/components/search/nsISearchService.idl index 769ba4eca7..4421c25974 100644 --- a/toolkit/components/search/nsISearchService.idl +++ b/toolkit/components/search/nsISearchService.idl @@ -99,7 +99,7 @@ interface nsISearchEngine : nsISupports * Width of the requested icon. If not specified, it is assumed that * 16x16 is desired. */ - jsval getIconURL([optional] in unsigned short preferredWidth); + Promise getIconURL([optional] in unsigned short preferredWidth); /** * Opens a speculative connection to the engine's search URI diff --git a/toolkit/components/search/schema/search-config-overrides-ui-schema.json b/toolkit/components/search/schema/search-config-overrides-ui-schema.json new file mode 100644 index 0000000000..d5c3fdc240 --- /dev/null +++ b/toolkit/components/search/schema/search-config-overrides-ui-schema.json @@ -0,0 +1,3 @@ +{ + "ui:order": ["telemetryId", "telemetrySuffix", "clickUrl", "params"] +} diff --git a/toolkit/components/search/schema/search-config-overrides-v2-schema.json b/toolkit/components/search/schema/search-config-overrides-v2-schema.json index cac02d8f2a..1fcb8e9cc0 100644 --- a/toolkit/components/search/schema/search-config-overrides-v2-schema.json +++ b/toolkit/components/search/schema/search-config-overrides-v2-schema.json @@ -56,6 +56,7 @@ } }, "type": "object", + "required": ["identifier", "partnerCode", "clickUrl", "urls"], "properties": { "identifier": { "title": "Identifier", diff --git a/toolkit/components/search/schema/search-config-overrides-v2-ui-schema.json b/toolkit/components/search/schema/search-config-overrides-v2-ui-schema.json new file mode 100644 index 0000000000..60fca49b8d --- /dev/null +++ b/toolkit/components/search/schema/search-config-overrides-v2-ui-schema.json @@ -0,0 +1,9 @@ +{ + "ui:order": [ + "identifier", + "clickUrl", + "telemetrySuffix", + "partnerCode", + "urls" + ] +} diff --git a/toolkit/components/search/schema/search-default-override-allowlist-ui-schema.json b/toolkit/components/search/schema/search-default-override-allowlist-ui-schema.json index 1b85489c13..65fb4f07e2 100644 --- a/toolkit/components/search/schema/search-default-override-allowlist-ui-schema.json +++ b/toolkit/components/search/schema/search-default-override-allowlist-ui-schema.json @@ -1,3 +1,3 @@ { - "ui:order": ["thirdPartyId", "overridesId", "urls"] + "ui:order": ["thirdPartyId", "overridesId", "engineName", "urls"] } diff --git a/toolkit/components/search/tests/SearchTestUtils.sys.mjs b/toolkit/components/search/tests/SearchTestUtils.sys.mjs index 8922070154..c3577d2887 100644 --- a/toolkit/components/search/tests/SearchTestUtils.sys.mjs +++ b/toolkit/components/search/tests/SearchTestUtils.sys.mjs @@ -429,6 +429,8 @@ export var SearchTestUtils = { * * @param {object} [options] * The options for the manifest. + * @param {object} [options.icons] + * The icons to use for the WebExtension. * @param {string} [options.id] * The id to use for the WebExtension. * @param {string} [options.name] @@ -478,6 +480,10 @@ export var SearchTestUtils = { }, }; + if (options.icons) { + manifest.icons = options.icons; + } + if (options.default_locale) { manifest.default_locale = options.default_locale; } @@ -541,11 +547,11 @@ export var SearchTestUtils = { QueryInterface: ChromeUtils.generateQI(["nsIUserIdleService"]), idleTime: 19999, - addIdleObserver(observer, time) { + addIdleObserver(observer) { this._observers.add(observer); }, - removeIdleObserver(observer, time) { + removeIdleObserver(observer) { this._observers.delete(observer); }, }, diff --git a/toolkit/components/search/tests/xpcshell/data/search-config-v2-no-order-hint.json b/toolkit/components/search/tests/xpcshell/data/search-config-v2-no-order-hint.json new file mode 100644 index 0000000000..6371a328f3 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/data/search-config-v2-no-order-hint.json @@ -0,0 +1,207 @@ +{ + "data": [ + { + "recordType": "engine", + "identifier": "engine", + "base": { + "name": "Test search engine", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "channel", + "searchAccessPoint": { + "addressbar": "fflb", + "contextmenu": "rcs" + } + } + ], + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["gd"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-rel-searchform-purpose", + "base": { + "name": "engine-rel-searchform-purpose", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "channel", + "searchAccessPoint": { + "addressbar": "fflb", + "contextmenu": "rcs", + "searchbar": "sb" + } + } + ], + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de", "fr"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-chromeicon", + "base": { + "name": "engine-chromeicon", + "urls": { + "search": { + "base": "https://www.google.com/search", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de", "fr"] } + }, + { + "environment": { "regions": ["ru"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-resourceicon", + "base": { + "name": "engine-resourceicon", + "urls": { + "search": { + "base": "https://www.google.com/search", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { + "excludedRegions": ["ru"], + "locales": ["en-US", "fr"] + } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-resourceicon-gd", + "base": { + "name": "engine-resourceicon-gd", + "urls": { + "search": { + "base": "https://www.google.com/search", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { + "locales": ["gd"] + } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-reordered", + "base": { + "name": "Test search engine (Reordered)", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "channel", + "searchAccessPoint": { + "addressbar": "fflb", + "contextmenu": "rcs" + } + } + ], + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de", "fr"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-pref", + "base": { + "name": "engine-pref", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "code", + "experimentConfig": "code" + }, + { + "name": "test", + "experimentConfig": "test" + } + ], + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de"] } + } + ] + }, + { + "recordType": "defaultEngines", + "globalDefault": "engine", + "specificDefaults": [ + { + "defaultPrivate": "engine-pref", + "environment": { "excludedLocales": ["de"] } + }, + { + "default": "engine-resourceicon-gd", + "environment": { "locales": ["gd"] } + } + ] + }, + { + "recordType": "engineOrders", + "orders": [ + { + "environment": { "allRegionsAndLocales": true }, + "order": ["engine-chromeicon", "engine-rel-searchform-purpose"] + } + ] + } + ] +} diff --git a/toolkit/components/search/tests/xpcshell/data/search-config-v2.json b/toolkit/components/search/tests/xpcshell/data/search-config-v2.json new file mode 100644 index 0000000000..569e16dfe4 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/data/search-config-v2.json @@ -0,0 +1,223 @@ +{ + "data": [ + { + "recordType": "engine", + "identifier": "engine", + "base": { + "name": "Test search engine", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "channel", + "searchAccessPoint": { + "addressbar": "fflb", + "contextmenu": "rcs" + } + } + ], + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["gd"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-pref", + "base": { + "name": "engine-pref", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "code", + "experimentConfig": "code" + }, + { + "name": "test", + "experimentConfig": "test" + } + ], + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-rel-searchform-purpose", + "base": { + "name": "engine-rel-searchform-purpose", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "channel", + "searchAccessPoint": { + "addressbar": "fflb", + "contextmenu": "rcs", + "searchbar": "sb" + } + } + ], + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de", "fr"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-chromeicon", + "base": { + "name": "engine-chromeicon", + "urls": { + "search": { + "base": "https://www.google.com/search", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de", "fr"] } + }, + { + "environment": { "regions": ["ru"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-resourceicon", + "base": { + "name": "engine-resourceicon", + "urls": { + "search": { + "base": "https://www.google.com/search", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { + "excludedRegions": ["ru"], + "locales": ["en-US", "fr"] + } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-resourceicon-gd", + "base": { + "name": "engine-resourceicon-gd", + "urls": { + "search": { + "base": "https://www.google.com/search", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "locales": ["gd"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "engine-reordered", + "base": { + "name": "Test search engine (Reordered)", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "channel", + "searchAccessPoint": { + "addressbar": "fflb", + "contextmenu": "rcs" + } + } + ], + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "excludedLocales": ["de", "fr"] } + } + ] + }, + { + "recordType": "defaultEngines", + "globalDefault": "engine", + "specificDefaults": [ + { + "defaultPrivate": "engine-pref", + "environment": { "excludedLocales": ["de"] } + }, + { + "default": "engine-resourceicon-gd", + "environment": { "locales": ["gd"] } + } + ] + }, + { + "recordType": "engineOrders", + "orders": [ + { + "environment": { "allRegionsAndLocales": true }, + "order": [ + "engine", + "engine-resourceicon", + "engine-chromeicon", + "engine-pref", + "engine-rel-searchform-purpose", + "engine-reordered" + ] + }, + { + "environment": { "locales": ["gd"] }, + "order": [ + "engine", + "engine-rel-searchform-purpose", + "engine-resourceicon", + "engine-chromeicon", + "engine-pref", + "engine-reordered" + ] + } + ] + } + ] +} diff --git a/toolkit/components/search/tests/xpcshell/method-extensions/search-config-v2.json b/toolkit/components/search/tests/xpcshell/method-extensions/search-config-v2.json new file mode 100644 index 0000000000..a8d3fcc515 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/method-extensions/search-config-v2.json @@ -0,0 +1,83 @@ +{ + "data": [ + { + "recordType": "engine", + "identifier": "get", + "base": { + "name": "Get Engine", + "urls": { + "search": { + "base": "https://example.com", + "params": [ + { + "name": "config", + "value": "1" + } + ], + "searchTermParamName": "search" + }, + "suggestions": { + "base": "https://example.com", + "params": [ + { + "name": "config", + "value": "1" + } + ], + "searchTermParamName": "suggest" + } + } + }, + "variants": [ + { + "environment": { "allRegionsAndLocales": true } + } + ] + }, + { + "recordType": "engine", + "identifier": "post", + "base": { + "name": "Post Engine", + "urls": { + "search": { + "base": "https://example.com", + "method": "POST", + "params": [ + { + "name": "config", + "value": "1" + } + ], + "searchTermParamName": "search" + }, + "suggestions": { + "base": "https://example.com", + "method": "POST", + "params": [ + { + "name": "config", + "value": "1" + } + ], + "searchTermParamName": "suggest" + } + } + }, + "variants": [ + { + "environment": { "allRegionsAndLocales": true } + } + ] + }, + { + "recordType": "defaultEngines", + "globalDefault": "get", + "specificDefaults": [] + }, + { + "recordType": "engineOrders", + "orders": [] + } + ] +} diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_ui_schemas_valid.js b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_ui_schemas_valid.js new file mode 100644 index 0000000000..3315bf974f --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_ui_schemas_valid.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let schemas = [ + ["search-config-schema.json", "search-config-ui-schema.json"], + ["search-config-v2-schema.json", "search-config-v2-ui-schema.json"], + ["search-config-icons-schema.json", "search-config-icons-ui-schema.json"], + [ + "search-config-overrides-schema.json", + "search-config-overrides-ui-schema.json", + ], + [ + "search-config-overrides-v2-schema.json", + "search-config-overrides-v2-ui-schema.json", + ], + [ + "search-default-override-allowlist-schema.json", + "search-default-override-allowlist-ui-schema.json", + ], +]; + +add_task(async function test_ui_schemas_valid() { + for (let [schema, uiSchema] of schemas) { + info(`Validating ${uiSchema} has every top-level from ${schema}`); + let schemaData = await IOUtils.readJSON( + PathUtils.join(do_get_cwd().path, schema) + ); + let uiSchemaData = await IOUtils.readJSON( + PathUtils.join(do_get_cwd().path, uiSchema) + ); + + await checkUISchemaValid(schemaData, uiSchemaData); + } +}); diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js index 51e71ff573..86686b62f7 100644 --- a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js +++ b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchconfig_validates.js @@ -65,103 +65,79 @@ function disallowAdditionalProperties(section) { } } -let searchConfigSchemaV1; -let searchConfigSchema; - -add_setup(async function () { - searchConfigSchemaV1 = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-schema.json") - ); - searchConfigSchema = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-v2-schema.json") +/** + * Asserts the remote setting collection validates against the schema. + * + * @param {object} options + * The options for the assertion. + * @param {string} options.collectionName + * The name of the collection under validation. + * @param {object[]} options.collectionData + * The collection data to validate. + * @param {string[]} [options.ignoreFields=[]] + * A list of fields to ignore in the collection data, e.g. where remote + * settings itself adds extra fields. `schema`, `id`, and `last_modified` are + * always ignored. + * @param {Function} [options.extraAssertsFn] + * An optional function to run additional assertions on each entry in the + * collection. + * @param {Function} options.getEntryId + * A function to get the identifier for each entry in the collection. + */ +async function assertSearchConfigValidates({ + collectionName, + collectionData, + ignoreFields = [], + extraAssertsFn, + getEntryId, +}) { + let schema = await IOUtils.readJSON( + PathUtils.join(do_get_cwd().path, `${collectionName}-schema.json`) ); -}); -async function checkSearchConfigValidates(schema, searchConfig) { disallowAdditionalProperties(schema); let validator = new JsonSchema.Validator(schema); - for (let entry of searchConfig) { + for (let entry of collectionData) { // Records in Remote Settings contain additional properties independent of // the schema. Hence, we don't want to validate their presence. - delete entry.schema; - delete entry.id; - delete entry.last_modified; + for (let field of [...ignoreFields, "schema", "id", "last_modified"]) { + delete entry[field]; + } let result = validator.validate(entry); - // entry.webExtension.id supports search-config v1. - let message = `Should validate ${ - entry.identifier ?? entry.recordType ?? entry.webExtension.id - }`; + let message = `Should validate ${getEntryId(entry)}`; if (!result.valid) { message += `:\n${JSON.stringify(result.errors, null, 2)}`; } Assert.ok(result.valid, message); - // All engine objects should have the base URL defined for each entry in - // entry.base.urls. - // Unfortunately this is difficult to enforce in the schema as it would - // need a `required` field that works across multiple levels. - if (entry.recordType == "engine") { - for (let urlEntry of Object.values(entry.base.urls)) { - Assert.ok( - urlEntry.base, - "Should have a base url for every URL defined on the top-level base object." - ); - } - } + extraAssertsFn?.(entry); } } -async function checkSearchConfigOverrideValidates( - schema, - searchConfigOverride -) { - let validator = new JsonSchema.Validator(schema); - - for (let entry of searchConfigOverride) { - // Records in Remote Settings contain additional properties independent of - // the schema. Hence, we don't want to validate their presence. - delete entry.schema; - delete entry.id; - delete entry.last_modified; - - let result = validator.validate(entry); - - let message = `Should validate ${entry.identifier ?? entry.telemetryId}`; - if (!result.valid) { - message += `:\n${JSON.stringify(result.errors, null, 2)}`; - } - Assert.ok(result.valid, message); - } -} +add_setup(async function () { + updateAppInfo({ ID: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}" }); +}); add_task(async function test_search_config_validates_to_schema_v1() { let selector = new SearchEngineSelectorOld(() => {}); - let searchConfig = await selector.getEngineConfiguration(); - await checkSearchConfigValidates(searchConfigSchemaV1, searchConfig); -}); - -add_task(async function test_ui_schema_valid_v1() { - let uiSchema = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-ui-schema.json") - ); - - await checkUISchemaValid(searchConfigSchemaV1, uiSchema); + await assertSearchConfigValidates({ + collectionName: "search-config", + collectionData: await selector.getEngineConfiguration(), + getEntryId: entry => entry.webExtension.id, + }); }); add_task(async function test_search_config_override_validates_to_schema_v1() { let selector = new SearchEngineSelectorOld(() => {}); - let searchConfigOverrides = await selector.getEngineConfigurationOverrides(); - let overrideSchema = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-overrides-schema.json") - ); - await checkSearchConfigOverrideValidates( - overrideSchema, - searchConfigOverrides - ); + await assertSearchConfigValidates({ + collectionName: "search-config-overrides", + collectionData: await selector.getEngineConfigurationOverrides(), + getEntryId: entry => entry.telemetryId, + }); }); add_task( @@ -171,20 +147,26 @@ add_task( SearchUtils.newSearchConfigEnabled = true; let selector = new SearchEngineSelector(() => {}); - let searchConfig = await selector.getEngineConfiguration(); - - await checkSearchConfigValidates(searchConfigSchema, searchConfig); - } -); - -add_task( - { skip_if: () => !SearchUtils.newSearchConfigEnabled }, - async function test_ui_schema_valid() { - let uiSchema = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-v2-ui-schema.json") - ); - await checkUISchemaValid(searchConfigSchema, uiSchema); + await assertSearchConfigValidates({ + collectionName: "search-config-v2", + collectionData: await selector.getEngineConfiguration(), + getEntryId: entry => entry.identifier, + extraAssertsFn: entry => { + // All engine objects should have the base URL defined for each entry in + // entry.base.urls. + // Unfortunately this is difficult to enforce in the schema as it would + // need a `required` field that works across multiple levels. + if (entry.recordType == "engine") { + for (let urlEntry of Object.values(entry.base.urls)) { + Assert.ok( + urlEntry.base, + "Should have a base url for every URL defined on the top-level base object." + ); + } + } + }, + }); } ); @@ -192,18 +174,33 @@ add_task( { skip_if: () => !SearchUtils.newSearchConfigEnabled }, async function test_search_config_override_validates_to_schema() { let selector = new SearchEngineSelector(() => {}); - let searchConfigOverrides = - await selector.getEngineConfigurationOverrides(); - let overrideSchema = await IOUtils.readJSON( - PathUtils.join( - do_get_cwd().path, - "search-config-overrides-v2-schema.json" - ) - ); - - await checkSearchConfigOverrideValidates( - overrideSchema, - searchConfigOverrides - ); + + await assertSearchConfigValidates({ + collectionName: "search-config-overrides-v2", + collectionData: await selector.getEngineConfigurationOverrides(), + getEntryId: entry => entry.identifier, + }); } ); + +add_task(async function test_search_config_icons_validates_to_schema() { + let searchIcons = RemoteSettings("search-config-icons"); + + await assertSearchConfigValidates({ + collectionName: "search-config-icons", + collectionData: await searchIcons.get(), + ignoreFields: ["attachment"], + getEntryId: entry => entry.engineIdentifiers[0], + }); +}); + +add_task(async function test_search_default_override_allowlist_validates() { + let allowlist = RemoteSettings("search-default-override-allowlist"); + + await assertSearchConfigValidates({ + collectionName: "search-default-override-allowlist", + collectionData: await allowlist.get(), + ignoreFields: ["attachment"], + getEntryId: entry => entry.engineName || entry.thirdPartyId, + }); +}); diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchicons_validates.js b/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchicons_validates.js deleted file mode 100644 index c830bb7ade..0000000000 --- a/toolkit/components/search/tests/xpcshell/searchconfigs/test_searchicons_validates.js +++ /dev/null @@ -1,20 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -let searchIconsSchema; - -add_setup(async function () { - searchIconsSchema = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-icons-schema.json") - ); -}); - -add_task(async function test_ui_schema_valid() { - let uiSchema = await IOUtils.readJSON( - PathUtils.join(do_get_cwd().path, "search-config-icons-ui-schema.json") - ); - - await checkUISchemaValid(searchIconsSchema, uiSchema); -}); diff --git a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell.toml b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell.toml index 8baff2a38d..07567005d6 100644 --- a/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell.toml +++ b/toolkit/components/search/tests/xpcshell/searchconfigs/xpcshell.toml @@ -40,20 +40,30 @@ requesttimeoutfactor = 2 ["test_rakuten.js"] -["test_searchconfig_validates.js"] +["test_searchconfig_ui_schemas_valid.js"] support-files = [ + "../../../schema/search-config-icons-schema.json", + "../../../schema/search-config-icons-ui-schema.json", "../../../schema/search-config-overrides-schema.json", + "../../../schema/search-config-overrides-ui-schema.json", "../../../schema/search-config-overrides-v2-schema.json", + "../../../schema/search-config-overrides-v2-ui-schema.json", "../../../schema/search-config-schema.json", "../../../schema/search-config-ui-schema.json", "../../../schema/search-config-v2-schema.json", "../../../schema/search-config-v2-ui-schema.json", + "../../../schema/search-default-override-allowlist-schema.json", + "../../../schema/search-default-override-allowlist-ui-schema.json", ] -["test_searchicons_validates.js"] +["test_searchconfig_validates.js"] support-files = [ "../../../schema/search-config-icons-schema.json", - "../../../schema/search-config-icons-ui-schema.json", + "../../../schema/search-config-overrides-schema.json", + "../../../schema/search-config-overrides-v2-schema.json", + "../../../schema/search-config-schema.json", + "../../../schema/search-config-v2-schema.json", + "../../../schema/search-default-override-allowlist-schema.json", ] ["test_selector_db_out_of_date.js"] diff --git a/toolkit/components/search/tests/xpcshell/test-extensions/search-config-v2.json b/toolkit/components/search/tests/xpcshell/test-extensions/search-config-v2.json new file mode 100644 index 0000000000..4f2e88a05b --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/test-extensions/search-config-v2.json @@ -0,0 +1,139 @@ +{ + "data": [ + { + "recordType": "engine", + "identifier": "plainengine", + "base": { + "name": "Plain", + "urls": { + "search": { + "base": "https://duckduckgo.com/", + "params": [ + { + "name": "t", + "searchAccessPoint": { + "newtab": "ffnt", + "homepage": "ffhp", + "searchbar": "ffsb", + "addressbar": "ffab", + "contextmenu": "ffcm" + } + } + ], + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://ac.duckduckgo.com/ac/q={searchTerms}&type=list" + } + } + }, + "variants": [ + { + "environment": { "allRegionsAndLocales": true } + } + ] + }, + { + "recordType": "engine", + "identifier": "special-engine", + "base": { + "name": "Special", + "urls": { + "search": { + "base": "https://www.google.com/search", + "params": [ + { + "name": "client", + "searchAccessPoint": { + "searchbar": "firefox-b-1", + "addressbar": "firefox-b-1-ab" + } + } + ], + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://www.google.com/complete/search?client=firefox&q={searchTerms}" + } + } + }, + "variants": [ + { + "environment": { "allRegionsAndLocales": true } + } + ] + }, + { + "recordType": "engine", + "identifier": "multilocale-an", + "base": { + "name": "Multilocale AN", + "urls": { + "search": { + "base": "https://an.wikipedia.org/wiki/Especial:Mirar", + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://an.wikipedia.org/w/api.php", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "regions": ["an"] } + } + ] + }, + { + "recordType": "engine", + "identifier": "multilocale-af", + "base": { + "name": "Multilocale AF", + "urls": { + "search": { + "base": "https://af.wikipedia.org/wiki/Spesiaal:Soek", + "searchTermParamName": "q" + }, + "suggestions": { + "base": "https://af.wikipedia.org/w/api.php", + "searchTermParamName": "q" + } + } + }, + "variants": [ + { + "environment": { "regions": ["af"] } + } + ] + }, + { + "recordType": "defaultEngines", + "globalDefault": "plainengine", + "specificDefaults": [ + { + "default": "special-engine", + "environment": { "regions": ["tr"] } + }, + { + "default": "multilocale-an", + "environment": { "regions": ["an"] } + } + ] + }, + { + "recordType": "engineOrders", + "orders": [ + { + "order": [ + "plainengine", + "special-engine", + "multilocale-af", + "multilocale-an" + ], + "environment": { "allRegionsAndLocales": true } + } + ] + } + ] +} diff --git a/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js b/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js new file mode 100644 index 0000000000..e4d8033993 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/test_appProvided_icons.js @@ -0,0 +1,211 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests to ensure that icons for application provided engines are correctly + * loaded from remote settings. + */ + +"use strict"; + +// A skeleton configuration that gets filled in from TESTS during `add_setup`. +let CONFIG = [ + { + recordType: "defaultEngines", + globalDefault: "engine_no_icon", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + +let TESTS = [ + { + engineId: "engine_no_icon", + expectedIcon: null, + }, + { + engineId: "engine_exact_match", + icons: [ + { + filename: "remoteIcon.ico", + engineIdentifiers: ["engine_exact_match"], + imageSize: 16, + }, + ], + expectedIcon: "remoteIcon.ico", + }, + { + engineId: "engine_begins_with", + icons: [ + { + filename: "remoteIcon.ico", + engineIdentifiers: ["engine_begins*"], + imageSize: 16, + }, + ], + expectedIcon: "remoteIcon.ico", + }, + { + engineId: "engine_non_default_sized_icon", + icons: [ + { + filename: "remoteIcon.ico", + engineIdentifiers: ["engine_non_default_sized_icon"], + imageSize: 32, + }, + ], + expectedIcon: "remoteIcon.ico", + }, + { + engineId: "engine_multiple_icons", + icons: [ + { + filename: "bigIcon.ico", + engineIdentifiers: ["engine_multiple_icons"], + imageSize: 16, + }, + { + filename: "remoteIcon.ico", + engineIdentifiers: ["engine_multiple_icons"], + imageSize: 32, + }, + ], + expectedIcon: "bigIcon.ico", + }, +]; + +async function getFileDataBuffer(filename) { + let data = await IOUtils.read( + PathUtils.join(do_get_cwd().path, "data", filename) + ); + return new TextEncoder().encode(data).buffer; +} + +async function mockRecordWithAttachment({ + filename, + engineIdentifiers, + imageSize, +}) { + let buffer = await getFileDataBuffer(filename); + + let stream = Cc["@mozilla.org/io/arraybuffer-input-stream;1"].createInstance( + Ci.nsIArrayBufferInputStream + ); + stream.setData(buffer, 0, buffer.byteLength); + + // Generate a hash. + let hasher = Cc["@mozilla.org/security/hash;1"].createInstance( + Ci.nsICryptoHash + ); + hasher.init(Ci.nsICryptoHash.SHA256); + hasher.updateFromStream(stream, -1); + let hash = hasher.finish(false); + hash = Array.from(hash, (_, i) => + ("0" + hash.charCodeAt(i).toString(16)).slice(-2) + ).join(""); + + let record = { + id: Services.uuid.generateUUID().toString(), + engineIdentifiers, + imageSize, + attachment: { + hash, + location: `main-workspace/search-config-icons/${filename}`, + filename, + size: buffer.byteLength, + mimetype: "application/json", + }, + }; + + let attachment = { + record, + blob: new Blob([buffer]), + }; + + return { record, attachment }; +} + +async function insertRecordIntoCollection(client, db, item) { + let { record, attachment } = await mockRecordWithAttachment(item); + await db.create(record); + await client.attachments.cacheImpl.set(record.id, attachment); + await db.importChanges({}, Date.now()); +} + +add_setup(async function () { + let client = RemoteSettings("search-config-icons"); + let db = client.db; + + await db.clear(); + + for (let test of TESTS) { + CONFIG.push({ + identifier: test.engineId, + recordType: "engine", + base: { + name: test.engineId + " name", + urls: { + search: { + base: "https://example.com/" + test.engineId, + searchTermParamName: "q", + }, + }, + }, + variants: [{ environment: { allRegionsAndLocales: true } }], + }); + + if ("icons" in test) { + for (let icon of test.icons) { + await insertRecordIntoCollection(client, db, { + ...icon, + id: test.engineId, + }); + } + } + } + + await SearchTestUtils.useTestEngines("simple-engines", null, CONFIG); + await Services.search.init(); +}); + +for (let test of TESTS) { + add_task(async function () { + info("Testing engine: " + test.engineId); + + let engine = Services.search.getEngineByName(test.engineId + " name"); + if (test.expectedIcon) { + let engineIconURL = await engine.getIconURL(16); + Assert.notEqual( + engineIconURL, + null, + "Should have an icon URL for the engine." + ); + + let response = await fetch(engineIconURL); + let buffer = new Uint8Array(await response.arrayBuffer()); + + let expectedBuffer = new Uint8Array( + await getFileDataBuffer(test.expectedIcon) + ); + + Assert.equal( + buffer.length, + expectedBuffer.length, + "Should have received matching buffer lengths for the expected icon" + ); + Assert.ok( + buffer.every((value, index) => value === expectedBuffer[index]), + "Should have received matching data for the expected icon" + ); + } else { + Assert.equal( + await engine.getIconURL(), + null, + "Should not have an icon URL for the engine." + ); + } + }); +} diff --git a/toolkit/components/search/tests/xpcshell/test_defaultPrivateEngine.js b/toolkit/components/search/tests/xpcshell/test_defaultPrivateEngine.js index 053d91fe48..392700d84a 100644 --- a/toolkit/components/search/tests/xpcshell/test_defaultPrivateEngine.js +++ b/toolkit/components/search/tests/xpcshell/test_defaultPrivateEngine.js @@ -108,7 +108,9 @@ add_task(async function test_defaultPrivateEngine() { loadPath: SearchUtils.newSearchConfigEnabled ? "[app]engine-rel-searchform-purpose@search.mozilla.org" : "[addon]engine-rel-searchform-purpose@search.mozilla.org", - submissionUrl: "https://www.google.com/search?q=&channel=sb", + submissionUrl: SearchUtils.newSearchConfigEnabled + ? "https://www.google.com/search?channel=sb&q=" + : "https://www.google.com/search?q=&channel=sb", verified: "default", }, }); diff --git a/toolkit/components/search/tests/xpcshell/test_engine_ids.js b/toolkit/components/search/tests/xpcshell/test_engine_ids.js index cef6a17c92..57b9ad26cf 100644 --- a/toolkit/components/search/tests/xpcshell/test_engine_ids.js +++ b/toolkit/components/search/tests/xpcshell/test_engine_ids.js @@ -47,9 +47,56 @@ const CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + add_setup(async function () { useHttpServer("opensearch"); - await SearchTestUtils.useTestEngines("data", null, CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await AddonTestUtils.promiseStartupManager(); await Services.search.init(); }); diff --git a/toolkit/components/search/tests/xpcshell/test_engine_set_alias.js b/toolkit/components/search/tests/xpcshell/test_engine_set_alias.js index ec79fe6783..f15e257996 100644 --- a/toolkit/components/search/tests/xpcshell/test_engine_set_alias.js +++ b/toolkit/components/search/tests/xpcshell/test_engine_set_alias.js @@ -119,7 +119,7 @@ add_task(async function test_engine_change_alias() { ); let observed = false; - Services.obs.addObserver(function observer(aSubject, aTopic, aData) { + Services.obs.addObserver(function observer() { observed = true; }, SearchUtils.TOPIC_ENGINE_MODIFIED); diff --git a/toolkit/components/search/tests/xpcshell/test_getSubmission_params_pref.js b/toolkit/components/search/tests/xpcshell/test_getSubmission_params_pref.js index 5283207394..54fd028474 100644 --- a/toolkit/components/search/tests/xpcshell/test_getSubmission_params_pref.js +++ b/toolkit/components/search/tests/xpcshell/test_getSubmission_params_pref.js @@ -14,6 +14,7 @@ const defaultBranch = Services.prefs.getDefaultBranch( SearchUtils.BROWSER_SEARCH_PREF ); const baseURL = "https://www.google.com/search?q=foo"; +const baseURLSearchConfigV2 = "https://www.google.com/search?"; add_setup(async function () { // The test engines used in this test need to be recognized as 'default' @@ -40,7 +41,9 @@ add_task(async function test_pref_initial_value() { const engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=good%26id%3Dunique", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=good%26id%3Dunique&q=foo" + : baseURL + "&code=good%26id%3Dunique", "Should have got the submission URL with the correct code" ); @@ -59,7 +62,9 @@ add_task(async function test_pref_updated() { const engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=supergood%26id%3Dunique123456", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=supergood%26id%3Dunique123456&q=foo" + : baseURL + "&code=supergood%26id%3Dunique123456", "Should have got the submission URL with the updated code" ); }); diff --git a/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus.js b/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus.js index e9b55ff3dc..3963a07368 100644 --- a/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus.js +++ b/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus.js @@ -11,6 +11,7 @@ const { NimbusFeatures } = ChromeUtils.importESModule( ); const baseURL = "https://www.google.com/search?q=foo"; +const baseURLSearchConfigV2 = "https://www.google.com/search?"; let getVariableStub; let updateStub; @@ -48,7 +49,9 @@ add_task(async function test_pref_initial_value() { const engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=good%26id%3Dunique", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=good%26id%3Dunique&q=foo" + : baseURL + "&code=good%26id%3Dunique", "Should have got the submission URL with the correct code" ); }); @@ -68,7 +71,9 @@ add_task(async function test_pref_updated() { const engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=supergood%26id%3Dunique123456", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=supergood%26id%3Dunique123456&q=foo" + : baseURL + "&code=supergood%26id%3Dunique123456", "Should have got the submission URL with the updated code" ); }); @@ -90,7 +95,9 @@ add_task(async function test_multiple_params() { let engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=sng&test=sup", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=sng&test=sup&q=foo" + : baseURL + "&code=sng&test=sup", "Should have got the submission URL with both parameters" ); @@ -107,7 +114,9 @@ add_task(async function test_multiple_params() { engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=sng", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=sng&q=foo" + : baseURL + "&code=sng", "Should have got the submission URL with one parameter" ); }); diff --git a/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus_invalid.js b/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus_invalid.js index e519a74c64..090b108acf 100644 --- a/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus_invalid.js +++ b/toolkit/components/search/tests/xpcshell/test_getSubmission_params_prefNimbus_invalid.js @@ -15,6 +15,7 @@ const { NimbusFeatures } = ChromeUtils.importESModule( ); const baseURL = "https://www.google.com/search?q=foo"; +const baseURLSearchConfigV2 = "https://www.google.com/search?"; let getVariableStub; let updateStub; @@ -66,7 +67,9 @@ add_task(async function test_switch_to_good_nimbus_setting() { const engine = Services.search.getEngineByName("engine-pref"); Assert.equal( engine.getSubmission("foo").uri.spec, - baseURL + "&code=supergood%26id%3Dunique123456", + SearchUtils.newSearchConfigEnabled + ? baseURLSearchConfigV2 + "code=supergood%26id%3Dunique123456&q=foo" + : baseURL + "&code=supergood%26id%3Dunique123456", "Should have got the submission URL with the updated code" ); }); diff --git a/toolkit/components/search/tests/xpcshell/test_initialization.js b/toolkit/components/search/tests/xpcshell/test_initialization.js index c89f3cfcb3..57894e1e55 100644 --- a/toolkit/components/search/tests/xpcshell/test_initialization.js +++ b/toolkit/components/search/tests/xpcshell/test_initialization.js @@ -43,13 +43,60 @@ const CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + add_setup(() => { do_get_profile(); Services.fog.initializeFOG(); }); add_task(async function test_initialization_delayed_addon_manager() { - let stub = await SearchTestUtils.useTestEngines("data", null, CONFIG); + let stub = await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); // Wait until the search service gets its configuration before starting // to initialise the add-on manager. This simulates the add-on manager // starting late which used to cause the search service to fail to load any @@ -58,7 +105,7 @@ add_task(async function test_initialization_delayed_addon_manager() { Services.tm.dispatchToMainThread(() => { AddonTestUtils.promiseStartupManager(); }); - return CONFIG; + return SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG; }); await Services.search.init(); diff --git a/toolkit/components/search/tests/xpcshell/test_initialization_with_region.js b/toolkit/components/search/tests/xpcshell/test_initialization_with_region.js index fb9cee5124..377772c6e1 100644 --- a/toolkit/components/search/tests/xpcshell/test_initialization_with_region.js +++ b/toolkit/components/search/tests/xpcshell/test_initialization_with_region.js @@ -76,6 +76,87 @@ const CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { excludedRegions: ["FR"] }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-pref", + base: { + name: "engine-pref", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "code", + experimentConfig: "code", + }, + { + name: "test", + experimentConfig: "test", + }, + ], + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + specificDefaults: [ + { + default: "engine", + defaultPrivate: "engine", + environment: { excludedRegions: ["FR"] }, + }, + { + default: "engine-pref", + defaultPrivate: "engine-pref", + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + // Default engine with no region defined. const DEFAULT = "Test search engine"; // Default engine with region set to FR. @@ -104,7 +185,11 @@ add_setup(async function () { ); SearchTestUtils.useMockIdleService(); - await SearchTestUtils.useTestEngines("data", null, CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await AddonTestUtils.promiseStartupManager(); }); diff --git a/toolkit/components/search/tests/xpcshell/test_maybereloadengine_order.js b/toolkit/components/search/tests/xpcshell/test_maybereloadengine_order.js index 663b7205a7..bdb8510812 100644 --- a/toolkit/components/search/tests/xpcshell/test_maybereloadengine_order.js +++ b/toolkit/components/search/tests/xpcshell/test_maybereloadengine_order.js @@ -1,56 +1,14 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -"use strict"; +/* + * Test engine order is not set after engine reload. + */ -const TEST_CONFIG = [ - { - webExtension: { - id: "plainengine@search.mozilla.org", - name: "Plain", - search_url: "https://duckduckgo.com/", - params: [ - { - name: "q", - value: "{searchTerms}", - }, - ], - suggest_url: "https://ac.duckduckgo.com/ac/q={searchTerms}&type=list", - }, - appliesTo: [{ included: { everywhere: true } }], - }, - { - webExtension: { - id: "special-engine@search.mozilla.org", - name: "Special", - search_url: "https://www.google.com/search", - params: [ - { - name: "q", - value: "{searchTerms}", - }, - { - name: "client", - condition: "purpose", - purpose: "keyword", - value: "firefox-b-1-ab", - }, - { - name: "client", - condition: "purpose", - purpose: "searchbar", - value: "firefox-b-1", - }, - ], - suggest_url: - "https://www.google.com/complete/search?client=firefox&q={searchTerms}", - }, - appliesTo: [{ default: "yes", included: { regions: ["FR"] } }], - }, -]; +"use strict"; add_setup(async function () { - await SearchTestUtils.useTestEngines("test-extensions", null, TEST_CONFIG); + await SearchTestUtils.useTestEngines("test-extensions"); await AddonTestUtils.promiseStartupManager(); registerCleanupFunction(AddonTestUtils.promiseShutdownManager); @@ -59,7 +17,7 @@ add_setup(async function () { add_task(async function basic_multilocale_test() { let resolver; let initPromise = new Promise(resolve => (resolver = resolve)); - useCustomGeoServer("FR", initPromise); + useCustomGeoServer("TR", initPromise); await Services.search.init(); await Services.search.getAppProvidedEngines(); diff --git a/toolkit/components/search/tests/xpcshell/test_missing_engine.js b/toolkit/components/search/tests/xpcshell/test_missing_engine.js index 3da9fe14a6..259baf9c1a 100644 --- a/toolkit/components/search/tests/xpcshell/test_missing_engine.js +++ b/toolkit/components/search/tests/xpcshell/test_missing_engine.js @@ -55,6 +55,48 @@ const BAD_CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + add_setup(async function () { SearchTestUtils.useMockIdleService(); await AddonTestUtils.promiseStartupManager(); @@ -66,7 +108,11 @@ add_setup(async function () { }); add_task(async function test_startup_with_missing() { - await SearchTestUtils.useTestEngines("data", null, BAD_CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : BAD_CONFIG + ); const result = await Services.search.init(); Assert.ok( @@ -89,7 +135,7 @@ add_task(async function test_update_with_missing() { await RemoteSettings(SearchUtils.SETTINGS_KEY).emit("sync", { data: { - current: GOOD_CONFIG, + current: SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : GOOD_CONFIG, }, }); @@ -110,7 +156,7 @@ add_task(async function test_update_with_missing() { await RemoteSettings(SearchUtils.SETTINGS_KEY).emit("sync", { data: { - current: BAD_CONFIG, + current: SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : BAD_CONFIG, }, }); diff --git a/toolkit/components/search/tests/xpcshell/test_opensearch_icon.js b/toolkit/components/search/tests/xpcshell/test_opensearch_icon.js index 68cd9577c1..23f2d9d575 100644 --- a/toolkit/components/search/tests/xpcshell/test_opensearch_icon.js +++ b/toolkit/components/search/tests/xpcshell/test_opensearch_icon.js @@ -56,9 +56,9 @@ add_task(async function test_icon_types() { engine.QueryInterface(Ci.nsISearchEngine); await promiseEngineChanged; - Assert.ok(engine.getIconURL(), `${test.name} engine has an icon`); + Assert.ok(await engine.getIconURL(), `${test.name} engine has an icon`); Assert.ok( - engine.getIconURL().startsWith(test.expected), + (await engine.getIconURL()).startsWith(test.expected), `${test.name} iconURI starts with the expected information` ); } @@ -69,13 +69,13 @@ add_task(async function test_multiple_icons_in_file() { url: `${gDataUrl}engineImages.xml`, }); - Assert.ok(engine.getIconURL().includes("ico16")); - Assert.ok(engine.getIconURL(16).includes("ico16")); - Assert.ok(engine.getIconURL(32).includes("ico32")); - Assert.ok(engine.getIconURL(74).includes("ico74")); + Assert.ok((await engine.getIconURL()).includes("ico16")); + Assert.ok((await engine.getIconURL(16)).includes("ico16")); + Assert.ok((await engine.getIconURL(32)).includes("ico32")); + Assert.ok((await engine.getIconURL(74)).includes("ico74")); info("Invalid dimensions should return null until bug 1655070 is fixed."); - Assert.equal(null, engine.getIconURL(50)); + Assert.equal(null, await engine.getIconURL(50)); }); add_task(async function test_icon_not_in_opensearch_file() { @@ -86,6 +86,6 @@ add_task(async function test_icon_not_in_opensearch_file() { ); // Even though the icon wasn't specified inside the XML file, it should be - // available both in the iconURI attribute and with getIconURLBySize. - Assert.ok(engine.getIconURL(16).includes("ico16")); + // available. + Assert.ok((await engine.getIconURL(16)).includes("ico16")); }); diff --git a/toolkit/components/search/tests/xpcshell/test_opensearch_icons_invalid.js b/toolkit/components/search/tests/xpcshell/test_opensearch_icons_invalid.js index 6db13a0da8..744d46052e 100644 --- a/toolkit/components/search/tests/xpcshell/test_opensearch_icons_invalid.js +++ b/toolkit/components/search/tests/xpcshell/test_opensearch_icons_invalid.js @@ -21,8 +21,8 @@ add_task(async function test_installedresourceicon() { url: `${gDataUrl}opensearch/chromeicon.xml`, }); - Assert.equal(undefined, engine1.getIconURL()); - Assert.equal(undefined, engine2.getIconURL()); + Assert.equal(undefined, await engine1.getIconURL()); + Assert.equal(undefined, await engine2.getIconURL()); }); add_task(async function test_installedhttpplace() { @@ -50,7 +50,7 @@ add_task(async function test_installedhttpplace() { Assert.equal( undefined, - engine.getIconURL(), + await engine.getIconURL(), "Should not have set an iconURI" ); }); diff --git a/toolkit/components/search/tests/xpcshell/test_override_allowlist_switch.js b/toolkit/components/search/tests/xpcshell/test_override_allowlist_switch.js index cd51e7bdee..5ec485c4ec 100644 --- a/toolkit/components/search/tests/xpcshell/test_override_allowlist_switch.js +++ b/toolkit/components/search/tests/xpcshell/test_override_allowlist_switch.js @@ -306,8 +306,16 @@ add_task(async function test_app_provided_engine_deployment_extended() { await assertCorrectlySwitchedWhenRemoved(async () => { info("Change configuration to remove engine from user's environment"); - await SearchTestUtils.updateRemoteSettingsConfig(CONFIG_SIMPLE_LOCALE_DE); - configStub.returns(CONFIG_SIMPLE_LOCALE_DE); + await SearchTestUtils.updateRemoteSettingsConfig( + SearchUtils.newSearchConfigEnabled + ? CONFIG_SIMPLE_LOCALE_DE_V2 + : CONFIG_SIMPLE_LOCALE_DE + ); + configStub.returns( + SearchUtils.newSearchConfigEnabled + ? CONFIG_SIMPLE_LOCALE_DE_V2 + : CONFIG_SIMPLE_LOCALE_DE + ); }); }); diff --git a/toolkit/components/search/tests/xpcshell/test_reload_engines.js b/toolkit/components/search/tests/xpcshell/test_reload_engines.js index ac63b62e59..60cd3c2a13 100644 --- a/toolkit/components/search/tests/xpcshell/test_reload_engines.js +++ b/toolkit/components/search/tests/xpcshell/test_reload_engines.js @@ -238,6 +238,260 @@ const CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-pref", + base: { + name: "engine-pref", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "code", + experimentConfig: "code", + }, + { + name: "test", + experimentConfig: "test", + }, + ], + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-chromeicon", + base: { + name: "engine-chromeicon", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + { + environment: { regions: ["FR"] }, + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "c", + value: "my-test", + }, + ], + searchTermParamName: "q1", + }, + }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-rel-searchform-purpose", + base: { + name: "engine-rel-searchform-purpose", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { excludedRegions: ["FR"] }, + urls: { + search: { + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + searchbar: "sb", + }, + }, + ], + }, + }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-reordered", + base: { + name: "Test search engine (Reordered)", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-resourceicon", + base: { + name: "engine-resourceicon", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { excludedRegions: ["FR"] }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-resourceicon-gd", + base: { + name: "engine-resourceicon-gd", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-same-name", + base: { + name: "engine-same-name", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { excludedRegions: ["FR"] }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-same-name-gd", + base: { + name: "engine-same-name-gd", + urls: { + search: { + base: "https://www.example.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "defaultEngines", + specificDefaults: [ + { + default: "engine", + defaultPrivate: "engine", + environment: { excludedRegions: ["FR"] }, + }, + { + default: "engine-pref", + defaultPrivate: "engine-pref", + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "engineOrders", + orders: [ + { + order: ["engine-resourceicon-gd"], + environment: { regions: ["FR"] }, + }, + ], + }, +]; + async function visibleEngines() { return (await Services.search.getVisibleEngines()).map(e => e.identifier); } @@ -250,7 +504,11 @@ add_setup(async function () { ); SearchTestUtils.useMockIdleService(); - await SearchTestUtils.useTestEngines("data", null, CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await AddonTestUtils.promiseStartupManager(); }); @@ -325,23 +583,47 @@ add_task(async function test_config_updated_engine_changes() { await reloadObserved; Services.obs.removeObserver(enginesObs, SearchUtils.TOPIC_ENGINE_MODIFIED); - Assert.deepEqual( - enginesAdded, - ["engine-resourceicon-gd", "engine-reordered"], - "Should have added the correct engines" - ); - - Assert.deepEqual( - enginesModified.sort(), - ["engine", "engine-chromeicon", "engine-pref", "engine-same-name-gd"], - "Should have modified the expected engines" - ); - - Assert.deepEqual( - enginesRemoved, - ["engine-rel-searchform-purpose", "engine-resourceicon"], - "Should have removed the expected engine" - ); + if (SearchUtils.newSearchConfigEnabled) { + Assert.deepEqual( + enginesAdded, + ["engine-resourceicon-gd", "engine-reordered", "engine-same-name-gd"], + "Should have added the correct engines" + ); + + Assert.deepEqual( + enginesModified.sort(), + ["engine", "engine-chromeicon", "engine-pref"], + "Should have modified the expected engines" + ); + + Assert.deepEqual( + enginesRemoved, + [ + "engine-rel-searchform-purpose", + "engine-resourceicon", + "engine-same-name", + ], + "Should have removed the expected engine" + ); + } else { + Assert.deepEqual( + enginesAdded, + ["engine-resourceicon-gd", "engine-reordered"], + "Should have added the correct engines" + ); + + Assert.deepEqual( + enginesModified.sort(), + ["engine", "engine-chromeicon", "engine-pref", "engine-same-name-gd"], + "Should have modified the expected engines" + ); + + Assert.deepEqual( + enginesRemoved, + ["engine-rel-searchform-purpose", "engine-resourceicon"], + "Should have removed the expected engine" + ); + } const installedEngines = await Services.search.getAppProvidedEngines(); @@ -382,7 +664,9 @@ add_task(async function test_config_updated_engine_changes() { ); const engineWithSameName = await Services.search.getEngineByName( - "engine-same-name" + SearchUtils.newSearchConfigEnabled + ? "engine-same-name-gd" + : "engine-same-name" ); Assert.equal( engineWithSameName.getSubmission("test").uri.spec, diff --git a/toolkit/components/search/tests/xpcshell/test_reload_engines_experiment.js b/toolkit/components/search/tests/xpcshell/test_reload_engines_experiment.js index ea5cdee3e5..5edcb6081b 100644 --- a/toolkit/components/search/tests/xpcshell/test_reload_engines_experiment.js +++ b/toolkit/components/search/tests/xpcshell/test_reload_engines_experiment.js @@ -72,8 +72,87 @@ const CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-same-name-en", + base: { + name: "engine-same-name", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-same-name-gd", + base: { + name: "engine-same-name", + urls: { + search: { + base: "https://www.example.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true, experiment: "xpcshell" }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + add_setup(async function () { - await SearchTestUtils.useTestEngines("data", null, CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await AddonTestUtils.promiseStartupManager(); }); @@ -125,19 +204,42 @@ add_task(async function test_config_updated_engine_changes() { await reloadObserved; Services.obs.removeObserver(enginesObs, SearchUtils.TOPIC_ENGINE_MODIFIED); - Assert.deepEqual(enginesAdded, [], "Should have added the correct engines"); + if (SearchUtils.newSearchConfigEnabled) { + // In the new config, engine-same-name-en and engine-same-name-gd are two + // different engine configs and they will be treated as different engines + // and not the same. That's the reason why the assertions are different below. + Assert.deepEqual( + enginesAdded, + ["engine-same-name-gd"], + "Should have added the correct engines" + ); - Assert.deepEqual( - enginesModified.sort(), - ["engine", "engine-same-name-gd"], - "Should have modified the expected engines" - ); + Assert.deepEqual( + enginesModified.sort(), + ["engine", "engine-same-name-en"], + "Should have modified the expected engines" + ); - Assert.deepEqual( - enginesRemoved, - [], - "Should have removed the expected engine" - ); + Assert.deepEqual( + enginesRemoved, + ["engine-same-name"], + "Should have removed the expected engine" + ); + } else { + Assert.deepEqual(enginesAdded, [], "Should have added the correct engines"); + + Assert.deepEqual( + enginesModified.sort(), + ["engine", "engine-same-name-gd"], + "Should have modified the expected engines" + ); + + Assert.deepEqual( + enginesRemoved, + [], + "Should have removed the expected engine" + ); + } const installedEngines = await Services.search.getAppProvidedEngines(); diff --git a/toolkit/components/search/tests/xpcshell/test_reload_engines_locales.js b/toolkit/components/search/tests/xpcshell/test_reload_engines_locales.js index 6fa277655d..ed516c5a15 100644 --- a/toolkit/components/search/tests/xpcshell/test_reload_engines_locales.js +++ b/toolkit/components/search/tests/xpcshell/test_reload_engines_locales.js @@ -72,6 +72,85 @@ const CONFIG = [ }, ]; +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-diff-name-en", + base: { + name: "engine-diff-name-en", + urls: { + search: { + base: "https://en.wikipedia.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { excludedLocales: ["gd"] }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-diff-name-gd", + base: { + name: "engine-diff-name-gd", + urls: { + search: { + base: "https://gd.wikipedia.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { locales: ["gd"] }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + add_setup(async () => { Services.locale.availableLocales = [ ...Services.locale.availableLocales, @@ -80,7 +159,11 @@ add_setup(async () => { ]; Services.locale.requestedLocales = ["gd"]; - await SearchTestUtils.useTestEngines("data", null, CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await AddonTestUtils.promiseStartupManager(); await Services.search.init(); }); @@ -101,7 +184,9 @@ add_task(async function test_config_updated_engine_changes() { ); Assert.equal( engine.getSubmission("test").uri.spec, - "https://gd.wikipedia.com/search", + SearchUtils.newSearchConfigEnabled + ? "https://gd.wikipedia.com/search?q=test" + : "https://gd.wikipedia.com/search", "Should have the gd search url" ); @@ -122,7 +207,9 @@ add_task(async function test_config_updated_engine_changes() { ); Assert.equal( engine.getSubmission("test").uri.spec, - "https://en.wikipedia.com/search", + SearchUtils.newSearchConfigEnabled + ? "https://en.wikipedia.com/search?q=test" + : "https://en.wikipedia.com/search", "Should have the en search url" ); }); diff --git a/toolkit/components/search/tests/xpcshell/test_remove_engine_notification_box.js b/toolkit/components/search/tests/xpcshell/test_remove_engine_notification_box.js index 0222334255..d5b1366b93 100644 --- a/toolkit/components/search/tests/xpcshell/test_remove_engine_notification_box.js +++ b/toolkit/components/search/tests/xpcshell/test_remove_engine_notification_box.js @@ -57,6 +57,100 @@ const CONFIG_UPDATED = CONFIG.filter(r => r.webExtension.id.startsWith("engine-pref") ); +const CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-pref", + base: { + name: "engine-pref", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + specificDefaults: [ + { + default: "engine", + environment: { excludedRegions: ["FR"] }, + }, + { + default: "engine-pref", + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + +const CONFIG_V2_UPDATED = [ + { + recordType: "engine", + identifier: "engine-pref", + base: { + name: "engine-pref", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + specificDefaults: [ + { + default: "engine", + environment: { excludedRegions: ["FR"] }, + }, + { + default: "engine-pref", + environment: { regions: ["FR"] }, + }, + ], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + let stub; let settingsFilePath; let userSettings; @@ -64,7 +158,11 @@ let userSettings; add_setup(async function () { SearchSettings.SETTINGS_INVALIDATION_DELAY = 100; SearchTestUtils.useMockIdleService(); - await SearchTestUtils.useTestEngines("data", null, CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await AddonTestUtils.promiseStartupManager(); stub = sinon.stub( @@ -246,7 +344,9 @@ add_task(async function test_default_engine_changed_and_metadata_unchanged() { }; // Update config by removing the app default engine - await setConfigToLoad(CONFIG_UPDATED); + await setConfigToLoad( + SearchUtils.newSearchConfigEnabled ? CONFIG_V2_UPDATED : CONFIG_UPDATED + ); await reloadEngines(structuredClone(userSettings)); Assert.ok( @@ -293,7 +393,9 @@ add_task(async function test_app_default_engine_changed_on_start_up() { settings.metaData.current = ""; // Update config by removing the app default engine - await setConfigToLoad(CONFIG_UPDATED); + await setConfigToLoad( + SearchUtils.newSearchConfigEnabled ? CONFIG_V2_UPDATED : CONFIG_UPDATED + ); await loadEngines(settings); Assert.ok( @@ -311,7 +413,9 @@ add_task(async function test_app_default_engine_change_start_up_still_exists() { settings.metaData.current = ""; settings.metaData.appDefaultEngine = "Test search engine"; - await setConfigToLoad(CONFIG); + await setConfigToLoad( + SearchUtils.newSearchConfigEnabled ? CONFIG_V2 : CONFIG + ); await loadEngines(settings); Assert.ok( diff --git a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js index 48769be41e..4a30eb741a 100644 --- a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js +++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js @@ -581,7 +581,7 @@ add_task(async function stop_search() { let histogram = TelemetryTestUtils.getAndClearKeyedHistogram( SEARCH_TELEMETRY_LATENCY ); - let controller = new SearchSuggestionController(result => { + let controller = new SearchSuggestionController(() => { do_throw("The callback shouldn't be called after stop()"); }); let resultPromise = controller.fetch("mo", false, getEngine); diff --git a/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js b/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js index cfa9e56144..042c74d86a 100644 --- a/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js +++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest_cookies.js @@ -28,7 +28,7 @@ function countCacheEntries() { ); storage.asyncVisitStorage( { - onCacheStorageInfo(num, consumption) { + onCacheStorageInfo(num) { this._num = num; }, onCacheEntryInfo(uri) { diff --git a/toolkit/components/search/tests/xpcshell/test_searchSuggest_extraParams.js b/toolkit/components/search/tests/xpcshell/test_searchSuggest_extraParams.js index 8ecf4b02f3..3261174ebf 100644 --- a/toolkit/components/search/tests/xpcshell/test_searchSuggest_extraParams.js +++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest_extraParams.js @@ -22,8 +22,62 @@ const TEST_CONFIG = [ }, ]; +const TEST_CONFIG_V2 = [ + { + recordType: "engine", + identifier: "get", + base: { + name: "Get Engine", + urls: { + search: { + base: "https://example.com", + params: [ + { + name: "webExtension", + value: "1", + }, + ], + searchTermParamName: "search", + }, + suggestions: { + base: "https://example.com", + params: [ + { + name: "custom_param", + experimentConfig: "test_pref_param", + }, + { + name: "webExtension", + value: "1", + }, + ], + searchTermParamName: "suggest", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "get", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + add_setup(async function () { - await SearchTestUtils.useTestEngines("method-extensions", null, TEST_CONFIG); + await SearchTestUtils.useTestEngines( + "method-extensions", + null, + SearchUtils.newSearchConfigEnabled ? TEST_CONFIG_V2 : TEST_CONFIG + ); await AddonTestUtils.promiseStartupManager(); await Services.search.init(); }); diff --git a/toolkit/components/search/tests/xpcshell/test_searchTermFromResult.js b/toolkit/components/search/tests/xpcshell/test_searchTermFromResult.js index 700c6a3450..ea040aacd4 100644 --- a/toolkit/components/search/tests/xpcshell/test_searchTermFromResult.js +++ b/toolkit/components/search/tests/xpcshell/test_searchTermFromResult.js @@ -6,6 +6,53 @@ * Tests searchTermFromResult API. */ +let CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine-purposes", + base: { + name: "Test Engine With Purposes", + urls: { + search: { + base: "https://www.example.com/search", + params: [ + { name: "pc", value: "FIREFOX" }, + { + name: "form", + searchAccessPoint: { + newtab: "MOZNEWTAB", + homepage: "MOZHOMEPAGE", + searchbar: "MOZSEARCHBAR", + addressbar: "MOZKEYWORD", + contextmenu: "MOZCONTEXT", + }, + }, + { + name: "channel", + experimentConfig: "testChannelEnabled", + }, + ], + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine-purpose", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + let defaultEngine; // The test string contains special characters to ensure @@ -14,66 +61,72 @@ const TERM = "c;,?:@&=+$-_.!~*'()# d\u00E8f"; const TERM_ENCODED = "c%3B%2C%3F%3A%40%26%3D%2B%24-_.!~*'()%23+d%C3%A8f"; add_setup(async function () { - await SearchTestUtils.useTestEngines("data", null, [ - { - webExtension: { - id: "engine-purposes@search.mozilla.org", - name: "Test Engine With Purposes", - search_url: "https://www.example.com/search", - params: [ - { - name: "form", - condition: "purpose", - purpose: "keyword", - value: "MOZKEYWORD", - }, - { - name: "form", - condition: "purpose", - purpose: "contextmenu", - value: "MOZCONTEXT", - }, + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled + ? CONFIG_V2 + : [ { - name: "form", - condition: "purpose", - purpose: "newtab", - value: "MOZNEWTAB", + webExtension: { + id: "engine-purposes@search.mozilla.org", + name: "Test Engine With Purposes", + search_url: "https://www.example.com/search", + params: [ + { + name: "form", + condition: "purpose", + purpose: "keyword", + value: "MOZKEYWORD", + }, + { + name: "form", + condition: "purpose", + purpose: "contextmenu", + value: "MOZCONTEXT", + }, + { + name: "form", + condition: "purpose", + purpose: "newtab", + value: "MOZNEWTAB", + }, + { + name: "form", + condition: "purpose", + purpose: "searchbar", + value: "MOZSEARCHBAR", + }, + { + name: "form", + condition: "purpose", + purpose: "homepage", + value: "MOZHOMEPAGE", + }, + { + name: "pc", + value: "FIREFOX", + }, + { + name: "channel", + condition: "pref", + pref: "testChannelEnabled", + }, + { + name: "q", + value: "{searchTerms}", + }, + ], + }, + appliesTo: [ + { + included: { everywhere: true }, + default: "yes", + }, + ], }, - { - name: "form", - condition: "purpose", - purpose: "searchbar", - value: "MOZSEARCHBAR", - }, - { - name: "form", - condition: "purpose", - purpose: "homepage", - value: "MOZHOMEPAGE", - }, - { - name: "pc", - value: "FIREFOX", - }, - { - name: "channel", - condition: "pref", - pref: "testChannelEnabled", - }, - { - name: "q", - value: "{searchTerms}", - }, - ], - }, - appliesTo: [ - { - included: { everywhere: true }, - default: "yes", - }, - ], - }, - ]); + ] + ); await AddonTestUtils.promiseStartupManager(); await Services.search.init(); diff --git a/toolkit/components/search/tests/xpcshell/test_settings_persist.js b/toolkit/components/search/tests/xpcshell/test_settings_persist.js index bed6771554..5c2cbd85c4 100644 --- a/toolkit/components/search/tests/xpcshell/test_settings_persist.js +++ b/toolkit/components/search/tests/xpcshell/test_settings_persist.js @@ -1,9 +1,13 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +/** + * Tests the removal of an engine is persisted in search settings. + */ + "use strict"; -const CONFIG_DEFAULT = [ +const CONFIG_UPDATED = [ { webExtension: { id: "plainengine@search.mozilla.org", @@ -18,26 +22,38 @@ const CONFIG_DEFAULT = [ }, appliesTo: [{ included: { everywhere: true } }], }, +]; + +const SEARCH_CONFIG_V2_UPDATED = [ { - webExtension: { - id: "special-engine@search.mozilla.org", - name: "Special", - search_url: "https://www.google.com/search", - params: [ - { - name: "q", - value: "{searchTerms}", + recordType: "engine", + identifier: "plainengine", + base: { + name: "Plain", + urls: { + search: { + base: "https://duckduckgo.com/", + searchTermParamName: "q", }, - ], + }, }, - appliesTo: [{ included: { everywhere: true } }], + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "plainengine", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], }, ]; -const CONFIG_UPDATED = CONFIG_DEFAULT.filter(r => - r.webExtension.id.startsWith("plainengine") -); - async function startup() { let settingsFileWritten = promiseAfterSettings(); let ss = new SearchService(); @@ -50,7 +66,10 @@ async function startup() { async function updateConfig(config) { const settings = await RemoteSettings(SearchUtils.SETTINGS_KEY); settings.get.restore(); - sinon.stub(settings, "get").returns(config); + + config == "test-extensions" + ? await SearchTestUtils.useTestEngines("test-extensions") + : sinon.stub(settings, "get").returns(config); } async function visibleEngines(ss) { @@ -58,7 +77,7 @@ async function visibleEngines(ss) { } add_setup(async function () { - await SearchTestUtils.useTestEngines("test-extensions", null, CONFIG_DEFAULT); + await SearchTestUtils.useTestEngines("test-extensions"); registerCleanupFunction(AddonTestUtils.promiseShutdownManager); await AddonTestUtils.promiseStartupManager(); // This is only needed as otherwise events will not be properly notified @@ -87,7 +106,11 @@ add_task(async function () { ); ss._removeObservers(); - updateConfig(CONFIG_UPDATED); + await updateConfig( + SearchUtils.newSearchConfigEnabled + ? SEARCH_CONFIG_V2_UPDATED + : CONFIG_UPDATED + ); ss = await startup(); Assert.ok( @@ -96,7 +119,8 @@ add_task(async function () { ); ss._removeObservers(); - updateConfig(CONFIG_DEFAULT); + await updateConfig("test-extensions"); + ss = await startup(); Assert.ok( diff --git a/toolkit/components/search/tests/xpcshell/test_sort_orders-no-hints.js b/toolkit/components/search/tests/xpcshell/test_sort_orders-no-hints.js index d2a2d7dd93..cb7e314fd4 100644 --- a/toolkit/components/search/tests/xpcshell/test_sort_orders-no-hints.js +++ b/toolkit/components/search/tests/xpcshell/test_sort_orders-no-hints.js @@ -15,7 +15,13 @@ add_setup(async function () { "data", null, ( - await readJSONFile(do_get_file("data/engines-no-order-hint.json")) + await readJSONFile( + do_get_file( + SearchUtils.newSearchConfigEnabled + ? "data/search-config-v2-no-order-hint.json" + : "data/engines-no-order-hint.json" + ) + ) ).data ); diff --git a/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js b/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js index 84bd1be9cc..f1b6208326 100644 --- a/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js +++ b/toolkit/components/search/tests/xpcshell/test_telemetry_event_default.js @@ -45,6 +45,49 @@ const BASE_CONFIG = [ default: "yes", }, ]; + +const BASE_CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine", + specificDefaults: [], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; const MAIN_CONFIG = [ { webExtension: { @@ -78,7 +121,6 @@ const MAIN_CONFIG = [ { webExtension: { id: "engine-chromeicon@search.mozilla.org", - name: "engine-chromeicon", search_url: "https://www.google.com/search", params: [ @@ -163,6 +205,154 @@ const MAIN_CONFIG = [ }, ]; +const MAIN_CONFIG_V2 = [ + { + recordType: "engine", + identifier: "engine", + base: { + name: "Test search engine", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "channel", + searchAccessPoint: { + addressbar: "fflb", + contextmenu: "rcs", + }, + }, + ], + searchTermParamName: "q", + }, + suggestions: { + base: "https://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-chromeicon", + base: { + name: "engine-chromeicon", + urls: { + search: { + base: "https://www.google.com/search", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-fr", + base: { + name: "Test search engine (fr)", + urls: { + search: { + base: "https://www.google.fr/search", + params: [ + { + name: "ie", + value: "iso-8859-1", + }, + { + name: "oe", + value: "iso-8859-1", + }, + ], + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine-pref", + base: { + name: "engine-pref", + urls: { + search: { + base: "https://www.google.com/search", + params: [ + { + name: "code", + experimentConfig: "code", + }, + { + name: "test", + experimentConfig: "test", + }, + ], + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "engine", + identifier: "engine2", + base: { + name: "A second test engine", + urls: { + search: { + base: "https://duckduckgo.com/", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }, + { + recordType: "defaultEngines", + globalDefault: "engine-chromeicon", + specificDefaults: [ + { + default: "engine-fr", + environment: { excludedRegions: ["DE"], locales: ["fr"] }, + }, + { + default: "engine-pref", + environment: { regions: ["DE"] }, + }, + { + default: "engine2", + environment: { experiment: "test1" }, + }, + ], + }, + { + recordType: "engineOrders", + orders: [], + }, +]; + const testSearchEngine = { id: "engine", name: "Test search engine", @@ -186,7 +376,9 @@ const testFrEngine = { loadPath: SearchUtils.newSearchConfigEnabled ? "[app]engine-fr@search.mozilla.org" : "[addon]engine-fr@search.mozilla.org", - submissionURL: "https://www.google.fr/search?q=&ie=iso-8859-1&oe=iso-8859-1", + submissionURL: SearchUtils.newSearchConfigEnabled + ? "https://www.google.fr/search?ie=iso-8859-1&oe=iso-8859-1&q=" + : "https://www.google.fr/search?q=&ie=iso-8859-1&oe=iso-8859-1", }; const testPrefEngine = { id: "engine-pref", @@ -335,7 +527,11 @@ add_setup(async () => { "_showRemovalOfSearchEngineNotificationBox" ); - await SearchTestUtils.useTestEngines("data", null, BASE_CONFIG); + await SearchTestUtils.useTestEngines( + "data", + null, + SearchUtils.newSearchConfigEnabled ? BASE_CONFIG_V2 : BASE_CONFIG + ); await AddonTestUtils.promiseStartupManager(); await Services.search.init(); @@ -344,7 +540,9 @@ add_setup(async () => { add_task(async function test_configuration_changes_default() { clearTelemetry(); - await SearchTestUtils.updateRemoteSettingsConfig(MAIN_CONFIG); + await SearchTestUtils.updateRemoteSettingsConfig( + SearchUtils.newSearchConfigEnabled ? MAIN_CONFIG_V2 : MAIN_CONFIG + ); await checkTelemetry( "config", diff --git a/toolkit/components/search/tests/xpcshell/test_validate_engines.js b/toolkit/components/search/tests/xpcshell/test_validate_engines.js index 8a25216057..d311253183 100644 --- a/toolkit/components/search/tests/xpcshell/test_validate_engines.js +++ b/toolkit/components/search/tests/xpcshell/test_validate_engines.js @@ -15,18 +15,52 @@ const ss = new SearchService(); add_task(async function test_validate_engines() { let settings = RemoteSettings(SearchUtils.SETTINGS_KEY); let config = await settings.get(); - config = config.map(e => { - return { - appliesTo: [ - { - included: { - everywhere: true, + + if (SearchUtils.newSearchConfigEnabled) { + // We do not load engines with the same name. However, in search-config-v2 + // we have multiple engines named eBay, so we error out. + // We never deploy more than one eBay in each environment so this issue + // won't be a problem. + // Ignore the error and test the configs can be created to engine objects. + consoleAllowList.push("Could not load engine"); + config = config.map(obj => { + if (obj.recordType == "engine") { + return { + recordType: "engine", + identifier: obj.identifier, + base: { + name: obj.base.name, + urls: { + search: { + base: obj.base.urls.search.base || "", + searchTermParamName: "q", + }, + }, + }, + variants: [ + { + environment: { allRegionsAndLocales: true }, + }, + ], + }; + } + + return obj; + }); + } else { + config = config.map(e => { + return { + appliesTo: [ + { + included: { + everywhere: true, + }, }, - }, - ], - webExtension: e.webExtension, - }; - }); + ], + webExtension: e.webExtension, + }; + }); + } sinon.stub(settings, "get").returns(config); await AddonTestUtils.promiseStartupManager(); diff --git a/toolkit/components/search/tests/xpcshell/test_webextensions_install.js b/toolkit/components/search/tests/xpcshell/test_webextensions_install.js index 6183d6f95a..a05218824a 100644 --- a/toolkit/components/search/tests/xpcshell/test_webextensions_install.js +++ b/toolkit/components/search/tests/xpcshell/test_webextensions_install.js @@ -80,7 +80,9 @@ add_task(async function test_install_duplicate_engine() { let submission = engine.getSubmission("foo"); Assert.equal( submission.uri.spec, - "https://duckduckgo.com/?q=foo&t=ffsb", + SearchUtils.newSearchConfigEnabled + ? "https://duckduckgo.com/?t=ffsb&q=foo" + : "https://duckduckgo.com/?q=foo&t=ffsb", "Should have not changed the app provided engine." ); @@ -127,7 +129,7 @@ add_task( let engine = await Services.search.getEngineByName("Multilocale AN"); Assert.ok( - engine.getIconURL().endsWith("favicon-an.ico"), + (await engine.getIconURL()).endsWith("favicon-an.ico"), "Should have the correct favicon for an extension of one locale using a different locale." ); Assert.equal( @@ -156,7 +158,11 @@ add_task(async function test_load_favicon_invalid() { await observed; let engine = await Services.search.getEngineByName("Example"); - Assert.equal(null, engine.getIconURL(), "Should not have set an iconURI"); + Assert.equal( + null, + await engine.getIconURL(), + "Should not have set an iconURI" + ); // User uninstalls their engine await extension.awaitStartup(); @@ -182,7 +188,11 @@ add_task(async function test_load_favicon_invalid_redirect() { await observed; let engine = await Services.search.getEngineByName("Example"); - Assert.equal(null, engine.getIconURL(), "Should not have set an iconURI"); + Assert.equal( + null, + await engine.getIconURL(), + "Should not have set an iconURI" + ); // User uninstalls their engine await extension.awaitStartup(); @@ -208,9 +218,9 @@ add_task(async function test_load_favicon_redirect() { await promiseEngineChanged; - Assert.ok(engine.getIconURL(), "Should have set an iconURI"); + Assert.ok(await engine.getIconURL(), "Should have set an iconURI"); Assert.ok( - engine.getIconURL().startsWith("data:image/x-icon;base64,"), + (await engine.getIconURL()).startsWith("data:image/x-icon;base64,"), "Should have saved the expected content type for the icon" ); diff --git a/toolkit/components/search/tests/xpcshell/test_webextensions_startup_duplicate.js b/toolkit/components/search/tests/xpcshell/test_webextensions_startup_duplicate.js index 3e42fdee13..eb7e67e66e 100644 --- a/toolkit/components/search/tests/xpcshell/test_webextensions_startup_duplicate.js +++ b/toolkit/components/search/tests/xpcshell/test_webextensions_startup_duplicate.js @@ -27,7 +27,7 @@ add_task(async function test_install_duplicate_engine_startup() { let name = "Plain"; let id = "plain@tests.mozilla.org"; consoleAllowList.push( - `#installExtensionEngine failed for ${id}`, + `#createAndAddAddonEngine failed for ${id}`, `An engine called ${name} already exists` ); // Do not use SearchTestUtils.installSearchExtension, as we need to manually @@ -51,7 +51,9 @@ add_task(async function test_install_duplicate_engine_startup() { let submission = engine.getSubmission("foo"); Assert.equal( submission.uri.spec, - "https://duckduckgo.com/?q=foo&t=ffsb", + SearchUtils.newSearchConfigEnabled + ? "https://duckduckgo.com/?t=ffsb&q=foo" + : "https://duckduckgo.com/?q=foo&t=ffsb", "Should have not changed the app provided engine." ); diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.toml b/toolkit/components/search/tests/xpcshell/xpcshell.toml index 47847c93de..7dd023cbec 100644 --- a/toolkit/components/search/tests/xpcshell/xpcshell.toml +++ b/toolkit/components/search/tests/xpcshell/xpcshell.toml @@ -33,6 +33,8 @@ support-files = [ "data/engine-same-name/_locales/gd/messages.json", "data/engines-no-order-hint.json", "data/engines.json", + "data/search-config-v2.json", + "data/search-config-v2-no-order-hint.json", "data/iconsRedirect.sjs", "data/search.json", "data/search-legacy.json", @@ -60,6 +62,7 @@ support-files = [ "simple-engines/basic/manifest.json", "simple-engines/simple/manifest.json", "test-extensions/engines.json", + "test-extensions/search-config-v2.json", "test-extensions/plainengine/favicon.ico", "test-extensions/plainengine/manifest.json", "test-extensions/special-engine/favicon.ico", @@ -75,6 +78,9 @@ support-files = [ ["test_appDefaultEngine.js"] +["test_appProvided_icons.js"] +prefs = ["browser.search.newSearchConfig.enabled=true"] + ["test_async.js"] ["test_config_engine_params.js"] @@ -82,6 +88,7 @@ support-files = [ "method-extensions/get/manifest.json", "method-extensions/post/manifest.json", "method-extensions/engines.json", + "method-extensions/search-config-v2.json", ] ["test_defaultEngine.js"] @@ -110,15 +117,15 @@ support-files = [ ["test_engine_old_selector_override.js"] +["test_engine_old_selector_remote_override.js"] + ["test_engine_old_selector_remote_settings.js"] tags = "remotesettings searchmain" -["test_engine_old_selector_remote_override.js"] +["test_engine_selector_defaults.js"] ["test_engine_selector_engine_orders.js"] -["test_engine_selector_defaults.js"] - ["test_engine_selector_environment.js"] ["test_engine_selector_variants.js"] |