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:
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>