Bug 1434768 - Part 2: Replace nsDocShell::mSessionHistory with ChildSHistory, r=bz
authorNika Layzell <nika@thelayzells.com>
Thu, 01 Feb 2018 17:35:47 -0500
changeset 412754 c1dc655523000be37d89f5231636c47d70f6d141
parent 412753 47c477fefd3b83f5888a558b546a92d525d1cbd1
child 412755 3bc418e5727e0f682909f0543db3c92376840d7c
push id33818
push userapavel@mozilla.com
push dateWed, 11 Apr 2018 14:36:40 +0000
treeherdermozilla-central@cfe6399e142c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1434768
milestone61.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 1434768 - Part 2: Replace nsDocShell::mSessionHistory with ChildSHistory, r=bz
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
docshell/base/nsIWebNavigation.idl
docshell/shistory/ParentSHistory.cpp
docshell/shistory/nsSHistory.cpp
dom/base/nsCCUncollectableMarker.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsHistory.cpp
dom/base/nsHistory.h
dom/browser-element/BrowserElementChildPreload.js
layout/base/nsDocumentViewer.cpp
toolkit/components/browser/nsWebBrowser.cpp
toolkit/components/browser/nsWebBrowser.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -48,16 +48,17 @@
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ServiceWorkerInterceptController.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/ChildSHistory.h"
 
 
 #include "mozilla/net/ReferrerPolicy.h"
 
 #include "nsIApplicationCacheChannel.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIAppShell.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
@@ -418,19 +419,18 @@ nsDocShell::~nsDocShell()
 {
   MOZ_ASSERT(!mObserved);
 
   // Avoid notifying observers while we're in the dtor.
   mIsBeingDestroyed = true;
 
   Destroy();
 
-  nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
-  if (shPrivate) {
-    shPrivate->SetRootDocShell(nullptr);
+  if (mSessionHistory) {
+    mSessionHistory->LegacySHistoryInternal()->SetRootDocShell(nullptr);
   }
 
   if (--gDocShellCount == 0) {
     NS_IF_RELEASE(sURIFixup);
   }
 
   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
 
@@ -501,17 +501,18 @@ nsDocShell::DestroyChildren()
 
   nsDocLoader::DestroyChildren();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell,
                                    nsDocLoader,
                                    mSessionStorageManager,
                                    mScriptGlobal,
-                                   mInitialClientSource)
+                                   mInitialClientSource,
+                                   mSessionHistory)
 
 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)
   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
@@ -601,20 +602,21 @@ nsDocShell::GetInterface(const nsIID& aI
 
     *aSink = prompt;
     return NS_OK;
   } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
              aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
     return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
       NS_OK : NS_NOINTERFACE;
   } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
-    nsCOMPtr<nsISHistory> shistory;
-    nsresult rv = GetSessionHistory(getter_AddRefs(shistory));
-    if (NS_SUCCEEDED(rv) && shistory) {
-      shistory.forget(aSink);
+    RefPtr<ChildSHistory> shistory = GetSessionHistory();
+    if (shistory) {
+      // XXX(nika): Stop exposing nsISHistory through GetInterface.
+      nsCOMPtr<nsISHistory> legacy = shistory->LegacySHistory();
+      legacy.forget(aSink);
       return NS_OK;
     }
     return NS_NOINTERFACE;
   } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
     nsresult rv = EnsureFind();
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -1159,24 +1161,21 @@ nsDocShell::FirePageHideNotificationInte
       if (child) {
         // Skip checking dynamic subframe entries in our children.
         child->FirePageHideNotificationInternal(aIsUnload, true);
       }
     }
 
     // If the document is unloading, remove all dynamic subframe entries.
     if (aIsUnload && !aSkipCheckingDynEntries) {
-      nsCOMPtr<nsISHistory> rootSH;
-      GetRootSessionHistory(getter_AddRefs(rootSH));
-      nsCOMPtr<nsISHistoryInternal> shPrivate = do_QueryInterface(rootSH);
+      RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
       nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE));
-      if (shPrivate && container) {
-        int32_t index = -1;
-        rootSH->GetIndex(&index);
-        shPrivate->RemoveDynEntries(index, container);
+      if (rootSH && container) {
+        int32_t index = rootSH->Index();
+        rootSH->LegacySHistoryInternal()->RemoveDynEntries(index, container);
       }
     }
 
     // Now make sure our editor, if any, is detached before we go
     // any farther.
     DetachEditorFromWindow();
   }
 }
@@ -3987,40 +3986,36 @@ nsDocShell::AddChildSHEntryInternal(nsIS
   if (mSessionHistory) {
     /* 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.
      */
-    int32_t index = -1;
     nsCOMPtr<nsISHEntry> currentHE;
-    mSessionHistory->GetIndex(&index);
+    int32_t index = mSessionHistory->Index();
     if (index < 0) {
       return NS_ERROR_FAILURE;
     }
 
-    rv = mSessionHistory->GetEntryAtIndex(index, false,
-                                          getter_AddRefs(currentHE));
+    rv = mSessionHistory->LegacySHistory()->GetEntryAtIndex(
+      index, false, getter_AddRefs(currentHE));
     NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
     if (currentEntry) {
       uint32_t cloneID = 0;
       nsCOMPtr<nsISHEntry> nextEntry;
       aCloneRef->GetID(&cloneID);
       rv = nsSHistory::CloneAndReplace(currentEntry, this, cloneID,
         aNewEntry, aCloneChildren, getter_AddRefs(nextEntry));
 
       if (NS_SUCCEEDED(rv)) {
-        nsCOMPtr<nsISHistoryInternal> shPrivate =
-          do_QueryInterface(mSessionHistory);
-        NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
-        rv = shPrivate->AddEntry(nextEntry, true);
+        rv = mSessionHistory->LegacySHistoryInternal()->AddEntry(nextEntry, true);
       }
     }
   } else {
     /* Just pass this along */
     nsCOMPtr<nsIDocShell> parent =
       do_QueryInterface(GetAsSupports(mParent), &rv);
     if (parent) {
       rv = static_cast<nsDocShell*>(parent.get())->AddChildSHEntryInternal(
@@ -4038,31 +4033,30 @@ nsDocShell::AddChildSHEntryToParent(nsIS
    * a new url has been loaded on you.
    * The mOSHE in this subframe will be the previous url's
    * mOSHE. This mOSHE will be used as the identification
    * for this subframe in the  CloneAndReplace function.
    */
 
   // In this case, we will end up calling AddEntry, which increases the
   // current index by 1
-  nsCOMPtr<nsISHistory> rootSH;
-  GetRootSessionHistory(getter_AddRefs(rootSH));
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   if (rootSH) {
-    rootSH->GetIndex(&mPreviousTransIndex);
+    mPreviousTransIndex = rootSH->Index();
   }
 
   nsresult rv;
   nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
   if (parent) {
     rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
                                  aCloneChildren);
   }
 
   if (rootSH) {
-    rootSH->GetIndex(&mLoadedTransIndex);
+    mLoadedTransIndex = rootSH->Index();
 #ifdef DEBUG_PAGE_CACHE
     printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
            mLoadedTransIndex);
 #endif
   }
 
   return rv;
 }
@@ -4098,35 +4092,29 @@ nsDocShell::GetUseGlobalHistory(bool* aU
 {
   *aUseGlobalHistory = mUseGlobalHistory;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::RemoveFromSessionHistory()
 {
-  nsCOMPtr<nsISHistoryInternal> internalHistory;
-  nsCOMPtr<nsISHistory> sessionHistory;
   nsCOMPtr<nsIDocShellTreeItem> root;
   GetSameTypeRootTreeItem(getter_AddRefs(root));
-  if (root) {
-    nsCOMPtr<nsIWebNavigation> rootAsWebnav = do_QueryInterface(root);
-    if (rootAsWebnav) {
-      rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
-      internalHistory = do_QueryInterface(sessionHistory);
-    }
-  }
-  if (!internalHistory) {
-    return NS_OK;
-  }
-
-  int32_t index = 0;
-  sessionHistory->GetIndex(&index);
+  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});
-  internalHistory->RemoveEntries(ids, index);
+  sessionHistory->LegacySHistoryInternal()->RemoveEntries(ids, index);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetCreatedDynamically(bool aDynamic)
 {
   mDynamicallyCreated = aDynamic;
   return NS_OK;
@@ -4197,36 +4185,33 @@ nsDocShell::GetDeviceSizeIsPageSize(bool
   *aValue = mDeviceSizeIsPageSize;
   return NS_OK;
 }
 
 void
 nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
 {
   nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
-  nsCOMPtr<nsISHistory> rootSH;
-  GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
-  if (!history || !shcontainer) {
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
+  if (!rootSH || !shcontainer) {
     return;
   }
 
   int32_t count = 0;
   shcontainer->GetChildCount(&count);
   AutoTArray<nsID, 16> ids;
   for (int32_t i = 0; i < count; ++i) {
     nsCOMPtr<nsISHEntry> child;
     shcontainer->GetChildAt(i, getter_AddRefs(child));
     if (child) {
       ids.AppendElement(child->DocshellID());
     }
   }
-  int32_t index = 0;
-  rootSH->GetIndex(&index);
-  history->RemoveEntries(ids, index);
+  int32_t index = rootSH->Index();
+  rootSH->LegacySHistoryInternal()->RemoveEntries(ids, index);
 }
 
 //-------------------------------------
 //-- Helper Method for Print discovery
 //-------------------------------------
 bool
 nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
 {
@@ -4256,88 +4241,80 @@ nsDocShell::IsNavigationAllowed(bool aDi
 
 //*****************************************************************************
 // nsDocShell::nsIWebNavigation
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsDocShell::GetCanGoBack(bool* aCanGoBack)
 {
+  *aCanGoBack = false;
   if (!IsNavigationAllowed(false)) {
-    *aCanGoBack = false;
     return NS_OK; // JS may not handle returning of an error code
   }
-  nsresult rv;
-  nsCOMPtr<nsISHistory> rootSH;
-  rv = GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
-  NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
-  rv = webnav->GetCanGoBack(aCanGoBack);
-  return rv;
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
+  if (rootSH) {
+    *aCanGoBack = rootSH->CanGo(-1);
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCanGoForward(bool* aCanGoForward)
 {
+  *aCanGoForward = false;
   if (!IsNavigationAllowed(false)) {
-    *aCanGoForward = false;
     return NS_OK; // JS may not handle returning of an error code
   }
-  nsresult rv;
-  nsCOMPtr<nsISHistory> rootSH;
-  rv = GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
-  NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
-  rv = webnav->GetCanGoForward(aCanGoForward);
-  return rv;
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
+  if (rootSH) {
+    *aCanGoForward = rootSH->CanGo(1);
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsDocShell::GoBack()
 {
   if (!IsNavigationAllowed()) {
     return NS_OK; // JS may not handle returning of an error code
   }
-  nsresult rv;
-  nsCOMPtr<nsISHistory> rootSH;
-  rv = GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
-  NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
-  rv = webnav->GoBack();
-  return rv;
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
+  NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
+  ErrorResult rv;
+  rootSH->Go(-1, rv);
+  return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 nsDocShell::GoForward()
 {
   if (!IsNavigationAllowed()) {
     return NS_OK; // JS may not handle returning of an error code
   }
-  nsresult rv;
-  nsCOMPtr<nsISHistory> rootSH;
-  rv = GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
-  NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
-  rv = webnav->GoForward();
-  return rv;
-}
-
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
+  NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
+  ErrorResult rv;
+  rootSH->Go(1, rv);
+  return rv.StealNSResult();
+}
+
+// XXX(nika): We may want to stop exposing this API in the child process? Going
+// to a specific index from multiple different processes could definitely race.
 NS_IMETHODIMP
 nsDocShell::GotoIndex(int32_t aIndex)
 {
   if (!IsNavigationAllowed()) {
     return NS_OK; // JS may not handle returning of an error code
   }
-  nsresult rv;
-  nsCOMPtr<nsISHistory> rootSH;
-  rv = GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
-  NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
-  rv = webnav->GotoIndex(aIndex);
-  return rv;
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
+  NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
+  return rootSH->LegacySHistoryWebNav()->GotoIndex(aIndex);
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadURI(const char16_t* aURI,
                     uint32_t aLoadFlags,
                     nsIURI* aReferringURI,
                     nsIInputStream* aPostStream,
                     nsIInputStream* aHeaderStream,
@@ -5051,22 +5028,21 @@ nsDocShell::Reload(uint32_t aReloadFlags
   NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
                "Don't pass these flags to Reload");
 
   uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
   NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
 
   // Send notifications to the HistoryListener if any, about the impending
   // reload
-  nsCOMPtr<nsISHistory> rootSH;
-  rv = GetRootSessionHistory(getter_AddRefs(rootSH));
-  nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   bool canReload = true;
   if (rootSH) {
-    shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
+    rootSH->LegacySHistoryInternal()
+      ->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
   }
 
   if (!canReload) {
     return NS_OK;
   }
 
   /* If you change this part of code, make sure bug 45297 does not re-occur */
   if (mOSHE) {
@@ -5237,48 +5213,38 @@ nsDocShell::GetReferringURI(nsIURI** aUR
 
   *aURI = mReferrerURI;
   NS_IF_ADDREF(*aURI);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory)
-{
-  NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
-  // make sure that we are the root docshell and
-  // set a handle to root docshell in SH.
-
+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;
-  /* Get the root docshell. If *this* is the root docshell
-   * then save a handle to *this* in SH. SH needs it to do
-   * traversions thro' its entries
-   */
   GetSameTypeRootTreeItem(getter_AddRefs(root));
-  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
-  if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
-    mSessionHistory = aSessionHistory;
-    nsCOMPtr<nsISHistoryInternal> shPrivate =
-      do_QueryInterface(mSessionHistory);
-    NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
-    shPrivate->SetRootDocShell(this);
-    return NS_OK;
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsDocShell::GetSessionHistory(nsISHistory** aSessionHistory)
+  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);
-  *aSessionHistory = mSessionHistory;
-  NS_IF_ADDREF(*aSessionHistory);
+  RefPtr<ChildSHistory> shistory = mSessionHistory;
+  shistory.forget(aSessionHistory);
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsDocShell::nsIWebPageDescriptor
 //*****************************************************************************
 
 NS_IMETHODIMP
@@ -5524,21 +5490,17 @@ nsDocShell::Destroy()
     mScriptGlobal->DetachFromDocShell();
     mScriptGlobal = nullptr;
   }
 
   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)
-    nsCOMPtr<nsISHistoryInternal> shPrivate =
-      do_QueryInterface(mSessionHistory);
-    if (shPrivate) {
-      shPrivate->EvictAllContentViewers();
-    }
+    mSessionHistory->LegacySHistoryInternal()->EvictAllContentViewers();
     mSessionHistory = nullptr;
   }
 
   SetTreeOwner(nullptr);
 
   mOnePermittedSandboxedNavigator = nullptr;
 
   // required to break ref cycle
@@ -8304,23 +8266,21 @@ nsDocShell::RestoreFromHistory()
                   mRestorePresentationEvent.get());
   forgetter.Forget();
 
   // Set mFiredUnloadEvent = false so that the unload handler for the
   // *new* document will fire.
   mFiredUnloadEvent = false;
 
   mURIResultedInDocument = true;
-  nsCOMPtr<nsISHistory> rootSH;
-  GetRootSessionHistory(getter_AddRefs(rootSH));
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   if (rootSH) {
-    nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
-    rootSH->GetIndex(&mPreviousTransIndex);
-    hist->UpdateIndex();
-    rootSH->GetIndex(&mLoadedTransIndex);
+    mPreviousTransIndex = rootSH->Index();
+    rootSH->LegacySHistoryInternal()->UpdateIndex();
+    mLoadedTransIndex = rootSH->Index();
 #ifdef DEBUG_PAGE_CACHE
     printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
            mLoadedTransIndex);
 #endif
   }
 
   // Rather than call Embed(), we will retrieve the viewer from the session
   // history entry and swap it in.
@@ -8835,21 +8795,22 @@ nsDocShell::CreateContentViewer(const ns
         failedURI, failedChannel, triggeringPrincipal,
         nullptr, mLoadType, false, false, false);
     }
 
     // Be sure to have a correct mLSHE, it may have been cleared by
     // EndPageLoad. See bug 302115.
     if (mSessionHistory && !mLSHE) {
       int32_t idx;
-      mSessionHistory->GetRequestedIndex(&idx);
+      mSessionHistory->LegacySHistory()->GetRequestedIndex(&idx);
       if (idx == -1) {
-        mSessionHistory->GetIndex(&idx);
-      }
-      mSessionHistory->GetEntryAtIndex(idx, false, getter_AddRefs(mLSHE));
+        idx = mSessionHistory->Index();
+      }
+      mSessionHistory->LegacySHistory()->
+        GetEntryAtIndex(idx, false, getter_AddRefs(mLSHE));
     }
 
     mLoadType = LOAD_ERROR_PAGE;
   }
 
   bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
 
   // let's try resetting the load group if we need to...
@@ -10104,20 +10065,20 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       /* Restore the original LSHE if we were loading something
        * while short-circuited load was initiated.
        */
       SetHistoryEntry(&mLSHE, oldLSHE);
       /* 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.
        */
       if (mSessionHistory) {
-        int32_t index = -1;
-        mSessionHistory->GetIndex(&index);
+        int32_t index = mSessionHistory->Index();
         nsCOMPtr<nsISHEntry> shEntry;
-        mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(shEntry));
+        mSessionHistory->LegacySHistory()->GetEntryAtIndex(
+          index, false, 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(aURI);
 
@@ -11473,24 +11434,24 @@ nsDocShell::OnNewURI(nsIURI* aURI, nsICh
   // 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
   // current frame or in the root docshell.
-  nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
+  RefPtr<ChildSHistory> rootSH = mSessionHistory;
   if (!rootSH) {
     // Get the handle to SH from the root docshell
-    GetRootSessionHistory(getter_AddRefs(rootSH));
-    if (!rootSH) {
-      updateSHistory = false;
-      updateGHistory = false; // XXX Why global history too?
-    }
+    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);
   }
 
 #ifdef DEBUG
@@ -11593,26 +11554,25 @@ nsDocShell::OnNewURI(nsIURI* aURI, nsICh
       (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
                                 aPrincipalToInherit, aCloneSHChildren,
                                 getter_AddRefs(mLSHE));
     }
   } 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.
     int32_t index = 0;
-    mSessionHistory->GetRequestedIndex(&index);
+    mSessionHistory->LegacySHistory()->GetRequestedIndex(&index);
     if (index == -1) {
-      mSessionHistory->GetIndex(&index);
+      index = mSessionHistory->Index();
     }
     nsCOMPtr<nsISHEntry> currentSH;
-    mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(currentSH));
+    mSessionHistory->LegacySHistory()->GetEntryAtIndex(
+      index, false, getter_AddRefs(currentSH));
     if (currentSH != mLSHE) {
-      nsCOMPtr<nsISHistoryInternal> shPrivate =
-        do_QueryInterface(mSessionHistory);
-      shPrivate->ReplaceEntry(index, mLSHE);
+      mSessionHistory->LegacySHistoryInternal()->ReplaceEntry(index, mLSHE);
     }
   }
 
   // If this is a POST request, we do not want to include this in global
   // history.
   if (updateGHistory && aAddToGlobalHistory && !ChannelIsPost(aChannel)) {
     nsCOMPtr<nsIURI> previousURI;
     uint32_t previousFlags = 0;
@@ -11634,26 +11594,23 @@ nsDocShell::OnNewURI(nsIURI* aURI, nsICh
   }
 
   // If this was a history load or a refresh, or it was a history load but
   // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
   // in session history.
   if (rootSH &&
        ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
          mLoadType == LOAD_NORMAL_REPLACE)) {
-    nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
-    if (shInternal) {
-      rootSH->GetIndex(&mPreviousTransIndex);
-      shInternal->UpdateIndex();
-      rootSH->GetIndex(&mLoadedTransIndex);
+    mPreviousTransIndex = rootSH->Index();
+    rootSH->LegacySHistoryInternal()->UpdateIndex();
+    mLoadedTransIndex = rootSH->Index();
 #ifdef DEBUG_PAGE_CACHE
-      printf("Previous index: %d, Loaded index: %d\n\n",
-             mPreviousTransIndex, mLoadedTransIndex);
+    printf("Previous index: %d, Loaded index: %d\n\n",
+           mPreviousTransIndex, mLoadedTransIndex);
 #endif
-    }
   }
 
   // aCloneSHChildren exactly means "we are not loading a new document".
   uint32_t locationFlags =
     aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
 
   bool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
                                               aFireOnLocationChange,
@@ -11944,36 +11901,29 @@ nsDocShell::AddState(JS::Handle<JS::Valu
   oldOSHE->GetURIWasModified(&oldURIWasModified);
   newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
 
   // Step 5: If aReplace is false, indicating that we're doing a pushState
   // rather than a replaceState, notify bfcache that we've added a page to
   // the history so it can evict content viewers if appropriate. Otherwise
   // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
   // was replaced.
-  nsCOMPtr<nsISHistory> rootSH;
-  GetRootSessionHistory(getter_AddRefs(rootSH));
-  NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<nsISHistoryInternal> internalSH = do_QueryInterface(rootSH);
-  NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
-
+  RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
   if (!aReplace) {
-    int32_t curIndex = -1;
-    rv = rootSH->GetIndex(&curIndex);
-    if (NS_SUCCEEDED(rv) && curIndex > -1) {
-      internalSH->EvictOutOfRangeContentViewers(curIndex);
+    int32_t curIndex = rootSH->Index();
+    if (curIndex > -1) {
+      rootSH->LegacySHistoryInternal()->EvictOutOfRangeContentViewers(curIndex);
     }
   } else {
     nsCOMPtr<nsISHEntry> rootSHEntry = nsSHistory::GetRootSHEntry(newSHEntry);
 
     int32_t index = -1;
-    rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
+    rv = rootSH->LegacySHistory()->GetIndexOfEntry(rootSHEntry, &index);
     if (NS_SUCCEEDED(rv) && index > -1) {
-      internalSH->ReplaceEntry(index, rootSHEntry);
+      rootSH->LegacySHistoryInternal()->ReplaceEntry(index, rootSHEntry);
     }
   }
 
   // Step 6: If the document's URI changed, update document's URI and update
   // global history.
   //
   // We need to call FireOnLocationChange so that the browser's address bar
   // gets updated and the back button is enabled, but we only need to
@@ -12271,43 +12221,39 @@ nsDocShell::AddToSessionHistory(nsIURI* 
 
     // This is the root docshell
     bool addToSHistory = !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
     if (!addToSHistory) {
       // Replace current entry in session history; If the requested index is
       // valid, it indicates the loading was triggered by a history load, and
       // we should replace the entry at requested index instead.
       int32_t index = 0;
-      mSessionHistory->GetRequestedIndex(&index);
+      mSessionHistory->LegacySHistory()->GetRequestedIndex(&index);
       if (index == -1) {
-        mSessionHistory->GetIndex(&index);
-      }
-      nsCOMPtr<nsISHistoryInternal> shPrivate =
-        do_QueryInterface(mSessionHistory);
+        index = mSessionHistory->Index();
+      }
+
       // Replace the current entry with the new entry
       if (index >= 0) {
-        if (shPrivate) {
-          rv = shPrivate->ReplaceEntry(index, entry);
-        }
+        rv = mSessionHistory->LegacySHistoryInternal()->ReplaceEntry(index,
+                                                                     entry);
       } else {
         // If we're trying to replace an inexistant shistory entry, append.
         addToSHistory = true;
       }
     }
 
     if (addToSHistory) {
       // Add to session history
-      nsCOMPtr<nsISHistoryInternal> shPrivate =
-        do_QueryInterface(mSessionHistory);
-      NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
-      mSessionHistory->GetIndex(&mPreviousTransIndex);
+      mPreviousTransIndex = mSessionHistory->Index();
 
       bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
-      rv = shPrivate->AddEntry(entry, shouldPersist);
-      mSessionHistory->GetIndex(&mLoadedTransIndex);
+      rv = mSessionHistory->LegacySHistoryInternal()->AddEntry(
+        entry, shouldPersist);
+      mLoadedTransIndex = mSessionHistory->Index();
 #ifdef DEBUG_PAGE_CACHE
       printf("Previous index: %d, Loaded index: %d\n\n",
              mPreviousTransIndex, mLoadedTransIndex);
 #endif
     }
   } else {
     // This is a subframe.
     if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
@@ -12554,31 +12500,29 @@ nsDocShell::SetHistoryEntry(nsCOMPtr<nsI
         NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
       }
     }
   }
 
   *aPtr = aEntry;
 }
 
-nsresult
-nsDocShell::GetRootSessionHistory(nsISHistory** aReturn)
-{
-  nsresult rv;
-
+already_AddRefed<ChildSHistory>
+nsDocShell::GetRootSessionHistory()
+{
   nsCOMPtr<nsIDocShellTreeItem> root;
-  // Get the root docshell
-  rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
-  // QI to nsIWebNavigation
-  nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
-  if (rootAsWebnav) {
-    // Get the handle to SH from the root docshell
-    rv = rootAsWebnav->GetSessionHistory(aReturn);
-  }
-  return rv;
+  nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
+  if (!webnav) {
+    return nullptr;
+  }
+  return webnav->GetSessionHistory();
 }
 
 nsresult
 nsDocShell::GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
   if (!aChannel) {
     return NS_ERROR_FAILURE;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -12,16 +12,17 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/gfx/Matrix.h"
+#include "mozilla/dom/ChildSHistory.h"
 
 #include "nsIAuthPromptProvider.h"
 #include "nsIBaseWindow.h"
 #include "nsIClipboardCommands.h"
 #include "nsIDeprecationWarner.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIDocShellTreeItem.h"
@@ -869,25 +870,26 @@ private: // member functions
   nsresult RefreshURIFromQueue();
   nsresult Embed(nsIContentViewer* aContentViewer,
                  const char* aCommand, nsISupports* aExtraInfo);
   nsresult GetEldestPresContext(nsPresContext** aPresContext);
   nsresult CheckLoadingPermissions();
   nsresult PersistLayoutHistoryState();
   nsresult LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType);
   nsresult SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer);
-  nsresult GetRootSessionHistory(nsISHistory** aReturn);
   nsresult GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn);
   nsresult ConfirmRepost(bool* aRepost);
   nsresult GetPromptAndStringBundle(nsIPrompt** aPrompt,
                                     nsIStringBundle** aStringBundle);
   nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);
   nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos,
                              int32_t aCurVerticalPos);
 
+  already_AddRefed<mozilla::dom::ChildSHistory> GetRootSessionHistory();
+
   inline bool UseErrorPages()
   {
     return (mObserveErrorPages ? sUseErrorPages : mUseErrorPages);
   }
 
 private: // data members
   static nsIURIFixup* sURIFixup;
 
@@ -917,17 +919,17 @@ private: // data members
   RefPtr<nsGlobalWindowOuter> mScriptGlobal;
   nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
   nsCOMPtr<nsILoadURIDelegate> mLoadURIDelegate;
   nsCOMPtr<nsIMutableArray> mRefreshURIList;
   nsCOMPtr<nsIMutableArray> mSavedRefreshURIList;
   nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
   nsCOMPtr<nsIContentViewer> mContentViewer;
   nsCOMPtr<nsIWidget> mParentWidget;
-  nsCOMPtr<nsISHistory> mSessionHistory;
+  RefPtr<mozilla::dom::ChildSHistory> mSessionHistory;
   nsCOMPtr<nsIGlobalHistory2> mGlobalHistory;
   nsCOMPtr<nsIWebBrowserFind> mFind;
   nsCOMPtr<nsICommandManager> mCommandManager;
 
   // Dimensions of the docshell
   nsIntRect mBounds;
 
   /**
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -1184,16 +1184,22 @@ interface nsIDocShell : nsIDocShellTreeI
   UniqueClientSource TakeInitialClientSource();
 
   void setColorMatrix([array, size_is(aMatrixLen)] in float aMatrix,
                       [optional] in unsigned long aMatrixLen);
 
   void getColorMatrix([optional] out unsigned long aMatrixLen,
                       [array, size_is(aMatrixLen), retval] out float aMatrix);
 
+  /**
+   * 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/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -5,16 +5,21 @@
 
 #include "nsISupports.idl"
 
 interface nsIDOMDocument;
 interface nsIInputStream;
 interface nsISHistory;
 interface nsIURI;
 interface nsIPrincipal;
+interface nsIChildSHistory;
+
+%{ C++
+#include "mozilla/dom/ChildSHistory.h"
+%}
 
 /**
  * The nsIWebNavigation interface defines an interface for navigating the web.
  * It provides methods and attributes to direct an object to navigate to a new
  * location, stop or restart an in process load, or determine where the object
  * has previously gone.
  */
 [scriptable, uuid(3ade79d4-8cb9-4952-b18d-4f9b63ca0d31)]
@@ -369,18 +374,37 @@ interface nsIWebNavigation : nsISupports
   readonly attribute nsIURI currentURI;
  
   /**
    * The referring URI for the currently loaded URI or null.
    */
   readonly attribute nsIURI referringURI;
 
   /**
-   * The session history object used by this web navigation instance.
+   * The session history object used by this web navigation instance. This
+   * object will be a mozilla::dom::ChildSHistory object, but is returned as
+   * nsISupports so it can be called from JS code.
    */
-  attribute nsISHistory sessionHistory;
+  [binaryname(SessionHistoryXPCOM)]
+  readonly attribute nsISupports sessionHistory;
+
+  %{ C++
+  /**
+   * Get the session history object used by this nsIWebNavigation instance.
+   * Use this method instead of the XPCOM method when getting the
+   * SessionHistory from C++ code.
+   */
+  already_AddRefed<mozilla::dom::ChildSHistory>
+  GetSessionHistory()
+  {
+    nsCOMPtr<nsISupports> history;
+    GetSessionHistoryXPCOM(getter_AddRefs(history));
+    return history.forget()
+      .downcast<mozilla::dom::ChildSHistory>();
+  }
+  %}
 
   /**
    * Set an OriginAttributes dictionary in the docShell. This can be done only
    * before loading any content.
    */
   void setOriginAttributesBeforeLoading(in jsval originAttributes);
 };
--- a/docshell/shistory/ParentSHistory.cpp
+++ b/docshell/shistory/ParentSHistory.cpp
@@ -34,22 +34,21 @@ TabParent*
 ParentSHistory::GetTabParent()
 {
   return static_cast<TabParent*>(mFrameLoader->GetRemoteBrowser());
 }
 
 already_AddRefed<ChildSHistory>
 ParentSHistory::GetChildIfSameProcess()
 {
-  if (XRE_IsContentProcess()) {
-    MOZ_ASSERT(!mDocShell);
-    return nullptr;
+  if (GetDocShell()) {
+    return GetDocShell()->GetSessionHistory();
   }
 
-  MOZ_CRASH("Unimplemented!");
+  return nullptr;
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ParentSHistory)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ParentSHistory)
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -1784,25 +1784,19 @@ NS_IMETHODIMP
 nsSHistory::GetReferringURI(nsIURI** aURI)
 {
   *aURI = nullptr;
   // Not implemented
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHistory::SetSessionHistory(nsISHistory* aSessionHistory)
+nsSHistory::GetSessionHistoryXPCOM(nsISupports** aSessionHistory)
 {
-  // Not implemented
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSHistory::GetSessionHistory(nsISHistory** aSessionHistory)
-{
+  *aSessionHistory = nullptr;
   // Not implemented
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::LoadURIWithOptions(const char16_t* aURI,
                                uint32_t aLoadFlags,
                                nsIURI* aReferringURI,
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -250,24 +250,23 @@ MarkDocShell(nsIDocShellTreeItem* aNode,
     return;
   }
 
   nsCOMPtr<nsIContentViewer> cview;
   shell->GetContentViewer(getter_AddRefs(cview));
   MarkContentViewer(cview, aCleanupJS);
 
   nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
-  nsCOMPtr<nsISHistory> history;
-  webNav->GetSessionHistory(getter_AddRefs(history));
+  RefPtr<ChildSHistory> history = webNav->GetSessionHistory();
   if (history) {
-    int32_t i, historyCount;
-    history->GetCount(&historyCount);
-    for (i = 0; i < historyCount; ++i) {
+    int32_t historyCount = history->Count();
+    for (int32_t i = 0; i < historyCount; ++i) {
       nsCOMPtr<nsISHEntry> shEntry;
-      history->GetEntryAtIndex(i, false, getter_AddRefs(shEntry));
+      history->LegacySHistory()->GetEntryAtIndex(
+        i, false, getter_AddRefs(shEntry));
 
       MarkSHEntry(shEntry, aCleanupJS);
     }
   }
 
   int32_t i, childCount;
   aNode->GetChildCount(&childCount);
   for (i = 0; i < childCount; ++i) {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -93,16 +93,18 @@
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/dom/CustomEvent.h"
 
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/WebBrowserPersistLocalDocument.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/GroupedHistoryEvent.h"
+#include "mozilla/dom/ParentSHistory.h"
+#include "mozilla/dom/ChildSHistory.h"
 
 #include "mozilla/dom/HTMLBodyElement.h"
 
 #include "ContentPrincipal.h"
 
 #ifdef XP_WIN
 #include "mozilla/plugins/PPluginWidgetParent.h"
 #include "../plugins/ipc/PluginWidgetParent.h"
@@ -1371,20 +1373,18 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
     do_QueryInterface(ourRootTreeItem);
   nsCOMPtr<nsIWebNavigation> otherRootWebnav =
     do_QueryInterface(otherRootTreeItem);
 
   if (!ourRootWebnav || !otherRootWebnav) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsCOMPtr<nsISHistory> ourHistory;
-  nsCOMPtr<nsISHistory> otherHistory;
-  ourRootWebnav->GetSessionHistory(getter_AddRefs(ourHistory));
-  otherRootWebnav->GetSessionHistory(getter_AddRefs(otherHistory));
+  RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory();
+  RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory();
 
   if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
       (ourHistory || otherHistory)) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   // Also make sure that the two docshells are the same type. Otherwise
   // swapping is certainly not safe. If this needs to be changed then
@@ -1604,25 +1604,21 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   // Perform the actual swap of the internal refptrs. We keep a strong reference
   // to ourselves to make sure we don't die while we overwrite our reference to
   // ourself.
   RefPtr<nsFrameLoader> kungFuDeathGrip(this);
   aThisOwner->InternalSetFrameLoader(aOther);
   aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip);
 
   // Drop any cached content viewers in the two session histories.
-  nsCOMPtr<nsISHistoryInternal> ourInternalHistory =
-    do_QueryInterface(ourHistory);
-  nsCOMPtr<nsISHistoryInternal> otherInternalHistory =
-    do_QueryInterface(otherHistory);
-  if (ourInternalHistory) {
-    ourInternalHistory->EvictAllContentViewers();
+  if (ourHistory) {
+    ourHistory->LegacySHistoryInternal()->EvictAllContentViewers();
   }
-  if (otherInternalHistory) {
-    otherInternalHistory->EvictAllContentViewers();
+  if (otherHistory) {
+    otherHistory->LegacySHistoryInternal()->EvictAllContentViewers();
   }
 
   NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
                otherFrame == otherContent->GetPrimaryFrame(),
                "changed primary frame");
 
   ourFrameFrame->EndSwapDocShells(otherFrame);
 
@@ -2105,23 +2101,19 @@ nsFrameLoader::MaybeCreateDocShell()
     // Do not call Destroy() here. See bug 472312.
     NS_WARNING("Something wrong when creating the docshell for a frameloader!");
     return NS_ERROR_FAILURE;
   }
 
   if (mIsTopLevelContent &&
       mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
       !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
-    nsresult rv;
-    nsCOMPtr<nsISHistory> sessionHistory =
-      do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
+    // XXX(nika): Set this up more explicitly?
+    nsresult rv = mDocShell->InitSessionHistory();
     NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
-    webNav->SetSessionHistory(sessionHistory);
   }
 
   OriginAttributes attrs;
   if (parentDocShell->ItemType() == mDocShell->ItemType()) {
     attrs = nsDocShell::Cast(parentDocShell)->GetOriginAttributes();
   }
 
   // Inherit origin attributes from parent document if
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -64,32 +64,24 @@ nsHistory::GetLength(ErrorResult& aRv) c
   nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
   if (!win || !win->HasActiveDocument()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 
     return 0;
   }
 
   // Get session History from docshell
-  nsCOMPtr<nsISHistory> sHistory = GetSessionHistory();
+  RefPtr<ChildSHistory> sHistory = GetSessionHistory();
   if (!sHistory) {
     aRv.Throw(NS_ERROR_FAILURE);
 
     return 0;
   }
 
-  int32_t len;
-  nsresult rv = sHistory->GetCount(&len);
-
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-
-    return 0;
-  }
-
+  int32_t len = sHistory->Count();;
   return len >= 0 ? len : 0;
 }
 
 ScrollRestoration
 nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
   if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
@@ -193,78 +185,66 @@ nsHistory::Go(int32_t aDelta, ErrorResul
       if (doc && (pcx = doc->GetPresContext())) {
         pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
       }
 
       return;
     }
   }
 
-  nsCOMPtr<nsISHistory> session_history = GetSessionHistory();
-  nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(session_history));
-  if (!webnav) {
+  RefPtr<ChildSHistory> session_history = GetSessionHistory();
+  if (!session_history) {
     aRv.Throw(NS_ERROR_FAILURE);
 
     return;
   }
 
-  int32_t curIndex = -1;
-  int32_t len = 0;
-  session_history->GetIndex(&curIndex);
-  session_history->GetCount(&len);
-
-  int32_t index = curIndex + aDelta;
-  if (index > -1 && index < len)
-    webnav->GotoIndex(index);
-
-  // Ignore the return value from GotoIndex(), since returning errors
-  // from GotoIndex() can lead to exceptions and a possible leak
-  // of history length
+  // Ignore the return value from Go(), since returning errors from Go() can
+  // lead to exceptions and a possible leak of history length
+  session_history->Go(aDelta, IgnoreErrors());
 }
 
 void
 nsHistory::Back(ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
   if (!win || !win->HasActiveDocument()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 
     return;
   }
 
-  nsCOMPtr<nsISHistory> sHistory = GetSessionHistory();
-  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(sHistory));
-  if (!webNav) {
+  RefPtr<ChildSHistory> sHistory = GetSessionHistory();
+  if (!sHistory) {
     aRv.Throw(NS_ERROR_FAILURE);
 
     return;
   }
 
-  webNav->GoBack();
+  sHistory->Go(-1, IgnoreErrors());
 }
 
 void
 nsHistory::Forward(ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
   if (!win || !win->HasActiveDocument()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
 
     return;
   }
 
-  nsCOMPtr<nsISHistory> sHistory = GetSessionHistory();
-  nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(sHistory));
-  if (!webNav) {
+  RefPtr<ChildSHistory> sHistory = GetSessionHistory();
+  if (!sHistory) {
     aRv.Throw(NS_ERROR_FAILURE);
 
     return;
   }
 
-  webNav->GoForward();
+  sHistory->Go(1, IgnoreErrors());
 }
 
 void
 nsHistory::PushState(JSContext* aCx, JS::Handle<JS::Value> aData,
                      const nsAString& aTitle, const nsAString& aUrl,
                      ErrorResult& aRv)
 {
   PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, false);
@@ -317,27 +297,23 @@ nsHistory::GetDocShell() const
 {
   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mInnerWindow);
   if (!win) {
     return nullptr;
   }
   return win->GetDocShell();
 }
 
-already_AddRefed<nsISHistory>
+already_AddRefed<ChildSHistory>
 nsHistory::GetSessionHistory() const
 {
   nsIDocShell *docShell = GetDocShell();
   NS_ENSURE_TRUE(docShell, nullptr);
 
   // Get the root DocShell from it
   nsCOMPtr<nsIDocShellTreeItem> root;
   docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
   nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(root));
   NS_ENSURE_TRUE(webNav, nullptr);
 
-  nsCOMPtr<nsISHistory> shistory;
-
   // Get SH from nsIWebNavigation
-  webNav->GetSessionHistory(getter_AddRefs(shistory));
-
-  return shistory.forget();
+  return webNav->GetSessionHistory();
 }
--- a/dom/base/nsHistory.h
+++ b/dom/base/nsHistory.h
@@ -4,16 +4,17 @@
  * 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 nsHistory_h___
 #define nsHistory_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/HistoryBinding.h"
+#include "mozilla/dom/ChildSHistory.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsPIDOMWindow.h" // for GetParentObject
 #include "nsStringFwd.h"
 #include "nsWrapperCache.h"
 
 class nsIDocShell;
 class nsISHistory;
@@ -54,14 +55,14 @@ protected:
   virtual ~nsHistory();
 
   nsIDocShell* GetDocShell() const;
 
   void PushOrReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
                           const nsAString& aTitle, const nsAString& aUrl,
                           mozilla::ErrorResult& aRv, bool aReplace);
 
-  already_AddRefed<nsISHistory> GetSessionHistory() const;
+  already_AddRefed<mozilla::dom::ChildSHistory> GetSessionHistory() const;
 
   nsCOMPtr<nsIWeakReference> mInnerWindow;
 };
 
 #endif /* nsHistory_h___ */
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -134,18 +134,18 @@ BrowserElementChild.prototype = {
     docShell.QueryInterface(Ci.nsIWebProgress)
             .addProgressListener(this._progressListener,
                                  Ci.nsIWebProgress.NOTIFY_LOCATION |
                                  Ci.nsIWebProgress.NOTIFY_SECURITY |
                                  Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
 
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     if (!webNavigation.sessionHistory) {
-      webNavigation.sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
-                                       .createInstance(Ci.nsISHistory);
+      // XXX(nika): We might need to start this up some other way?
+      docShell.initSessionHistory();
     }
 
     // This is necessary to get security web progress notifications.
     var securityUI = Cc['@mozilla.org/secure_browser_ui;1']
                        .createInstance(Ci.nsISecureBrowserUI);
     securityUI.init(content);
 
     // A cache of the menuitem dom objects keyed by the id we generate
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2108,29 +2108,28 @@ nsDocumentViewer::Show(void)
     // Make sure we don't have too many cached ContentViewers
     nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
     if (treeItem) {
       // We need to find the root DocShell since only that object has an
       // SHistory and we need the SHistory to evict content viewers
       nsCOMPtr<nsIDocShellTreeItem> root;
       treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
       nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
-      nsCOMPtr<nsISHistory> history;
-      webNav->GetSessionHistory(getter_AddRefs(history));
-      nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
-      if (historyInt) {
+      RefPtr<ChildSHistory> history = webNav->GetSessionHistory();
+      if (history) {
         int32_t prevIndex,loadedIndex;
         nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
         docShell->GetPreviousTransIndex(&prevIndex);
         docShell->GetLoadedTransIndex(&loadedIndex);
 #ifdef DEBUG_PAGE_CACHE
         printf("About to evict content viewers: prev=%d, loaded=%d\n",
                prevIndex, loadedIndex);
 #endif
-        historyInt->EvictOutOfRangeContentViewers(loadedIndex);
+        history->LegacySHistoryInternal()->
+          EvictOutOfRangeContentViewers(loadedIndex);
       }
     }
   }
 
   if (mWindow) {
     // When attached to a top level xul window, we do not need to call
     // Show on the widget. Underlying window management code handles
     // this when the window is initialized.
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -726,40 +726,28 @@ nsWebBrowser::GetCurrentURI(nsIURI** aUR
 NS_IMETHODIMP
 nsWebBrowser::GetReferringURI(nsIURI** aURI)
 {
   NS_ENSURE_STATE(mDocShell);
 
   return mDocShellAsNav->GetReferringURI(aURI);
 }
 
+// XXX(nika): Consider making the mozilla::dom::ChildSHistory version the
+// canonical one?
 NS_IMETHODIMP
-nsWebBrowser::SetSessionHistory(nsISHistory* aSessionHistory)
-{
-  if (mDocShell) {
-    return mDocShellAsNav->SetSessionHistory(aSessionHistory);
-  } else {
-    mInitInfo->sessionHistory = aSessionHistory;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebBrowser::GetSessionHistory(nsISHistory** aSessionHistory)
+nsWebBrowser::GetSessionHistoryXPCOM(nsISupports** aSessionHistory)
 {
   NS_ENSURE_ARG_POINTER(aSessionHistory);
+  *aSessionHistory = nullptr;
   if (mDocShell) {
-    return mDocShellAsNav->GetSessionHistory(aSessionHistory);
-  } else {
-    *aSessionHistory = mInitInfo->sessionHistory;
+    RefPtr<mozilla::dom::ChildSHistory> shistory =
+      mDocShellAsNav->GetSessionHistory();
+    shistory.forget(aSessionHistory);
   }
-
-  NS_IF_ADDREF(*aSessionHistory);
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebBrowser::GetDocument(nsIDOMDocument** aDocument)
 {
   NS_ENSURE_STATE(mDocShell);
 
@@ -1258,21 +1246,17 @@ nsWebBrowser::Create()
   }
   mDocShell->SetTreeOwner(mDocShellTreeOwner);
 
   // 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 (!mInitInfo->sessionHistory) {
-    mInitInfo->sessionHistory = do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  mDocShellAsNav->SetSessionHistory(mInitInfo->sessionHistory);
+  mDocShell->InitSessionHistory();
 
   if (XRE_IsParentProcess()) {
     // Hook up global history. Do not fail if we can't - just warn.
     rv = EnableGlobalHistory(mShouldEnableHistory);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed");
   }
 
   NS_ENSURE_SUCCESS(mDocShellAsWin->Create(), NS_ERROR_FAILURE);
--- a/toolkit/components/browser/nsWebBrowser.h
+++ b/toolkit/components/browser/nsWebBrowser.h
@@ -45,17 +45,16 @@ class nsWebBrowserInitInfo
 {
 public:
   // nsIBaseWindow Stuff
   int32_t x;
   int32_t y;
   int32_t cx;
   int32_t cy;
   bool visible;
-  nsCOMPtr<nsISHistory> sessionHistory;
   nsString name;
 };
 
 class nsWebBrowserListenerState
 {
 public:
   bool Equals(nsIWeakReference* aListener, const nsIID& aID)
   {