Bug 1570255 - Reboot session history in parent part 1. r=smaug,necko-reviewers,valentin
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 20 May 2020 09:09:12 +0000
changeset 530987 aaef82cfb75f1d9ee66665761a2f8473658e0457
parent 530986 7232f4360094c28c7dd7a242ab3353be6583eceb
child 530988 583aeb1df2f805355875c9c54945d2cb0cb6a793
push id37435
push userapavel@mozilla.com
push dateWed, 20 May 2020 15:28:23 +0000
treeherdermozilla-central@5415da14ec9a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, necko-reviewers, valentin
bugs1570255
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1570255 - Reboot session history in parent part 1. r=smaug,necko-reviewers,valentin This adds a new implementation of nsISHEntry (mozilla::dom::SessionHistoryEntry). When session history in the parent is turned on, we'll instantiate the existing nsSHistory in the parent process, but it will store entries of this new type. The nsSHistory in the child process will also be instantiated for now, to avoid breaking too many assumptions, and we try to keep parent and child implementations in sync. mozilla::dom::SessionHistoryEntry stores most of its data in a new structure (mozilla::dom::SessionHistoryInfo) which can be sent over IPC. When a load starts through the DocumentChannel we create an entry of this new type for it in the parent process in DocumentLoadListener::Open. The SessionHistoryInfo for that entry (with an associated ID) is then sent over IPC in the RedirectToRealChannelArgs to the process that does the actual load, where we store it in the nsDocShell in mLoadingEntry (and mLoadingEntryId). The parent process keeps track of outstanding loading entries in an array (mLoadingEntries) in the CanonicalBrowsingContext. When a load finishes the nsDocShell transfers mLoadingEntry into mActiveEntry, and notifies the parent process through an IPC message (HistoryCommit) with the id of that entry. The CanonicalBrowsingContext then removes the entry from the array and stores it in its mActiveEntry, and adds the entry to the nsSHistory object. There are a number of things in this patch that are broken, and a lot of FIXME comments. However, with the pref turned off things should just be working as before. The goal is to land this first part, and then iterate on the new implementation until we can switch over. Differential Revision: https://phabricator.services.mozilla.com/D65329
docshell/base/CanonicalBrowsingContext.cpp
docshell/base/CanonicalBrowsingContext.h
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellLoadState.cpp
docshell/base/nsDocShellLoadState.h
docshell/shistory/SessionHistoryEntry.cpp
docshell/shistory/SessionHistoryEntry.h
docshell/shistory/moz.build
docshell/shistory/nsSHEntryShared.h
dom/ipc/BrowserParent.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
netwerk/ipc/DocumentLoadListener.cpp
netwerk/ipc/DocumentLoadListener.h
netwerk/ipc/NeckoChannelParams.ipdlh
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -13,22 +13,24 @@
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/MediaController.h"
 #include "mozilla/dom/MediaControlService.h"
 #include "mozilla/dom/ContentPlaybackController.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/net/DocumentLoadListener.h"
 #include "mozilla/NullPrincipal.h"
 #include "nsGlobalWindowOuter.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsNetUtil.h"
+#include "nsSHistory.h"
 
 using namespace mozilla::ipc;
 
 extern mozilla::LazyLogModule gAutoplayPermissionLog;
 
 #define AUTOPLAY_LOG(msg, ...) \
   MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
 
@@ -181,29 +183,81 @@ CanonicalBrowsingContext::GetParentCross
   if (GetEmbedderElement()) {
     return do_AddRef(
         Cast(GetEmbedderElement()->OwnerDoc()->GetBrowsingContext()));
   }
   return nullptr;
 }
 
 nsISHistory* CanonicalBrowsingContext::GetSessionHistory() {
-  if (mSessionHistory) {
-    return mSessionHistory;
+  if (!IsTop()) {
+    return Cast(Top())->GetSessionHistory();
+  }
+
+  // Check GetChildSessionHistory() to make sure that this BrowsingContext has
+  // session history enabled.
+  if (!mSessionHistory && GetChildSessionHistory()) {
+    mSessionHistory = new nsSHistory(this);
   }
 
-  nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(GetDocShell());
-  if (webNav) {
-    RefPtr<ChildSHistory> shistory = webNav->GetSessionHistory();
-    if (shistory) {
-      return shistory->LegacySHistory();
+  return mSessionHistory;
+}
+
+static uint64_t gNextHistoryEntryId = 0;
+
+UniquePtr<SessionHistoryInfoAndId>
+CanonicalBrowsingContext::CreateSessionHistoryEntryForLoad(
+    nsDocShellLoadState* aLoadState, nsIChannel* aChannel) {
+  MOZ_ASSERT(GetChildSessionHistory(),
+             "Creating an entry but session history is not enabled for this "
+             "browsing context!");
+  uint64_t id = ++gNextHistoryEntryId;
+  RefPtr<SessionHistoryEntry> entry =
+      new SessionHistoryEntry(GetSessionHistory(), aLoadState, aChannel);
+  mLoadingEntries.AppendElement(SessionHistoryEntryAndId(id, entry));
+  return MakeUnique<SessionHistoryInfoAndId>(
+      id, MakeUnique<SessionHistoryInfo>(entry->GetInfo()));
+}
+
+void CanonicalBrowsingContext::SessionHistoryCommit(
+    uint64_t aSessionHistoryEntryId) {
+  for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {
+    if (mLoadingEntries[i].mId == aSessionHistoryEntryId) {
+      RefPtr<SessionHistoryEntry> oldActiveEntry = mActiveEntry.forget();
+      mActiveEntry = mLoadingEntries[i].mEntry;
+      mLoadingEntries.RemoveElementAt(i);
+      if (IsTop()) {
+        GetSessionHistory()->AddEntry(mActiveEntry,
+                                      /* FIXME aPersist = */ true);
+      } else {
+        // FIXME Check if we're replacing before adding a child.
+        // FIXME The old implementations adds it to the parent's mLSHE if there
+        //       is one, need to figure out if that makes sense here (peterv
+        //       doesn't think it would).
+        if (oldActiveEntry) {
+          // FIXME Need to figure out the right value for aCloneChildren.
+          GetSessionHistory()->AddChildSHEntryHelper(oldActiveEntry,
+                                                     mActiveEntry, Top(), true);
+        } else {
+          SessionHistoryEntry* parentEntry =
+              static_cast<CanonicalBrowsingContext*>(GetParent())->mActiveEntry;
+          if (parentEntry) {
+            // FIXME The docshell code sometime uses -1 for aChildOffset!
+            // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite
+            //       right, but aUseRemoteSubframes should be going away.
+            parentEntry->AddChild(mActiveEntry, Children().Length() - 1,
+                                  IsInProcess());
+          }
+        }
+      }
+      return;
     }
   }
-
-  return nullptr;
+  // FIXME Should we throw an error if we don't find an entry for
+  // aSessionHistoryEntryId?
 }
 
 JSObject* CanonicalBrowsingContext::WrapObject(
     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void CanonicalBrowsingContext::DispatchWheelZoomChange(bool aIncrease) {
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -8,33 +8,43 @@
 #define mozilla_dom_CanonicalBrowsingContext_h
 
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/MediaControlKeysEvent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/MozPromise.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
+#include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
-#include "nsISHistory.h"
-#include "nsISHEntry.h"
+
+class nsISHistory;
 
 namespace mozilla {
 namespace net {
 class DocumentLoadListener;
 }
 
 namespace dom {
 
-class WindowGlobalParent;
 class BrowserParent;
 class MediaController;
+struct SessionHistoryInfoAndId;
+class SessionHistoryEntry;
 class WindowGlobalParent;
 
+struct SessionHistoryEntryAndId {
+  SessionHistoryEntryAndId(uint64_t aId, SessionHistoryEntry* aEntry)
+      : mId(aId), mEntry(aEntry) {}
+
+  uint64_t mId;
+  RefPtr<SessionHistoryEntry> mEntry;
+};
+
 // CanonicalBrowsingContext is a BrowsingContext living in the parent
 // process, with whatever extra data that a BrowsingContext in the
 // parent needs.
 class CanonicalBrowsingContext final : public BrowsingContext {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanonicalBrowsingContext,
                                            BrowsingContext)
@@ -78,19 +88,20 @@ class CanonicalBrowsingContext final : p
 
   // Same as `GetParentWindowContext`, but will also cross <browser> and
   // content/chrome boundaries.
   already_AddRefed<WindowGlobalParent> GetEmbedderWindowGlobal() const;
 
   already_AddRefed<CanonicalBrowsingContext> GetParentCrossChromeBoundary();
 
   nsISHistory* GetSessionHistory();
-  void SetSessionHistory(nsISHistory* aSHistory) {
-    mSessionHistory = aSHistory;
-  }
+  UniquePtr<SessionHistoryInfoAndId> CreateSessionHistoryEntryForLoad(
+      nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
+  void SessionHistoryCommit(uint64_t aSessionHistoryEntryId);
+
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // Dispatches a wheel zoom change to the embedder element.
   void DispatchWheelZoomChange(bool aIncrease);
 
   // This function is used to start the autoplay media which are delayed to
   // start. If needed, it would also notify the content browsing context which
@@ -197,14 +208,17 @@ class CanonicalBrowsingContext final : p
   nsCOMPtr<nsISHistory> mSessionHistory;
 
   // Tab media controller is used to control all media existing in the same
   // browsing context tree, so it would only exist in the top level browsing
   // context.
   RefPtr<MediaController> mTabMediaController;
 
   RefPtr<net::DocumentLoadListener> mCurrentLoad;
+
+  nsTArray<SessionHistoryEntryAndId> mLoadingEntries;
+  RefPtr<SessionHistoryEntry> mActiveEntry;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_CanonicalBrowsingContext_h)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -61,16 +61,17 @@
 #include "mozilla/dom/PerformanceNavigation.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ServiceWorkerInterceptController.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
 #include "mozilla/dom/SessionStorageManager.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/UserActivation.h"
 #include "mozilla/dom/ChildSHistory.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/LoadURIOptionsBinding.h"
 #include "mozilla/dom/JSWindowActorChild.h"
@@ -5483,16 +5484,28 @@ nsresult nsDocShell::Embed(nsIContentVie
     // Restore the editing state, if it's stored in session history.
     if (mLSHE->HasDetachedEditor()) {
       ReattachEditorToWindow(mLSHE);
     }
     // Set history.state
     SetDocCurrentStateObj(mLSHE);
 
     SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
+    if (StaticPrefs::fission_sessionHistoryInParent()) {
+      mActiveEntry = nullptr;
+      mLoadingEntry.swap(mActiveEntry);
+      if (XRE_IsParentProcess()) {
+        mBrowsingContext->Canonical()->SessionHistoryCommit(mLoadingEntryId);
+      } else {
+        ContentChild* cc = ContentChild::GetSingleton();
+        mozilla::Unused << cc->SendHistoryCommit(mBrowsingContext,
+                                                 mLoadingEntryId);
+      }
+      mLoadingEntryId = 0;
+    }
   }
 
   bool updateHistory = true;
 
   // Determine if this type of load should update history
   switch (mLoadType) {
     case LOAD_NORMAL_REPLACE:
     case LOAD_STOP_CONTENT_AND_REPLACE:
@@ -9472,16 +9485,25 @@ nsresult nsDocShell::DoURILoad(nsDocShel
       nestedURI->GetInnerURI(getter_AddRefs(tempURI));
       nestedURI = do_QueryInterface(tempURI);
     }
   } else {
     MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
                "DoURILoad thinks this is a document and InternalLoad does not");
   }
 
+  // FIXME We still have a ton of codepaths that don't pass through
+  //       DocumentLoadListener, so probably need to create session history info
+  //       in more places.
+  if (aLoadState->GetSessionHistoryID() != 0) {
+    mLoadingEntry =
+        MakeUnique<SessionHistoryInfo>(aLoadState->GetSessionHistoryInfo());
+    mLoadingEntryId = aLoadState->GetSessionHistoryID();
+  }
+
   // open a channel for the url
 
   // If we have a pending channel, use the channel we've already created here.
   // We don't need to set up load flags for our channel, as it has already been
   // created.
 
   if (nsCOMPtr<nsIChannel> channel =
           aLoadState->GetPendingRedirectedChannel()) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -59,16 +59,17 @@
 namespace mozilla {
 class Encoding;
 class HTMLEditor;
 enum class TaskCategory;
 namespace dom {
 class ClientInfo;
 class ClientSource;
 class EventTarget;
+class SessionHistoryInfo;
 }  // namespace dom
 namespace net {
 class LoadInfo;
 class DocumentLoadListener;
 }  // namespace net
 }  // namespace mozilla
 
 class nsIContentViewer;
@@ -1127,16 +1128,21 @@ class nsDocShell final : public nsDocLoa
   // Reference to the SHEntry for this docshell until the page is loaded
   // Somebody give me better name.
   // If mLSHE is non-null, non-pushState subframe loads don't create separate
   // root history entries. That is, frames loaded during the parent page
   // load don't generate history entries the way frame navigation after the
   // parent has loaded does. (This isn't the only purpose of mLSHE.)
   nsCOMPtr<nsISHEntry> mLSHE;
 
+  // These are only set when fission.sessionHistoryInParent is set.
+  mozilla::UniquePtr<mozilla::dom::SessionHistoryInfo> mActiveEntry;
+  mozilla::UniquePtr<mozilla::dom::SessionHistoryInfo> mLoadingEntry;
+  uint64_t mLoadingEntryId;
+
   // Holds a weak pointer to a RestorePresentationEvent object if any that
   // holds a weak pointer back to us. We use this pointer to possibly revoke
   // the event whenever necessary.
   nsRevocableEventPtr<RestorePresentationEvent> mRestorePresentationEvent;
 
   // Editor data, if this document is designMode or contentEditable.
   mozilla::UniquePtr<nsDocShellEditorData> mEditorData;
 
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -483,16 +483,30 @@ void nsDocShellLoadState::SetLoadType(ui
 }
 
 nsISHEntry* nsDocShellLoadState::SHEntry() const { return mSHEntry; }
 
 void nsDocShellLoadState::SetSHEntry(nsISHEntry* aSHEntry) {
   mSHEntry = aSHEntry;
 }
 
+void nsDocShellLoadState::SetSessionHistoryInfo(
+    const mozilla::dom::SessionHistoryInfoAndId& aIdAndInfo) {
+  mSessionHistoryInfo = aIdAndInfo;
+}
+
+uint64_t nsDocShellLoadState::GetSessionHistoryID() const {
+  return mSessionHistoryInfo.mId;
+}
+
+const mozilla::dom::SessionHistoryInfo&
+nsDocShellLoadState::GetSessionHistoryInfo() const {
+  return *mSessionHistoryInfo.mInfo;
+}
+
 const nsString& nsDocShellLoadState::Target() const { return mTarget; }
 
 void nsDocShellLoadState::SetTarget(const nsAString& aTarget) {
   mTarget = aTarget;
 }
 
 nsIInputStream* nsDocShellLoadState::PostDataStream() const {
   return mPostDataStream;
--- a/docshell/base/nsDocShellLoadState.h
+++ b/docshell/base/nsDocShellLoadState.h
@@ -3,21 +3,23 @@
 /* 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/. */
 
 #ifndef nsDocShellLoadState_h__
 #define nsDocShellLoadState_h__
 
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
 
 // Helper Classes
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsDocShellLoadTypes.h"
+#include "nsTArrayForwardDeclare.h"
 
 class nsIContentSecurityPolicy;
 class nsIInputStream;
 class nsISHEntry;
 class nsIURI;
 class nsIDocShell;
 class nsIChannel;
 class nsIReferrerInfo;
@@ -122,16 +124,22 @@ class nsDocShellLoadState final {
   uint32_t LoadType() const;
 
   void SetLoadType(uint32_t aLoadType);
 
   nsISHEntry* SHEntry() const;
 
   void SetSHEntry(nsISHEntry* aSHEntry);
 
+  const mozilla::dom::SessionHistoryInfo& GetSessionHistoryInfo() const;
+  uint64_t GetSessionHistoryID() const;
+
+  void SetSessionHistoryInfo(
+      const mozilla::dom::SessionHistoryInfoAndId& aIdAndInfo);
+
   const nsString& Target() const;
 
   void SetTarget(const nsAString& aTarget);
 
   nsIInputStream* PostDataStream() const;
 
   void SetPostDataStream(nsIInputStream* aStream);
 
@@ -337,16 +345,19 @@ class nsDocShellLoadState final {
 
   // Contains a load type as specified by the nsDocShellLoadTypes::load*
   // constants
   uint32_t mLoadType;
 
   // Active Session History entry (if loading from SH)
   nsCOMPtr<nsISHEntry> mSHEntry;
 
+  // Session history info for the load
+  mozilla::dom::SessionHistoryInfoAndId mSessionHistoryInfo;
+
   // Target for load, like _content, _blank etc.
   nsString mTarget;
 
   // Post data stream (if POSTing)
   nsCOMPtr<nsIInputStream> mPostDataStream;
 
   // Additional Headers
   nsCOMPtr<nsIInputStream> mHeadersStream;
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SessionHistoryEntry.cpp
@@ -0,0 +1,827 @@
+/* -*- 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 "nsDocShellLoadState.h"
+#include "nsILayoutHistoryState.h"
+#include "nsSHEntryShared.h"
+#include "nsStructuredCloneContainer.h"
+
+namespace mozilla {
+namespace dom {
+
+SessionHistoryInfo::SessionHistoryInfo(nsDocShellLoadState* aLoadState,
+                                       nsIChannel* aChannel)
+    : mURI(aLoadState->URI()),
+      mOriginalURI(aLoadState->OriginalURI()),
+      mResultPrincipalURI(aLoadState->ResultPrincipalURI()),
+      mReferrerInfo(aLoadState->GetReferrerInfo()),
+      mPostData(aLoadState->PostDataStream()),
+      mLoadType(aLoadState->LoadType()),
+      mScrollPositionX(0),
+      mScrollPositionY(0),
+      mSrcdocData(aLoadState->SrcdocData()),
+      mBaseURI(aLoadState->BaseURI()),
+      mLoadReplace(aLoadState->LoadReplace()),
+      mURIWasModified(false),
+      /* FIXME Should this be aLoadState->IsSrcdocLoad()? */
+      mIsSrcdocEntry(!aLoadState->SrcdocData().IsEmpty()),
+      mScrollRestorationIsManual(false) {
+  bool isNoStore = false;
+  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
+    Unused << httpChannel->IsNoStoreResponse(&isNoStore);
+    mPersist = !isNoStore;
+  }
+}
+
+static uint32_t gEntryID;
+
+SessionHistoryEntry::SessionHistoryEntry(nsISHistory* aSessionHistory,
+                                         nsDocShellLoadState* aLoadState,
+                                         nsIChannel* aChannel)
+    : mInfo(new SessionHistoryInfo(aLoadState, aChannel)),
+      mSharedInfo(new SHEntrySharedParentState(aSessionHistory)),
+      mID(++gEntryID) {
+  mSharedInfo->mTriggeringPrincipal = aLoadState->TriggeringPrincipal();
+  mSharedInfo->mPrincipalToInherit = aLoadState->PrincipalToInherit();
+  mSharedInfo->mStoragePrincipalToInherit =
+      aLoadState->StoragePrincipalToInherit();
+  mSharedInfo->mCsp = aLoadState->Csp();
+  // FIXME Set remaining shared fields!
+}
+
+NS_IMPL_ISUPPORTS(SessionHistoryEntry, nsISHEntry)
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetURI(nsIURI** aURI) {
+  nsCOMPtr<nsIURI> uri = mInfo->mURI;
+  uri.forget(aURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetURI(nsIURI* aURI) {
+  mInfo->mURI = aURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetOriginalURI(nsIURI** aOriginalURI) {
+  nsCOMPtr<nsIURI> originalURI = mInfo->mOriginalURI;
+  originalURI.forget(aOriginalURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetOriginalURI(nsIURI* aOriginalURI) {
+  mInfo->mOriginalURI = aOriginalURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetResultPrincipalURI(nsIURI** aResultPrincipalURI) {
+  nsCOMPtr<nsIURI> resultPrincipalURI = mInfo->mResultPrincipalURI;
+  resultPrincipalURI.forget(aResultPrincipalURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetResultPrincipalURI(nsIURI* aResultPrincipalURI) {
+  mInfo->mResultPrincipalURI = aResultPrincipalURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::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->mTitle = aTitle;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetIsSubFrame(bool* aIsSubFrame) {
+  *aIsSubFrame = mSharedInfo->mIsFrameNavigation;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetIsSubFrame(bool aIsSubFrame) {
+  mSharedInfo->mIsFrameNavigation = aIsSubFrame;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
+  nsCOMPtr<nsIReferrerInfo> referrerInfo = mInfo->mReferrerInfo;
+  referrerInfo.forget(aReferrerInfo);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  mInfo->mReferrerInfo = aReferrerInfo;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetContentViewer(nsIContentViewer** aContentViewer) {
+  NS_WARNING("This lives in the child process");
+  *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::GetSticky(bool* aSticky) {
+  *aSticky = mSharedInfo->mSticky;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetSticky(bool aSticky) {
+  mSharedInfo->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) {
+  nsCOMPtr<nsIInputStream> postData = mInfo->mPostData;
+  postData.forget(aPostData);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetPostData(nsIInputStream* aPostData) {
+  mInfo->mPostData = aPostData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetLayoutHistoryState(
+    nsILayoutHistoryState** aLayoutHistoryState) {
+  nsCOMPtr<nsILayoutHistoryState> layoutHistoryState =
+      mSharedInfo->mLayoutHistoryState;
+  layoutHistoryState.forget(aLayoutHistoryState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetLayoutHistoryState(
+    nsILayoutHistoryState* aLayoutHistoryState) {
+  mSharedInfo->mLayoutHistoryState = aLayoutHistoryState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetParent(nsISHEntry** aParent) {
+  nsCOMPtr<nsISHEntry> parent = mParent;
+  parent.forget(aParent);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetParent(nsISHEntry* aParent) {
+  mParent = 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 = mSharedInfo->mCacheKey;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetCacheKey(uint32_t aCacheKey) {
+  mSharedInfo->mCacheKey = aCacheKey;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetSaveLayoutStateFlag(bool* aSaveLayoutStateFlag) {
+  *aSaveLayoutStateFlag = mSharedInfo->mSaveLayoutState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag) {
+  mSharedInfo->mSaveLayoutState = aSaveLayoutStateFlag;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetExpirationStatus(bool* aExpirationStatus) {
+  MOZ_CRASH("This lives in the child process");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetExpirationStatus(bool aExpirationStatus) {
+  MOZ_CRASH("This lives in the child process");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetContentType(nsACString& aContentType) {
+  aContentType = mSharedInfo->mContentType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetContentType(const nsACString& aContentType) {
+  mSharedInfo->mContentType = aContentType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetURIWasModified(bool* aURIWasModified) {
+  *aURIWasModified = mInfo->mURIWasModified;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetURIWasModified(bool aURIWasModified) {
+  mInfo->mURIWasModified = aURIWasModified;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetTriggeringPrincipal(
+    nsIPrincipal** aTriggeringPrincipal) {
+  nsCOMPtr<nsIPrincipal> triggeringPrincipal =
+      mSharedInfo->mTriggeringPrincipal;
+  triggeringPrincipal.forget(aTriggeringPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetTriggeringPrincipal(
+    nsIPrincipal* aTriggeringPrincipal) {
+  mSharedInfo->mTriggeringPrincipal = aTriggeringPrincipal;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetPrincipalToInherit(nsIPrincipal** aPrincipalToInherit) {
+  nsCOMPtr<nsIPrincipal> principalToInherit = mSharedInfo->mPrincipalToInherit;
+  principalToInherit.forget(aPrincipalToInherit);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit) {
+  mSharedInfo->mPrincipalToInherit = aPrincipalToInherit;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetStoragePrincipalToInherit(
+    nsIPrincipal** aStoragePrincipalToInherit) {
+  nsCOMPtr<nsIPrincipal> storagePrincipalToInherit =
+      mSharedInfo->mStoragePrincipalToInherit;
+  storagePrincipalToInherit.forget(aStoragePrincipalToInherit);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetStoragePrincipalToInherit(
+    nsIPrincipal* aStoragePrincipalToInherit) {
+  mSharedInfo->mStoragePrincipalToInherit = aStoragePrincipalToInherit;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetCsp(nsIContentSecurityPolicy** aCsp) {
+  nsCOMPtr<nsIContentSecurityPolicy> csp = mSharedInfo->mCsp;
+  csp.forget(aCsp);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetCsp(nsIContentSecurityPolicy* aCsp) {
+  mSharedInfo->mCsp = aCsp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetStateData(nsIStructuredCloneContainer** aStateData) {
+  RefPtr<nsStructuredCloneContainer> stateData = mInfo->mStateData;
+  stateData.forget(aStateData);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetStateData(nsIStructuredCloneContainer* aStateData) {
+  mInfo->mStateData = static_cast<nsStructuredCloneContainer*>(aStateData);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetDocshellID(nsID& aDocshellID) {
+  MOZ_CRASH("This lives in the child process");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetDocshellID(const nsID& aDocshellID) {
+  NS_WARNING("This lives in the child process");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetIsSrcdocEntry(bool* aIsSrcdocEntry) {
+  *aIsSrcdocEntry = mInfo->mIsSrcdocEntry;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetSrcdocData(nsAString& aSrcdocData) {
+  aSrcdocData = mInfo->mSrcdocData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetSrcdocData(const nsAString& aSrcdocData) {
+  mInfo->mSrcdocData = aSrcdocData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetBaseURI(nsIURI** aBaseURI) {
+  nsCOMPtr<nsIURI> baseURI = mInfo->mBaseURI;
+  baseURI.forget(aBaseURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetBaseURI(nsIURI* aBaseURI) {
+  mInfo->mBaseURI = aBaseURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetScrollRestorationIsManual(
+    bool* aScrollRestorationIsManual) {
+  *aScrollRestorationIsManual = mInfo->mScrollRestorationIsManual;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetScrollRestorationIsManual(
+    bool aScrollRestorationIsManual) {
+  mInfo->mScrollRestorationIsManual = aScrollRestorationIsManual;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetLoadedInThisProcess(bool* aLoadedInThisProcess) {
+  // FIXME
+  //*aLoadedInThisProcess = mInfo->mLoadedInThisProcess;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetShistory(nsISHistory** aShistory) {
+  nsCOMPtr<nsISHistory> sHistory = do_QueryReferent(mSharedInfo->mSHistory);
+  sHistory.forget(aShistory);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetLastTouched(uint32_t* aLastTouched) {
+  *aLastTouched = mSharedInfo->mLastTouched;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetLastTouched(uint32_t aLastTouched) {
+  mSharedInfo->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 = mSharedInfo->mViewerBounds;
+}
+
+NS_IMETHODIMP_(void)
+SessionHistoryEntry::SetViewerBounds(const nsIntRect& bounds) {
+  mSharedInfo->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 (!mSharedInfo->mLayoutHistoryState) {
+    nsCOMPtr<nsILayoutHistoryState> historyState;
+    historyState = NS_NewLayoutHistoryState();
+    SetLayoutHistoryState(historyState);
+  }
+
+  return GetLayoutHistoryState(aLayoutHistoryState);
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::Create(
+    nsIURI* aURI, const nsAString& aTitle, nsIInputStream* aInputStream,
+    uint32_t aCacheKey, const nsACString& aContentType,
+    nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
+    nsIPrincipal* aStoragePrincipalToInherit, nsIContentSecurityPolicy* aCsp,
+    const nsID& aDocshellID, bool aDynamicCreation, nsIURI* aOriginalURI,
+    nsIURI* aResultPrincipalURI, bool aLoadReplace,
+    nsIReferrerInfo* aReferrerInfo, const nsAString& aSrcdoc, bool aSrcdocEntry,
+    nsIURI* aBaseURI, bool aSaveLayoutState, bool aExpired) {
+  MOZ_CRASH("Might need to implement this");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::Clone(nsISHEntry** _retval) {
+  MOZ_CRASH("Might need to implement this");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(nsDocShellEditorData*)
+SessionHistoryEntry::ForgetEditorData() {
+  MOZ_CRASH("This lives in the child process");
+  return nullptr;
+}
+
+NS_IMETHODIMP_(void)
+SessionHistoryEntry::SetEditorData(nsDocShellEditorData* aData) {
+  MOZ_CRASH("This lives in the child process");
+}
+
+NS_IMETHODIMP_(bool)
+SessionHistoryEntry::HasDetachedEditor() {
+  MOZ_CRASH("This lives in the child process");
+  return false;
+}
+
+NS_IMETHODIMP_(bool)
+SessionHistoryEntry::IsDynamicallyAdded() {
+  return mSharedInfo->mDynamicallyCreated;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::HasDynamicallyAddedChild(bool* aHasDynamicallyAddedChild) {
+  for (const auto& child : mChildren) {
+    if (child->IsDynamicallyAdded()) {
+      *aHasDynamicallyAddedChild = true;
+      return NS_OK;
+    }
+  }
+  *aHasDynamicallyAddedChild = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(bool)
+SessionHistoryEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry) {
+  MOZ_CRASH("This lives in the child process");
+  return false;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::AdoptBFCacheEntry(nsISHEntry* aEntry) {
+  MOZ_CRASH("This lives in the child process");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::AbandonBFCacheEntry() {
+  MOZ_CRASH("This lives in the child process");
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SharesDocumentWith(nsISHEntry* aEntry,
+                                        bool* aSharesDocumentWith) {
+  MOZ_CRASH("Might need to implement this");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetLoadTypeAsHistory() {
+  mInfo->mLoadType = LOAD_HISTORY;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::AddChild(nsISHEntry* aChild, int32_t aOffset,
+                              bool aUseRemoteSubframes) {
+  MOZ_CRASH("Need to implement this");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::RemoveChild(nsISHEntry* aChild) {
+  MOZ_CRASH("Need to implement this");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetChildAt(int32_t aIndex, nsISHEntry** aChild) {
+  nsCOMPtr<nsISHEntry> child = mChildren.SafeElementAt(aIndex);
+  child.forget(aChild);
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SessionHistoryEntry::GetChildSHEntryIfHasNoDynamicallyAddedChild(
+    int32_t aChildOffset, nsISHEntry** aChild) {
+  MOZ_CRASH("Need to implement this");
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::ReplaceChild(nsISHEntry* aNewChild) {
+  MOZ_CRASH("Need to implement this");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(void)
+SessionHistoryEntry::ClearEntry() {
+  int32_t childCount = GetChildCount();
+  // Remove all children of this entry
+  for (int32_t i = childCount; i > 0; --i) {
+    nsCOMPtr<nsISHEntry> child;
+    GetChildAt(i - 1, getter_AddRefs(child));
+    RemoveChild(child);
+  }
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::CreateLoadInfo(nsDocShellLoadState** aLoadState) {
+  nsCOMPtr<nsIURI> uri = GetURI();
+  RefPtr<nsDocShellLoadState> loadState(new nsDocShellLoadState(mInfo->mURI));
+
+  loadState->SetOriginalURI(mInfo->mOriginalURI);
+  loadState->SetMaybeResultPrincipalURI(Some(mInfo->mResultPrincipalURI));
+  loadState->SetLoadReplace(mInfo->mLoadReplace);
+  loadState->SetPostDataStream(mInfo->mPostData);
+  loadState->SetReferrerInfo(mInfo->mReferrerInfo);
+
+  loadState->SetTypeHint(mSharedInfo->mContentType);
+  loadState->SetTriggeringPrincipal(mSharedInfo->mTriggeringPrincipal);
+  loadState->SetPrincipalToInherit(mSharedInfo->mPrincipalToInherit);
+  loadState->SetStoragePrincipalToInherit(
+      mSharedInfo->mStoragePrincipalToInherit);
+  loadState->SetCsp(mSharedInfo->mCsp);
+
+  // Do not inherit principal from document (security-critical!);
+  uint32_t flags = nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_NONE;
+
+  // Passing nullptr as aSourceDocShell gives the same behaviour as before
+  // aSourceDocShell was introduced. According to spec we should be passing
+  // the source browsing context that was used when the history entry was
+  // first created. bug 947716 has been created to address this issue.
+  nsAutoString srcdoc;
+  nsCOMPtr<nsIURI> baseURI;
+  if (mInfo->mIsSrcdocEntry) {
+    srcdoc = mInfo->mSrcdocData;
+    baseURI = mInfo->mBaseURI;
+    flags |= nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+  } else {
+    srcdoc = VoidString();
+  }
+  loadState->SetSrcdocData(srcdoc);
+  loadState->SetBaseURI(baseURI);
+  loadState->SetLoadFlags(flags);
+
+  loadState->SetFirstParty(true);
+  loadState->SetSHEntry(this);
+
+  loadState.forget(aLoadState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::GetBfcacheID(uint64_t* aBfcacheID) {
+  *aBfcacheID = mSharedInfo->mID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SynchronizeLayoutHistoryState() {
+  // No-op on purpose. See nsISHEntry.idl
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SessionHistoryEntry::SyncTreesForSubframeNavigation(
+    nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aTopBC,
+    mozilla::dom::BrowsingContext* aIgnoreBC) {
+  MOZ_CRASH("Need to implement this");
+}
+
+}  // namespace dom
+
+namespace ipc {
+
+void IPDLParamTraits<dom::SessionHistoryInfoAndId>::Write(
+    IPC::Message* aMsg, IProtocol* aActor,
+    const dom::SessionHistoryInfoAndId& aParam) {
+  WriteIPDLParam(aMsg, aActor, aParam.mId);
+
+  const dom::SessionHistoryInfo* info = aParam.mInfo.get();
+  dom::ClonedMessageData stateData;
+  if (info->mStateData) {
+    JSStructuredCloneData& data = info->mStateData->Data();
+    auto iter = data.Start();
+    bool success;
+    stateData.data().data = data.Borrow(iter, data.Size(), &success);
+    if (NS_WARN_IF(!success)) {
+      return;
+    }
+    MOZ_ASSERT(info->mStateData->PortIdentifiers().IsEmpty() &&
+               info->mStateData->BlobImpls().IsEmpty() &&
+               info->mStateData->InputStreams().IsEmpty());
+  }
+
+  WriteIPDLParam(aMsg, aActor, info->mURI);
+  WriteIPDLParam(aMsg, aActor, info->mOriginalURI);
+  WriteIPDLParam(aMsg, aActor, info->mResultPrincipalURI);
+  WriteIPDLParam(aMsg, aActor, info->mReferrerInfo);
+  WriteIPDLParam(aMsg, aActor, info->mTitle);
+  WriteIPDLParam(aMsg, aActor, info->mPostData);
+  WriteIPDLParam(aMsg, aActor, info->mLoadType);
+  WriteIPDLParam(aMsg, aActor, info->mScrollPositionX);
+  WriteIPDLParam(aMsg, aActor, info->mScrollPositionY);
+  WriteIPDLParam(aMsg, aActor, stateData);
+  WriteIPDLParam(aMsg, aActor, info->mSrcdocData);
+  WriteIPDLParam(aMsg, aActor, info->mBaseURI);
+  WriteIPDLParam(aMsg, aActor, info->mLoadReplace);
+  WriteIPDLParam(aMsg, aActor, info->mURIWasModified);
+  WriteIPDLParam(aMsg, aActor, info->mIsSrcdocEntry);
+  WriteIPDLParam(aMsg, aActor, info->mScrollRestorationIsManual);
+  WriteIPDLParam(aMsg, aActor, info->mPersist);
+}
+
+bool IPDLParamTraits<dom::SessionHistoryInfoAndId>::Read(
+    const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
+    dom::SessionHistoryInfoAndId* aResult) {
+  uint64_t id;
+  UniquePtr<dom::SessionHistoryInfo> info =
+      MakeUnique<dom::SessionHistoryInfo>();
+  dom::ClonedMessageData stateData;
+  if (!ReadIPDLParam(aMsg, aIter, aActor, &id) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mURI) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mOriginalURI) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mResultPrincipalURI) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mReferrerInfo) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mTitle) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mPostData) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mLoadType) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mScrollPositionX) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mScrollPositionY) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &stateData) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mSrcdocData) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mBaseURI) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mLoadReplace) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mURIWasModified) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mIsSrcdocEntry) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mScrollRestorationIsManual) ||
+      !ReadIPDLParam(aMsg, aIter, aActor, &info->mPersist)) {
+    aActor->FatalError("Error reading fields for SessionHistoryInfo");
+    return false;
+  }
+  info->mStateData = new nsStructuredCloneContainer();
+  if (aActor->GetSide() == ChildSide) {
+    UnpackClonedMessageDataForChild(stateData, *info->mStateData);
+  } else {
+    UnpackClonedMessageDataForParent(stateData, *info->mStateData);
+  }
+  aResult->mId = id;
+  aResult->mInfo = std::move(info);
+  return true;
+}
+
+}  // namespace ipc
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SessionHistoryEntry.h
@@ -0,0 +1,125 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_SessionHistoryEntry_h
+#define mozilla_dom_SessionHistoryEntry_h
+
+#include "mozilla/UniquePtr.h"
+#include "nsISHEntry.h"
+#include "nsStructuredCloneContainer.h"
+
+class nsDocShellLoadState;
+class nsIChannel;
+class nsIInputStream;
+class nsIReferrerInfo;
+class nsISHistory;
+class nsIURI;
+
+namespace mozilla {
+namespace dom {
+
+struct SessionHistoryInfoAndId;
+class SHEntrySharedParentState;
+
+// SessionHistoryInfo stores session history data for a load. It can be sent
+// over IPC and is used in both the parent and the child processes.
+class SessionHistoryInfo {
+ public:
+  SessionHistoryInfo() = default;
+  SessionHistoryInfo(nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
+
+  bool operator==(const SessionHistoryInfo& aInfo) const {
+    return false;  // FIXME
+  }
+
+  nsIURI* GetURI() const { return mURI; }
+
+ private:
+  friend class SessionHistoryEntry;
+  friend struct mozilla::ipc::IPDLParamTraits<SessionHistoryInfoAndId>;
+
+  nsCOMPtr<nsIURI> mURI;
+  nsCOMPtr<nsIURI> mOriginalURI;
+  nsCOMPtr<nsIURI> mResultPrincipalURI;
+  nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
+  nsString mTitle;
+  nsCOMPtr<nsIInputStream> mPostData;
+  uint32_t mLoadType = 0;
+  int32_t mScrollPositionX = 0;
+  int32_t mScrollPositionY = 0;
+  RefPtr<nsStructuredCloneContainer> mStateData;
+  nsString mSrcdocData;
+  nsCOMPtr<nsIURI> mBaseURI;
+  bool mLoadReplace = false;
+  bool mURIWasModified = false;
+  bool mIsSrcdocEntry = false;
+  bool mScrollRestorationIsManual = false;
+  bool mPersist = false;
+};
+
+// XXX Not sure that the id shouldn't just live in SessionHistoryInfo.
+struct SessionHistoryInfoAndId {
+  SessionHistoryInfoAndId() = default;
+  SessionHistoryInfoAndId(uint64_t aId,
+                          UniquePtr<mozilla::dom::SessionHistoryInfo> aInfo)
+      : mId(aId), mInfo(std::move(aInfo)) {}
+  SessionHistoryInfoAndId(SessionHistoryInfoAndId&& aInfoAndId) = default;
+  SessionHistoryInfoAndId& operator=(
+      const SessionHistoryInfoAndId& aInfoAndId) {
+    mId = aInfoAndId.mId;
+    mInfo = MakeUnique<SessionHistoryInfo>(*aInfoAndId.mInfo);
+    return *this;
+  }
+  bool operator==(const SessionHistoryInfoAndId& aInfoAndId) const {
+    return mId == aInfoAndId.mId && !mInfo == !aInfoAndId.mInfo &&
+           *mInfo == *aInfoAndId.mInfo;
+  }
+
+  uint64_t mId = 0;
+  UniquePtr<mozilla::dom::SessionHistoryInfo> mInfo;
+};
+
+// SessionHistoryEntry is used to store session history data in the parent
+// process. It holds a SessionHistoryInfo, some state shared amongst multiple
+// SessionHistoryEntries, a parent and children.
+class SessionHistoryEntry : public nsISHEntry {
+ public:
+  SessionHistoryEntry(nsISHistory* aSessionHistory,
+                      nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISHENTRY
+
+  const SessionHistoryInfo& GetInfo() const { return *mInfo; }
+
+ private:
+  virtual ~SessionHistoryEntry() = default;
+
+  UniquePtr<SessionHistoryInfo> mInfo;
+  RefPtr<SHEntrySharedParentState> mSharedInfo;
+  nsISHEntry* mParent = nullptr;
+  uint32_t mID;
+  nsTArray<RefPtr<SessionHistoryEntry>> mChildren;
+};
+
+}  // namespace dom
+
+// Allow sending SessionHistoryInfo objects over IPC.
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<dom::SessionHistoryInfoAndId> {
+  static void Write(IPC::Message* aMsg, IProtocol* aActor,
+                    const dom::SessionHistoryInfoAndId& aParam);
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, dom::SessionHistoryInfoAndId* aResult);
+};
+
+}  // namespace ipc
+
+}  // namespace mozilla
+
+#endif /* mozilla_dom_SessionHistoryEntry_h */
--- a/docshell/shistory/moz.build
+++ b/docshell/shistory/moz.build
@@ -16,23 +16,25 @@ XPIDL_MODULE = 'shistory'
 EXPORTS += [
     'nsSHEntry.h',
     'nsSHEntryShared.h',
     'nsSHistory.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'ChildSHistory.h',
+    'SessionHistoryEntry.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChildSHistory.cpp',
     'nsSHEntry.cpp',
     'nsSHEntryShared.cpp',
     'nsSHistory.cpp',
+    'SessionHistoryEntry.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/docshell/shistory/nsSHEntryShared.h
+++ b/docshell/shistory/nsSHEntryShared.h
@@ -48,18 +48,19 @@ class Document;
  * process.
  */
 class SHEntrySharedParentState {
  public:
   uint64_t GetID() const { return mID; }
 
   void NotifyListenersContentViewerEvicted();
 
+  explicit SHEntrySharedParentState(nsISHistory* aSHistory);
+
  protected:
-  explicit SHEntrySharedParentState(nsISHistory* aSHistory);
   explicit SHEntrySharedParentState(SHEntrySharedParentState* aDuplicate)
       : SHEntrySharedParentState(aDuplicate->mSHistory) {}
   explicit SHEntrySharedParentState(nsIWeakReference* aSHistory);
   virtual ~SHEntrySharedParentState();
   NS_INLINE_DECL_VIRTUAL_REFCOUNTING_WITH_DESTROY(SHEntrySharedParentState,
                                                   Destroy())
 
   virtual void Destroy() { delete this; }
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/dom/DataTransferItemList.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/PaymentRequestParent.h"
 #include "mozilla/dom/BrowserBridgeParent.h"
 #include "mozilla/dom/RemoteDragStartData.h"
 #include "mozilla/dom/RemoteWebProgress.h"
 #include "mozilla/dom/RemoteWebProgressRequest.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
 #include "mozilla/dom/SessionStoreUtils.h"
 #include "mozilla/dom/SessionStoreUtilsBinding.h"
 #include "mozilla/dom/UserActivation.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/Hal.h"
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3445,16 +3445,20 @@ mozilla::ipc::IPCResult ContentChild::Re
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return IPC_OK();
   }
   loadState->SetLoadFlags(aArgs.loadStateLoadFlags());
   if (IsValidLoadType(aArgs.loadStateLoadType())) {
     loadState->SetLoadType(aArgs.loadStateLoadType());
   }
 
+  if (aArgs.sessionHistoryInfo().isSome()) {
+    loadState->SetSessionHistoryInfo(aArgs.sessionHistoryInfo().ref());
+  }
+
   RefPtr<ChildProcessChannelListener> processListener =
       ChildProcessChannelListener::GetSingleton();
   // The listener will call completeRedirectSetup or asyncOpen on the channel.
   processListener->OnChannelReady(
       loadState, aArgs.redirectIdentifier(), std::move(aEndpoints),
       aArgs.timing().refOr(nullptr), std::move(resolve));
   scopeExit.release();
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -6358,16 +6358,26 @@ mozilla::ipc::IPCResult ContentParent::R
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_RELEASE_ASSERT(swm, "ServiceWorkers should shutdown before SWM.");
 
   swm->ReportServiceWorkerShutdownProgress(aShutdownStateId, aProgress);
 
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentParent::RecvHistoryCommit(
+    const MaybeDiscarded<BrowsingContext>& aContext,
+    uint64_t aSessionHistoryEntryID) {
+  if (!aContext.IsDiscarded()) {
+    aContext.get_canonical()->SessionHistoryCommit(aSessionHistoryEntryID);
+  }
+
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult ContentParent::RecvCommitWindowContextTransaction(
     const MaybeDiscarded<WindowContext>& aContext,
     WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
   // Record the new BrowsingContextFieldEpoch associated with this transaction.
   // This should be done unconditionally, so that we're always in-sync.
   //
   // The order the parent process receives transactions is considered the
   // "canonical" ordering, so we don't need to worry about doing any
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1276,16 +1276,20 @@ class ContentParent final
 
   mozilla::ipc::IPCResult RecvRawMessage(const JSActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData,
                                          const ClonedMessageData& aStack);
 
   mozilla::ipc::IPCResult RecvAbortOtherOrientationPendingPromises(
       const MaybeDiscarded<BrowsingContext>& aContext);
 
+  mozilla::ipc::IPCResult RecvHistoryCommit(
+      const MaybeDiscarded<BrowsingContext>& aContext,
+      uint64_t aSessionHistoryEntryID);
+
   // Notify the ContentChild to enable the input event prioritization when
   // initializing.
   void MaybeEnableRemoteInputEventQueue();
 
 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
   void AppendSandboxParams(std::vector<std::string>& aArgs);
   void AppendDynamicSandboxParams(std::vector<std::string>& aArgs);
 #endif
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1572,16 +1572,19 @@ parent:
                                               Progress aProgress);
 
     /**
      * Whenever a document is updating the OrientationLock, we need to
      * reject the orientationPendingPromises in other processes.
      */
     async AbortOtherOrientationPendingPromises(MaybeDiscardedBrowsingContext aContext);
 
+    async HistoryCommit(MaybeDiscardedBrowsingContext aContext,
+                        uint64_t aSessionHistoryEntryID);
+
 both:
     async ScriptError(nsString message, nsString sourceName, nsString sourceLine,
                       uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
                       nsCString category, bool privateWindow, uint64_t innerWindowId,
                       bool fromChromeContext);
 
     async CommitBrowsingContextTransaction(MaybeDiscardedBrowsingContext aContext,
                                            BrowsingContextTransaction aTransaction,
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -6,24 +6,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocumentLoadListener.h"
 
 #include "mozilla/AntiTrackingUtils.h"
 #include "mozilla/ContentBlockingAllowList.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/MozPromiseInlines.h"  // For MozPromise::FromDomPromise
+#include "mozilla/StaticPrefs_fission.h"
 #include "mozilla/StaticPrefs_security.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/ClientChannelHelper.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/RedirectChannelRegistrar.h"
 #include "mozilla/net/UrlClassifierCommon.h"
 #include "nsContentSecurityUtils.h"
 #include "nsDocShell.h"
 #include "nsDocShellLoadState.h"
 #include "nsExternalHelperAppService.h"
 #include "nsHttpChannel.h"
@@ -525,16 +527,21 @@ bool DocumentLoadListener::Open(
   }
 
   mChannelCreationURI = aLoadState->URI();
   mLoadStateLoadFlags = aLoadState->LoadFlags();
   mLoadStateLoadType = aLoadState->LoadType();
   mTiming = aTiming;
   mSrcdocData = aLoadState->SrcdocData();
   mBaseURI = aLoadState->BaseURI();
+  if (StaticPrefs::fission_sessionHistoryInParent() &&
+      browsingContext->GetSessionHistory()) {
+    mSessionHistoryInfo =
+        browsingContext->CreateSessionHistoryEntryForLoad(aLoadState, mChannel);
+  }
 
   if (auto* ctx = GetBrowsingContext()) {
     ctx->StartDocumentLoad(this);
   }
   return true;
 }
 
 bool DocumentLoadListener::OpenFromParent(
@@ -1148,16 +1155,21 @@ void DocumentLoadListener::SerializeRedi
   aArgs.newLoadFlags() = aLoadFlags;
   aArgs.redirectFlags() = aRedirectFlags;
   aArgs.redirectIdentifier() = mCrossProcessRedirectIdentifier;
   aArgs.properties() = do_QueryObject(mChannel.get());
   aArgs.srcdocData() = mSrcdocData;
   aArgs.baseUri() = mBaseURI;
   aArgs.loadStateLoadFlags() = mLoadStateLoadFlags;
   aArgs.loadStateLoadType() = mLoadStateLoadType;
+  if (mSessionHistoryInfo) {
+    aArgs.sessionHistoryInfo().emplace(
+        mSessionHistoryInfo->mId, MakeUnique<mozilla::dom::SessionHistoryInfo>(
+                                      *mSessionHistoryInfo->mInfo));
+  }
 }
 
 bool DocumentLoadListener::MaybeTriggerProcessSwitch() {
   MOZ_DIAGNOSTIC_ASSERT(!mDoingProcessSwitch,
                         "Already in the middle of switching?");
   MOZ_DIAGNOSTIC_ASSERT(mChannel);
   MOZ_DIAGNOSTIC_ASSERT(mParentChannelListener);
 
--- a/netwerk/ipc/DocumentLoadListener.h
+++ b/netwerk/ipc/DocumentLoadListener.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/MozPromise.h"
 #include "mozilla/Variant.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/PDocumentChannelParent.h"
 #include "mozilla/net/ParentChannelListener.h"
 #include "mozilla/net/ADocumentChannelBridge.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIParentChannel.h"
 #include "nsIParentRedirectingChannel.h"
 #include "nsIRedirectResultListener.h"
 #include "nsIMultiPartChannel.h"
 
 #define DOCUMENT_LOAD_LISTENER_IID                   \
@@ -394,16 +395,18 @@ class DocumentLoadListener : public nsII
   // True when we have seen at least one non-interal redirect.
   bool mHaveVisibleRedirect = false;
 
   nsTArray<StreamFilterRequest> mStreamFilterRequests;
 
   nsString mSrcdocData;
   nsCOMPtr<nsIURI> mBaseURI;
 
+  mozilla::UniquePtr<mozilla::dom::SessionHistoryInfoAndId> mSessionHistoryInfo;
+
   // Flags from nsDocShellLoadState::LoadFlags/Type that we want to make
   // available to the new docshell if we switch processes.
   uint32_t mLoadStateLoadFlags = 0;
   uint32_t mLoadStateLoadType = 0;
 
   // Corresponding redirect channel registrar Id for the final channel that
   // we want to use when redirecting the child, or doing a process switch.
   // 0 means redirection is not started.
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -19,16 +19,17 @@ include DOMTypes;
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
 using struct nsHttpAtom from "nsHttp.h";
 using class mozilla::net::nsHttpResponseHead from "nsHttpResponseHead.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using refcounted class nsIPropertyBag2 from "mozilla/dom/PropertyBagUtils.h";
 using refcounted class nsDOMNavigationTiming from "nsDOMNavigationTiming.h";
 using nsILoadInfo::CrossOriginEmbedderPolicy from "nsILoadInfo.h";
+using moveonly mozilla::dom::SessionHistoryInfoAndId from "mozilla/dom/SessionHistoryEntry.h";
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // CookieJarSettings IPDL structs
 //-----------------------------------------------------------------------------
 
@@ -465,16 +466,17 @@ struct RedirectToRealChannelArgs {
   nsString? contentDispositionFilename;
   uint64_t redirectIdentifier;
   nsIPropertyBag2 properties;
   uint32_t loadStateLoadFlags;
   uint32_t loadStateLoadType;
   nsDOMNavigationTiming? timing;
   nsString srcdocData;
   nsIURI baseUri;
+  SessionHistoryInfoAndId? sessionHistoryInfo;
 };
 
 struct TimingStructArgs {
   TimeStamp domainLookupStart;
   TimeStamp domainLookupEnd;
   TimeStamp connectStart;
   TimeStamp tcpConnectEnd;
   TimeStamp secureConnectionStart;