From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../sessionstore/BrowserSessionStore.cpp | 292 +++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 toolkit/components/sessionstore/BrowserSessionStore.cpp (limited to 'toolkit/components/sessionstore/BrowserSessionStore.cpp') diff --git a/toolkit/components/sessionstore/BrowserSessionStore.cpp b/toolkit/components/sessionstore/BrowserSessionStore.cpp new file mode 100644 index 0000000000..f4da9646b2 --- /dev/null +++ b/toolkit/components/sessionstore/BrowserSessionStore.cpp @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 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 "mozilla/dom/BrowserSessionStore.h" +#include +#include +#include + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Assertions.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/IntegerRange.h" +#include "mozilla/RefPtr.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPtr.h" + +#include "mozilla/dom/BrowserSessionStoreBinding.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/dom/SessionStoreFormData.h" +#include "mozilla/dom/SessionStoreScrollData.h" +#include "mozilla/dom/WindowGlobalParent.h" + +#include "nsTHashMap.h" +#include "nsHashtablesFwd.h" + +#include "js/RootingAPI.h" + +using namespace mozilla; +using namespace mozilla::dom; + +static StaticAutoPtr> + sSessionStore; + +NS_IMPL_CYCLE_COLLECTION(BrowserSessionStore, mBrowsingContext, mFormData, + mScrollData) + +/* static */ +already_AddRefed BrowserSessionStore::GetOrCreate( + CanonicalBrowsingContext* aBrowsingContext) { + if (!aBrowsingContext->IsTop()) { + return nullptr; + } + + if (!sSessionStore) { + sSessionStore = new nsTHashMap(); + ClearOnShutdown(&sSessionStore); + } + + return do_AddRef(sSessionStore->LookupOrInsertWith( + aBrowsingContext->Id(), + [&] { return new BrowserSessionStore(aBrowsingContext); })); +} + +BrowserSessionStore::BrowserSessionStore( + CanonicalBrowsingContext* aBrowsingContext) + : mBrowsingContext(aBrowsingContext) {} + +SessionStoreFormData* BrowserSessionStore::GetFormdata() { return mFormData; } + +SessionStoreScrollData* BrowserSessionStore::GetScroll() { return mScrollData; } + +static bool ShouldUpdateSessionStore(CanonicalBrowsingContext* aBrowsingContext, + uint32_t aEpoch) { + if (!aBrowsingContext) { + return false; + } + + if (aBrowsingContext->Top()->GetSessionStoreEpoch() != aEpoch) { + return false; + } + + if (aBrowsingContext->IsReplaced()) { + return false; + } + + if (aBrowsingContext->IsDynamic()) { + return false; + } + + return true; +} + +// With GetOrCreate we can create either of the weak fields: +// WeakPtr mFormdata; +// WeakPtr mScroll; +// in CanonicalBrowsingContext. If one already exists, then we return that. +template & (CanonicalBrowsingContext::*GetWeakRef)()> +static already_AddRefed GetOrCreateEntry( + CanonicalBrowsingContext* aBrowsingContext) { + typename T::LocationType& location = (aBrowsingContext->*GetWeakRef)(); + RefPtr entry = location.get(); + if (!entry) { + entry = MakeRefPtr(); + location = entry; + } + + return entry.forget(); +} + +// With InsertEntry we can insert an entry in the session store data tree in +// either of the weak fields: +// WeakPtr mFormdata; +// WeakPtr mScroll; +// in CanonicalBrowsingContext. If an entry is inserted where there is no parent +// entry, a spine of entries will be created until one is found, or we reach the +// top browsing context. +template +void InsertEntry(BrowsingContext* aBrowsingContext, T* aParent, T* aUpdate) { + int32_t offset = aBrowsingContext->ChildOffset(); + if (offset < 0) { + return; + } + + aParent->ClearCachedChildren(); + + auto& children = aParent->Children(); + + children.EnsureLengthAtLeast(offset + 1); + if (children[offset] && !aBrowsingContext->Children().IsEmpty()) { + children[offset]->ClearCachedChildren(); + aUpdate->ClearCachedChildren(); + } + + children[offset] = aUpdate; +} + +// With RemoveEntry we can remove an entry in the session store data tree in +// either of the weak fields: +// WeakPtr mFormdata; +// WeakPtr mScroll; +// in CanonicalBrowsingContext. If an entry is removed, where its parent doesn't +// contain data, we'll remove the parent and repeat until we either find an +// entry with data or reach the top browsing context. +template +void RemoveEntry(BrowsingContext* aBrowsingContext, T* aParent) { + int32_t offset = aBrowsingContext->ChildOffset(); + if (offset < 0) { + return; + } + + if (!aParent) { + return; + } + + aParent->ClearCachedChildren(); + + auto& children = aParent->Children(); + size_t length = children.Length(); + if (children.Length() <= static_cast(offset)) { + // The children array doesn't extend to offset. + return; + } + + if (static_cast(offset) < length - 1) { + // offset is before the last item in the children array. + children[offset] = nullptr; + return; + } + + // offset is the last item, find the first non-null item before it + // and remove anything after that item. + while (offset > 0 && !children[offset - 1]) { + --offset; + } + + children.TruncateLength(offset); +} + +// With UpdateSessionStoreField we can update an entry in the session store +// data tree in either of the weak fields: +// WeakPtr mFormdata; +// WeakPtr mScroll; +// in CanonicalBrowsingContext. UpdateSessionStoreField uses the above +// functions, `GetOrCreateEntry`, `InsertEntry` and `RemoveEntry` to operate on +// the weak fields. We return the top-level entry attached to the top browsing +// context through `aEntry`. If the entire browsing context tree contains no +// session store data this will be set to nullptr. +template & (CanonicalBrowsingContext::*GetWeakRef)()> +void UpdateSessionStoreField(CanonicalBrowsingContext* aBrowsingContext, + const typename T::CollectedType& aUpdate, + T** aEntry) { + RefPtr currentEntry; + + if (T::HasData(aUpdate)) { + currentEntry = GetOrCreateEntry(aBrowsingContext); + currentEntry->Update(aUpdate); + + CanonicalBrowsingContext* currentBrowsingContext = aBrowsingContext; + while (CanonicalBrowsingContext* parent = + currentBrowsingContext->GetParent()) { + WeakPtr& parentEntry = (parent->*GetWeakRef)(); + if (parentEntry) { + InsertEntry(aBrowsingContext, parentEntry.get(), currentEntry.get()); + break; + } + + RefPtr entry = GetOrCreateEntry(parent); + InsertEntry(currentBrowsingContext, entry.get(), currentEntry.get()); + + currentEntry = entry; + currentBrowsingContext = parent; + } + + currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get(); + } else if ((currentEntry = (aBrowsingContext->*GetWeakRef)())) { + currentEntry->Update(aUpdate); + + CanonicalBrowsingContext* currentBrowsingContext = aBrowsingContext; + while (CanonicalBrowsingContext* parent = + currentBrowsingContext->GetParent()) { + if (!currentEntry || !currentEntry->IsEmpty()) { + break; + } + + T* parentEntry = (parent->*GetWeakRef)().get(); + RemoveEntry(currentBrowsingContext, parentEntry); + + currentEntry = parentEntry; + currentBrowsingContext = parent; + } + + if (currentEntry && currentEntry->IsEmpty()) { + currentEntry = nullptr; + } else { + currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get(); + } + } else { + currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get(); + } + + *aEntry = currentEntry.forget().take(); +} + +void BrowserSessionStore::UpdateSessionStore( + CanonicalBrowsingContext* aBrowsingContext, + const Maybe& aFormData, + const Maybe& aScrollPosition, uint32_t aEpoch) { + if (!aFormData && !aScrollPosition) { + return; + } + + if (!ShouldUpdateSessionStore(aBrowsingContext, aEpoch)) { + return; + } + + if (aFormData) { + UpdateSessionStoreField< + SessionStoreFormData, + &CanonicalBrowsingContext::GetSessionStoreFormDataRef>( + aBrowsingContext, *aFormData, getter_AddRefs(mFormData)); + } + + if (aScrollPosition) { + UpdateSessionStoreField< + SessionStoreScrollData, + &CanonicalBrowsingContext::GetSessionStoreScrollDataRef>( + aBrowsingContext, *aScrollPosition, getter_AddRefs(mScrollData)); + } +} + +void BrowserSessionStore::RemoveSessionStore( + CanonicalBrowsingContext* aBrowsingContext) { + if (!aBrowsingContext) { + return; + } + + CanonicalBrowsingContext* parentContext = aBrowsingContext->GetParent(); + + if (parentContext) { + RemoveEntry(aBrowsingContext, + parentContext->GetSessionStoreFormDataRef().get()); + + RemoveEntry(aBrowsingContext, + parentContext->GetSessionStoreScrollDataRef().get()); + + return; + } + + if (aBrowsingContext->IsTop()) { + mFormData = nullptr; + mScrollData = nullptr; + } +} + +BrowserSessionStore::~BrowserSessionStore() { + if (sSessionStore) { + sSessionStore->Remove(mBrowsingContext->Id()); + } +} -- cgit v1.2.3