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.cpp944
1 files changed, 944 insertions, 0 deletions
diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp
new file mode 100644
index 000000000..9d972136f
--- /dev/null
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -0,0 +1,944 @@
+/* -*- 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 "nsIDocShellLoadInfo.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsDocShellEditorData.h"
+#include "nsSHEntryShared.h"
+#include "nsILayoutHistoryState.h"
+#include "nsIContentViewer.h"
+#include "nsIStructuredCloneContainer.h"
+#include "nsIInputStream.h"
+#include "nsIURI.h"
+#include "mozilla/net/ReferrerPolicy.h"
+#include <algorithm>
+
+namespace dom = mozilla::dom;
+
+static uint32_t gEntryID = 0;
+
+nsSHEntry::nsSHEntry()
+ : mShared(new nsSHEntryShared())
+ , mLoadReplace(false)
+ , mReferrerPolicy(mozilla::net::RP_Default)
+ , mLoadType(0)
+ , mID(gEntryID++)
+ , mScrollPositionX(0)
+ , mScrollPositionY(0)
+ , mParent(nullptr)
+ , mURIWasModified(false)
+ , mIsSrcdocEntry(false)
+ , mScrollRestorationIsManual(false)
+{
+}
+
+nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
+ : mShared(aOther.mShared)
+ , mURI(aOther.mURI)
+ , mOriginalURI(aOther.mOriginalURI)
+ , mLoadReplace(aOther.mLoadReplace)
+ , mReferrerURI(aOther.mReferrerURI)
+ , mReferrerPolicy(aOther.mReferrerPolicy)
+ , 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)
+ , mURIWasModified(aOther.mURIWasModified)
+ , mStateData(aOther.mStateData)
+ , mIsSrcdocEntry(aOther.mIsSrcdocEntry)
+ , mScrollRestorationIsManual(false)
+ , mSrcdocData(aOther.mSrcdocData)
+ , mBaseURI(aOther.mBaseURI)
+{
+}
+
+static bool
+ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
+{
+ if (aEntry) {
+ aEntry->SetParent(nullptr);
+ }
+ return true;
+}
+
+nsSHEntry::~nsSHEntry()
+{
+ // Null out the mParent pointers on all our kids.
+ mChildren.EnumerateForwards(ClearParentPtr, nullptr);
+}
+
+NS_IMPL_ISUPPORTS(nsSHEntry, nsISHContainer, nsISHEntry, nsISHEntryInternal)
+
+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::GetLoadReplace(bool* aLoadReplace)
+{
+ *aLoadReplace = mLoadReplace;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetLoadReplace(bool aLoadReplace)
+{
+ mLoadReplace = aLoadReplace;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetReferrerURI(nsIURI** aReferrerURI)
+{
+ *aReferrerURI = mReferrerURI;
+ NS_IF_ADDREF(*aReferrerURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetReferrerURI(nsIURI* aReferrerURI)
+{
+ mReferrerURI = aReferrerURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetReferrerPolicy(uint32_t* aReferrerPolicy)
+{
+ *aReferrerPolicy = mReferrerPolicy;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetReferrerPolicy(uint32_t aReferrerPolicy)
+{
+ mReferrerPolicy = aReferrerPolicy;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetContentViewer(nsIContentViewer* aViewer)
+{
+ return mShared->SetContentViewer(aViewer);
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetContentViewer(nsIContentViewer** aResult)
+{
+ *aResult = mShared->mContentViewer;
+ NS_IF_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetAnyContentViewer(nsISHEntry** aOwnerEntry,
+ nsIContentViewer** aResult)
+{
+ // Find a content viewer in the root node or any of its children,
+ // assuming that there is only one content viewer total in any one
+ // nsSHEntry tree
+ GetContentViewer(aResult);
+ if (*aResult) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("Found content viewer\n");
+#endif
+ *aOwnerEntry = this;
+ NS_ADDREF(*aOwnerEntry);
+ return NS_OK;
+ }
+ // The root SHEntry doesn't have a ContentViewer, so check child nodes
+ for (int32_t i = 0; i < mChildren.Count(); i++) {
+ nsISHEntry* child = mChildren[i];
+ if (child) {
+#ifdef DEBUG_PAGE_CACHE
+ printf("Evaluating SHEntry child %d\n", i);
+#endif
+ child->GetAnyContentViewer(aOwnerEntry, aResult);
+ if (*aResult) {
+ return NS_OK;
+ }
+ }
+ }
+ 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(char16_t** 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 = ToNewUnicode(mTitle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetTitle(const nsAString& aTitle)
+{
+ mTitle = aTitle;
+ 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::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::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;
+}
+
+nsSHEntryShared*
+nsSHEntry::GetSharedState()
+{
+ return mShared;
+}
+
+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::GetCacheKey(nsISupports** aResult)
+{
+ *aResult = mShared->mCacheKey;
+ NS_IF_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetCacheKey(nsISupports* aCacheKey)
+{
+ mShared->mCacheKey = aCacheKey;
+ 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::GetExpirationStatus(bool* aFlag)
+{
+ *aFlag = mShared->mExpired;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetExpirationStatus(bool aFlag)
+{
+ mShared->mExpired = aFlag;
+ 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,
+ nsILayoutHistoryState* aLayoutHistoryState,
+ nsISupports* aCacheKey, const nsACString& aContentType,
+ nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aPrincipalToInherit,
+ uint64_t aDocShellID,
+ bool aDynamicCreation)
+{
+ mURI = aURI;
+ mTitle = aTitle;
+ mPostData = aInputStream;
+
+ // Set the LoadType by default to loadHistory during creation
+ mLoadType = (uint32_t)nsIDocShellLoadInfo::loadHistory;
+
+ mShared->mCacheKey = aCacheKey;
+ mShared->mContentType = aContentType;
+ mShared->mTriggeringPrincipal = aTriggeringPrincipal;
+ mShared->mPrincipalToInherit = aPrincipalToInherit;
+ 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;
+
+ // By default we save LayoutHistoryState
+ mShared->mSaveLayoutState = true;
+ mShared->mLayoutHistoryState = aLayoutHistoryState;
+
+ // By default the page is not expired
+ mShared->mExpired = false;
+
+ mIsSrcdocEntry = false;
+ mSrcdocData = NullString();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::Clone(nsISHEntry** aResult)
+{
+ *aResult = new nsSHEntry(*this);
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetParent(nsISHEntry** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mParent;
+ NS_IF_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetParent(nsISHEntry* aParent)
+{
+ /* parent not Addrefed on purpose to avoid cyclic reference
+ * Null parent is OK
+ *
+ * XXX this method should not be scriptable if this is the case!!
+ */
+ mParent = aParent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetWindowState(nsISupports* aState)
+{
+ mShared->mWindowState = aState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetWindowState(nsISupports** aState)
+{
+ NS_IF_ADDREF(*aState = mShared->mWindowState);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetViewerBounds(const nsIntRect& aBounds)
+{
+ mShared->mViewerBounds = aBounds;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetViewerBounds(nsIntRect& aBounds)
+{
+ aBounds = mShared->mViewerBounds;
+ return NS_OK;
+}
+
+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::GetBFCacheEntry(nsIBFCacheEntry** aEntry)
+{
+ NS_ENSURE_ARG_POINTER(aEntry);
+ NS_IF_ADDREF(*aEntry = mShared);
+ return NS_OK;
+}
+
+bool
+nsSHEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry)
+{
+ return static_cast<nsIBFCacheEntry*>(mShared) == aEntry;
+}
+
+NS_IMETHODIMP
+nsSHEntry::AdoptBFCacheEntry(nsISHEntry* aEntry)
+{
+ nsCOMPtr<nsISHEntryInternal> shEntry = do_QueryInterface(aEntry);
+ NS_ENSURE_STATE(shEntry);
+
+ nsSHEntryShared* shared = shEntry->GetSharedState();
+ NS_ENSURE_STATE(shared);
+
+ mShared = shared;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SharesDocumentWith(nsISHEntry* aEntry, bool* aOut)
+{
+ NS_ENSURE_ARG_POINTER(aOut);
+
+ nsCOMPtr<nsISHEntryInternal> internal = do_QueryInterface(aEntry);
+ NS_ENSURE_STATE(internal);
+
+ *aOut = mShared == internal->GetSharedState();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::AbandonBFCacheEntry()
+{
+ mShared = nsSHEntryShared::Duplicate(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::GetChildCount(int32_t* aCount)
+{
+ *aCount = mChildren.Count();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::AddChild(nsISHEntry* aChild, int32_t aOffset)
+{
+ 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 = false;
+ if (aChild) {
+ aChild->IsDynamicallyAdded(&newChildIsDyn);
+ }
+
+ // 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) {
+ bool dyn = false;
+ entry->IsDynamicallyAdded(&dyn);
+ if (dyn) {
+ 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) {
+ bool dyn = false;
+ entry->IsDynamicallyAdded(&dyn);
+ if (dyn) {
+ 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) {
+ NS_ERROR("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;
+ bool dynamic = false;
+ aChild->IsDynamicallyAdded(&dynamic);
+ if (dynamic) {
+ childRemoved = mChildren.RemoveObject(aChild);
+ } else {
+ int32_t index = mChildren.IndexOfObject(aChild);
+ if (index >= 0) {
+ 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
+nsSHEntry::ReplaceChild(nsISHEntry* aNewEntry)
+{
+ NS_ENSURE_STATE(aNewEntry);
+
+ uint64_t docshellID;
+ aNewEntry->GetDocshellID(&docshellID);
+
+ uint64_t otherID;
+ for (int32_t i = 0; i < mChildren.Count(); ++i) {
+ if (mChildren[i] && NS_SUCCEEDED(mChildren[i]->GetDocshellID(&otherID)) &&
+ docshellID == otherID) {
+ mChildren[i]->SetParent(nullptr);
+ mChildren.ReplaceObjectAt(aNewEntry, i);
+ return aNewEntry->SetParent(this);
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsSHEntry::AddChildShell(nsIDocShellTreeItem* aShell)
+{
+ NS_ASSERTION(aShell, "Null child shell added to history entry");
+ mShared->mChildShells.AppendObject(aShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::ChildShellAt(int32_t aIndex, nsIDocShellTreeItem** aShell)
+{
+ NS_IF_ADDREF(*aShell = mShared->mChildShells.SafeObjectAt(aIndex));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::ClearChildShells()
+{
+ mShared->mChildShells.Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetRefreshURIList(nsIMutableArray** aList)
+{
+ NS_IF_ADDREF(*aList = mShared->mRefreshURIList);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetRefreshURIList(nsIMutableArray* aList)
+{
+ mShared->mRefreshURIList = aList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SyncPresentationState()
+{
+ return mShared->SyncPresentationState();
+}
+
+void
+nsSHEntry::RemoveFromBFCacheSync()
+{
+ mShared->RemoveFromBFCacheSync();
+}
+
+void
+nsSHEntry::RemoveFromBFCacheAsync()
+{
+ mShared->RemoveFromBFCacheAsync();
+}
+
+nsDocShellEditorData*
+nsSHEntry::ForgetEditorData()
+{
+ // XXX jlebar Check how this is used.
+ return mShared->mEditorData.forget();
+}
+
+void
+nsSHEntry::SetEditorData(nsDocShellEditorData* aData)
+{
+ NS_ASSERTION(!(aData && mShared->mEditorData),
+ "We're going to overwrite an owning ref!");
+ if (mShared->mEditorData != aData) {
+ mShared->mEditorData = aData;
+ }
+}
+
+bool
+nsSHEntry::HasDetachedEditor()
+{
+ return mShared->mEditorData != nullptr;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetStateData(nsIStructuredCloneContainer** aContainer)
+{
+ NS_ENSURE_ARG_POINTER(aContainer);
+ NS_IF_ADDREF(*aContainer = mStateData);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetStateData(nsIStructuredCloneContainer* aContainer)
+{
+ mStateData = aContainer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::IsDynamicallyAdded(bool* aAdded)
+{
+ *aAdded = mShared->mDynamicallyCreated;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::HasDynamicallyAddedChild(bool* aAdded)
+{
+ *aAdded = false;
+ for (int32_t i = 0; i < mChildren.Count(); ++i) {
+ nsISHEntry* entry = mChildren[i];
+ if (entry) {
+ entry->IsDynamicallyAdded(aAdded);
+ if (*aAdded) {
+ break;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetDocshellID(uint64_t* aID)
+{
+ *aID = mShared->mDocShellID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetDocshellID(uint64_t 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;
+}