/* -*- 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 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 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 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 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 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 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(mSharedState.Get())->mSaveLayoutState = aSaveLayoutStateFlag; } void SessionHistoryInfo::FillLoadInfo(nsDocShellLoadState& aLoadState) const { aLoadState.SetOriginalURI(mOriginalURI); aLoadState.SetMaybeResultPrincipalURI(Some(mResultPrincipalURI)); aLoadState.SetUnstrippedURI(mUnstrippedURI); aLoadState.SetLoadReplace(mLoadReplace); nsCOMPtr 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 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. aLoadState.SetIsExemptFromHTTPSOnlyMode(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( aTriggeringPrincipal, aPrincipalToInherit, aPartitionedPrincipalToInherit, aCsp, aContentType)); } SessionHistoryInfo::SharedState::SharedState() { Init(); } SessionHistoryInfo::SharedState::SharedState( const SessionHistoryInfo::SharedState& aOther) { Init(aOther); } SessionHistoryInfo::SharedState::SharedState( const Maybe& aOther) { if (aOther.isSome()) { Init(aOther.ref()); } else { Init(); } } SessionHistoryInfo::SharedState::~SharedState() { if (XRE_IsParentProcess()) { mParent .RefPtr::~RefPtr(); } else { mChild.UniquePtr::~UniquePtr(); } } SessionHistoryInfo::SharedState& SessionHistoryInfo::SharedState::operator=( const SessionHistoryInfo::SharedState& aOther) { if (this != &aOther) { if (XRE_IsParentProcess()) { mParent = aOther.mParent; } else { mChild = MakeUnique(*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(new SHEntrySharedParentState()); } else { new (&mChild) UniquePtr(MakeUnique()); } } void SessionHistoryInfo::SharedState::Init( const SessionHistoryInfo::SharedState& aOther) { if (XRE_IsParentProcess()) { new (&mParent) RefPtr(aOther.mParent); } else { new (&mChild) UniquePtr( MakeUnique(*aOther.mChild)); } } static uint64_t gLoadingSessionHistoryInfoLoadId = 0; nsTHashMap* 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 LoadingSessionHistoryInfo::CreateLoadInfo() const { RefPtr 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(); } MOZ_LOG( gSHLog, LogLevel::Verbose, ("SessionHistoryEntry::SetByLoadId(%" PRIu64 " - %p)", aLoadId, aEntry)); sLoadIdToEntry->InsertOrUpdate( aLoadId, LoadingEntry{ .mEntry = aEntry, .mInfoSnapshotForValidation = MakeUnique(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(*aInfo)), mID(++gEntryID) { MOZ_ASSERT(mozilla::SessionHistoryInParent()); } SessionHistoryEntry::SessionHistoryEntry(const SessionHistoryEntry& aEntry) : mInfo(MakeUnique(*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 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 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 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 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 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 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 referrerInfo = mInfo->mReferrerInfo; referrerInfo.forget(aReferrerInfo); return NS_OK; } NS_IMETHODIMP SessionHistoryEntry::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) { mInfo->mReferrerInfo = aReferrerInfo; return NS_OK; } NS_IMETHODIMP SessionHistoryEntry::GetContentViewer(nsIContentViewer** aContentViewer) { *aContentViewer = nullptr; return NS_OK; } NS_IMETHODIMP SessionHistoryEntry::SetContentViewer(nsIContentViewer* aContentViewer) { 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 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 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 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 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 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 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 stateData = mInfo->mStateData; stateData.forget(aStateData); return NS_OK; } NS_IMETHODIMP SessionHistoryEntry::SetStateData(nsIStructuredCloneContainer* aStateData) { mInfo->mStateData = static_cast(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 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 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 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 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& 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 she = do_QueryInterface(aEntry); NS_ENSURE_STATE(she && she->mInfo->mSharedState.Get()); mInfo->mSharedState = static_cast(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(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 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 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 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 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 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 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 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 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 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 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(*aSource.mInfo); mChildren.Clear(); } SHEntrySharedParentState* SessionHistoryEntry::SharedInfo() const { return static_cast(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 shistory; GetShistory(getter_AddRefs(shistory)); if (shistory) { int32_t index = 0; shistory->GetIndex(&index); shistory->EvictOutOfRangeContentViewers(index); } } } nsFrameLoader* SessionHistoryEntry::GetFrameLoader() { return SharedInfo()->mFrameLoader; } void SessionHistoryEntry::SetInfo(SessionHistoryInfo* aInfo) { // FIXME Assert that we're not changing shared state! mInfo = MakeUnique(*aInfo); } } // namespace dom namespace ipc { void IPDLParamTraits::Write( IPC::MessageWriter* aWriter, IProtocol* aActor, const dom::SessionHistoryInfo& aParam) { nsCOMPtr postData = aParam.GetPostData(); Maybe> 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::Read( IPC::MessageReader* aReader, IProtocol* aActor, dom::SessionHistoryInfo* aResult) { Maybe> 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 triggeringPrincipal; nsCOMPtr principalToInherit; nsCOMPtr partitionedPrincipalToInherit; nsCOMPtr 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::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::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::Write( IPC::MessageWriter* aWriter, IProtocol* aActor, nsILayoutHistoryState* aParam) { if (aParam) { WriteIPDLParam(aWriter, aActor, true); bool scrollPositionOnly = false; nsTArray keys; nsTArray 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::Read( IPC::MessageReader* aReader, IProtocol* aActor, RefPtr* aResult) { bool hasLayoutHistoryState = false; if (!ReadIPDLParam(aReader, aActor, &hasLayoutHistoryState)) { aActor->FatalError("Error reading fields for nsILayoutHistoryState"); return false; } if (hasLayoutHistoryState) { bool scrollPositionOnly = false; nsTArray keys; nsTArray 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 newState = MakeUnique(state); (*aResult)->AddState(keys[i], std::move(newState)); } } return true; } void IPDLParamTraits::Write( IPC::MessageWriter* aWriter, IProtocol* aActor, const mozilla::dom::Wireframe& aParam) { WriteParam(aWriter, aParam.mCanvasBackground); WriteParam(aWriter, aParam.mRects); } bool IPDLParamTraits::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 : public ContiguousEnumSerializer< mozilla::dom::WireframeRectType, mozilla::dom::WireframeRectType::Image, mozilla::dom::WireframeRectType::EndGuard_> {}; template <> struct ParamTraits { static void Write(MessageWriter* aWriter, const mozilla::dom::WireframeTaggedRect& aParam); static bool Read(MessageReader* aReader, mozilla::dom::WireframeTaggedRect* aResult); }; void ParamTraits::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::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