Skip to content

Commit

Permalink
Bug 1546759 - Change nsSHistory::WalkHistoryEntries to walk browsing …
Browse files Browse the repository at this point in the history
…context tree instead of doc shell tree, r=peterv,nika

WalkHistoryEntries function gets called by nsSHistory::CloneAndReplaceChild
and nsSHistory::SetChildHistoryEntry recursively, so those have to be moved
into the parent process. This eliminates many sync IPC calls.

To facilitate transition to a new session history design,
we are mirroring mOSHE and mLSHE SH entries from docshell to browsing context.
Whenever we update those entries in docshell, we will also update those in BC,
and vice versa.

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

--HG--
extra : moz-landing-system : lando
  • Loading branch information
annygakh committed Mar 10, 2020
1 parent 9542f19 commit 5986a2d
Show file tree
Hide file tree
Showing 24 changed files with 823 additions and 215 deletions.
24 changes: 24 additions & 0 deletions docshell/base/CanonicalBrowsingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsISHistory.h"
#include "nsISHEntry.h"

namespace mozilla {
namespace dom {
Expand Down Expand Up @@ -110,6 +111,25 @@ class CanonicalBrowsingContext final : public BrowsingContext {
// if the top-level browsing context has been discarded.
MediaController* GetMediaController();

bool HasHistoryEntry(nsISHEntry* aEntry) const {
return aEntry && (aEntry == mOSHE || aEntry == mLSHE);
}

void UpdateSHEntries(nsISHEntry* aNewLSHE, nsISHEntry* aNewOSHE) {
mLSHE = aNewLSHE;
mOSHE = aNewOSHE;
}

void SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry) {
if (aOldEntry == mOSHE) {
mOSHE = aNewEntry;
}

if (aOldEntry == mLSHE) {
mLSHE = aNewEntry;
}
}

protected:
// Called when the browsing context is being discarded.
void CanonicalDiscard();
Expand Down Expand Up @@ -171,6 +191,10 @@ class CanonicalBrowsingContext final : public BrowsingContext {
// browsing context tree, so it would only exist in the top level browsing
// context.
RefPtr<MediaController> mTabMediaController;

// These are being mirrored from docshell
nsCOMPtr<nsISHEntry> mOSHE;
nsCOMPtr<nsISHEntry> mLSHE;
};

} // namespace dom
Expand Down
177 changes: 68 additions & 109 deletions docshell/base/nsDocShell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "mozilla/StaticPrefs_extensions.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/Telemetry.h"
Expand Down Expand Up @@ -71,8 +72,8 @@
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/LoadURIOptionsBinding.h"
#include "mozilla/dom/JSWindowActorChild.h"

#include "mozilla/net/DocumentChannel.h"
#include "nsSHEntry.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "ReferrerInfo.h"
Expand Down Expand Up @@ -194,6 +195,7 @@
#include "nsRefreshTimer.h"
#include "nsSandboxFlags.h"
#include "nsSHistory.h"
#include "SHEntryChild.h"
#include "nsStructuredCloneContainer.h"
#include "nsSubDocumentFrame.h"
#include "nsView.h"
Expand Down Expand Up @@ -3192,35 +3194,8 @@ nsresult nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
bool aCloneChildren) {
nsresult rv = NS_OK;
if (mSessionHistory) {
/* You are currently in the rootDocShell.
* You will get here when a subframe has a new url
* to load and you have walked up the tree all the
* way to the top to clone the current SHEntry hierarchy
* and replace the subframe where a new url was loaded with
* a new entry.
*/
nsCOMPtr<nsISHEntry> currentHE;
int32_t index = mSessionHistory->Index();
if (index < 0) {
return NS_ERROR_FAILURE;
}

rv = mSessionHistory->LegacySHistory()->GetEntryAtIndex(
index, getter_AddRefs(currentHE));
NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);

nsCOMPtr<nsISHEntry> currentEntry(currentHE);
if (currentEntry) {
nsCOMPtr<nsISHEntry> nextEntry;
uint32_t cloneID = aCloneRef->GetID();
rv = nsSHistory::CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
aCloneChildren,
getter_AddRefs(nextEntry));

if (NS_SUCCEEDED(rv)) {
rv = mSessionHistory->LegacySHistory()->AddEntry(nextEntry, true);
}
}
rv = mSessionHistory->LegacySHistory()->AddChildSHEntryHelper(
aCloneRef, aNewEntry, mBrowsingContext, aCloneChildren);
} else {
/* Just pass this along */
nsCOMPtr<nsIDocShell> parent =
Expand Down Expand Up @@ -4258,8 +4233,7 @@ nsDocShell::Stop(uint32_t aStopFlags) {
if (mLoadType == LOAD_ERROR_PAGE) {
if (mLSHE) {
// Since error page loads never unset mLSHE, do so now
SetHistoryEntry(&mOSHE, mLSHE);
SetHistoryEntry(&mLSHE, nullptr);
SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
}

mFailedChannel = nullptr;
Expand Down Expand Up @@ -5799,7 +5773,7 @@ nsresult nsDocShell::Embed(nsIContentViewer* aContentViewer,
// Set history.state
SetDocCurrentStateObj(mLSHE);

SetHistoryEntry(&mOSHE, mLSHE);
SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
}

bool updateHistory = true;
Expand Down Expand Up @@ -6018,7 +5992,7 @@ void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
mLoadType = LOAD_NORMAL_REPLACE;
SetHistoryEntry(&mLSHE, nullptr);
SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
}
}

Expand Down Expand Up @@ -6153,7 +6127,7 @@ nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,

// Clear the mLSHE reference to indicate document loading is done one
// way or another.
SetHistoryEntry(&mLSHE, nullptr);
SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
}
// if there's a refresh header in the channel, this method
// will set it up for us.
Expand Down Expand Up @@ -6637,7 +6611,7 @@ nsresult nsDocShell::CreateAboutBlankContentViewer(
}

// The transient about:blank viewer doesn't have a session history entry.
SetHistoryEntry(&mOSHE, nullptr);
SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));

// Clear out our mTiming like we would in EndPageLoad, if we didn't
// have one before entering this function.
Expand Down Expand Up @@ -7057,7 +7031,7 @@ nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
MOZ_LOG(gPageCacheLog, LogLevel::Debug,
("restoring presentation from session history: %s", spec.get()));

SetHistoryEntry(&mLSHE, aSHEntry);
SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());

// Post an event that will remove the request after we've returned
// to the event loop. This mimics the way it is called by nsIChannel
Expand Down Expand Up @@ -7361,7 +7335,7 @@ nsresult nsDocShell::RestoreFromHistory() {
NS_ENSURE_SUCCESS(rv, rv);

// mLSHE is now our currently-loaded document.
SetHistoryEntry(&mOSHE, mLSHE);
SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));

// We aren't going to restore any items from the LayoutHistoryState,
// but we don't want them to stay around in case the page is reloaded.
Expand Down Expand Up @@ -8613,7 +8587,8 @@ nsresult nsDocShell::HandleSameDocumentNavigation(
// History loads, SetCurrentURI() called from OnNewURI() will send proper
// onLocationChange() notifications to the browser to update back/forward
// buttons.
SetHistoryEntry(&mLSHE, aLoadState->SHEntry());
SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
Nothing());

// Set the doc's URI according to the new history entry's URI.
RefPtr<Document> doc = GetDocument();
Expand Down Expand Up @@ -8697,7 +8672,7 @@ nsresult nsDocShell::HandleSameDocumentNavigation(
* loads.
*/
if (mLSHE) {
SetHistoryEntry(&mOSHE, mLSHE);
SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
// Save the postData obtained from the previous page
// in to the session history entry created for the
// anchor page, so that any history load of the anchor
Expand All @@ -8716,7 +8691,7 @@ nsresult nsDocShell::HandleSameDocumentNavigation(
/* Restore the original LSHE if we were loading something
* while same document navigation was initiated.
*/
SetHistoryEntry(&mLSHE, oldLSHE);
SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
/* Set the title for the SH entry for this target url. so that
* SH menus in go/back/forward buttons won't be empty for this.
*/
Expand Down Expand Up @@ -9128,7 +9103,8 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
// been called. But when loading an error page, do not clear the
// mLSHE for the real page.
if (mLoadType != LOAD_ERROR_PAGE) {
SetHistoryEntry(&mLSHE, aLoadState->SHEntry());
SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
Nothing());
if (aLoadState->SHEntry()) {
// We're making history navigation or a reload. Make sure our history ID
// points to the same ID as SHEntry's docshell ID.
Expand Down Expand Up @@ -10543,7 +10519,7 @@ bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
// If this is a refresh to the currently loaded url, we don't
// have to update session or global history.
if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
SetHistoryEntry(&mLSHE, mOSHE);
SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
}

/* If the user pressed shift-reload, cache will create a new cache key
Expand Down Expand Up @@ -11253,48 +11229,19 @@ nsresult nsDocShell::AddToSessionHistory(
saveLayoutState, expired);

if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
// If we need to clone our children onto the new session
// history entry, do so now.
if (aCloneChildren && mOSHE) {
uint32_t cloneID = mOSHE->GetID();
nsCOMPtr<nsISHEntry> newEntry;
nsSHistory::CloneAndReplace(mOSHE, this, cloneID, entry, true,
getter_AddRefs(newEntry));
NS_ASSERTION(entry == newEntry,
"The new session history should be in the new entry");
}

// This is the root docshell
bool addToSHistory =
!LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
if (!addToSHistory) {
// Replace current entry in session history; If the requested index is
// valid, it indicates the loading was triggered by a history load, and
// we should replace the entry at requested index instead.
int32_t index = mSessionHistory->LegacySHistory()->GetRequestedIndex();
if (index == -1) {
index = mSessionHistory->Index();
}
bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
Maybe<int32_t> previousEntryIndex;
Maybe<int32_t> loadedEntryIndex;
rv = mSessionHistory->LegacySHistory()->AddToRootSessionHistory(
aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
shouldPersist, &previousEntryIndex, &loadedEntryIndex);

// Replace the current entry with the new entry
if (index >= 0) {
rv = mSessionHistory->LegacySHistory()->ReplaceEntry(index, entry);
} else {
// If we're trying to replace an inexistant shistory entry, append.
addToSHistory = true;
}
MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
if (previousEntryIndex.isSome()) {
mPreviousEntryIndex = previousEntryIndex.value();
}

if (addToSHistory) {
// Add to session history
mPreviousEntryIndex = mSessionHistory->Index();

bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
rv = mSessionHistory->LegacySHistory()->AddEntry(entry, shouldPersist);
mLoadedEntryIndex = mSessionHistory->Index();
MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
mLoadedEntryIndex));
if (loadedEntryIndex.isSome()) {
mLoadedEntryIndex = loadedEntryIndex.value();
}
} else {
// This is a subframe.
Expand Down Expand Up @@ -11427,6 +11374,36 @@ void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
}
}

void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
const Maybe<nsISHEntry*>& aOSHE) {
if (aLSHE.isSome()) {
SetHistoryEntry(&mLSHE, aLSHE.value());
MOZ_ASSERT(mLSHE.get() == aLSHE.value());
}
if (aOSHE.isSome()) {
SetHistoryEntry(&mOSHE, aOSHE.value());
MOZ_ASSERT(mOSHE.get() == aOSHE.value());
}

// If the SH pref is off and we are not a parent process do not update
// canonical BC
if (!StaticPrefs::fission_sessionHistoryInParent() &&
XRE_IsContentProcess()) {
return;
}
ContentChild* cc = ContentChild::GetSingleton();
RefPtr<BrowsingContext> bc =
mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
if (XRE_IsParentProcess()) {
// We are in the parent process, thus we can update the entries directly
mBrowsingContext->Canonical()->UpdateSHEntries(mLSHE, mOSHE);
} else {
// We can't update canonical BC directly, so do it over IPC call
cc->SendUpdateSHEntriesInBC(static_cast<SHEntryChild*>(mLSHE.get()),
static_cast<SHEntryChild*>(mOSHE.get()), bc);
}
}

void nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr,
nsISHEntry* aEntry) {
// We need to sync up the docshell and session history trees for
Expand All @@ -11435,32 +11412,14 @@ void nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr,
// to their corresponding entries in the new session history tree.
// If we don't do this, then we can cache a content viewer on the wrong
// cloned entry, and subsequently restore it at the wrong time.

nsCOMPtr<nsISHEntry> newRootEntry = nsSHistory::GetRootSHEntry(aEntry);
if (newRootEntry) {
// newRootEntry is now the new root entry.
// Find the old root entry as well.

// Need a strong ref. on |oldRootEntry| so it isn't destroyed when
// SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
nsCOMPtr<nsISHEntry> oldRootEntry = nsSHistory::GetRootSHEntry(*aPtr);
if (oldRootEntry) {
nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
GetInProcessSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
if (rootShell) { // if we're the root just set it, nothing to swap
nsSHistory::SwapEntriesData data = {this, newRootEntry};
nsIDocShell* rootIDocShell = static_cast<nsIDocShell*>(rootShell);
nsDocShell* rootDocShell = static_cast<nsDocShell*>(rootIDocShell);

#ifdef DEBUG
nsresult rv =
#endif
nsSHistory::SetChildHistoryEntry(oldRootEntry, rootDocShell, 0,
&data);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
}
}
RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
if (topBC->IsDiscarded()) {
topBC = nullptr;
}
RefPtr<BrowsingContext> currBC =
mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
if (topBC && *aPtr) {
(*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
}

*aPtr = aEntry;
Expand Down
5 changes: 5 additions & 0 deletions docshell/base/nsDocShell.h
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,11 @@ class nsDocShell final : public nsDocLoader,
// in the case of a subframe navigation.
void SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry);

// This method calls SetHistoryEntry and updates mOSHE and mLSHE in BC to be
// the same as in docshell
void SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
const Maybe<nsISHEntry*>& aOSHE);

//
// URI Load
//
Expand Down
4 changes: 4 additions & 0 deletions docshell/shistory/PSHEntry.ipdl
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ parent:
sync ReplaceChild(PSHEntry newChildEntry) returns (nsresult result);
async ClearEntry(uint64_t aNewSharedID);
sync CreateLoadInfo() returns (nsDocShellLoadState loadState);
sync Clone() returns (CrossProcessSHEntry result);
sync SyncTreesForSubframeNavigation(nullable PSHEntry entry, MaybeDiscardedBrowsingContext BC,
MaybeDiscardedBrowsingContext ignoreBC)
returns (SwapEntriesDocshellData[] entriesToUpdate);

async UpdateLayoutHistoryState(bool scrollPositionOnly,
nsCString[] keys, PresState[] states);
Expand Down
Loading

0 comments on commit 5986a2d

Please sign in to comment.