tor-browser

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

commit bfe0525bedbac2db07d1a7f1adae45d1005ba66e
parent 0f4a3a9aac2fe2b03698c4c6a8b3451b813ed88b
Author: Adam Vandolder <avandolder@mozilla.com>
Date:   Tue,  4 Nov 2025 01:44:39 +0000

Bug 1997823 - Allow active entry lists to be shared among browsing contexts. r=dom-core,smaug

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

Diffstat:
Mdocshell/base/BrowsingContext.cpp | 2+-
Mdocshell/base/CanonicalBrowsingContext.cpp | 74++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mdocshell/base/CanonicalBrowsingContext.h | 5++++-
Adocshell/shistory/EntryList.cpp | 25+++++++++++++++++++++++++
Adocshell/shistory/EntryList.h | 37+++++++++++++++++++++++++++++++++++++
Mdocshell/shistory/moz.build | 2++
Mdocshell/shistory/nsSHistory.cpp | 41+++++++++++++++++++++++------------------
Mdocshell/shistory/nsSHistory.h | 17++++++++++++-----
8 files changed, 148 insertions(+), 55 deletions(-)

diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp @@ -1030,7 +1030,7 @@ void BrowsingContext::Detach(bool aFromIPC) { if (XRE_IsParentProcess()) { Canonical()->AddPendingDiscard(); - Canonical()->mActiveEntryList.clear(); + Canonical()->mActiveEntryList = nullptr; } auto callListeners = MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] { diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp @@ -161,7 +161,7 @@ CanonicalBrowsingContext::~CanonicalBrowsingContext() { mSessionHistory->SetBrowsingContext(nullptr); } - mActiveEntryList.clear(); + mActiveEntryList = nullptr; } /* static */ @@ -359,7 +359,7 @@ void CanonicalBrowsingContext::ReplacedBy( MOZ_ASSERT(!aNewContext->mActiveEntry); mActiveEntry.swap(aNewContext->mActiveEntry); if (Navigation::IsAPIEnabled()) { - MOZ_ASSERT(aNewContext->mActiveEntryList.isEmpty()); + MOZ_ASSERT(!aNewContext->mActiveEntryList); aNewContext->mActiveEntryList = std::move(mActiveEntryList); } @@ -486,13 +486,14 @@ SessionHistoryEntry* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() { void CanonicalBrowsingContext::SetActiveSessionHistoryEntryFromBFCache( SessionHistoryEntry* aEntry) { mActiveEntry = aEntry; - if (Navigation::IsAPIEnabled()) { + auto* activeEntries = GetActiveEntries(); + if (Navigation::IsAPIEnabled() && activeEntries) { if (StaticPrefs::dom_navigation_api_strict_enabled()) { - MOZ_DIAGNOSTIC_ASSERT(!aEntry || mActiveEntryList.contains(aEntry)); - MOZ_DIAGNOSTIC_ASSERT(aEntry || mActiveEntryList.isEmpty()); + MOZ_DIAGNOSTIC_ASSERT(!aEntry || activeEntries->contains(aEntry)); + MOZ_DIAGNOSTIC_ASSERT(aEntry || activeEntries->isEmpty()); } else { - MOZ_ASSERT(!aEntry || mActiveEntryList.contains(aEntry)); - MOZ_ASSERT(aEntry || mActiveEntryList.isEmpty()); + MOZ_ASSERT(!aEntry || activeEntries->contains(aEntry)); + MOZ_ASSERT(aEntry || activeEntries->isEmpty()); } } } @@ -510,6 +511,7 @@ void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry* aOldEntry, } nsCOMPtr<SessionHistoryEntry> newEntry = do_QueryInterface(aNewEntry); + auto* activeEntries = GetActiveEntries(); MOZ_LOG(gSHLog, LogLevel::Verbose, ("Swapping History Entries: mActiveEntry=%p, aNewEntry=%p. " "Is in list? mActiveEntry %s, aNewEntry %s. " @@ -517,9 +519,9 @@ void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry* aOldEntry, mActiveEntry.get(), aNewEntry, mActiveEntry && mActiveEntry->isInList() ? "yes" : "no", newEntry && newEntry->isInList() ? "yes" : "no", - mActiveEntryList.contains(newEntry) ? "yes" : "no")); + activeEntries->contains(newEntry) ? "yes" : "no")); if (!newEntry) { - mActiveEntryList.clear(); + activeEntries->clear(); mActiveEntry = nullptr; return; } @@ -534,7 +536,7 @@ void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry* aOldEntry, if (beforeOldEntry) { beforeOldEntry->setNext(newEntry); } else { - mActiveEntryList.insertFront(newEntry); + activeEntries->insertFront(newEntry); } } else { newEntry->setPrevious(mActiveEntry); @@ -646,9 +648,12 @@ CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad( bool sessionHistoryLoad = existingLoadingInfo && existingLoadingInfo->mLoadIsFromSessionHistory; - if (sessionHistoryLoad && !mActiveEntry && mActiveEntryList.isEmpty()) { - nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); - mActiveEntryList = shistory->ConstructContiguousEntryListFrom(entry); + if (sessionHistoryLoad && !mActiveEntry) { + auto* activeEntries = GetActiveEntries(); + if (activeEntries->isEmpty()) { + nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); + shistory->ReconstructContiguousEntryListFrom(entry); + } } MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, @@ -1172,12 +1177,13 @@ void CanonicalBrowsingContext::SessionHistoryCommit( [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); }); } + auto* activeEntries = GetActiveEntries(); MOZ_LOG(gSHLog, LogLevel::Verbose, ("SessionHistoryCommit called with mActiveEntry=%p, " "newActiveEntry=%p, " "active entry list does%s contain the active entry.", mActiveEntry.get(), newActiveEntry.get(), - mActiveEntryList.contains(mActiveEntry) ? "" : "n't")); + activeEntries->contains(mActiveEntry) ? "" : "n't")); bool addEntry = ShouldUpdateSessionHistory(aLoadType); if (IsTop()) { @@ -1211,7 +1217,7 @@ void CanonicalBrowsingContext::SessionHistoryCommit( } } if (Navigation::IsAPIEnabled() && !newActiveEntry->isInList()) { - mActiveEntryList.insertBack(newActiveEntry); + activeEntries->insertBack(newActiveEntry); } mActiveEntry = newActiveEntry; } else if (LOAD_TYPE_HAS_FLAGS( @@ -1231,7 +1237,7 @@ void CanonicalBrowsingContext::SessionHistoryCommit( } // TODO(avandolder): Can this check ever actually be false? if (!newActiveEntry->isInList()) { - mActiveEntryList.insertBack(newActiveEntry); + activeEntries->insertBack(newActiveEntry); } } mActiveEntry = newActiveEntry; @@ -1239,7 +1245,7 @@ void CanonicalBrowsingContext::SessionHistoryCommit( MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, "IsTop: No active entry, adding new entry"); if (Navigation::IsAPIEnabled() && !newActiveEntry->isInList()) { - mActiveEntryList.insertBack(newActiveEntry); + activeEntries->insertBack(newActiveEntry); } mActiveEntry = newActiveEntry; } else { @@ -1247,7 +1253,7 @@ void CanonicalBrowsingContext::SessionHistoryCommit( "IsTop: Loading from session history"); mActiveEntry = newActiveEntry; if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) { - mActiveEntryList.insertBack(mActiveEntry); + activeEntries->insertBack(mActiveEntry); } } @@ -1279,11 +1285,8 @@ void CanonicalBrowsingContext::SessionHistoryCommit( "NotTop: Loading from session history"); mActiveEntry = newActiveEntry; if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) { - mActiveEntryList.clear(); - mActiveEntryList = - shistory->ConstructContiguousEntryListFrom(mActiveEntry); + shistory->ReconstructContiguousEntryListFrom(mActiveEntry); } - shistory->InternalSetRequestedIndex(indexOfHistoryLoad); // FIXME UpdateIndex() here may update index too early (but even the // old implementation seems to have similar issues). @@ -1309,7 +1312,7 @@ void CanonicalBrowsingContext::SessionHistoryCommit( aCloneEntryChildren); if (Navigation::IsAPIEnabled()) { if (!mActiveEntry->isInList()) { - mActiveEntryList.insertBack(mActiveEntry); + activeEntries->insertBack(mActiveEntry); } mActiveEntry->setNext(newActiveEntry); } @@ -1324,7 +1327,7 @@ void CanonicalBrowsingContext::SessionHistoryCommit( "NotTop: Adding entry without an active entry"); mActiveEntry = newActiveEntry; if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) { - mActiveEntryList.insertBack(mActiveEntry); + activeEntries->insertBack(mActiveEntry); } // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite // right, but aUseRemoteSubframes should be going away. @@ -1448,21 +1451,22 @@ void CanonicalBrowsingContext::SetActiveSessionHistoryEntry( } } + auto* activeEntries = GetActiveEntries(); MOZ_LOG( gSHLog, LogLevel::Verbose, ("SetActiveSessionHistoryEntry called with oldActiveEntry=%p, " "mActiveEntry=%p, active entry list does%s contain the active entry. ", oldActiveEntry.get(), mActiveEntry.get(), - mActiveEntryList.contains(mActiveEntry) ? "" : "n't")); + activeEntries->contains(mActiveEntry) ? "" : "n't")); if (Navigation::IsAPIEnabled() && (!oldActiveEntry || oldActiveEntry->isInList())) { - RefPtr toRemove = oldActiveEntry ? oldActiveEntry->getNext() - : mActiveEntryList.getFirst(); + RefPtr toRemove = + oldActiveEntry ? oldActiveEntry->getNext() : activeEntries->getFirst(); while (toRemove) { toRemove = toRemove->removeAndGetNext(); } - mActiveEntryList.insertBack(mActiveEntry); + activeEntries->insertBack(mActiveEntry); } ResetSHEntryHasUserInteractionCache(); @@ -3714,15 +3718,25 @@ void CanonicalBrowsingContext::MaybeReconstructActiveEntryList() { auto* shistory = static_cast<nsSHistory*>(GetSessionHistory()); if (mActiveEntry && !shistory->ContainsEntry(mActiveEntry)) { - mActiveEntryList.clear(); - mActiveEntryList = shistory->ConstructContiguousEntryList(); + shistory->ReconstructContiguousEntryList(); + } +} + +EntryList* CanonicalBrowsingContext::GetActiveEntries() { + if (!mActiveEntryList) { + auto* shistory = static_cast<nsSHistory*>(GetSessionHistory()); + if (shistory) { + mActiveEntryList = shistory->EntryListFor(GetHistoryID()); + } } + return mActiveEntryList; } NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext, BrowsingContext) + tmp->mActiveEntryList = nullptr; tmp->mPermanentKey.setNull(); if (tmp->mSessionHistory) { tmp->mSessionHistory->SetBrowsingContext(nullptr); diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h @@ -10,6 +10,7 @@ #include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/MediaControlKeySource.h" #include "mozilla/dom/BrowsingContextWebProgress.h" +#include "mozilla/dom/EntryList.h" #include "mozilla/dom/FeaturePolicy.h" #include "mozilla/dom/ProcessIsolation.h" #include "mozilla/dom/Promise.h" @@ -589,6 +590,8 @@ class CanonicalBrowsingContext final : public BrowsingContext { void GetContiguousEntriesForLoad(LoadingSessionHistoryInfo& aLoadingInfo, const RefPtr<SessionHistoryEntry>& aEntry); + EntryList* GetActiveEntries(); + // XXX(farre): Store a ContentParent pointer here rather than mProcessId? // Indicates which process owns the docshell. uint64_t mProcessId; @@ -629,7 +632,7 @@ class CanonicalBrowsingContext final : public BrowsingContext { RefPtr<SessionHistoryEntry> mEntry; }; nsTArray<LoadingSessionHistoryEntry> mLoadingEntries; - LinkedList<SessionHistoryEntry> mActiveEntryList; + RefPtr<EntryList> mActiveEntryList; RefPtr<SessionHistoryEntry> mActiveEntry; RefPtr<nsSecureBrowserUI> mSecureBrowserUI; diff --git a/docshell/shistory/EntryList.cpp b/docshell/shistory/EntryList.cpp @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "EntryList.h" + +#include "nsIWeakReferenceUtils.h" +#include "nsSHistory.h" + +namespace mozilla::dom { + +EntryList::EntryList(nsSHistory* aSessionHistory, const nsID& aHistoryID) + : mSessionHistory(do_GetWeakReference(aSessionHistory)), + mHistoryID(aHistoryID) {} + +EntryList::~EntryList() { + clear(); + if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSessionHistory)) { + static_cast<nsSHistory*>(shistory.get())->RemoveEntryList(mHistoryID); + } +} + +} // namespace mozilla::dom diff --git a/docshell/shistory/EntryList.h b/docshell/shistory/EntryList.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_EntryList_h +#define mozilla_dom_EntryList_h + +#include "mozilla/LinkedList.h" +#include "mozilla/RefCounted.h" +#include "mozilla/WeakPtr.h" +#include "nsID.h" + +class nsSHistory; + +namespace mozilla::dom { + +class SessionHistoryEntry; + +class EntryList final : public LinkedList<SessionHistoryEntry>, + public RefCounted<EntryList>, + public SupportsWeakPtr { + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(EntryList) + + explicit EntryList(nsSHistory* aSessionHistory, const nsID& aHistoryID); + ~EntryList(); + + private: + nsCOMPtr<nsIWeakReference> mSessionHistory; + nsID mHistoryID; +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_EntryList_h) diff --git a/docshell/shistory/moz.build b/docshell/shistory/moz.build @@ -21,11 +21,13 @@ EXPORTS += [ EXPORTS.mozilla.dom += [ "ChildSHistory.h", + "EntryList.h", "SessionHistoryEntry.h", ] UNIFIED_SOURCES += [ "ChildSHistory.cpp", + "EntryList.cpp", "nsSHEntry.cpp", "nsSHEntryShared.cpp", "nsSHistory.cpp", diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp @@ -37,6 +37,7 @@ #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/EntryList.h" #include "mozilla/dom/Navigation.h" #include "mozilla/dom/RemoteWebProgressRequest.h" #include "mozilla/dom/WindowGlobalParent.h" @@ -2500,35 +2501,25 @@ mozilla::dom::SessionHistoryEntry* nsSHistory::FindAdjacentContiguousEntryFor( return nullptr; } -LinkedList<SessionHistoryEntry> nsSHistory::ConstructContiguousEntryListFrom( +void nsSHistory::ReconstructContiguousEntryListFrom( SessionHistoryEntry* aEntry) { - if (aEntry->isInList()) { - aEntry->remove(); - } - - LinkedList<SessionHistoryEntry> entryList; - entryList.insertBack(aEntry); + RefPtr entryList = EntryListFor(aEntry->DocshellID()); + entryList->clear(); + entryList->insertBack(aEntry); for (auto* entry = aEntry; (entry = FindAdjacentContiguousEntryFor(entry, -1));) { - if (entry->isInList()) { - entry->remove(); - } - entryList.insertFront(entry); + entryList->insertFront(entry); } for (auto* entry = aEntry; (entry = FindAdjacentContiguousEntryFor(entry, 1));) { - if (entry->isInList()) { - entry->remove(); - } - entryList.insertBack(entry); + entryList->insertBack(entry); } - return entryList; } -LinkedList<SessionHistoryEntry> nsSHistory::ConstructContiguousEntryList() { +void nsSHistory::ReconstructContiguousEntryList() { MOZ_ASSERT(mIndex >= 0 && mIndex < Length()); nsCOMPtr currentEntry = mEntries[mIndex]; - return ConstructContiguousEntryListFrom( + ReconstructContiguousEntryListFrom( static_cast<SessionHistoryEntry*>(currentEntry.get())); } @@ -2747,3 +2738,17 @@ bool nsSHistory::ContainsEntry(nsISHEntry* aEntry) { nsCOMPtr rootEntry = GetRootSHEntry(aEntry); return GetIndexOfEntry(rootEntry) != -1; } + +already_AddRefed<EntryList> nsSHistory::EntryListFor(const nsID& aID) { + return mEntryLists.WithEntryHandle( + aID, [self = RefPtr{this}, aID](auto&& entry) { + if (entry && *entry) { + return do_AddRef(entry->get()); + } + RefPtr entryList = MakeRefPtr<EntryList>(self, aID); + entry.InsertOrUpdate(entryList); + return entryList.forget(); + }); +} + +void nsSHistory::RemoveEntryList(const nsID& aID) { mEntryLists.Remove(aID); } diff --git a/docshell/shistory/nsSHistory.h b/docshell/shistory/nsSHistory.h @@ -27,8 +27,9 @@ class nsISHEntry; namespace mozilla { namespace dom { +class EntryList; class LoadSHEntryResult; -} +} // namespace dom } // namespace mozilla class nsSHistory : public mozilla::LinkedListElement<nsSHistory>, @@ -219,10 +220,11 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>, mozilla::dom::SessionHistoryEntry* FindAdjacentContiguousEntryFor( mozilla::dom::SessionHistoryEntry* aEntry, int32_t aSearchDirection); - mozilla::LinkedList<mozilla::dom::SessionHistoryEntry> - ConstructContiguousEntryListFrom(mozilla::dom::SessionHistoryEntry* aEntry); - mozilla::LinkedList<mozilla::dom::SessionHistoryEntry> - ConstructContiguousEntryList(); + void ReconstructContiguousEntryListFrom( + mozilla::dom::SessionHistoryEntry* aEntry); + void ReconstructContiguousEntryList(); + already_AddRefed<mozilla::dom::EntryList> EntryListFor(const nsID& aID); + void RemoveEntryList(const nsID& aID); bool ContainsEntry(nsISHEntry* aEntry); @@ -320,6 +322,11 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>, // update the epoch via a runnable on each ::Go (including AsyncGo). uint64_t mEpoch = 0; mozilla::Maybe<mozilla::dom::ContentParentId> mEpochParentId; + + // Session history entries grouped by DocshellID, which are deduplicated by + // SessionHistoryEntry ID. + nsTHashMap<nsIDHashKey, mozilla::WeakPtr<mozilla::dom::EntryList>> + mEntryLists; }; // CallerWillNotifyHistoryIndexAndLengthChanges is used to prevent