Bug 1588491 - Associate session history entries with a session history object from creation. r=smaug
authorPeter Van der Beken <peterv@propagandism.org>
Thu, 17 Oct 2019 20:03:49 +0200
changeset 500513 9a60f8caece89baa705f4f54862eff3b996a6b22
parent 500512 71bcae2d0e17b19d2eddc9cdfa1fcfa7699d503a
child 500514 13da9115c96bb79d33ff7bb56a170c9834157bf6
push id114165
push userpvanderbeken@mozilla.com
push dateWed, 06 Nov 2019 09:18:40 +0000
treeherdermozilla-inbound@db1ddab2985d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1588491
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1588491 - Associate session history entries with a session history object from creation. r=smaug Differential Revision: https://phabricator.services.mozilla.com/D49170
docshell/base/nsDocShell.cpp
docshell/build/components.conf
docshell/shistory/ChildSHistory.cpp
docshell/shistory/ChildSHistory.h
docshell/shistory/SHEntryChild.cpp
docshell/shistory/SHEntryParent.cpp
docshell/shistory/SHEntryParent.h
docshell/shistory/SHistoryChild.cpp
docshell/shistory/SHistoryParent.cpp
docshell/shistory/SHistoryParent.h
docshell/shistory/nsISHEntry.idl
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/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
toolkit/components/viewsource/content/viewSource-content.js
toolkit/modules/sessionstore/SessionHistory.jsm
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11324,17 +11324,23 @@ nsresult nsDocShell::AddToSessionHistory
     entry = mOSHE;
     if (entry) {
       entry->ClearEntry();
     }
   }
 
   // Create a new entry if necessary.
   if (!entry) {
-    entry = components::SHEntry::Create();
+    nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
+    NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
+
+    RefPtr<ChildSHistory> shistory = webnav->GetSessionHistory();
+    NS_ENSURE_TRUE(shistory, NS_ERROR_FAILURE);
+
+    shistory->LegacySHistory()->CreateEntry(getter_AddRefs(entry));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   // Get the post data & referrer
   nsCOMPtr<nsIInputStream> inputStream;
--- a/docshell/build/components.conf
+++ b/docshell/build/components.conf
@@ -39,27 +39,16 @@ if buildconfig.substs['MOZ_WIDGET_TOOLKI
 
 Headers = ['/docshell/build/nsDocShellModule.h']
 
 InitFunc = 'mozilla::InitDocShellModule'
 UnloadFunc = 'mozilla::UnloadDocShellModule'
 
 Classes = [
     {
-        'name': 'SHEntry',
-        'cid': '{bfd1a791-ad9f-11d3-bdc7-0050040a9b44}',
-        'contract_ids': ['@mozilla.org/browser/session-history-entry;1'],
-        'type': 'nsISHEntry',
-        'constructor': 'mozilla::dom::CreateSHEntry',
-        'headers': [
-            'nsISHEntry.h',
-            'mozilla/dom/ChildSHistory.h',
-        ],
-    },
-    {
         'name': 'DocLoader',
         'cid': '{057b04d0-0ccf-11d2-beba-00805f8a66dc}',
         'contract_ids': ['@mozilla.org/docloaderservice;1'],
         'type': 'nsDocLoader',
         'headers': ['nsDocLoader.h'],
         'init_method': 'Init',
     },
     {
--- a/docshell/shistory/ChildSHistory.cpp
+++ b/docshell/shistory/ChildSHistory.cpp
@@ -120,21 +120,10 @@ nsISupports* ChildSHistory::GetParentObj
   RefPtr<ContentFrameMessageManager> mm;
   if (mDocShell) {
     mm = mDocShell->GetMessageManager();
   }
   // else we must be unlinked... can that happen here?
   return ToSupports(mm);
 }
 
-already_AddRefed<nsISHEntry> CreateSHEntry() {
-  uint64_t sharedID = SHEntryChildShared::CreateSharedID();
-  if (XRE_IsContentProcess() && StaticPrefs::fission_sessionHistoryInParent()) {
-    return do_AddRef(static_cast<SHEntryChild*>(
-        ContentChild::GetSingleton()->SendPSHEntryConstructor(sharedID)));
-  }
-
-  nsCOMPtr<nsISHEntry> entry = new nsLegacySHEntry(sharedID);
-  return entry.forget();
-}
-
 }  // namespace dom
 }  // namespace mozilla
--- a/docshell/shistory/ChildSHistory.h
+++ b/docshell/shistory/ChildSHistory.h
@@ -103,14 +103,12 @@ class ChildSHistory : public nsISupports
     int32_t mOffset;
   };
 
   RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsISHistory> mHistory;
   mozilla::LinkedList<PendingAsyncHistoryNavigation> mPendingNavigations;
 };
 
-already_AddRefed<nsISHEntry> CreateSHEntry();
-
 }  // namespace dom
 }  // namespace mozilla
 
 #endif /* mozilla_dom_ChildSHistory_h */
--- a/docshell/shistory/SHEntryChild.cpp
+++ b/docshell/shistory/SHEntryChild.cpp
@@ -54,17 +54,21 @@ void SHEntryChildShared::EvictContentVie
     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);
-    MOZ_ASSERT(shared, "shared entry can't be null");
+    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() &&
@@ -584,17 +588,17 @@ SHEntryChild::Create(
              ? NS_OK
              : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 SHEntryChild::Clone(nsISHEntry** aResult) {
   NS_IF_ADDREF(*aResult = static_cast<SHEntryChild*>(
                    ContentChild::GetSingleton()->SendPSHEntryConstructor(
-                       this)));
+                       mShared->mSHistory, this)));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SHEntryChild::GetParent(nsISHEntry** aResult) {
   RefPtr<CrossProcessSHEntry> parent;
   if (!SendGetParent(&parent)) {
     return NS_ERROR_FAILURE;
@@ -950,30 +954,22 @@ SHEntryChild::GetLastTouched(uint32_t* a
 }
 
 NS_IMETHODIMP
 SHEntryChild::SetLastTouched(uint32_t aLastTouched) {
   return SendSetLastTouched(aLastTouched) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-SHEntryChild::GetSHistory(nsISHistory** aSHistory) {
+SHEntryChild::GetShistory(nsISHistory** aSHistory) {
   *aSHistory = do_AddRef(mShared->mSHistory).take();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SHEntryChild::SetSHistory(nsISHistory* aSHistory) {
-  // mSHistory can not be changed once it's set
-  MOZ_ASSERT(!mShared->mSHistory || (mShared->mSHistory == aSHistory));
-  mShared->mSHistory = static_cast<SHistoryChild*>(aSHistory);
-  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;
 }
--- a/docshell/shistory/SHEntryParent.cpp
+++ b/docshell/shistory/SHEntryParent.cpp
@@ -9,27 +9,34 @@
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/MaybeNewPSHEntry.h"
 #include "nsStructuredCloneContainer.h"
 
 namespace mozilla {
 namespace dom {
 
 SHEntrySharedParent::SHEntrySharedParent(PContentParent* aContentParent,
+                                         LegacySHistory* aSHistory,
                                          uint64_t aSharedID)
-    : SHEntrySharedParentState(aSharedID), mContentParent(aContentParent) {}
+    : 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;
 }
 
@@ -39,22 +46,18 @@ MaybeNewPSHEntryParent LegacySHEntry::Ge
     return AsVariant(static_cast<PSHEntryParent*>(mActor));
   }
 
   return AsVariant(NewPSHEntry{
       aContentParent->OpenPSHEntryEndpoint(CreateActor()), mShared->mID});
 }
 
 void LegacySHEntry::AbandonBFCacheEntry(uint64_t aNewSharedID) {
-  PContentParent* contentParent =
-      static_cast<SHEntrySharedParent*>(mShared.get())->GetContentParent();
-  RefPtr<SHEntrySharedParent> shared =
-      new SHEntrySharedParent(contentParent, aNewSharedID);
-  shared->CopyFrom(mShared);
-  mShared = shared.forget();
+  mShared =
+      static_cast<SHEntrySharedParent*>(mShared.get())->Duplicate(aNewSharedID);
 }
 
 NS_IMETHODIMP
 LegacySHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
   *aBFCacheID = mShared->GetID();
   return NS_OK;
 }
 
@@ -296,17 +299,17 @@ bool SHEntryParent::RecvSetCsp(nsIConten
   return true;
 }
 
 bool SHEntryParent::RecvGetStateData(ClonedMessageData* aData) {
   nsCOMPtr<nsIStructuredCloneContainer> container = mEntry->GetStateData();
   if (container) {
     static_cast<nsStructuredCloneContainer*>(container.get())
         ->BuildClonedMessageDataForParent(
-            static_cast<ContentParent*>(Manager()), *aData);
+            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) {
--- a/docshell/shistory/SHEntryParent.h
+++ b/docshell/shistory/SHEntryParent.h
@@ -11,47 +11,60 @@
 #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, uint64_t aSharedID);
+  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;
 
-  PContentParent* GetContentParent() { return mContentParent.get(); }
-
  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* aParent, uint64_t aSharedID)
-      : nsSHEntry(new SHEntrySharedParent(aParent, aSharedID)),
-        mActor(nullptr) {}
+  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;
@@ -61,17 +74,17 @@ class LegacySHEntry final : public nsSHE
   uint64_t GetSharedStateID() const { return mShared->GetID(); }
 
   dom::SHEntrySharedParentState* GetSharedState() const {
     return mShared.get();
   }
 
  private:
   friend class SHEntryParent;
-  friend class ContentParent;
+  friend class SHistoryParent;
 
   ~LegacySHEntry() {}
 
   SHEntryParent* CreateActor();
 
   SHEntryParent* mActor;
 };
 
--- a/docshell/shistory/SHistoryChild.cpp
+++ b/docshell/shistory/SHistoryChild.cpp
@@ -190,17 +190,16 @@ SHistoryChild::AddEntry(nsISHEntry* aEnt
   nsresult rv;
   int32_t entriesPurged;
   if (!SendAddEntry(static_cast<SHEntryChild*>(aEntry), aPersist, &rv,
                     &entriesPurged)) {
     return NS_ERROR_FAILURE;
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
-  aEntry->SetSHistory(this);
   if (mRootDocShell) {
     aEntry->SetDocshellID(mRootDocShell->HistoryID());
 
     if (entriesPurged > 0) {
       mRootDocShell->HistoryPurged(entriesPurged);
     }
   }
 
@@ -219,17 +218,16 @@ 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);
 
-  aReplaceEntry->SetSHistory(this);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SHistoryChild::NotifyOnHistoryReload(bool* _retval) {
   return SendNotifyOnHistoryReload(_retval) ? NS_OK : NS_ERROR_FAILURE;
 }
 
@@ -354,16 +352,28 @@ SHistoryChild::Reload(uint32_t aReloadFl
 
   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(LoadSHEntryData& aLoadData) {
   nsCOMPtr<nsIDocShell> docShell = aLoadData.browsingContext()->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   RefPtr<SHEntryChild> entry;
   if (aLoadData.shEntry()) {
     entry = aLoadData.shEntry()->ToSHEntryChild();
   }
--- a/docshell/shistory/SHistoryParent.cpp
+++ b/docshell/shistory/SHistoryParent.cpp
@@ -31,20 +31,36 @@ static void FillInLoadResult(PContentPar
         static_cast<LegacySHEntry*>(aLoadResult.mLoadState->SHEntry()),
         aLoadResult.mBrowsingContext, aLoadResult.mLoadState);
   } else {
     *aResult = aRv;
   }
 }
 
 SHistoryParent::SHistoryParent(CanonicalBrowsingContext* aContext)
-    : mContext(aContext), mHistory(new LegacySHistory(aContext, nsID())) {}
+    : mHistory(new LegacySHistory(aContext, nsID())) {}
 
 SHistoryParent::~SHistoryParent() {}
 
+SHEntryParent* SHistoryParent::CreateEntry(
+    PContentParent* aContentParent, PSHistoryParent* aSHistoryParent,
+    const PSHEntryOrSharedID& aEntryOrSharedID) {
+  RefPtr<LegacySHEntry> entry;
+  if (aEntryOrSharedID.type() == PSHEntryOrSharedID::Tuint64_t) {
+    entry = new LegacySHEntry(
+        aContentParent, static_cast<SHistoryParent*>(aSHistoryParent)->mHistory,
+        aEntryOrSharedID.get_uint64_t());
+  } else {
+    entry = new LegacySHEntry(*(
+        static_cast<const SHEntryParent*>(aEntryOrSharedID.get_PSHEntryParent())
+            ->mEntry));
+  }
+  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));
@@ -135,16 +151,17 @@ bool SHistoryParent::RecvEvictOutOfRange
 }
 
 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,
@@ -327,10 +344,21 @@ void LegacySHistory::EvictOutOfRangeWind
     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;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/docshell/shistory/SHistoryParent.h
+++ b/docshell/shistory/SHistoryParent.h
@@ -2,54 +2,59 @@
 /* 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 CanonicalBrowsingContext;
 class SHistoryParent;
 
 /**
  * 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:
   virtual ~LegacySHistory() {}
 
   void EvictOutOfRangeWindowContentViewers(int32_t aIndex) override;
 
  public:
-  LegacySHistory(mozilla::dom::CanonicalBrowsingContext* aRootBC,
-                 const nsID& aDocShellID);
+  LegacySHistory(CanonicalBrowsingContext* aRootBC, const nsID& aDocShellID);
+
+  NS_IMETHOD CreateEntry(nsISHEntry** aEntry) override;
 };
 
 /**
  * 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 PSHistoryParent;
   friend class SHEntryParent;
 
  public:
   explicit SHistoryParent(CanonicalBrowsingContext* aContext);
   virtual ~SHistoryParent();
 
+  static SHEntryParent* CreateEntry(PContentParent* aContentParent,
+                                    PSHistoryParent* aSHistoryParent,
+                                    const PSHEntryOrSharedID& aEntryOrSharedID);
+
  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);
@@ -79,16 +84,15 @@ class SHistoryParent final : public PSHi
                                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);
 
-  RefPtr<CanonicalBrowsingContext> mContext;
   RefPtr<LegacySHistory> mHistory;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif /* mozilla_dom_SHistoryParent_h */
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -213,21 +213,19 @@ interface nsISHEntry : nsISupports
 
     /**
      * Flag to indicate that the history entry was originally loaded in the
      * current process. This flag does not survive a browser process switch.
      */
     [infallible] readonly attribute boolean loadedInThisProcess;
 
     /**
-     * The session history it belongs to. It's usually only set on root entries.
-     * SHEntry is strictly bound to the SHistory it belongs to; it should not be
-     * changed once set to a non-null value.
+     * The session history it belongs to.
      */
-    [noscript, infallible] attribute nsISHistory SHistory;
+    [infallible] readonly attribute nsISHistory shistory;
 
     /**
      * A number that is assigned by the sHistory when the entry is activated
      */
     [noscript, infallible] attribute unsigned long lastTouched;
 
     /**
      * The current number of nsISHEntries which are immediate children of this
--- a/docshell/shistory/nsISHistory.idl
+++ b/docshell/shistory/nsISHistory.idl
@@ -259,9 +259,11 @@ interface nsISHistory: nsISupports
   void RemoveFrameEntries(in nsISHEntry aEntry);
 
   [noscript]
   void Reload(in unsigned long aReloadFlags);
 
   [notxpcom] void EnsureCorrectEntryAtCurrIndex(in nsISHEntry aEntry);
   
   [notxpcom] void EvictContentViewersOrReplaceEntry(in nsISHEntry aNewSHEntry, in bool aReplace);
+
+  nsISHEntry createEntry();
 };
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -915,32 +915,23 @@ nsSHEntry::GetLastTouched(uint32_t* aLas
 
 NS_IMETHODIMP
 nsSHEntry::SetLastTouched(uint32_t aLastTouched) {
   mShared->mLastTouched = aLastTouched;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetSHistory(nsISHistory** aSHistory) {
+nsSHEntry::GetShistory(nsISHistory** aSHistory) {
   nsCOMPtr<nsISHistory> shistory(do_QueryReferent(mShared->mSHistory));
   shistory.forget(aSHistory);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetSHistory(nsISHistory* aSHistory) {
-  nsWeakPtr shistory = do_GetWeakReference(aSHistory);
-  // mSHistory can not be changed once it's set
-  MOZ_ASSERT(!mShared->mSHistory || (mShared->mSHistory == shistory));
-  mShared->mSHistory = shistory;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsSHEntry::SetLoadTypeAsHistory() {
   // Set the LoadType by default to loadHistory during creation
   mLoadType = LOAD_HISTORY;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetPersist(bool* aPersist) {
@@ -1042,18 +1033,18 @@ void nsSHEntry::EvictContentViewer() {
     // Drop the presentation state before destroying the viewer, so that
     // document teardown is able to correctly persist the state.
     SetContentViewer(nullptr);
     SyncPresentationState();
     viewer->Destroy();
   }
 }
 
-nsLegacySHEntry::nsLegacySHEntry(uint64_t aID)
-    : nsSHEntry(new nsSHEntryShared(aID)) {}
+nsLegacySHEntry::nsLegacySHEntry(nsSHistory* aHistory, uint64_t aID)
+    : nsSHEntry(new nsSHEntryShared(aHistory, aID)) {}
 
 NS_IMETHODIMP
 nsLegacySHEntry::SetContentViewer(nsIContentViewer* aViewer) {
   return GetState()->SetContentViewer(aViewer);
 }
 
 NS_IMETHODIMP
 nsLegacySHEntry::GetContentViewer(nsIContentViewer** aResult) {
@@ -1200,18 +1191,18 @@ bool nsLegacySHEntry::HasDetachedEditor(
 }
 
 bool nsLegacySHEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry) {
   return static_cast<nsIBFCacheEntry*>(GetState()) == aEntry;
 }
 
 NS_IMETHODIMP
 nsLegacySHEntry::AbandonBFCacheEntry() {
-  mShared = nsSHEntryShared::Duplicate(
-      GetState(), mozilla::dom::SHEntryChildShared::CreateSharedID());
+  mShared =
+      GetState()->Duplicate(mozilla::dom::SHEntryChildShared::CreateSharedID());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLegacySHEntry::GetBfcacheID(uint64_t* aBFCacheID) {
   *aBFCacheID = mShared->GetID();
   return NS_OK;
 }
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -74,17 +74,17 @@ class nsSHEntry : public nsISHEntry {
 /**
  * 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(uint64_t aID);
+  explicit nsLegacySHEntry(nsSHistory* 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;
--- a/docshell/shistory/nsSHEntryShared.cpp
+++ b/docshell/shistory/nsSHEntryShared.cpp
@@ -8,42 +8,49 @@
 
 #include "nsArray.h"
 #include "nsDocShellEditorData.h"
 #include "nsIContentViewer.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "mozilla/dom/Document.h"
 #include "nsILayoutHistoryState.h"
-#include "nsISHistory.h"
 #include "nsIWebNavigation.h"
+#include "nsSHistory.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
 
 namespace dom = mozilla::dom;
 
-void nsSHEntryShared::Shutdown() {}
+namespace mozilla {
+namespace dom {
 
-dom::SHEntrySharedParentState::SHEntrySharedParentState(uint64_t aID)
+SHEntrySharedParentState::SHEntrySharedParentState(nsSHistory* aSHistory,
+                                                   uint64_t aID)
+    : SHEntrySharedParentState(nsWeakPtr(do_GetWeakReference(aSHistory)).get(),
+                               aID) {}
+
+SHEntrySharedParentState::SHEntrySharedParentState(nsIWeakReference* aSHistory,
+                                                   uint64_t aID)
     : mDocShellID({0}),
       mViewerBounds(0, 0, 0, 0),
       mCacheKey(0),
       mLastTouched(0),
       mID(aID),
+      mSHistory(aSHistory),
       mIsFrameNavigation(false),
       mSticky(true),
       mDynamicallyCreated(false),
       mExpired(false) {}
 
-dom::SHEntrySharedParentState::~SHEntrySharedParentState() {}
+SHEntrySharedParentState::~SHEntrySharedParentState() {}
 
-void dom::SHEntrySharedParentState::CopyFrom(
-    dom::SHEntrySharedParentState* aEntry) {
+void SHEntrySharedParentState::CopyFrom(SHEntrySharedParentState* aEntry) {
   mDocShellID = aEntry->mDocShellID;
   mTriggeringPrincipal = aEntry->mTriggeringPrincipal;
   mPrincipalToInherit = aEntry->mPrincipalToInherit;
   mStoragePrincipalToInherit = aEntry->mStoragePrincipalToInherit;
   mCsp = aEntry->mCsp;
   mContentType.Assign(aEntry->mContentType);
   mIsFrameNavigation = aEntry->mIsFrameNavigation;
   mSticky = aEntry->mSticky;
@@ -57,24 +64,25 @@ void dom::SHEntrySharedParentState::Noti
     RefPtr<nsSHistory> nsshistory = static_cast<nsSHistory*>(shistory.get());
     nsshistory->NotifyListenersContentViewerEvicted(1);
   }
 }
 
 dom::SHEntrySharedChildState::SHEntrySharedChildState()
     : mSaveLayoutState(true) {}
 
-void dom::SHEntrySharedChildState::CopyFrom(
-    dom::SHEntrySharedChildState* aEntry) {
+void SHEntrySharedChildState::CopyFrom(SHEntrySharedChildState* aEntry) {
   mChildShells.AppendObjects(aEntry->mChildShells);
   mSaveLayoutState = aEntry->mSaveLayoutState;
 }
 
-nsSHEntryShared::nsSHEntryShared(uint64_t aID)
-    : dom::SHEntrySharedParentState(aID) {}
+}  // namespace dom
+}  // namespace mozilla
+
+void nsSHEntryShared::Shutdown() {}
 
 nsSHEntryShared::~nsSHEntryShared() {
   // 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();
 
@@ -89,21 +97,21 @@ nsSHEntryShared::~nsSHEntryShared() {
   }
 }
 
 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(
-    nsSHEntryShared* aEntry, uint64_t aNewSharedID) {
-  RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(aNewSharedID);
+    uint64_t aNewSharedID) {
+  RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(this, aNewSharedID);
 
-  newEntry->dom::SHEntrySharedParentState::CopyFrom(aEntry);
-  newEntry->dom::SHEntrySharedChildState::CopyFrom(aEntry);
+  newEntry->dom::SHEntrySharedParentState::CopyFrom(this);
+  newEntry->dom::SHEntrySharedChildState::CopyFrom(this);
 
   return newEntry.forget();
 }
 
 void nsSHEntryShared::RemoveFromExpirationTracker() {
   nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory);
   if (shistory && GetExpirationState()->IsTracked()) {
     shistory->RemoveFromExpirationTracker(this);
--- a/docshell/shistory/nsSHEntryShared.h
+++ b/docshell/shistory/nsSHEntryShared.h
@@ -21,16 +21,17 @@
 
 class nsSHEntry;
 class nsISHEntry;
 class nsIContentViewer;
 class nsIDocShellTreeItem;
 class nsILayoutHistoryState;
 class nsDocShellEditorData;
 class nsIMutableArray;
+class nsSHistory;
 
 // A document may have multiple SHEntries, either due to hash navigations or
 // calls to history.pushState.  SHEntries corresponding to the same document
 // share many members; in particular, they share state related to the
 // back/forward cache.
 //
 // The classes defined here are the vehicle for this sharing.
 //
@@ -42,25 +43,25 @@ namespace dom {
 class Document;
 
 /**
  * SHEntrySharedParentState holds the shared state that can live in the parent
  * process.
  */
 class SHEntrySharedParentState {
  public:
-  explicit SHEntrySharedParentState(uint64_t aID);
-
   uint64_t GetID() const { return mID; }
 
   void NotifyListenersContentViewerEvicted();
 
  protected:
-  friend class nsSHEntry;
-
+  SHEntrySharedParentState(nsSHistory* aSHistory, uint64_t aID);
+  SHEntrySharedParentState(SHEntrySharedParentState* aDuplicate, uint64_t aID)
+      : SHEntrySharedParentState(aDuplicate->mSHistory, aID) {}
+  SHEntrySharedParentState(nsIWeakReference* aDuplicate, uint64_t aID);
   virtual ~SHEntrySharedParentState();
   NS_INLINE_DECL_VIRTUAL_REFCOUNTING_WITH_DESTROY(SHEntrySharedParentState,
                                                   Destroy())
 
   virtual void Destroy() { delete this; }
 
   void CopyFrom(SHEntrySharedParentState* aSource);
 
@@ -132,33 +133,32 @@ class SHEntrySharedChildState {
 class nsSHEntryShared final : public nsIBFCacheEntry,
                               public nsStubMutationObserver,
                               public mozilla::dom::SHEntrySharedParentState,
                               public mozilla::dom::SHEntrySharedChildState {
  public:
   static void EnsureHistoryTracker();
   static void Shutdown();
 
-  explicit nsSHEntryShared(uint64_t aID);
+  using SHEntrySharedParentState::SHEntrySharedParentState;
+
+  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; }
 
-  static already_AddRefed<nsSHEntryShared> Duplicate(nsSHEntryShared* aEntry,
-                                                     uint64_t aNewSharedID);
-
  private:
   ~nsSHEntryShared();
 
   friend class nsLegacySHEntry;
 
   void RemoveFromExpirationTracker();
   void SyncPresentationState();
   void DropPresentationState();
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -189,20 +189,20 @@ void nsSHistory::EvictContentViewerForEn
   // well.
   int32_t index = GetIndexOfEntry(aEntry);
   if (index != -1) {
     RemoveDynEntries(index, aEntry);
   }
 }
 
 nsSHistory::nsSHistory(BrowsingContext* aRootBC, const nsID& aRootDocShellID)
-    : mIsRemote(false),
+    : mRootBC(aRootBC),
+      mIsRemote(false),
       mIndex(-1),
       mRequestedIndex(-1),
-      mRootBC(aRootBC),
       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())) {
@@ -548,27 +548,25 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntr
   int32_t dummy = 0;
   return AddEntry(aSHEntry, aPersist, &dummy);
 }
 
 nsresult nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist,
                               int32_t* aEntriesPurged) {
   NS_ENSURE_ARG(aSHEntry);
 
-  nsCOMPtr<nsISHistory> shistoryOfEntry = aSHEntry->GetSHistory();
-  if (shistoryOfEntry && shistoryOfEntry != this) {
+  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;
   }
 
-  aSHEntry->SetSHistory(this);
-
   // If we have a root docshell, update the docshell id of the root shentry to
   // match the id of that docshell
   if (mRootBC) {
     aSHEntry->SetDocshellID(mRootDocShellID);
   }
 
   if (mIndex >= 0) {
     MOZ_ASSERT(mIndex < Length(), "Index out of range!");
@@ -765,27 +763,25 @@ nsSHistory::RemoveSHistoryListener(nsISH
 NS_IMETHODIMP
 nsSHistory::ReplaceEntry(int32_t aIndex, nsISHEntry* aReplaceEntry) {
   NS_ENSURE_ARG(aReplaceEntry);
 
   if (aIndex < 0 || aIndex >= Length()) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsISHistory> shistoryOfEntry = aReplaceEntry->GetSHistory();
-  if (shistoryOfEntry && shistoryOfEntry != this) {
+  nsCOMPtr<nsISHistory> shistoryOfEntry = aReplaceEntry->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;
   }
 
-  aReplaceEntry->SetSHistory(this);
-
   NOTIFY_LISTENERS(OnHistoryReplaceEntry, ());
 
   aReplaceEntry->SetPersist(true);
   mEntries[aIndex] = aReplaceEntry;
 
   return NS_OK;
 }
 
@@ -1573,8 +1569,16 @@ nsresult nsSHistory::InitiateLoad(nsISHE
   loadState->SetFirstParty(false);
   nsCOMPtr<nsIContentSecurityPolicy> csp = aFrameEntry->GetCsp();
   loadState->SetCsp(csp);
 
   aLoadResult.mLoadState = loadState.forget();
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsSHistory::CreateEntry(nsISHEntry** aEntry) {
+  nsCOMPtr<nsISHEntry> entry =
+      new nsLegacySHEntry(this, SHEntryChildShared::CreateSharedID());
+  entry.forget(aEntry);
+  return NS_OK;
+}
--- a/docshell/shistory/nsSHistory.h
+++ b/docshell/shistory/nsSHistory.h
@@ -143,16 +143,19 @@ class nsSHistory : public mozilla::Linke
 
   void WindowIndices(int32_t aIndex, int32_t* aOutStartIndex,
                      int32_t* aOutEndIndex);
   void NotifyListenersContentViewerEvicted(uint32_t aNumEvicted);
 
  protected:
   virtual ~nsSHistory();
 
+  // Weak reference. Do not refcount this.
+  mozilla::dom::BrowsingContext* mRootBC;
+
  private:
   friend class nsSHistoryObserver;
 
   nsresult LoadDifferingEntries(nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry,
                                 mozilla::dom::BrowsingContext* aRootBC,
                                 long aLoadType, bool& aDifferenceFound,
                                 LoadEntryResult& aLoadResult);
   nsresult InitiateLoad(nsISHEntry* aFrameEntry,
@@ -202,18 +205,16 @@ class nsSHistory : public mozilla::Linke
   mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
 
   int32_t mIndex;           // -1 means "no index"
   int32_t mRequestedIndex;  // -1 means "no requested index"
 
   // Session History listeners
   nsAutoTObserverArray<nsWeakPtr, 2> mListeners;
 
-  // Weak reference. Do not refcount this.
-  mozilla::dom::BrowsingContext* mRootBC;
   nsID mRootDocShellID;
 
   // Max viewers allowed total, across all SHistory objects
   static int32_t sHistoryMaxTotalViewers;
 };
 
 inline nsISupports* ToSupports(nsSHistory* aObj) {
   return static_cast<nsISHistory*>(aObj);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3474,17 +3474,17 @@ bool ContentChild::DeallocPSessionStorag
     PSessionStorageObserverChild* aActor) {
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
 PSHEntryChild* ContentChild::AllocPSHEntryChild(
-    const PSHEntryOrSharedID& aEntryOrSharedID) {
+    PSHistoryChild* aSHistory, const PSHEntryOrSharedID& aEntryOrSharedID) {
   // 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;
   if (aEntryOrSharedID.type() == PSHEntryOrSharedID::Tuint64_t) {
     child = new SHEntryChild(aEntryOrSharedID.get_uint64_t());
   } else {
     child = new SHEntryChild(
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -619,17 +619,18 @@ class ContentChild final : public PConte
 
   bool DeallocPLoginReputationChild(PLoginReputationChild* aActor);
 
   PSessionStorageObserverChild* AllocPSessionStorageObserverChild();
 
   bool DeallocPSessionStorageObserverChild(
       PSessionStorageObserverChild* aActor);
 
-  PSHEntryChild* AllocPSHEntryChild(const PSHEntryOrSharedID& aEntryOrSharedID);
+  PSHEntryChild* AllocPSHEntryChild(PSHistoryChild* aSHistory,
+                                    const PSHEntryOrSharedID& aEntryOrSharedID);
   void DeallocPSHEntryChild(PSHEntryChild*);
 
   PSHistoryChild* AllocPSHistoryChild(BrowsingContext* aContext);
 
   void DeallocPSHistoryChild(PSHistoryChild* aActor);
 
   nsTArray<LookAndFeelInt>& LookAndFeelCache() { return mLookAndFeelCache; }
 
@@ -686,17 +687,18 @@ class ContentChild final : public PConte
   }
   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 RecvEvictContentViewers(
+      nsTArray<uint64_t>&& aToEvictSharedStateIDs);
 
 #ifdef NIGHTLY_BUILD
   // Fetch the current number of pending input events.
   //
   // NOTE: This method performs an atomic read, and is safe to call from all
   // threads.
   uint32_t GetPendingInputEvents() { return mPendingInputEvents; }
 #endif
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5697,26 +5697,18 @@ bool ContentParent::DeallocPSessionStora
     PSessionStorageObserverParent* aActor) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
 }
 
 PSHEntryParent* ContentParent::AllocPSHEntryParent(
-    const PSHEntryOrSharedID& aEntryOrSharedID) {
-  RefPtr<LegacySHEntry> entry;
-  if (aEntryOrSharedID.type() == PSHEntryOrSharedID::Tuint64_t) {
-    entry = new LegacySHEntry(this, aEntryOrSharedID.get_uint64_t());
-  } else {
-    entry = new LegacySHEntry(*(
-        static_cast<const SHEntryParent*>(aEntryOrSharedID.get_PSHEntryParent())
-            ->mEntry));
-  }
-  return entry->CreateActor();
+    PSHistoryParent* aSHistory, const PSHEntryOrSharedID& aEntryOrSharedID) {
+  return SHistoryParent::CreateEntry(this, aSHistory, aEntryOrSharedID);
 }
 
 void ContentParent::DeallocPSHEntryParent(PSHEntryParent* aEntry) {
   delete static_cast<SHEntryParent*>(aEntry);
 }
 
 PSHistoryParent* ContentParent::AllocPSHistoryParent(
     BrowsingContext* aContext) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -578,17 +578,17 @@ class ContentParent final : public PCont
 
   virtual mozilla::ipc::IPCResult RecvPSessionStorageObserverConstructor(
       PSessionStorageObserverParent* aActor) override;
 
   bool DeallocPSessionStorageObserverParent(
       PSessionStorageObserverParent* aActor);
 
   PSHEntryParent* AllocPSHEntryParent(
-      const PSHEntryOrSharedID& aEntryOrSharedID);
+      PSHistoryParent* aSHistory, const PSHEntryOrSharedID& aEntryOrSharedID);
 
   void DeallocPSHEntryParent(PSHEntryParent*);
 
   PSHistoryParent* AllocPSHistoryParent(BrowsingContext* aContext);
 
   void DeallocPSHistoryParent(PSHistoryParent* aActor);
 
   bool DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -923,17 +923,17 @@ parent:
 
     async PLoginReputation(URIParams formURI);
 
     async PSessionStorageObserver();
 
     async PSHistory(BrowsingContext aContext);
 
     // Clone from entry or use shared id.
-    sync PSHEntry(PSHEntryOrSharedID entryOrSharedID);
+    sync PSHEntry(PSHistory shistory, PSHEntryOrSharedID entryOrSharedID);
 
     async PBenchmarkStorage();
 
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async SetURITitle(URIParams uri, nsString title);
 
--- a/toolkit/components/viewsource/content/viewSource-content.js
+++ b/toolkit/components/viewsource/content/viewSource-content.js
@@ -270,28 +270,26 @@ var ViewSourceContent = {
       );
     } catch (e) {
       // We were not able to load the source from the network cache.
       this.loadSourceFromURL(viewSrcURL);
       return;
     }
 
     let shEntrySource = pageDescriptor.QueryInterface(Ci.nsISHEntry);
-    let shEntry = Cc[
-      "@mozilla.org/browser/session-history-entry;1"
-    ].createInstance(Ci.nsISHEntry);
+    let shistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory
+      .legacySHistory;
+    let shEntry = shistory.createEntry();
     shEntry.URI = Services.io.newURI(viewSrcURL);
     shEntry.title = viewSrcURL;
     let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     shEntry.triggeringPrincipal = systemPrincipal;
     shEntry.setLoadTypeAsHistory();
     shEntry.cacheKey = shEntrySource.cacheKey;
-    docShell
-      .QueryInterface(Ci.nsIWebNavigation)
-      .sessionHistory.legacySHistory.addEntry(shEntry, true);
+    shistory.addEntry(shEntry, true);
   },
 
   /**
    * Load some URL in the browser.
    *
    * @param URL
    *        The URL string to load.
    */
--- a/toolkit/modules/sessionstore/SessionHistory.jsm
+++ b/toolkit/modules/sessionstore/SessionHistory.jsm
@@ -346,17 +346,17 @@ var SessionHistoryInternal = {
     for (let i = 0; i < tabData.entries.length; i++) {
       let entry = tabData.entries[i];
       // XXXzpao Wallpaper patch for bug 514751
       if (!entry.url) {
         continue;
       }
       let persist = "persist" in entry ? entry.persist : true;
       history.addEntry(
-        this.deserializeEntry(entry, idMap, docIdentMap),
+        this.deserializeEntry(entry, idMap, docIdentMap, history),
         persist
       );
     }
 
     // Select the right history entry.
     let index = tabData.index - 1;
     if (index < history.count && history.index != index) {
       history.index = index;
@@ -370,20 +370,18 @@ var SessionHistoryInternal = {
    * @param entry
    *        Object containing serialized history data for a URL
    * @param idMap
    *        Hash for ensuring unique frame IDs
    * @param docIdentMap
    *        Hash to ensure reuse of BFCache entries
    * @returns nsISHEntry
    */
-  deserializeEntry(entry, idMap, docIdentMap) {
-    var shEntry = Cc[
-      "@mozilla.org/browser/session-history-entry;1"
-    ].createInstance(Ci.nsISHEntry);
+  deserializeEntry(entry, idMap, docIdentMap, shistory) {
+    var shEntry = shistory.createEntry();
 
     shEntry.URI = Services.io.newURI(entry.url);
     shEntry.title = entry.title || entry.url;
     if (entry.subframe) {
       shEntry.isSubFrame = entry.subframe || false;
     }
     shEntry.setLoadTypeAsHistory();
     if (entry.contentType) {
@@ -558,17 +556,22 @@ var SessionHistoryInternal = {
         //
         // So as a hack to fix this, we restrict the scope of a doc identifier
         // to be a node's siblings and cousins, and pass childDocIdents, not
         // aDocIdents, to _deserializeHistoryEntry.  That is, we say that two
         // SHEntries with the same doc identifier have the same document iff
         // they have the same parent or their parents have the same document.
 
         shEntry.AddChild(
-          this.deserializeEntry(entry.children[i], idMap, childDocIdents),
+          this.deserializeEntry(
+            entry.children[i],
+            idMap,
+            childDocIdents,
+            shEntry.shistory
+          ),
           i
         );
       }
     }
 
     return shEntry;
   },