Backed out 4 changesets (bug 1629866, bug 1570255) for assertion failures on DocumentChannelChild.cpp. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Wed, 13 May 2020 18:30:42 +0300
changeset 529643 02f46362fdb0c35a8e4dd208f0de70827a75d1a1
parent 529642 0c7811dc2ea9493f195f554f7cbb68193f77f6e5
child 529644 b2c0bab59ddc197d02c9d8651bc4f5876235935d
push id115826
push userncsoregi@mozilla.com
push dateWed, 13 May 2020 15:33:13 +0000
treeherderautoland@02f46362fdb0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1629866, 1570255
milestone78.0a1
backs out214e4a11be0d55daae2288e6d8521e6ffb13aa78
db066dda1bb8a4f15c444e387cbdec427384561b
d9f75d88613ea746d5a0c477e0914e4d563ac243
fe2d4790b73a0c404e2516c7c4aa510807fc318f
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
Backed out 4 changesets (bug 1629866, bug 1570255) for assertion failures on DocumentChannelChild.cpp. CLOSED TREE Backed out changeset 214e4a11be0d (bug 1570255) Backed out changeset db066dda1bb8 (bug 1570255) Backed out changeset d9f75d88613e (bug 1570255) Backed out changeset fe2d4790b73a (bug 1629866)
docshell/base/BrowsingContext.cpp
docshell/base/BrowsingContext.h
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/base/nsIDocShell.idl
docshell/build/nsDocShellModule.cpp
docshell/shistory/ChildSHistory.cpp
docshell/shistory/ChildSHistory.h
docshell/shistory/MaybeNewPSHEntry.cpp
docshell/shistory/MaybeNewPSHEntry.h
docshell/shistory/PSHEntry.ipdl
docshell/shistory/PSHistory.ipdl
docshell/shistory/SHEntryChild.cpp
docshell/shistory/SHEntryChild.h
docshell/shistory/SHEntryParent.cpp
docshell/shistory/SHEntryParent.h
docshell/shistory/SHistoryChild.cpp
docshell/shistory/SHistoryChild.h
docshell/shistory/SHistoryParent.cpp
docshell/shistory/SHistoryParent.h
docshell/shistory/SessionHistoryEntry.cpp
docshell/shistory/SessionHistoryEntry.h
docshell/shistory/moz.build
docshell/shistory/nsISHistory.idl
docshell/shistory/nsSHEntry.cpp
docshell/shistory/nsSHEntry.h
docshell/shistory/nsSHEntryShared.cpp
docshell/shistory/nsSHEntryShared.h
docshell/shistory/nsSHistory.cpp
docshell/shistory/nsSHistory.h
dom/base/nsFrameLoader.cpp
dom/ipc/BrowserParent.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/PContent.ipdl
dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
ipc/ipdl/sync-messages.ini
netwerk/ipc/DocumentLoadListener.cpp
netwerk/ipc/DocumentLoadListener.h
netwerk/ipc/NeckoChannelParams.ipdlh
toolkit/components/browser/nsWebBrowser.cpp
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -32,17 +32,16 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Components.h"
 #include "mozilla/HashTable.h"
 #include "mozilla/Logging.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/Services.h"
-#include "mozilla/StaticPrefs_fission.h"
 #include "mozilla/StaticPrefs_page_load.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIURIFixup.h"
 
 #include "nsDocShell.h"
 #include "nsFocusManager.h"
 #include "nsGlobalWindowOuter.h"
 #include "nsIObserverService.h"
@@ -355,19 +354,16 @@ void BrowsingContext::CreateFromIPC(Brow
                                            embedderProcessId, Type::Content,
                                            std::move(aInit.mFields));
   } else {
     context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
                                   std::move(aInit.mFields));
   }
 
   context->mWindowless = aInit.mWindowless;
-  if (aInit.mHasSessionHistory) {
-    context->InitSessionHistory();
-  }
 
   // NOTE: Call through the `Set` methods for these values to ensure that any
   // relevant process-local state is also updated.
   context->SetOriginAttributes(aInit.mOriginAttributes);
   context->SetRemoteTabs(aInit.mUseRemoteTabs);
   context->SetRemoteSubframes(aInit.mUseRemoteSubframes);
   // NOTE: Private browsing ID is set by `SetOriginAttributes`.
 
@@ -403,19 +399,16 @@ BrowsingContext::BrowsingContext(WindowC
 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
   // XXX(nika): We should communicate that we are now an active BrowsingContext
   // process to the parent & do other validation here.
   MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
   MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
   mDocShell = aDocShell;
   mDanglingRemoteOuterProxies = !mIsInProcess;
   mIsInProcess = true;
-  if (mChildSessionHistory) {
-    mChildSessionHistory->SetIsInProcess(true);
-  }
 }
 
 // This class implements a callback that will return the remote window proxy for
 // mBrowsingContext in that compartment, if it has one. It also removes the
 // proxy from the map, because the object will be transplanted into another kind
 // of object.
 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
     : public js::CompartmentTransplantCallback {
@@ -519,20 +512,16 @@ void BrowsingContext::Attach(bool aFromI
   } else {
     mGroup->Toplevels().AppendElement(this);
   }
 
   if (GetIsPopupSpam()) {
     PopupBlocker::RegisterOpenPopupSpam();
   }
 
-  if (IsTop() && GetHasSessionHistory()) {
-    CreateChildSHistory();
-  }
-
   if (XRE_IsContentProcess() && !aFromIPC) {
     // Send attach to our parent if we need to.
     ContentChild::GetSingleton()->SendCreateBrowsingContext(
         mGroup->Id(), GetIPCInitializer());
   } else if (XRE_IsParentProcess()) {
     mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) {
       MOZ_DIAGNOSTIC_ASSERT(IsContent(),
                             "chrome BCG cannot be synced to content process");
@@ -632,21 +621,16 @@ void BrowsingContext::PrepareForProcessC
 
   mIsInProcess = false;
   mUserGestureStart = TimeStamp();
 
   // NOTE: For now, clear our nsDocShell reference, as we're primarily in a
   // different process now. This may need to change in the future with
   // Cross-Process BFCache.
   mDocShell = nullptr;
-  if (mChildSessionHistory) {
-    // This can be removed once session history is stored exclusively in the
-    // parent process.
-    mChildSessionHistory->SetIsInProcess(false);
-  }
 
   if (!mWindowProxy) {
     return;
   }
 
   // We have to go through mWindowProxy rather than calling GetDOMWindow() on
   // mDocShell because the mDocshell reference gets cleared immediately after
   // the window is closed.
@@ -1397,26 +1381,26 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Br
 
   if (tmp->GetIsPopupSpam()) {
     PopupBlocker::UnregisterOpenPopupSpam();
     // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
     // automatically.
     tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
   }
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(
-      mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
-      mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mParentWindow, mGroup,
+                                  mEmbedderElement, mWindowContexts,
+                                  mCurrentWindowContext, mSessionStorageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
       mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
-      mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
+      mCurrentWindowContext, mSessionStorageManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 class RemoteLocationProxy
     : public RemoteObjectProxy<BrowsingContext::LocationProxy,
                                Location_Binding::sCrossOriginAttributes,
                                Location_Binding::sCrossOriginMethods> {
  public:
   typedef RemoteObjectProxy Base;
@@ -1785,17 +1769,16 @@ BrowsingContext::IPCInitializer Browsing
 
   IPCInitializer init;
   init.mId = Id();
   init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
   init.mWindowless = mWindowless;
   init.mUseRemoteTabs = mUseRemoteTabs;
   init.mUseRemoteSubframes = mUseRemoteSubframes;
   init.mOriginAttributes = mOriginAttributes;
-  init.mHasSessionHistory = mChildSessionHistory != nullptr;
   init.mFields = mFields.Fields();
   return init;
 }
 
 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
   RefPtr<WindowContext> parent;
   if (mParentId != 0) {
     parent = WindowContext::GetById(mParentId);
@@ -2211,65 +2194,16 @@ void BrowsingContext::AddDeprioritizedLo
 
   RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
   mDeprioritizedLoadRunner.insertBack(runner);
   NS_DispatchToCurrentThreadQueue(
       runner.forget(), StaticPrefs::page_load_deprioritization_period(),
       EventQueuePriority::Idle);
 }
 
-void BrowsingContext::InitSessionHistory() {
-  MOZ_ASSERT(!IsDiscarded());
-  MOZ_ASSERT(IsTop());
-  MOZ_ASSERT(EverAttached());
-
-  if (!GetHasSessionHistory()) {
-    SetHasSessionHistory(true);
-
-    // If the top browsing context (this one) is loaded in this process then we
-    // also create the session history implementation for the child process.
-    // This can be removed once session history is stored exclusively in the
-    // parent process.
-    mChildSessionHistory->SetIsInProcess(mDocShell);
-  }
-}
-
-ChildSHistory* BrowsingContext::GetChildSessionHistory() {
-  if (!StaticPrefs::fission_sessionHistoryInParent()) {
-    // For now we're checking that the session history object for the child
-    // process is available before returning the ChildSHistory object, because
-    // it is the actual implementation that ChildSHistory forwards to. This can
-    // be removed once session history is stored exclusively in the parent
-    // process.
-    return mChildSessionHistory && mChildSessionHistory->IsInProcess()
-               ? mChildSessionHistory.get()
-               : nullptr;
-  }
-
-  return mChildSessionHistory;
-}
-
-void BrowsingContext::CreateChildSHistory() {
-  MOZ_ASSERT(IsTop());
-
-  // Because session history is global in a browsing context tree, every process
-  // that has access to a browsing context tree needs access to its session
-  // history. That is why we create the ChildSHistory object in every process
-  // where we have access to this browsing context (which is the top one).
-  mChildSessionHistory = new ChildSHistory(this);
-}
-
-void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
-                             bool aOldValue) {
-  MOZ_ASSERT(GetHasSessionHistory() || !aOldValue,
-             "We don't support turning off session history.");
-
-  CreateChildSHistory();
-}
-
 }  // namespace dom
 
 namespace ipc {
 
 void IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Write(
     IPC::Message* aMsg, IProtocol* aActor,
     const dom::MaybeDiscarded<dom::BrowsingContext>& aParam) {
   MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -56,17 +56,16 @@ class IProtocol;
 template <typename T>
 struct IPDLParamTraits;
 }  // namespace ipc
 
 namespace dom {
 class BrowsingContent;
 class BrowsingContextGroup;
 class CanonicalBrowsingContext;
-class ChildSHistory;
 class ContentParent;
 class Element;
 template <typename>
 struct Nullable;
 template <typename T>
 class Sequence;
 class StructuredCloneHolder;
 class WindowContext;
@@ -128,21 +127,17 @@ class WindowProxyHolder;
   FIELD(UserAgentOverride, nsString)                                         \
   FIELD(EmbedderElementType, Maybe<nsString>)                                \
   FIELD(MessageManagerGroup, nsString)                                       \
   FIELD(MaxTouchPointsOverride, uint8_t)                                     \
   FIELD(FullZoom, float)                                                     \
   FIELD(WatchedByDevToolsInternal, bool)                                     \
   FIELD(TextZoom, float)                                                     \
   /* See nsIRequest for possible flags. */                                   \
-  FIELD(DefaultLoadFlags, uint32_t)                                          \
-  /* Signals that session history is enabled for this browsing context tree. \
-   * This is only ever set to true on the top BC, so consumers need to get   \
-   * the value from the top BC! */                                           \
-  FIELD(HasSessionHistory, bool)
+  FIELD(DefaultLoadFlags, uint32_t)
 
 // BrowsingContext, in this context, is the cross process replicated
 // environment in which information about documents is stored. In
 // particular the tree structure of nested browsing contexts is
 // represented by the tree of BrowsingContexts.
 //
 // The tree of BrowsingContexts is created in step with its
 // corresponding nsDocShell, and when nsDocShells are connected
@@ -557,17 +552,16 @@ class BrowsingContext : public nsILoadCo
     already_AddRefed<WindowContext> GetParent();
     already_AddRefed<BrowsingContext> GetOpener();
 
     uint64_t GetOpenerId() const { return mozilla::Get<IDX_OpenerId>(mFields); }
 
     bool mWindowless = false;
     bool mUseRemoteTabs = false;
     bool mUseRemoteSubframes = false;
-    bool mHasSessionHistory = false;
     OriginAttributes mOriginAttributes;
 
     FieldTuple mFields;
 
     bool operator==(const IPCInitializer& aOther) const {
       return mId == aOther.mId && mParentId == aOther.mParentId &&
              mWindowless == aOther.mWindowless &&
              mUseRemoteTabs == aOther.mUseRemoteTabs &&
@@ -598,23 +592,16 @@ class BrowsingContext : public nsILoadCo
   RefPtr<SessionStorageManager> GetSessionStorageManager();
 
   bool PendingInitialization() const { return mPendingInitialization; };
   void SetPendingInitialization(bool aVal) { mPendingInitialization = aVal; };
 
   const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
   nsresult SetOriginAttributes(const OriginAttributes& aAttrs);
 
-  // This should only be called on the top browsing context.
-  void InitSessionHistory();
-
-  // This will only ever return a non-null value if called on the top browsing
-  // context.
-  ChildSHistory* GetChildSessionHistory();
-
  protected:
   virtual ~BrowsingContext();
   BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup,
                   uint64_t aBrowsingContextId, Type aType,
                   FieldTuple&& aFields);
 
  private:
   void Attach(bool aFromIPC, ContentParent* aOriginProcess);
@@ -745,18 +732,16 @@ class BrowsingContext : public nsILoadCo
 
   bool CanSet(FieldIndex<IDX_DefaultLoadFlags>,
               const uint32_t& aDefaultLoadFlags, ContentParent* aSource);
   void DidSet(FieldIndex<IDX_DefaultLoadFlags>);
 
   bool CanSet(FieldIndex<IDX_UseGlobalHistory>, const bool& aUseGlobalHistory,
               ContentParent* aSource);
 
-  void DidSet(FieldIndex<IDX_HasSessionHistory>, bool aOldValue);
-
   template <size_t I, typename T>
   bool CanSet(FieldIndex<I>, const T&, ContentParent*) {
     return true;
   }
 
   // Overload `DidSet` to get notifications for a particular field being set.
   //
   // You can also overload the variant that gets the old value if you need it.
@@ -771,18 +756,16 @@ class BrowsingContext : public nsILoadCo
   // True if the process attemping to set field is the same as the owning
   // process.
   bool CheckOnlyOwningProcessCanSet(ContentParent* aSource);
 
   // True if the process attempting to set field is the same as the embedder's
   // process.
   bool CheckOnlyEmbedderCanSet(ContentParent* aSource);
 
-  void CreateChildSHistory();
-
   // Type of BrowsingContent
   const Type mType;
 
   // Unique id identifying BrowsingContext
   const uint64_t mBrowsingContextId;
 
   RefPtr<BrowsingContextGroup> mGroup;
   RefPtr<WindowContext> mParentWindow;
@@ -867,18 +850,17 @@ class BrowsingContext : public nsILoadCo
     }
 
    private:
     RefPtr<nsIRunnable> mInner;
   };
 
   mozilla::LinkedList<DeprioritizedLoadRunner> mDeprioritizedLoadRunner;
 
-  RefPtr<SessionStorageManager> mSessionStorageManager;
-  RefPtr<ChildSHistory> mChildSessionHistory;
+  RefPtr<dom::SessionStorageManager> mSessionStorageManager;
 };
 
 /**
  * Gets a WindowProxy object for a BrowsingContext that lives in a different
  * process (creating the object if it doesn't already exist). The WindowProxy
  * object will be in the compartment that aCx is currently in. This should only
  * be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
  * lives in this process, and a same-process WindowProxy should be used (see
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -13,24 +13,22 @@
 #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__))
 
@@ -183,87 +181,29 @@ CanonicalBrowsingContext::GetParentCross
   if (GetEmbedderElement()) {
     return do_AddRef(
         Cast(GetEmbedderElement()->OwnerDoc()->GetBrowsingContext()));
   }
   return nullptr;
 }
 
 nsISHistory* CanonicalBrowsingContext::GetSessionHistory() {
-  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);
+  if (mSessionHistory) {
+    return mSessionHistory;
   }
 
-  return mSessionHistory;
-}
-
-static uint64_t gNextHistoryEntryId = 0;
-
-UniquePtr<SessionHistoryInfoAndId>
-CanonicalBrowsingContext::CreateSessionHistoryEntryForLoad(
-    nsDocShellLoadState* aLoadState, nsIChannel* aChannel) {
-  MOZ_ASSERT(GetSessionHistory(),
-             "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());
-          }
-        }
-      }
-      Group()->EachParent([&](ContentParent* aParent) {
-        // FIXME Should we return the length to the one process that committed
-        //       as an async return value? Or should this use synced fields?
-        Unused << aParent->SendHistoryCommitLength(
-            Top(), GetSessionHistory()->GetCount());
-      });
-      return;
+  nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(GetDocShell());
+  if (webNav) {
+    RefPtr<ChildSHistory> shistory = webNav->GetSessionHistory();
+    if (shistory) {
+      return shistory->LegacySHistory();
     }
   }
-  // FIXME Should we throw an error if we don't find an entry for
-  // aSessionHistoryEntryId?
+
+  return nullptr;
 }
 
 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,43 +8,33 @@
 #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"
-
-class nsISHistory;
+#include "nsISHistory.h"
+#include "nsISHEntry.h"
 
 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)
@@ -88,20 +78,19 @@ 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();
-  UniquePtr<SessionHistoryInfoAndId> CreateSessionHistoryEntryForLoad(
-      nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
-  void SessionHistoryCommit(uint64_t aSessionHistoryEntryId);
-
+  void SetSessionHistory(nsISHistory* aSHistory) {
+    mSessionHistory = aSHistory;
+  }
   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
@@ -136,16 +125,35 @@ class CanonicalBrowsingContext final : p
   // Return a media controller from the top-level browsing context that can
   // control all media belonging to this browsing context tree. Return nullptr
   // if the top-level browsing context has been discarded.
   MediaController* GetMediaController();
 
   bool AttemptLoadURIInParent(nsDocShellLoadState* aLoadState,
                               uint32_t* aLoadIdentifier);
 
+  bool HasHistoryEntry(nsISHEntry* aEntry) const {
+    return aEntry && (aEntry == mOSHE || aEntry == mLSHE);
+  }
+
+  void UpdateSHEntries(nsISHEntry* aNewLSHE, nsISHEntry* aNewOSHE) {
+    mLSHE = aNewLSHE;
+    mOSHE = aNewOSHE;
+  }
+
+  void SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry) {
+    if (aOldEntry == mOSHE) {
+      mOSHE = aNewEntry;
+    }
+
+    if (aOldEntry == mLSHE) {
+      mLSHE = aNewEntry;
+    }
+  }
+
  protected:
   // Called when the browsing context is being discarded.
   void CanonicalDiscard();
 
   using Type = BrowsingContext::Type;
   CanonicalBrowsingContext(WindowContext* aParentWindow,
                            BrowsingContextGroup* aGroup,
                            uint64_t aBrowsingContextId,
@@ -209,16 +217,17 @@ class CanonicalBrowsingContext final : p
 
   // 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;
+  // These are being mirrored from docshell
+  nsCOMPtr<nsISHEntry> mOSHE;
+  nsCOMPtr<nsISHEntry> mLSHE;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_CanonicalBrowsingContext_h)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -61,17 +61,16 @@
 #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"
@@ -194,17 +193,17 @@
 #include "nsPoint.h"
 #include "nsQueryObject.h"
 #include "nsQueryActor.h"
 #include "nsRect.h"
 #include "nsRefreshTimer.h"
 #include "nsSandboxFlags.h"
 #include "nsSHEntry.h"
 #include "nsSHistory.h"
-#include "nsSHEntry.h"
+#include "SHEntryChild.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsSubDocumentFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsViewSourceHandler.h"
 #include "nsWebBrowserFind.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsWidgetsCID.h"
@@ -419,16 +418,20 @@ nsDocShell::nsDocShell(BrowsingContext* 
 nsDocShell::~nsDocShell() {
   MOZ_ASSERT(!mObserved);
 
   // Avoid notifying observers while we're in the dtor.
   mIsBeingDestroyed = true;
 
   Destroy();
 
+  if (mSessionHistory) {
+    mSessionHistory->LegacySHistory()->ClearRootBrowsingContext();
+  }
+
   if (mContentViewer) {
     mContentViewer->Close(nullptr);
     mContentViewer->Destroy();
     mContentViewer = nullptr;
   }
 
   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
 
@@ -536,17 +539,17 @@ void nsDocShell::DestroyChildren() {
     }
   }
 
   nsDocLoader::DestroyChildren();
 }
 
 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
                                             mScriptGlobal, mInitialClientSource,
-                                            mBrowsingContext,
+                                            mSessionHistory, mBrowsingContext,
                                             mChromeEventHandler)
 
 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
@@ -2947,18 +2950,18 @@ nsDocShell::AddChildSHEntry(nsISHEntry* 
 }
 
 nsresult nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
                                              nsISHEntry* aNewEntry,
                                              int32_t aChildOffset,
                                              uint32_t aLoadType,
                                              bool aCloneChildren) {
   nsresult rv = NS_OK;
-  if (GetSessionHistory()) {
-    rv = GetSessionHistory()->LegacySHistory()->AddChildSHEntryHelper(
+  if (mSessionHistory) {
+    rv = mSessionHistory->LegacySHistory()->AddChildSHEntryHelper(
         aCloneRef, aNewEntry, mBrowsingContext, aCloneChildren);
   } else {
     /* Just pass this along */
     nsCOMPtr<nsIDocShell> parent =
         do_QueryInterface(GetAsSupports(mParent), &rv);
     if (parent) {
       rv = static_cast<nsDocShell*>(parent.get())
                ->AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset,
@@ -3002,18 +3005,23 @@ nsresult nsDocShell::AddChildSHEntryToPa
     }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::RemoveFromSessionHistory() {
-  RefPtr<ChildSHistory> sessionHistory =
-      mBrowsingContext->Top()->GetChildSessionHistory();
+  nsCOMPtr<nsIDocShellTreeItem> root;
+  GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
+  nsCOMPtr<nsIWebNavigation> rootAsWebnav = do_QueryInterface(root);
+  if (!rootAsWebnav) {
+    return NS_OK;
+  }
+  RefPtr<ChildSHistory> sessionHistory = rootAsWebnav->GetSessionHistory();
   if (!sessionHistory) {
     return NS_OK;
   }
   int32_t index = sessionHistory->Index();
   AutoTArray<nsID, 16> ids({mHistoryID});
   sessionHistory->LegacySHistory()->RemoveEntries(ids, index);
   return NS_OK;
 }
@@ -4069,19 +4077,35 @@ nsDocShell::GetCurrentURI(nsIURI** aURI)
   NS_ENSURE_ARG_POINTER(aURI);
 
   nsCOMPtr<nsIURI> uri = mCurrentURI;
   uri.forget(aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::InitSessionHistory() {
+  MOZ_ASSERT(!mIsBeingDestroyed);
+
+  // Make sure that we are the root DocShell, and set a handle to root docshell
+  // in the session history.
+  nsCOMPtr<nsIDocShellTreeItem> root;
+  GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
+  if (root != this) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mSessionHistory = new ChildSHistory(this);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
   NS_ENSURE_ARG_POINTER(aSessionHistory);
-  RefPtr<ChildSHistory> shistory = GetSessionHistory();
+  RefPtr<ChildSHistory> shistory = mSessionHistory;
   shistory.forget(aSessionHistory);
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsDocShell::nsIWebPageDescriptor
 //*****************************************************************************
 
@@ -4292,21 +4316,22 @@ nsDocShell::Destroy() {
   mParentWidget = nullptr;
   mCurrentURI = nullptr;
 
   if (mScriptGlobal) {
     mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
     mScriptGlobal = nullptr;
   }
 
-  if (GetSessionHistory()) {
+  if (mSessionHistory) {
     // We want to destroy these content viewers now rather than
     // letting their destruction wait for the session history
     // entries to get garbage collected.  (Bug 488394)
-    GetSessionHistory()->EvictLocalContentViewers();
+    mSessionHistory->EvictLocalContentViewers();
+    mSessionHistory = nullptr;
   }
 
   if (mWillChangeProcess) {
     mBrowsingContext->PrepareForProcessChange();
   }
 
   SetTreeOwner(nullptr);
 
@@ -5465,28 +5490,16 @@ 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:
@@ -7502,23 +7515,23 @@ nsresult nsDocShell::CreateContentViewer
     if (failedURI) {
       errorOnLocationChangeNeeded =
           OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
                    nullptr, mLoadType, nullptr, false, false, false);
     }
 
     // Be sure to have a correct mLSHE, it may have been cleared by
     // EndPageLoad. See bug 302115.
-    ChildSHistory* shistory = GetSessionHistory();
-    if (shistory && !mLSHE) {
-      int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
+    if (mSessionHistory && !mLSHE) {
+      int32_t idx = mSessionHistory->LegacySHistory()->GetRequestedIndex();
       if (idx == -1) {
-        idx = shistory->Index();
-      }
-      shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
+        idx = mSessionHistory->Index();
+      }
+      mSessionHistory->LegacySHistory()->GetEntryAtIndex(idx,
+                                                         getter_AddRefs(mLSHE));
     }
 
     mLoadType = LOAD_ERROR_PAGE;
   }
 
   nsCOMPtr<nsIURI> finalURI;
   // If this a redirect, use the final url (uri)
   // else use the original url
@@ -8428,21 +8441,21 @@ nsresult nsDocShell::HandleSameDocumentN
 
   /* Restore the original LSHE if we were loading something
    * while same document navigation was initiated.
    */
   SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
   /* Set the title for the SH entry for this target url. so that
    * SH menus in go/back/forward buttons won't be empty for this.
    */
-  ChildSHistory* shistory = GetSessionHistory();
-  if (shistory) {
-    int32_t index = shistory->Index();
+  if (mSessionHistory) {
+    int32_t index = mSessionHistory->Index();
     nsCOMPtr<nsISHEntry> shEntry;
-    shistory->LegacySHistory()->GetEntryAtIndex(index, getter_AddRefs(shEntry));
+    mSessionHistory->LegacySHistory()->GetEntryAtIndex(index,
+                                                       getter_AddRefs(shEntry));
     NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
     shEntry->SetTitle(mTitle);
   }
 
   /* Set the title for the Global History entry for this anchor url.
    */
   UpdateGlobalHistoryTitle(aLoadState->URI());
 
@@ -9466,25 +9479,16 @@ 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()) {
@@ -10086,19 +10090,22 @@ bool nsDocShell::OnNewURI(nsIURI* aURI, 
 
   // We don't update session history on reload unless we're loading
   // an iframe in shift-reload case.
   bool updateSHistory =
       updateGHistory && (!(aLoadType & LOAD_CMD_RELOAD) ||
                          (IsForceReloadType(aLoadType) && IsFrame()));
 
   // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
-  // in the root browsing context.
-  RefPtr<ChildSHistory> rootSH =
-      mBrowsingContext->Top()->GetChildSessionHistory();
+  // current frame or in the root docshell.
+  RefPtr<ChildSHistory> rootSH = mSessionHistory;
+  if (!rootSH) {
+    // Get the handle to SH from the root docshell
+    rootSH = GetRootSessionHistory();
+  }
   if (!rootSH) {
     updateSHistory = false;
     updateGHistory = false;  // XXX Why global history too?
   }
 
   // Check if the url to be loaded is the same as the one already loaded.
   if (mCurrentURI) {
     aURI->Equals(mCurrentURI, &equalUri);
@@ -10191,21 +10198,21 @@ bool nsDocShell::OnNewURI(nsIURI* aURI, 
       /* This is  a fresh page getting loaded for the first time
        *.Create a Entry for it and add it to SH, if this is the
        * rootDocShell
        */
       (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
                                 aPrincipalToInherit, aStoragePrincipalToInherit,
                                 aCsp, aCloneSHChildren, getter_AddRefs(mLSHE));
     }
-  } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
+  } else if (mSessionHistory && mLSHE && mURIResultedInDocument) {
     // Even if we don't add anything to SHistory, ensure the current index
     // points to the same SHEntry as our mLSHE.
 
-    GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(mLSHE);
+    mSessionHistory->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(mLSHE);
   }
 
   // If this is a POST request, we do not want to include this in global
   // history.
   if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
       !net::ChannelIsPost(aChannel)) {
     nsCOMPtr<nsIURI> previousURI;
     uint32_t previousFlags = 0;
@@ -10705,17 +10712,19 @@ nsresult nsDocShell::AddToSessionHistory
   }
 
   // Create a new entry if necessary.
   if (!entry) {
     nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
 
     RefPtr<ChildSHistory> shistory = webnav->GetSessionHistory();
-    entry = new nsSHEntry(shistory ? shistory->LegacySHistory() : nullptr);
+    entry = CreateSHEntryForDocShell(shistory ? shistory->LegacySHistory()
+                                              : nullptr);
+    NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
   }
 
   // Get the post data & referrer
   nsCOMPtr<nsIInputStream> inputStream;
   nsCOMPtr<nsIURI> originalURI;
   nsCOMPtr<nsIURI> resultPrincipalURI;
   bool loadReplace = false;
   nsCOMPtr<nsIReferrerInfo> referrerInfo;
@@ -10832,21 +10841,21 @@ nsresult nsDocShell::AddToSessionHistory
                 cacheKey,             // CacheKey
                 mContentTypeHint,     // Content-type
                 triggeringPrincipal,  // Channel or provided principal
                 principalToInherit, storagePrincipalToInherit, csp, HistoryID(),
                 mDynamicallyCreated, originalURI, resultPrincipalURI,
                 loadReplace, referrerInfo, srcdoc, srcdocEntry, baseURI,
                 saveLayoutState, expired);
 
-  if (root == static_cast<nsIDocShellTreeItem*>(this) && GetSessionHistory()) {
+  if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
     bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
     Maybe<int32_t> previousEntryIndex;
     Maybe<int32_t> loadedEntryIndex;
-    rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
+    rv = mSessionHistory->LegacySHistory()->AddToRootSessionHistory(
         aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
         shouldPersist, &previousEntryIndex, &loadedEntryIndex);
 
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
     if (previousEntryIndex.isSome()) {
       mPreviousEntryIndex = previousEntryIndex.value();
     }
     if (loadedEntryIndex.isSome()) {
@@ -10994,16 +11003,34 @@ void nsDocShell::SetHistoryEntryAndUpdat
     deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
     MOZ_ASSERT(mLSHE.get() == aLSHE.value());
   }
   nsCOMPtr<nsISHEntry> deathGripOldOSHE;
   if (aOSHE.isSome()) {
     deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
     MOZ_ASSERT(mOSHE.get() == aOSHE.value());
   }
+
+  // Do not update the BC if the SH pref is off and we are not a parent process
+  // or if it is discarded
+  if ((!StaticPrefs::fission_sessionHistoryInParent() &&
+       XRE_IsContentProcess()) ||
+      mBrowsingContext->IsDiscarded()) {
+    return;
+  }
+  if (XRE_IsParentProcess()) {
+    // We are in the parent process, thus we can update the entries directly
+    mBrowsingContext->Canonical()->UpdateSHEntries(mLSHE, mOSHE);
+  } else {
+    ContentChild* cc = ContentChild::GetSingleton();
+    // We can't update canonical BC directly, so do it over IPC call
+    cc->SendUpdateSHEntriesInBC(static_cast<SHEntryChild*>(mLSHE.get()),
+                                static_cast<SHEntryChild*>(mOSHE.get()),
+                                mBrowsingContext);
+  }
 }
 
 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
     nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
   // We need to sync up the docshell and session history trees for
   // subframe navigation.  If the load was in a subframe, we forward up to
   // the root docshell, which will then recursively sync up all docshells
   // to their corresponding entries in the new session history tree.
@@ -12227,19 +12254,19 @@ nsDocShell::ResumeRedirectedLoad(uint64_
                             previousURI, previousFlags);
 
         if (aTiming) {
           self->mTiming = new nsDOMNavigationTiming(self, aTiming);
         }
 
         // If we're performing a history load, locate the correct history entry,
         // and set the relevant bits on our loadState.
-        if (aHistoryIndex >= 0 && self->GetSessionHistory()) {
+        if (aHistoryIndex >= 0 && self->mSessionHistory) {
           nsCOMPtr<nsISHistory> legacySHistory =
-              self->GetSessionHistory()->LegacySHistory();
+              self->mSessionHistory->LegacySHistory();
 
           nsCOMPtr<nsISHEntry> entry;
           nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
                                                         getter_AddRefs(entry));
           if (NS_SUCCEEDED(rv)) {
             legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
             aLoadState->SetLoadType(LOAD_HISTORY);
             aLoadState->SetSHEntry(entry);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -59,17 +59,16 @@
 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;
@@ -658,20 +657,16 @@ class nsDocShell final : public nsDocLoa
   already_AddRefed<nsISHEntry> SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr,
                                                nsISHEntry* aEntry);
 
   // This method calls SetHistoryEntry and updates mOSHE and mLSHE in BC to be
   // the same as in docshell
   void SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
                                   const Maybe<nsISHEntry*>& aOSHE);
 
-  mozilla::dom::ChildSHistory* GetSessionHistory() {
-    return mBrowsingContext->GetChildSessionHistory();
-  }
-
   //
   // URI Load
   //
 
   // Actually open a channel and perform a URI load. Callers need to pass a
   // non-null aLoadState->TriggeringPrincipal() which initiated the URI load.
   // Please note that the TriggeringPrincipal will be used for performing
   // security checks. If aLoadState->URI() is provided by the web, then please
@@ -1128,21 +1123,16 @@ 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
@@ -1,16 +1,18 @@
 /* -*- 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 "nsDocShellLoadState.h"
 #include "nsIDocShell.h"
+#include "SHEntryParent.h"
+#include "SHEntryChild.h"
 #include "nsDocShell.h"
 #include "nsISHEntry.h"
 #include "nsIURIFixup.h"
 #include "nsIWebNavigation.h"
 #include "nsIChannel.h"
 #include "nsNetUtil.h"
 #include "nsQueryObject.h"
 #include "ReferrerInfo.h"
@@ -128,16 +130,24 @@ nsDocShellLoadState::nsDocShellLoadState
   mStoragePrincipalToInherit = aLoadState.StoragePrincipalToInherit();
   mCsp = aLoadState.Csp();
   mOriginalURIString = aLoadState.OriginalURIString();
   mCancelContentJSEpoch = aLoadState.CancelContentJSEpoch();
   mPostDataStream = aLoadState.PostDataStream();
   mHeadersStream = aLoadState.HeadersStream();
   mSrcdocData = aLoadState.SrcdocData();
   mLoadIdentifier = aLoadState.LoadIdentifier();
+  if (!aLoadState.SHEntry() || !StaticPrefs::fission_sessionHistoryInParent()) {
+    return;
+  }
+  if (XRE_IsParentProcess()) {
+    mSHEntry = static_cast<LegacySHEntry*>(aLoadState.SHEntry());
+  } else {
+    mSHEntry = static_cast<SHEntryChild*>(aLoadState.SHEntry());
+  }
 }
 
 nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther)
     : mReferrerInfo(aOther.mReferrerInfo),
       mURI(aOther.mURI),
       mOriginalURI(aOther.mOriginalURI),
       mResultPrincipalURI(aOther.mResultPrincipalURI),
       mResultPrincipalURIIsSome(aOther.mResultPrincipalURIIsSome),
@@ -483,30 +493,16 @@ 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;
@@ -876,10 +872,23 @@ DocShellLoadStateInit nsDocShellLoadStat
   loadState.OriginalURIString() = mOriginalURIString;
   loadState.CancelContentJSEpoch() = mCancelContentJSEpoch;
   loadState.ReferrerInfo() = mReferrerInfo;
   loadState.PostDataStream() = mPostDataStream;
   loadState.HeadersStream() = mHeadersStream;
   loadState.SrcdocData() = mSrcdocData;
   loadState.ResultPrincipalURI() = mResultPrincipalURI;
   loadState.LoadIdentifier() = mLoadIdentifier;
+  if (!mSHEntry || !StaticPrefs::fission_sessionHistoryInParent()) {
+    // Without the pref, we don't have an actor for shentry and thus
+    // we can't serialize it. We could write custom (de)serializers,
+    // but a session history rewrite is on the way anyway.
+    return loadState;
+  }
+  if (XRE_IsParentProcess()) {
+    loadState.SHEntry() = static_cast<CrossProcessSHEntry*>(
+        static_cast<LegacySHEntry*>(mSHEntry.get()));
+  } else {
+    loadState.SHEntry() = static_cast<CrossProcessSHEntry*>(
+        static_cast<SHEntryChild*>(mSHEntry.get()));
+  }
   return loadState;
 }
--- a/docshell/base/nsDocShellLoadState.h
+++ b/docshell/base/nsDocShellLoadState.h
@@ -3,23 +3,21 @@
 /* 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;
@@ -124,22 +122,16 @@ 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);
 
@@ -345,19 +337,16 @@ 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;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -982,16 +982,22 @@ interface nsIDocShell : nsIDocShellTreeI
   /**
    * Returns true if the current load is a forced reload,
    * e.g. started by holding shift whilst triggering reload.
    */
   readonly attribute bool isForceReloading;
 
   Array<float> getColorMatrix();
 
+  /**
+   * Initialize session history for this docshell. The docshell must be the root
+   * docshell.
+   */
+  void initSessionHistory();
+
 %{C++
   /**
    * These methods call nsDocShell::GetHTMLEditorInternal() and
    * nsDocShell::SetHTMLEditorInternal() with static_cast.
    */
   mozilla::HTMLEditor* GetHTMLEditor();
   nsresult SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor);
 %}
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -1,27 +1,32 @@
 /* -*- 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 "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/SHEntryChild.h"
 
 // session history
 #include "nsSHEntryShared.h"
 #include "nsSHistory.h"
 
 namespace mozilla {
 
 // The one time initialization for this module
 nsresult InitDocShellModule() {
   mozilla::dom::BrowsingContext::Init();
   nsresult rv = nsSHistory::Startup();
   NS_ENSURE_SUCCESS(rv, rv);
+  mozilla::dom::SHEntryChildShared::Init();
 
   return NS_OK;
 }
 
-void UnloadDocShellModule() { nsSHistory::Shutdown(); }
+void UnloadDocShellModule() {
+  nsSHistory::Shutdown();
+  nsSHEntryShared::Shutdown();
+}
 
 }  // namespace mozilla
--- a/docshell/shistory/ChildSHistory.cpp
+++ b/docshell/shistory/ChildSHistory.cpp
@@ -3,49 +3,46 @@
 /* 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 "mozilla/dom/ChildSHistory.h"
 #include "mozilla/dom/ChildSHistoryBinding.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
+#include "mozilla/dom/SHEntryChild.h"
+#include "mozilla/dom/SHistoryChild.h"
 #include "mozilla/StaticPrefs_fission.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSHEntry.h"
 #include "nsSHistory.h"
 #include "nsDocShell.h"
 #include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace dom {
 
-ChildSHistory::ChildSHistory(BrowsingContext* aBrowsingContext)
-    : mBrowsingContext(aBrowsingContext) {}
-
-void ChildSHistory::SetIsInProcess(bool aIsInProcess) {
-  if (!aIsInProcess) {
-    mHistory = nullptr;
-
-    return;
+static already_AddRefed<nsISHistory> CreateSHistory(nsDocShell* aDocShell) {
+  if (XRE_IsContentProcess() && StaticPrefs::fission_sessionHistoryInParent()) {
+    return do_AddRef(static_cast<SHistoryChild*>(
+        ContentChild::GetSingleton()->SendPSHistoryConstructor(
+            aDocShell->GetBrowsingContext())));
   }
 
-  if (mHistory) {
-    return;
-  }
-
-  mHistory = new nsSHistory(mBrowsingContext);
+  nsCOMPtr<nsISHistory> history =
+      new nsSHistory(aDocShell->GetBrowsingContext(), aDocShell->HistoryID());
+  return history.forget();
 }
 
-int32_t ChildSHistory::Count() {
-  if (StaticPrefs::fission_sessionHistoryInParent()) {
-    return mLength;
-  }
-  return mHistory->GetCount();
-}
+ChildSHistory::ChildSHistory(nsDocShell* aDocShell)
+    : mDocShell(aDocShell), mHistory(CreateSHistory(aDocShell)) {}
+
+ChildSHistory::~ChildSHistory() {}
+
+int32_t ChildSHistory::Count() { return mHistory->GetCount(); }
 
 int32_t ChildSHistory::Index() {
   int32_t index;
   mHistory->GetIndex(&index);
   return index;
 }
 
 void ChildSHistory::Reload(uint32_t aReloadFlags, ErrorResult& aRv) {
@@ -63,29 +60,17 @@ bool ChildSHistory::CanGo(int32_t aOffse
 
 void ChildSHistory::Go(int32_t aOffset, ErrorResult& aRv) {
   CheckedInt<int32_t> index = Index();
   index += aOffset;
   if (!index.isValid()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
-  if (StaticPrefs::fission_sessionHistoryInParent()) {
-    nsCOMPtr<nsISHistory> shistory = mHistory;
-    ContentChild::GetSingleton()->SendHistoryGo(
-        mBrowsingContext, index.value(),
-        [shistory](int32_t&& aRequestedIndex) {
-          // FIXME Should probably only do this for non-fission.
-          shistory->InternalSetRequestedIndex(aRequestedIndex);
-        },
-        [](mozilla::ipc::
-               ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
-  } else {
-    aRv = mHistory->GotoIndex(index.value());
-  }
+  aRv = mHistory->GotoIndex(index.value());
 }
 
 void ChildSHistory::AsyncGo(int32_t aOffset) {
   if (!CanGo(aOffset)) {
     return;
   }
 
   RefPtr<PendingAsyncHistoryNavigation> asyncNav =
@@ -97,34 +82,50 @@ void ChildSHistory::AsyncGo(int32_t aOff
 void ChildSHistory::RemovePendingHistoryNavigations() {
   mPendingNavigations.clear();
 }
 
 void ChildSHistory::EvictLocalContentViewers() {
   mHistory->EvictAllContentViewers();
 }
 
-nsISHistory* ChildSHistory::LegacySHistory() {
-  MOZ_RELEASE_ASSERT(mHistory);
-  return mHistory;
-}
+nsISHistory* ChildSHistory::LegacySHistory() { return mHistory; }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChildSHistory)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ChildSHistory)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ChildSHistory)
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ChildSHistory, mBrowsingContext, mHistory)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ChildSHistory, mDocShell, mHistory)
 
 JSObject* ChildSHistory::WrapObject(JSContext* cx,
                                     JS::Handle<JSObject*> aGivenProto) {
   return ChildSHistory_Binding::Wrap(cx, this, aGivenProto);
 }
 
 nsISupports* ChildSHistory::GetParentObject() const {
-  return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+  // We want to get the BrowserChildMessageManager, which is the
+  // messageManager on mDocShell.
+  RefPtr<ContentFrameMessageManager> mm;
+  if (mDocShell) {
+    mm = mDocShell->GetMessageManager();
+  }
+  // else we must be unlinked... can that happen here?
+  return ToSupports(mm);
+}
+
+already_AddRefed<nsISHEntry> CreateSHEntryForDocShell(nsISHistory* aSHistory) {
+  uint64_t sharedID = SHEntryChildShared::CreateSharedID();
+  if (XRE_IsContentProcess() && StaticPrefs::fission_sessionHistoryInParent()) {
+    return do_AddRef(static_cast<SHEntryChild*>(
+        ContentChild::GetSingleton()->SendPSHEntryConstructor(
+            static_cast<SHistoryChild*>(aSHistory), sharedID)));
+  }
+
+  nsCOMPtr<nsISHEntry> entry = new nsLegacySHEntry(aSHistory, sharedID);
+  return entry.forget();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/docshell/shistory/ChildSHistory.h
+++ b/docshell/shistory/ChildSHistory.h
@@ -20,39 +20,35 @@
 #define mozilla_dom_ChildSHistory_h
 
 #include "nsCOMPtr.h"
 #include "mozilla/ErrorResult.h"
 #include "nsWrapperCache.h"
 #include "nsThreadUtils.h"
 #include "mozilla/LinkedList.h"
 
+class nsSHistory;
+class nsDocShell;
 class nsISHEntry;
 class nsISHistory;
+class nsIWebNavigation;
+class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
-class BrowsingContext;
-
 class ChildSHistory : public nsISupports, public nsWrapperCache {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ChildSHistory)
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* cx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
-  explicit ChildSHistory(BrowsingContext* aBrowsingContext);
-
-  // Create or destroy the session history implementation in the child process.
-  // This can be removed once session history is stored exclusively in the
-  // parent process.
-  void SetIsInProcess(bool aIsInProcess);
-  bool IsInProcess() { return !!mHistory; }
+  explicit ChildSHistory(nsDocShell* aDocShell);
 
   int32_t Count();
   int32_t Index();
 
   /**
    * Reload the current entry in the session history.
    */
   void Reload(uint32_t aReloadFlags, ErrorResult& aRv);
@@ -70,20 +66,18 @@ class ChildSHistory : public nsISupports
 
   /**
    * Evicts all content viewers within the current process.
    */
   void EvictLocalContentViewers();
 
   nsISHistory* LegacySHistory();
 
-  void SetLength(uint32_t aLength) { mLength = aLength; }
-
  private:
-  virtual ~ChildSHistory() = default;
+  virtual ~ChildSHistory();
 
   class PendingAsyncHistoryNavigation
       : public Runnable,
         public mozilla::LinkedListElement<PendingAsyncHistoryNavigation> {
    public:
     PendingAsyncHistoryNavigation(ChildSHistory* aHistory, int32_t aOffset)
         : Runnable("PendingAsyncHistoryNavigation"),
           mHistory(aHistory),
@@ -97,18 +91,19 @@ class ChildSHistory : public nsISupports
       return NS_OK;
     }
 
    private:
     RefPtr<ChildSHistory> mHistory;
     int32_t mOffset;
   };
 
-  RefPtr<BrowsingContext> mBrowsingContext;
+  RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsISHistory> mHistory;
   mozilla::LinkedList<PendingAsyncHistoryNavigation> mPendingNavigations;
-  uint32_t mLength = 0;
 };
 
+already_AddRefed<nsISHEntry> CreateSHEntryForDocShell(nsISHistory* aSHistory);
+
 }  // namespace dom
 }  // namespace mozilla
 
 #endif /* mozilla_dom_ChildSHistory_h */
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/MaybeNewPSHEntry.cpp
@@ -0,0 +1,101 @@
+/* -*- 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 "mozilla/dom/MaybeNewPSHEntry.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PContentParent.h"
+#include "mozilla/dom/PSHistoryParent.h"
+#include "mozilla/dom/SHEntryChild.h"
+#include "mozilla/dom/SHEntryParent.h"
+#include "mozilla/dom/SHistoryChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<dom::NewPSHEntry> {
+  static void Write(IPC::Message* aMsg, IProtocol* aActor,
+                    dom::NewPSHEntry&& aEntry) {
+    MOZ_RELEASE_ASSERT(aActor->GetSide() == ParentSide, "wrong side!");
+
+    WriteIPDLParam(aMsg, aActor, std::move(aEntry.mEndpoint));
+    WriteIPDLParam(aMsg, aActor, aEntry.mSHistoryParent);
+    WriteIPDLParam(aMsg, aActor, aEntry.mSharedID);
+  }
+
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, dom::NewPSHEntry* aEntry) {
+    MOZ_RELEASE_ASSERT(aActor->GetSide() == ChildSide, "wrong side!");
+
+    return ReadIPDLParam(aMsg, aIter, aActor, &aEntry->mEndpoint) &&
+           ReadIPDLParam(aMsg, aIter, aActor, &aEntry->mSHistoryChild) &&
+           ReadIPDLParam(aMsg, aIter, aActor, &aEntry->mSharedID);
+  }
+};
+
+/* static */
+void IPDLParamTraits<dom::CrossProcessSHEntry*>::Write(
+    IPC::Message* aMsg, IProtocol* aActor, dom::CrossProcessSHEntry* aEntry) {
+  MOZ_DIAGNOSTIC_ASSERT(aActor->ToplevelProtocol()->GetProtocolId() ==
+                        PContentMsgStart);
+  if (aActor->GetSide() == ChildSide) {
+    WriteIPDLParam(aMsg, aActor,
+                   static_cast<dom::PSHEntryChild*>(
+                       static_cast<dom::SHEntryChild*>(aEntry)));
+
+    return;
+  }
+
+  dom::MaybeNewPSHEntryParent entry(static_cast<dom::PSHEntryParent*>(nullptr));
+  if (aEntry) {
+    entry = static_cast<dom::LegacySHEntry*>(aEntry)->GetOrCreateActor(
+        static_cast<dom::PContentParent*>(aActor->ToplevelProtocol()));
+  }
+
+  WriteIPDLParam(aMsg, aActor, std::move(entry));
+}
+
+/* static */
+bool IPDLParamTraits<dom::CrossProcessSHEntry*>::Read(
+    const IPC::Message* aMsg, PickleIterator* aIter,
+    mozilla::ipc::IProtocol* aActor, RefPtr<dom::CrossProcessSHEntry>* aEntry) {
+  if (aActor->GetSide() == ParentSide) {
+    dom::PSHEntryParent* actor;
+    if (!ReadIPDLParam(aMsg, aIter, aActor, &actor)) {
+      aActor->FatalError("Error deserializing MaybeNewPSHEntry");
+      return false;
+    }
+
+    *aEntry =
+        actor ? static_cast<dom::SHEntryParent*>(actor)->GetSHEntry() : nullptr;
+
+    return true;
+  }
+  dom::MaybeNewPSHEntryChild actor(static_cast<dom::PSHEntryChild*>(nullptr));
+  if (!ReadIPDLParam(aMsg, aIter, aActor, &actor)) {
+    aActor->FatalError("Error deserializing MaybeNewPSHEntry");
+    return false;
+  }
+
+  return actor.match(
+      [&](dom::PSHEntryChild*& entry) {
+        *aEntry = static_cast<dom::SHEntryChild*>(entry);
+        return true;
+      },
+      [&](dom::NewPSHEntry& newEntry) {
+        RefPtr<dom::SHEntryChild> entry = new dom::SHEntryChild(
+            static_cast<dom::SHistoryChild*>(newEntry.mSHistoryChild),
+            newEntry.mSharedID);
+        dom::ContentChild::GetSingleton()->BindPSHEntryEndpoint(
+            std::move(newEntry.mEndpoint), do_AddRef(entry).take());
+        *aEntry = std::move(entry);
+        return true;
+      });
+}
+
+}  // namespace ipc
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/MaybeNewPSHEntry.h
@@ -0,0 +1,62 @@
+/* -*- 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_MaybeNewPSHEntry_h
+#define mozilla_dom_MaybeNewPSHEntry_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Variant.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+class LegacySHEntry;
+class PSHistoryChild;
+class PSHistoryParent;
+class PSHEntryChild;
+class PSHEntryParent;
+class PSHistoryChild;
+class PSHistoryParent;
+class SHEntryChild;
+
+struct NewPSHEntry final {
+  mozilla::ipc::ManagedEndpoint<PSHEntryChild> mEndpoint;
+  PSHistoryParent* mSHistoryParent;
+  PSHistoryChild* mSHistoryChild;
+  uint64_t mSharedID;
+};
+
+typedef Variant<PSHEntryParent*, NewPSHEntry> MaybeNewPSHEntryParent;
+typedef Variant<PSHEntryChild*, NewPSHEntry> MaybeNewPSHEntryChild;
+
+// Any IPDL protocol trying to pass this (as argument or return value) needs to
+// be managed by PContent.
+class CrossProcessSHEntry {
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  SHEntryChild* ToSHEntryChild();
+};
+
+}  // namespace dom
+
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<dom::CrossProcessSHEntry*> {
+  static void Write(IPC::Message* aMsg, IProtocol* aActor,
+                    dom::CrossProcessSHEntry* aEntry);
+
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, RefPtr<dom::CrossProcessSHEntry>* aEntry);
+};
+
+}  // namespace ipc
+}  // namespace mozilla
+
+#endif /* mozilla_dom_MaybeNewPSHEntry_h */
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/PSHEntry.ipdl
@@ -0,0 +1,123 @@
+/* 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 protocol PContent;
+include protocol PSHistory;
+
+include DOMTypes;
+include PresState;
+
+using refcounted class mozilla::dom::CrossProcessSHEntry from "mozilla/dom/MaybeNewPSHEntry.h";
+using refcounted class nsDocShellLoadState from "mozilla/dom/DocShellMessageUtils.h";
+using struct nsID from "nsID.h";
+using nsIntRect from "nsRect.h";
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PSHEntry {
+  // IPDLParamTraits<dom::CrossProcessSHEntry*>::Write relies on PContent being
+  // the manager.
+  manager PContent;
+
+parent:
+  sync GetURI() returns (nsIURI uri);
+  async SetURI(nsIURI uri);
+  sync GetOriginalURI() returns (nsIURI originalUri);
+  async SetOriginalURI(nsIURI originalUri);
+  sync GetResultPrincipalURI() returns (nsIURI resultPrincipalUri);
+  async SetResultPrincipalURI(nsIURI resultPrincipalUri);
+  sync GetLoadReplace() returns (bool loadReplace);
+  async SetLoadReplace(bool loadReplace);
+  sync GetTitle() returns (nsString title);
+  async SetTitle(nsString title);
+  sync GetIsSubFrame() returns (bool isSubFrame);
+  async SetIsSubFrame(bool isSubFrame);
+  sync GetReferrerInfo() returns (nsIReferrerInfo referrerInfo);
+  async SetReferrerInfo(nsIReferrerInfo referrerInfo);
+  sync GetSticky() returns (bool sticky);
+  async SetSticky(bool sticky);
+  sync GetPostData() returns (nsIInputStream postData);
+  async SetPostData(nsIInputStream postData);
+  sync GetParent() returns (CrossProcessSHEntry parentEntry);
+  async SetParent(nullable PSHEntry parentEntry);
+  sync GetLoadType() returns (uint32_t loadType);
+  async SetLoadType(uint32_t loadType);
+  sync GetID() returns (uint32_t id);
+  async SetID(uint32_t id);
+  sync GetCacheKey() returns (uint32_t cacheKey);
+  async SetCacheKey(uint32_t cacheKey);
+  sync GetExpirationStatus() returns (bool expirationStatus);
+  async SetExpirationStatus(bool expirationStatus);
+  sync GetContentType() returns (nsCString contentType);
+  async SetContentType(nsCString contentType);
+  sync GetURIWasModified() returns (bool uriWasModified);
+  async SetURIWasModified(bool uriWasModified);
+  sync GetTriggeringPrincipal() returns (nsIPrincipal triggeringPrincipal);
+  async SetTriggeringPrincipal(nsIPrincipal triggeringPrincipal);
+  sync GetPrincipalToInherit() returns (nsIPrincipal principalToInherit);
+  async SetPrincipalToInherit(nsIPrincipal principalToInherit);
+  sync GetStoragePrincipalToInherit()
+    returns (nsIPrincipal storagePrincipalToInherit);
+  async SetStoragePrincipalToInherit(nsIPrincipal storagePrincipalToInherit);
+  sync GetCsp() returns (nsIContentSecurityPolicy csp);
+  async SetCsp(nsIContentSecurityPolicy csp);
+  sync GetStateData() returns (ClonedMessageData stateData);
+  async SetStateData(ClonedMessageData stateData);
+  sync GetDocshellID() returns (nsID docshellId);
+  async SetDocshellID(nsID docshellId);
+  sync GetIsSrcdocEntry() returns (bool isSrcdocEntry);
+  sync GetSrcdocData() returns (nsString srcdocData);
+  async SetSrcdocData(nsString srcdocData);
+  sync GetBaseURI() returns (nsIURI baseUri);
+  async SetBaseURI(nsIURI baseUri);
+  sync GetScrollRestorationIsManual() returns (bool scrollRestorationIsManual);
+  async SetScrollRestorationIsManual(bool scrollRestorationIsManual);
+  sync GetLoadedInThisProcess() returns (bool loadedInThisProcess);
+  sync GetLastTouched() returns (uint32_t lastTouched);
+  async SetLastTouched(uint32_t lastTouched);
+  sync GetChildCount() returns (int32_t childCount);
+  sync GetPersist() returns (bool persist);
+  async SetPersist(bool persist);
+  async SetScrollPosition(int32_t x, int32_t y);
+  sync GetScrollPosition() returns (int32_t x, int32_t y);
+  sync GetViewerBounds() returns (nsIntRect bounds);
+  async SetViewerBounds(nsIntRect bounds);
+  async Create(nsIURI URI, nsString title, nsIInputStream inputStream,
+               uint32_t cacheKey, nsCString contentType,
+               nsIPrincipal triggeringPrincipal, nsIPrincipal principalToInherit,
+               nsIPrincipal storagePrincipalToInherit,
+               nsIContentSecurityPolicy csp, nsID docshellID,
+               bool dynamicCreation, nsIURI originalUri, nsIURI resultPrincipalURI,
+               bool loadReplace, nsIReferrerInfo referrerInfo,
+               nsString srcdoc, bool srcdocEntry, nsIURI baseURI,
+               bool saveLayoutState, bool expired);
+  sync HasDetachedEditor() returns (bool hasDetachedEditor);
+  sync IsDynamicallyAdded() returns (bool isDynamicallyAdded);
+  sync HasDynamicallyAddedChild() returns (bool hasDynamicallyAddedChild);
+  sync AdoptBFCacheEntry(PSHEntry entry) returns (nsresult result);
+  async AbandonBFCacheEntry(uint64_t aNewSharedID);
+  sync SharesDocumentWith(PSHEntry entry) returns (bool sharesDocumentWith,
+                                                   nsresult result);
+  async SetLoadTypeAsHistory();
+  sync AddChild(nullable PSHEntry childEntry, int32_t offset, bool useRemoteSubframes) returns (nsresult result);
+  sync RemoveChild(PSHEntry childEntry) returns (nsresult result);
+  sync GetChildAt(int32_t index) returns (CrossProcessSHEntry childEntry);
+  sync GetChildSHEntryIfHasNoDynamicallyAddedChild(int32_t childOffset) returns (CrossProcessSHEntry childEntry);
+  sync ReplaceChild(PSHEntry newChildEntry) returns (nsresult result);
+  async ClearEntry(uint64_t aNewSharedID);
+  sync CreateLoadInfo() returns (nsDocShellLoadState loadState);
+  sync Clone() returns (CrossProcessSHEntry result);
+  sync SyncTreesForSubframeNavigation(nullable PSHEntry entry, MaybeDiscardedBrowsingContext BC,
+                                     MaybeDiscardedBrowsingContext ignoreBC)
+                        returns (SwapEntriesDocshellData[] entriesToUpdate);
+
+  async UpdateLayoutHistoryState(bool scrollPositionOnly,
+                                 nsCString[] keys, PresState[] states);
+
+  sync __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/PSHistory.ipdl
@@ -0,0 +1,88 @@
+/* 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 protocol PContent;
+include protocol PSHEntry;
+
+include DOMTypes;
+
+using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
+using refcounted class mozilla::dom::CrossProcessSHEntry from "mozilla/dom/MaybeNewPSHEntry.h";
+using refcounted class nsDocShellLoadState from "mozilla/dom/DocShellMessageUtils.h";
+
+using struct nsID from "nsID.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+struct LoadSHEntryData
+{
+  CrossProcessSHEntry shEntry;
+  MaybeDiscardedBrowsingContext browsingContext;
+  nsDocShellLoadState loadState;
+};
+
+union LoadSHEntryResult {
+  nsresult;
+  LoadSHEntryData[];
+};
+
+sync protocol PSHistory {
+  // IPDLParamTraits<dom::CrossProcessSHEntry*>::Write relies on PContent being
+  // the manager.
+  manager PContent;
+
+parent:
+  sync GetCount() returns (int32_t count);
+  sync GetIndex() returns (int32_t index);
+  sync SetIndex(int32_t index) returns (nsresult result);
+  sync GetRequestedIndex() returns (int32_t index);
+  async InternalSetRequestedIndex(int32_t index);
+  sync GetEntryAtIndex(int32_t index) returns (nsresult result, CrossProcessSHEntry entry);
+  sync PurgeHistory(int32_t numEntries) returns (nsresult result);
+  sync ReloadCurrentEntry() returns (LoadSHEntryResult load);
+  sync GotoIndex(int32_t index) returns (LoadSHEntryResult load);
+  sync GetIndexOfEntry(PSHEntry entry) returns (int32_t index);
+  sync AddEntry(PSHEntry entry, bool persist) returns (nsresult result, int32_t entriesPurged);
+  async UpdateIndex();
+  sync ReplaceEntry(int32_t index, PSHEntry entry) returns (nsresult result);
+  sync NotifyOnHistoryReload() returns (bool ok);
+  async EvictOutOfRangeContentViewers(int32_t index);
+  async EvictAllContentViewers();
+  async RemoveDynEntries(int32_t index, PSHEntry entry);
+  sync RemoveEntries(nsID[] ids, int32_t index) returns (bool didRemove);
+  async RemoveFrameEntries(PSHEntry entry);
+  sync Reload(uint32_t reloadFlags) returns (LoadSHEntryResult load);
+  sync GetAllEntries() returns (CrossProcessSHEntry[] entries);
+  sync FindEntryForBFCache(uint64_t sharedID, bool includeCurrentEntry)
+    returns (CrossProcessSHEntry entries, int32_t startIndex);
+  async Evict(PSHEntry[] entry);
+  async EnsureCorrectEntryAtCurrIndex(PSHEntry entry);
+  async EvictContentViewersOrReplaceEntry(nullable PSHEntry newSHEntry, bool replace);
+  async NotifyListenersContentViewerEvicted(uint32_t numEvicted);
+  // See below for some explanation
+  sync AddToRootSessionHistory(bool cloneChildren, nullable PSHEntry OSHE, MaybeDiscardedBrowsingContext BC, PSHEntry entry,
+                                uint32_t loadType, bool shouldPersist) returns (int32_t? previousEntryIndex, int32_t? loadedEntryIndex,
+                                SwapEntriesDocshellData[] entriesToUpdate, int32_t entriesPurged, nsresult result);
+  // In CloneAndReplaceChild and SetChildHistoryEntry methods (which get called within this
+  // and above function) there are calls to SwapHistoryEntries to update entries in the docshell.
+  // We don't have access to the docshell in the parent
+  // process so we have to either wait to update the docshell or update it via an IPC call.
+  // We pass in the ChildID of the process who invoked the IPC to make decisions
+  // about whether we should update entries in the docshell via an IPC call -
+  // if the caller is a process different from where the docshell lives.
+  // Else, we return entries that we need to update in entriesToUpdate array
+  // and update the docshell with such entries in the child process.
+  sync AddChildSHEntryHelper(PSHEntry cloneRef, PSHEntry newEntry, MaybeDiscardedBrowsingContext BC,
+                             bool cloneChildren) returns (SwapEntriesDocshellData[] entriesToUpdate,
+                             int32_t entriesPurged, CrossProcessSHEntry entry, nsresult result);
+  async __delete__();
+
+child:
+  async ReloadCurrentEntryFromChild();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHEntryChild.cpp
@@ -0,0 +1,1054 @@
+/* -*- 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 "SHEntryChild.h"
+#include "SHistoryChild.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/MaybeNewPSHEntry.h"
+#include "mozilla/StaticPrefs_docshell.h"
+#include "nsDocShell.h"
+#include "nsDocShellEditorData.h"
+#include "nsDocShellLoadState.h"
+#include "nsIContentViewer.h"
+#include "nsILayoutHistoryState.h"
+#include "nsIMutableArray.h"
+#include "nsStructuredCloneContainer.h"
+
+namespace mozilla {
+namespace dom {
+
+StaticAutoPtr<nsRefPtrHashtable<nsUint64HashKey, SHEntryChildShared>>
+    sSHEntryChildSharedTable;
+
+uint64_t SHEntryChildShared::sNextSharedID = 0;
+
+/* static */
+void SHEntryChildShared::Init() {
+  sSHEntryChildSharedTable =
+      new nsRefPtrHashtable<nsUint64HashKey, SHEntryChildShared>();
+  ClearOnShutdown(&sSHEntryChildSharedTable);
+}
+
+/* static */
+SHEntryChildShared* SHEntryChildShared::GetOrCreate(SHistoryChild* aSHistory,
+                                                    uint64_t aSharedID) {
+  MOZ_DIAGNOSTIC_ASSERT(
+      sSHEntryChildSharedTable,
+      "SHEntryChildShared::Init should have created the table.");
+  RefPtr<SHEntryChildShared>& shared =
+      sSHEntryChildSharedTable->GetOrInsert(aSharedID);
+  if (!shared) {
+    shared = new SHEntryChildShared(aSHistory, aSharedID);
+  }
+  return shared;
+}
+
+void SHEntryChildShared::Remove(uint64_t aSharedID) {
+  if (sSHEntryChildSharedTable) {
+    sSHEntryChildSharedTable->Remove(aSharedID);
+  }
+}
+
+void SHEntryChildShared::EvictContentViewers(
+    const nsTArray<uint64_t>& aToEvictSharedStateIDs) {
+  MOZ_ASSERT(sSHEntryChildSharedTable,
+             "we have content viewers to evict, but the table hasn't been "
+             "initialized yet");
+  uint32_t numEvictedSoFar = 0;
+  for (auto iter = aToEvictSharedStateIDs.begin();
+       iter != aToEvictSharedStateIDs.end(); ++iter) {
+    RefPtr<SHEntryChildShared> shared = sSHEntryChildSharedTable->Get(*iter);
+    if (!shared) {
+      // This can happen if we've created an entry in the parent, and have never
+      // sent it over IPC to the child.
+      continue;
+    }
+    nsCOMPtr<nsIContentViewer> viewer = shared->mContentViewer;
+    if (viewer) {
+      numEvictedSoFar++;
+      shared->SetContentViewer(nullptr);
+      shared->SyncPresentationState();
+      viewer->Destroy();
+    }
+    if (std::next(iter) == aToEvictSharedStateIDs.end() &&
+        numEvictedSoFar > 0) {
+      // This is the last shared object, so we should notify our
+      // listeners about any content viewers that were evicted.
+      // It does not matter which shared entry we will use for notifying.
+      shared->NotifyListenersContentViewerEvicted(numEvictedSoFar);
+    }
+  }
+}
+
+SHEntryChildShared::SHEntryChildShared(SHistoryChild* aSHistory, uint64_t aID)
+    : mID(aID), mSHistory(aSHistory) {}
+
+SHEntryChildShared::~SHEntryChildShared() {
+  // The destruction can be caused by either the entry is removed from session
+  // history and no one holds the reference, or the whole session history is on
+  // destruction. We want to ensure that we invoke
+  // shistory->RemoveFromExpirationTracker for the former case.
+  RemoveFromExpirationTracker();
+
+  // Calling RemoveDynEntriesForBFCacheEntry on destruction is unnecessary since
+  // there couldn't be any SHEntry holding this shared entry, and we noticed
+  // that calling RemoveDynEntriesForBFCacheEntry in the middle of
+  // nsSHistory::Release can cause a crash, so set mSHistory to null explicitly
+  // before RemoveFromBFCacheSync.
+  mSHistory = nullptr;
+  if (mContentViewer) {
+    RemoveFromBFCacheSync();
+  }
+}
+
+NS_IMPL_ISUPPORTS(SHEntryChildShared, nsIBFCacheEntry, nsIMutationObserver)
+
+already_AddRefed<SHEntryChildShared> SHEntryChildShared::Duplicate() {
+  RefPtr<SHEntryChildShared> newEntry = new SHEntryChildShared(
+      mSHistory, mozilla::dom::SHEntryChildShared::CreateSharedID());
+  newEntry->CopyFrom(this);
+  return newEntry.forget();
+}
+
+void SHEntryChildShared::RemoveFromExpirationTracker() {
+  if (mSHistory && GetExpirationState()->IsTracked()) {
+    mSHistory->RemoveFromExpirationTracker(this);
+  }
+}
+
+void SHEntryChildShared::SyncPresentationState() {
+  if (mContentViewer && mWindowState) {
+    // If we have a content viewer and a window state, we should be ok.
+    return;
+  }
+
+  DropPresentationState();
+}
+
+void SHEntryChildShared::DropPresentationState() {
+  RefPtr<SHEntryChildShared> kungFuDeathGrip = this;
+
+  if (mDocument) {
+    mDocument->SetBFCacheEntry(nullptr);
+    mDocument->RemoveMutationObserver(this);
+    mDocument = nullptr;
+  }
+  if (mContentViewer) {
+    mContentViewer->ClearHistoryEntry();
+  }
+
+  RemoveFromExpirationTracker();
+  mContentViewer = nullptr;
+  // FIXME Bug 1547735
+  // mSticky = true;
+  mWindowState = nullptr;
+  // FIXME Bug 1547735
+  // mViewerBounds.SetRect(0, 0, 0, 0);
+  mChildShells.Clear();
+  mRefreshURIList = nullptr;
+  mEditorData = nullptr;
+}
+
+void SHEntryChildShared::NotifyListenersContentViewerEvicted(
+    uint32_t aNumEvicted) {
+  if (StaticPrefs::docshell_shistory_testing_bfevict() && mSHistory) {
+    mSHistory->SendNotifyListenersContentViewerEvicted(aNumEvicted);
+  }
+}
+
+nsresult SHEntryChildShared::SetContentViewer(nsIContentViewer* aViewer) {
+  MOZ_ASSERT(!aViewer || !mContentViewer,
+             "SHEntryShared already contains viewer");
+
+  if (mContentViewer || !aViewer) {
+    DropPresentationState();
+  }
+
+  // If we're setting mContentViewer to null, state should already be cleared
+  // in the DropPresentationState() call above; If we're setting it to a
+  // non-null content viewer, the entry shouldn't have been tracked either.
+  MOZ_ASSERT(!GetExpirationState()->IsTracked());
+  mContentViewer = aViewer;
+
+  if (mContentViewer) {
+    // mSHistory is only set for root entries, but in general bfcache only
+    // applies to root entries as well. BFCache for subframe navigation has been
+    // disabled since 2005 in bug 304860.
+    if (mSHistory) {
+      mSHistory->AddToExpirationTracker(this);
+    }
+
+    // Store observed document in strong pointer in case it is removed from
+    // the contentviewer
+    mDocument = mContentViewer->GetDocument();
+    if (mDocument) {
+      mDocument->SetBFCacheEntry(this);
+      mDocument->AddMutationObserver(this);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP SHEntryChildShared::RemoveFromBFCacheSync() {
+  MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
+
+  // The call to DropPresentationState could drop the last reference, so hold
+  // |this| until RemoveDynEntriesForBFCacheEntry finishes.
+  RefPtr<SHEntryChildShared> kungFuDeathGrip = this;
+
+  // DropPresentationState would clear mContentViewer.
+  nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
+  DropPresentationState();
+
+  if (viewer) {
+    viewer->Destroy();
+  }
+
+  // Now that we've dropped the viewer, we have to clear associated dynamic
+  // subframe entries.
+  if (mSHistory) {
+    mSHistory->RemoveDynEntriesForBFCacheEntry(this);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP SHEntryChildShared::RemoveFromBFCacheAsync() {
+  MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
+
+  // Check it again to play safe in release builds.
+  if (!mDocument) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // DropPresentationState would clear mContentViewer & mDocument. Capture and
+  // release the references asynchronously so that the document doesn't get
+  // nuked mid-mutation.
+  nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
+  RefPtr<dom::Document> _document = mDocument;
+  RefPtr<SHEntryChildShared> self = this;
+  nsresult rv = mDocument->Dispatch(
+      mozilla::TaskCategory::Other,
+      NS_NewRunnableFunction(
+          "SHEntryChildShared::RemoveFromBFCacheAsync",
+          // _document isn't used in the closure, but just keeps mDocument
+          // alive until the closure runs.
+          [self, viewer, _document]() {
+            if (viewer) {
+              viewer->Destroy();
+            }
+
+            if (self->mSHistory) {
+              self->mSHistory->RemoveDynEntriesForBFCacheEntry(self);
+            }
+          }));
+
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch RemoveFromBFCacheAsync runnable.");
+  } else {
+    // Drop presentation. Only do this if we succeeded in posting the event
+    // since otherwise the document could be torn down mid-mutation, causing
+    // crashes.
+    DropPresentationState();
+  }
+
+  return NS_OK;
+}
+
+void SHEntryChildShared::CharacterDataChanged(nsIContent* aContent,
+                                              const CharacterDataChangeInfo&) {
+  RemoveFromBFCacheAsync();
+}
+
+void SHEntryChildShared::AttributeChanged(dom::Element* aElement,
+                                          int32_t aNameSpaceID,
+                                          nsAtom* aAttribute, int32_t aModType,
+                                          const nsAttrValue* aOldValue) {
+  RemoveFromBFCacheAsync();
+}
+
+void SHEntryChildShared::ContentAppended(nsIContent* aFirstNewContent) {
+  RemoveFromBFCacheAsync();
+}
+
+void SHEntryChildShared::ContentInserted(nsIContent* aChild) {
+  RemoveFromBFCacheAsync();
+}
+
+void SHEntryChildShared::ContentRemoved(nsIContent* aChild,
+                                        nsIContent* aPreviousSibling) {
+  RemoveFromBFCacheAsync();
+}
+
+NS_IMPL_ADDREF(SHEntryChild)
+NS_IMETHODIMP_(MozExternalRefCountType) SHEntryChild::Release() {
+  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+  NS_ASSERT_OWNINGTHREAD(SHEntryChild);
+  nsrefcnt count = --mRefCnt;
+  NS_LOG_RELEASE(this, count, "SHEntryChild");
+  if (count == 0) {
+    mRefCnt = 1; /* stabilize */
+    delete this;
+    return 0;
+  }
+  if (count == 1 && !mIPCActorDeleted) {
+    Send__delete__(this);
+  }
+  return count;
+}
+NS_IMPL_QUERY_INTERFACE(SHEntryChild, nsISHEntry)
+
+NS_IMETHODIMP
+SHEntryChild::SetScrollPosition(int32_t aX, int32_t aY) {
+  return SendSetScrollPosition(aX, aY) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetScrollPosition(int32_t* aX, int32_t* aY) {
+  return SendGetScrollPosition(aX, aY) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetURIWasModified(bool* aURIWasModified) {
+  return SendGetURIWasModified(aURIWasModified) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetURIWasModified(bool aURIWasModified) {
+  return SendSetURIWasModified(aURIWasModified) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetURI(nsIURI** aURI) {
+  RefPtr<nsIURI> uri;
+  if (!SendGetURI(&uri)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uri.forget(aURI);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetURI(nsIURI* aURI) {
+  return SendSetURI(aURI) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetOriginalURI(nsIURI** aOriginalURI) {
+  RefPtr<nsIURI> originalURI;
+  if (!SendGetOriginalURI(&originalURI)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  originalURI.forget(aOriginalURI);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetOriginalURI(nsIURI* aOriginalURI) {
+  return SendSetOriginalURI(aOriginalURI) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetResultPrincipalURI(nsIURI** aResultPrincipalURI) {
+  RefPtr<nsIURI> resultPrincipalURI;
+  if (!SendGetResultPrincipalURI(&resultPrincipalURI)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  resultPrincipalURI.forget(aResultPrincipalURI);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetResultPrincipalURI(nsIURI* aResultPrincipalURI) {
+  return SendSetResultPrincipalURI(aResultPrincipalURI) ? NS_OK
+                                                        : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetLoadReplace(bool* aLoadReplace) {
+  return SendGetLoadReplace(aLoadReplace) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetLoadReplace(bool aLoadReplace) {
+  return SendSetLoadReplace(aLoadReplace) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
+  RefPtr<nsIReferrerInfo> referrerInfo;
+  if (SendGetReferrerInfo(&referrerInfo)) {
+    referrerInfo.forget(aReferrerInfo);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  return SendSetReferrerInfo(aReferrerInfo) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetContentViewer(nsIContentViewer* aViewer) {
+  return mShared->SetContentViewer(aViewer);
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetContentViewer(nsIContentViewer** aResult) {
+  NS_IF_ADDREF(*aResult = mShared->mContentViewer);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetSticky(bool aSticky) {
+  return SendSetSticky(aSticky) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetSticky(bool* aSticky) {
+  return SendGetSticky(aSticky) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetTitle(nsAString& aTitle) {
+  nsString title;
+  if (!SendGetTitle(&title)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aTitle = std::move(title);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetTitle(const nsAString& aTitle) {
+  return SendSetTitle(nsString(aTitle)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetPostData(nsIInputStream** aPostData) {
+  RefPtr<nsIInputStream> postData;
+  if (!SendGetPostData(&postData)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  postData.forget(aPostData);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetPostData(nsIInputStream* aPostData) {
+  return SendSetPostData(aPostData) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetLayoutHistoryState(nsILayoutHistoryState** aResult) {
+  NS_IF_ADDREF(*aResult = mShared->mLayoutHistoryState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetLayoutHistoryState(nsILayoutHistoryState* aState) {
+  mShared->mLayoutHistoryState = aState;
+  if (mShared->mLayoutHistoryState) {
+    mShared->mLayoutHistoryState->SetScrollPositionOnly(
+        !mShared->mSaveLayoutState);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::InitLayoutHistoryState(nsILayoutHistoryState** aState) {
+  nsCOMPtr<nsILayoutHistoryState> historyState;
+  if (mShared->mLayoutHistoryState) {
+    historyState = mShared->mLayoutHistoryState;
+  } else {
+    historyState = NS_NewLayoutHistoryState();
+    SetLayoutHistoryState(historyState);
+  }
+
+  historyState.forget(aState);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SynchronizeLayoutHistoryState() {
+  if (!mShared->mLayoutHistoryState) {
+    return NS_OK;
+  }
+
+  bool scrollPositionOnly = false;
+  nsTArray<nsCString> keys;
+  nsTArray<mozilla::PresState> states;
+  mShared->mLayoutHistoryState->GetContents(&scrollPositionOnly, keys, states);
+  Unused << SendUpdateLayoutHistoryState(scrollPositionOnly, keys, states);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetLoadType(uint32_t* aResult) {
+  return SendGetLoadType(aResult) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetLoadType(uint32_t aLoadType) {
+  return SendSetLoadType(aLoadType) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetID(uint32_t* aResult) {
+  return SendGetID(aResult) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetID(uint32_t aID) {
+  return SendSetID(aID) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetIsSubFrame(bool* aFlag) {
+  return SendGetIsSubFrame(aFlag) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetIsSubFrame(bool aFlag) {
+  return SendSetIsSubFrame(aFlag) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetCacheKey(uint32_t* aResult) {
+  return SendGetCacheKey(aResult) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetCacheKey(uint32_t aCacheKey) {
+  return SendSetCacheKey(aCacheKey) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetSaveLayoutStateFlag(bool* aFlag) {
+  *aFlag = mShared->mSaveLayoutState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetSaveLayoutStateFlag(bool aFlag) {
+  mShared->mSaveLayoutState = aFlag;
+  if (mShared->mLayoutHistoryState) {
+    mShared->mLayoutHistoryState->SetScrollPositionOnly(!aFlag);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetExpirationStatus(bool* aFlag) {
+  return SendGetExpirationStatus(aFlag) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetExpirationStatus(bool aFlag) {
+  return SendSetExpirationStatus(aFlag) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetContentType(nsACString& aContentType) {
+  nsCString contentType;
+  if (!SendGetContentType(&contentType)) {
+    return NS_ERROR_FAILURE;
+  }
+  aContentType = contentType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetContentType(const nsACString& aContentType) {
+  return SendSetContentType(nsCString(aContentType)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::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& srcdoc, bool srcdocEntry,
+    nsIURI* aBaseURI, bool aSaveLayoutState, bool aExpired) {
+  mShared->mLayoutHistoryState = nullptr;
+
+  mShared->mSaveLayoutState = aSaveLayoutState;
+  return SendCreate(aURI, nsString(aTitle), aInputStream, aCacheKey,
+                    nsCString(aContentType), aTriggeringPrincipal,
+                    aPrincipalToInherit, aStoragePrincipalToInherit, aCsp,
+                    aDocShellID, aDynamicCreation, aOriginalURI,
+                    aResultPrincipalURI, aLoadReplace, aReferrerInfo,
+                    nsString(srcdoc), srcdocEntry, aBaseURI, aSaveLayoutState,
+                    aExpired)
+             ? NS_OK
+             : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::Clone(nsISHEntry** aResult) {
+  RefPtr<CrossProcessSHEntry> result;
+  if (!SendClone(&result)) {
+    return NS_ERROR_FAILURE;
+  }
+  *aResult = result ? do_AddRef(result->ToSHEntryChild()).take() : nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetParent(nsISHEntry** aResult) {
+  RefPtr<CrossProcessSHEntry> parent;
+  if (!SendGetParent(&parent)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResult = parent ? do_AddRef(parent->ToSHEntryChild()).take() : nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetParent(nsISHEntry* aParent) {
+  return SendSetParent(static_cast<SHEntryChild*>(aParent)) ? NS_OK
+                                                            : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetWindowState(nsISupports* aState) {
+  mShared->mWindowState = aState;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetWindowState(nsISupports** aState) {
+  NS_IF_ADDREF(*aState = mShared->mWindowState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::SetViewerBounds(const nsIntRect& aBounds) {
+  Unused << SendSetViewerBounds(aBounds);
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::GetViewerBounds(nsIntRect& aBounds) {
+  Unused << SendGetViewerBounds(&aBounds);
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetTriggeringPrincipal(nsIPrincipal** aTriggeringPrincipal) {
+  RefPtr<nsIPrincipal> triggeringPrincipal;
+  if (!SendGetTriggeringPrincipal(&triggeringPrincipal)) {
+    return NS_ERROR_FAILURE;
+  }
+  triggeringPrincipal.forget(aTriggeringPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal) {
+  return SendSetTriggeringPrincipal(aTriggeringPrincipal) ? NS_OK
+                                                          : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetPrincipalToInherit(nsIPrincipal** aPrincipalToInherit) {
+  RefPtr<nsIPrincipal> principalToInherit;
+  if (!SendGetPrincipalToInherit(&principalToInherit)) {
+    return NS_ERROR_FAILURE;
+  }
+  principalToInherit.forget(aPrincipalToInherit);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit) {
+  return SendSetPrincipalToInherit(aPrincipalToInherit) ? NS_OK
+                                                        : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetStoragePrincipalToInherit(
+    nsIPrincipal** aStoragePrincipalToInherit) {
+  RefPtr<nsIPrincipal> storagePrincipalToInherit;
+  if (!SendGetStoragePrincipalToInherit(&storagePrincipalToInherit)) {
+    return NS_ERROR_FAILURE;
+  }
+  storagePrincipalToInherit.forget(aStoragePrincipalToInherit);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetStoragePrincipalToInherit(
+    nsIPrincipal* aStoragePrincipalToInherit) {
+  return SendSetStoragePrincipalToInherit(aStoragePrincipalToInherit)
+             ? NS_OK
+             : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetCsp(nsIContentSecurityPolicy** aCsp) {
+  RefPtr<nsIContentSecurityPolicy> csp;
+  if (!SendGetCsp(&csp)) {
+    return NS_ERROR_FAILURE;
+  }
+  csp.forget(aCsp);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetCsp(nsIContentSecurityPolicy* aCsp) {
+  return SendSetCsp(aCsp) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+bool SHEntryChild::HasBFCacheEntry(nsIBFCacheEntry* aEntry) {
+  return mShared == aEntry;
+}
+
+NS_IMETHODIMP
+SHEntryChild::AdoptBFCacheEntry(nsISHEntry* aEntry) {
+  RefPtr<SHEntryChild> entry = static_cast<SHEntryChild*>(aEntry);
+  nsresult rv;
+  if (!SendAdoptBFCacheEntry(entry, &rv)) {
+    return NS_ERROR_FAILURE;
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+  mShared = entry->mShared;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SharesDocumentWith(nsISHEntry* aEntry, bool* aOut) {
+  nsresult rv;
+  // FIXME Maybe check local shared state instead?
+  return SendSharesDocumentWith(static_cast<SHEntryChild*>(aEntry), aOut, &rv)
+             ? rv
+             : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::AbandonBFCacheEntry() {
+  RefPtr<SHEntryChildShared> shared = mShared->Duplicate();
+  if (!SendAbandonBFCacheEntry(shared->GetID())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  shared.swap(mShared);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetIsSrcdocEntry(bool* aIsSrcdocEntry) {
+  return SendGetIsSrcdocEntry(aIsSrcdocEntry) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetSrcdocData(nsAString& aSrcdocData) {
+  nsString srcdocData;
+  if (!SendGetSrcdocData(&srcdocData)) {
+    return NS_ERROR_FAILURE;
+  }
+  aSrcdocData = srcdocData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetSrcdocData(const nsAString& aSrcdocData) {
+  return SendSetSrcdocData(nsString(aSrcdocData)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetBaseURI(nsIURI** aBaseURI) {
+  RefPtr<nsIURI> baseURI;
+  if (!SendGetBaseURI(&baseURI)) {
+    return NS_ERROR_FAILURE;
+  }
+  baseURI.forget(aBaseURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetBaseURI(nsIURI* aBaseURI) {
+  return SendSetBaseURI(aBaseURI) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetScrollRestorationIsManual(bool* aIsManual) {
+  return SendGetScrollRestorationIsManual(aIsManual) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetScrollRestorationIsManual(bool aIsManual) {
+  return SendSetScrollRestorationIsManual(aIsManual) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetLoadedInThisProcess(bool* aLoadedInThisProcess) {
+  return SendGetLoadedInThisProcess(aLoadedInThisProcess) ? NS_OK
+                                                          : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetChildCount(int32_t* aCount) {
+  return SendGetChildCount(aCount) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::AddChild(nsISHEntry* aChild, int32_t aOffset,
+                       bool aUseRemoteSubframes) {
+  nsresult rv;
+  return SendAddChild(static_cast<SHEntryChild*>(aChild), aOffset,
+                      aUseRemoteSubframes, &rv)
+             ? rv
+             : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::RemoveChild(nsISHEntry* aChild) {
+  nsresult rv;
+  return aChild && SendRemoveChild(static_cast<SHEntryChild*>(aChild), &rv)
+             ? rv
+             : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetChildAt(int32_t aIndex, nsISHEntry** aResult) {
+  RefPtr<CrossProcessSHEntry> child;
+  if (!SendGetChildAt(aIndex, &child)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResult = child ? do_AddRef(child->ToSHEntryChild()).take() : nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::GetChildSHEntryIfHasNoDynamicallyAddedChild(int32_t aChildOffset,
+                                                          nsISHEntry** aChild) {
+  RefPtr<CrossProcessSHEntry> child;
+  SendGetChildSHEntryIfHasNoDynamicallyAddedChild(aChildOffset, &child);
+  *aChild = child ? do_AddRef(child->ToSHEntryChild()).take() : nullptr;
+}
+
+NS_IMETHODIMP
+SHEntryChild::ReplaceChild(nsISHEntry* aNewEntry) {
+  nsresult rv;
+  return SendReplaceChild(static_cast<SHEntryChild*>(aNewEntry), &rv)
+             ? rv
+             : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::ClearEntry() {
+  // We want to call AbandonBFCacheEntry in SHEntryParent,
+  // hence the need for duplciating the shared entry here
+  RefPtr<SHEntryChildShared> shared = mShared->Duplicate();
+  SendClearEntry(shared->GetID());
+  shared.swap(mShared);
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::AddChildShell(nsIDocShellTreeItem* aShell) {
+  mShared->mChildShells.AppendObject(aShell);
+}
+
+NS_IMETHODIMP
+SHEntryChild::ChildShellAt(int32_t aIndex, nsIDocShellTreeItem** aShell) {
+  NS_IF_ADDREF(*aShell = mShared->mChildShells.SafeObjectAt(aIndex));
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::ClearChildShells() { mShared->mChildShells.Clear(); }
+
+NS_IMETHODIMP
+SHEntryChild::GetRefreshURIList(nsIMutableArray** aList) {
+  // FIXME Move to parent.
+  NS_IF_ADDREF(*aList = mShared->mRefreshURIList);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetRefreshURIList(nsIMutableArray* aList) {
+  // FIXME Move to parent.
+  mShared->mRefreshURIList = aList;
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::SyncPresentationState() { mShared->SyncPresentationState(); }
+
+nsDocShellEditorData* SHEntryChild::ForgetEditorData() {
+  return mShared->mEditorData.release();
+}
+
+void SHEntryChild::SetEditorData(nsDocShellEditorData* aData) {
+  NS_ASSERTION(!(aData && mShared->mEditorData),
+               "We're going to overwrite an owning ref!");
+  if (mShared->mEditorData != aData) {
+    mShared->mEditorData = WrapUnique(aData);
+  }
+}
+
+bool SHEntryChild::HasDetachedEditor() {
+  return mShared->mEditorData != nullptr;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetStateData(nsIStructuredCloneContainer** aContainer) {
+  ClonedMessageData data;
+  if (!SendGetStateData(&data)) {
+    return NS_ERROR_FAILURE;
+  }
+  // FIXME Should we signal null separately from the ClonedMessageData?
+  if (data.data().data.Size() == 0) {
+    *aContainer = nullptr;
+  } else {
+    RefPtr<nsStructuredCloneContainer> container =
+        new nsStructuredCloneContainer();
+    container->StealFromClonedMessageDataForParent(data);
+    container.forget(aContainer);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetStateData(nsIStructuredCloneContainer* aContainer) {
+  // FIXME nsIStructuredCloneContainer is not builtin_class
+  ClonedMessageData data;
+  if (aContainer) {
+    static_cast<nsStructuredCloneContainer*>(aContainer)
+        ->BuildClonedMessageDataForChild(ContentChild::GetSingleton(), data);
+  }
+  return SendSetStateData(data) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP_(bool)
+SHEntryChild::IsDynamicallyAdded() {
+  bool isDynamicallyAdded;
+  return SendIsDynamicallyAdded(&isDynamicallyAdded) && isDynamicallyAdded;
+}
+
+NS_IMETHODIMP
+SHEntryChild::HasDynamicallyAddedChild(bool* aAdded) {
+  return SendHasDynamicallyAddedChild(aAdded) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetDocshellID(nsID& aID) {
+  if (!SendGetDocshellID(&aID)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetDocshellID(const nsID& aID) {
+  return SendSetDocshellID(aID) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetLastTouched(uint32_t* aLastTouched) {
+  return SendGetLastTouched(aLastTouched) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetLastTouched(uint32_t aLastTouched) {
+  return SendSetLastTouched(aLastTouched) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetShistory(nsISHistory** aSHistory) {
+  *aSHistory = do_AddRef(mShared->mSHistory).take();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetLoadTypeAsHistory() {
+  return SendSetLoadTypeAsHistory() ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetPersist(bool* aPersist) {
+  return SendGetPersist(aPersist) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::SetPersist(bool aPersist) {
+  return SendSetPersist(aPersist) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHEntryChild::CreateLoadInfo(nsDocShellLoadState** aLoadState) {
+  *aLoadState = nullptr;
+  RefPtr<nsDocShellLoadState> loadState;
+  if (!SendCreateLoadInfo(&loadState)) {
+    return NS_ERROR_FAILURE;
+  }
+  loadState.forget(aLoadState);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHEntryChild::GetBfcacheID(uint64_t* aBFCacheID) {
+  *aBFCacheID = mShared->GetID();
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHEntryChild::SyncTreesForSubframeNavigation(nsISHEntry* aEntry,
+                                             BrowsingContext* aBC,
+                                             BrowsingContext* aIgnoreBC) {
+  nsTArray<SwapEntriesDocshellData> entriesToUpdate;
+  Unused << SendSyncTreesForSubframeNavigation(
+      static_cast<SHEntryChild*>(aEntry), aBC, aIgnoreBC, &entriesToUpdate);
+  for (auto& data : entriesToUpdate) {
+    // data.context() is a MaybeDiscardedBrowsingContext
+    // It can't be null, but if it has been discarded we will update
+    // the docshell anyway
+    MOZ_ASSERT(!data.context().IsNull(), "Browsing context cannot be null");
+    nsDocShell* docshell = static_cast<nsDocShell*>(
+        data.context().GetMaybeDiscarded()->GetDocShell());
+    if (docshell) {
+      RefPtr<SHEntryChild> oldEntry = data.oldEntry()->ToSHEntryChild();
+      RefPtr<SHEntryChild> newEntry;
+      if (data.newEntry()) {
+        newEntry = data.newEntry()->ToSHEntryChild();
+      }
+      docshell->SwapHistoryEntries(oldEntry, newEntry);
+    }
+  }
+}
+
+void SHEntryChild::EvictContentViewer() {
+  nsCOMPtr<nsIContentViewer> viewer = GetContentViewer();
+  if (viewer) {
+    // Drop the presentation state before destroying the viewer, so that
+    // document teardown is able to correctly persist the state.
+    mShared->NotifyListenersContentViewerEvicted();
+    SetContentViewer(nullptr);
+    SyncPresentationState();
+    viewer->Destroy();
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHEntryChild.h
@@ -0,0 +1,127 @@
+/* -*- 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_SHEntryChild_h
+#define mozilla_dom_SHEntryChild_h
+
+#include "mozilla/dom/PSHEntryChild.h"
+#include "mozilla/dom/MaybeNewPSHEntry.h"
+#include "nsContentUtils.h"
+#include "nsExpirationTracker.h"
+#include "nsIBFCacheEntry.h"
+#include "nsISHEntry.h"
+#include "nsRect.h"
+#include "nsSHEntryShared.h"
+#include "nsStubMutationObserver.h"
+
+class nsDocShellEditorData;
+class nsIContentViewer;
+class nsILayoutHistoryState;
+class nsIMutableArray;
+
+namespace mozilla {
+namespace dom {
+
+class SHistoryChild;
+
+/**
+ * Implementation of the shared state for session history entries in the child
+ * process.
+ */
+class SHEntryChildShared final : public nsIBFCacheEntry,
+                                 public nsStubMutationObserver,
+                                 public SHEntrySharedChildState {
+ public:
+  static void Init();
+
+  static SHEntryChildShared* GetOrCreate(SHistoryChild* aSHistory,
+                                         uint64_t aSharedID);
+  static void Remove(uint64_t aSharedID);
+
+  static uint64_t CreateSharedID() {
+    return nsContentUtils::GenerateProcessSpecificId(++sNextSharedID);
+  }
+  static void EvictContentViewers(
+      const nsTArray<uint64_t>& aToEvictSharedStateIDs);
+
+  void NotifyListenersContentViewerEvicted(uint32_t aNumEvicted = 1);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIBFCACHEENTRY
+
+  // The nsIMutationObserver bits we actually care about.
+  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+
+  nsExpirationState* GetExpirationState() { return &mExpirationState; }
+
+  uint64_t GetID() { return mID; }
+
+ private:
+  static uint64_t sNextSharedID;
+
+  SHEntryChildShared(SHistoryChild* aSHistory, uint64_t aID);
+  ~SHEntryChildShared();
+
+  friend class SHEntryChild;
+
+  already_AddRefed<SHEntryChildShared> Duplicate();
+
+  void RemoveFromExpirationTracker();
+  void SyncPresentationState();
+  void DropPresentationState();
+
+  nsresult SetContentViewer(nsIContentViewer* aViewer);
+
+  uint64_t mID;
+  RefPtr<SHistoryChild> mSHistory;
+};
+
+/**
+ * Session history entry actor for the child process.
+ */
+class SHEntryChild final : public PSHEntryChild,
+                           public nsISHEntry,
+                           public CrossProcessSHEntry {
+  friend class PSHEntryChild;
+  using PSHEntryChild::CrossProcessSHEntry;
+
+ public:
+  explicit SHEntryChild(const SHEntryChild* aClone)
+      : mShared(aClone->mShared.get()), mIPCActorDeleted(false) {}
+  SHEntryChild(SHistoryChild* aSHistory, uint64_t aSharedID)
+      : mShared(SHEntryChildShared::GetOrCreate(aSHistory, aSharedID)),
+        mIPCActorDeleted(false) {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISHENTRY
+
+  void EvictContentViewer();
+
+ protected:
+  void ActorDestroy(ActorDestroyReason aWhy) override {
+    mIPCActorDeleted = true;
+  }
+
+ private:
+  ~SHEntryChild() = default;
+
+  RefPtr<SHEntryChildShared> mShared;
+  bool mIPCActorDeleted;
+};
+
+inline SHEntryChild* CrossProcessSHEntry::ToSHEntryChild() {
+  MOZ_ASSERT(XRE_IsContentProcess(), "Wrong side!");
+  return static_cast<SHEntryChild*>(this);
+}
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif /* mozilla_dom_SHEntryChild_h */
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHEntryParent.cpp
@@ -0,0 +1,620 @@
+/* -*- 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 "SHEntryParent.h"
+#include "SHistoryParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/MaybeNewPSHEntry.h"
+#include "nsILayoutHistoryState.h"
+#include "nsStructuredCloneContainer.h"
+
+namespace mozilla {
+namespace dom {
+
+SHEntrySharedParent::SHEntrySharedParent(PContentParent* aContentParent,
+                                         LegacySHistory* aSHistory,
+                                         uint64_t aSharedID)
+    : SHEntrySharedParentState(aSHistory, aSharedID),
+      mContentParent(aContentParent) {}
+
+void SHEntrySharedParent::Destroy() {
+  if (mContentParent &&
+      !static_cast<ContentParent*>(mContentParent.get())->IsDestroyed()) {
+    Unused << mContentParent->SendDestroySHEntrySharedState(mID);
+  }
+  SHEntrySharedParentState::Destroy();
+}
+
+LegacySHEntry::LegacySHEntry(PContentParent* aContentParent,
+                             LegacySHistory* aSHistory, uint64_t aSharedID)
+    : nsSHEntry(new SHEntrySharedParent(aContentParent, aSHistory, aSharedID)),
+      mActor(nullptr) {}
+
+NS_IMPL_ISUPPORTS_INHERITED0(LegacySHEntry, nsSHEntry)
+
+SHEntryParent* LegacySHEntry::CreateActor() {
+  MOZ_ASSERT(!mActor);
+  mActor = new SHEntryParent(this);
+  return mActor;
+}
+
+MaybeNewPSHEntryParent LegacySHEntry::GetOrCreateActor(
+    PContentParent* aContentParent) {
+  if (mActor) {
+    return AsVariant(static_cast<PSHEntryParent*>(mActor));
+  }
+
+  nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mShared->mSHistory);
+  return AsVariant(
+      NewPSHEntry{aContentParent->OpenPSHEntryEndpoint(CreateActor()),
+                  static_cast<LegacySHistory*>(shistory.get())->GetActor(),
+                  nullptr, mShared->mID});
+}
+
+void LegacySHEntry::AbandonBFCacheEntry(uint64_t aNewSharedID) {
+  mShared =
+      static_cast<SHEntrySharedParent*>(mShared.get())->Duplicate(aNewSharedID);
+}
+
+NS_IMETHODIMP
+LegacySHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
+  *aBFCacheID = mShared->GetID();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LegacySHEntry::Clone(nsISHEntry** aResult) {
+  nsCOMPtr<nsISHEntry> entry = new LegacySHEntry(*this);
+  entry.forget(aResult);
+  return NS_OK;
+}
+
+void SHEntryParent::ActorDestroy(ActorDestroyReason aWhy) {
+  mEntry->mActor = nullptr;
+}
+
+bool SHEntryParent::RecvGetURI(RefPtr<nsIURI>* aURI) {
+  *aURI = mEntry->GetURI();
+  return true;
+}
+
+bool SHEntryParent::RecvSetURI(nsIURI* aURI) {
+  DebugOnly<nsresult> rv = mEntry->SetURI(aURI);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetOriginalURI(RefPtr<nsIURI>* aOriginalURI) {
+  *aOriginalURI = mEntry->GetOriginalURI();
+  return true;
+}
+
+bool SHEntryParent::RecvSetOriginalURI(nsIURI* aOriginalURI) {
+  DebugOnly<nsresult> rv = mEntry->SetOriginalURI(aOriginalURI);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetResultPrincipalURI(
+    RefPtr<nsIURI>* aResultPrincipalURI) {
+  *aResultPrincipalURI = mEntry->GetResultPrincipalURI();
+  return true;
+}
+
+bool SHEntryParent::RecvSetResultPrincipalURI(nsIURI* aResultPrincipalURI) {
+  DebugOnly<nsresult> rv = mEntry->SetResultPrincipalURI(aResultPrincipalURI);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetLoadReplace(bool* aLoadReplace) {
+  *aLoadReplace = mEntry->GetLoadReplace();
+  return true;
+}
+
+bool SHEntryParent::RecvSetLoadReplace(const bool& aLoadReplace) {
+  DebugOnly<nsresult> rv = mEntry->SetLoadReplace(aLoadReplace);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetTitle(nsString* aTitle) {
+  DebugOnly<nsresult> rv = mEntry->GetTitle(*aTitle);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvSetTitle(const nsString& aTitle) {
+  DebugOnly<nsresult> rv = mEntry->SetTitle(aTitle);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetIsSubFrame(bool* aIsSubFrame) {
+  *aIsSubFrame = mEntry->GetIsSubFrame();
+  return true;
+}
+
+bool SHEntryParent::RecvSetIsSubFrame(const bool& aIsSubFrame) {
+  DebugOnly<nsresult> rv = mEntry->SetIsSubFrame(aIsSubFrame);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetReferrerInfo(
+    RefPtr<nsIReferrerInfo>* aReferrerInfo) {
+  *aReferrerInfo = mEntry->GetReferrerInfo();
+  return true;
+}
+
+bool SHEntryParent::RecvSetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
+  DebugOnly<nsresult> rv = mEntry->SetReferrerInfo(aReferrerInfo);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetSticky(bool* aSticky) {
+  *aSticky = mEntry->GetSticky();
+  return true;
+}
+
+bool SHEntryParent::RecvSetSticky(const bool& aSticky) {
+  DebugOnly<nsresult> rv = mEntry->SetSticky(aSticky);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetPostData(RefPtr<nsIInputStream>* aPostData) {
+  *aPostData = mEntry->GetPostData();
+  return true;
+}
+
+bool SHEntryParent::RecvSetPostData(nsIInputStream* aPostData) {
+  DebugOnly<nsresult> rv = mEntry->SetPostData(aPostData);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetParent(RefPtr<CrossProcessSHEntry>* aParentEntry) {
+  nsCOMPtr<nsISHEntry> parent = mEntry->GetParent();
+  *aParentEntry = parent.forget().downcast<LegacySHEntry>();
+  return true;
+}
+
+bool SHEntryParent::RecvSetParent(PSHEntryParent* aParentEntry) {
+  DebugOnly<nsresult> rv = mEntry->SetParent(
+      aParentEntry ? static_cast<SHEntryParent*>(aParentEntry)->mEntry.get()
+                   : nullptr);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetLoadType(uint32_t* aLoadType) {
+  *aLoadType = mEntry->GetLoadType();
+  return true;
+}
+
+bool SHEntryParent::RecvSetLoadType(const uint32_t& aLoadType) {
+  DebugOnly<nsresult> rv = mEntry->SetLoadType(aLoadType);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetID(uint32_t* aID) {
+  *aID = mEntry->GetID();
+  return true;
+}
+
+bool SHEntryParent::RecvSetID(const uint32_t& aID) {
+  DebugOnly<nsresult> rv = mEntry->SetID(aID);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetCacheKey(uint32_t* aCacheKey) {
+  *aCacheKey = mEntry->GetCacheKey();
+  return true;
+}
+
+bool SHEntryParent::RecvSetCacheKey(const uint32_t& aCacheKey) {
+  DebugOnly<nsresult> rv = mEntry->SetCacheKey(aCacheKey);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetExpirationStatus(bool* aExpirationStatus) {
+  *aExpirationStatus = mEntry->GetExpirationStatus();
+  return true;
+}
+
+bool SHEntryParent::RecvSetExpirationStatus(const bool& aExpirationStatus) {
+  DebugOnly<nsresult> rv = mEntry->SetExpirationStatus(aExpirationStatus);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetContentType(nsCString* aContentType) {
+  DebugOnly<nsresult> rv = mEntry->GetContentType(*aContentType);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvSetContentType(const nsCString& aContentType) {
+  DebugOnly<nsresult> rv = mEntry->SetContentType(aContentType);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetURIWasModified(bool* aURIWasModified) {
+  *aURIWasModified = mEntry->GetURIWasModified();
+  return true;
+}
+
+bool SHEntryParent::RecvSetURIWasModified(const bool& aURIWasModified) {
+  DebugOnly<nsresult> rv = mEntry->SetURIWasModified(aURIWasModified);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetTriggeringPrincipal(
+    RefPtr<nsIPrincipal>* aTriggeringPrincipal) {
+  *aTriggeringPrincipal = mEntry->GetTriggeringPrincipal();
+  return true;
+}
+
+bool SHEntryParent::RecvSetTriggeringPrincipal(
+    nsIPrincipal* aTriggeringPrincipal) {
+  DebugOnly<nsresult> rv = mEntry->SetTriggeringPrincipal(aTriggeringPrincipal);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetPrincipalToInherit(
+    RefPtr<nsIPrincipal>* aPrincipalToInherit) {
+  *aPrincipalToInherit = mEntry->GetPrincipalToInherit();
+  return true;
+}
+
+bool SHEntryParent::RecvSetPrincipalToInherit(
+    nsIPrincipal* aPrincipalToInherit) {
+  DebugOnly<nsresult> rv = mEntry->SetPrincipalToInherit(aPrincipalToInherit);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetStoragePrincipalToInherit(
+    RefPtr<nsIPrincipal>* aStoragePrincipalToInherit) {
+  *aStoragePrincipalToInherit = mEntry->GetStoragePrincipalToInherit();
+  return true;
+}
+
+bool SHEntryParent::RecvSetStoragePrincipalToInherit(
+    nsIPrincipal* aStoragePrincipalToInherit) {
+  DebugOnly<nsresult> rv =
+      mEntry->SetStoragePrincipalToInherit(aStoragePrincipalToInherit);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetCsp(RefPtr<nsIContentSecurityPolicy>* aCsp) {
+  *aCsp = mEntry->GetCsp();
+  return true;
+}
+
+bool SHEntryParent::RecvSetCsp(nsIContentSecurityPolicy* aCsp) {
+  DebugOnly<nsresult> rv = mEntry->SetCsp(aCsp);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetStateData(ClonedMessageData* aData) {
+  nsCOMPtr<nsIStructuredCloneContainer> container = mEntry->GetStateData();
+  if (container) {
+    static_cast<nsStructuredCloneContainer*>(container.get())
+        ->BuildClonedMessageDataForParent(
+            static_cast<ContentParent*>(ToplevelProtocol()), *aData);
+  }
+  return true;
+}
+
+bool SHEntryParent::RecvSetStateData(ClonedMessageData&& aData) {
+  // FIXME Need more data! Should we signal null separately from the
+  //       ClonedMessageData?
+  if (aData.data().data.Size() == 0) {
+    mEntry->SetStateData(nullptr);
+  } else {
+    RefPtr<nsStructuredCloneContainer> container =
+        new nsStructuredCloneContainer();
+    container->StealFromClonedMessageDataForParent(aData);
+    mEntry->SetStateData(container);
+  }
+  return true;
+}
+
+bool SHEntryParent::RecvGetDocshellID(nsID* aDocshellID) {
+  DebugOnly<nsresult> rv = mEntry->GetDocshellID(*aDocshellID);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+
+  return true;
+}
+
+bool SHEntryParent::RecvSetDocshellID(const nsID& aDocshellID) {
+  DebugOnly<nsresult> rv = mEntry->SetDocshellID(aDocshellID);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetIsSrcdocEntry(bool* aIsSrcdocEntry) {
+  *aIsSrcdocEntry = mEntry->GetIsSrcdocEntry();
+  return true;
+}
+
+bool SHEntryParent::RecvGetSrcdocData(nsString* aSrcdocData) {
+  DebugOnly<nsresult> rv = mEntry->GetSrcdocData(*aSrcdocData);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvSetSrcdocData(const nsString& aSrcdocData) {
+  DebugOnly<nsresult> rv = mEntry->SetSrcdocData(aSrcdocData);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetBaseURI(RefPtr<nsIURI>* aBaseURI) {
+  *aBaseURI = mEntry->GetBaseURI();
+  return true;
+}
+
+bool SHEntryParent::RecvSetBaseURI(nsIURI* aBaseURI) {
+  DebugOnly<nsresult> rv = mEntry->SetBaseURI(aBaseURI);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetScrollRestorationIsManual(
+    bool* aScrollRestorationIsManual) {
+  DebugOnly<nsresult> rv =
+      mEntry->GetScrollRestorationIsManual(aScrollRestorationIsManual);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvSetScrollRestorationIsManual(
+    const bool& aScrollRestorationIsManual) {
+  DebugOnly<nsresult> rv =
+      mEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetLoadedInThisProcess(bool* aLoadedInThisProcess) {
+  *aLoadedInThisProcess = mEntry->GetLoadedInThisProcess();
+  return true;
+}
+
+bool SHEntryParent::RecvGetLastTouched(uint32_t* aLastTouched) {
+  *aLastTouched = mEntry->GetLastTouched();
+  return true;
+}
+
+bool SHEntryParent::RecvSetLastTouched(const uint32_t& aLastTouched) {
+  DebugOnly<nsresult> rv = mEntry->SetLastTouched(aLastTouched);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetChildCount(int32_t* aChildCount) {
+  *aChildCount = mEntry->GetChildCount();
+  return true;
+}
+
+bool SHEntryParent::RecvGetPersist(bool* aPersist) {
+  *aPersist = mEntry->GetPersist();
+  return true;
+}
+
+bool SHEntryParent::RecvSetPersist(const bool& aPersist) {
+  DebugOnly<nsresult> rv = mEntry->SetPersist(aPersist);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetScrollPosition(int32_t* aX, int32_t* aY) {
+  DebugOnly<nsresult> rv = mEntry->GetScrollPosition(aX, aY);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvSetScrollPosition(const int32_t& aX,
+                                          const int32_t& aY) {
+  DebugOnly<nsresult> rv = mEntry->SetScrollPosition(aX, aY);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvGetViewerBounds(nsIntRect* aBounds) {
+  mEntry->GetViewerBounds(*aBounds);
+  return true;
+}
+
+bool SHEntryParent::RecvSetViewerBounds(const nsIntRect& aBounds) {
+  mEntry->SetViewerBounds(aBounds);
+  return true;
+}
+
+bool SHEntryParent::RecvCreate(
+    nsIURI* aURI, const nsString& aTitle, nsIInputStream* aInputStream,
+    const uint32_t& aCacheKey, const nsCString& aContentType,
+    nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
+    nsIPrincipal* aStoragePrincipalToInherit, nsIContentSecurityPolicy* aCsp,
+    const nsID& aDocshellID, const bool& aDynamicCreation, nsIURI* aOriginalURI,
+    nsIURI* aResultPrincipalURI, const bool& aLoadReplace,
+    nsIReferrerInfo* aReferrerInfo, const nsAString& srcdoc,
+    const bool& srcdocEntry, nsIURI* aBaseURI, const bool& aSaveLayoutState,
+    const bool& aExpired) {
+  DebugOnly<nsresult> rv = mEntry->Create(
+      aURI, aTitle, aInputStream, aCacheKey, aContentType, aTriggeringPrincipal,
+      aPrincipalToInherit, aStoragePrincipalToInherit, aCsp, aDocshellID,
+      aDynamicCreation, aOriginalURI, aResultPrincipalURI, aLoadReplace,
+      aReferrerInfo, srcdoc, srcdocEntry, aBaseURI, aSaveLayoutState, aExpired);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvHasDetachedEditor(bool* aHasDetachedEditor) {
+  *aHasDetachedEditor = mEntry->HasDetachedEditor();
+  return true;
+}
+
+bool SHEntryParent::RecvIsDynamicallyAdded(bool* aIsDynamicallyAdded) {
+  *aIsDynamicallyAdded = mEntry->IsDynamicallyAdded();
+  return true;
+}
+
+bool SHEntryParent::RecvHasDynamicallyAddedChild(
+    bool* aHasDynamicallyAddedChild) {
+  DebugOnly<nsresult> rv =
+      mEntry->HasDynamicallyAddedChild(aHasDynamicallyAddedChild);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvDocshellID(nsID* aDocshellID) {
+  mEntry->GetDocshellID(*aDocshellID);
+  return true;
+}
+
+bool SHEntryParent::RecvAdoptBFCacheEntry(PSHEntryParent* aEntry,
+                                          nsresult* aResult) {
+  *aResult =
+      mEntry->AdoptBFCacheEntry(static_cast<SHEntryParent*>(aEntry)->mEntry);
+  return true;
+}
+
+bool SHEntryParent::RecvAbandonBFCacheEntry(const uint64_t& aNewSharedID) {
+  mEntry->AbandonBFCacheEntry(aNewSharedID);
+  return true;
+}
+
+bool SHEntryParent::RecvSharesDocumentWith(PSHEntryParent* aEntry,
+                                           bool* aSharesDocumentWith,
+                                           nsresult* aResult) {
+  *aResult = mEntry->SharesDocumentWith(
+      static_cast<SHEntryParent*>(aEntry)->mEntry, aSharesDocumentWith);
+  return true;
+}
+
+bool SHEntryParent::RecvSetLoadTypeAsHistory() {
+  DebugOnly<nsresult> rv = mEntry->SetLoadTypeAsHistory();
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  return true;
+}
+
+bool SHEntryParent::RecvAddChild(PSHEntryParent* aChild, const int32_t& aOffset,
+                                 const bool& aUseRemoteSubframes,
+                                 nsresult* aResult) {
+  *aResult = mEntry->AddChild(
+      aChild ? static_cast<SHEntryParent*>(aChild)->mEntry.get() : nullptr,
+      aOffset, aUseRemoteSubframes);
+  return true;
+}
+
+bool SHEntryParent::RecvRemoveChild(PSHEntryParent* aChild, nsresult* aResult) {
+  *aResult = mEntry->RemoveChild(static_cast<SHEntryParent*>(aChild)->mEntry);
+  return true;
+}
+
+bool SHEntryParent::RecvGetChildAt(const int32_t& aIndex,
+                                   RefPtr<CrossProcessSHEntry>* aChild) {
+  nsCOMPtr<nsISHEntry> child;
+  DebugOnly<nsresult> rv = mEntry->GetChildAt(aIndex, getter_AddRefs(child));
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+
+  *aChild = child.forget().downcast<LegacySHEntry>();
+  return true;
+}
+
+bool SHEntryParent::RecvGetChildSHEntryIfHasNoDynamicallyAddedChild(
+    const int32_t& aChildOffset, RefPtr<CrossProcessSHEntry>* aChild) {
+  nsCOMPtr<nsISHEntry> child;
+  mEntry->GetChildSHEntryIfHasNoDynamicallyAddedChild(aChildOffset,
+                                                      getter_AddRefs(child));
+  *aChild = child.forget().downcast<LegacySHEntry>();
+  return true;
+}
+
+bool SHEntryParent::RecvReplaceChild(PSHEntryParent* aNewChild,
+                                     nsresult* aResult) {
+  *aResult =
+      mEntry->ReplaceChild(static_cast<SHEntryParent*>(aNewChild)->mEntry);
+  return true;
+}
+
+bool SHEntryParent::RecvClearEntry(const uint64_t& aNewSharedID) {
+  mEntry->ClearEntry();
+  mEntry->AbandonBFCacheEntry(aNewSharedID);
+  return true;
+}
+
+bool SHEntryParent::RecvCreateLoadInfo(
+    RefPtr<nsDocShellLoadState>* aLoadState) {
+  mEntry->CreateLoadInfo(getter_AddRefs(*aLoadState));
+  return true;
+}
+
+bool SHEntryParent::RecvUpdateLayoutHistoryState(
+    const bool& aScrollPositionOnly, nsTArray<nsCString>&& aKeys,
+    nsTArray<PresState>&& aStates) {
+  nsCOMPtr<nsILayoutHistoryState> layoutHistoryState;
+  // InitLayoutHistoryState creates a new object only if there isn't one
+  // already.
+  mEntry->InitLayoutHistoryState(getter_AddRefs(layoutHistoryState));
+  layoutHistoryState->Reset();
+
+  if (aKeys.Length() != aStates.Length()) {
+    NS_WARNING("Bogus data sent from the child process?");
+    return true;
+  }
+
+  layoutHistoryState->SetScrollPositionOnly(aScrollPositionOnly);
+
+  for (uint32_t i = 0; i < aKeys.Length(); ++i) {
+    PresState& state = aStates[i];
+    UniquePtr<PresState> newState = MakeUnique<PresState>(state);
+    layoutHistoryState->AddState(aKeys[i], std::move(newState));
+  }
+  return true;
+}
+
+bool SHEntryParent::RecvClone(RefPtr<CrossProcessSHEntry>* aResult) {
+  nsCOMPtr<nsISHEntry> result;
+  DebugOnly<nsresult> rv =
+      static_cast<LegacySHEntry*>(mEntry)->Clone(getter_AddRefs(result));
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
+  *aResult = result.forget().downcast<LegacySHEntry>();
+  return true;
+}
+
+bool SHEntryParent::RecvSyncTreesForSubframeNavigation(
+    PSHEntryParent* aSHEntry, const MaybeDiscarded<BrowsingContext>& aBC,
+    const MaybeDiscarded<BrowsingContext>& aIgnoreBC,
+    nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate) {
+  nsTArray<EntriesAndBrowsingContextData> entriesToSendOverIPC;
+  // aBC or aIgnoreBC can be discarded but we can update them anyway if they are
+  // not null
+  mEntry->SyncTreesForSubframeNavigation(
+      static_cast<ContentParent*>(Manager())->ChildID(),
+      aSHEntry ? static_cast<SHEntryParent*>(aSHEntry)->mEntry.get() : nullptr,
+      aBC.GetMaybeDiscarded(), aIgnoreBC.GetMaybeDiscarded(),
+      &entriesToSendOverIPC);
+  SHistoryParent::CreateActorsForSwapEntries(entriesToSendOverIPC,
+                                             aEntriesToUpdate, Manager());
+  return true;
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHEntryParent.h
@@ -0,0 +1,225 @@
+/* -*- 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_SHistoryEntry_h
+#define mozilla_dom_SHistoryEntry_h
+
+#include "mozilla/dom/PSHEntryParent.h"
+#include "mozilla/dom/MaybeNewPSHEntry.h"
+#include "mozilla/WeakPtr.h"
+#include "nsSHEntry.h"
+#include "nsSHEntryShared.h"
+
+namespace mozilla {
+namespace dom {
+
+class LegacySHistory;
+class PContentParent;
+class SHEntryParent;
+
+/**
+ * Implementation of the shared state for session history entries in the parent
+ * process.
+ */
+class SHEntrySharedParent : public SHEntrySharedParentState {
+ public:
+  SHEntrySharedParent(PContentParent* aContentParent, LegacySHistory* aSHistory,
+                      uint64_t aSharedID);
+
+  already_AddRefed<SHEntrySharedParent> Duplicate(uint64_t aNewSharedID) {
+    RefPtr<SHEntrySharedParent> shared =
+        new SHEntrySharedParent(this, aNewSharedID);
+    shared->CopyFrom(this);
+    return shared.forget();
+  }
+
+  PContentParent* GetContentParent() { return mContentParent.get(); }
+
+ protected:
+  SHEntrySharedParent(SHEntrySharedParent* aDuplicate, uint64_t aSharedID)
+      : SHEntrySharedParentState(aDuplicate, aSharedID),
+        mContentParent(aDuplicate->mContentParent) {}
+
+  void Destroy() override;
+
+ private:
+  mozilla::WeakPtr<PContentParent> mContentParent;
+};
+
+/**
+ * Session history entry implementation based on the legacy implementation that
+ * used to live in the child process. Ideally this wouldn't implement nsISHEntry
+ * (it should only ever be accessed by SHEntryParent and LegacySHistory).
+ * The actor is (re)created as needed, whenever we need to return an entry to
+ * the child process. The lifetime is determined by the child side.
+ */
+class LegacySHEntry final : public nsSHEntry, public CrossProcessSHEntry {
+ public:
+  LegacySHEntry(PContentParent* aContentParent, LegacySHistory* aSHistory,
+                uint64_t aSharedID);
+  explicit LegacySHEntry(const LegacySHEntry& aEntry)
+      : nsSHEntry(aEntry), mActor(nullptr) {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  MaybeNewPSHEntryParent GetOrCreateActor(PContentParent* aContentParent);
+
+  using nsSHEntry::AbandonBFCacheEntry;
+  void AbandonBFCacheEntry(uint64_t aNewSharedID);
+  NS_IMETHODIMP GetBfcacheID(uint64_t* aBFCacheID) override;
+
+  uint64_t GetSharedStateID() const { return mShared->GetID(); }
+
+  dom::SHEntrySharedParentState* GetSharedState() const {
+    return mShared.get();
+  }
+
+  NS_IMETHOD Clone(nsISHEntry** aResult) override;
+
+ private:
+  friend class SHEntryParent;
+  friend class SHistoryParent;
+
+  ~LegacySHEntry() = default;
+
+  SHEntryParent* CreateActor();
+
+  SHEntryParent* mActor;
+};
+
+/**
+ * Session history entry actor for the parent process. Forwards to the legacy
+ * implementation that used to live in the child process (see LegacySHEntry).
+ */
+class SHEntryParent final : public PSHEntryParent {
+  friend class PSHEntryParent;
+  friend class SHistoryParent;
+  friend class ContentParent;
+
+ public:
+  explicit SHEntryParent(LegacySHEntry* aEntry)
+      : PSHEntryParent(), mEntry(aEntry) {}
+
+  LegacySHEntry* GetSHEntry() { return mEntry; }
+
+ protected:
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+  bool RecvGetURI(RefPtr<nsIURI>* aURI);
+  bool RecvSetURI(nsIURI* aURI);
+  bool RecvGetOriginalURI(RefPtr<nsIURI>* aOriginalURI);
+  bool RecvSetOriginalURI(nsIURI* aOriginalURI);
+  bool RecvGetResultPrincipalURI(RefPtr<nsIURI>* aResultPrincipalURI);
+  bool RecvSetResultPrincipalURI(nsIURI* aResultPrincipalURI);
+  bool RecvGetLoadReplace(bool* aLoadReplace);
+  bool RecvSetLoadReplace(const bool& aLoadReplace);
+  bool RecvGetTitle(nsString* aTitle);
+  bool RecvSetTitle(const nsString& aTitle);
+  bool RecvGetIsSubFrame(bool* aIsSubFrame);
+  bool RecvSetIsSubFrame(const bool& aIsSubFrame);
+  bool RecvGetReferrerInfo(RefPtr<nsIReferrerInfo>* aReferrerInfo);
+  bool RecvSetReferrerInfo(nsIReferrerInfo* aReferrerInfo);
+  bool RecvGetSticky(bool* aSticky);
+  bool RecvSetSticky(const bool& aSticky);
+  bool RecvGetPostData(RefPtr<nsIInputStream>* aPostData);
+  bool RecvSetPostData(nsIInputStream* aPostData);
+  bool RecvGetParent(RefPtr<CrossProcessSHEntry>* aParentEntry);
+  bool RecvSetParent(PSHEntryParent* aParentEntry);
+  bool RecvGetLoadType(uint32_t* aLoadType);
+  bool RecvSetLoadType(const uint32_t& aLoadType);
+  bool RecvGetID(uint32_t* aID);
+  bool RecvSetID(const uint32_t& aID);
+  bool RecvGetCacheKey(uint32_t* aCacheKey);
+  bool RecvSetCacheKey(const uint32_t& aCacheKey);
+  bool RecvGetExpirationStatus(bool* aExpirationStatus);
+  bool RecvSetExpirationStatus(const bool& aExpirationStatus);
+  bool RecvGetContentType(nsCString* aContentType);
+  bool RecvSetContentType(const nsCString& aContentType);
+  bool RecvGetURIWasModified(bool* aURIWasModified);
+  bool RecvSetURIWasModified(const bool& aURIWasModified);
+  bool RecvGetTriggeringPrincipal(RefPtr<nsIPrincipal>* aTriggeringPrincipal);
+  bool RecvSetTriggeringPrincipal(nsIPrincipal* aTriggeringPrincipal);
+  bool RecvGetPrincipalToInherit(RefPtr<nsIPrincipal>* aPrincipalToInherit);
+  bool RecvSetPrincipalToInherit(nsIPrincipal* aPrincipalToInherit);
+  bool RecvGetStoragePrincipalToInherit(
+      RefPtr<nsIPrincipal>* aStoragePrincipalToInherit);
+  bool RecvSetStoragePrincipalToInherit(
+      nsIPrincipal* aStoragePrincipalToInherit);
+  bool RecvGetCsp(RefPtr<nsIContentSecurityPolicy>* aCsp);
+  bool RecvSetCsp(nsIContentSecurityPolicy* aCsp);
+  bool RecvGetStateData(ClonedMessageData* aData);
+  bool RecvSetStateData(ClonedMessageData&& aData);
+  bool RecvGetDocshellID(nsID* aDocshellID);
+  bool RecvSetDocshellID(const nsID& aDocshellID);
+  bool RecvGetIsSrcdocEntry(bool* aIsSrcdocEntry);
+  bool RecvGetSrcdocData(nsString* aSrcdocData);
+  bool RecvSetSrcdocData(const nsString& aSrcdocData);
+  bool RecvGetBaseURI(RefPtr<nsIURI>* aBaseURI);
+  bool RecvSetBaseURI(nsIURI* aBaseURI);
+  bool RecvGetScrollRestorationIsManual(bool* aScrollRestorationIsManual);
+  bool RecvSetScrollRestorationIsManual(const bool& aScrollRestorationIsManual);
+  bool RecvGetLoadedInThisProcess(bool* aLoadedInThisProcess);
+  bool RecvGetLastTouched(uint32_t* aLastTouched);
+  bool RecvSetLastTouched(const uint32_t& aLastTouched);
+  bool RecvGetChildCount(int32_t* aChildCount);
+  bool RecvGetPersist(bool* aPersist);
+  bool RecvSetPersist(const bool& aPersist);
+  bool RecvGetScrollPosition(int32_t* aX, int32_t* aY);
+  bool RecvSetScrollPosition(const int32_t& aX, const int32_t& aY);
+  bool RecvGetViewerBounds(nsIntRect* aBounds);
+  bool RecvSetViewerBounds(const nsIntRect& aBounds);
+  bool RecvCreate(nsIURI* aURI, const nsString& aTitle,
+                  nsIInputStream* aInputStream, const uint32_t& aCacheKey,
+                  const nsCString& aContentType,
+                  nsIPrincipal* aTriggeringPrincipal,
+                  nsIPrincipal* aPrincipalToInherit,
+                  nsIPrincipal* aStoragePrincipalToInherit,
+                  nsIContentSecurityPolicy* aCsp, const nsID& aDocshellID,
+                  const bool& aDynamicCreation, nsIURI* aOriginalURI,
+                  nsIURI* aResultPrincipalURI, const bool& aLoadReplace,
+                  nsIReferrerInfo* aReferrerInfo, const nsAString& srcdoc,
+                  const bool& srcdocEntry, nsIURI* aBaseURI,
+                  const bool& aSaveLayoutState, const bool& aExpired);
+  bool RecvClone(PSHEntryParent** aCloneEntry);
+  bool RecvHasDetachedEditor(bool* aHasDetachedEditor);
+  bool RecvIsDynamicallyAdded(bool* aIsDynamicallyAdded);
+  bool RecvHasDynamicallyAddedChild(bool* aHasDynamicallyAddedChild);
+  bool RecvDocshellID(nsID* aDocshellID);
+  bool RecvAdoptBFCacheEntry(PSHEntryParent* aEntry, nsresult* aResult);
+  bool RecvAbandonBFCacheEntry(const uint64_t& aNewSharedID);
+  bool RecvSharesDocumentWith(PSHEntryParent* aEntry, bool* aSharesDocumentWith,
+                              nsresult* aResult);
+  bool RecvSetLoadTypeAsHistory();
+  bool RecvAddChild(PSHEntryParent* aChild, const int32_t& aOffset,
+                    const bool& aUseRemoteSubframes, nsresult* aResult);
+  bool RecvRemoveChild(PSHEntryParent* aChild, nsresult* aResult);
+  bool RecvGetChildAt(const int32_t& aIndex,
+                      RefPtr<CrossProcessSHEntry>* aChild);
+  bool RecvGetChildSHEntryIfHasNoDynamicallyAddedChild(
+      const int32_t& aChildOffset, RefPtr<CrossProcessSHEntry>* aChild);
+  bool RecvReplaceChild(PSHEntryParent* aNewChild, nsresult* aResult);
+  bool RecvClearEntry(const uint64_t& aNewSharedID);
+
+  bool RecvCreateLoadInfo(RefPtr<nsDocShellLoadState>* aLoadState);
+  bool RecvClone(RefPtr<CrossProcessSHEntry>* aResult);
+
+  bool RecvSyncTreesForSubframeNavigation(
+      PSHEntryParent* aSHEntry, const MaybeDiscarded<BrowsingContext>& aBC,
+      const MaybeDiscarded<BrowsingContext>& aIgnoreBC,
+      nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate);
+
+  bool RecvUpdateLayoutHistoryState(const bool& aScrollPositionOnly,
+                                    nsTArray<nsCString>&& aKeys,
+                                    nsTArray<PresState>&& aStates);
+
+  RefPtr<LegacySHEntry> mEntry;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif /* mozilla_dom_SHEntryParent_h */
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHistoryChild.cpp
@@ -0,0 +1,470 @@
+/* -*- 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 "SHistoryChild.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PSHistoryChild.h"
+#include "nsDocShell.h"
+#include "nsISHistoryListener.h"
+#include "SHEntryChild.h"
+
+namespace mozilla {
+namespace dom {
+class SwapEntriesDocshellData;
+}
+}  // namespace mozilla
+#define CONTENT_VIEWER_TIMEOUT_SECONDS \
+  "browser.sessionhistory.contentViewerTimeout"
+
+// Default this to time out unused content viewers after 30 minutes
+#define CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT (30 * 60)
+
+namespace mozilla {
+namespace dom {
+
+void SHistoryChild::HistoryTracker::NotifyExpired(SHEntryChildShared* aObj) {
+  RemoveObject(aObj);
+  mSHistory->EvictExpiredContentViewerForEntry(aObj);
+}
+
+SHistoryChild::SHistoryChild(BrowsingContext* aRootBC)
+    : mRootDocShell(static_cast<nsDocShell*>(aRootBC->GetDocShell())),
+      mIPCActorDeleted(false) {
+  // Bind mHistoryTracker's event target to the tabGroup for aRootBC.
+  // Maybe move this to ChildSHistory?
+  nsCOMPtr<nsPIDOMWindowOuter> win = aRootBC->GetDOMWindow();
+  if (win) {
+    // Seamonkey moves shistory between <xul:browser>s when restoring a tab.
+    // Let's try not to break our friend too badly...
+    if (mHistoryTracker) {
+      NS_WARNING(
+          "Change the root docshell of a shistory is unsafe and "
+          "potentially problematic.");
+      mHistoryTracker->AgeAllGenerations();
+    }
+
+    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(win);
+
+    mHistoryTracker = mozilla::MakeUnique<SHistoryChild::HistoryTracker>(
+        this,
+        mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS,
+                                      CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT),
+        global->EventTargetFor(mozilla::TaskCategory::Other));
+  }
+}
+
+NS_IMPL_ADDREF(SHistoryChild)
+NS_IMETHODIMP_(MozExternalRefCountType) SHistoryChild::Release() {
+  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+  NS_ASSERT_OWNINGTHREAD(SHEntryChild);
+  nsrefcnt count = --mRefCnt;
+  NS_LOG_RELEASE(this, count, "SHistoryChild");
+  if (count == 0) {
+    mRefCnt = 1; /* stabilize */
+    delete this;
+    return 0;
+  }
+  if (count == 1 && !mIPCActorDeleted) {
+    Unused << Send__delete__(this);
+  }
+  return count;
+}
+NS_IMPL_QUERY_INTERFACE(SHistoryChild, nsISHistory, nsISupportsWeakReference)
+
+NS_IMETHODIMP
+SHistoryChild::GetCount(int32_t* aCount) {
+  return SendGetCount(aCount) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHistoryChild::GetIndex(int32_t* aIndex) {
+  return SendGetIndex(aIndex) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHistoryChild::SetIndex(int32_t aIndex) {
+  nsresult rv;
+  return SendSetIndex(aIndex, &rv) ? rv : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHistoryChild::GetRequestedIndex(int32_t* aRequestedIndex) {
+  return SendGetRequestedIndex(aRequestedIndex) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::InternalSetRequestedIndex(int32_t aRequestedIndex) {
+  SendInternalSetRequestedIndex(aRequestedIndex);
+}
+
+NS_IMETHODIMP
+SHistoryChild::GetEntryAtIndex(int32_t aIndex, nsISHEntry** aResult) {
+  nsresult rv;
+  RefPtr<CrossProcessSHEntry> entry;
+  if (!SendGetEntryAtIndex(aIndex, &rv, &entry)) {
+    return NS_ERROR_FAILURE;
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aResult = entry ? do_AddRef(entry->ToSHEntryChild()).take() : nullptr;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::PurgeHistory(int32_t aNumEntries) {
+  nsresult rv;
+  if (!SendPurgeHistory(aNumEntries, &rv)) {
+    return NS_ERROR_FAILURE;
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mRootDocShell) {
+    mRootDocShell->HistoryPurged(aNumEntries);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::AddSHistoryListener(nsISHistoryListener* aListener) {
+  NS_ENSURE_ARG_POINTER(aListener);
+
+  // Check if the listener supports Weak Reference. This is a must.
+  // This listener functionality is used by embedders and we want to
+  // have the right ownership with who ever listens to SHistory
+  nsWeakPtr listener = do_GetWeakReference(aListener);
+  if (!listener) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mListeners.AppendElementUnlessExists(listener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::RemoveSHistoryListener(nsISHistoryListener* aListener) {
+  // Make sure the listener that wants to be removed is the
+  // one we have in store.
+  nsWeakPtr listener = do_GetWeakReference(aListener);
+  mListeners.RemoveElement(listener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::ReloadCurrentEntry() {
+  LoadSHEntryResult loadResult;
+  if (!SendReloadCurrentEntry(&loadResult)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (loadResult.type() == LoadSHEntryResult::Tnsresult) {
+    return loadResult;
+  }
+
+  return LoadURI(loadResult);
+}
+
+NS_IMETHODIMP
+SHistoryChild::GotoIndex(int32_t aIndex) {
+  LoadSHEntryResult loadResult;
+  if (!SendGotoIndex(aIndex, &loadResult)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (loadResult.type() == LoadSHEntryResult::Tnsresult) {
+    return loadResult;
+  }
+
+  return LoadURI(loadResult);
+}
+
+NS_IMETHODIMP_(int32_t)
+SHistoryChild::GetIndexOfEntry(nsISHEntry* aEntry) {
+  int32_t index;
+  if (!SendGetIndexOfEntry(static_cast<SHEntryChild*>(aEntry), &index)) {
+    return 0;
+  }
+  return index;
+}
+
+NS_IMETHODIMP
+SHistoryChild::AddEntry(nsISHEntry* aEntry, bool aPersist) {
+  NS_ENSURE_ARG(aEntry);
+
+  nsresult rv;
+  int32_t entriesPurged;
+  if (!SendAddEntry(static_cast<SHEntryChild*>(aEntry), aPersist, &rv,
+                    &entriesPurged)) {
+    return NS_ERROR_FAILURE;
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mRootDocShell) {
+    aEntry->SetDocshellID(mRootDocShell->HistoryID());
+
+    if (entriesPurged > 0) {
+      mRootDocShell->HistoryPurged(entriesPurged);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::ClearRootBrowsingContext() { mRootDocShell = nullptr; }
+
+NS_IMETHODIMP
+SHistoryChild::UpdateIndex(void) {
+  return SendUpdateIndex() ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHistoryChild::ReplaceEntry(int32_t aIndex, nsISHEntry* aReplaceEntry) {
+  nsresult rv;
+  if (!SendReplaceEntry(aIndex, static_cast<SHEntryChild*>(aReplaceEntry),
+                        &rv)) {
+    return NS_ERROR_FAILURE;
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::NotifyOnHistoryReload(bool* _retval) {
+  return SendNotifyOnHistoryReload(_retval) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHistoryChild::EvictOutOfRangeContentViewers(int32_t aIndex) {
+  // FIXME Need to get out of range entries and entries that are safe (to
+  //       compare content viewers so we don't evict live content viewers).
+  return SendEvictOutOfRangeContentViewers(aIndex) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+SHistoryChild::EvictExpiredContentViewerForEntry(nsIBFCacheEntry* aBFEntry) {
+  SHEntryChildShared* shared = static_cast<SHEntryChildShared*>(aBFEntry);
+
+  RefPtr<CrossProcessSHEntry> entry;
+  int32_t index;
+  if (!SendFindEntryForBFCache(shared->GetID(), false, &entry, &index)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<SHEntryChild> shEntry;
+  if (entry && (shEntry = entry->ToSHEntryChild())) {
+    shEntry->EvictContentViewer();
+    SendEvict(nsTArray<PSHEntryChild*>({shEntry.get()}));
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::EvictAllContentViewers(void) {
+  nsTArray<RefPtr<CrossProcessSHEntry>> entries;
+  if (!SendGetAllEntries(&entries)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Keep a strong reference to all the entries, we're going to send the array
+  // back to the parent!
+  nsTArray<RefPtr<SHEntryChild>> shEntries(entries.Length());
+  for (RefPtr<CrossProcessSHEntry>& entry : entries) {
+    RefPtr<SHEntryChild> shEntry = entry->ToSHEntryChild();
+    shEntry->EvictContentViewer();
+    shEntries.AppendElement(shEntry.forget());
+  }
+
+  nsTArray<PSHEntryChild*> pshEntries;
+  pshEntries.AppendElements(shEntries);
+  SendEvict(pshEntries);
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::EvictContentViewersOrReplaceEntry(nsISHEntry* aNewSHEntry,
+                                                 bool aReplace) {
+  SendEvictContentViewersOrReplaceEntry(static_cast<SHEntryChild*>(aNewSHEntry),
+                                        aReplace);
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::AddToExpirationTracker(nsIBFCacheEntry* aBFEntry) {
+  RefPtr<SHEntryChildShared> entry = static_cast<SHEntryChildShared*>(aBFEntry);
+  if (mHistoryTracker && entry) {
+    mHistoryTracker->AddObject(entry);
+  }
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::RemoveFromExpirationTracker(nsIBFCacheEntry* aBFEntry) {
+  RefPtr<SHEntryChildShared> entry = static_cast<SHEntryChildShared*>(aBFEntry);
+  MOZ_ASSERT(mHistoryTracker && !mHistoryTracker->IsEmpty());
+  if (mHistoryTracker && entry) {
+    mHistoryTracker->RemoveObject(entry);
+  }
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::RemoveDynEntries(int32_t aIndex, nsISHEntry* aEntry) {
+  SendRemoveDynEntries(aIndex, static_cast<SHEntryChild*>(aEntry));
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::EnsureCorrectEntryAtCurrIndex(nsISHEntry* aEntry) {
+  SendEnsureCorrectEntryAtCurrIndex(static_cast<SHEntryChild*>(aEntry));
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::RemoveDynEntriesForBFCacheEntry(nsIBFCacheEntry* aBFEntry) {
+  RefPtr<CrossProcessSHEntry> entry;
+  int32_t index;
+  if (!SendFindEntryForBFCache(
+          static_cast<SHEntryChildShared*>(aBFEntry)->GetID(), true, &entry,
+          &index)) {
+    return;
+  }
+
+  RefPtr<SHEntryChild> shEntry;
+  if (entry && (shEntry = entry->ToSHEntryChild())) {
+    RemoveDynEntries(index, shEntry);
+  }
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex) {
+  bool didRemove = false;
+  if (SendRemoveEntries(aIDs, aStartIndex, &didRemove) && didRemove &&
+      mRootDocShell) {
+    mRootDocShell->DispatchLocationChangeEvent();
+  }
+}
+
+NS_IMETHODIMP_(void)
+SHistoryChild::RemoveFrameEntries(nsISHEntry* aEntry) {
+  SendRemoveFrameEntries(static_cast<SHEntryChild*>(aEntry));
+}
+
+NS_IMETHODIMP
+SHistoryChild::Reload(uint32_t aReloadFlags) {
+  LoadSHEntryResult loadResult;
+  if (!SendReload(aReloadFlags, &loadResult)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (loadResult.type() == LoadSHEntryResult::Tnsresult) {
+    return loadResult;
+  }
+
+  return LoadURI(loadResult);
+}
+
+NS_IMETHODIMP
+SHistoryChild::CreateEntry(nsISHEntry** aEntry) {
+  uint64_t sharedID = SHEntryChildShared::CreateSharedID();
+  RefPtr<SHEntryChild> entry = static_cast<SHEntryChild*>(
+      Manager()->SendPSHEntryConstructor(this, sharedID));
+  if (!entry) {
+    return NS_ERROR_FAILURE;
+  }
+  entry.forget(aEntry);
+  return NS_OK;
+}
+
+nsresult SHistoryChild::LoadURI(nsTArray<LoadSHEntryData>& aLoadData) {
+  for (LoadSHEntryData& l : aLoadData) {
+    if (l.browsingContext().IsNullOrDiscarded()) {
+      continue;
+    }
+
+    nsCOMPtr<nsIDocShell> docShell = l.browsingContext().get()->GetDocShell();
+    if (!docShell) {
+      continue;
+    }
+
+    RefPtr<SHEntryChild> entry;
+    if (l.shEntry()) {
+      entry = l.shEntry()->ToSHEntryChild();
+    }
+
+    // FIXME Should this be sent through IPC?
+    l.loadState()->SetSHEntry(entry);
+    docShell->LoadURI(l.loadState(), false);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SHistoryChild::AddToRootSessionHistory(bool aCloneChildren, nsISHEntry* aOSHE,
+                                       BrowsingContext* aBC, nsISHEntry* aEntry,
+                                       uint32_t aLoadType, bool aShouldPersist,
+                                       Maybe<int32_t>* aPreviousEntryIndex,
+                                       Maybe<int32_t>* aLoadedEntryIndex) {
+  nsresult rv;
+  int32_t entriesPurged;
+  nsTArray<SwapEntriesDocshellData> entriesToUpdate;
+  if (!SendAddToRootSessionHistory(
+          aCloneChildren, static_cast<SHEntryChild*>(aOSHE), aBC,
+          static_cast<SHEntryChild*>(aEntry), aLoadType, aShouldPersist,
+          aPreviousEntryIndex, aLoadedEntryIndex, &entriesToUpdate,
+          &entriesPurged, &rv)) {
+    return NS_ERROR_FAILURE;
+  }
+  for (auto& data : entriesToUpdate) {
+    MOZ_ASSERT(!data.context().IsNull(), "Browsing context cannot be null");
+    nsDocShell* docshell = static_cast<nsDocShell*>(
+        data.context().GetMaybeDiscarded()->GetDocShell());
+    if (docshell) {
+      docshell->SwapHistoryEntries(data.oldEntry()->ToSHEntryChild(),
+                                   data.newEntry()->ToSHEntryChild());
+    }
+  }
+  if (NS_SUCCEEDED(rv) && mRootDocShell && entriesPurged > 0) {
+    mRootDocShell->HistoryPurged(entriesPurged);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+SHistoryChild::AddChildSHEntryHelper(nsISHEntry* aCloneRef,
+                                     nsISHEntry* aNewEntry,
+                                     BrowsingContext* aBC,
+                                     bool aCloneChildren) {
+  nsresult rv;
+  RefPtr<CrossProcessSHEntry> child;
+  int32_t entriesPurged;
+  nsTArray<SwapEntriesDocshellData> entriesToUpdate;
+  if (!SendAddChildSHEntryHelper(static_cast<SHEntryChild*>(aCloneRef),
+                                 static_cast<SHEntryChild*>(aNewEntry), aBC,
+                                 aCloneChildren, &entriesToUpdate,
+                                 &entriesPurged, &child, &rv)) {
+    return NS_ERROR_FAILURE;
+  }
+  for (auto& data : entriesToUpdate) {
+    MOZ_ASSERT(!data.context().IsNull(), "Browsing context cannot be null");
+    nsDocShell* docshell = static_cast<nsDocShell*>(
+        data.context().GetMaybeDiscarded()->GetDocShell());
+    if (docshell) {
+      docshell->SwapHistoryEntries(data.oldEntry()->ToSHEntryChild(),
+                                   data.newEntry()->ToSHEntryChild());
+    }
+  }
+  if (!child) {
+    return rv;
+  }
+  if (NS_SUCCEEDED(rv) && mRootDocShell && entriesPurged > 0) {
+    mRootDocShell->HistoryPurged(entriesPurged);
+  }
+
+  return rv;
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHistoryChild.h
@@ -0,0 +1,88 @@
+/* -*- 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_SHistoryChild_h
+#define mozilla_dom_SHistoryChild_h
+
+#include "mozilla/dom/PSHistoryChild.h"
+#include "nsExpirationTracker.h"
+#include "nsISHistory.h"
+#include "nsWeakReference.h"
+
+class nsIDocShell;
+
+namespace mozilla {
+namespace dom {
+
+class LoadSHEntryData;
+class SHEntryChildShared;
+
+/**
+ * Session history actor for the child process.
+ */
+class SHistoryChild final : public PSHistoryChild,
+                            public nsISHistory,
+                            public nsSupportsWeakReference {
+  friend class PSHistoryChild;
+
+ public:
+  explicit SHistoryChild(BrowsingContext* aBrowsingContext);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISHISTORY
+
+ protected:
+  void ActorDestroy(ActorDestroyReason aWhy) override {
+    mIPCActorDeleted = true;
+  }
+
+ private:
+  bool RecvReloadCurrentEntryFromChild() {
+    ReloadCurrentEntry();
+    return true;
+  }
+
+  // The timer based history tracker is used to evict bfcache on expiration.
+  class HistoryTracker final
+      : public nsExpirationTracker<SHEntryChildShared, 3> {
+   public:
+    explicit HistoryTracker(SHistoryChild* aSHistory, uint32_t aTimeout,
+                            nsIEventTarget* aEventTarget)
+        : nsExpirationTracker(1000 * aTimeout / 2, "HistoryTracker",
+                              aEventTarget),
+          mSHistory(aSHistory) {
+      MOZ_ASSERT(aSHistory);
+      mSHistory = aSHistory;
+    }
+
+   protected:
+    void NotifyExpired(SHEntryChildShared* aObj) override;
+
+   private:
+    // HistoryTracker is owned by SHistoryChild; it always outlives
+    // HistoryTracker so it's safe to use raw pointer here.
+    SHistoryChild* mSHistory;
+  };
+
+  ~SHistoryChild() = default;
+
+  nsresult LoadURI(nsTArray<LoadSHEntryData>& aLoadData);
+
+  // Track all bfcache entries and evict on expiration.
+  mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
+
+  // Session History listeners
+  nsAutoTObserverArray<nsWeakPtr, 2> mListeners;
+
+  WeakPtr<nsDocShell> mRootDocShell;
+
+  bool mIPCActorDeleted;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif /* mozilla_dom_SHistoryChild_h */
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHistoryParent.cpp
@@ -0,0 +1,436 @@
+/* -*- 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 "SHistoryParent.h"
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/SHEntryParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/PContentParent.h"
+#include "mozilla/Logging.h"
+#include "nsTHashtable.h"
+#include "SHEntryChild.h"
+
+extern mozilla::LazyLogModule gSHistoryLog;
+
+namespace mozilla {
+namespace dom {
+
+LegacySHistory::LegacySHistory(SHistoryParent* aSHistoryParent,
+                               CanonicalBrowsingContext* aRootBC,
+                               const nsID& aDocShellID)
+    : nsSHistory(aRootBC, aDocShellID), mSHistoryParent(aSHistoryParent) {
+  mIsRemote = true;
+  aRootBC->SetSessionHistory(this);
+}
+
+static void FillInLoadResult(
+    nsresult aRv, const nsTArray<nsSHistory::LoadEntryResult>& aLoadResults,
+    LoadSHEntryResult* aResult) {
+  if (NS_SUCCEEDED(aRv)) {
+    nsTArray<LoadSHEntryData> data;
+    data.SetCapacity(aLoadResults.Length());
+    for (const nsSHistory::LoadEntryResult& l : aLoadResults) {
+      data.AppendElement(
+          LoadSHEntryData(static_cast<LegacySHEntry*>(l.mLoadState->SHEntry()),
+                          l.mBrowsingContext, l.mLoadState));
+    }
+
+    *aResult = data;
+  } else {
+    *aResult = aRv;
+  }
+}
+
+SHistoryParent::SHistoryParent(CanonicalBrowsingContext* aContext)
+    : mHistory(new LegacySHistory(this, aContext, nsID())) {}
+
+SHistoryParent::~SHistoryParent() { mHistory->mSHistoryParent = nullptr; }
+
+SHEntryParent* SHistoryParent::CreateEntry(PContentParent* aContentParent,
+                                           PSHistoryParent* aSHistoryParent,
+                                           uint64_t aSharedID) {
+  RefPtr<LegacySHEntry> entry = new LegacySHEntry(
+      aContentParent, static_cast<SHistoryParent*>(aSHistoryParent)->mHistory,
+      aSharedID);
+  return entry->CreateActor();
+}
+
+void SHistoryParent::ActorDestroy(ActorDestroyReason aWhy) {}
+
+bool SHistoryParent::RecvGetCount(int32_t* aCount) {
+  return NS_SUCCEEDED(mHistory->GetCount(aCount));
+}
+
+bool SHistoryParent::RecvGetIndex(int32_t* aIndex) {
+  return NS_SUCCEEDED(mHistory->GetIndex(aIndex));
+}
+
+bool SHistoryParent::RecvSetIndex(int32_t aIndex, nsresult* aResult) {
+  *aResult = mHistory->SetIndex(aIndex);
+  return true;
+}
+
+bool SHistoryParent::RecvGetRequestedIndex(int32_t* aIndex) {
+  return NS_SUCCEEDED(mHistory->GetRequestedIndex(aIndex));
+}
+
+bool SHistoryParent::RecvInternalSetRequestedIndex(int32_t aIndex) {
+  mHistory->InternalSetRequestedIndex(aIndex);
+  return true;
+}
+
+bool SHistoryParent::RecvGetEntryAtIndex(int32_t aIndex, nsresult* aResult,
+                                         RefPtr<CrossProcessSHEntry>* aEntry) {
+  nsCOMPtr<nsISHEntry> entry;
+  *aResult = mHistory->GetEntryAtIndex(aIndex, getter_AddRefs(entry));
+  *aEntry = entry.forget().downcast<LegacySHEntry>();
+  return true;
+}
+
+bool SHistoryParent::RecvPurgeHistory(int32_t aNumEntries, nsresult* aResult) {
+  *aResult = mHistory->PurgeHistory(aNumEntries);
+  return true;
+}
+
+bool SHistoryParent::RecvReloadCurrentEntry(LoadSHEntryResult* aLoadResult) {
+  nsTArray<nsSHistory::LoadEntryResult> loadResults;
+  nsresult rv = mHistory->ReloadCurrentEntry(loadResults);
+  if (NS_SUCCEEDED(rv)) {
+    FillInLoadResult(rv, loadResults, aLoadResult);
+  } else {
+    *aLoadResult = rv;
+  }
+  return true;
+}
+
+bool SHistoryParent::RecvGotoIndex(int32_t aIndex,
+                                   LoadSHEntryResult* aLoadResult) {
+  nsTArray<nsSHistory::LoadEntryResult> loadResults;
+  nsresult rv = mHistory->GotoIndex(aIndex, loadResults);
+  FillInLoadResult(rv, loadResults, aLoadResult);
+  return true;
+}
+
+bool SHistoryParent::RecvGetIndexOfEntry(PSHEntryParent* aEntry,
+                                         int32_t* aIndex) {
+  MOZ_ASSERT(Manager() == aEntry->Manager());
+  *aIndex =
+      mHistory->GetIndexOfEntry(static_cast<SHEntryParent*>(aEntry)->mEntry);
+  return true;
+}
+
+bool SHistoryParent::RecvAddEntry(PSHEntryParent* aEntry, bool aPersist,
+                                  nsresult* aResult, int32_t* aEntriesPurged) {
+  MOZ_ASSERT(Manager() == aEntry->Manager());
+  *aResult = mHistory->AddEntry(static_cast<SHEntryParent*>(aEntry)->mEntry,
+                                aPersist, aEntriesPurged);
+  return true;
+}
+
+bool SHistoryParent::RecvUpdateIndex() {
+  return NS_SUCCEEDED(mHistory->UpdateIndex());
+}
+
+bool SHistoryParent::RecvReplaceEntry(int32_t aIndex, PSHEntryParent* aEntry,
+                                      nsresult* aResult) {
+  MOZ_ASSERT(Manager() == aEntry->Manager());
+  *aResult = mHistory->ReplaceEntry(
+      aIndex, static_cast<SHEntryParent*>(aEntry)->mEntry);
+  return true;
+}
+
+bool SHistoryParent::RecvNotifyOnHistoryReload(bool* aOk) {
+  return NS_SUCCEEDED(mHistory->NotifyOnHistoryReload(aOk));
+}
+
+bool SHistoryParent::RecvEvictOutOfRangeContentViewers(int32_t aIndex) {
+  return NS_SUCCEEDED(mHistory->EvictOutOfRangeContentViewers(aIndex));
+}
+
+bool SHistoryParent::RecvEvictAllContentViewers() {
+  return NS_SUCCEEDED(mHistory->EvictAllContentViewers());
+}
+
+bool SHistoryParent::RecvEvictContentViewersOrReplaceEntry(
+    PSHEntryParent* aNewSHEntry, bool aReplace) {
+  MOZ_ASSERT(Manager() == aNewSHEntry->Manager());
+  mHistory->EvictContentViewersOrReplaceEntry(
+      aNewSHEntry ? static_cast<SHEntryParent*>(aNewSHEntry)->mEntry.get()
+                  : nullptr,
+      aReplace);
+  return true;
+}
+
+bool SHistoryParent::RecvRemoveDynEntries(int32_t aIndex,
+                                          PSHEntryParent* aEntry) {
+  MOZ_ASSERT(Manager() == aEntry->Manager());
+  mHistory->RemoveDynEntries(aIndex,
+                             static_cast<SHEntryParent*>(aEntry)->mEntry);
+  return true;
+}
+
+bool SHistoryParent::RecvEnsureCorrectEntryAtCurrIndex(PSHEntryParent* aEntry) {
+  mHistory->EnsureCorrectEntryAtCurrIndex(
+      static_cast<SHEntryParent*>(aEntry)->mEntry);
+  return true;
+}
+
+bool SHistoryParent::RecvRemoveEntries(nsTArray<nsID>&& aIds, int32_t aIndex,
+                                       bool* aDidRemove) {
+  mHistory->RemoveEntries(aIds, aIndex, aDidRemove);
+  return true;
+}
+
+bool SHistoryParent::RecvRemoveFrameEntries(PSHEntryParent* aEntry) {
+  mHistory->RemoveFrameEntries(
+      static_cast<SHEntryParent*>(aEntry)->GetSHEntry());
+  return true;
+}
+
+bool SHistoryParent::RecvReload(const uint32_t& aReloadFlags,
+                                LoadSHEntryResult* aLoadResult) {
+  nsTArray<nsSHistory::LoadEntryResult> loadResults;
+  nsresult rv = mHistory->Reload(aReloadFlags, loadResults);
+  if (NS_SUCCEEDED(rv) && loadResults.IsEmpty()) {
+    *aLoadResult = NS_OK;
+  } else {
+    FillInLoadResult(rv, loadResults, aLoadResult);
+  }
+  return true;
+}
+
+bool SHistoryParent::RecvGetAllEntries(
+    nsTArray<RefPtr<CrossProcessSHEntry>>* aEntries) {
+  nsTArray<nsCOMPtr<nsISHEntry>>& entries = mHistory->Entries();
+  uint32_t length = entries.Length();
+  aEntries->AppendElements(length);
+  for (uint32_t i = 0; i < length; ++i) {
+    aEntries->ElementAt(i) = static_cast<LegacySHEntry*>(entries[i].get());
+  }
+  return true;
+}
+
+bool SHistoryParent::RecvFindEntryForBFCache(
+    const uint64_t& aSharedID, const bool& aIncludeCurrentEntry,
+    RefPtr<CrossProcessSHEntry>* aEntry, int32_t* aIndex) {
+  int32_t currentIndex;
+  mHistory->GetIndex(&currentIndex);
+  int32_t startSafeIndex, endSafeIndex;
+  mHistory->WindowIndices(currentIndex, &startSafeIndex, &endSafeIndex);
+  for (int32_t i = startSafeIndex; i <= endSafeIndex; ++i) {
+    nsCOMPtr<nsISHEntry> entry;
+    nsresult rv = mHistory->GetEntryAtIndex(i, getter_AddRefs(entry));
+    NS_ENSURE_SUCCESS(rv, false);
+
+    RefPtr<LegacySHEntry> shEntry = entry.forget().downcast<LegacySHEntry>();
+    if (shEntry->GetSharedStateID() == aSharedID) {
+      if (!aIncludeCurrentEntry && i == currentIndex) {
+        *aEntry = nullptr;
+        *aIndex = -1;
+      } else {
+        *aEntry = std::move(shEntry);
+        *aIndex = i;
+      }
+
+      return true;
+    }
+  }
+  *aEntry = nullptr;
+  *aIndex = -1;
+  return true;
+}
+
+bool SHistoryParent::RecvEvict(nsTArray<PSHEntryParent*>&& aEntries) {
+  for (PSHEntryParent* entry : aEntries) {
+    int32_t index =
+        mHistory->GetIndexOfEntry(static_cast<SHEntryParent*>(entry)->mEntry);
+    if (index != -1) {
+      mHistory->RemoveDynEntries(index,
+                                 static_cast<SHEntryParent*>(entry)->mEntry);
+    }
+  }
+  return true;
+}
+
+bool SHistoryParent::RecvNotifyListenersContentViewerEvicted(
+    uint32_t aNumEvicted) {
+  mHistory->NotifyListenersContentViewerEvicted(aNumEvicted);
+  return true;
+}
+
+void LegacySHistory::EvictOutOfRangeWindowContentViewers(int32_t aIndex) {
+  if (aIndex < 0) {
+    return;
+  }
+  NS_ENSURE_TRUE_VOID(aIndex < Length());
+
+  // Calculate the range that's safe from eviction.
+  int32_t startSafeIndex, endSafeIndex;
+  WindowIndices(aIndex, &startSafeIndex, &endSafeIndex);
+
+  // See nsSHistory::EvictOutOfRangeWindowContentViewers for more comments.
+
+  MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug,
+          ("EvictOutOfRangeWindowContentViewers(index=%d), "
+           "Length()=%d. Safe range [%d, %d]",
+           aIndex, Length(), startSafeIndex, endSafeIndex));
+
+  // Collect content viewers within safe range so we don't accidentally evict
+  // one of them if it appears outside this range.
+  nsTHashtable<nsUint64HashKey> safeSharedStateIDs;
+  for (int32_t i = startSafeIndex; i <= endSafeIndex; i++) {
+    RefPtr<LegacySHEntry> entry =
+        static_cast<LegacySHEntry*>(mEntries[i].get());
+    MOZ_ASSERT(entry);
+    safeSharedStateIDs.PutEntry(entry->GetSharedStateID());
+  }
+
+  // Iterate over entries that are not within safe range and save the IDs
+  // of shared state and content parents into a hashtable.
+  // Format of the hashtable: content parent -> list of shared state IDs
+  nsDataHashtable<nsPtrHashKey<PContentParent>, nsTHashtable<nsUint64HashKey>>
+      toEvict;
+  for (int32_t i = 0; i < Length(); i++) {
+    if (i >= startSafeIndex && i <= endSafeIndex) {
+      continue;
+    }
+    RefPtr<LegacySHEntry> entry =
+        static_cast<LegacySHEntry*>(mEntries[i].get());
+    dom::SHEntrySharedParentState* sharedParentState = entry->GetSharedState();
+    uint64_t id = entry->GetSharedStateID();
+    PContentParent* parent =
+        static_cast<SHEntrySharedParent*>(sharedParentState)
+            ->GetContentParent();
+    MOZ_ASSERT(parent);
+    if (!safeSharedStateIDs.Contains(id)) {
+      nsTHashtable<nsUint64HashKey>& ids = toEvict.GetOrInsert(parent);
+      ids.PutEntry(id);
+    }
+  }
+  if (toEvict.Count() == 0) {
+    return;
+  }
+  // Remove dynamically created children from entries that will be evicted
+  // later. We are iterating over the mEntries (instead of toEvict) to gain
+  // access to each nsSHEntry because toEvict only contains content parents and
+  // IDs of SHEntrySharedParentState
+
+  // (It's important that the condition checks Length(), rather than a cached
+  // copy of Length(), because the length might change between iterations due to
+  // RemoveDynEntries call.)
+  for (int32_t i = 0; i < Length(); i++) {
+    RefPtr<LegacySHEntry> entry =
+        static_cast<LegacySHEntry*>(mEntries[i].get());
+    MOZ_ASSERT(entry);
+    uint64_t id = entry->GetSharedStateID();
+    if (!safeSharedStateIDs.Contains(id)) {
+      // When dropping bfcache, we have to remove associated dynamic entries as
+      // well.
+      int32_t index = GetIndexOfEntry(entry);
+      if (index != -1) {
+        RemoveDynEntries(index, entry);
+      }
+    }
+  }
+
+  // Iterate over the 'toEvict' hashtable to get the IDs of content viewers to
+  // evict for each parent
+  for (auto iter = toEvict.ConstIter(); !iter.Done(); iter.Next()) {
+    auto parent = iter.Key();
+    const nsTHashtable<nsUint64HashKey>& ids = iter.Data();
+
+    // Convert ids into an array because we don't have support for passing
+    // nsTHashtable over IPC
+    AutoTArray<uint64_t, 4> evictArray;
+    for (auto iter = ids.ConstIter(); !iter.Done(); iter.Next()) {
+      evictArray.AppendElement(iter.Get()->GetKey());
+    }
+
+    Unused << parent->SendEvictContentViewers(evictArray);
+  }
+}
+
+NS_IMETHODIMP
+LegacySHistory::CreateEntry(nsISHEntry** aEntry) {
+  NS_ENSURE_TRUE(mRootBC, NS_ERROR_FAILURE);
+
+  NS_ADDREF(*aEntry = new LegacySHEntry(
+                static_cast<CanonicalBrowsingContext*>(mRootBC)
+                    ->GetContentParent(),
+                this, SHEntryChildShared::CreateSharedID()));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LegacySHistory::ReloadCurrentEntry() {
+  return mSHistoryParent->SendReloadCurrentEntryFromChild() ? NS_OK
+                                                            : NS_ERROR_FAILURE;
+}
+
+bool SHistoryParent::RecvAddToRootSessionHistory(
+    bool aCloneChildren, PSHEntryParent* aOSHE,
+    const MaybeDiscarded<BrowsingContext>& aBC, PSHEntryParent* aEntry,
+    uint32_t aLoadType, bool aShouldPersist,
+    Maybe<int32_t>* aPreviousEntryIndex, Maybe<int32_t>* aLoadedEntryIndex,
+    nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate,
+    int32_t* aEntriesPurged, nsresult* aResult) {
+  MOZ_ASSERT(!aBC.IsNull(), "Browsing context cannot be null");
+  nsTArray<EntriesAndBrowsingContextData> entriesToSendOverIDL;
+  *aResult = mHistory->AddToRootSessionHistory(
+      aCloneChildren,
+      aOSHE ? static_cast<SHEntryParent*>(aOSHE)->mEntry.get() : nullptr,
+      aBC.GetMaybeDiscarded(),
+      static_cast<SHEntryParent*>(aEntry)->mEntry.get(), aLoadType,
+      aShouldPersist, static_cast<ContentParent*>(Manager())->ChildID(),
+      aPreviousEntryIndex, aLoadedEntryIndex, &entriesToSendOverIDL,
+      aEntriesPurged);
+  SHistoryParent::CreateActorsForSwapEntries(entriesToSendOverIDL,
+                                             aEntriesToUpdate, Manager());
+  return true;
+}
+
+bool SHistoryParent::RecvAddChildSHEntryHelper(
+    PSHEntryParent* aCloneRef, PSHEntryParent* aNewEntry,
+    const MaybeDiscarded<BrowsingContext>& aBC, bool aCloneChildren,
+    nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate,
+    int32_t* aEntriesPurged, RefPtr<CrossProcessSHEntry>* aChild,
+    nsresult* aResult) {
+  MOZ_ASSERT(!aBC.IsNull(), "Browsing context cannot be null");
+  nsCOMPtr<nsISHEntry> child;
+  nsTArray<EntriesAndBrowsingContextData> entriesToSendOverIPC;
+  *aResult = mHistory->AddChildSHEntryHelper(
+      static_cast<SHEntryParent*>(aCloneRef)->mEntry.get(),
+      aNewEntry ? static_cast<SHEntryParent*>(aNewEntry)->mEntry.get()
+                : nullptr,
+      aBC.GetMaybeDiscarded(), aCloneChildren,
+      static_cast<ContentParent*>(Manager())->ChildID(), &entriesToSendOverIPC,
+      aEntriesPurged, getter_AddRefs(child));
+  SHistoryParent::CreateActorsForSwapEntries(entriesToSendOverIPC,
+                                             aEntriesToUpdate, Manager());
+  *aChild = child.forget().downcast<LegacySHEntry>();
+  return true;
+}
+
+void SHistoryParent::CreateActorsForSwapEntries(
+    const nsTArray<EntriesAndBrowsingContextData>& aEntriesToSendOverIPC,
+    nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate,
+    PContentParent* aContentParent) {
+  for (auto& data : aEntriesToSendOverIPC) {
+    SwapEntriesDocshellData* toUpdate = aEntriesToUpdate->AppendElement();
+
+    // Old entry
+    toUpdate->oldEntry() = static_cast<LegacySHEntry*>(data.oldEntry.get());
+
+    // New entry
+    toUpdate->newEntry() = static_cast<LegacySHEntry*>(data.newEntry.get());
+    toUpdate->context() = data.context;
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/shistory/SHistoryParent.h
@@ -0,0 +1,130 @@
+/* -*- 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_SHistoryParent_h
+#define mozilla_dom_SHistoryParent_h
+
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/PSHistoryParent.h"
+#include "mozilla/RefPtr.h"
+#include "nsSHistory.h"
+
+namespace mozilla {
+namespace dom {
+
+class PSHEntryOrSharedID;
+class SHistoryParent;
+class SHEntryParent;
+
+/**
+ * Session history implementation based on the legacy implementation that used
+ * to live in the child process. Ideally this wouldn't implement nsISHistory
+ * (it should only ever be accessed by SHistoryParent).
+ */
+class LegacySHistory final : public nsSHistory {
+ private:
+  friend class SHistoryParent;
+  virtual ~LegacySHistory() = default;
+
+  void EvictOutOfRangeWindowContentViewers(int32_t aIndex) override;
+
+  SHistoryParent* mSHistoryParent;
+
+ public:
+  LegacySHistory(SHistoryParent* aSHistoryParent,
+                 CanonicalBrowsingContext* aRootBC, const nsID& aDocShellID);
+
+  NS_IMETHOD CreateEntry(nsISHEntry** aEntry) override;
+  using nsSHistory::ReloadCurrentEntry;
+  NS_IMETHOD ReloadCurrentEntry() override;
+
+  SHistoryParent* GetActor() { return mSHistoryParent; }
+};
+
+/**
+ * Session history actor for the parent process. Forwards to the legacy
+ * implementation that used to live in the child process (see LegacySHistory).
+ */
+class SHistoryParent final : public PSHistoryParent {
+  friend class LegacySHistory;
+  friend class PSHistoryParent;
+  friend class SHEntryParent;
+
+ public:
+  explicit SHistoryParent(CanonicalBrowsingContext* aContext);
+  virtual ~SHistoryParent();
+
+  static SHEntryParent* CreateEntry(PContentParent* aContentParent,
+                                    PSHistoryParent* aSHistoryParent,
+                                    uint64_t aSharedID);
+
+  // We have a list of entries we would like to return from
+  // the IPC call and we need to create actors for all of them
+  static void CreateActorsForSwapEntries(
+      const nsTArray<EntriesAndBrowsingContextData>& aEntriesToSendOverIPC,
+      nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate,
+      PContentParent* aContentParent);
+
+ protected:
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+  bool RecvGetCount(int32_t* aCount);
+  bool RecvGetIndex(int32_t* aIndex);
+  bool RecvSetIndex(int32_t aIndex, nsresult* aResult);
+  bool RecvGetRequestedIndex(int32_t* aIndex);
+  bool RecvInternalSetRequestedIndex(int32_t aIndex);
+  bool RecvGetEntryAtIndex(int32_t aIndex, nsresult* aResult,
+                           RefPtr<CrossProcessSHEntry>* aEntry);
+  bool RecvPurgeHistory(int32_t aNumEntries, nsresult* aResult);
+  bool RecvReloadCurrentEntry(LoadSHEntryResult* aLoadResult);
+  bool RecvGotoIndex(int32_t aIndex, LoadSHEntryResult* aLoadResult);
+  bool RecvGetIndexOfEntry(PSHEntryParent* aEntry, int32_t* aIndex);
+  bool RecvAddEntry(PSHEntryParent* aEntry, bool aPersist, nsresult* aResult,
+                    int32_t* aEntriesPurged);
+  bool RecvUpdateIndex();
+  bool RecvReplaceEntry(int32_t aIndex, PSHEntryParent* aEntry,
+                        nsresult* aResult);
+  bool RecvNotifyOnHistoryReload(bool* aOk);
+  bool RecvEvictOutOfRangeContentViewers(int32_t aIndex);
+  bool RecvEvictAllContentViewers();
+  bool RecvRemoveDynEntries(int32_t aIndex, PSHEntryParent* aEntry);
+  bool RecvRemoveEntries(nsTArray<nsID>&& ids, int32_t aIndex,
+                         bool* aDidRemove);
+  bool RecvRemoveFrameEntries(PSHEntryParent* aEntry);
+  bool RecvReload(const uint32_t& aReloadFlags, LoadSHEntryResult* aLoadResult);
+  bool RecvGetAllEntries(nsTArray<RefPtr<CrossProcessSHEntry>>* aEntries);
+  bool RecvFindEntryForBFCache(const uint64_t& aSharedID,
+                               const bool& aIncludeCurrentEntry,
+                               RefPtr<CrossProcessSHEntry>* aEntry,
+                               int32_t* aIndex);
+  bool RecvEvict(nsTArray<PSHEntryParent*>&& aEntries);
+  bool RecvEnsureCorrectEntryAtCurrIndex(PSHEntryParent* aEntry);
+  bool RecvEvictContentViewersOrReplaceEntry(PSHEntryParent* aNewSHEntry,
+                                             bool aReplace);
+  bool RecvNotifyListenersContentViewerEvicted(uint32_t aNumEvicted);
+  bool RecvAddToRootSessionHistory(
+      bool aCloneChildren, PSHEntryParent* aOSHE,
+      const MaybeDiscarded<BrowsingContext>& aBC, PSHEntryParent* aEntry,
+      uint32_t aLoadType, bool aShouldPersist,
+      Maybe<int32_t>* aPreviousEntryIndex, Maybe<int32_t>* aLoadedEntryIndex,
+      nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate,
+      int32_t* aEntriesPurged, nsresult* aResult);
+  bool RecvAddChildSHEntryHelper(
+      PSHEntryParent* aCloneRef, PSHEntryParent* aNewEntry,
+      const MaybeDiscarded<BrowsingContext>& aBC, bool aCloneChildren,
+      nsTArray<SwapEntriesDocshellData>* aEntriesToUpdate,
+      int32_t* aEntriesPurged, RefPtr<CrossProcessSHEntry>* aChild,
+      nsresult* aResult);
+
+  RefPtr<CanonicalBrowsingContext> mContext;
+  RefPtr<LegacySHistory> mHistory;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif /* mozilla_dom_SHistoryParent_h */
deleted file mode 100644
--- a/docshell/shistory/SessionHistoryEntry.cpp
+++ /dev/null
@@ -1,827 +0,0 @@
-/* -*- 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
deleted file mode 100644
--- a/docshell/shistory/SessionHistoryEntry.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- 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
@@ -8,33 +8,46 @@ XPIDL_SOURCES += [
     'nsIBFCacheEntry.idl',
     'nsISHEntry.idl',
     'nsISHistory.idl',
     'nsISHistoryListener.idl',
 ]
 
 XPIDL_MODULE = 'shistory'
 
+IPDL_SOURCES += [
+    'PSHEntry.ipdl',
+    'PSHistory.ipdl',
+]
+
 EXPORTS += [
     'nsSHEntry.h',
     'nsSHEntryShared.h',
     'nsSHistory.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'ChildSHistory.h',
-    'SessionHistoryEntry.h',
+    'MaybeNewPSHEntry.h',
+    'SHEntryChild.h',
+    'SHEntryParent.h',
+    'SHistoryChild.h',
+    'SHistoryParent.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChildSHistory.cpp',
+    'MaybeNewPSHEntry.cpp',
     'nsSHEntry.cpp',
     'nsSHEntryShared.cpp',
     'nsSHistory.cpp',
-    'SessionHistoryEntry.cpp',
+    'SHEntryChild.cpp',
+    'SHEntryParent.cpp',
+    'SHistoryChild.cpp',
+    'SHistoryParent.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/docshell/shistory/nsISHistory.idl
+++ b/docshell/shistory/nsISHistory.idl
@@ -146,16 +146,23 @@ interface nsISHistory: nsISupports
    * @param aPersist          If true this specifies that the entry should
    *                          persist in the list. If false, this means that
    *                          when new entries are added this element will not
    *                          appear in the session history list.
    */
   void addEntry(in nsISHEntry aEntry, in boolean aPersist);
 
   /**
+   * Clear the reference to the toplevel browsing context object that this
+   * SHistory object belongs to.
+   */
+  [noscript, notxpcom]
+  void ClearRootBrowsingContext();
+
+  /**
    * Update the index maintained by sessionHistory
    */
   void updateIndex();
 
   /**
    * Replace the nsISHEntry at a particular index
    *
    * @param aIndex            The index at which the entry should be replaced.
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -5,40 +5,40 @@
  * 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 "nsIContentViewer.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIInputStream.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIMutableArray.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIURI.h"
 #include "nsSHEntryShared.h"
 #include "nsSHistory.h"
+#include "SHEntryChild.h"
 
 #include "mozilla/Logging.h"
 #include "nsIReferrerInfo.h"
 
 extern mozilla::LazyLogModule gPageCacheLog;
 
 namespace dom = mozilla::dom;
 
 static uint32_t gEntryID = 0;
 
-nsSHEntry::nsSHEntry(nsISHistory* aSHistory)
-    : mShared(new nsSHEntryShared(aSHistory)),
+nsSHEntry::nsSHEntry(dom::SHEntrySharedParentState* aState)
+    : mShared(aState),
       mLoadType(0),
       mID(gEntryID++),
       mScrollPositionX(0),
       mScrollPositionY(0),
       mParent(nullptr),
       mLoadReplace(false),
       mURIWasModified(false),
       mIsSrcdocEntry(false),
@@ -169,16 +169,32 @@ nsSHEntry::GetReferrerInfo(nsIReferrerIn
 
 NS_IMETHODIMP
 nsSHEntry::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
   mReferrerInfo = aReferrerInfo;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSHEntry::SetContentViewer(nsIContentViewer* aViewer) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetContentViewer(nsIContentViewer** aResult) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSHEntry::SetSticky(bool aSticky) {
   mShared->mSticky = aSticky;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetSticky(bool* aSticky) {
   *aSticky = mShared->mSticky;
@@ -294,16 +310,32 @@ nsSHEntry::GetCacheKey(uint32_t* aResult
 
 NS_IMETHODIMP
 nsSHEntry::SetCacheKey(uint32_t aCacheKey) {
   mShared->mCacheKey = aCacheKey;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSHEntry::GetSaveLayoutStateFlag(bool* aFlag) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetSaveLayoutStateFlag(bool aFlag) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSHEntry::GetExpirationStatus(bool* aFlag) {
   *aFlag = mShared->mExpired;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::SetExpirationStatus(bool aFlag) {
   mShared->mExpired = aFlag;
@@ -365,21 +397,24 @@ nsSHEntry::Create(
   mBaseURI = aBaseURI;
 
   mLoadedInThisProcess = true;
 
   mOriginalURI = aOriginalURI;
   mResultPrincipalURI = aResultPrincipalURI;
   mLoadReplace = aLoadReplace;
   mReferrerInfo = aReferrerInfo;
-
-  mShared->mLayoutHistoryState = nullptr;
+  return NS_OK;
+}
 
-  mShared->mSaveLayoutState = aSaveLayoutState;
-
+NS_IMETHODIMP
+nsSHEntry::Clone(nsISHEntry** aResult) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetParent(nsISHEntry** aResult) {
   *aResult = mParent;
   NS_IF_ADDREF(*aResult);
   return NS_OK;
@@ -391,16 +426,32 @@ nsSHEntry::SetParent(nsISHEntry* aParent
    * 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) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetWindowState(nsISupports** aState) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
 NS_IMETHODIMP_(void)
 nsSHEntry::SetViewerBounds(const nsIntRect& aBounds) {
   mShared->mViewerBounds = aBounds;
 }
 
 NS_IMETHODIMP_(void)
 nsSHEntry::GetViewerBounds(nsIntRect& aBounds) {
   aBounds = mShared->mViewerBounds;
@@ -452,34 +503,50 @@ nsSHEntry::GetCsp(nsIContentSecurityPoli
 }
 
 NS_IMETHODIMP
 nsSHEntry::SetCsp(nsIContentSecurityPolicy* aCsp) {
   mShared->mCsp = aCsp;
   return NS_OK;
 }
 
+bool nsSHEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return false;
+}
+
 NS_IMETHODIMP
 nsSHEntry::AdoptBFCacheEntry(nsISHEntry* aEntry) {
-  nsSHEntryShared* shared = static_cast<nsSHEntry*>(aEntry)->mShared;
+  dom::SHEntrySharedParentState* 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::AbandonBFCacheEntry() {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSHEntry::GetIsSrcdocEntry(bool* aIsSrcdocEntry) {
   *aIsSrcdocEntry = mIsSrcdocEntry;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetSrcdocData(nsAString& aSrcdocData) {
   aSrcdocData = mSrcdocData;
@@ -736,17 +803,81 @@ nsSHEntry::ReplaceChild(nsISHEntry* aNew
 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_(void)
+nsSHEntry::AddChildShell(nsIDocShellTreeItem* aShell) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+}
+
+NS_IMETHODIMP
+nsSHEntry::ChildShellAt(int32_t aIndex, nsIDocShellTreeItem** aShell) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+nsSHEntry::ClearChildShells() {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetRefreshURIList(nsIMutableArray** aList) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetRefreshURIList(nsIMutableArray* aList) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(void)
+nsSHEntry::SyncPresentationState() {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+}
+
+nsDocShellEditorData* nsSHEntry::ForgetEditorData() {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return nullptr;
+}
+
+void nsSHEntry::SetEditorData(nsDocShellEditorData* aData) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+}
+
+bool nsSHEntry::HasDetachedEditor() {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return false;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetStateData(nsIStructuredCloneContainer** aContainer) {
   NS_IF_ADDREF(*aContainer = mStateData);
   return NS_OK;
 }
 
@@ -879,42 +1010,66 @@ nsSHEntry::CreateLoadInfo(nsDocShellLoad
 
   loadState->SetFirstParty(true);
   loadState->SetSHEntry(this);
 
   loadState.forget(aLoadState);
   return NS_OK;
 }
 
-NS_IMETHODIMP_(void)
-nsSHEntry::SyncTreesForSubframeNavigation(
-    nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aTopBC,
-    mozilla::dom::BrowsingContext* aIgnoreBC) {
+NS_IMETHODIMP
+nsSHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
+  MOZ_CRASH(
+      "Classes inheriting from nsSHEntry should implement this. "
+      "Bug 1546344 will clean this up.");
+  return NS_OK;
+}
+
+void nsSHEntry::SyncTreesForSubframeNavigation(
+    uint64_t aOtherPid, nsISHEntry* aEntry,
+    mozilla::dom::BrowsingContext* aTopBC,
+    mozilla::dom::BrowsingContext* aIgnoreBC,
+    nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate) {
+  MOZ_ASSERT(aEntriesToUpdate || aOtherPid == 0,
+             "our entries to update is null");
+
   // 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::SwapEntriesData data = {aIgnoreBC, newRootEntry, nullptr,
+                                          aOtherPid, aEntriesToUpdate};
       nsSHistory::SetChildHistoryEntry(oldRootEntry, aTopBC, 0, &data);
     }
   }
 }
 
+NS_IMETHODIMP_(void)
+nsSHEntry::SyncTreesForSubframeNavigation(
+    nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aTopBC,
+    mozilla::dom::BrowsingContext* aIgnoreBC) {
+  SyncTreesForSubframeNavigation(
+        0 /* unused, this will be set in SHEntryParent::RecvSyncTrees */,
+        aEntry, aTopBC, aIgnoreBC,
+        nullptr /* this will be given in SHEntryCHild::SyncTrees if we
+                are going over IPC, else, it is not needed */);
+}
+
 void nsSHEntry::EvictContentViewer() {
   nsCOMPtr<nsIContentViewer> viewer = GetContentViewer();
   if (viewer) {
     mShared->NotifyListenersContentViewerEvicted();
     // Drop the presentation state before destroying the viewer, so that
     // document teardown is able to correctly persist the state.
     SetContentViewer(nullptr);
     SyncPresentationState();
@@ -923,117 +1078,155 @@ void nsSHEntry::EvictContentViewer() {
 }
 
 NS_IMETHODIMP
 nsSHEntry::SynchronizeLayoutHistoryState() {
   // No-op on purpose. See nsISHEntry.idl
   return NS_OK;
 }
 
+nsLegacySHEntry::nsLegacySHEntry(nsISHistory* aHistory, uint64_t aID)
+    : nsSHEntry(new nsSHEntryShared(aHistory, aID)) {}
+
 NS_IMETHODIMP
-nsSHEntry::SetContentViewer(nsIContentViewer* aViewer) {
+nsLegacySHEntry::SetContentViewer(nsIContentViewer* aViewer) {
   return GetState()->SetContentViewer(aViewer);
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetContentViewer(nsIContentViewer** aResult) {
+nsLegacySHEntry::GetContentViewer(nsIContentViewer** aResult) {
   *aResult = GetState()->mContentViewer;
   NS_IF_ADDREF(*aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::Clone(nsISHEntry** aResult) {
-  nsCOMPtr<nsISHEntry> entry = new nsSHEntry(*this);
+nsLegacySHEntry::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& aSrcdocData,
+    bool aSrcdocEntry, nsIURI* aBaseURI, bool aSaveLayoutState, bool aExpired) {
+  mShared->mLayoutHistoryState = nullptr;
+
+  mShared->mSaveLayoutState = aSaveLayoutState;
+
+  return nsSHEntry::Create(aURI, aTitle, aInputStream, aCacheKey, aContentType,
+                           aTriggeringPrincipal, aPrincipalToInherit,
+                           aStoragePrincipalToInherit, aCsp, aDocShellID,
+                           aDynamicCreation, aOriginalURI, aResultPrincipalURI,
+                           aLoadReplace, aReferrerInfo, aSrcdocData,
+                           aSrcdocEntry, aBaseURI, aSaveLayoutState, aExpired);
+}
+
+NS_IMETHODIMP
+nsLegacySHEntry::Clone(nsISHEntry** aResult) {
+  nsCOMPtr<nsISHEntry> entry = new nsLegacySHEntry(*this);
   entry.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetSaveLayoutStateFlag(bool* aFlag) {
+nsLegacySHEntry::GetSaveLayoutStateFlag(bool* aFlag) {
   *aFlag = mShared->mSaveLayoutState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetSaveLayoutStateFlag(bool aFlag) {
+nsLegacySHEntry::SetSaveLayoutStateFlag(bool aFlag) {
   mShared->mSaveLayoutState = aFlag;
   if (mShared->mLayoutHistoryState) {
     mShared->mLayoutHistoryState->SetScrollPositionOnly(!aFlag);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetWindowState(nsISupports* aState) {
+nsLegacySHEntry::SetWindowState(nsISupports* aState) {
   GetState()->mWindowState = aState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetWindowState(nsISupports** aState) {
+nsLegacySHEntry::GetWindowState(nsISupports** aState) {
   NS_IF_ADDREF(*aState = GetState()->mWindowState);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetRefreshURIList(nsIMutableArray** aList) {
+nsLegacySHEntry::GetRefreshURIList(nsIMutableArray** aList) {
   NS_IF_ADDREF(*aList = GetState()->mRefreshURIList);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetRefreshURIList(nsIMutableArray* aList) {
+nsLegacySHEntry::SetRefreshURIList(nsIMutableArray* aList) {
   GetState()->mRefreshURIList = aList;
   return NS_OK;
 }
 
 NS_IMETHODIMP_(void)
-nsSHEntry::AddChildShell(nsIDocShellTreeItem* aShell) {
+nsLegacySHEntry::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) {
+nsLegacySHEntry::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(); }
+nsLegacySHEntry::ClearChildShells() { GetState()->mChildShells.Clear(); }
 
 NS_IMETHODIMP_(void)
-nsSHEntry::SyncPresentationState() { GetState()->SyncPresentationState(); }
+nsLegacySHEntry::SyncPresentationState() {
+  GetState()->SyncPresentationState();
+}
 
-nsDocShellEditorData* nsSHEntry::ForgetEditorData() {
+nsDocShellEditorData* nsLegacySHEntry::ForgetEditorData() {
   // XXX jlebar Check how this is used.
   return GetState()->mEditorData.release();
 }
 
-void nsSHEntry::SetEditorData(nsDocShellEditorData* aData) {
+void nsLegacySHEntry::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() {
+bool nsLegacySHEntry::HasDetachedEditor() {
   return GetState()->mEditorData != nullptr;
 }
 
-bool nsSHEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry) {
+bool nsLegacySHEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry) {
   return static_cast<nsIBFCacheEntry*>(GetState()) == aEntry;
 }
 
 NS_IMETHODIMP
-nsSHEntry::AbandonBFCacheEntry() {
-  mShared = GetState()->Duplicate();
+nsLegacySHEntry::AbandonBFCacheEntry() {
+  mShared =
+      GetState()->Duplicate(mozilla::dom::SHEntryChildShared::CreateSharedID());
   return NS_OK;
 }
 
+NS_IMETHODIMP_(void)
+nsLegacySHEntry::ClearEntry() {
+  nsSHEntry::ClearEntry();
+  AbandonBFCacheEntry();
+}
+
 NS_IMETHODIMP
-nsSHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
+nsLegacySHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
   *aBFCacheID = mShared->GetID();
   return NS_OK;
 }
+
+nsSHEntryShared* nsLegacySHEntry::GetState() {
+  return static_cast<nsSHEntryShared*>(mShared.get());
+}
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -9,42 +9,64 @@
 
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsISHEntry.h"
 #include "nsString.h"
 
 #include "mozilla/Attributes.h"
 
+namespace mozilla {
+namespace dom {
+
+class SHEntrySharedChildState;
+class SHEntrySharedParentState;
+
+}  // namespace dom
+}  // namespace mozilla
+
 class nsSHEntryShared;
 class nsIInputStream;
 class nsIURI;
 class nsIReferrerInfo;
 
+// Structure for passing around entries via XPCOM from methods such as
+// nsSHistory::CloneAndReplaceChild, nsSHistory::SetChildHistoryEntry and
+// nsSHEntry::SyncTreesForSubframeNavigation, that need to swap entries in
+// docshell, to corresponding Recv methods so that we don't have to create
+// actors until we are about to return from the parent process
+struct EntriesAndBrowsingContextData {
+  nsCOMPtr<nsISHEntry> oldEntry;
+  nsCOMPtr<nsISHEntry> newEntry;
+  mozilla::dom::BrowsingContext* context;
+};
+
 class nsSHEntry : public nsISHEntry {
  public:
-  explicit nsSHEntry(nsISHistory* aSHistory);
-
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHENTRY
 
   virtual void EvictContentViewer();
 
   static nsresult Startup();
   static void Shutdown();
+  void SyncTreesForSubframeNavigation(
+      uint64_t aOtherPid, nsISHEntry* aEntry,
+      mozilla::dom::BrowsingContext* aTopBC,
+      mozilla::dom::BrowsingContext* aIgnoreBC,
+      nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate);
 
  protected:
+  explicit nsSHEntry(mozilla::dom::SHEntrySharedParentState* aState);
   explicit nsSHEntry(const nsSHEntry& aOther);
   virtual ~nsSHEntry();
 
-  nsSHEntryShared* GetState() { return mShared; }
-
   // We share the state in here with other SHEntries which correspond to the
   // same document.
-  RefPtr<nsSHEntryShared> mShared;
+  RefPtr<mozilla::dom::SHEntrySharedParentState> mShared;
 
   // See nsSHEntry.idl for comments on these members.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsIURI> mResultPrincipalURI;
   nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
   nsString mTitle;
   nsCOMPtr<nsIInputStream> mPostData;
@@ -60,9 +82,58 @@ class nsSHEntry : public nsISHEntry {
   bool mLoadReplace;
   bool mURIWasModified;
   bool mIsSrcdocEntry;
   bool mScrollRestorationIsManual;
   bool mLoadedInThisProcess;
   bool mPersist;
 };
 
+/**
+ * Session history entry class used for implementing session history for
+ * docshells in the parent process (a different solution would be to use the
+ * IPC actors for that too, with both parent and child actor created in the
+ * parent process).
+ */
+class nsLegacySHEntry final : public nsSHEntry {
+ public:
+  explicit nsLegacySHEntry(nsISHistory* aHistory, uint64_t aID);
+  explicit nsLegacySHEntry(const nsLegacySHEntry& aOther) : nsSHEntry(aOther) {}
+
+  NS_IMETHOD GetContentViewer(nsIContentViewer** aResult) override;
+  NS_IMETHOD SetContentViewer(nsIContentViewer* aViewer) override;
+  NS_IMETHOD GetWindowState(nsISupports** aState) override;
+  NS_IMETHOD SetWindowState(nsISupports* aState) override;
+  using nsISHEntry::GetRefreshURIList;
+  NS_IMETHOD GetRefreshURIList(nsIMutableArray** aRefreshURIList) override;
+  NS_IMETHOD SetRefreshURIList(nsIMutableArray* aRefreshURIList) override;
+  using nsISHEntry::GetSaveLayoutStateFlag;
+  NS_IMETHOD GetSaveLayoutStateFlag(bool* aSaveLayoutStateFlag) override;
+  NS_IMETHOD SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag) override;
+  NS_IMETHOD_(void) AddChildShell(nsIDocShellTreeItem* aShell) override;
+  NS_IMETHOD ChildShellAt(int32_t aIndex,
+                          nsIDocShellTreeItem** aShell) override;
+  NS_IMETHOD_(void) ClearChildShells() override;
+  NS_IMETHOD_(void) SyncPresentationState() override;
+  NS_IMETHOD 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& aSrcdocData,
+      bool aSrcdocEntry, nsIURI* aBaseURI, bool aSaveLayoutState,
+      bool aExpired) override;
+  NS_IMETHOD Clone(nsISHEntry** aResult) override;
+  NS_IMETHOD_(nsDocShellEditorData*) ForgetEditorData(void) override;
+  NS_IMETHOD_(void) SetEditorData(nsDocShellEditorData* aData) override;
+  NS_IMETHOD_(bool) HasDetachedEditor() override;
+  NS_IMETHOD_(bool) HasBFCacheEntry(nsIBFCacheEntry* aEntry) override;
+  NS_IMETHOD AbandonBFCacheEntry() override;
+  NS_IMETHOD_(void) ClearEntry() override;
+  NS_IMETHOD GetBfcacheID(uint64_t* aBFCacheID) override;
+
+ private:
+  nsSHEntryShared* GetState();
+};
+
 #endif /* nsSHEntry_h */
--- a/docshell/shistory/nsSHEntryShared.cpp
+++ b/docshell/shistory/nsSHEntryShared.cpp
@@ -16,33 +16,31 @@
 #include "nsSHistory.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
 
 namespace dom = mozilla::dom;
 
-namespace {
-uint64_t gSHEntrySharedID = 0;
-}  // namespace
-
 namespace mozilla {
 namespace dom {
 
-SHEntrySharedParentState::SHEntrySharedParentState(nsISHistory* aSHistory)
-    : SHEntrySharedParentState(
-          nsWeakPtr(do_GetWeakReference(aSHistory)).get()) {}
+SHEntrySharedParentState::SHEntrySharedParentState(nsISHistory* aSHistory,
+                                                   uint64_t aID)
+    : SHEntrySharedParentState(nsWeakPtr(do_GetWeakReference(aSHistory)).get(),
+                               aID) {}
 
-SHEntrySharedParentState::SHEntrySharedParentState(nsIWeakReference* aSHistory)
+SHEntrySharedParentState::SHEntrySharedParentState(nsIWeakReference* aSHistory,
+                                                   uint64_t aID)
     : mDocShellID({0}),
       mViewerBounds(0, 0, 0, 0),
       mCacheKey(0),
       mLastTouched(0),
-      mID(++gSHEntrySharedID),
+      mID(aID),
       mSHistory(aSHistory),
       mIsFrameNavigation(false),
       mSticky(true),
       mDynamicallyCreated(false),
       mExpired(false),
       mSaveLayoutState(true) {}
 
 SHEntrySharedParentState::~SHEntrySharedParentState() {}
@@ -64,18 +62,22 @@ void SHEntrySharedParentState::CopyFrom(
 
 void dom::SHEntrySharedParentState::NotifyListenersContentViewerEvicted() {
   if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
     RefPtr<nsSHistory> nsshistory = static_cast<nsSHistory*>(shistory.get());
     nsshistory->NotifyListenersContentViewerEvicted(1);
   }
 }
 
+dom::SHEntrySharedChildState::SHEntrySharedChildState()
+    : mSaveLayoutState(true) {}
+
 void SHEntrySharedChildState::CopyFrom(SHEntrySharedChildState* aEntry) {
   mChildShells.AppendObjects(aEntry->mChildShells);
+  mSaveLayoutState = aEntry->mSaveLayoutState;
 }
 
 }  // namespace dom
 }  // namespace mozilla
 
 void nsSHEntryShared::Shutdown() {}
 
 nsSHEntryShared::~nsSHEntryShared() {
@@ -95,18 +97,19 @@ nsSHEntryShared::~nsSHEntryShared() {
     RemoveFromBFCacheSync();
   }
 }
 
 NS_IMPL_QUERY_INTERFACE(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver)
 NS_IMPL_ADDREF_INHERITED(nsSHEntryShared, dom::SHEntrySharedParentState)
 NS_IMPL_RELEASE_INHERITED(nsSHEntryShared, dom::SHEntrySharedParentState)
 
-already_AddRefed<nsSHEntryShared> nsSHEntryShared::Duplicate() {
-  RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(this);
+already_AddRefed<nsSHEntryShared> nsSHEntryShared::Duplicate(
+    uint64_t aNewSharedID) {
+  RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(this, aNewSharedID);
 
   newEntry->dom::SHEntrySharedParentState::CopyFrom(this);
   newEntry->dom::SHEntrySharedChildState::CopyFrom(this);
 
   return newEntry.forget();
 }
 
 void nsSHEntryShared::RemoveFromExpirationTracker() {
--- a/docshell/shistory/nsSHEntryShared.h
+++ b/docshell/shistory/nsSHEntryShared.h
@@ -48,22 +48,21 @@ class Document;
  * process.
  */
 class SHEntrySharedParentState {
  public:
   uint64_t GetID() const { return mID; }
 
   void NotifyListenersContentViewerEvicted();
 
-  explicit SHEntrySharedParentState(nsISHistory* aSHistory);
-
  protected:
-  explicit SHEntrySharedParentState(SHEntrySharedParentState* aDuplicate)
-      : SHEntrySharedParentState(aDuplicate->mSHistory) {}
-  explicit SHEntrySharedParentState(nsIWeakReference* aSHistory);
+  SHEntrySharedParentState(nsISHistory* aSHistory, uint64_t aID);
+  SHEntrySharedParentState(SHEntrySharedParentState* aDuplicate, uint64_t aID)
+      : SHEntrySharedParentState(aDuplicate->mSHistory, aID) {}
+  SHEntrySharedParentState(nsIWeakReference* aSHistory, uint64_t aID);
   virtual ~SHEntrySharedParentState();
   NS_INLINE_DECL_VIRTUAL_REFCOUNTING_WITH_DESTROY(SHEntrySharedParentState,
                                                   Destroy())
 
   virtual void Destroy() { delete this; }
 
   void CopyFrom(SHEntrySharedParentState* aSource);
 
@@ -100,32 +99,39 @@ class SHEntrySharedParentState {
 };
 
 /**
  * SHEntrySharedChildState holds the shared state that needs to live in the
  * process where the document was loaded.
  */
 class SHEntrySharedChildState {
  protected:
+  SHEntrySharedChildState();
+
   void CopyFrom(SHEntrySharedChildState* aSource);
 
  public:
   // These members are copied by SHEntrySharedChildState::CopyFrom(). If you
   // add a member here, be sure to update the CopyFrom() implementation.
   nsCOMArray<nsIDocShellTreeItem> mChildShells;
 
   // These members aren't copied by SHEntrySharedChildState::CopyFrom() because
   // they're specific to a particular content viewer.
   nsCOMPtr<nsIContentViewer> mContentViewer;
   RefPtr<mozilla::dom::Document> mDocument;
+  // FIXME Move to parent?
+  nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
   nsCOMPtr<nsISupports> mWindowState;
   // FIXME Move to parent?
   nsCOMPtr<nsIMutableArray> mRefreshURIList;
   nsExpirationState mExpirationState;
   UniquePtr<nsDocShellEditorData> mEditorData;
+
+  // FIXME Move to parent?
+  bool mSaveLayoutState;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 /**
  * nsSHEntryShared holds the shared state if the session history is not stored
  * in the parent process, or if the load itself happens in the parent process.
@@ -138,34 +144,34 @@ class nsSHEntryShared final : public nsI
                               public mozilla::dom::SHEntrySharedParentState,
                               public mozilla::dom::SHEntrySharedChildState {
  public:
   static void EnsureHistoryTracker();
   static void Shutdown();
 
   using SHEntrySharedParentState::SHEntrySharedParentState;
 
-  already_AddRefed<nsSHEntryShared> Duplicate();
+  already_AddRefed<nsSHEntryShared> Duplicate(uint64_t aNewSharedID);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIBFCACHEENTRY
 
   // The nsIMutationObserver bits we actually care about.
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   nsExpirationState* GetExpirationState() { return &mExpirationState; }
 
  private:
   ~nsSHEntryShared();
 
-  friend class nsSHEntry;
+  friend class nsLegacySHEntry;
 
   void RemoveFromExpirationTracker();
   void SyncPresentationState();
   void DropPresentationState();
 
   nsresult SetContentViewer(nsIContentViewer* aViewer);
 };
 
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -24,23 +24,25 @@
 #include "nsNetUtil.h"
 #include "nsSHEntry.h"
 #include "nsTArray.h"
 #include "prsystem.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/MaybeNewPSHEntry.h"
+#include "mozilla/dom/SHEntryChild.h"
+#include "mozilla/dom/SHEntryParent.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPrefs_fission.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "nsIWebNavigation.h"
 #include "nsDocShellLoadTypes.h"
 #include "base/process.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
@@ -197,22 +199,22 @@ void nsSHistory::EvictContentViewerForEn
   // When dropping bfcache, we have to remove associated dynamic entries as
   // well.
   int32_t index = GetIndexOfEntry(aEntry);
   if (index != -1) {
     RemoveDynEntries(index, aEntry);
   }
 }
 
-nsSHistory::nsSHistory(BrowsingContext* aRootBC)
+nsSHistory::nsSHistory(BrowsingContext* aRootBC, const nsID& aRootDocShellID)
     : mRootBC(aRootBC),
       mIsRemote(false),
       mIndex(-1),
       mRequestedIndex(-1),
-      mRootDocShellID(aRootBC->GetHistoryID()) {
+      mRootDocShellID(aRootDocShellID) {
   // Add this new SHistory object to the list
   gSHistoryList.insertBack(this);
 
   // Init mHistoryTracker on setting mRootBC so we can bind its event
   // target to the tabGroup.
   nsPIDOMWindowOuter* win;
   if (mRootBC && (win = mRootBC->GetDOMWindow())) {
     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(win);
@@ -389,55 +391,76 @@ nsresult nsSHistory::WalkHistoryEntries(
       continue;
     }
 
     BrowsingContext* childBC = nullptr;
     if (aBC) {
       for (BrowsingContext* child : aBC->Children()) {
         // If the SH pref is on, or we are in the parent process, update
         // canonical BC directly
-        nsDocShell* docshell = static_cast<nsDocShell*>(child->GetDocShell());
-        if (docshell && docshell->HasHistoryEntry(childEntry)) {
-          childBC = docshell->GetBrowsingContext();
-          break;
+        if (StaticPrefs::fission_sessionHistoryInParent() ||
+            XRE_IsParentProcess()) {
+          // Walk the children of the browsing context and see if one of them
+          // has childEntry as mOSHE or mLSHE
+          if (child->Canonical()->HasHistoryEntry(childEntry)) {
+            childBC = child;
+            break;
+          }
+        } else {
+          nsDocShell* docshell = static_cast<nsDocShell*>(child->GetDocShell());
+          if (docshell && docshell->HasHistoryEntry(childEntry)) {
+            childBC = docshell->GetBrowsingContext();
+            break;
+          }
         }
       }
     }
 
     nsresult rv = aCallback(childEntry, childBC, i, aData);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 // callback data for WalkHistoryEntries
 struct MOZ_STACK_CLASS CloneAndReplaceData {
   CloneAndReplaceData(uint32_t aCloneID, nsISHEntry* aReplaceEntry,
-                      bool aCloneChildren, nsISHEntry* aDestTreeParent)
+                      bool aCloneChildren, nsISHEntry* aDestTreeParent,
+                      uint64_t aOtherPid,
+                      nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate)
       : cloneID(aCloneID),
         cloneChildren(aCloneChildren),
         replaceEntry(aReplaceEntry),
-        destTreeParent(aDestTreeParent) {}
+        destTreeParent(aDestTreeParent),
+        otherPid(aOtherPid),
+        entriesToUpdate(aEntriesToUpdate) {}
 
   uint32_t cloneID;
   bool cloneChildren;
   nsISHEntry* replaceEntry;
   nsISHEntry* destTreeParent;
   nsCOMPtr<nsISHEntry> resultEntry;
+  uint64_t otherPid;
+  // see comment for WalkHistoryEntriesFunc
+  nsTArray<EntriesAndBrowsingContextData>* entriesToUpdate;
 };
 
 nsresult nsSHistory::CloneAndReplaceChild(nsISHEntry* aEntry,
                                           BrowsingContext* aOwnerBC,
                                           int32_t aChildIndex, void* aData) {
+  MOZ_ASSERT(
+      XRE_IsParentProcess() || !StaticPrefs::fission_sessionHistoryInParent(),
+      "We should be in the parent process, or SH pref should be off");
   nsCOMPtr<nsISHEntry> dest;
 
   CloneAndReplaceData* data = static_cast<CloneAndReplaceData*>(aData);
   uint32_t cloneID = data->cloneID;
   nsISHEntry* replaceEntry = data->replaceEntry;
+  base::ProcessId otherPid = data->otherPid;
 
   if (!aEntry) {
     if (data->destTreeParent) {
       data->destTreeParent->AddChild(nullptr, aChildIndex);
     }
     return NS_OK;
   }
 
@@ -452,62 +475,78 @@ nsresult nsSHistory::CloneAndReplaceChil
     rv = aEntry->Clone(getter_AddRefs(dest));
     NS_ENSURE_SUCCESS(rv, rv);
   }
   dest->SetIsSubFrame(true);
 
   if (srcID != cloneID || data->cloneChildren) {
     // Walk the children
     CloneAndReplaceData childData(cloneID, replaceEntry, data->cloneChildren,
-                                  dest);
+                                  dest, otherPid, data->entriesToUpdate);
     rv = nsSHistory::WalkHistoryEntries(aEntry, aOwnerBC, CloneAndReplaceChild,
                                         &childData);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (srcID != cloneID && aOwnerBC) {
-    nsSHistory::HandleEntriesToSwapInDocShell(aOwnerBC, aEntry, dest);
+    nsSHistory::HandleEntriesToSwapInDocShell(aOwnerBC, aEntry, dest,
+                                              data->entriesToUpdate, otherPid);
   }
 
   if (data->destTreeParent) {
     data->destTreeParent->AddChild(dest, aChildIndex);
   }
   data->resultEntry = dest;
   return rv;
 }
 
 // static
 nsresult nsSHistory::CloneAndReplace(
     nsISHEntry* aSrcEntry, BrowsingContext* aOwnerBC, uint32_t aCloneID,
-    nsISHEntry* aReplaceEntry, bool aCloneChildren, nsISHEntry** aDestEntry) {
+    nsISHEntry* aReplaceEntry, bool aCloneChildren, nsISHEntry** aDestEntry,
+    uint64_t aOtherPid,
+    nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate) {
   NS_ENSURE_ARG_POINTER(aDestEntry);
   NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
-  CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
+  CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr,
+                           aOtherPid, aEntriesToUpdate);
   nsresult rv = CloneAndReplaceChild(aSrcEntry, aOwnerBC, 0, &data);
   data.resultEntry.swap(*aDestEntry);
   return rv;
 }
 
 NS_IMETHODIMP
 nsSHistory::AddChildSHEntryHelper(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
                                   BrowsingContext* aBC, bool aCloneChildren) {
   nsCOMPtr<nsISHEntry> child;
-  nsresult rv = AddChildSHEntryHelper(aCloneRef, aNewEntry, aBC, aCloneChildren,
-                                      getter_AddRefs(child));
+  int32_t entriesPurged;
+  nsresult rv = AddChildSHEntryHelper(
+      aCloneRef, aNewEntry, aBC, aCloneChildren,
+      0 /*unused, passed in by SHEntryParent::RecvAddChildSHEntryHelper
+         */
+      ,
+      nullptr /* array - this will be set in
+      SHistory::RecvAddChildSHEntryHelper if we are going over IPC,
+      else, it is not needed */
+      ,
+      &entriesPurged /*used by SHEntryChild::AddToRootSessionHistory but
+                        not here */
+      ,
+      getter_AddRefs(child));
   if (NS_SUCCEEDED(rv)) {
     child->SetDocshellID(aBC->GetHistoryID());
   }
   return rv;
 }
 
-nsresult nsSHistory::AddChildSHEntryHelper(nsISHEntry* aCloneRef,
-                                           nsISHEntry* aNewEntry,
-                                           BrowsingContext* aBC,
-                                           bool aCloneChildren,
-                                           nsISHEntry** aNextEntry) {
+nsresult nsSHistory::AddChildSHEntryHelper(
+    nsISHEntry* aCloneRef, nsISHEntry* aNewEntry, BrowsingContext* aBC,
+    bool aCloneChildren, uint64_t aOtherPid,
+    nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate,
+    int32_t* aEntriesPurged, nsISHEntry** aNextEntry) {
   /* You are currently in the rootDocShell.
    * You will get here when a subframe has a new url
    * to load and you have walked up the tree all the
    * way to the top to clone the current SHEntry hierarchy
    * and replace the subframe where a new url was loaded with
    * a new entry.
    */
   nsCOMPtr<nsISHEntry> currentHE;
@@ -517,27 +556,36 @@ nsresult nsSHistory::AddChildSHEntryHelp
   }
 
   GetEntryAtIndex(index, getter_AddRefs(currentHE));
   NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
 
   nsresult rv = NS_OK;
   uint32_t cloneID = aCloneRef->GetID();
   rv = nsSHistory::CloneAndReplace(currentHE, aBC, cloneID, aNewEntry,
-                                   aCloneChildren, aNextEntry);
+                                   aCloneChildren, aNextEntry, aOtherPid,
+                                   aEntriesToUpdate);
   if (NS_SUCCEEDED(rv)) {
-    rv = AddEntry(*aNextEntry, true);
+    rv = AddEntry(*aNextEntry, true, aEntriesPurged);
   }
   return rv;
 }
 
 nsresult nsSHistory::SetChildHistoryEntry(nsISHEntry* aEntry,
                                           BrowsingContext* aBC,
                                           int32_t aEntryIndex, void* aData) {
+  MOZ_ASSERT(
+      XRE_IsParentProcess() || !StaticPrefs::fission_sessionHistoryInParent(),
+      "We should be in the parent process, or 'SH in parent' pref should be "
+      "off");
   SwapEntriesData* data = static_cast<SwapEntriesData*>(aData);
+  uint64_t otherPid = data->otherPid;
+  MOZ_ASSERT(data->entriesToUpdate || otherPid == 0,
+             "entriesToUpdate can't be null if we were called over IPC");
+
   if (!aBC || aBC == data->ignoreBC) {
     return NS_OK;
   }
 
   nsISHEntry* destTreeRoot = data->destTreeRoot;
 
   nsCOMPtr<nsISHEntry> destEntry;
 
@@ -568,53 +616,102 @@ nsresult nsSHistory::SetChildHistoryEntr
           break;
         }
       }
     }
   } else {
     destEntry = destTreeRoot;
   }
 
-  nsSHistory::HandleEntriesToSwapInDocShell(aBC, aEntry, destEntry);
+  nsSHistory::HandleEntriesToSwapInDocShell(aBC, aEntry, destEntry,
+                                            data->entriesToUpdate, otherPid);
   // Now handle the children of aEntry.
-  SwapEntriesData childData = {data->ignoreBC, destTreeRoot, destEntry};
+  SwapEntriesData childData = {data->ignoreBC, destTreeRoot, destEntry,
+                               otherPid, data->entriesToUpdate};
   return nsSHistory::WalkHistoryEntries(aEntry, aBC, SetChildHistoryEntry,
                                         &childData);
 }
 
 // static
 void nsSHistory::HandleEntriesToSwapInDocShell(
     mozilla::dom::BrowsingContext* aBC, nsISHEntry* aOldEntry,
-    nsISHEntry* aNewEntry) {
+    nsISHEntry* aNewEntry,
+    nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate,
+    uint64_t aOtherPid) {
   bool shPref = StaticPrefs::fission_sessionHistoryInParent();
+  if (XRE_IsParentProcess()) {
+    aBC->Canonical()->SwapHistoryEntries(aOldEntry, aNewEntry);
+  }
   if (aBC->IsInProcess() || !shPref) {
     nsDocShell* docshell = static_cast<nsDocShell*>(aBC->GetDocShell());
     if (docshell) {
       docshell->SwapHistoryEntries(aOldEntry, aNewEntry);
     }
+  } else if (aBC->Canonical()->IsOwnedByProcess(aOtherPid)) {
+    EntriesAndBrowsingContextData* toUpdate = aEntriesToUpdate->AppendElement();
+    toUpdate->oldEntry = aOldEntry;
+    toUpdate->newEntry = aNewEntry;
+    toUpdate->context = aBC;
   } else {
-    // FIXME Bug 1633988: Need to update entries?
+    // XXX anny: this is currently not working! see bug 1581970
+
+    // This BC is owned by a process different from the caller,
+    // so we can send the entries right away to a different process
+    ContentParent* cp = aBC->Canonical()->GetContentParent();
+
+    auto convert = [](auto entry) {
+      return static_cast<CrossProcessSHEntry*>(
+          static_cast<LegacySHEntry*>(static_cast<nsSHEntry*>(entry)));
+    };
+
+    // We are performing a nested IPC call to a different process
+    Unused << cp->SendUpdateSHEntriesInDocShell(convert(aOldEntry),
+                                                convert(aNewEntry), aBC);
   }
 }
 
 NS_IMETHODIMP
 nsSHistory::AddToRootSessionHistory(bool aCloneChildren, nsISHEntry* aOSHE,
                                     BrowsingContext* aBC, nsISHEntry* aEntry,
                                     uint32_t aLoadType, bool aShouldPersist,
                                     Maybe<int32_t>* aPreviousEntryIndex,
                                     Maybe<int32_t>* aLoadedEntryIndex) {
+  int32_t entriesPurged;
+  nsresult rv = AddToRootSessionHistory(
+      aCloneChildren, aOSHE, aBC, aEntry, aLoadType, aShouldPersist,
+      0 /* unused, passed in by SHEntryParent::RecvAddToRootSessionHistory */,
+      aPreviousEntryIndex, aLoadedEntryIndex,
+      nullptr /* array - this will be set in RecvAddToRootSessionHistory if we
+                are going over IPC, else, it is not needed */
+      ,
+      &entriesPurged
+      /* used by SHEntryChild::AddToRootSessionHistory but not here */);
+
+  if (NS_SUCCEEDED(rv)) {
+    aEntry->SetDocshellID(aBC->GetHistoryID());
+  }
+  return rv;
+}
+nsresult nsSHistory::AddToRootSessionHistory(
+    bool aCloneChildren, nsISHEntry* aOSHE, BrowsingContext* aBC,
+    nsISHEntry* aEntry, uint32_t aLoadType, bool aShouldPersist,
+    uint64_t aOtherPid, Maybe<int32_t>* previousEntryIndex,
+    Maybe<int32_t>* aLoadedEntryIndex,
+    nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate,
+    int32_t* aEntriesPurged) {
   nsresult rv = NS_OK;
 
   // If we need to clone our children onto the new session
   // history entry, do so now.
   if (aCloneChildren && aOSHE) {
     uint32_t cloneID = aOSHE->GetID();
     nsCOMPtr<nsISHEntry> newEntry;
     nsSHistory::CloneAndReplace(aOSHE, aBC, cloneID, aEntry, true,
-                                getter_AddRefs(newEntry));
+                                getter_AddRefs(newEntry), aOtherPid,
+                                aEntriesToUpdate);
     NS_ASSERTION(aEntry == newEntry,
                  "The new session history should be in the new entry");
   }
   // This is the root docshell
   bool addToSHistory = !LOAD_TYPE_HAS_FLAGS(
       aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY);
   if (!addToSHistory) {
     // Replace current entry in session history; If the requested index is
@@ -630,35 +727,39 @@ nsSHistory::AddToRootSessionHistory(bool
       rv = ReplaceEntry(index, aEntry);
     } else {
       // If we're trying to replace an inexistant shistory entry, append.
       addToSHistory = true;
     }
   }
   if (addToSHistory) {
     // Add to session history
-    *aPreviousEntryIndex = Some(mIndex);
-    rv = AddEntry(aEntry, aShouldPersist);
+    *previousEntryIndex = Some(mIndex);
+    rv = AddEntry(aEntry, aShouldPersist, aEntriesPurged);
     *aLoadedEntryIndex = Some(mIndex);
     MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
             ("Previous index: %d, Loaded index: %d",
-             aPreviousEntryIndex->value(), aLoadedEntryIndex->value()));
-  }
-  if (NS_SUCCEEDED(rv)) {
-    aEntry->SetDocshellID(aBC->GetHistoryID());
+             previousEntryIndex->value(), aLoadedEntryIndex->value()));
   }
   return rv;
 }
 
 /* Add an entry to the History list at mIndex and
  * increment the index to point to the new entry
  */
 NS_IMETHODIMP
 nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist) {
+  int32_t dummy = 0;
+  return AddEntry(aSHEntry, aPersist, &dummy);
+}
+
+nsresult nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist,
+                              int32_t* aEntriesPurged) {
   NS_ENSURE_ARG(aSHEntry);
+  *aEntriesPurged = 0;
 
   nsCOMPtr<nsISHistory> shistoryOfEntry = aSHEntry->GetShistory();
   if (shistoryOfEntry != this) {
     NS_WARNING(
         "The entry has been associated to another nsISHistory instance. "
         "Try nsISHEntry.clone() and nsISHEntry.abandonBFCacheEntry() "
         "first if you're copying an entry from another nsISHistory.");
     return NS_ERROR_FAILURE;
@@ -692,22 +793,26 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntr
   MOZ_ASSERT(mIndex >= -1);
   aSHEntry->SetPersist(aPersist);
   mEntries.TruncateLength(mIndex + 1);
   mEntries.AppendElement(aSHEntry);
   mIndex++;
 
   // Purge History list if it is too long
   if (gHistoryMaxSize >= 0 && Length() > gHistoryMaxSize) {
-    PurgeHistory(Length() - gHistoryMaxSize);
+    *aEntriesPurged = Length() - gHistoryMaxSize;
+    PurgeHistory(*aEntriesPurged);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP_(void)
+nsSHistory::ClearRootBrowsingContext() { mRootBC = nullptr; }
+
 /* Get size of the history list */
 NS_IMETHODIMP
 nsSHistory::GetCount(int32_t* aResult) {
   MOZ_ASSERT(aResult, "null out param?");
   *aResult = Length();
   return NS_OK;
 }
 
@@ -923,19 +1028,18 @@ nsSHistory::EvictAllContentViewers() {
   // we might have viewers quite far from mIndex.  So just evict everything.
   for (int32_t i = 0; i < Length(); i++) {
     EvictContentViewerForEntry(mEntries[i]);
   }
 
   return NS_OK;
 }
 
-/* static */
-void nsSHistory::LoadURIs(nsTArray<LoadEntryResult>& aLoadResults) {
-  for (LoadEntryResult& loadEntry : aLoadResults) {
+static void LoadURIs(nsTArray<nsSHistory::LoadEntryResult>& aLoadResults) {
+  for (nsSHistory::LoadEntryResult& loadEntry : aLoadResults) {
     loadEntry.mBrowsingContext->LoadURI(loadEntry.mLoadState, false);
   }
 }
 
 NS_IMETHODIMP
 nsSHistory::Reload(uint32_t aReloadFlags) {
   nsTArray<LoadEntryResult> loadResults;
   nsresult rv = Reload(aReloadFlags, loadResults);
@@ -1687,12 +1791,13 @@ nsresult nsSHistory::InitiateLoad(nsISHE
 
   loadResult->mLoadState = std::move(loadState);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::CreateEntry(nsISHEntry** aEntry) {
-  nsCOMPtr<nsISHEntry> entry = new nsSHEntry(this);
+  nsCOMPtr<nsISHEntry> entry =
+      new nsLegacySHEntry(this, SHEntryChildShared::CreateSharedID());
   entry.forget(aEntry);
   return NS_OK;
 }
--- a/docshell/shistory/nsSHistory.h
+++ b/docshell/shistory/nsSHistory.h
@@ -59,19 +59,23 @@ class nsSHistory : public mozilla::Linke
 
   // Structure used in SetChildHistoryEntry
   struct SwapEntriesData {
     mozilla::dom::BrowsingContext*
         ignoreBC;                // constant; the browsing context to ignore
     nsISHEntry* destTreeRoot;    // constant; the root of the dest tree
     nsISHEntry* destTreeParent;  // constant; the node under destTreeRoot
                                  // whose children will correspond to aEntry
+    uint64_t otherPid;  // constant; pid of the process which indirectly called
+                        // SetChildHistoryEntry
+    // see comment for WalkHistoryEntriesFunc
+    nsTArray<EntriesAndBrowsingContextData>* entriesToUpdate;
   };
 
-  explicit nsSHistory(mozilla::dom::BrowsingContext* aRootBC);
+  nsSHistory(mozilla::dom::BrowsingContext* aRootBC, const nsID& aDocShellID);
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHISTORY
 
   // One time initialization method called upon docshell module construction
   static nsresult Startup();
   static void Shutdown();
   static void UpdatePrefs();
 
@@ -99,20 +103,21 @@ class nsSHistory : public mozilla::Linke
   // Clone a session history tree for subframe navigation.
   // The tree rooted at |aSrcEntry| will be cloned into |aDestEntry|, except
   // for the entry with id |aCloneID|, which will be replaced with
   // |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
   // corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
   // have that pointer updated to point to the cloned history entry.
   // If aCloneChildren is true then the children of the entry with id
   // |aCloneID| will be cloned into |aReplaceEntry|.
-  static nsresult CloneAndReplace(nsISHEntry* aSrcEntry,
-                                  mozilla::dom::BrowsingContext* aOwnerBC,
-                                  uint32_t aCloneID, nsISHEntry* aReplaceEntry,
-                                  bool aCloneChildren, nsISHEntry** aDestEntry);
+  static nsresult CloneAndReplace(
+      nsISHEntry* aSrcEntry, mozilla::dom::BrowsingContext* aOwnerBC,
+      uint32_t aCloneID, nsISHEntry* aReplaceEntry, bool aCloneChildren,
+      nsISHEntry** aDestEntry, uint64_t aOtherPid,
+      nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate);
 
   // Child-walking callback for CloneAndReplace
   static nsresult CloneAndReplaceChild(nsISHEntry* aEntry,
                                        mozilla::dom::BrowsingContext* aOwnerBC,
                                        int32_t aChildIndex, void* aData);
 
   // Child-walking callback for SetHistoryEntry
   static nsresult SetChildHistoryEntry(nsISHEntry* aEntry,
@@ -122,39 +127,51 @@ class nsSHistory : public mozilla::Linke
   // For each child of aRootEntry, find the corresponding shell which is
   // a child of aBC, and call aCallback. The opaque pointer aData
   // is passed to the callback.
   static nsresult WalkHistoryEntries(nsISHEntry* aRootEntry,
                                      mozilla::dom::BrowsingContext* aBC,
                                      WalkHistoryEntriesFunc aCallback,
                                      void* aData);
 
-  nsresult AddChildSHEntryHelper(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
-                                 mozilla::dom::BrowsingContext* aBC,
-                                 bool aCloneChildren, nsISHEntry** aNextEntry);
+  nsresult AddToRootSessionHistory(
+      bool aCloneChildren, nsISHEntry* aOSHE,
+      mozilla::dom::BrowsingContext* aBC, nsISHEntry* aEntry,
+      uint32_t aLoadType, bool aShouldPersist, uint64_t aOtherPid,
+      mozilla::Maybe<int32_t>* aPreviousEntryIndex,
+      mozilla::Maybe<int32_t>* aLoadedEntryIndex,
+      nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate,
+      int32_t* aEntriesPurged);
+
+  nsresult AddChildSHEntryHelper(
+      nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
+      mozilla::dom::BrowsingContext* aBC, bool aCloneChildren,
+      uint64_t aOtherPid,
+      nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate,
+      int32_t* aEntriesPurged, nsISHEntry** aNextEntry);
 
   nsTArray<nsCOMPtr<nsISHEntry>>& Entries() { return mEntries; }
 
+  nsresult AddEntry(nsISHEntry* aSHEntry, bool aPersist,
+                    int32_t* aEntriesPurged);
   void RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex,
                      bool* aDidRemove);
 
   // The size of the window of SHEntries which can have alive viewers in the
   // bfcache around the currently active SHEntry.
   //
   // We try to keep viewers for SHEntries between index - VIEWER_WINDOW and
   // index + VIEWER_WINDOW alive.
   static const int32_t VIEWER_WINDOW = 3;
 
   struct LoadEntryResult {
     RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
     RefPtr<nsDocShellLoadState> mLoadState;
   };
 
-  static void LoadURIs(nsTArray<LoadEntryResult>& aLoadResults);
-
   // If this doesn't return an error then either aLoadResult is set to nothing,
   // in which case the caller should ignore the load, or it returns a valid
   // LoadEntryResult in aLoadResult which the caller should use to do the load.
   nsresult Reload(uint32_t aReloadFlags,
                   nsTArray<LoadEntryResult>& aLoadResults);
   nsresult ReloadCurrentEntry(nsTArray<LoadEntryResult>& aLoadResults);
   nsresult GotoIndex(int32_t aIndex, nsTArray<LoadEntryResult>& aLoadResults);
 
@@ -215,19 +232,21 @@ class nsSHistory : public mozilla::Linke
   // If our docshell is located in parent or 'SH in parent' pref is off we can
   // update it directly, Otherwise, we have two choices. If the browsing context
   // that owns the docshell is in the same process as the process who called us
   // over IPC, then we save entries that need to be updated in a list, and once
   // we have returned from the IPC call, we update the docshell in the child
   // process. Otherwise, if the browsing context is in a different process, we
   // do a nested IPC call to that process to update the docshell in that
   // process.
-  static void HandleEntriesToSwapInDocShell(mozilla::dom::BrowsingContext* aBC,
-                                            nsISHEntry* aOldEntry,
-                                            nsISHEntry* aNewEntry);
+  static void HandleEntriesToSwapInDocShell(
+      mozilla::dom::BrowsingContext* aBC, nsISHEntry* aOldEntry,
+      nsISHEntry* aNewEntry,
+      nsTArray<EntriesAndBrowsingContextData>* aEntriesToUpdate,
+      uint64_t aOtherPid);
 
  protected:
   // Length of mEntries.
   int32_t Length() { return int32_t(mEntries.Length()); }
 
   bool mIsRemote;
   nsTArray<nsCOMPtr<nsISHEntry>> mEntries;  // entries are never null
  private:
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2127,17 +2127,18 @@ nsresult nsFrameLoader::MaybeCreateDocSh
     NS_WARNING("Something wrong when creating the docshell for a frameloader!");
     return NS_ERROR_FAILURE;
   }
 
   // If we are an in-process browser, we want to set up our session history.
   if (mIsTopLevelContent && mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
       !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
     // XXX(nika): Set this up more explicitly?
-    mPendingBrowsingContext->InitSessionHistory();
+    nsresult rv = docShell->InitSessionHistory();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Apply sandbox flags even if our owner is not an iframe, as this copies
   // flags from our owning content's owning document.
   // Note: ApplySandboxFlags should be called after docShell->SetIsFrame
   // because we need to get the correct presentation URL in ApplySandboxFlags.
   uint32_t sandboxFlags = 0;
   HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -21,17 +21,16 @@
 #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"
@@ -69,17 +68,16 @@
 #include "nsILoadInfo.h"
 #include "nsIPromptFactory.h"
 #include "nsIURI.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIWebProtocolHandlerRegistrar.h"
 #include "nsIXPConnect.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIAppWindow.h"
-#include "nsSHistory.h"
 #include "nsViewManager.h"
 #include "nsVariant.h"
 #include "nsIWidget.h"
 #include "nsNetUtil.h"
 #ifndef XP_WIN
 #  include "nsJARProtocolHandler.h"
 #endif
 #include "nsPIDOMWindow.h"
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -56,16 +56,18 @@
 #include "mozilla/dom/PLoginReputationChild.h"
 #include "mozilla/dom/PSessionStorageObserverChild.h"
 #include "mozilla/dom/ContentPlaybackController.h"
 #include "mozilla/dom/PostMessageEvent.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/RemoteWorkerService.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
+#include "mozilla/dom/SHEntryChild.h"
+#include "mozilla/dom/SHistoryChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
 #include "mozilla/dom/WindowGlobalChild.h"
 #include "mozilla/dom/WorkerDebugger.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/Logging.h"
@@ -3227,16 +3229,52 @@ ContentChild::AllocPSessionStorageObserv
 bool ContentChild::DeallocPSessionStorageObserverChild(
     PSessionStorageObserverChild* aActor) {
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+PSHEntryChild* ContentChild::AllocPSHEntryChild(PSHistoryChild* aSHistory,
+                                                uint64_t aSharedID) {
+  // We take a strong reference for the IPC layer. The Release implementation
+  // for SHEntryChild will ask the IPC layer to release it (through
+  // DeallocPSHEntryChild) if that is the only remaining reference.
+  RefPtr<SHEntryChild> child;
+  child = new SHEntryChild(static_cast<SHistoryChild*>(aSHistory), aSharedID);
+  return child.forget().take();
+}
+
+void ContentChild::DeallocPSHEntryChild(PSHEntryChild* aActor) {
+  // Release the strong reference we took in AllocPSHEntryChild for the IPC
+  // layer.
+  RefPtr<SHEntryChild> child(dont_AddRef(static_cast<SHEntryChild*>(aActor)));
+}
+
+PSHistoryChild* ContentChild::AllocPSHistoryChild(
+    const MaybeDiscarded<BrowsingContext>& aContext) {
+  // FIXME: How should SHistoryChild construction deal with a null or discarded
+  // BrowsingContext? This will likely kill the current child process.
+  if (NS_WARN_IF(aContext.IsNullOrDiscarded())) {
+    return nullptr;
+  }
+
+  // We take a strong reference for the IPC layer. The Release implementation
+  // for SHistoryChild will ask the IPC layer to release it (through
+  // DeallocPSHistoryChild) if that is the only remaining reference.
+  return do_AddRef(new SHistoryChild(aContext.get())).take();
+}
+
+void ContentChild::DeallocPSHistoryChild(PSHistoryChild* aActor) {
+  // Release the strong reference we took in AllocPSHistoryChild for the IPC
+  // layer.
+  RefPtr<SHistoryChild> child(dont_AddRef(static_cast<SHistoryChild*>(aActor)));
+}
+
 mozilla::ipc::IPCResult ContentChild::RecvActivate(PBrowserChild* aTab) {
   BrowserChild* tab = static_cast<BrowserChild*>(aTab);
   return tab->RecvActivate();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvDeactivate(PBrowserChild* aTab) {
   BrowserChild* tab = static_cast<BrowserChild*>(aTab);
   return tab->RecvDeactivate();
@@ -3445,20 +3483,16 @@ 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();
 
@@ -3483,30 +3517,54 @@ mozilla::ipc::IPCResult ContentChild::Re
     return IPC_OK();
   }
 
   ContentMediaActionHandler::HandleMediaControlKeysEvent(aContext.get(),
                                                          aEvent);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentChild::RecvDestroySHEntrySharedState(
+    const uint64_t& aID) {
+  SHEntryChildShared::Remove(aID);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvEvictContentViewers(
+    nsTArray<uint64_t>&& aToEvictSharedStateIDs) {
+  SHEntryChildShared::EvictContentViewers(std::move(aToEvictSharedStateIDs));
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult ContentChild::RecvSessionStorageData(
     const uint64_t aTopContextId, const nsACString& aOriginAttrs,
     const nsACString& aOriginKey, const nsTArray<KeyValuePair>& aDefaultData,
     const nsTArray<KeyValuePair>& aSessionData) {
   if (const RefPtr<BrowsingContext> topContext =
           BrowsingContext::Get(aTopContextId)) {
     topContext->GetSessionStorageManager()->LoadSessionStorageData(
         nullptr, aOriginAttrs, aOriginKey, aDefaultData, aSessionData);
   } else {
     NS_WARNING("Got session storage data for a discarded session");
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentChild::RecvUpdateSHEntriesInDocShell(
+    CrossProcessSHEntry* aOldEntry, CrossProcessSHEntry* aNewEntry,
+    const MaybeDiscarded<BrowsingContext>& aContext) {
+  MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null");
+  nsDocShell* docshell =
+      static_cast<nsDocShell*>(aContext.GetMaybeDiscarded()->GetDocShell());
+  if (docshell) {
+    docshell->SwapHistoryEntries(aOldEntry->ToSHEntryChild(),
+                                 aNewEntry->ToSHEntryChild());
+  }
+  return IPC_OK();
+}
 
 mozilla::ipc::IPCResult ContentChild::RecvOnAllowAccessFor(
     const MaybeDiscarded<BrowsingContext>& aContext,
     const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
     const ContentBlockingNotifier::StorageAccessGrantedReason& aReason) {
   MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null");
 
   ContentBlocking::OnAllowAccessFor(aContext.GetMaybeDiscarded(),
@@ -4123,24 +4181,16 @@ mozilla::ipc::IPCResult ContentChild::Re
   nsCOMPtr<nsPIDOMWindowOuter> window = context->GetDOMWindow();
   BrowserChild* bc = BrowserChild::GetFrom(window);
   if (bc) {
     bc->NotifyNavigationFinished();
   }
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult ContentChild::RecvHistoryCommitLength(
-    const MaybeDiscarded<BrowsingContext>& aContext, uint32_t aLength) {
-  if (!aContext.IsNullOrDiscarded()) {
-    aContext.get()->GetChildSessionHistory()->SetLength(aLength);
-  }
-  return IPC_OK();
-}
-
 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
 mozilla::ipc::IPCResult ContentChild::RecvInitSandboxTesting(
     Endpoint<PSandboxTestingChild>&& aEndpoint) {
   if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
     return IPC_FAIL(
         this, "InitSandboxTesting failed to initialise the child process.");
   }
   return IPC_OK();
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -595,16 +595,26 @@ class ContentChild final : public PConte
 
   bool DeallocPLoginReputationChild(PLoginReputationChild* aActor);
 
   PSessionStorageObserverChild* AllocPSessionStorageObserverChild();
 
   bool DeallocPSessionStorageObserverChild(
       PSessionStorageObserverChild* aActor);
 
+  PSHEntryChild* AllocPSHEntryChild(PSHistoryChild* aSHistory,
+                                    uint64_t aSharedID);
+
+  void DeallocPSHEntryChild(PSHEntryChild*);
+
+  PSHistoryChild* AllocPSHistoryChild(
+      const MaybeDiscarded<BrowsingContext>& aContext);
+
+  void DeallocPSHistoryChild(PSHistoryChild* aActor);
+
   nsTArray<LookAndFeelInt>& LookAndFeelCache() { return mLookAndFeelCache; }
 
   /**
    * Helper function for protocols that use the GPU process when available.
    * Overrides FatalError to just be a warning when communicating with the
    * GPU process since we don't want to crash the content process when the
    * GPU process crashes.
    */
@@ -643,21 +653,30 @@ class ContentChild final : public PConte
   uint64_t GetBrowsingContextFieldEpoch() const {
     return mBrowsingContextFieldEpoch;
   }
   uint64_t NextBrowsingContextFieldEpoch() {
     mBrowsingContextFieldEpoch++;
     return mBrowsingContextFieldEpoch;
   }
 
+  mozilla::ipc::IPCResult RecvDestroySHEntrySharedState(const uint64_t& aID);
+
+  mozilla::ipc::IPCResult RecvEvictContentViewers(
+      nsTArray<uint64_t>&& aToEvictSharedStateIDs);
+
   mozilla::ipc::IPCResult RecvSessionStorageData(
       uint64_t aTopContextId, const nsACString& aOriginAttrs,
       const nsACString& aOriginKey, const nsTArray<KeyValuePair>& aDefaultData,
       const nsTArray<KeyValuePair>& aSessionData);
 
+  mozilla::ipc::IPCResult RecvUpdateSHEntriesInDocShell(
+      CrossProcessSHEntry* aOldEntry, CrossProcessSHEntry* aNewEntry,
+      const MaybeDiscarded<BrowsingContext>& aContext);
+
   mozilla::ipc::IPCResult RecvOnAllowAccessFor(
       const MaybeDiscarded<BrowsingContext>& aContext,
       const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
       const ContentBlockingNotifier::StorageAccessGrantedReason& aReason);
 
   mozilla::ipc::IPCResult RecvOnContentBlockingDecision(
       const MaybeDiscarded<BrowsingContext>& aContext,
       const ContentBlockingNotifier::BlockingDecision& aDecision,
@@ -785,20 +804,17 @@ class ContentChild final : public PConte
                                          const ClonedMessageData& aStack);
 
   void ReceiveRawMessage(const JSActorMessageMeta& aMeta,
                          ipc::StructuredCloneData&& aData,
                          ipc::StructuredCloneData&& aStack);
 
   JSActor::Type GetSide() override { return JSActor::Type::Child; }
 
-  mozilla::ipc::IPCResult RecvHistoryCommitLength(
-      const MaybeDiscarded<BrowsingContext>& aContext, uint32_t aLength);
-
-private:
+ private:
 #ifdef NIGHTLY_BUILD
   virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override;
 #else
   using PContentChild::OnMessageReceived;
 #endif
 
   virtual PContentChild::Result OnMessageReceived(const Message& aMsg,
                                                   Message*& aReply) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -103,16 +103,18 @@
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PPresentationParent.h"
 #include "mozilla/dom/ParentProcessMessageManager.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/PresentationParent.h"
 #include "mozilla/dom/ProcessMessageManager.h"
 #include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/SHEntryParent.h"
+#include "mozilla/dom/SHistoryParent.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/URLClassifierParent.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/ipc/SharedMap.h"
@@ -5641,16 +5643,35 @@ mozilla::ipc::IPCResult ContentParent::R
 bool ContentParent::DeallocPSessionStorageObserverParent(
     PSessionStorageObserverParent* aActor) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
 }
 
+PSHEntryParent* ContentParent::AllocPSHEntryParent(PSHistoryParent* aSHistory,
+                                                   uint64_t aSharedID) {
+  return SHistoryParent::CreateEntry(this, aSHistory, aSharedID);
+}
+
+void ContentParent::DeallocPSHEntryParent(PSHEntryParent* aEntry) {
+  delete static_cast<SHEntryParent*>(aEntry);
+}
+
+PSHistoryParent* ContentParent::AllocPSHistoryParent(
+    const MaybeDiscarded<BrowsingContext>& aContext) {
+  MOZ_ASSERT(!aContext.IsNull());
+  return new SHistoryParent(aContext.GetMaybeDiscarded()->Canonical());
+}
+
+void ContentParent::DeallocPSHistoryParent(PSHistoryParent* aActor) {
+  delete static_cast<SHistoryParent*>(aActor);
+}
+
 mozilla::ipc::IPCResult ContentParent::RecvMaybeReloadPlugins() {
   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
   pluginHost->ReloadPlugins();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvDeviceReset() {
   GPUProcessManager* pm = GPUProcessManager::Get();
@@ -5800,16 +5821,45 @@ mozilla::ipc::IPCResult ContentParent::R
   }
   if (RefPtr<MediaController> controller =
           aContext.get_canonical()->GetMediaController()) {
     controller->SetIsInPictureInPictureMode(aEnabled);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentParent::RecvUpdateSHEntriesInBC(
+    PSHEntryParent* aNewLSHE, PSHEntryParent* aNewOSHE,
+    const MaybeDiscarded<BrowsingContext>& aMaybeContext) {
+  if (aMaybeContext.IsNullOrDiscarded()) {
+    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+            ("ParentIPC: Trying to update a browsing context that does not "
+             "exist or has been discarded"));
+    return IPC_OK();
+  }
+  auto aContext = aMaybeContext.get()->Canonical();
+  MOZ_ASSERT(aContext);
+  if (!aContext->IsOwnedByProcess(ChildID())) {
+    // We are trying to update a child BrowsingContext in another child
+    // process. This is illegal since the owner of the BrowsingContext
+    // is the proccess with the in-process docshell, which is tracked
+    // by OwnerProcessId.
+    MOZ_DIAGNOSTIC_ASSERT(
+        false,
+        "Trying to update a child BrowsingContext in another child process");
+    return IPC_OK();
+  }
+  SHEntryParent* newLSHEparent = static_cast<SHEntryParent*>(aNewLSHE);
+  SHEntryParent* newOSHEparent = static_cast<SHEntryParent*>(aNewOSHE);
+  nsISHEntry* lshe = newLSHEparent ? newLSHEparent->mEntry.get() : nullptr;
+  nsISHEntry* oshe = newOSHEparent ? newOSHEparent->mEntry.get() : nullptr;
+  aContext->UpdateSHEntries(lshe, oshe);
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult ContentParent::RecvAbortOtherOrientationPendingPromises(
     const MaybeDiscarded<BrowsingContext>& aContext) {
   if (aContext.IsNullOrDiscarded()) {
     return IPC_OK();
   }
 
   CanonicalBrowsingContext* context = aContext.get_canonical();
 
@@ -6347,43 +6397,16 @@ 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::RecvHistoryGo(
-    const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
-    HistoryGoResolver&& aResolveRequestedIndex) {
-  if (!aContext.IsDiscarded()) {
-    nsSHistory* shistory =
-      static_cast<nsSHistory*>(aContext.get_canonical()->GetSessionHistory());
-    nsTArray<nsSHistory::LoadEntryResult> loadResults;
-    nsresult rv = shistory->GotoIndex(aOffset, loadResults);
-    if (NS_FAILED(rv)) {
-      return IPC_FAIL(this, "GotoIndex failed");
-    }
-    aResolveRequestedIndex(shistory->GetRequestedIndex());
-    shistory->LoadURIs(loadResults);
-  }
-  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
@@ -579,16 +579,26 @@ class ContentParent final
   PSessionStorageObserverParent* AllocPSessionStorageObserverParent();
 
   virtual mozilla::ipc::IPCResult RecvPSessionStorageObserverConstructor(
       PSessionStorageObserverParent* aActor) override;
 
   bool DeallocPSessionStorageObserverParent(
       PSessionStorageObserverParent* aActor);
 
+  PSHEntryParent* AllocPSHEntryParent(PSHistoryParent* aSHistory,
+                                      uint64_t aSharedID);
+
+  void DeallocPSHEntryParent(PSHEntryParent*);
+
+  PSHistoryParent* AllocPSHistoryParent(
+      const MaybeDiscarded<BrowsingContext>& aContext);
+
+  void DeallocPSHistoryParent(PSHistoryParent* aActor);
+
   bool DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor);
 
   bool DeallocPURLClassifierParent(PURLClassifierParent* aActor);
 
   // Use the PHangMonitor channel to ask the child to repaint a tab.
   void PaintTabWhileInterruptingJS(BrowserParent* aBrowserParent,
                                    const layers::LayersObserverEpoch& aEpoch);
 
@@ -1269,31 +1279,27 @@ class ContentParent final
       uint64_t aTopContextId, const nsACString& aOriginAttrs,
       const nsACString& aOriginKey, const nsTArray<KeyValuePair>& aDefaultData,
       const nsTArray<KeyValuePair>& aSessionData);
 
   mozilla::ipc::IPCResult RecvReportServiceWorkerShutdownProgress(
       uint32_t aShutdownStateId,
       ServiceWorkerShutdownState::Progress aProgress);
 
+  mozilla::ipc::IPCResult RecvUpdateSHEntriesInBC(
+      PSHEntryParent* aNewLSHE, PSHEntryParent* aNewOSHE,
+      const MaybeDiscarded<BrowsingContext>& aMaybeContext);
+
   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);
-
-  mozilla::ipc::IPCResult RecvHistoryGo(
-      const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
-      HistoryGoResolver&& aResolveRequestedIndex);
-
   // 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/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -8,16 +8,17 @@ include "mozilla/GfxMessageUtils.h";
 include "mozilla/dom/DocShellMessageUtils.h";
 include "mozilla/dom/TabMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
 include IPCBlob;
 include IPCStream;
 include protocol PIPCBlobInputStream;
 include ProtocolTypes;
+include protocol PSHEntry;
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
 
 using moveonly struct mozilla::SerializedStructuredCloneBuffer
   from "ipc/IPCMessageUtils.h";
 
 using LayoutDeviceIntRect from "Units.h";
@@ -35,17 +36,19 @@ using mozilla::gfx::SurfaceFormat from "
 using refcounted class nsIPrincipal from "mozilla/dom/PermissionMessageUtils.h";
 using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
 using refcounted class nsIURI from "mozilla/ipc/URIUtils.h";
 using refcounted class nsIContentSecurityPolicy from "mozilla/dom/CSPMessageUtils.h";
 using refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
 using refcounted class nsIReferrerInfo from "mozilla/dom/ReferrerInfoUtils.h";
 using refcounted class nsIVariant from "mozilla/dom/PropertyBagUtils.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using refcounted class nsISHEntry from "nsISHEntry.h";
 using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.h";
+using refcounted class mozilla::dom::CrossProcessSHEntry from "mozilla/dom/MaybeNewPSHEntry.h";
 using mozilla::dom::WindowContextInitializer from "mozilla/dom/WindowContext.h";
 
 namespace mozilla {
 namespace dom {
 
 struct MessagePortIdentifier
 {
   nsID uuid;
@@ -270,16 +273,20 @@ struct DocShellLoadStateInit
 
   nsCString? OriginalURIString;
   int32_t? CancelContentJSEpoch;
 
   nsIInputStream PostDataStream;
   nsIInputStream HeadersStream;
 
   nsString SrcdocData; // useless without sourcedocshell
+  // This might be missing if serialization of session history entries is
+  // not supported. This is the case when 'fission.sessionHistoryInParent'
+  // pref is off.
+  CrossProcessSHEntry SHEntry;
 
   // Fields missing due to lack of need or serialization
   // nsCOMPtr<nsIDocShell> mSourceDocShell;
   // bool mIsSrcDocLoad; // useless without sourcedocshell
   // nsIChannel pendingRedirectedChannel; // sent through other mechanism
 
   uint32_t LoadIdentifier;
 };
@@ -374,10 +381,16 @@ struct OwnerShowInfo {
   ScrollbarPreference scrollbarPreference;
 
   // TODO(emilio): I think we should really be able to figure out these from the
   // parent process too instead.
   bool parentWindowIsActive;
   nsSizeMode sizeMode;
 };
 
+struct SwapEntriesDocshellData {
+  CrossProcessSHEntry oldEntry;
+  CrossProcessSHEntry newEntry;
+  MaybeDiscardedBrowsingContext context;
+};
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -41,16 +41,18 @@ include protocol PWindowGlobal;
 include protocol PPresentation;
 include protocol PURLClassifier;
 include protocol PURLClassifierLocal;
 include protocol PVRManager;
 include protocol PRemoteDecoderManager;
 include protocol PProfiler;
 include protocol PScriptCache;
 include protocol PSessionStorageObserver;
+include protocol PSHEntry;
+include protocol PSHistory;
 include protocol PBenchmarkStorage;
 include DOMTypes;
 include JavaScriptTypes;
 include IPCBlob;
 include IPCStream;
 include PTabContext;
 include PluginTypes;
 include ProtocolTypes;
@@ -115,16 +117,17 @@ using gfxSparseBitSet from "gfxFontUtils
 using FontVisibility from "gfxFontEntry.h";
 using mozilla::dom::MediaControlKeysEvent from "ipc/MediaControlIPC.h";
 using mozilla::dom::MediaPlaybackState from "ipc/MediaControlIPC.h";
 using mozilla::dom::MediaAudibleState from "ipc/MediaControlIPC.h";
 using mozilla::dom::MaybeMediaMetadataBase from "mozilla/dom/MediaSessionIPCUtils.h";
 using mozilla::dom::MediaSessionPlaybackState from "mozilla/dom/MediaSessionBinding.h";
 using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
 using mozilla::dom::ServiceWorkerShutdownState::Progress from "mozilla/dom/ServiceWorkerShutdownState.h";
+using refcounted class mozilla::dom::CrossProcessSHEntry from "mozilla/dom/MaybeNewPSHEntry.h";
 using mozilla::ContentBlockingNotifier::StorageAccessGrantedReason from "mozilla/ContentBlockingNotifier.h";
 using mozilla::ContentBlockingNotifier::BlockingDecision from "mozilla/ContentBlockingNotifier.h";
 using mozilla::ContentBlocking::StorageAccessPromptChoices from "mozilla/ContentBlocking.h";
 using JSActorMessageKind from "mozilla/dom/JSActor.h";
 using JSActorMessageMeta from "mozilla/dom/PWindowGlobal.h";
 
 union ChromeRegistryItem
 {
@@ -397,16 +400,18 @@ nested(upto inside_cpow) sync protocol P
     manages PWebBrowserPersistDocument;
     manages PWebrtcGlobal;
     manages PPresentation;
     manages PURLClassifier;
     manages PURLClassifierLocal;
     manages PScriptCache;
     manages PLoginReputation;
     manages PSessionStorageObserver;
+    manages PSHEntry;
+    manages PSHistory;
     manages PBenchmarkStorage;
 
     // Depending on exactly how the new browser is being created, it might be
     // created from either the child or parent process!
     //
     // The child creates the PBrowser as part of
     // BrowserChild::BrowserFrameProvideWindow (which happens when the child's
     // content calls window.open()), and the parent creates the PBrowser as part
@@ -858,41 +863,47 @@ child:
     // value for every individual BrowsingContext.
     async RegisterBrowsingContextGroup(uint64_t aGroupId, SyncedContextInitializer[] aInits);
 
 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
     // Initialize top-level actor for testing content process sandbox.
     async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint);
 #endif
 
+    async DestroySHEntrySharedState(uint64_t aID);
+
+    async EvictContentViewers(uint64_t[] aToEvictSharedStateIDs);
+
     async LoadURI(MaybeDiscardedBrowsingContext aContext, nsDocShellLoadState aLoadState, bool aSetNavigating)
         returns (bool aSuccess);
 
     async InternalLoad(MaybeDiscardedBrowsingContext aContext, nsDocShellLoadState aLoadState, bool aTakeFocus);
 
     async DisplayLoadError(MaybeDiscardedBrowsingContext aContext, nsString aURI);
 
+    // Tell aContext's docshell to update its mOSHE and mLSHE entries
+    async UpdateSHEntriesInDocShell(CrossProcessSHEntry aOldEntry,
+                                    CrossProcessSHEntry aNewEntry,
+                                    MaybeDiscardedBrowsingContext aContext);
+
     async OnAllowAccessFor(MaybeDiscardedBrowsingContext aParentContext,
                            nsCString aTrackingOrigin,
                            uint32_t aCookieBehavior,
                            StorageAccessGrantedReason aReason);
 
     async OnContentBlockingDecision(MaybeDiscardedBrowsingContext aContext,
                                     BlockingDecision aReason,
                                     uint32_t aRejectedReason);
 
     /**
      * Abort orientationPendingPromises for documents in the child which
      * are part of a BrowsingContextGroup.
      */
     async AbortOrientationPendingPromises(MaybeDiscardedBrowsingContext aContext);
 
-    async HistoryCommitLength(MaybeDiscardedBrowsingContext aContext,
-                              uint32_t aLength);
-
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     async CreateGMPService();
 
     async InitStreamFilter(uint64_t channelId, nsString addonId)
         returns (Endpoint<PStreamFilterChild> aEndpoint);
 
@@ -956,16 +967,21 @@ parent:
         returns (bool success);
 
     async PURLClassifierLocal(nsIURI uri, IPCURLClassifierFeature[] features);
 
     async PLoginReputation(nsIURI formURI);
 
     async PSessionStorageObserver();
 
+    async PSHistory(MaybeDiscardedBrowsingContext aContext);
+
+    // Clone using shared id.
+    sync PSHEntry(nullable PSHistory shistory, uint64_t sharedID);
+
     async PBenchmarkStorage();
 
     // Services remoting
 
     async StartVisitedQueries(nsIURI[] uri);
     async SetURITitle(nsIURI uri, nsString title);
 
     async LoadURIExternal(nsIURI uri, MaybeDiscardedBrowsingContext browsingContext);
@@ -1570,27 +1586,28 @@ parent:
      * ServiceWorkers to the parent process' ServiceWorkerManager's
      * ServiceWorkerShutdownBlocker. (The only other actor chain available
      * for this would be very convoluted and create ordering problems).
      */
     async ReportServiceWorkerShutdownProgress(uint32_t aShutdownStateId,
                                               Progress aProgress);
 
     /**
+     * Whenever docshell updates its SH entries, we need to update them in
+     * the corresponding BrowsingContext
+     */
+    async UpdateSHEntriesInBC(nullable PSHEntry aNewLSHE, nullable
+                              PSHEntry aNewOSHE, MaybeDiscardedBrowsingContext aContext);
+
+    /**
      * 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);
-
-    async HistoryGo(MaybeDiscardedBrowsingContext aContext,
-                    int32_t aOffset) returns(int32_t requestedIndex);
-
 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/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
+++ b/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
@@ -1,30 +1,30 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 protocol PContent;
+include protocol PSHEntry;
 include protocol PWebBrowserPersistResources;
 include protocol PWebBrowserPersistSerialize;
 include protocol PFileDescriptorSet;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 
 include PBackgroundSharedTypes;
 include IPCStream;
 
 using refcounted class nsIReferrerInfo from "mozilla/dom/ReferrerInfoUtils.h";
 
 namespace mozilla {
 
-// FIXME When session history lives in the parent we need to figure out how to
-//       point the parent process at the right entry to get the cache key.
 union SessionHistoryEntryOrCacheKey {
+  nullable PSHEntry;
   uint32_t;
 };
 
 // nsIWebBrowserPersistDocument has attributes which can be read
 // synchronously.  To avoid using sync IPC for them, the actor sends
 // this structure from the child to the parent before the parent actor
 // is exposed to XPCOM.
 struct WebBrowserPersistDocumentAttrs {
--- a/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/dom/Document.h"
 #include "nsIInputStream.h"
 #include "WebBrowserPersistLocalDocument.h"
 #include "WebBrowserPersistResourcesChild.h"
 #include "WebBrowserPersistSerializeChild.h"
+#include "SHEntryChild.h"
 #include "mozilla/StaticPrefs_fission.h"
 
 namespace mozilla {
 
 WebBrowserPersistDocumentChild::WebBrowserPersistDocumentChild() = default;
 
 WebBrowserPersistDocumentChild::~WebBrowserPersistDocumentChild() = default;
 
@@ -52,17 +53,23 @@ void WebBrowserPersistDocumentChild::Sta
   ENSURE(aDocument->GetIsPrivate(&(attrs.isPrivate())));
   ENSURE(aDocument->GetDocumentURI(attrs.documentURI()));
   ENSURE(aDocument->GetBaseURI(attrs.baseURI()));
   ENSURE(aDocument->GetContentType(attrs.contentType()));
   ENSURE(aDocument->GetCharacterSet(attrs.characterSet()));
   ENSURE(aDocument->GetTitle(attrs.title()));
   ENSURE(aDocument->GetContentDisposition(attrs.contentDisposition()));
 
-  if (!StaticPrefs::fission_sessionHistoryInParent()) {
+  // shEntryChild needs to remain in scope until after the SendAttributes call,
+  // to keep the actor alive.
+  RefPtr<dom::SHEntryChild> shEntryChild;
+  if (StaticPrefs::fission_sessionHistoryInParent()) {
+    shEntryChild = aDocument->GetHistory().downcast<dom::SHEntryChild>();
+    attrs.sessionHistoryEntryOrCacheKey() = shEntryChild;
+  } else {
     attrs.sessionHistoryEntryOrCacheKey() = aDocument->GetCacheKey();
   }
 
   ENSURE(aDocument->GetPersistFlags(&(attrs.persistFlags())));
 
   ENSURE(aDocument->GetPrincipal(getter_AddRefs(principal)));
   ENSURE(ipc::PrincipalToPrincipalInfo(principal, &(attrs.principal())));
 
--- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
@@ -3,16 +3,17 @@
  * 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 "WebBrowserPersistRemoteDocument.h"
 #include "WebBrowserPersistDocumentParent.h"
 #include "WebBrowserPersistResourcesParent.h"
 #include "WebBrowserPersistSerializeParent.h"
+#include "SHEntryParent.h"
 #include "mozilla/Unused.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 
 #include "nsDebug.h"
 #include "nsIPrincipal.h"
 
 namespace mozilla {
 
@@ -23,16 +24,22 @@ WebBrowserPersistRemoteDocument ::WebBro
     nsIInputStream* aPostData)
     : mActor(aActor), mAttrs(aAttrs), mPostData(aPostData) {
   auto principalOrErr = ipc::PrincipalInfoToPrincipal(mAttrs.principal());
   if (principalOrErr.isOk()) {
     mPrincipal = principalOrErr.unwrap();
   } else {
     NS_WARNING("Failed to obtain principal!");
   }
+  if (mAttrs.sessionHistoryEntryOrCacheKey().type() ==
+      SessionHistoryEntryOrCacheKey::TPSHEntryParent) {
+    mSHEntry = static_cast<dom::SHEntryParent*>(
+                   mAttrs.sessionHistoryEntryOrCacheKey().get_PSHEntryParent())
+                   ->GetSHEntry();
+  }
 }
 
 WebBrowserPersistRemoteDocument::~WebBrowserPersistRemoteDocument() {
   if (mActor) {
     Unused << WebBrowserPersistDocumentParent::Send__delete__(mActor);
     // That will call mActor->ActorDestroy, which calls this->ActorDestroy
     // (whether or not the IPC send succeeds).
   }
@@ -90,17 +97,21 @@ WebBrowserPersistRemoteDocument::GetCont
   aDisp = mAttrs.contentDisposition();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebBrowserPersistRemoteDocument::GetCacheKey(uint32_t* aCacheKey) {
   *aCacheKey = 0;
   if (mAttrs.sessionHistoryEntryOrCacheKey().type() ==
-      SessionHistoryEntryOrCacheKey::Tuint32_t) {
+      SessionHistoryEntryOrCacheKey::TPSHEntryParent) {
+    if (mSHEntry) {
+      *aCacheKey = mSHEntry->GetCacheKey();
+    }
+  } else {
     *aCacheKey = mAttrs.sessionHistoryEntryOrCacheKey();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebBrowserPersistRemoteDocument::GetPersistFlags(uint32_t* aFlags) {
   *aFlags = mAttrs.persistFlags();
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -802,16 +802,149 @@ description = Legacy NPAPI IPC
 [PPluginScriptableObject::GetParentProperty]
 description = Legacy NPAPI IPC
 [PPluginScriptableObject::GetChildProperty]
 description = Legacy NPAPI IPC
 [PPluginStream::NPN_Write]
 description = Legacy NPAPI IPC
 [PPluginStream::__delete__]
 description = Legacy NPAPI IPC
+
+# Session history
+[PSHistory::GetCount]
+description = Standing up Fission
+[PSHistory::GetIndex]
+description = Standing up Fission
+[PSHistory::SetIndex]
+description = Standing up Fission
+[PSHistory::GetRequestedIndex]
+description = Standing up Fission
+[PSHistory::GetEntryAtIndex]
+description = Standing up Fission
+[PSHistory::PurgeHistory]
+description = Standing up Fission
+[PSHistory::ReloadCurrentEntry]
+description = Standing up Fission
+[PSHistory::GotoIndex]
+description = Standing up Fission
+[PSHistory::GetIndexOfEntry]
+description = Standing up Fission
+[PSHistory::AddEntry]
+description = Standing up Fission
+[PSHistory::ReplaceEntry]
+description = Standing up Fission
+[PSHistory::NotifyOnHistoryReload]
+description = Standing up Fission
+[PSHistory::AddToRootSessionHistory]
+description = Standing up Fission
+[PSHistory::AddChildSHEntryHelper]
+description = Standing up Fission
+[PSHistory::RemoveEntries]
+description = Standing up Fission
+[PSHistory::Reload]
+description = Standing up Fission
+[PSHistory::GetAllEntries]
+description = Standing up Fission
+[PSHistory::FindEntryForBFCache]
+description = Standing up Fission
+[PContent::PSHEntry]
+description = Standing up Fission
+[PSHEntry::Clone]
+description = Standing up Fission
+[PSHEntry::GetURI]
+description = Standing up Fission
+[PSHEntry::GetOriginalURI]
+description = Standing up Fission
+[PSHEntry::GetResultPrincipalURI]
+description = Standing up Fission
+[PSHEntry::GetLoadReplace]
+description = Standing up Fission
+[PSHEntry::GetTitle]
+description = Standing up Fission
+[PSHEntry::GetIsSubFrame]
+description = Standing up Fission
+[PSHEntry::GetReferrerInfo]
+description = Standing up Fission
+[PSHEntry::GetSticky]
+description = Standing up Fission
+[PSHEntry::GetPostData]
+description = Standing up Fission
+[PSHEntry::GetParent]
+description = Standing up Fission
+[PSHEntry::GetLoadType]
+description = Standing up Fission
+[PSHEntry::GetID]
+description = Standing up Fission
+[PSHEntry::GetCacheKey]
+description = Standing up Fission
+[PSHEntry::GetExpirationStatus]
+description = Standing up Fission
+[PSHEntry::GetContentType]
+description = Standing up Fission
+[PSHEntry::GetURIWasModified]
+description = Standing up Fission
+[PSHEntry::GetTriggeringPrincipal]
+description = Standing up Fission
+[PSHEntry::GetPrincipalToInherit]
+description = Standing up Fission
+[PSHEntry::GetStoragePrincipalToInherit]
+description = Standing up Fission
+[PSHEntry::GetCsp]
+description = Standing up Fission
+[PSHEntry::GetStateData]
+description = Standing up Fission
+[PSHEntry::GetDocshellID]
+description = Standing up Fission
+[PSHEntry::GetIsSrcdocEntry]
+description = Standing up Fission
+[PSHEntry::GetSrcdocData]
+description = Standing up Fission
+[PSHEntry::GetBaseURI]
+description = Standing up Fission
+[PSHEntry::GetScrollRestorationIsManual]
+description = Standing up Fission
+[PSHEntry::GetLoadedInThisProcess]
+description = Standing up Fission
+[PSHEntry::GetLastTouched]
+description = Standing up Fission
+[PSHEntry::GetChildCount]
+description = Standing up Fission
+[PSHEntry::GetPersist]
+description = Standing up Fission
+[PSHEntry::GetScrollPosition]
+description = Standing up Fission
+[PSHEntry::GetViewerBounds]
+description = Standing up Fission
+[PSHEntry::HasDetachedEditor]
+description = Standing up Fission
+[PSHEntry::IsDynamicallyAdded]
+description = Standing up Fission
+[PSHEntry::HasDynamicallyAddedChild]
+description = Standing up Fission
+[PSHEntry::AdoptBFCacheEntry]
+description = Standing up Fission
+[PSHEntry::SharesDocumentWith]
+description = Standing up Fission
+[PSHEntry::AddChild]
+description = Standing up Fission
+[PSHEntry::RemoveChild]
+description = Standing up Fission
+[PSHEntry::GetChildAt]
+description = Standing up Fission
+[PSHEntry::GetChildSHEntryIfHasNoDynamicallyAddedChild]
+description = Standing up Fission
+[PSHEntry::ReplaceChild]
+description = Standing up Fission
+[PSHEntry::__delete__]
+description = Standing up Fission
+[PSHEntry::CreateLoadInfo]
+description = Standing up Fission
+[PSHEntry::SyncTreesForSubframeNavigation]
+description = Standing up Fission
+
 # The rest
 [PHeapSnapshotTempFileHelper::OpenHeapSnapshotTempFile]
 description = legacy sync IPC - please add detailed description
 [PBackgroundMutableFile::GetFileId]
 description = legacy sync IPC - please add detailed description
 [PBackgroundIndexedDBUtils::GetFileReferences]
 description = legacy sync IPC - please add detailed description
 [PBrowser::SyncMessage]
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -6,26 +6,24 @@
  * 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"
@@ -527,21 +525,16 @@ 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(
@@ -1155,21 +1148,16 @@ 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,17 +9,16 @@
 
 #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                   \
@@ -395,18 +394,16 @@ 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
@@ -18,17 +18,16 @@ 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 moveonly mozilla::dom::SessionHistoryInfoAndId from "mozilla/dom/SessionHistoryEntry.h";
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // CookieJarSettings IPDL structs
 //-----------------------------------------------------------------------------
 
@@ -462,17 +461,16 @@ 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;
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -149,19 +149,17 @@ already_AddRefed<nsWebBrowser> nsWebBrow
 
   docShell->SetTreeOwner(docShellTreeOwner);
 
   // If the webbrowser is a content docshell item then we won't hear any
   // events from subframes. To solve that we install our own chrome event
   // handler that always gets called (even for subframes) for any bubbling
   // event.
 
-  if (aBrowsingContext->IsTop()) {
-    aBrowsingContext->InitSessionHistory();
-  }
+  docShell->InitSessionHistory();
 
   NS_ENSURE_SUCCESS(docShellAsWin->Create(), nullptr);
 
   // Hook into the OnSecurityChange() notification for lock/unlock icon
   // updates
   // this works because the implementation of nsISecureBrowserUI
   // (nsSecureBrowserUIImpl) calls docShell->SetSecurityUI(this);
   nsCOMPtr<nsISecureBrowserUI> securityUI =