tor-browser

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

commit 78331ef5e3af9e455b8f311e3fe7a78080819ef5
parent 303198a05b6c12480bbe6f13537dfecb52c10f2b
Author: Rolf Rando <rrando@mozilla.com>
Date:   Tue,  7 Oct 2025 18:23:41 +0000

Bug 1980666 - Cull inferred personalization places older history r=nbarrett

Periodically (every 5 days) checks and deletes older personalization stats (raw topic impression/click data) that is older than 6 months.

Clears data when places is cleared

Adds an experimenter pref
newtabInferredPersonalization {
    "history_cull_days": <integer>
}
be used to change the expiration date from 6 months to something less.

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

Diffstat:
Mbrowser/extensions/newtab/lib/InferredPersonalizationFeed.sys.mjs | 45++++++++++++++++++++++++++++++++++++++++++++-
Mtoolkit/components/nimbus/FeatureManifest.yaml | 4++++
2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/browser/extensions/newtab/lib/InferredPersonalizationFeed.sys.mjs b/browser/extensions/newtab/lib/InferredPersonalizationFeed.sys.mjs @@ -44,6 +44,9 @@ const CLICK_TABLE = "moz_newtab_story_click"; const IMPRESSION_TABLE = "moz_newtab_story_impression"; const TEST_MODEL_ID = "TEST"; +const OLD_DATA_PRESERVE_DAYS_DEFAULT = 30 * 6; +const OLD_DATA_CLEAR_CHECK_FREQUENCY_MS = 5 * 3600 * 24 * 1000; // 5 days + /** * A feature that periodically generates a interest vector for inferred personalization. */ @@ -193,9 +196,20 @@ export class InferredPersonalizationFeed { interestVectorRefreshHours * HOURS_TO_MS ) ) { + let lastClearedDB = interest_vector?.lastClearedDB ?? this.Date().now(); + const needsCleanup = + this.Date().now() - lastClearedDB >= OLD_DATA_CLEAR_CHECK_FREQUENCY_MS; + if (needsCleanup) { + await this.clearOldData( + values?.inferredPersonalizationConfig?.history_cull_days || + OLD_DATA_PRESERVE_DAYS_DEFAULT + ); + lastClearedDB = this.Date().now(); + } interest_vector = { data: await this.generateInterestVector(), lastUpdated: this.Date().now(), + lastClearedDB, }; } await this.cache.set("interest_vector", interest_vector); @@ -283,6 +297,35 @@ export class InferredPersonalizationFeed { return interactions; } + /** + * Deletes older data from a table + * @param {int} preserveAgeDays Number of days to preserve + * @param {*} table Table to clear + */ + async clearOldDataOfTable(preserveAgeDays, table) { + let sql = `DELETE FROM ${table} + WHERE timestamp_s < ${timeMSToSeconds(this.Date().now()) - preserveAgeDays * 60 * 24}`; + try { + await lazy.PlacesUtils.withConnectionWrapper( + "newtab/lib/InferredPersonalizationFeed.sys.mjs: clearOldDataOfTable", + async db => { + await db.execute(sql); + } + ); + } catch (ex) { + console.error(`Error clearning places data ${ex}`); + } + } + + /** + * Deletes older data from impression and click tables + * @param {int} preserveAgeDays Number of days to preserve (defaults to 6 months) + */ + async clearOldData(preserveAgeDays) { + await this.clearOldDataOfTable(preserveAgeDays, IMPRESSION_TABLE); + await this.clearOldDataOfTable(preserveAgeDays, CLICK_TABLE); + } + async recordInferredPersonalizationInteraction( table, tile, @@ -385,7 +428,7 @@ export class InferredPersonalizationFeed { } break; case at.PLACES_HISTORY_CLEARED: - // TODO Handle places history clear + await this.clearOldData(0); break; case at.DISCOVERY_STREAM_IMPRESSION_STATS: if (this.loaded && this.isEnabled()) { diff --git a/toolkit/components/nimbus/FeatureManifest.yaml b/toolkit/components/nimbus/FeatureManifest.yaml @@ -1428,6 +1428,10 @@ newtabInferredPersonalization: type: boolean description: >- Whether to use Unary encoding with differential privacy in the interest vector. If false or not set, we default to laplace noise with real values + history_cull_days: + type: int + description: >- + Number of days local inferred feature history to preserve normalized_time_zone_offset: type: boolean description: >-