tor-browser

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

commit db7c9796b2a43c3c65e3c4feae9c8da1242022f8
parent 5d72084dabcd2191a8a7bc00337d0213bcf4fca2
Author: Marco Bonardo <mbonardo@mozilla.com>
Date:   Mon,  8 Dec 2025 09:41:40 +0000

Bug 2004330 - canUseSemanticSearch is wrongly removing the database on startup- r=jteow

Differential Revision: https://phabricator.services.mozilla.com/D275221

Diffstat:
Mtoolkit/components/places/PlacesSemanticHistoryManager.sys.mjs | 81+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mtoolkit/components/places/tests/unit/test_PlacesSemanticHistoryManager.js | 24++++++++++++++++++++----
2 files changed, 68 insertions(+), 37 deletions(-)

diff --git a/toolkit/components/places/PlacesSemanticHistoryManager.sys.mjs b/toolkit/components/places/PlacesSemanticHistoryManager.sys.mjs @@ -62,6 +62,7 @@ XPCOMUtils.defineLazyPreferenceGetter( try { return new Map(JSON.parse(val)); } catch (ex) { + lazy.logger.debug("Invalid json in supportedRegions pref."); // Supposing a user may empty the pref to disable the feature, as they // don't know it should be a JSON string, we'll treat that as an empty // Map, so the feature is disabled. @@ -106,7 +107,7 @@ class PlacesSemanticHistoryManager { #updateTaskLatency = []; embedder; qualifiedForSemanticSearch = false; - #promiseRemoved = null; + #promiseInitialized = null; enoughEntries = false; #shutdownProgress = { state: "Not started" }; #deferredTaskInterval = DEFERRED_TASK_INTERVAL_MS; @@ -195,34 +196,45 @@ class PlacesSemanticHistoryManager { this.#updateTaskLatency = []; lazy.logger.trace("PlaceSemanticManager constructor"); - // When semantic history is disabled or not available anymore due to system - // requirements, we want to remove the database files, though we don't want - // to check on disk on every startup, thus we use a pref. The removal is - // done on startup anyway, as it's less likely to fail. - // We check UserValue because users may set it to false to try to disable - // the feature, then if we'd check the value the files would not be removed. - let wasInitialized = Services.prefs.prefHasUserValue( - "places.semanticHistory.initialized" - ); - let isAvailable = this.canUseSemanticSearch; - let removeFiles = - (wasInitialized && !isAvailable) || - Services.prefs.getBoolPref( - "places.semanticHistory.removeOnStartup", - false + this.#promiseInitialized = (async () => { + // canUseSemanticSearch depends on Region being initialized. + if (!lazy.Region.home) { + await lazy.Region.init(); + } + + // When semantic history is disabled or not available anymore due to + // system requirements, we want to remove the database files, though we + // don't want to check on disk on every startup, thus we use a pref. + // The removal is done on startup anyway, as it's less likely to fail. + // We check prefHasUserValue instead of the value itself, because users + // may set it to false to try to disable the feature, then checking value + // the files would not be removed. + lazy.logger.debug( + "PlaceSemanticManager detected region:", + lazy.Region.home ); - if (removeFiles) { - lazy.logger.info("Removing database files on startup"); - Services.prefs.clearUserPref("places.semanticHistory.removeOnStartup"); - this.#promiseRemoved = this.semanticDB - .removeDatabaseFiles() - .catch(console.error); - } - if (!isAvailable) { - Services.prefs.clearUserPref("places.semanticHistory.initialized"); - } else if (!wasInitialized) { - Services.prefs.setBoolPref("places.semanticHistory.initialized", true); - } + let wasInitialized = Services.prefs.prefHasUserValue( + "places.semanticHistory.initialized" + ); + + let isAvailable = this.canUseSemanticSearch; + let removeFiles = + (wasInitialized && !isAvailable) || + Services.prefs.getBoolPref( + "places.semanticHistory.removeOnStartup", + false + ); + if (removeFiles) { + lazy.logger.info("Removing database files on startup"); + Services.prefs.clearUserPref("places.semanticHistory.removeOnStartup"); + await this.semanticDB.removeDatabaseFiles().catch(console.error); + } + if (!isAvailable) { + Services.prefs.clearUserPref("places.semanticHistory.initialized"); + } else if (!wasInitialized) { + Services.prefs.setBoolPref("places.semanticHistory.initialized", true); + } + })(); } /** @@ -235,13 +247,16 @@ class PlacesSemanticHistoryManager { if ( Services.startup.isInOrBeyondShutdownPhase( Ci.nsIAppStartup.SHUTDOWN_PHASE_APPSHUTDOWNCONFIRMED - ) || - !this.canUseSemanticSearch + ) ) { return null; } - // We must eventually wait for removal to finish. - await this.#promiseRemoved; + + await this.#promiseInitialized; + + if (!this.canUseSemanticSearch) { + return null; + } // Avoid re-entrance using a cached promise rather than handing off a conn. if (!this.#promiseConn) { @@ -357,7 +372,7 @@ class PlacesSemanticHistoryManager { localePattern = localePattern.toLowerCase(); if ( localePattern.endsWith("*") && - appLocale.startsWith(localePattern.slice(0, -1)) + appLocale.startsWith(localePattern.replace(/-?\*$/, "")) ) { return true; } else if (localePattern == appLocale) { diff --git a/toolkit/components/places/tests/unit/test_PlacesSemanticHistoryManager.js b/toolkit/components/places/tests/unit/test_PlacesSemanticHistoryManager.js @@ -8,6 +8,7 @@ ChromeUtils.defineESModuleGetters(this, { sinon: "resource://testing-common/Sinon.sys.mjs", getPlacesSemanticHistoryManager: "resource://gre/modules/PlacesSemanticHistoryManager.sys.mjs", + Region: "resource://gre/modules/Region.sys.mjs", }); ChromeUtils.defineLazyGetter(this, "QuickSuggestTestUtils", () => { @@ -220,10 +221,10 @@ add_task(async function test_canUseSemanticSearch_region_locale() { setPref: "invalid json", // invalid, should use default. }, { - region: "US", - locale: "en", // wrong locale format - supported: false, - setPref: '[["US",["en-*"]]]', + region: "IT", + locale: "it", // short locale format + supported: true, + setPref: '[["IT",["it-*"]]]', }, { region: "US", @@ -356,6 +357,21 @@ add_task(async function test_removeDatabaseFilesOnStartup() { ); }); +add_task(async function test_empty_region() { + // Test that if region is empty (uninitialized) creating a semantic manager + // will try to initialize Region. + let stub = sinon.stub(Region, "home").get(() => ""); + let spy = sinon.spy(Region, "init"); + let semanticManager = createPlacesSemanticHistoryManager(); + Assert.ok( + !semanticManager.canUseSemanticSearch, + `Check semantic search disabled when region not set` + ); + Assert.ok(spy.calledOnce, "Region.init should have been called"); + spy.restore(); + stub.restore(); +}); + add_task(async function test_chunksTelemetry() { await PlacesTestUtils.addVisits([ { url: "https://test1.moz.com/", title: "test 1" },