diff options
Diffstat (limited to '')
-rw-r--r-- | docshell/shistory/SessionHistoryEntry.cpp | 1831 |
1 files changed, 1831 insertions, 0 deletions
diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/SessionHistoryEntry.cpp new file mode 100644 index 0000000000..692ab4fe44 --- /dev/null +++ b/docshell/shistory/SessionHistoryEntry.cpp @@ -0,0 +1,1831 @@ +/* -*- 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 "SessionHistoryEntry.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "nsDocShell.h" +#include "nsDocShellLoadState.h" +#include "nsFrameLoader.h" +#include "nsIFormPOSTActionChannel.h" +#include "nsIHttpChannel.h" +#include "nsIUploadChannel2.h" +#include "nsIXULRuntime.h" +#include "nsSHEntryShared.h" +#include "nsSHistory.h" +#include "nsStreamUtils.h" +#include "nsStructuredCloneContainer.h" +#include "nsXULAppAPI.h" +#include "mozilla/PresState.h" +#include "mozilla/StaticPrefs_fission.h" + +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/CSPMessageUtils.h" +#include "mozilla/dom/DocumentBinding.h" +#include "mozilla/dom/DOMTypes.h" +#include "mozilla/dom/nsCSPContext.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/ReferrerInfoUtils.h" +#include "mozilla/ipc/IPDLParamTraits.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/URIUtils.h" + +extern mozilla::LazyLogModule gSHLog; + +namespace mozilla { +namespace dom { + +SessionHistoryInfo::SessionHistoryInfo(nsDocShellLoadState* aLoadState, + nsIChannel* aChannel) + : mURI(aLoadState->URI()), + mOriginalURI(aLoadState->OriginalURI()), + mResultPrincipalURI(aLoadState->ResultPrincipalURI()), + mUnstrippedURI(aLoadState->GetUnstrippedURI()), + mLoadType(aLoadState->LoadType()), + mSrcdocData(aLoadState->SrcdocData().IsVoid() + ? Nothing() + : Some(aLoadState->SrcdocData())), + mBaseURI(aLoadState->BaseURI()), + mLoadReplace(aLoadState->LoadReplace()), + mHasUserInteraction(false), + mHasUserActivation(aLoadState->HasValidUserGestureActivation()), + mSharedState(SharedState::Create( + aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(), + aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(), + /* FIXME Is this correct? */ + aLoadState->TypeHint())) { + // Pull the upload stream off of the channel instead of the load state, as + // ownership has already been transferred from the load state to the channel. + if (nsCOMPtr<nsIUploadChannel2> postChannel = do_QueryInterface(aChannel)) { + int64_t contentLength; + MOZ_ALWAYS_SUCCEEDS(postChannel->CloneUploadStream( + &contentLength, getter_AddRefs(mPostData))); + MOZ_ASSERT_IF(mPostData, NS_InputStreamIsCloneable(mPostData)); + } + + if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) { + mReferrerInfo = httpChannel->GetReferrerInfo(); + } + + MaybeUpdateTitleFromURI(); +} + +SessionHistoryInfo::SessionHistoryInfo( + const SessionHistoryInfo& aSharedStateFrom, nsIURI* aURI) + : mURI(aURI), mSharedState(aSharedStateFrom.mSharedState) { + MaybeUpdateTitleFromURI(); +} + +SessionHistoryInfo::SessionHistoryInfo( + nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal, + nsIPrincipal* aPrincipalToInherit, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp, const nsACString& aContentType) + : mURI(aURI), + mSharedState(SharedState::Create( + aTriggeringPrincipal, aPrincipalToInherit, + aPartitionedPrincipalToInherit, aCsp, aContentType)) { + MaybeUpdateTitleFromURI(); +} + +SessionHistoryInfo::SessionHistoryInfo( + nsIChannel* aChannel, uint32_t aLoadType, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp) { + aChannel->GetURI(getter_AddRefs(mURI)); + mLoadType = aLoadType; + + nsCOMPtr<nsILoadInfo> loadInfo; + aChannel->GetLoadInfo(getter_AddRefs(loadInfo)); + + loadInfo->GetResultPrincipalURI(getter_AddRefs(mResultPrincipalURI)); + loadInfo->GetUnstrippedURI(getter_AddRefs(mUnstrippedURI)); + loadInfo->GetTriggeringPrincipal( + getter_AddRefs(mSharedState.Get()->mTriggeringPrincipal)); + loadInfo->GetPrincipalToInherit( + getter_AddRefs(mSharedState.Get()->mPrincipalToInherit)); + + mSharedState.Get()->mPartitionedPrincipalToInherit = + aPartitionedPrincipalToInherit; + mSharedState.Get()->mCsp = aCsp; + aChannel->GetContentType(mSharedState.Get()->mContentType); + aChannel->GetOriginalURI(getter_AddRefs(mOriginalURI)); + + uint32_t loadFlags; + aChannel->GetLoadFlags(&loadFlags); + mLoadReplace = !!(loadFlags & nsIChannel::LOAD_REPLACE); + + MaybeUpdateTitleFromURI(); + + if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) { + mReferrerInfo = httpChannel->GetReferrerInfo(); + } +} + +void SessionHistoryInfo::Reset(nsIURI* aURI, const nsID& aDocShellID, + bool aDynamicCreation, + nsIPrincipal* aTriggeringPrincipal, + nsIPrincipal* aPrincipalToInherit, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp, + const nsACString& aContentType) { + mURI = aURI; + mOriginalURI = nullptr; + mResultPrincipalURI = nullptr; + mUnstrippedURI = nullptr; + mReferrerInfo = nullptr; + // Default title is the URL. + nsAutoCString spec; + if (NS_SUCCEEDED(mURI->GetSpec(spec))) { + CopyUTF8toUTF16(spec, mTitle); + } + mPostData = nullptr; + mLoadType = 0; + mScrollPositionX = 0; + mScrollPositionY = 0; + mStateData = nullptr; + mSrcdocData = Nothing(); + mBaseURI = nullptr; + mLoadReplace = false; + mURIWasModified = false; + mScrollRestorationIsManual = false; + mPersist = false; + mHasUserInteraction = false; + mHasUserActivation = false; + + mSharedState.Get()->mTriggeringPrincipal = aTriggeringPrincipal; + mSharedState.Get()->mPrincipalToInherit = aPrincipalToInherit; + mSharedState.Get()->mPartitionedPrincipalToInherit = + aPartitionedPrincipalToInherit; + mSharedState.Get()->mCsp = aCsp; + mSharedState.Get()->mContentType = aContentType; + mSharedState.Get()->mLayoutHistoryState = nullptr; +} + +void SessionHistoryInfo::MaybeUpdateTitleFromURI() { + if (mTitle.IsEmpty() && mURI) { + // Default title is the URL. + nsAutoCString spec; + if (NS_SUCCEEDED(mURI->GetSpec(spec))) { + AppendUTF8toUTF16(spec, mTitle); + } + } +} + +already_AddRefed<nsIInputStream> SessionHistoryInfo::GetPostData() const { + // Return a clone of our post data stream. Our caller will either be + // transferring this stream to a different SessionHistoryInfo, or passing it + // off to necko/another process which will consume it, and we want to preserve + // our local instance. + nsCOMPtr<nsIInputStream> postData; + if (mPostData) { + MOZ_ALWAYS_SUCCEEDS( + NS_CloneInputStream(mPostData, getter_AddRefs(postData))); + } + return postData.forget(); +} + +void SessionHistoryInfo::SetPostData(nsIInputStream* aPostData) { + MOZ_ASSERT_IF(aPostData, NS_InputStreamIsCloneable(aPostData)); + mPostData = aPostData; +} + +uint64_t SessionHistoryInfo::SharedId() const { + return mSharedState.Get()->mId; +} + +nsILayoutHistoryState* SessionHistoryInfo::GetLayoutHistoryState() { + return mSharedState.Get()->mLayoutHistoryState; +} + +void SessionHistoryInfo::SetLayoutHistoryState(nsILayoutHistoryState* aState) { + mSharedState.Get()->mLayoutHistoryState = aState; + if (mSharedState.Get()->mLayoutHistoryState) { + mSharedState.Get()->mLayoutHistoryState->SetScrollPositionOnly( + !mSharedState.Get()->mSaveLayoutState); + } +} + +nsIPrincipal* SessionHistoryInfo::GetTriggeringPrincipal() const { + return mSharedState.Get()->mTriggeringPrincipal; +} + +nsIPrincipal* SessionHistoryInfo::GetPrincipalToInherit() const { + return mSharedState.Get()->mPrincipalToInherit; +} + +nsIPrincipal* SessionHistoryInfo::GetPartitionedPrincipalToInherit() const { + return mSharedState.Get()->mPartitionedPrincipalToInherit; +} + +nsIContentSecurityPolicy* SessionHistoryInfo::GetCsp() const { + return mSharedState.Get()->mCsp; +} + +uint32_t SessionHistoryInfo::GetCacheKey() const { + return mSharedState.Get()->mCacheKey; +} + +void SessionHistoryInfo::SetCacheKey(uint32_t aCacheKey) { + mSharedState.Get()->mCacheKey = aCacheKey; +} + +bool SessionHistoryInfo::IsSubFrame() const { + return mSharedState.Get()->mIsFrameNavigation; +} + +void SessionHistoryInfo::SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag) { + MOZ_ASSERT(XRE_IsParentProcess()); + static_cast<SHEntrySharedParentState*>(mSharedState.Get())->mSaveLayoutState = + aSaveLayoutStateFlag; +} + +void SessionHistoryInfo::FillLoadInfo(nsDocShellLoadState& aLoadState) const { + aLoadState.SetOriginalURI(mOriginalURI); + aLoadState.SetMaybeResultPrincipalURI(Some(mResultPrincipalURI)); + aLoadState.SetUnstrippedURI(mUnstrippedURI); + aLoadState.SetLoadReplace(mLoadReplace); + nsCOMPtr<nsIInputStream> postData = GetPostData(); + aLoadState.SetPostDataStream(postData); + aLoadState.SetReferrerInfo(mReferrerInfo); + + aLoadState.SetTypeHint(mSharedState.Get()->mContentType); + aLoadState.SetTriggeringPrincipal(mSharedState.Get()->mTriggeringPrincipal); + aLoadState.SetPrincipalToInherit(mSharedState.Get()->mPrincipalToInherit); + aLoadState.SetPartitionedPrincipalToInherit( + mSharedState.Get()->mPartitionedPrincipalToInherit); + aLoadState.SetCsp(mSharedState.Get()->mCsp); + + // Do not inherit principal from document (security-critical!); + uint32_t flags = nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_NONE; + + // Passing nullptr as aSourceDocShell gives the same behaviour as before + // aSourceDocShell was introduced. According to spec we should be passing + // the source browsing context that was used when the history entry was + // first created. bug 947716 has been created to address this issue. + nsAutoString srcdoc; + nsCOMPtr<nsIURI> baseURI; + if (mSrcdocData) { + srcdoc = mSrcdocData.value(); + baseURI = mBaseURI; + flags |= nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC; + } else { + srcdoc = VoidString(); + } + aLoadState.SetSrcdocData(srcdoc); + aLoadState.SetBaseURI(baseURI); + aLoadState.SetInternalLoadFlags(flags); + + aLoadState.SetFirstParty(true); + + // When we create a load state from the history info we already know if + // https-first was able to upgrade the request from http to https. There is no + // point in re-retrying to upgrade. On a reload we still want to check, + // because the exemptions set by the user could have changed. + if ((mLoadType & nsIDocShell::LOAD_CMD_RELOAD) == 0) { + aLoadState.SetIsExemptFromHTTPSFirstMode(true); + } +} +/* static */ +SessionHistoryInfo::SharedState SessionHistoryInfo::SharedState::Create( + nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp, const nsACString& aContentType) { + if (XRE_IsParentProcess()) { + return SharedState(new SHEntrySharedParentState( + aTriggeringPrincipal, aPrincipalToInherit, + aPartitionedPrincipalToInherit, aCsp, aContentType)); + } + + return SharedState(MakeUnique<SHEntrySharedState>( + aTriggeringPrincipal, aPrincipalToInherit, aPartitionedPrincipalToInherit, + aCsp, aContentType)); +} + +SessionHistoryInfo::SharedState::SharedState() { Init(); } + +SessionHistoryInfo::SharedState::SharedState( + const SessionHistoryInfo::SharedState& aOther) { + Init(aOther); +} + +SessionHistoryInfo::SharedState::SharedState( + const Maybe<const SessionHistoryInfo::SharedState&>& aOther) { + if (aOther.isSome()) { + Init(aOther.ref()); + } else { + Init(); + } +} + +SessionHistoryInfo::SharedState::~SharedState() { + if (XRE_IsParentProcess()) { + mParent + .RefPtr<SHEntrySharedParentState>::~RefPtr<SHEntrySharedParentState>(); + } else { + mChild.UniquePtr<SHEntrySharedState>::~UniquePtr<SHEntrySharedState>(); + } +} + +SessionHistoryInfo::SharedState& SessionHistoryInfo::SharedState::operator=( + const SessionHistoryInfo::SharedState& aOther) { + if (this != &aOther) { + if (XRE_IsParentProcess()) { + mParent = aOther.mParent; + } else { + mChild = MakeUnique<SHEntrySharedState>(*aOther.mChild); + } + } + return *this; +} + +SHEntrySharedState* SessionHistoryInfo::SharedState::Get() const { + if (XRE_IsParentProcess()) { + return mParent; + } + + return mChild.get(); +} + +void SessionHistoryInfo::SharedState::ChangeId(uint64_t aId) { + if (XRE_IsParentProcess()) { + mParent->ChangeId(aId); + } else { + mChild->mId = aId; + } +} + +void SessionHistoryInfo::SharedState::Init() { + if (XRE_IsParentProcess()) { + new (&mParent) + RefPtr<SHEntrySharedParentState>(new SHEntrySharedParentState()); + } else { + new (&mChild) + UniquePtr<SHEntrySharedState>(MakeUnique<SHEntrySharedState>()); + } +} + +void SessionHistoryInfo::SharedState::Init( + const SessionHistoryInfo::SharedState& aOther) { + if (XRE_IsParentProcess()) { + new (&mParent) RefPtr<SHEntrySharedParentState>(aOther.mParent); + } else { + new (&mChild) UniquePtr<SHEntrySharedState>( + MakeUnique<SHEntrySharedState>(*aOther.mChild)); + } +} + +static uint64_t gLoadingSessionHistoryInfoLoadId = 0; + +nsTHashMap<nsUint64HashKey, SessionHistoryEntry::LoadingEntry>* + SessionHistoryEntry::sLoadIdToEntry = nullptr; + +LoadingSessionHistoryInfo::LoadingSessionHistoryInfo( + SessionHistoryEntry* aEntry) + : mInfo(aEntry->Info()), mLoadId(++gLoadingSessionHistoryInfoLoadId) { + SessionHistoryEntry::SetByLoadId(mLoadId, aEntry); +} + +LoadingSessionHistoryInfo::LoadingSessionHistoryInfo( + SessionHistoryEntry* aEntry, const LoadingSessionHistoryInfo* aInfo) + : mInfo(aEntry->Info()), + mLoadId(aInfo->mLoadId), + mLoadIsFromSessionHistory(aInfo->mLoadIsFromSessionHistory), + mOffset(aInfo->mOffset), + mLoadingCurrentEntry(aInfo->mLoadingCurrentEntry) { + MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(mLoadId)->mEntry == aEntry); +} + +LoadingSessionHistoryInfo::LoadingSessionHistoryInfo( + const SessionHistoryInfo& aInfo) + : mInfo(aInfo), mLoadId(UINT64_MAX) {} + +already_AddRefed<nsDocShellLoadState> +LoadingSessionHistoryInfo::CreateLoadInfo() const { + RefPtr<nsDocShellLoadState> loadState( + new nsDocShellLoadState(mInfo.GetURI())); + + mInfo.FillLoadInfo(*loadState); + + loadState->SetLoadingSessionHistoryInfo(*this); + + return loadState.forget(); +} + +static uint32_t gEntryID; + +SessionHistoryEntry::LoadingEntry* SessionHistoryEntry::GetByLoadId( + uint64_t aLoadId) { + MOZ_ASSERT(XRE_IsParentProcess()); + if (!sLoadIdToEntry) { + return nullptr; + } + + return sLoadIdToEntry->Lookup(aLoadId).DataPtrOrNull(); +} + +void SessionHistoryEntry::SetByLoadId(uint64_t aLoadId, + SessionHistoryEntry* aEntry) { + if (!sLoadIdToEntry) { + sLoadIdToEntry = new nsTHashMap<nsUint64HashKey, LoadingEntry>(); + } + + MOZ_LOG( + gSHLog, LogLevel::Verbose, + ("SessionHistoryEntry::SetByLoadId(%" PRIu64 " - %p)", aLoadId, aEntry)); + sLoadIdToEntry->InsertOrUpdate( + aLoadId, LoadingEntry{ + .mEntry = aEntry, + .mInfoSnapshotForValidation = + MakeUnique<SessionHistoryInfo>(aEntry->Info()), + }); +} + +void SessionHistoryEntry::RemoveLoadId(uint64_t aLoadId) { + MOZ_ASSERT(XRE_IsParentProcess()); + if (!sLoadIdToEntry) { + return; + } + + MOZ_LOG(gSHLog, LogLevel::Verbose, + ("SHEntry::RemoveLoadId(%" PRIu64 ")", aLoadId)); + sLoadIdToEntry->Remove(aLoadId); +} + +SessionHistoryEntry::SessionHistoryEntry() + : mInfo(new SessionHistoryInfo()), mID(++gEntryID) { + MOZ_ASSERT(mozilla::SessionHistoryInParent()); +} + +SessionHistoryEntry::SessionHistoryEntry(nsDocShellLoadState* aLoadState, + nsIChannel* aChannel) + : mInfo(new SessionHistoryInfo(aLoadState, aChannel)), mID(++gEntryID) { + MOZ_ASSERT(mozilla::SessionHistoryInParent()); +} + +SessionHistoryEntry::SessionHistoryEntry(SessionHistoryInfo* aInfo) + : mInfo(MakeUnique<SessionHistoryInfo>(*aInfo)), mID(++gEntryID) { + MOZ_ASSERT(mozilla::SessionHistoryInParent()); +} + +SessionHistoryEntry::SessionHistoryEntry(const SessionHistoryEntry& aEntry) + : mInfo(MakeUnique<SessionHistoryInfo>(*aEntry.mInfo)), + mParent(aEntry.mParent), + mID(aEntry.mID), + mBCHistoryLength(aEntry.mBCHistoryLength) { + MOZ_ASSERT(mozilla::SessionHistoryInParent()); +} + +SessionHistoryEntry::~SessionHistoryEntry() { + // Null out the mParent pointers on all our kids. + for (nsISHEntry* entry : mChildren) { + if (entry) { + entry->SetParent(nullptr); + } + } + + if (sLoadIdToEntry) { + sLoadIdToEntry->RemoveIf( + [this](auto& aIter) { return aIter.Data().mEntry == this; }); + if (sLoadIdToEntry->IsEmpty()) { + delete sLoadIdToEntry; + sLoadIdToEntry = nullptr; + } + } +} + +NS_IMPL_ISUPPORTS(SessionHistoryEntry, nsISHEntry, SessionHistoryEntry, + nsISupportsWeakReference) + +NS_IMETHODIMP +SessionHistoryEntry::GetURI(nsIURI** aURI) { + nsCOMPtr<nsIURI> uri = mInfo->mURI; + uri.forget(aURI); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetURI(nsIURI* aURI) { + mInfo->mURI = aURI; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetOriginalURI(nsIURI** aOriginalURI) { + nsCOMPtr<nsIURI> originalURI = mInfo->mOriginalURI; + originalURI.forget(aOriginalURI); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetOriginalURI(nsIURI* aOriginalURI) { + mInfo->mOriginalURI = aOriginalURI; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetResultPrincipalURI(nsIURI** aResultPrincipalURI) { + nsCOMPtr<nsIURI> resultPrincipalURI = mInfo->mResultPrincipalURI; + resultPrincipalURI.forget(aResultPrincipalURI); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetResultPrincipalURI(nsIURI* aResultPrincipalURI) { + mInfo->mResultPrincipalURI = aResultPrincipalURI; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetUnstrippedURI(nsIURI** aUnstrippedURI) { + nsCOMPtr<nsIURI> unstrippedURI = mInfo->mUnstrippedURI; + unstrippedURI.forget(aUnstrippedURI); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetUnstrippedURI(nsIURI* aUnstrippedURI) { + mInfo->mUnstrippedURI = aUnstrippedURI; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetLoadReplace(bool* aLoadReplace) { + *aLoadReplace = mInfo->mLoadReplace; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetLoadReplace(bool aLoadReplace) { + mInfo->mLoadReplace = aLoadReplace; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetTitle(nsAString& aTitle) { + aTitle = mInfo->mTitle; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetTitle(const nsAString& aTitle) { + mInfo->SetTitle(aTitle); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetName(nsAString& aName) { + aName = mInfo->mName; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetName(const nsAString& aName) { + mInfo->mName = aName; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetIsSubFrame(bool* aIsSubFrame) { + *aIsSubFrame = SharedInfo()->mIsFrameNavigation; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetIsSubFrame(bool aIsSubFrame) { + SharedInfo()->mIsFrameNavigation = aIsSubFrame; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetHasUserInteraction(bool* aFlag) { + // The back button and menulist deal with root/top-level + // session history entries, thus we annotate only the root entry. + if (!mParent) { + *aFlag = mInfo->mHasUserInteraction; + } else { + nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(this); + root->GetHasUserInteraction(aFlag); + } + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetHasUserInteraction(bool aFlag) { + // The back button and menulist deal with root/top-level + // session history entries, thus we annotate only the root entry. + if (!mParent) { + mInfo->mHasUserInteraction = aFlag; + } else { + nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(this); + root->SetHasUserInteraction(aFlag); + } + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetHasUserActivation(bool* aFlag) { + *aFlag = mInfo->mHasUserActivation; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetHasUserActivation(bool aFlag) { + mInfo->mHasUserActivation = aFlag; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) { + nsCOMPtr<nsIReferrerInfo> referrerInfo = mInfo->mReferrerInfo; + referrerInfo.forget(aReferrerInfo); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) { + mInfo->mReferrerInfo = aReferrerInfo; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetDocumentViewer(nsIDocumentViewer** aDocumentViewer) { + *aDocumentViewer = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetDocumentViewer(nsIDocumentViewer* aDocumentViewer) { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetIsInBFCache(bool* aResult) { + *aResult = !!SharedInfo()->mFrameLoader; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetSticky(bool* aSticky) { + *aSticky = SharedInfo()->mSticky; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetSticky(bool aSticky) { + SharedInfo()->mSticky = aSticky; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetWindowState(nsISupports** aWindowState) { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetWindowState(nsISupports* aWindowState) { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetRefreshURIList(nsIMutableArray** aRefreshURIList) { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetRefreshURIList(nsIMutableArray* aRefreshURIList) { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetPostData(nsIInputStream** aPostData) { + *aPostData = mInfo->GetPostData().take(); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetPostData(nsIInputStream* aPostData) { + mInfo->SetPostData(aPostData); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetHasPostData(bool* aResult) { + *aResult = mInfo->HasPostData(); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetLayoutHistoryState( + nsILayoutHistoryState** aLayoutHistoryState) { + nsCOMPtr<nsILayoutHistoryState> layoutHistoryState = + SharedInfo()->mLayoutHistoryState; + layoutHistoryState.forget(aLayoutHistoryState); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetLayoutHistoryState( + nsILayoutHistoryState* aLayoutHistoryState) { + SharedInfo()->mLayoutHistoryState = aLayoutHistoryState; + if (SharedInfo()->mLayoutHistoryState) { + SharedInfo()->mLayoutHistoryState->SetScrollPositionOnly( + !SharedInfo()->mSaveLayoutState); + } + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetParent(nsISHEntry** aParent) { + nsCOMPtr<nsISHEntry> parent = do_QueryReferent(mParent); + parent.forget(aParent); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetParent(nsISHEntry* aParent) { + mParent = do_GetWeakReference(aParent); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetLoadType(uint32_t* aLoadType) { + *aLoadType = mInfo->mLoadType; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetLoadType(uint32_t aLoadType) { + mInfo->mLoadType = aLoadType; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetID(uint32_t* aID) { + *aID = mID; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetID(uint32_t aID) { + mID = aID; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetCacheKey(uint32_t* aCacheKey) { + *aCacheKey = SharedInfo()->mCacheKey; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetCacheKey(uint32_t aCacheKey) { + SharedInfo()->mCacheKey = aCacheKey; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetSaveLayoutStateFlag(bool* aSaveLayoutStateFlag) { + *aSaveLayoutStateFlag = SharedInfo()->mSaveLayoutState; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag) { + SharedInfo()->mSaveLayoutState = aSaveLayoutStateFlag; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetContentType(nsACString& aContentType) { + aContentType = SharedInfo()->mContentType; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetContentType(const nsACString& aContentType) { + SharedInfo()->mContentType = aContentType; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetURIWasModified(bool* aURIWasModified) { + *aURIWasModified = mInfo->mURIWasModified; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetURIWasModified(bool aURIWasModified) { + mInfo->mURIWasModified = aURIWasModified; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetTriggeringPrincipal( + nsIPrincipal** aTriggeringPrincipal) { + nsCOMPtr<nsIPrincipal> triggeringPrincipal = + SharedInfo()->mTriggeringPrincipal; + triggeringPrincipal.forget(aTriggeringPrincipal); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetTriggeringPrincipal( + nsIPrincipal* aTriggeringPrincipal) { + SharedInfo()->mTriggeringPrincipal = aTriggeringPrincipal; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetPrincipalToInherit(nsIPrincipal** aPrincipalToInherit) { + nsCOMPtr<nsIPrincipal> principalToInherit = SharedInfo()->mPrincipalToInherit; + principalToInherit.forget(aPrincipalToInherit); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit) { + SharedInfo()->mPrincipalToInherit = aPrincipalToInherit; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetPartitionedPrincipalToInherit( + nsIPrincipal** aPartitionedPrincipalToInherit) { + nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit = + SharedInfo()->mPartitionedPrincipalToInherit; + partitionedPrincipalToInherit.forget(aPartitionedPrincipalToInherit); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetPartitionedPrincipalToInherit( + nsIPrincipal* aPartitionedPrincipalToInherit) { + SharedInfo()->mPartitionedPrincipalToInherit = aPartitionedPrincipalToInherit; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetCsp(nsIContentSecurityPolicy** aCsp) { + nsCOMPtr<nsIContentSecurityPolicy> csp = SharedInfo()->mCsp; + csp.forget(aCsp); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetCsp(nsIContentSecurityPolicy* aCsp) { + SharedInfo()->mCsp = aCsp; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetStateData(nsIStructuredCloneContainer** aStateData) { + RefPtr<nsStructuredCloneContainer> stateData = mInfo->mStateData; + stateData.forget(aStateData); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetStateData(nsIStructuredCloneContainer* aStateData) { + mInfo->mStateData = static_cast<nsStructuredCloneContainer*>(aStateData); + return NS_OK; +} + +const nsID& SessionHistoryEntry::DocshellID() const { + return SharedInfo()->mDocShellID; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetDocshellID(nsID& aDocshellID) { + aDocshellID = DocshellID(); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetDocshellID(const nsID& aDocshellID) { + SharedInfo()->mDocShellID = aDocshellID; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetIsSrcdocEntry(bool* aIsSrcdocEntry) { + *aIsSrcdocEntry = mInfo->mSrcdocData.isSome(); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetSrcdocData(nsAString& aSrcdocData) { + aSrcdocData = mInfo->mSrcdocData.valueOr(EmptyString()); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetSrcdocData(const nsAString& aSrcdocData) { + mInfo->mSrcdocData = Some(nsString(aSrcdocData)); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetBaseURI(nsIURI** aBaseURI) { + nsCOMPtr<nsIURI> baseURI = mInfo->mBaseURI; + baseURI.forget(aBaseURI); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetBaseURI(nsIURI* aBaseURI) { + mInfo->mBaseURI = aBaseURI; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetScrollRestorationIsManual( + bool* aScrollRestorationIsManual) { + *aScrollRestorationIsManual = mInfo->mScrollRestorationIsManual; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetScrollRestorationIsManual( + bool aScrollRestorationIsManual) { + mInfo->mScrollRestorationIsManual = aScrollRestorationIsManual; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetLoadedInThisProcess(bool* aLoadedInThisProcess) { + // FIXME + //*aLoadedInThisProcess = mInfo->mLoadedInThisProcess; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetShistory(nsISHistory** aShistory) { + nsCOMPtr<nsISHistory> sHistory = do_QueryReferent(SharedInfo()->mSHistory); + sHistory.forget(aShistory); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetShistory(nsISHistory* aShistory) { + nsWeakPtr shistory = do_GetWeakReference(aShistory); + // mSHistory can not be changed once it's set + MOZ_ASSERT(!SharedInfo()->mSHistory || (SharedInfo()->mSHistory == shistory)); + SharedInfo()->mSHistory = shistory; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetLastTouched(uint32_t* aLastTouched) { + *aLastTouched = SharedInfo()->mLastTouched; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetLastTouched(uint32_t aLastTouched) { + SharedInfo()->mLastTouched = aLastTouched; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetChildCount(int32_t* aChildCount) { + *aChildCount = mChildren.Length(); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetPersist(bool* aPersist) { + *aPersist = mInfo->mPersist; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetPersist(bool aPersist) { + mInfo->mPersist = aPersist; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetScrollPosition(int32_t* aX, int32_t* aY) { + *aX = mInfo->mScrollPositionX; + *aY = mInfo->mScrollPositionY; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetScrollPosition(int32_t aX, int32_t aY) { + mInfo->mScrollPositionX = aX; + mInfo->mScrollPositionY = aY; + return NS_OK; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::GetViewerBounds(nsIntRect& bounds) { + bounds = SharedInfo()->mViewerBounds; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::SetViewerBounds(const nsIntRect& bounds) { + SharedInfo()->mViewerBounds = bounds; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::AddChildShell(nsIDocShellTreeItem* shell) { + MOZ_CRASH("This lives in the child process"); +} + +NS_IMETHODIMP +SessionHistoryEntry::ChildShellAt(int32_t index, + nsIDocShellTreeItem** _retval) { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::ClearChildShells() { + MOZ_CRASH("This lives in the child process"); +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::SyncPresentationState() { + MOZ_CRASH("This lives in the child process"); +} + +NS_IMETHODIMP +SessionHistoryEntry::InitLayoutHistoryState( + nsILayoutHistoryState** aLayoutHistoryState) { + if (!SharedInfo()->mLayoutHistoryState) { + nsCOMPtr<nsILayoutHistoryState> historyState; + historyState = NS_NewLayoutHistoryState(); + SetLayoutHistoryState(historyState); + } + + return GetLayoutHistoryState(aLayoutHistoryState); +} + +NS_IMETHODIMP +SessionHistoryEntry::Create( + nsIURI* aURI, const nsAString& aTitle, nsIInputStream* aInputStream, + uint32_t aCacheKey, const nsACString& aContentType, + nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit, + nsIPrincipal* aPartitionedPrincipalToInherit, + nsIContentSecurityPolicy* aCsp, const nsID& aDocshellID, + bool aDynamicCreation, nsIURI* aOriginalURI, nsIURI* aResultPrincipalURI, + nsIURI* aUnstrippedURI, bool aLoadReplace, nsIReferrerInfo* aReferrerInfo, + const nsAString& aSrcdoc, bool aSrcdocEntry, nsIURI* aBaseURI, + bool aSaveLayoutState, bool aExpired, bool aUserActivation) { + MOZ_CRASH("Might need to implement this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +SessionHistoryEntry::Clone(nsISHEntry** aEntry) { + RefPtr<SessionHistoryEntry> entry = new SessionHistoryEntry(*this); + + // These are not copied for some reason, we're not sure why. + entry->mInfo->mLoadType = 0; + entry->mInfo->mScrollPositionX = 0; + entry->mInfo->mScrollPositionY = 0; + entry->mInfo->mScrollRestorationIsManual = false; + + entry->mInfo->mHasUserInteraction = false; + + entry.forget(aEntry); + + return NS_OK; +} + +NS_IMETHODIMP_(nsDocShellEditorData*) +SessionHistoryEntry::ForgetEditorData() { + MOZ_CRASH("This lives in the child process"); + return nullptr; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::SetEditorData(nsDocShellEditorData* aData) { + NS_WARNING("This lives in the child process"); +} + +NS_IMETHODIMP_(bool) +SessionHistoryEntry::HasDetachedEditor() { + NS_WARNING("This lives in the child process"); + return false; +} + +NS_IMETHODIMP_(bool) +SessionHistoryEntry::IsDynamicallyAdded() { + return SharedInfo()->mDynamicallyCreated; +} + +void SessionHistoryEntry::SetWireframe(const Maybe<Wireframe>& aWireframe) { + mWireframe = aWireframe; +} + +void SessionHistoryEntry::SetIsDynamicallyAdded(bool aDynamic) { + MOZ_ASSERT_IF(SharedInfo()->mDynamicallyCreated, aDynamic); + SharedInfo()->mDynamicallyCreated = aDynamic; +} + +NS_IMETHODIMP +SessionHistoryEntry::HasDynamicallyAddedChild(bool* aHasDynamicallyAddedChild) { + for (const auto& child : mChildren) { + if (child && child->IsDynamicallyAdded()) { + *aHasDynamicallyAddedChild = true; + return NS_OK; + } + } + *aHasDynamicallyAddedChild = false; + return NS_OK; +} + +NS_IMETHODIMP_(bool) +SessionHistoryEntry::HasBFCacheEntry(SHEntrySharedParentState* aEntry) { + return SharedInfo() == aEntry; +} + +NS_IMETHODIMP +SessionHistoryEntry::AdoptBFCacheEntry(nsISHEntry* aEntry) { + nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(aEntry); + NS_ENSURE_STATE(she && she->mInfo->mSharedState.Get()); + + mInfo->mSharedState = + static_cast<SessionHistoryEntry*>(aEntry)->mInfo->mSharedState; + + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::AbandonBFCacheEntry() { + MOZ_CRASH("This lives in the child process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +SessionHistoryEntry::SharesDocumentWith(nsISHEntry* aEntry, + bool* aSharesDocumentWith) { + SessionHistoryEntry* entry = static_cast<SessionHistoryEntry*>(aEntry); + + MOZ_ASSERT_IF(entry->SharedInfo() != SharedInfo(), + entry->SharedInfo()->GetId() != SharedInfo()->GetId()); + + *aSharesDocumentWith = entry->SharedInfo() == SharedInfo(); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetLoadTypeAsHistory() { + mInfo->mLoadType = LOAD_HISTORY; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::AddChild(nsISHEntry* aChild, int32_t aOffset, + bool aUseRemoteSubframes) { + nsCOMPtr<SessionHistoryEntry> child = do_QueryInterface(aChild); + MOZ_ASSERT_IF(aChild, child); + AddChild(child, aOffset, aUseRemoteSubframes); + + return NS_OK; +} + +void SessionHistoryEntry::AddChild(SessionHistoryEntry* aChild, int32_t aOffset, + bool aUseRemoteSubframes) { + if (aChild) { + aChild->SetParent(this); + } + + if (aOffset < 0) { + mChildren.AppendElement(aChild); + return; + } + + // + // Bug 52670: Ensure children are added in order. + // + // Later frames in the child list may load faster and get appended + // before earlier frames, causing session history to be scrambled. + // By growing the list here, they are added to the right position. + + int32_t length = mChildren.Length(); + + // Assert that aOffset will not be so high as to grow us a lot. + NS_ASSERTION(aOffset < length + 1023, "Large frames array!\n"); + + // If the new child is dynamically added, try to add it to aOffset, but if + // there are non-dynamically added children, the child must be after those. + if (aChild && aChild->IsDynamicallyAdded()) { + int32_t lastNonDyn = aOffset - 1; + for (int32_t i = aOffset; i < length; ++i) { + SessionHistoryEntry* entry = mChildren[i]; + if (entry) { + if (entry->IsDynamicallyAdded()) { + break; + } + + lastNonDyn = i; + } + } + + // If aOffset is larger than Length(), we must first truncate the array. + if (aOffset > length) { + mChildren.SetLength(aOffset); + } + + mChildren.InsertElementAt(lastNonDyn + 1, aChild); + + return; + } + + // If the new child isn't dynamically added, it should be set to aOffset. + // If there are dynamically added children before that, those must be moved + // to be after aOffset. + if (length > 0) { + int32_t start = std::min(length - 1, aOffset); + int32_t dynEntryIndex = -1; + DebugOnly<SessionHistoryEntry*> dynEntry = nullptr; + for (int32_t i = start; i >= 0; --i) { + SessionHistoryEntry* entry = mChildren[i]; + if (entry) { + if (!entry->IsDynamicallyAdded()) { + break; + } + + dynEntryIndex = i; + dynEntry = entry; + } + } + + if (dynEntryIndex >= 0) { + mChildren.InsertElementsAt(dynEntryIndex, aOffset - dynEntryIndex + 1); + NS_ASSERTION(mChildren[aOffset + 1] == dynEntry, "Whaat?"); + } + } + + // Make sure there isn't anything at aOffset. + if ((uint32_t)aOffset < mChildren.Length()) { + SessionHistoryEntry* oldChild = mChildren[aOffset]; + if (oldChild && oldChild != aChild) { + // Under Fission, this can happen when a network-created iframe starts + // out in-process, moves out-of-process, and then switches back. At that + // point, we'll create a new network-created DocShell at the same index + // where we already have an entry for the original network-created + // DocShell. + // + // This should ideally stop being an issue once the Fission-aware + // session history rewrite is complete. + NS_ASSERTION( + aUseRemoteSubframes || NS_IsAboutBlank(oldChild->Info().GetURI()), + "Adding a child where we already have a child? This may misbehave"); + oldChild->SetParent(nullptr); + } + } else { + mChildren.SetLength(aOffset + 1); + } + + mChildren.ReplaceElementAt(aOffset, aChild); +} + +NS_IMETHODIMP +SessionHistoryEntry::RemoveChild(nsISHEntry* aChild) { + NS_ENSURE_TRUE(aChild, NS_ERROR_FAILURE); + + nsCOMPtr<SessionHistoryEntry> child = do_QueryInterface(aChild); + MOZ_ASSERT(child); + RemoveChild(child); + + return NS_OK; +} + +void SessionHistoryEntry::RemoveChild(SessionHistoryEntry* aChild) { + bool childRemoved = false; + if (aChild->IsDynamicallyAdded()) { + childRemoved = mChildren.RemoveElement(aChild); + } else { + int32_t index = mChildren.IndexOf(aChild); + if (index >= 0) { + // Other alive non-dynamic child docshells still keep mChildOffset, + // so we don't want to change the indices here. + mChildren.ReplaceElementAt(index, nullptr); + childRemoved = true; + } + } + + if (childRemoved) { + aChild->SetParent(nullptr); + + // reduce the child count, i.e. remove empty children at the end + for (int32_t i = mChildren.Length() - 1; i >= 0 && !mChildren[i]; --i) { + mChildren.RemoveElementAt(i); + } + } +} + +NS_IMETHODIMP +SessionHistoryEntry::GetChildAt(int32_t aIndex, nsISHEntry** aChild) { + nsCOMPtr<nsISHEntry> child = mChildren.SafeElementAt(aIndex); + child.forget(aChild); + return NS_OK; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::GetChildSHEntryIfHasNoDynamicallyAddedChild( + int32_t aChildOffset, nsISHEntry** aChild) { + *aChild = nullptr; + + bool dynamicallyAddedChild = false; + HasDynamicallyAddedChild(&dynamicallyAddedChild); + if (dynamicallyAddedChild) { + return; + } + + // If the user did a shift-reload on this frameset page, + // we don't want to load the subframes from history. + if (IsForceReloadType(mInfo->mLoadType) || mInfo->mLoadType == LOAD_REFRESH) { + return; + } + + /* Before looking for the subframe's url, check + * the expiration status of the parent. If the parent + * has expired from cache, then subframes will not be + * loaded from history in certain situations. + * If the user pressed reload and the parent frame has expired + * from cache, we do not want to load the child frame from history. + */ + if (SharedInfo()->mExpired && (mInfo->mLoadType == LOAD_RELOAD_NORMAL)) { + // The parent has expired. Return null. + *aChild = nullptr; + return; + } + // Get the child subframe from session history. + GetChildAt(aChildOffset, aChild); + if (*aChild) { + // Set the parent's Load Type on the child + (*aChild)->SetLoadType(mInfo->mLoadType); + } +} + +NS_IMETHODIMP +SessionHistoryEntry::ReplaceChild(nsISHEntry* aNewChild) { + NS_ENSURE_STATE(aNewChild); + + nsCOMPtr<SessionHistoryEntry> newChild = do_QueryInterface(aNewChild); + MOZ_ASSERT(newChild); + return ReplaceChild(newChild) ? NS_OK : NS_ERROR_FAILURE; +} + +bool SessionHistoryEntry::ReplaceChild(SessionHistoryEntry* aNewChild) { + const nsID& docshellID = aNewChild->DocshellID(); + + for (uint32_t i = 0; i < mChildren.Length(); ++i) { + if (mChildren[i] && docshellID == mChildren[i]->DocshellID()) { + mChildren[i]->SetParent(nullptr); + mChildren.ReplaceElementAt(i, aNewChild); + aNewChild->SetParent(this); + + return true; + } + } + + return false; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::ClearEntry() { + int32_t childCount = GetChildCount(); + // Remove all children of this entry + for (int32_t i = childCount; i > 0; --i) { + nsCOMPtr<nsISHEntry> child; + GetChildAt(i - 1, getter_AddRefs(child)); + RemoveChild(child); + } +} + +NS_IMETHODIMP +SessionHistoryEntry::CreateLoadInfo(nsDocShellLoadState** aLoadState) { + NS_WARNING("We shouldn't be calling this!"); + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetBfcacheID(uint64_t* aBfcacheID) { + *aBfcacheID = SharedInfo()->mId; + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::GetWireframe(JSContext* aCx, + JS::MutableHandle<JS::Value> aOut) { + if (mWireframe.isNothing()) { + aOut.set(JS::NullValue()); + } else if (NS_WARN_IF(!mWireframe->ToObjectInternal(aCx, aOut))) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +SessionHistoryEntry::SetWireframe(JSContext* aCx, JS::Handle<JS::Value> aArg) { + if (aArg.isNullOrUndefined()) { + mWireframe = Nothing(); + return NS_OK; + } + + Wireframe wireframe; + if (aArg.isObject() && wireframe.Init(aCx, aArg)) { + mWireframe = Some(std::move(wireframe)); + return NS_OK; + } + + return NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP_(void) +SessionHistoryEntry::SyncTreesForSubframeNavigation( + nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aTopBC, + mozilla::dom::BrowsingContext* aIgnoreBC) { + // XXX Keep this in sync with nsSHEntry::SyncTreesForSubframeNavigation. + // + // We need to sync up the browsing context and session history trees for + // subframe navigation. If the load was in a subframe, we forward up to + // the top browsing context, which will then recursively sync up all browsing + // contexts 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(this); + + if (oldRootEntry) { + nsSHistory::SwapEntriesData data = {aIgnoreBC, newRootEntry, nullptr}; + nsSHistory::SetChildHistoryEntry(oldRootEntry, aTopBC, 0, &data); + } + } +} + +void SessionHistoryEntry::ReplaceWith(const SessionHistoryEntry& aSource) { + mInfo = MakeUnique<SessionHistoryInfo>(*aSource.mInfo); + mChildren.Clear(); +} + +SHEntrySharedParentState* SessionHistoryEntry::SharedInfo() const { + return static_cast<SHEntrySharedParentState*>(mInfo->mSharedState.Get()); +} + +void SessionHistoryEntry::SetFrameLoader(nsFrameLoader* aFrameLoader) { + MOZ_DIAGNOSTIC_ASSERT(!aFrameLoader || !SharedInfo()->mFrameLoader); + // If the pref is disabled, we still allow evicting the existing entries. + MOZ_RELEASE_ASSERT(!aFrameLoader || mozilla::BFCacheInParent()); + SharedInfo()->SetFrameLoader(aFrameLoader); + if (aFrameLoader) { + if (BrowsingContext* bc = aFrameLoader->GetMaybePendingBrowsingContext()) { + bc->PreOrderWalk([&](BrowsingContext* aContext) { + if (BrowserParent* bp = aContext->Canonical()->GetBrowserParent()) { + bp->Deactivated(); + } + }); + } + + // When a new frameloader is stored, try to evict some older + // frameloaders. Non-SHIP session history has a similar call in + // nsDocumentViewer::Show. + nsCOMPtr<nsISHistory> shistory; + GetShistory(getter_AddRefs(shistory)); + if (shistory) { + int32_t index = 0; + shistory->GetIndex(&index); + shistory->EvictOutOfRangeDocumentViewers(index); + } + } +} + +nsFrameLoader* SessionHistoryEntry::GetFrameLoader() { + return SharedInfo()->mFrameLoader; +} + +void SessionHistoryEntry::SetInfo(SessionHistoryInfo* aInfo) { + // FIXME Assert that we're not changing shared state! + mInfo = MakeUnique<SessionHistoryInfo>(*aInfo); +} + +} // namespace dom + +namespace ipc { + +void IPDLParamTraits<dom::SessionHistoryInfo>::Write( + IPC::MessageWriter* aWriter, IProtocol* aActor, + const dom::SessionHistoryInfo& aParam) { + nsCOMPtr<nsIInputStream> postData = aParam.GetPostData(); + + Maybe<std::tuple<uint32_t, dom::ClonedMessageData>> stateData; + if (aParam.mStateData) { + stateData.emplace(); + // FIXME: We should fail more aggressively if this fails, as currently we'll + // just early return and the deserialization will break. + NS_ENSURE_SUCCESS_VOID( + aParam.mStateData->GetFormatVersion(&std::get<0>(*stateData))); + NS_ENSURE_TRUE_VOID( + aParam.mStateData->BuildClonedMessageData(std::get<1>(*stateData))); + } + + WriteIPDLParam(aWriter, aActor, aParam.mURI); + WriteIPDLParam(aWriter, aActor, aParam.mOriginalURI); + WriteIPDLParam(aWriter, aActor, aParam.mResultPrincipalURI); + WriteIPDLParam(aWriter, aActor, aParam.mUnstrippedURI); + WriteIPDLParam(aWriter, aActor, aParam.mReferrerInfo); + WriteIPDLParam(aWriter, aActor, aParam.mTitle); + WriteIPDLParam(aWriter, aActor, aParam.mName); + WriteIPDLParam(aWriter, aActor, postData); + WriteIPDLParam(aWriter, aActor, aParam.mLoadType); + WriteIPDLParam(aWriter, aActor, aParam.mScrollPositionX); + WriteIPDLParam(aWriter, aActor, aParam.mScrollPositionY); + WriteIPDLParam(aWriter, aActor, stateData); + WriteIPDLParam(aWriter, aActor, aParam.mSrcdocData); + WriteIPDLParam(aWriter, aActor, aParam.mBaseURI); + WriteIPDLParam(aWriter, aActor, aParam.mLoadReplace); + WriteIPDLParam(aWriter, aActor, aParam.mURIWasModified); + WriteIPDLParam(aWriter, aActor, aParam.mScrollRestorationIsManual); + WriteIPDLParam(aWriter, aActor, aParam.mPersist); + WriteIPDLParam(aWriter, aActor, aParam.mHasUserInteraction); + WriteIPDLParam(aWriter, aActor, aParam.mHasUserActivation); + WriteIPDLParam(aWriter, aActor, aParam.mSharedState.Get()->mId); + WriteIPDLParam(aWriter, aActor, + aParam.mSharedState.Get()->mTriggeringPrincipal); + WriteIPDLParam(aWriter, aActor, + aParam.mSharedState.Get()->mPrincipalToInherit); + WriteIPDLParam(aWriter, aActor, + aParam.mSharedState.Get()->mPartitionedPrincipalToInherit); + WriteIPDLParam(aWriter, aActor, aParam.mSharedState.Get()->mCsp); + WriteIPDLParam(aWriter, aActor, aParam.mSharedState.Get()->mContentType); + WriteIPDLParam(aWriter, aActor, + aParam.mSharedState.Get()->mLayoutHistoryState); + WriteIPDLParam(aWriter, aActor, aParam.mSharedState.Get()->mCacheKey); + WriteIPDLParam(aWriter, aActor, + aParam.mSharedState.Get()->mIsFrameNavigation); + WriteIPDLParam(aWriter, aActor, aParam.mSharedState.Get()->mSaveLayoutState); +} + +bool IPDLParamTraits<dom::SessionHistoryInfo>::Read( + IPC::MessageReader* aReader, IProtocol* aActor, + dom::SessionHistoryInfo* aResult) { + Maybe<std::tuple<uint32_t, dom::ClonedMessageData>> stateData; + uint64_t sharedId; + if (!ReadIPDLParam(aReader, aActor, &aResult->mURI) || + !ReadIPDLParam(aReader, aActor, &aResult->mOriginalURI) || + !ReadIPDLParam(aReader, aActor, &aResult->mResultPrincipalURI) || + !ReadIPDLParam(aReader, aActor, &aResult->mUnstrippedURI) || + !ReadIPDLParam(aReader, aActor, &aResult->mReferrerInfo) || + !ReadIPDLParam(aReader, aActor, &aResult->mTitle) || + !ReadIPDLParam(aReader, aActor, &aResult->mName) || + !ReadIPDLParam(aReader, aActor, &aResult->mPostData) || + !ReadIPDLParam(aReader, aActor, &aResult->mLoadType) || + !ReadIPDLParam(aReader, aActor, &aResult->mScrollPositionX) || + !ReadIPDLParam(aReader, aActor, &aResult->mScrollPositionY) || + !ReadIPDLParam(aReader, aActor, &stateData) || + !ReadIPDLParam(aReader, aActor, &aResult->mSrcdocData) || + !ReadIPDLParam(aReader, aActor, &aResult->mBaseURI) || + !ReadIPDLParam(aReader, aActor, &aResult->mLoadReplace) || + !ReadIPDLParam(aReader, aActor, &aResult->mURIWasModified) || + !ReadIPDLParam(aReader, aActor, &aResult->mScrollRestorationIsManual) || + !ReadIPDLParam(aReader, aActor, &aResult->mPersist) || + !ReadIPDLParam(aReader, aActor, &aResult->mHasUserInteraction) || + !ReadIPDLParam(aReader, aActor, &aResult->mHasUserActivation) || + !ReadIPDLParam(aReader, aActor, &sharedId)) { + aActor->FatalError("Error reading fields for SessionHistoryInfo"); + return false; + } + + nsCOMPtr<nsIPrincipal> triggeringPrincipal; + nsCOMPtr<nsIPrincipal> principalToInherit; + nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit; + nsCOMPtr<nsIContentSecurityPolicy> csp; + nsCString contentType; + if (!ReadIPDLParam(aReader, aActor, &triggeringPrincipal) || + !ReadIPDLParam(aReader, aActor, &principalToInherit) || + !ReadIPDLParam(aReader, aActor, &partitionedPrincipalToInherit) || + !ReadIPDLParam(aReader, aActor, &csp) || + !ReadIPDLParam(aReader, aActor, &contentType)) { + aActor->FatalError("Error reading fields for SessionHistoryInfo"); + return false; + } + + // We should always see a cloneable input stream passed to SessionHistoryInfo. + // This is because it will be cloneable when first read in the parent process + // from the nsHttpChannel (which forces streams to be cloneable), and future + // streams in content will be wrapped in + // nsMIMEInputStream(RemoteLazyInputStream) which is also cloneable. + if (aResult->mPostData && !NS_InputStreamIsCloneable(aResult->mPostData)) { + aActor->FatalError( + "Unexpected non-cloneable postData for SessionHistoryInfo"); + return false; + } + + dom::SHEntrySharedParentState* sharedState = nullptr; + if (XRE_IsParentProcess()) { + sharedState = dom::SHEntrySharedParentState::Lookup(sharedId); + } + + if (sharedState) { + aResult->mSharedState.Set(sharedState); + + MOZ_ASSERT(triggeringPrincipal + ? triggeringPrincipal->Equals( + aResult->mSharedState.Get()->mTriggeringPrincipal) + : !aResult->mSharedState.Get()->mTriggeringPrincipal, + "We don't expect this to change!"); + MOZ_ASSERT(principalToInherit + ? principalToInherit->Equals( + aResult->mSharedState.Get()->mPrincipalToInherit) + : !aResult->mSharedState.Get()->mPrincipalToInherit, + "We don't expect this to change!"); + MOZ_ASSERT( + partitionedPrincipalToInherit + ? partitionedPrincipalToInherit->Equals( + aResult->mSharedState.Get()->mPartitionedPrincipalToInherit) + : !aResult->mSharedState.Get()->mPartitionedPrincipalToInherit, + "We don't expect this to change!"); + MOZ_ASSERT( + csp ? nsCSPContext::Equals(csp, aResult->mSharedState.Get()->mCsp) + : !aResult->mSharedState.Get()->mCsp, + "We don't expect this to change!"); + MOZ_ASSERT(contentType.Equals(aResult->mSharedState.Get()->mContentType), + "We don't expect this to change!"); + } else { + aResult->mSharedState.ChangeId(sharedId); + aResult->mSharedState.Get()->mTriggeringPrincipal = + triggeringPrincipal.forget(); + aResult->mSharedState.Get()->mPrincipalToInherit = + principalToInherit.forget(); + aResult->mSharedState.Get()->mPartitionedPrincipalToInherit = + partitionedPrincipalToInherit.forget(); + aResult->mSharedState.Get()->mCsp = csp.forget(); + aResult->mSharedState.Get()->mContentType = contentType; + } + + if (!ReadIPDLParam(aReader, aActor, + &aResult->mSharedState.Get()->mLayoutHistoryState) || + !ReadIPDLParam(aReader, aActor, + &aResult->mSharedState.Get()->mCacheKey) || + !ReadIPDLParam(aReader, aActor, + &aResult->mSharedState.Get()->mIsFrameNavigation) || + !ReadIPDLParam(aReader, aActor, + &aResult->mSharedState.Get()->mSaveLayoutState)) { + aActor->FatalError("Error reading fields for SessionHistoryInfo"); + return false; + } + + if (stateData.isSome()) { + uint32_t version = std::get<0>(*stateData); + aResult->mStateData = new nsStructuredCloneContainer(version); + aResult->mStateData->StealFromClonedMessageData(std::get<1>(*stateData)); + } + MOZ_ASSERT_IF(stateData.isNothing(), !aResult->mStateData); + return true; +} + +void IPDLParamTraits<dom::LoadingSessionHistoryInfo>::Write( + IPC::MessageWriter* aWriter, IProtocol* aActor, + const dom::LoadingSessionHistoryInfo& aParam) { + WriteIPDLParam(aWriter, aActor, aParam.mInfo); + WriteIPDLParam(aWriter, aActor, aParam.mLoadId); + WriteIPDLParam(aWriter, aActor, aParam.mLoadIsFromSessionHistory); + WriteIPDLParam(aWriter, aActor, aParam.mOffset); + WriteIPDLParam(aWriter, aActor, aParam.mLoadingCurrentEntry); + WriteIPDLParam(aWriter, aActor, aParam.mForceMaybeResetName); +} + +bool IPDLParamTraits<dom::LoadingSessionHistoryInfo>::Read( + IPC::MessageReader* aReader, IProtocol* aActor, + dom::LoadingSessionHistoryInfo* aResult) { + if (!ReadIPDLParam(aReader, aActor, &aResult->mInfo) || + !ReadIPDLParam(aReader, aActor, &aResult->mLoadId) || + !ReadIPDLParam(aReader, aActor, &aResult->mLoadIsFromSessionHistory) || + !ReadIPDLParam(aReader, aActor, &aResult->mOffset) || + !ReadIPDLParam(aReader, aActor, &aResult->mLoadingCurrentEntry) || + !ReadIPDLParam(aReader, aActor, &aResult->mForceMaybeResetName)) { + aActor->FatalError("Error reading fields for LoadingSessionHistoryInfo"); + return false; + } + + return true; +} + +void IPDLParamTraits<nsILayoutHistoryState*>::Write( + IPC::MessageWriter* aWriter, IProtocol* aActor, + nsILayoutHistoryState* aParam) { + if (aParam) { + WriteIPDLParam(aWriter, aActor, true); + bool scrollPositionOnly = false; + nsTArray<nsCString> keys; + nsTArray<mozilla::PresState> states; + aParam->GetContents(&scrollPositionOnly, keys, states); + WriteIPDLParam(aWriter, aActor, scrollPositionOnly); + WriteIPDLParam(aWriter, aActor, keys); + WriteIPDLParam(aWriter, aActor, states); + } else { + WriteIPDLParam(aWriter, aActor, false); + } +} + +bool IPDLParamTraits<nsILayoutHistoryState*>::Read( + IPC::MessageReader* aReader, IProtocol* aActor, + RefPtr<nsILayoutHistoryState>* aResult) { + bool hasLayoutHistoryState = false; + if (!ReadIPDLParam(aReader, aActor, &hasLayoutHistoryState)) { + aActor->FatalError("Error reading fields for nsILayoutHistoryState"); + return false; + } + + if (hasLayoutHistoryState) { + bool scrollPositionOnly = false; + nsTArray<nsCString> keys; + nsTArray<mozilla::PresState> states; + if (!ReadIPDLParam(aReader, aActor, &scrollPositionOnly) || + !ReadIPDLParam(aReader, aActor, &keys) || + !ReadIPDLParam(aReader, aActor, &states)) { + aActor->FatalError("Error reading fields for nsILayoutHistoryState"); + } + + if (keys.Length() != states.Length()) { + aActor->FatalError("Error reading fields for nsILayoutHistoryState"); + return false; + } + + *aResult = NS_NewLayoutHistoryState(); + (*aResult)->SetScrollPositionOnly(scrollPositionOnly); + for (uint32_t i = 0; i < keys.Length(); ++i) { + PresState& state = states[i]; + UniquePtr<PresState> newState = MakeUnique<PresState>(state); + (*aResult)->AddState(keys[i], std::move(newState)); + } + } + return true; +} + +void IPDLParamTraits<mozilla::dom::Wireframe>::Write( + IPC::MessageWriter* aWriter, IProtocol* aActor, + const mozilla::dom::Wireframe& aParam) { + WriteParam(aWriter, aParam.mCanvasBackground); + WriteParam(aWriter, aParam.mRects); +} + +bool IPDLParamTraits<mozilla::dom::Wireframe>::Read( + IPC::MessageReader* aReader, IProtocol* aActor, + mozilla::dom::Wireframe* aResult) { + return ReadParam(aReader, &aResult->mCanvasBackground) && + ReadParam(aReader, &aResult->mRects); +} + +} // namespace ipc +} // namespace mozilla + +namespace IPC { +// Allow sending mozilla::dom::WireframeRectType enums over IPC. +template <> +struct ParamTraits<mozilla::dom::WireframeRectType> + : public ContiguousEnumSerializer< + mozilla::dom::WireframeRectType, + mozilla::dom::WireframeRectType::Image, + mozilla::dom::WireframeRectType::EndGuard_> {}; + +template <> +struct ParamTraits<mozilla::dom::WireframeTaggedRect> { + static void Write(MessageWriter* aWriter, + const mozilla::dom::WireframeTaggedRect& aParam); + static bool Read(MessageReader* aReader, + mozilla::dom::WireframeTaggedRect* aResult); +}; + +void ParamTraits<mozilla::dom::WireframeTaggedRect>::Write( + MessageWriter* aWriter, const mozilla::dom::WireframeTaggedRect& aParam) { + WriteParam(aWriter, aParam.mColor); + WriteParam(aWriter, aParam.mType); + WriteParam(aWriter, aParam.mX); + WriteParam(aWriter, aParam.mY); + WriteParam(aWriter, aParam.mWidth); + WriteParam(aWriter, aParam.mHeight); +} + +bool ParamTraits<mozilla::dom::WireframeTaggedRect>::Read( + IPC::MessageReader* aReader, mozilla::dom::WireframeTaggedRect* aResult) { + return ReadParam(aReader, &aResult->mColor) && + ReadParam(aReader, &aResult->mType) && + ReadParam(aReader, &aResult->mX) && ReadParam(aReader, &aResult->mY) && + ReadParam(aReader, &aResult->mWidth) && + ReadParam(aReader, &aResult->mHeight); +} +} // namespace IPC |