tor-browser

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

commit 9ee356d4f4b015f72f6e4568e5cc741aa74809a6
parent d156ec1e68675ed4b800759cf9610ebb3af92a6d
Author: Narcis Beleuzu <nbeleuzu@mozilla.com>
Date:   Tue,  7 Oct 2025 04:08:52 +0300

Revert "Bug 1886894 - add DomainMatchingMode enum so browser:purge-sessionStorage does exact domain match when domain is provided. r=robwu,dom-storage-reviewers,edenchuang" for causing bc failure on /browser_sessionStorage.js

This reverts commit 32759256a6cfd63bfdcda0574f3cf48d4a02ea91.

This reverts commit 642862cc005336f5415ac47c934c2b581f172258.

This reverts commit a315c687a53916f96d862e83021db8543395b2cc.

Diffstat:
Mdom/storage/PBackgroundSessionStorageManager.ipdl | 2+-
Mdom/storage/SessionStorageManager.cpp | 45+++++++++------------------------------------
Mdom/storage/SessionStorageManager.h | 25++++++-------------------
Mdom/storage/StorageIPC.cpp | 6++----
Mdom/storage/StorageIPC.h | 4++--
Mdom/storage/StorageObserver.cpp | 8+++-----
Mtoolkit/components/extensions/parent/ext-browsingData.js | 21---------------------
Mtoolkit/components/extensions/test/mochitest/mochitest-common.toml | 2--
Dtoolkit/components/extensions/test/mochitest/test_ext_browsingData_sessionStorage.html | 263-------------------------------------------------------------------------------
9 files changed, 23 insertions(+), 353 deletions(-)

diff --git a/dom/storage/PBackgroundSessionStorageManager.ipdl b/dom/storage/PBackgroundSessionStorageManager.ipdl @@ -23,7 +23,7 @@ sync protocol PBackgroundSessionStorageManager parent: async PBackgroundSessionStorageCache(PrincipalInfo aPrincipalInfo, nsCString aOriginKey); - async ClearStorages(OriginAttributesPattern aPattern, nsCString aOriginScope, uint32_t aMode); + async ClearStorages(OriginAttributesPattern aPattern, nsCString aOriginScope); async DeleteMe(); diff --git a/dom/storage/SessionStorageManager.cpp b/dom/storage/SessionStorageManager.cpp @@ -34,21 +34,6 @@ namespace mozilla::dom { using namespace StorageUtils; -static bool ExactDomainMatch(const nsACString& aOriginKey, - const nsACString& aOriginScope) { - // aOriginScope is reversed domain with trailing dot. - // e.g: example.com => moc.elpmaxe. (see StorageUtils.cpp) - // aOriginKey is reversed domain with trailing dot, plus ":", - // scheme, and optional port. e.g: moc.elpmaxe.:http:80 (see - // https://searchfox.org/firefox-main/rev/987f566373ea82403c5c1235b219bd9e7d56a4aa/caps/BasePrincipal.cpp#1539) - // To check if it is an exact match, we need to ensure that aOriginKey starts - // with aOriginScope and the first character immediately following the - // matching part is ":". i.e: Domain part of aOriginKey is identical to - // aOriginScope. - return StringBeginsWith(aOriginKey, aOriginScope) && - aOriginKey.CharAt(aOriginScope.Length()) == ':'; -} - // Parent process, background thread hashmap that stores top context id and // manager pair. static StaticAutoPtr< @@ -166,10 +151,7 @@ bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs, } void SessionStorageManagerBase::ClearStoragesInternal( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - DomainMatchingMode aMode) { - const bool isExactMatch = aMode == DomainMatchingMode::EXACT_MATCH; - + const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { for (const auto& oaEntry : mOATable) { OriginAttributes oa; DebugOnly<bool> ok = oa.PopulateFromSuffix(oaEntry.GetKey()); @@ -182,10 +164,7 @@ void SessionStorageManagerBase::ClearStoragesInternal( OriginKeyHashTable* table = oaEntry.GetWeak(); for (const auto& originKeyEntry : *table) { if (aOriginScope.IsEmpty() || - (!isExactMatch && - StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) || - (isExactMatch && - ExactDomainMatch(originKeyEntry.GetKey(), aOriginScope))) { + StringBeginsWith(originKeyEntry.GetKey(), aOriginScope)) { const auto cache = originKeyEntry.GetData()->mCache; cache->Clear(false); cache->ResetWriteInfos(); @@ -647,19 +626,17 @@ SessionStorageManager::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage, } void SessionStorageManager::ClearStorages( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - DomainMatchingMode aMode) { + const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { if (CanLoadData()) { nsresult rv = EnsureManager(); if (NS_WARN_IF(NS_FAILED(rv))) { return; } - mActor->SendClearStorages(aPattern, nsCString(aOriginScope), - static_cast<uint32_t>(aMode)); + mActor->SendClearStorages(aPattern, nsCString(aOriginScope)); } - ClearStoragesInternal(aPattern, aOriginScope, aMode); + ClearStoragesInternal(aPattern, aOriginScope); } nsresult SessionStorageManager::Observe( @@ -685,12 +662,9 @@ nsresult SessionStorageManager::Observe( } // Clear everything (including so and pb data) from caches and database - // for the given domain. + // for the given domain and subdomains. if (!strcmp(aTopic, "browser:purge-sessionStorage")) { - // Only pass in EXACT_MATCH mode when browser:purge-sessionStorage event is - // triggered. When EXACT_MATCH mode is passed, ClearStorages only clears - // data for a given domain and does not affect the subdomain(s). - ClearStorages(pattern, aOriginScope, DomainMatchingMode::EXACT_MATCH); + ClearStorages(pattern, aOriginScope); return NS_OK; } @@ -919,11 +893,10 @@ void BackgroundSessionStorageManager::UpdateData( } void BackgroundSessionStorageManager::ClearStorages( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - DomainMatchingMode aMode) { + const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { MOZ_ASSERT(XRE_IsParentProcess()); ::mozilla::ipc::AssertIsOnBackgroundThread(); - ClearStoragesInternal(aPattern, aOriginScope, aMode); + ClearStoragesInternal(aPattern, aOriginScope); } void BackgroundSessionStorageManager::ClearStoragesForOrigin( diff --git a/dom/storage/SessionStorageManager.h b/dom/storage/SessionStorageManager.h @@ -43,16 +43,6 @@ bool RecvLoadSessionStorageData( bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs, const nsACString& aOriginKey); -// DomainMatchingMode is used to allow ClearStorages to mimic one of -// the LSNG behaviours - exact domain match. -// When ClearStorages is invoked with EXACT_MATCH, it clears only the -// data for a given domain, and would not affect any subdomains. -// Currently EXACT_MATCH is only passed when browser:purge-sessionStorage event -// is triggered. By default, ClearStorages is called with PREFIX_MATCH, which -// clears data for a given domain and all the subdomains. This ensures that the -// behaviour of other events that call ClearStorages remain unchanged. -enum class DomainMatchingMode { PREFIX_MATCH, EXACT_MATCH }; - class BrowsingContext; class ContentParent; class SSSetItemInfo; @@ -100,9 +90,8 @@ class SessionStorageManagerBase { FlippedOnce<false> mLoaded; }; - void ClearStoragesInternal( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - DomainMatchingMode aMode = DomainMatchingMode::PREFIX_MATCH); + void ClearStoragesInternal(const OriginAttributesPattern& aPattern, + const nsACString& aOriginScope); void ClearStoragesForOriginInternal(const nsACString& aOriginAttrs, const nsACString& aOriginKey); @@ -164,9 +153,8 @@ class SessionStorageManager final : public SessionStorageManagerBase, SessionStorageCache* aCloneFrom, RefPtr<SessionStorageCache>* aRetVal); - void ClearStorages( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - DomainMatchingMode aMode = DomainMatchingMode::PREFIX_MATCH); + void ClearStorages(const OriginAttributesPattern& aPattern, + const nsACString& aOriginScope); SessionStorageCacheChild* EnsureCache(nsIPrincipal& aPrincipal, const nsACString& aOriginKey, @@ -225,9 +213,8 @@ class BackgroundSessionStorageManager final : public SessionStorageManagerBase { void UpdateData(const nsACString& aOriginAttrs, const nsACString& aOriginKey, const nsTArray<SSSetItemInfo>& aData); - void ClearStorages( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - DomainMatchingMode aMode = DomainMatchingMode::PREFIX_MATCH); + void ClearStorages(const OriginAttributesPattern& aPattern, + const nsACString& aOriginScope); void ClearStoragesForOrigin(const nsACString& aOriginAttrs, const nsACString& aOriginKey); diff --git a/dom/storage/StorageIPC.cpp b/dom/storage/StorageIPC.cpp @@ -1452,11 +1452,9 @@ BackgroundSessionStorageManager* SessionStorageManagerParent::GetManager() } mozilla::ipc::IPCResult SessionStorageManagerParent::RecvClearStorages( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - const uint32_t& aMode) { + const OriginAttributesPattern& aPattern, const nsACString& aOriginScope) { ::mozilla::ipc::AssertIsOnBackgroundThread(); - mBackgroundManager->ClearStorages(aPattern, aOriginScope, - static_cast<DomainMatchingMode>(aMode)); + mBackgroundManager->ClearStorages(aPattern, aOriginScope); return IPC_OK(); } diff --git a/dom/storage/StorageIPC.h b/dom/storage/StorageIPC.h @@ -561,8 +561,8 @@ class SessionStorageManagerParent final BackgroundSessionStorageManager* GetManager() const; mozilla::ipc::IPCResult RecvClearStorages( - const OriginAttributesPattern& aPattern, const nsACString& aOriginScope, - const uint32_t& aMode) override; + const OriginAttributesPattern& aPattern, + const nsACString& aOriginScope) override; private: ~SessionStorageManagerParent(); diff --git a/dom/storage/StorageObserver.cpp b/dom/storage/StorageObserver.cpp @@ -376,11 +376,9 @@ StorageObserver::Observe(nsISupports* aSubject, const char* aTopic, NS_ENSURE_SUCCESS(rv, rv); nsCString originScope; - if (!schemelessSite.IsEmpty()) { - rv = GetOriginScope(NS_ConvertUTF8toUTF16(schemelessSite).get(), - originScope); - NS_ENSURE_SUCCESS(rv, rv); - } + rv = GetOriginScope(NS_ConvertUTF8toUTF16(schemelessSite).get(), + originScope); + NS_ENSURE_SUCCESS(rv, rv); Notify(aTopic, patternJSON, originScope); } else if (aData) { diff --git a/toolkit/components/extensions/parent/ext-browsingData.js b/toolkit/components/extensions/parent/ext-browsingData.js @@ -210,24 +210,6 @@ const clearLocalStorage = async function (options) { message: "Firefox does not support clearing localStorage with 'since'.", }); } - const notifySessionStorage = function (hostname, cookieStoreId) { - if (hostname || cookieStoreId) { - const entry = Cc["@mozilla.org/clear-by-site-entry;1"].createInstance( - Ci.nsIClearBySiteEntry - ); - - entry.schemelessSite = hostname || ""; - entry.patternJSON = cookieStoreId - ? JSON.stringify( - getOriginAttributesPatternForCookieStoreId(cookieStoreId) - ) - : ""; - - Services.obs.notifyObservers(entry, "browser:purge-sessionStorage"); - } else { - Services.obs.notifyObservers(null, "browser:purge-sessionStorage"); - } - }; // The legacy LocalStorage implementation that will eventually be removed // depends on this observer notification. Some other subsystems like @@ -240,12 +222,9 @@ const clearLocalStorage = async function (options) { "extension:purge-localStorage", hostname ); - - notifySessionStorage(hostname, options.cookieStoreId); } } else { Services.obs.notifyObservers(null, "extension:purge-localStorage"); - notifySessionStorage(null, options.cookieStoreId); } if (Services.domStorageManager.nextGenLocalStorageEnabled) { diff --git a/toolkit/components/extensions/test/mochitest/mochitest-common.toml b/toolkit/components/extensions/test/mochitest/mochitest-common.toml @@ -159,8 +159,6 @@ skip-if = [ "http2", ] -["test_ext_browsingData_sessionStorage.html"] - ["test_ext_browsingData_settings.html"] ["test_ext_canvas_resistFingerprinting.html"] diff --git a/toolkit/components/extensions/test/mochitest/test_ext_browsingData_sessionStorage.html b/toolkit/components/extensions/test/mochitest/test_ext_browsingData_sessionStorage.html @@ -1,263 +0,0 @@ -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<!DOCTYPE HTML> -<html> -<head> - <title>Test browsingData.removeLocalStorage also removes sessionStorage</title> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script> - <script src="head.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> -</head> -<body> - -<script type="text/javascript"> -"use strict"; - -add_task(async function setup() { - // make sure userContext is enabled. - return SpecialPowers.pushPrefEnv({ - set: [["privacy.userContext.enabled", true]], - }); -}); - -add_task(async function testLocalStorage() { - async function background() { - function waitForTabs() { - return new Promise(resolve => { - let tabs = {}; - - let listener = async (msg, { tab }) => { - if (msg !== "content-script-ready") { - return; - } - - tabs[tab.url] = tab; - if (Object.keys(tabs).length == 3) { - browser.runtime.onMessage.removeListener(listener); - resolve(tabs); - } - }; - browser.runtime.onMessage.addListener(listener); - }); - } - - function sendMessageToTabs(tabs, message) { - return Promise.all( - Object.values(tabs).map(tab => { - return browser.tabs.sendMessage(tab.id, message); - }) - ); - } - - let tabs = await waitForTabs(); - - browser.test.assertRejects( - browser.browsingData.removeLocalStorage({ since: Date.now() }), - "Firefox does not support clearing localStorage with 'since'.", - "Expected error received when using unimplemented parameter 'since'." - ); - - await sendMessageToTabs(tabs, "resetSessionStorage"); - - await browser.browsingData.removeLocalStorage({ - hostnames: ["example.com"], - }); - await browser.tabs.sendMessage(tabs["https://example.com/"].id, "checkSessionStorageCleared"); - await browser.tabs.sendMessage(tabs["https://example.net/"].id, "checkSessionStorageSet"); - await browser.tabs.sendMessage(tabs["https://test1.example.com/"].id, "checkSessionStorageSet"); - - - await sendMessageToTabs(tabs, "resetSessionStorage"); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - await browser.browsingData.removeLocalStorage({}); - await sendMessageToTabs(tabs, "checkSessionStorageCleared"); - - await sendMessageToTabs(tabs, "resetSessionStorage"); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - await browser.browsingData.remove({}, { localStorage: true }); - await sendMessageToTabs(tabs, "checkSessionStorageCleared"); - - - // Can only delete cookieStoreId with LSNG enabled. - if (SpecialPowers.Services.domStorageManager.nextGenLocalStorageEnabled) { - await sendMessageToTabs(tabs, "resetSessionStorage"); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - // Now passing only the cookieStoreId should clear all sessionStorage for that container - await browser.browsingData.removeLocalStorage({ - cookieStoreId: "firefox-container-1", - }); - await browser.tabs.sendMessage(tabs["https://example.com/"].id, "checkSessionStorageSet"); - await browser.tabs.sendMessage(tabs["https://example.net/"].id, "checkSessionStorageSet"); - - // TODO: containers support is lacking on GeckoView (Bug 1643740) - if (!navigator.userAgent.includes("Android")) { - await browser.tabs.sendMessage(tabs["https://test1.example.com/"].id, "checkSessionStorageCleared"); - } - - await sendMessageToTabs(tabs, "resetSessionStorage"); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - - // Happy path: Passing both hostname and cookieStoreId would clear the sessionStorage. - await browser.browsingData.removeLocalStorage({ - cookieStoreId: "firefox-container-1", - hostnames: ["test1.example.com"], - }); - - await browser.tabs.sendMessage(tabs["https://example.com/"].id, "checkSessionStorageSet"); - await browser.tabs.sendMessage(tabs["https://example.net/"].id, "checkSessionStorageSet"); - - // TODO: containers support is lacking on GeckoView (Bug 1643740) - if (!navigator.userAgent.includes("Android")) { - await browser.tabs.sendMessage(tabs["https://test1.example.com/"].id, "checkSessionStorageCleared"); - } - await sendMessageToTabs(tabs, "resetSessionStorage"); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - // Hostname doesn't match, so nothing cleared. - await browser.browsingData.removeLocalStorage({ - cookieStoreId: "firefox-container-1", - hostnames: ["example.net"], - }); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - - await sendMessageToTabs(tabs, "resetSessionStorage"); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - // Deleting private browsing mode data is silently ignored. - await browser.browsingData.removeLocalStorage({ - cookieStoreId: "firefox-private", - }); - await sendMessageToTabs(tabs, "checkSessionStorageSet"); - } else { - await browser.test.assertRejects( - browser.browsingData.removeLocalStorage({ - cookieStoreId: "firefox-container-1", - }), - "Firefox does not support clearing localStorage with 'cookieStoreId'.", - "removeLocalStorage with cookieStoreId requires LSNG" - ); - } - - await browser.browsingData.removeLocalStorage({}); - - browser.test.notifyPass("done"); - } - - function contentScript() { - browser.runtime.onMessage.addListener(async msg => { - if (msg === "resetSessionStorage") { - sessionStorage.clear(); - sessionStorage.setItem("key", "value"); - } else if (msg === "checkSessionStorageSet") { - browser.test.assertEq( - "value", - sessionStorage.getItem("key"), - `checkSessionStorageSet: ${location.href}` - ); - } else if (msg === "checkSessionStorageCleared") { - browser.test.assertEq( - null, - sessionStorage.getItem("key"), - `checkSessionStorageCleared: ${location.href}` - ); - } - }); - browser.runtime.sendMessage("content-script-ready"); - } - - // This extension is responsible for opening tabs with a specified - // cookieStoreId, we use a separate extension to make sure that browsingData - // works without the cookies permission. - let openTabsExtension = ExtensionTestUtils.loadExtension({ - useAddonManager: "permanent", - manifest: { - name: "Open tabs", - browser_specific_settings: { gecko: { id: "open-tabs@tests.mozilla.org" }, }, - permissions: ["cookies"], - }, - async background() { - const TABS = [ - { url: "https://example.com" }, - { url: "https://example.net" }, - { - url: "https://test1.example.com", - cookieStoreId: 'firefox-container-1', - }, - ]; - - function awaitLoad(tabId) { - return new Promise(resolve => { - browser.tabs.onUpdated.addListener(function listener(tabId_, changed) { - if (tabId == tabId_ && changed.status == "complete") { - browser.tabs.onUpdated.removeListener(listener); - resolve(); - } - }); - }); - } - - let tabs = []; - let loaded = []; - for (let options of TABS) { - let tab = await browser.tabs.create(options); - loaded.push(awaitLoad(tab.id)); - tabs.push(tab); - } - - await Promise.all(loaded); - - browser.test.onMessage.addListener(async msg => { - if (msg === "cleanup") { - const tabIds = tabs.map(tab => tab.id); - let removedTabs = 0; - browser.tabs.onRemoved.addListener(tabId => { - browser.test.log(`Removing tab ${tabId}.`); - if (tabIds.includes(tabId)) { - removedTabs++; - if (removedTabs == tabIds.length) { - browser.test.sendMessage("done"); - } - } - }); - await browser.tabs.remove(tabIds); - } - }); - } - }); - - let extension = ExtensionTestUtils.loadExtension({ - useAddonManager: "permanent", - background, - manifest: { - name: "Test Extension", - browser_specific_settings: { gecko: { id: "localStorage@tests.mozilla.org" } }, - permissions: ["browsingData", "tabs"], - content_scripts: [ - { - matches: [ - "https://example.com/", - "https://example.net/", - "https://test1.example.com/", - ], - js: ["content-script.js"], - run_at: "document_end", - }, - ], - }, - files: { - "content-script.js": contentScript, - }, - }); - - await openTabsExtension.startup(); - - await extension.startup(); - await extension.awaitFinish("done"); - await extension.unload(); - - await openTabsExtension.sendMessage("cleanup"); - await openTabsExtension.awaitMessage("done"); - await openTabsExtension.unload(); -}); -</script> -</body> -</html>