tor-browser

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

commit 2aba8a94991c5062a3c51605a236d47101f5010d
parent 9590957bfee52c1a836a135834eee255aa336905
Author: Jeremy Swinarton <jswinarton@mozilla.com>
Date:   Fri, 14 Nov 2025 16:09:19 +0000

Bug 1994523: Proof-of-concept API and data store for Tab Notes r=sthompson,tabbrowser-reviewers

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

Diffstat:
Abrowser/components/tabbrowser/TabNotes.sys.mjs | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/components/tabbrowser/content/tabbrowser.js | 1+
Mbrowser/components/tabbrowser/moz.build | 1+
Mbrowser/components/tabbrowser/test/browser/tabs/browser.toml | 2++
Abrowser/components/tabbrowser/test/browser/tabs/browser_tab_notes.js | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 151 insertions(+), 0 deletions(-)

diff --git a/browser/components/tabbrowser/TabNotes.sys.mjs b/browser/components/tabbrowser/TabNotes.sys.mjs @@ -0,0 +1,79 @@ +/* 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/. */ + +export class TabNotesStorage { + /** @type {Map<URL, string>} */ + #store; + + constructor() { + this.reset(); + } + + /** + * Retrieve a note for a URL, if it exists. + * + * @param {URL} url + * The URL that the note is associated with + * @returns {string | undefined } + */ + get(url) { + return this.#store.get(url); + } + + /** + * Set a note for a URL. + * + * @param {URL} url + * The URL that the note should be associated with + * @param {string} note + * The note itself + * @returns {string} + * The actual note that was set after sanitization + */ + set(url, note) { + let sanitized = this.#sanitizeInput(note); + this.#store.set(url, sanitized); + return sanitized; + } + + /** + * Delete a note for a URL. + * + * @param {URL} url + * The URL of the note + * @returns {boolean} + * True if there was a note and it was deleted; false otherwise + */ + delete(url) { + return this.#store.delete(url); + } + + /** + * Check if a URL has a note. + * + * @param {URL} url + * The URL of the note + * @returns {boolean} + * True if a note is associated with this URL; false otherwise + */ + has(url) { + return this.#store.has(url); + } + + /** + * Clear all notes for all URLs. + * + * @returns {void} + */ + reset() { + this.#store = new Map(); + } + + #sanitizeInput(value) { + return value.slice(0, 1000); + } +} + +// Singleton object accessible from all windows +export const TabNotes = new TabNotesStorage(); diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js @@ -110,6 +110,7 @@ "moz-src:///browser/components/newtab/SponsorProtection.sys.mjs", TabMetrics: "moz-src:///browser/components/tabbrowser/TabMetrics.sys.mjs", + TabNotes: "moz-src:///browser/components/tabbrowser/TabNotes.sys.mjs", TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs", TaskbarTabsUtils: diff --git a/browser/components/tabbrowser/moz.build b/browser/components/tabbrowser/moz.build @@ -14,6 +14,7 @@ MOZ_SRC_FILES += [ "OpenInTabsUtils.sys.mjs", "SmartTabGrouping.sys.mjs", "TabMetrics.sys.mjs", + "TabNotes.sys.mjs", "TabsList.sys.mjs", "TabUnloader.sys.mjs", ] diff --git a/browser/components/tabbrowser/test/browser/tabs/browser.toml b/browser/components/tabbrowser/test/browser/tabs/browser.toml @@ -579,6 +579,8 @@ tags = "vertical-tabs" ["browser_tab_move_to_new_window_reload.js"] tags = "vertical-tabs" +["browser_tab_notes.js"] + ["browser_tab_play.js"] tags = "vertical-tabs" diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_tab_notes.js b/browser/components/tabbrowser/test/browser/tabs/browser_tab_notes.js @@ -0,0 +1,68 @@ +/* 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/. */ + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["browser.tabs.notes.enabled", true]], + }); + + registerCleanupFunction(async function () { + await SpecialPowers.popPrefEnv(); + }); +}); + +add_task(async function tabNotesBasicStorageTests() { + let url = "https://example.com/abc"; + let value = "some note"; + + let result = gBrowser.TabNotes.set(url, value); + Assert.equal(result, value, "TabNotes.set returns note value"); + + Assert.equal( + gBrowser.TabNotes.has(url), + true, + "TabNotes.has indicates that URL has a note" + ); + + Assert.equal( + gBrowser.TabNotes.get(url), + value, + "TabNotes.get returns previously set note value" + ); + + let fgWindow = await BrowserTestUtils.openNewBrowserWindow(); + Assert.equal( + fgWindow.gBrowser.TabNotes.get(url), + value, + "TabNotes.get returns previously set note value from any window" + ); + await BrowserTestUtils.closeWindow(fgWindow); + + gBrowser.TabNotes.delete(url); + + Assert.equal( + gBrowser.TabNotes.has(url), + false, + "TabNotes.has indicates that the deleted URL no longer has a note" + ); + + Assert.equal( + gBrowser.TabNotes.get(url), + undefined, + "TabNotes.get returns undefined for URL that does not have a note" + ); + + gBrowser.TabNotes.reset(); +}); + +add_task(async function tabNotesSanitizationTests() { + let url = "https://example.com/"; + let tooLongValue = "x".repeat(1500); + let correctValue = "x".repeat(1000); + + gBrowser.TabNotes.set(url, tooLongValue); + let result = gBrowser.TabNotes.get(url); + + Assert.equal(result, correctValue, "TabNotes.set truncates note length"); +});