/* -*- 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 "nsSHEntry.h" #include #include "nsDocShell.h" #include "nsDocShellEditorData.h" #include "nsDocShellLoadState.h" #include "nsDocShellLoadTypes.h" #include "nsIContentSecurityPolicy.h" #include "nsIDocShellTreeItem.h" #include "nsIDocumentViewer.h" #include "nsIInputStream.h" #include "nsILayoutHistoryState.h" #include "nsIMutableArray.h" #include "nsIStructuredCloneContainer.h" #include "nsIURI.h" #include "nsSHEntryShared.h" #include "nsSHistory.h" #include "mozilla/Logging.h" #include "nsIReferrerInfo.h" extern mozilla::LazyLogModule gPageCacheLog; static uint32_t gEntryID = 0; nsSHEntry::nsSHEntry() : mShared(new nsSHEntryShared()), mLoadType(0), mID(++gEntryID), // SessionStore has special handling for 0 values. mScrollPositionX(0), mScrollPositionY(0), mLoadReplace(false), mURIWasModified(false), mIsSrcdocEntry(false), mScrollRestorationIsManual(false), mLoadedInThisProcess(false), mPersist(true), mHasUserInteraction(false), mHasUserActivation(false) {} nsSHEntry::nsSHEntry(const nsSHEntry& aOther) : mShared(aOther.mShared), mURI(aOther.mURI), mOriginalURI(aOther.mOriginalURI), mResultPrincipalURI(aOther.mResultPrincipalURI), mUnstrippedURI(aOther.mUnstrippedURI), mReferrerInfo(aOther.mReferrerInfo), mTitle(aOther.mTitle), mPostData(aOther.mPostData), mLoadType(0), // XXX why not copy? mID(aOther.mID), mScrollPositionX(0), // XXX why not copy? mScrollPositionY(0), // XXX why not copy? mParent(aOther.mParent), mStateData(aOther.mStateData), mSrcdocData(aOther.mSrcdocData), mBaseURI(aOther.mBaseURI), mLoadReplace(aOther.mLoadReplace), mURIWasModified(aOther.mURIWasModified), mIsSrcdocEntry(aOther.mIsSrcdocEntry), mScrollRestorationIsManual(false), mLoadedInThisProcess(aOther.mLoadedInThisProcess), mPersist(aOther.mPersist), mHasUserInteraction(false), mHasUserActivation(aOther.mHasUserActivation) {} nsSHEntry::~nsSHEntry() { // Null out the mParent pointers on all our kids. for (nsISHEntry* entry : mChildren) { if (entry) { entry->SetParent(nullptr); } } } NS_IMPL_ISUPPORTS(nsSHEntry, nsISHEntry, nsISupportsWeakReference) NS_IMETHODIMP nsSHEntry::SetScrollPosition(int32_t aX, int32_t aY) { mScrollPositionX = aX; mScrollPositionY = aY; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetScrollPosition(int32_t* aX, int32_t* aY) { *aX = mScrollPositionX; *aY = mScrollPositionY; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetURIWasModified(bool* aOut) { *aOut = mURIWasModified; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetURIWasModified(bool aIn) { mURIWasModified = aIn; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetURI(nsIURI** aURI) { *aURI = mURI; NS_IF_ADDREF(*aURI); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetURI(nsIURI* aURI) { mURI = aURI; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetOriginalURI(nsIURI** aOriginalURI) { *aOriginalURI = mOriginalURI; NS_IF_ADDREF(*aOriginalURI); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetOriginalURI(nsIURI* aOriginalURI) { mOriginalURI = aOriginalURI; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetResultPrincipalURI(nsIURI** aResultPrincipalURI) { *aResultPrincipalURI = mResultPrincipalURI; NS_IF_ADDREF(*aResultPrincipalURI); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetResultPrincipalURI(nsIURI* aResultPrincipalURI) { mResultPrincipalURI = aResultPrincipalURI; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetUnstrippedURI(nsIURI** aUnstrippedURI) { *aUnstrippedURI = mUnstrippedURI; NS_IF_ADDREF(*aUnstrippedURI); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetUnstrippedURI(nsIURI* aUnstrippedURI) { mUnstrippedURI = aUnstrippedURI; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetLoadReplace(bool* aLoadReplace) { *aLoadReplace = mLoadReplace; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetLoadReplace(bool aLoadReplace) { mLoadReplace = aLoadReplace; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) { *aReferrerInfo = mReferrerInfo; NS_IF_ADDREF(*aReferrerInfo); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) { mReferrerInfo = aReferrerInfo; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetSticky(bool aSticky) { mShared->mSticky = aSticky; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetSticky(bool* aSticky) { *aSticky = mShared->mSticky; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetTitle(nsAString& aTitle) { // Check for empty title... if (mTitle.IsEmpty() && mURI) { // Default title is the URL. nsAutoCString spec; if (NS_SUCCEEDED(mURI->GetSpec(spec))) { AppendUTF8toUTF16(spec, mTitle); } } aTitle = mTitle; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetTitle(const nsAString& aTitle) { mTitle = aTitle; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetName(nsAString& aName) { aName = mName; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetName(const nsAString& aName) { mName = aName; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetPostData(nsIInputStream** aResult) { *aResult = mPostData; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetPostData(nsIInputStream* aPostData) { mPostData = aPostData; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetHasPostData(bool* aResult) { *aResult = !!mPostData; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetLayoutHistoryState(nsILayoutHistoryState** aResult) { *aResult = mShared->mLayoutHistoryState; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetLayoutHistoryState(nsILayoutHistoryState* aState) { mShared->mLayoutHistoryState = aState; if (mShared->mLayoutHistoryState) { mShared->mLayoutHistoryState->SetScrollPositionOnly( !mShared->mSaveLayoutState); } return NS_OK; } NS_IMETHODIMP nsSHEntry::InitLayoutHistoryState(nsILayoutHistoryState** aState) { if (!mShared->mLayoutHistoryState) { nsCOMPtr historyState; historyState = NS_NewLayoutHistoryState(); SetLayoutHistoryState(historyState); } nsCOMPtr state = GetLayoutHistoryState(); state.forget(aState); return NS_OK; } NS_IMETHODIMP nsSHEntry::GetLoadType(uint32_t* aResult) { *aResult = mLoadType; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetLoadType(uint32_t aLoadType) { mLoadType = aLoadType; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetID(uint32_t* aResult) { *aResult = mID; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetID(uint32_t aID) { mID = aID; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetIsSubFrame(bool* aFlag) { *aFlag = mShared->mIsFrameNavigation; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetIsSubFrame(bool aFlag) { mShared->mIsFrameNavigation = aFlag; return NS_OK; } NS_IMETHODIMP nsSHEntry::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 = mHasUserInteraction; } else { nsCOMPtr root = nsSHistory::GetRootSHEntry(this); root->GetHasUserInteraction(aFlag); } return NS_OK; } NS_IMETHODIMP nsSHEntry::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) { mHasUserInteraction = aFlag; } else { nsCOMPtr root = nsSHistory::GetRootSHEntry(this); root->SetHasUserInteraction(aFlag); } return NS_OK; } NS_IMETHODIMP nsSHEntry::GetHasUserActivation(bool* aFlag) { *aFlag = mHasUserActivation; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetHasUserActivation(bool aFlag) { mHasUserActivation = aFlag; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetCacheKey(uint32_t* aResult) { *aResult = mShared->mCacheKey; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetCacheKey(uint32_t aCacheKey) { mShared->mCacheKey = aCacheKey; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetContentType(nsACString& aContentType) { aContentType = mShared->mContentType; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetContentType(const nsACString& aContentType) { mShared->mContentType = aContentType; return NS_OK; } NS_IMETHODIMP nsSHEntry::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& aSrcdocData, bool aSrcdocEntry, nsIURI* aBaseURI, bool aSaveLayoutState, bool aExpired, bool aUserActivation) { MOZ_ASSERT( aTriggeringPrincipal, "need a valid triggeringPrincipal to create a session history entry"); mURI = aURI; mTitle = aTitle; mPostData = aInputStream; // Set the LoadType by default to loadHistory during creation mLoadType = LOAD_HISTORY; mShared->mCacheKey = aCacheKey; mShared->mContentType = aContentType; mShared->mTriggeringPrincipal = aTriggeringPrincipal; mShared->mPrincipalToInherit = aPrincipalToInherit; mShared->mPartitionedPrincipalToInherit = aPartitionedPrincipalToInherit; mShared->mCsp = aCsp; mShared->mDocShellID = aDocShellID; mShared->mDynamicallyCreated = aDynamicCreation; // By default all entries are set false for subframe flag. // nsDocShell::CloneAndReplace() which creates entries for // all subframe navigations, sets the flag to true. mShared->mIsFrameNavigation = false; mHasUserInteraction = false; mShared->mExpired = aExpired; mIsSrcdocEntry = aSrcdocEntry; mSrcdocData = aSrcdocData; mBaseURI = aBaseURI; mLoadedInThisProcess = true; mOriginalURI = aOriginalURI; mResultPrincipalURI = aResultPrincipalURI; mUnstrippedURI = aUnstrippedURI; mLoadReplace = aLoadReplace; mReferrerInfo = aReferrerInfo; mHasUserActivation = aUserActivation; mShared->mLayoutHistoryState = nullptr; mShared->mSaveLayoutState = aSaveLayoutState; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetParent(nsISHEntry** aResult) { nsCOMPtr parent = do_QueryReferent(mParent); parent.forget(aResult); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetParent(nsISHEntry* aParent) { mParent = do_GetWeakReference(aParent); return NS_OK; } NS_IMETHODIMP_(void) nsSHEntry::SetViewerBounds(const nsIntRect& aBounds) { mShared->mViewerBounds = aBounds; } NS_IMETHODIMP_(void) nsSHEntry::GetViewerBounds(nsIntRect& aBounds) { aBounds = mShared->mViewerBounds; } NS_IMETHODIMP nsSHEntry::GetTriggeringPrincipal(nsIPrincipal** aTriggeringPrincipal) { NS_IF_ADDREF(*aTriggeringPrincipal = mShared->mTriggeringPrincipal); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal) { mShared->mTriggeringPrincipal = aTriggeringPrincipal; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetPrincipalToInherit(nsIPrincipal** aPrincipalToInherit) { NS_IF_ADDREF(*aPrincipalToInherit = mShared->mPrincipalToInherit); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit) { mShared->mPrincipalToInherit = aPrincipalToInherit; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetPartitionedPrincipalToInherit( nsIPrincipal** aPartitionedPrincipalToInherit) { NS_IF_ADDREF(*aPartitionedPrincipalToInherit = mShared->mPartitionedPrincipalToInherit); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetPartitionedPrincipalToInherit( nsIPrincipal* aPartitionedPrincipalToInherit) { mShared->mPartitionedPrincipalToInherit = aPartitionedPrincipalToInherit; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetCsp(nsIContentSecurityPolicy** aCsp) { NS_IF_ADDREF(*aCsp = mShared->mCsp); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetCsp(nsIContentSecurityPolicy* aCsp) { mShared->mCsp = aCsp; return NS_OK; } NS_IMETHODIMP nsSHEntry::AdoptBFCacheEntry(nsISHEntry* aEntry) { nsSHEntryShared* shared = static_cast(aEntry)->mShared; NS_ENSURE_STATE(shared); mShared = shared; return NS_OK; } NS_IMETHODIMP nsSHEntry::SharesDocumentWith(nsISHEntry* aEntry, bool* aOut) { NS_ENSURE_ARG_POINTER(aOut); *aOut = mShared == static_cast(aEntry)->mShared; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetIsSrcdocEntry(bool* aIsSrcdocEntry) { *aIsSrcdocEntry = mIsSrcdocEntry; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetSrcdocData(nsAString& aSrcdocData) { aSrcdocData = mSrcdocData; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetSrcdocData(const nsAString& aSrcdocData) { mSrcdocData = aSrcdocData; mIsSrcdocEntry = true; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetBaseURI(nsIURI** aBaseURI) { *aBaseURI = mBaseURI; NS_IF_ADDREF(*aBaseURI); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetBaseURI(nsIURI* aBaseURI) { mBaseURI = aBaseURI; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetScrollRestorationIsManual(bool* aIsManual) { *aIsManual = mScrollRestorationIsManual; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetScrollRestorationIsManual(bool aIsManual) { mScrollRestorationIsManual = aIsManual; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetLoadedInThisProcess(bool* aLoadedInThisProcess) { *aLoadedInThisProcess = mLoadedInThisProcess; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetChildCount(int32_t* aCount) { *aCount = mChildren.Count(); return NS_OK; } NS_IMETHODIMP nsSHEntry::AddChild(nsISHEntry* aChild, int32_t aOffset, bool aUseRemoteSubframes) { if (aChild) { NS_ENSURE_SUCCESS(aChild->SetParent(this), NS_ERROR_FAILURE); } if (aOffset < 0) { mChildren.AppendObject(aChild); return NS_OK; } // // 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. // // Assert that aOffset will not be so high as to grow us a lot. // NS_ASSERTION(aOffset < (mChildren.Count() + 1023), "Large frames array!\n"); bool newChildIsDyn = aChild ? aChild->IsDynamicallyAdded() : false; // 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 (newChildIsDyn) { int32_t lastNonDyn = aOffset - 1; for (int32_t i = aOffset; i < mChildren.Count(); ++i) { nsISHEntry* entry = mChildren[i]; if (entry) { if (entry->IsDynamicallyAdded()) { break; } else { lastNonDyn = i; } } } // InsertObjectAt allows only appending one object. // If aOffset is larger than Count(), we must first manually // set the capacity. if (aOffset > mChildren.Count()) { mChildren.SetCount(aOffset); } if (!mChildren.InsertObjectAt(aChild, lastNonDyn + 1)) { NS_WARNING("Adding a child failed!"); aChild->SetParent(nullptr); return NS_ERROR_FAILURE; } } else { // 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 (mChildren.Count() > 0) { int32_t start = std::min(mChildren.Count() - 1, aOffset); int32_t dynEntryIndex = -1; nsISHEntry* dynEntry = nullptr; for (int32_t i = start; i >= 0; --i) { nsISHEntry* entry = mChildren[i]; if (entry) { if (entry->IsDynamicallyAdded()) { dynEntryIndex = i; dynEntry = entry; } else { break; } } } if (dynEntry) { nsCOMArray tmp; tmp.SetCount(aOffset - dynEntryIndex + 1); mChildren.InsertObjectsAt(tmp, dynEntryIndex); NS_ASSERTION(mChildren[aOffset + 1] == dynEntry, "Whaat?"); } } // Make sure there isn't anything at aOffset. if (aOffset < mChildren.Count()) { nsISHEntry* 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, "Adding a child where we already have a child? This may misbehave"); oldChild->SetParent(nullptr); } } mChildren.ReplaceObjectAt(aChild, aOffset); } return NS_OK; } NS_IMETHODIMP nsSHEntry::RemoveChild(nsISHEntry* aChild) { NS_ENSURE_TRUE(aChild, NS_ERROR_FAILURE); bool childRemoved = false; if (aChild->IsDynamicallyAdded()) { childRemoved = mChildren.RemoveObject(aChild); } else { int32_t index = mChildren.IndexOfObject(aChild); if (index >= 0) { // Other alive non-dynamic child docshells still keep mChildOffset, // so we don't want to change the indices here. mChildren.ReplaceObjectAt(nullptr, index); childRemoved = true; } } if (childRemoved) { aChild->SetParent(nullptr); // reduce the child count, i.e. remove empty children at the end for (int32_t i = mChildren.Count() - 1; i >= 0 && !mChildren[i]; --i) { if (!mChildren.RemoveObjectAt(i)) { break; } } } return NS_OK; } NS_IMETHODIMP nsSHEntry::GetChildAt(int32_t aIndex, nsISHEntry** aResult) { if (aIndex >= 0 && aIndex < mChildren.Count()) { *aResult = mChildren[aIndex]; // yes, mChildren can have holes in it. AddChild's offset parameter makes // that possible. NS_IF_ADDREF(*aResult); } else { *aResult = nullptr; } return NS_OK; } NS_IMETHODIMP_(void) nsSHEntry::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(mLoadType) || 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 (mShared->mExpired && (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(mLoadType); } } NS_IMETHODIMP nsSHEntry::ReplaceChild(nsISHEntry* aNewEntry) { NS_ENSURE_STATE(aNewEntry); nsID docshellID; aNewEntry->GetDocshellID(docshellID); for (int32_t i = 0; i < mChildren.Count(); ++i) { if (mChildren[i]) { nsID childDocshellID; nsresult rv = mChildren[i]->GetDocshellID(childDocshellID); NS_ENSURE_SUCCESS(rv, rv); if (docshellID == childDocshellID) { mChildren[i]->SetParent(nullptr); mChildren.ReplaceObjectAt(aNewEntry, i); return aNewEntry->SetParent(this); } } } return NS_ERROR_FAILURE; } NS_IMETHODIMP_(void) nsSHEntry::ClearEntry() { int32_t childCount = GetChildCount(); // Remove all children of this entry for (int32_t i = childCount - 1; i >= 0; i--) { nsCOMPtr child; GetChildAt(i, getter_AddRefs(child)); RemoveChild(child); } AbandonBFCacheEntry(); } NS_IMETHODIMP nsSHEntry::GetStateData(nsIStructuredCloneContainer** aContainer) { NS_IF_ADDREF(*aContainer = mStateData); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetStateData(nsIStructuredCloneContainer* aContainer) { mStateData = aContainer; return NS_OK; } NS_IMETHODIMP_(bool) nsSHEntry::IsDynamicallyAdded() { return mShared->mDynamicallyCreated; } NS_IMETHODIMP nsSHEntry::HasDynamicallyAddedChild(bool* aAdded) { *aAdded = false; for (int32_t i = 0; i < mChildren.Count(); ++i) { nsISHEntry* entry = mChildren[i]; if (entry) { *aAdded = entry->IsDynamicallyAdded(); if (*aAdded) { break; } } } return NS_OK; } NS_IMETHODIMP nsSHEntry::GetDocshellID(nsID& aID) { aID = mShared->mDocShellID; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetDocshellID(const nsID& aID) { mShared->mDocShellID = aID; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetLastTouched(uint32_t* aLastTouched) { *aLastTouched = mShared->mLastTouched; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetLastTouched(uint32_t aLastTouched) { mShared->mLastTouched = aLastTouched; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetShistory(nsISHistory** aSHistory) { nsCOMPtr shistory(do_QueryReferent(mShared->mSHistory)); shistory.forget(aSHistory); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetShistory(nsISHistory* aSHistory) { nsWeakPtr shistory = do_GetWeakReference(aSHistory); // mSHistory can not be changed once it's set MOZ_ASSERT(!mShared->mSHistory || (mShared->mSHistory == shistory)); mShared->mSHistory = shistory; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetLoadTypeAsHistory() { // Set the LoadType by default to loadHistory during creation mLoadType = LOAD_HISTORY; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetPersist(bool* aPersist) { *aPersist = mPersist; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetPersist(bool aPersist) { mPersist = aPersist; return NS_OK; } NS_IMETHODIMP nsSHEntry::CreateLoadInfo(nsDocShellLoadState** aLoadState) { nsCOMPtr uri = GetURI(); RefPtr loadState(new nsDocShellLoadState(uri)); nsCOMPtr originalURI = GetOriginalURI(); loadState->SetOriginalURI(originalURI); mozilla::Maybe> emplacedResultPrincipalURI; nsCOMPtr resultPrincipalURI = GetResultPrincipalURI(); emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI)); loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI); nsCOMPtr unstrippedURI = GetUnstrippedURI(); loadState->SetUnstrippedURI(unstrippedURI); loadState->SetLoadReplace(GetLoadReplace()); nsCOMPtr postData = GetPostData(); loadState->SetPostDataStream(postData); nsAutoCString contentType; GetContentType(contentType); loadState->SetTypeHint(contentType); nsCOMPtr triggeringPrincipal = GetTriggeringPrincipal(); loadState->SetTriggeringPrincipal(triggeringPrincipal); nsCOMPtr principalToInherit = GetPrincipalToInherit(); loadState->SetPrincipalToInherit(principalToInherit); nsCOMPtr partitionedPrincipalToInherit = GetPartitionedPrincipalToInherit(); loadState->SetPartitionedPrincipalToInherit(partitionedPrincipalToInherit); nsCOMPtr csp = GetCsp(); loadState->SetCsp(csp); nsCOMPtr referrerInfo = GetReferrerInfo(); loadState->SetReferrerInfo(referrerInfo); // 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 (GetIsSrcdocEntry()) { GetSrcdocData(srcdoc); baseURI = GetBaseURI(); flags |= nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC; } else { srcdoc = VoidString(); } loadState->SetSrcdocData(srcdoc); loadState->SetBaseURI(baseURI); loadState->SetInternalLoadFlags(flags); loadState->SetFirstParty(true); const bool hasUserActivation = GetHasUserActivation(); loadState->SetHasValidUserGestureActivation(hasUserActivation); loadState->SetTextDirectiveUserActivation(hasUserActivation); loadState->SetSHEntry(this); // When we create a load state from the history entry 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. loadState->SetIsExemptFromHTTPSFirstMode(true); loadState.forget(aLoadState); return NS_OK; } NS_IMETHODIMP_(void) nsSHEntry::SyncTreesForSubframeNavigation( nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aTopBC, mozilla::dom::BrowsingContext* aIgnoreBC) { // XXX Keep this in sync with // SessionHistoryEntry::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 nsSHEntry::EvictDocumentViewer() { nsCOMPtr viewer = GetDocumentViewer(); if (viewer) { mShared->NotifyListenersDocumentViewerEvicted(); // Drop the presentation state before destroying the viewer, so that // document teardown is able to correctly persist the state. SetDocumentViewer(nullptr); SyncPresentationState(); viewer->Destroy(); } } NS_IMETHODIMP nsSHEntry::SetDocumentViewer(nsIDocumentViewer* aViewer) { return GetState()->SetDocumentViewer(aViewer); } NS_IMETHODIMP nsSHEntry::GetDocumentViewer(nsIDocumentViewer** aResult) { *aResult = GetState()->mDocumentViewer; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsSHEntry::GetIsInBFCache(bool* aResult) { *aResult = !!GetState()->mDocumentViewer; return NS_OK; } NS_IMETHODIMP nsSHEntry::Clone(nsISHEntry** aResult) { nsCOMPtr entry = new nsSHEntry(*this); entry.forget(aResult); return NS_OK; } NS_IMETHODIMP nsSHEntry::GetSaveLayoutStateFlag(bool* aFlag) { *aFlag = mShared->mSaveLayoutState; return NS_OK; } NS_IMETHODIMP nsSHEntry::SetSaveLayoutStateFlag(bool aFlag) { mShared->mSaveLayoutState = aFlag; if (mShared->mLayoutHistoryState) { mShared->mLayoutHistoryState->SetScrollPositionOnly(!aFlag); } return NS_OK; } NS_IMETHODIMP nsSHEntry::SetWindowState(nsISupports* aState) { GetState()->mWindowState = aState; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetWindowState(nsISupports** aState) { NS_IF_ADDREF(*aState = GetState()->mWindowState); return NS_OK; } NS_IMETHODIMP nsSHEntry::GetRefreshURIList(nsIMutableArray** aList) { NS_IF_ADDREF(*aList = GetState()->mRefreshURIList); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetRefreshURIList(nsIMutableArray* aList) { GetState()->mRefreshURIList = aList; return NS_OK; } NS_IMETHODIMP_(void) nsSHEntry::AddChildShell(nsIDocShellTreeItem* aShell) { MOZ_ASSERT(aShell, "Null child shell added to history entry"); GetState()->mChildShells.AppendObject(aShell); } NS_IMETHODIMP nsSHEntry::ChildShellAt(int32_t aIndex, nsIDocShellTreeItem** aShell) { NS_IF_ADDREF(*aShell = GetState()->mChildShells.SafeObjectAt(aIndex)); return NS_OK; } NS_IMETHODIMP_(void) nsSHEntry::ClearChildShells() { GetState()->mChildShells.Clear(); } NS_IMETHODIMP_(void) nsSHEntry::SyncPresentationState() { GetState()->SyncPresentationState(); } nsDocShellEditorData* nsSHEntry::ForgetEditorData() { // XXX jlebar Check how this is used. return GetState()->mEditorData.release(); } void nsSHEntry::SetEditorData(nsDocShellEditorData* aData) { NS_ASSERTION(!(aData && GetState()->mEditorData), "We're going to overwrite an owning ref!"); if (GetState()->mEditorData != aData) { GetState()->mEditorData = mozilla::WrapUnique(aData); } } bool nsSHEntry::HasDetachedEditor() { return GetState()->mEditorData != nullptr; } bool nsSHEntry::HasBFCacheEntry( mozilla::dom::SHEntrySharedParentState* aEntry) { return GetState() == aEntry; } NS_IMETHODIMP nsSHEntry::AbandonBFCacheEntry() { mShared = GetState()->Duplicate(); return NS_OK; } NS_IMETHODIMP nsSHEntry::GetBfcacheID(uint64_t* aBFCacheID) { *aBFCacheID = mShared->GetId(); return NS_OK; } NS_IMETHODIMP nsSHEntry::GetWireframe(JSContext* aCx, JS::MutableHandle aOut) { aOut.set(JS::NullValue()); return NS_OK; } NS_IMETHODIMP nsSHEntry::SetWireframe(JSContext* aCx, JS::Handle aArg) { return NS_ERROR_NOT_IMPLEMENTED; }