summaryrefslogtreecommitdiffstats
path: root/docshell/shistory/nsSHEntry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docshell/shistory/nsSHEntry.cpp')
-rw-r--r--docshell/shistory/nsSHEntry.cpp1131
1 files changed, 1131 insertions, 0 deletions
diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp
new file mode 100644
index 0000000000..8fc4f74844
--- /dev/null
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -0,0 +1,1131 @@
+/* -*- 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 <algorithm>
+
+#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<nsILayoutHistoryState> historyState;
+ historyState = NS_NewLayoutHistoryState();
+ SetLayoutHistoryState(historyState);
+ }
+
+ nsCOMPtr<nsILayoutHistoryState> 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<nsISHEntry> 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<nsISHEntry> 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<nsISHEntry> 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<nsSHEntry*>(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<nsSHEntry*>(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<nsISHEntry> 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<nsISHEntry> 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<nsISHistory> 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<nsIURI> uri = GetURI();
+ RefPtr<nsDocShellLoadState> loadState(new nsDocShellLoadState(uri));
+
+ nsCOMPtr<nsIURI> originalURI = GetOriginalURI();
+ loadState->SetOriginalURI(originalURI);
+
+ mozilla::Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
+ nsCOMPtr<nsIURI> resultPrincipalURI = GetResultPrincipalURI();
+ emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
+ loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
+
+ nsCOMPtr<nsIURI> unstrippedURI = GetUnstrippedURI();
+ loadState->SetUnstrippedURI(unstrippedURI);
+
+ loadState->SetLoadReplace(GetLoadReplace());
+ nsCOMPtr<nsIInputStream> postData = GetPostData();
+ loadState->SetPostDataStream(postData);
+
+ nsAutoCString contentType;
+ GetContentType(contentType);
+ loadState->SetTypeHint(contentType);
+
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal = GetTriggeringPrincipal();
+ loadState->SetTriggeringPrincipal(triggeringPrincipal);
+ nsCOMPtr<nsIPrincipal> principalToInherit = GetPrincipalToInherit();
+ loadState->SetPrincipalToInherit(principalToInherit);
+ nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
+ GetPartitionedPrincipalToInherit();
+ loadState->SetPartitionedPrincipalToInherit(partitionedPrincipalToInherit);
+ nsCOMPtr<nsIContentSecurityPolicy> csp = GetCsp();
+ loadState->SetCsp(csp);
+ nsCOMPtr<nsIReferrerInfo> 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<nsIURI> 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);
+
+ loadState->SetHasValidUserGestureActivation(GetHasUserActivation());
+
+ 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<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 nsSHEntry::EvictDocumentViewer() {
+ nsCOMPtr<nsIDocumentViewer> 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<nsISHEntry> 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<JS::Value> aOut) {
+ aOut.set(JS::NullValue());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetWireframe(JSContext* aCx, JS::Handle<JS::Value> aArg) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}