tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit d59db0f008c681c0c623a004a8347e94375c8f4c
parent 590be5def684a944bf3d30a97d74e0f2bf5dcb2b
Author: Pier Angelo Vendrame <pierov@torproject.org>
Date:   Tue,  2 Sep 2025 10:04:03 +0200

BB 43525: Skip Remote Settings for search engine customization.

Also, add some bundled search engines.

Diffstat:
Mtoolkit/components/search/ConfigSearchEngine.sys.mjs | 54++++++++++++++++--------------------------------------
Mtoolkit/components/search/SearchEngineSelector.sys.mjs | 71++++++++++++++++++++++-------------------------------------------------
Atoolkit/components/search/content/base-browser-search-engine-icons.json | 18++++++++++++++++++
Atoolkit/components/search/content/base-browser-search-engines.json | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/components/search/content/duckduckgo.ico | 0
Atoolkit/components/search/content/startpage-16.png | 0
Atoolkit/components/search/content/startpage-32.png | 0
Atoolkit/components/search/jar.mn | 6++++++
Mtoolkit/components/search/moz.build | 2++
Atoolkit/components/search/tests/xpcshell/test_base_browser.js | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/search/tests/xpcshell/xpcshell.toml | 2++
11 files changed, 198 insertions(+), 87 deletions(-)

diff --git a/toolkit/components/search/ConfigSearchEngine.sys.mjs b/toolkit/components/search/ConfigSearchEngine.sys.mjs @@ -112,6 +112,10 @@ class IconHandler { await this.#buildIconMap(); } + if (AppConstants.BASE_BROWSER_VERSION) { + return this.#iconMap.get(engineIdentifier); + } + let iconList = this.#iconMap.get(this.getKey(engineIdentifier)) || []; return iconList.filter(r => this.#identifierMatches(engineIdentifier, r.engineIdentifiers) @@ -128,29 +132,7 @@ class IconHandler { * source object or null of there is no icon with the supplied width. */ async createIconURL(iconRecord) { - let iconData; - try { - iconData = await this.#iconCollection.attachments.get(iconRecord); - } catch (ex) { - console.error(ex); - } - if (!iconData) { - console.warn("Unable to find the attachment for", iconRecord.id); - // Queue an update in case we haven't downloaded it yet. - this.#pendingUpdatesMap.set(iconRecord.id, iconRecord); - this.#maybeQueueIdle(); - return null; - } - - if (iconData.record.last_modified != iconRecord.last_modified) { - // The icon we have stored is out of date, queue an update so that we'll - // download the new icon. - this.#pendingUpdatesMap.set(iconRecord.id, iconRecord); - this.#maybeQueueIdle(); - } - return URL.createObjectURL( - new Blob([iconData.buffer], { type: iconRecord.attachment.mimetype }) - ); + return iconRecord.url; } QueryInterface = ChromeUtils.generateQI(["nsIObserver"]); @@ -234,27 +216,23 @@ class IconHandler { * Obtains the icon list from the remote settings collection. */ async #buildIconMap() { - let iconList = []; try { - iconList = await this.#iconCollection.get(); + this.#iconMap = new Map( + Object.entries( + await ( + await fetch( + "chrome://global/content/search/base-browser-search-engine-icons.json" + ) + ).json() + ) + ); } catch (ex) { console.error(ex); + this.#iconMap = null; } - if (!iconList.length) { + if (!this.#iconMap) { console.error("Failed to obtain search engine icon list records"); } - - this.#iconMap = new Map(); - for (let record of iconList) { - let keys = new Set(record.engineIdentifiers.map(this.getKey)); - for (let key of keys) { - if (this.#iconMap.has(key)) { - this.#iconMap.get(key).push(record); - } else { - this.#iconMap.set(key, [record]); - } - } - } } /** diff --git a/toolkit/components/search/SearchEngineSelector.sys.mjs b/toolkit/components/search/SearchEngineSelector.sys.mjs @@ -9,6 +9,7 @@ * } from "../uniffi-bindgen-gecko-js/components/generated/RustSearch.sys.mjs"; */ +import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; const lazy = XPCOMUtils.declareLazy({ @@ -91,42 +92,19 @@ export class SearchEngineSelector { return this.#getConfigurationPromise; } - this.#getConfigurationPromise = Promise.all([ - this.#getConfiguration(), - this.#getConfigurationOverrides(), - ]); - let remoteSettingsData = await this.#getConfigurationPromise; - this.#configuration = remoteSettingsData[0]; - this.#getConfigurationPromise = null; - - if (!this.#configuration?.length) { - throw Components.Exception( - "Failed to get engine data from Remote Settings", - Cr.NS_ERROR_UNEXPECTED - ); - } - - /** - * Records whether the listeners have been added or not. - */ - if (!this.#listenerAdded) { - this.#remoteConfig.on("sync", this.#boundOnConfigurationUpdated); - this.#remoteConfigOverrides.on( - "sync", - this.#boundOnConfigurationOverridesUpdated - ); - /** - * Records whether the listeners have been added or not. - */ - this.#listenerAdded = true; - } + let { promise, resolve } = Promise.withResolvers(); + this.#getConfigurationPromise = promise; + this.#configuration = await ( + await fetch( + "chrome://global/content/search/base-browser-search-engines.json" + ) + ).json(); + resolve(this.#configuration); this.#selector.setSearchConfig( JSON.stringify({ data: this.#configuration }) ); - this.#selector.setConfigOverrides( - JSON.stringify({ data: remoteSettingsData[1] }) - ); + this.#selector.setConfigOverrides(JSON.stringify({ data: [] })); return this.#configuration; } @@ -369,6 +347,12 @@ export class SearchEngineSelector { * The new configuration object */ _onConfigurationUpdated({ data: { current } }) { + // tor-browser#43525: Even though RemoteSettings are a no-op for us, we do + // not want them to interfere in any way. + if (AppConstants.BASE_BROWSER_VERSION) { + return; + } + this.#configuration = current; this.#selector.setSearchConfig( @@ -396,6 +380,12 @@ export class SearchEngineSelector { * The new configuration object */ _onConfigurationOverridesUpdated({ data: { current } }) { + // tor-browser#43525: Even though RemoteSettings are a no-op for us, we do + // not want them to interfere in any way. + if (AppConstants.BASE_BROWSER_VERSION) { + return; + } + this.#selector.setConfigOverrides(JSON.stringify({ data: current })); lazy.logConsole.debug("Search configuration overrides updated remotely"); @@ -405,23 +395,6 @@ export class SearchEngineSelector { } /** - * Obtains the configuration overrides from remote settings. - * - * @returns {Promise<object[]>} - * An array of objects in the database, or an empty array if none - * could be obtained. - */ - async #getConfigurationOverrides() { - let result = []; - try { - result = await this.#remoteConfigOverrides.get(); - } catch (ex) { - // This data is remote only, so we just return an empty array if it fails. - } - return result; - } - - /** * @type {InstanceType<typeof lazy.SearchEngineSelector>?} */ #cachedSelector = null; diff --git a/toolkit/components/search/content/base-browser-search-engine-icons.json b/toolkit/components/search/content/base-browser-search-engine-icons.json @@ -0,0 +1,18 @@ +{ + "ddg": [ + { "url": "chrome://global/content/search/duckduckgo.ico", "imageSize": 32 } + ], + "ddg-noai": [ + { "url": "chrome://global/content/search/duckduckgo.ico", "imageSize": 32 } + ], + "startpage": [ + { + "url": "chrome://global/content/search/startpage-16.png", + "imageSize": 16 + }, + { + "url": "chrome://global/content/search/startpage-32.png", + "imageSize": 32 + } + ] +} diff --git a/toolkit/components/search/content/base-browser-search-engines.json b/toolkit/components/search/content/base-browser-search-engines.json @@ -0,0 +1,74 @@ +[ + { + "base": { + "aliases": ["duckduckgo", "ddg"], + "classification": "general", + "name": "DuckDuckGo", + "urls": { + "search": { + "base": "https://duckduckgo.com/", + "params": [], + "searchTermParamName": "q" + } + } + }, + "id": "04e99a38-13ee-47d8-8aa4-64482b3dea99", + "identifier": "ddg", + "recordType": "engine", + "variants": [{ "environment": { "allRegionsAndLocales": true } }] + }, + { + "base": { + "aliases": ["ddgnoai"], + "classification": "general", + "name": "DuckDuckGo (no AI)", + "urls": { + "search": { + "base": "https://noai.duckduckgo.com/", + "params": [], + "searchTermParamName": "q" + } + } + }, + "id": "91687f02-56dd-4fef-ba26-bf139dff3166", + "identifier": "ddg-noai", + "recordType": "engine", + "variants": [{ "environment": { "allRegionsAndLocales": true } }] + }, + { + "base": { + "aliases": ["startpage", "sp"], + "classification": "general", + "name": "Startpage", + "urls": { + "search": { + "base": "https://www.startpage.com/sp/search", + "params": [], + "searchTermParamName": "q" + } + } + }, + "id": "927bbd9f-b2f3-48b4-8974-1c1148028f4d", + "identifier": "startpage", + "recordType": "engine", + "variants": [{ "environment": { "allRegionsAndLocales": true } }] + }, + { + "recordType": "defaultEngines", + "globalDefault": "ddg", + "globalDefaultPrivate": "ddg" + }, + { + "recordType": "engineOrders", + "orders": [ + { + "environment": { "allRegionsAndLocales": true }, + "order": [ + "ddg", + "ddg-noai", + "startpage" + ] + } + ] + } +] diff --git a/toolkit/components/search/content/duckduckgo.ico b/toolkit/components/search/content/duckduckgo.ico Binary files differ. diff --git a/toolkit/components/search/content/startpage-16.png b/toolkit/components/search/content/startpage-16.png Binary files differ. diff --git a/toolkit/components/search/content/startpage-32.png b/toolkit/components/search/content/startpage-32.png Binary files differ. diff --git a/toolkit/components/search/jar.mn b/toolkit/components/search/jar.mn @@ -0,0 +1,6 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +toolkit.jar: + content/global/search/ (content/*) diff --git a/toolkit/components/search/moz.build b/toolkit/components/search/moz.build @@ -46,5 +46,7 @@ TESTING_JS_MODULES += [ "tests/SearchTestUtils.sys.mjs", ] +JAR_MANIFESTS += ["jar.mn"] + with Files("**"): BUG_COMPONENT = ("Firefox", "Search") diff --git a/toolkit/components/search/tests/xpcshell/test_base_browser.js b/toolkit/components/search/tests/xpcshell/test_base_browser.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This tests the SearchService to check our override of the remote settings is + * working as expected. + * + * When adding new engines, it should be enough to change expectedURLs below. + */ + +"use strict"; + +const expectedURLs = { + ddg: "https://duckduckgo.com/?q=test", + "ddg-noai": "https://noai.duckduckgo.com/?q=test", + startpage: "https://www.startpage.com/sp/search?q=test", +}; +const defaultEngine = "ddg"; + +add_setup(async function setup() { + await Services.search.init(); +}); + +add_task(async function test_listEngines() { + const { engines } = + await Services.search.wrappedJSObject._fetchEngineSelectorEngines(); + const foundIdentifiers = engines.map(e => e.identifier); + Assert.deepEqual(foundIdentifiers, Object.keys(expectedURLs)); +}); + +add_task(async function test_default() { + Assert.equal( + (await Services.search.getDefault()).id, + defaultEngine, + `${defaultEngine} is our default search engine in normal mode.` + ); + Assert.equal( + (await Services.search.getDefaultPrivate()).id, + defaultEngine, + `${defaultEngine} is our default search engine in PBM.` + ); +}); + +add_task(function test_checkSearchURLs() { + for (const [id, url] of Object.entries(expectedURLs)) { + const engine = Services.search.getEngineById(id); + const foundUrl = engine.getSubmission("test").uri.spec; + Assert.equal(foundUrl, url, `The URL of ${engine.name} is not altered.`); + } +}); + +add_task(async function test_iconsDoesNotFail() { + for (const id of Object.keys(expectedURLs)) { + const engine = Services.search.getEngineById(id); + // No need to assert anything, as in case of error this method should throw. + await engine.getIconURL(); + } +}); diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.toml b/toolkit/components/search/tests/xpcshell/xpcshell.toml @@ -37,6 +37,8 @@ tags = "remote-settings" ["test_async.js"] +["test_base_browser.js"] + ["test_configExpansion.js"] support-files = [ "../../schema/search-config-v2-schema.json",