Bug 1574261 - Make nsDocShell::MaybeHandleSubFrameHistory work with session history in parent, r=peterv
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 17 Sep 2020 18:22:08 +0000
changeset 549207 db7ab542d0ec73b94457d1abae7344bf0cdf82f0
parent 549206 ad4be03cc660b857881009caaed3140146fdb63e
child 549208 e4c133e2811eecfc5c7bf4a7f6472a4925a8837a
push id37792
push usermalexandru@mozilla.com
push dateFri, 18 Sep 2020 09:45:00 +0000
treeherdermozilla-central@8084c8793a55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1574261
milestone82.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 1574261 - Make nsDocShell::MaybeHandleSubFrameHistory work with session history in parent, r=peterv This has couple of different pieces and one may want to focus on each of those separately when reviewing. The first two as small changes. - Moving mDynamicallyCreated from nsDocShell to be a sync'ed field on BrowsingContext. CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad sets that on a newly created entry. - Adding mActiveEntryIsLoadingFromSessionHistory. mLoadingEntry + mActiveEntryIsLoadingFromSessionHistory has roughly the same lifetime as mLSHE. mLoadingActiveEntry is needed so that child docshell can know whether its parent is loading from session history. - The main part is in MaybeHandleSubframeHistory which checks if the parent docshell is loading from session history, and if so, asks for a LoadingSessionHistoryInfo. In the case of docshell living in a child process that operation is asynchronous, so when the data is back from the parent process, LoadURI is called again with the possibly updated data. One could possibly split the code to smaller methods and then deal with aContinueHandlingSubframeHistory only in LoadURI, but MaybeHandleSubframeHistory does have some early returns which would make that approach possibly hard to follow. Differential Revision: https://phabricator.services.mozilla.com/D89685
docshell/base/BrowsingContext.h
docshell/base/CanonicalBrowsingContext.cpp
docshell/base/CanonicalBrowsingContext.h
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/shistory/SessionHistoryEntry.cpp
docshell/shistory/SessionHistoryEntry.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/PContent.ipdl
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -149,17 +149,18 @@ class WindowProxyHolder;
    * This is only ever set to true on the top BC, so consumers need to get   \
    * the value from the top BC! */                                           \
   FIELD(HasSessionHistory, bool)                                             \
   /* Tracks if this context is the only top-level document in the session    \
    * history of the context. */                                              \
   FIELD(IsSingleToplevelInHistory, bool)                                     \
   FIELD(UseErrorPages, bool)                                                 \
   FIELD(PlatformOverride, nsString)                                          \
-  FIELD(HasLoadedNonInitialDocument, bool)
+  FIELD(HasLoadedNonInitialDocument, bool)                                   \
+  FIELD(CreatedDynamically, bool)
 
 // BrowsingContext, in this context, is the cross process replicated
 // environment in which information about documents is stored. In
 // particular the tree structure of nested browsing contexts is
 // represented by the tree of BrowsingContexts.
 //
 // The tree of BrowsingContexts is created in step with its
 // corresponding nsDocShell, and when nsDocShells are connected
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -304,27 +304,64 @@ void CanonicalBrowsingContext::SwapHisto
 }
 
 void CanonicalBrowsingContext::AddLoadingSessionHistoryEntry(
     uint64_t aLoadId, SessionHistoryEntry* aEntry) {
   Unused << SetHistoryID(aEntry->DocshellID());
   mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{aLoadId, aEntry});
 }
 
+void CanonicalBrowsingContext::GetLoadingSessionHistoryInfoFromParent(
+    Maybe<LoadingSessionHistoryInfo>& aLoadingInfo, int32_t* aRequestedIndex,
+    int32_t* aLength) {
+  *aRequestedIndex = -1;
+  *aLength = 0;
+
+  nsISHistory* shistory = GetSessionHistory();
+  if (!shistory || !GetParent()) {
+    return;
+  }
+
+  SessionHistoryEntry* parentSHE =
+      GetParent()->Canonical()->GetActiveSessionHistoryEntry();
+  if (parentSHE) {
+    int32_t index = -1;
+    for (BrowsingContext* sibling : GetParent()->Children()) {
+      ++index;
+      if (sibling == this) {
+        nsCOMPtr<nsISHEntry> shEntry;
+        parentSHE->GetChildSHEntryIfHasNoDynamicallyAddedChild(
+            index, getter_AddRefs(shEntry));
+        nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(shEntry);
+        if (she) {
+          aLoadingInfo.emplace(she);
+          mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{
+              aLoadingInfo.value().mLoadId, she.get()});
+          *aRequestedIndex = shistory->GetRequestedIndex();
+          *aLength = shistory->GetCount();
+          Unused << SetHistoryID(she->DocshellID());
+        }
+        break;
+      }
+    }
+  }
+}
+
 UniquePtr<LoadingSessionHistoryInfo>
 CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
     nsDocShellLoadState* aLoadState, nsIChannel* aChannel) {
   RefPtr<SessionHistoryEntry> entry;
   const LoadingSessionHistoryInfo* existingLoadingInfo =
       aLoadState->GetLoadingSessionHistoryInfo();
   if (existingLoadingInfo) {
     entry = SessionHistoryEntry::GetByLoadId(existingLoadingInfo->mLoadId);
   } else {
     entry = new SessionHistoryEntry(aLoadState, aChannel);
     entry->SetDocshellID(GetHistoryID());
+    entry->SetIsDynamicallyAdded(GetCreatedDynamically());
     entry->SetForInitialLoad(true);
   }
   MOZ_DIAGNOSTIC_ASSERT(entry);
 
   UniquePtr<LoadingSessionHistoryInfo> loadingInfo;
   if (existingLoadingInfo) {
     loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(*existingLoadingInfo);
   } else {
@@ -378,19 +415,26 @@ void CanonicalBrowsingContext::SessionHi
                              /* FIXME aPersist = */ true);
         }
       } else {
         // FIXME Check if we're replacing before adding a child.
         // FIXME The old implementations adds it to the parent's mLSHE if there
         //       is one, need to figure out if that makes sense here (peterv
         //       doesn't think it would).
         if (loadFromSessionHistory) {
-          oldActiveEntry->SyncTreesForSubframeNavigation(newActiveEntry, Top(),
-                                                         this);
+          if (oldActiveEntry) {
+            // oldActiveEntry is null if we're loading iframes from session
+            // history while also parent page is loading from session history.
+            // In that case there isn't anything to sync.
+            oldActiveEntry->SyncTreesForSubframeNavigation(newActiveEntry,
+                                                           Top(), this);
+          }
           mActiveEntry = newActiveEntry;
+          // FIXME UpdateIndex() here may update index too early (but even the
+          //       old implementation seems to have similar issues).
           shistory->UpdateIndex();
         } else if (oldActiveEntry) {
           // AddChildSHEntryHelper does update the index of the session history!
           // FIXME Need to figure out the right value for aCloneChildren.
           shistory->AddChildSHEntryHelper(oldActiveEntry, newActiveEntry, Top(),
                                           true);
           mActiveEntry = newActiveEntry;
         } else {
@@ -408,16 +452,17 @@ void CanonicalBrowsingContext::SessionHi
           }
         }
       }
 
       HistoryCommitIndexAndLength(aChangeID);
 
       return;
     }
+    // XXX Should the loading entries before [i] be removed?
   }
   // FIXME Should we throw an error if we don't find an entry for
   // aSessionHistoryEntryId?
 }
 
 static already_AddRefed<nsDocShellLoadState> CreateLoadInfo(
     SessionHistoryEntry* aEntry, Maybe<uint64_t> aLoadId) {
   const SessionHistoryInfo& info = aEntry->Info();
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -219,16 +219,20 @@ class CanonicalBrowsingContext final : p
 
   bool HasHistoryEntry(nsISHEntry* aEntry);
 
   void SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry);
 
   void AddLoadingSessionHistoryEntry(uint64_t aLoadId,
                                      SessionHistoryEntry* aEntry);
 
+  void GetLoadingSessionHistoryInfoFromParent(
+      Maybe<LoadingSessionHistoryInfo>& aLoadingInfo, int32_t* aRequestedIndex,
+      int32_t* aLength);
+
  protected:
   // Called when the browsing context is being discarded.
   void CanonicalDiscard();
 
   using Type = BrowsingContext::Type;
   CanonicalBrowsingContext(WindowContext* aParentWindow,
                            BrowsingContextGroup* aGroup,
                            uint64_t aBrowsingContextId,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -400,17 +400,16 @@ nsDocShell::nsDocShell(BrowsingContext* 
       mCanExecuteScripts(false),
       mFiredUnloadEvent(false),
       mEODForCurrentDocument(false),
       mURIResultedInDocument(false),
       mIsBeingDestroyed(false),
       mIsExecutingOnLoadHandler(false),
       mIsPrintingOrPP(false),
       mSavingOldViewer(false),
-      mDynamicallyCreated(false),
       mAffectPrivateSessionLifetime(true),
       mInvisible(false),
       mHasLoadedNonBlankURI(false),
       mBlankTiming(false),
       mTitleValidForCurrentURI(false),
       mWillChangeProcess(false),
       mIsNavigating(false),
       mSuspendMediaWhenInactive(false) {
@@ -736,16 +735,22 @@ nsDocShell::SetCancelContentJSEpoch(int3
   nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
   static_cast<BrowserChild*>(browserChild.get())
       ->SetCancelContentJSEpoch(aEpoch);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
+  return LoadURI(aLoadState, aSetNavigating, false);
+}
+
+nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
+                             bool aSetNavigating,
+                             bool aContinueHandlingSubframeHistory) {
   MOZ_ASSERT(aLoadState, "Must have a valid load state!");
   MOZ_ASSERT(
       (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
       "Should not have these flags set");
   MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
              "Targeting doesn't occur until InternalLoad");
 
   if (!aLoadState->TriggeringPrincipal()) {
@@ -801,24 +806,30 @@ nsDocShell::LoadURI(nsDocShellLoadState*
   // 1492648. LoadType should now be set up by the caller at the time they
   // create their nsDocShellLoadState object to pass into LoadURI.
 
   MOZ_LOG(
       gDocShellLeakLog, LogLevel::Debug,
       ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
        aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
 
-  if (!aLoadState->LoadIsFromSessionHistory() &&
-      !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
-                           LOAD_FLAGS_REPLACE_HISTORY)) {
+  if ((!aLoadState->LoadIsFromSessionHistory() &&
+       !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
+                            LOAD_FLAGS_REPLACE_HISTORY)) ||
+      aContinueHandlingSubframeHistory) {
     // This is possibly a subframe, so handle it accordingly.
     //
     // If history exists, it will be loaded into the aLoadState object, and the
     // LoadType will be changed.
-    MaybeHandleSubframeHistory(aLoadState);
+    if (MaybeHandleSubframeHistory(aLoadState,
+                                   aContinueHandlingSubframeHistory)) {
+      // MaybeHandleSubframeHistory returns true if we need to continue loading
+      // asynchronously.
+      return NS_OK;
+    }
   }
 
   if (aLoadState->LoadIsFromSessionHistory()) {
     MOZ_LOG(gSHLog, LogLevel::Debug,
             ("nsDocShell[%p]: loading from session history", this));
 
     if (!StaticPrefs::fission_sessionHistoryInParent()) {
       return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType());
@@ -869,75 +880,146 @@ nsDocShell::LoadURI(nsDocShellLoadState*
     // Save URI string in case it's needed later when
     // sending to search engine service in EndPageLoad()
     mOriginalUriString = *aLoadState->GetOriginalURIString();
   }
 
   return NS_OK;
 }
 
-void nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState) {
+bool nsDocShell::IsLoadingFromSessionHistory() {
+  return mActiveEntryIsLoadingFromSessionHistory;
+}
+
+bool nsDocShell::MaybeHandleSubframeHistory(
+    nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
   // First, verify if this is a subframe.
+  // Note, it is ok to rely on docshell here and not browsing context since when
+  // an iframe is created, it has first in-process docshell.
   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
   nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
 
   if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
-    // This is the root docshell. If we got here while
-    // executing an onLoad Handler,this load will not go
-    // into session history.
-    bool inOnLoadHandler = false;
-    GetIsExecutingOnLoadHandler(&inOnLoadHandler);
-    if (inOnLoadHandler) {
-      aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
-    }
-    return;
+    if (mBrowsingContext && mBrowsingContext->IsTop()) {
+      // This is the root docshell. If we got here while
+      // executing an onLoad Handler,this load will not go
+      // into session history.
+      // XXX Why is this code in a method which deals with iframes!
+      bool inOnLoadHandler = false;
+      GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+      if (inOnLoadHandler) {
+        aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
+      }
+    }
+    return false;
   }
 
   /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
    * loaded through a history mechanism, then get the SH entry for the child
    * from the parent. This is done to restore frameset navigation while going
    * back/forward. If the parent was loaded through any other loadType, set the
    * child's loadType too accordingly, so that session history does not get
    * confused.
    */
 
   // Get the parent's load type
   uint32_t parentLoadType;
   parentDS->GetLoadType(&parentLoadType);
 
-  // Get the ShEntry for the child from the parent
-  nsCOMPtr<nsISHEntry> currentSH;
-  bool oshe = false;
-  parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
-  bool dynamicallyAddedChild = mDynamicallyCreated;
-
-  if (!dynamicallyAddedChild && !oshe && currentSH) {
-    // Only use the old SHEntry, if we're sure enough that
-    // it wasn't originally for some other frame.
-    nsCOMPtr<nsISHEntry> shEntry;
-    currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
-        mChildOffset, getter_AddRefs(shEntry));
-    if (shEntry) {
-      aLoadState->SetSHEntry(shEntry);
+  if (!aContinueHandlingSubframeHistory) {
+    if (StaticPrefs::fission_sessionHistoryInParent()) {
+      if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
+          !GetCreatedDynamically()) {
+        if (XRE_IsContentProcess()) {
+          dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+          if (contentChild) {
+            RefPtr<Document> parentDoc = parentDS->GetDocument();
+            parentDoc->BlockOnload();
+            RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
+            Maybe<uint64_t> currentLoadIdentifier =
+                mBrowsingContext->GetCurrentLoadIdentifier();
+            RefPtr<nsDocShellLoadState> loadState = aLoadState;
+            bool isNavigating = mIsNavigating;
+
+            auto resolve =
+                [currentLoadIdentifier, browsingContext, parentDoc, loadState,
+                 isNavigating](Tuple<mozilla::Maybe<LoadingSessionHistoryInfo>,
+                                     int32_t, int32_t>&& aResult) {
+                  if (currentLoadIdentifier ==
+                          browsingContext->GetCurrentLoadIdentifier() &&
+                      Get<0>(aResult).isSome()) {
+                    loadState->SetLoadingSessionHistoryInfo(
+                        Get<0>(aResult).value());
+                    loadState->SetLoadIsFromSessionHistory(
+                        Get<1>(aResult), Get<2>(aResult), false);
+                  }
+                  RefPtr<nsDocShell> docShell =
+                      static_cast<nsDocShell*>(browsingContext->GetDocShell());
+                  if (docShell) {
+                    // We got the results back from the parent process, call
+                    // LoadURI again with the possibly updated data.
+                    docShell->LoadURI(loadState, isNavigating, true);
+                  }
+                  parentDoc->UnblockOnload(false);
+                };
+            auto reject = [parentDoc](mozilla::ipc::ResponseRejectReason) {
+              parentDoc->UnblockOnload(false);
+            };
+            contentChild->SendGetLoadingSessionHistoryInfoFromParent(
+                mBrowsingContext, std::move(resolve), std::move(reject));
+            return true;
+          }
+        } else {
+          Maybe<LoadingSessionHistoryInfo> info;
+          int32_t requestedIndex = -1;
+          int32_t sessionHistoryLength = 0;
+          mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
+              info, &requestedIndex, &sessionHistoryLength);
+          if (info.isSome()) {
+            aLoadState->SetLoadingSessionHistoryInfo(info.value());
+            aLoadState->SetLoadIsFromSessionHistory(
+                requestedIndex, sessionHistoryLength, false);
+          }
+        }
+      }
+    } else {
+      // Get the ShEntry for the child from the parent
+      nsCOMPtr<nsISHEntry> currentSH;
+      bool oshe = false;
+      parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+      bool dynamicallyAddedChild = GetCreatedDynamically();
+
+      if (!dynamicallyAddedChild && !oshe && currentSH) {
+        // Only use the old SHEntry, if we're sure enough that
+        // it wasn't originally for some other frame.
+        nsCOMPtr<nsISHEntry> shEntry;
+        currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
+            mChildOffset, getter_AddRefs(shEntry));
+        if (shEntry) {
+          aLoadState->SetSHEntry(shEntry);
+        }
+      }
     }
   }
 
   // Make some decisions on the child frame's loadType based on the
   // parent's loadType, if the subframe hasn't loaded anything into it.
   //
   // In some cases privileged scripts may try to get the DOMWindow
   // reference of this docshell before the loading starts, causing the
   // initial about:blank content viewer being created and mCurrentURI being
   // set. To handle this case we check if mCurrentURI is about:blank and
   // currentSHEntry is null.
+  bool oshe = false;
   nsCOMPtr<nsISHEntry> currentChildEntry;
   GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
 
-  if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry)) {
+  if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry ||
+                      mLoadingEntry || mActiveEntry)) {
     // This is a pre-existing subframe. If
     // 1. The load of this frame was not originally initiated by session
     //    history directly (i.e. (!shEntry) condition succeeded, but it can
     //    still be a history load on parent which causes this frame being
     //    loaded), which we checked with the above assert, and
     // 2. mCurrentURI is not null, nor the initial about:blank,
     // it is possible that a parent's onLoadHandler or even self's
     // onLoadHandler is loading a new page in this child. Check parent's and
@@ -945,17 +1027,17 @@ void nsDocShell::MaybeHandleSubframeHist
     // load to get in to session history.
     BusyFlags parentBusy = parentDS->GetBusyFlags();
     BusyFlags selfBusy = GetBusyFlags();
 
     if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
       aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
       aLoadState->ClearLoadIsFromSessionHistory();
     }
-    return;
+    return false;
   }
 
   // This is a newly created frame. Check for exception cases first.
   // By default the subframe will inherit the parent's loadType.
   if (aLoadState->LoadIsFromSessionHistory() &&
       (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK ||
        parentLoadType == LOAD_NORMAL_EXTERNAL)) {
     // The parent was loaded normally. In this case, this *brand new*
@@ -994,16 +1076,18 @@ void nsDocShell::MaybeHandleSubframeHist
     aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
   } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
              (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
              (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
     // the new frame should inherit the parent's load type so that it also
     // bypasses the cache and/or proxy
     aLoadState->SetLoadType(parentLoadType);
   }
+
+  return false;
 }
 
 /*
  * Reset state to a new content model within the current document and the
  * document viewer. Called by the document before initiating an out of band
  * document.write().
  */
 NS_IMETHODIMP
@@ -3035,23 +3119,25 @@ nsresult nsDocShell::AddChildSHEntryToPa
     }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetCreatedDynamically(bool aDynamic) {
-  mDynamicallyCreated = aDynamic;
+  if (mBrowsingContext) {
+    Unused << mBrowsingContext->SetCreatedDynamically(aDynamic);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCreatedDynamically(bool* aDynamic) {
-  *aDynamic = mDynamicallyCreated;
+  *aDynamic = mBrowsingContext && mBrowsingContext->GetCreatedDynamically();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
   *aOSHE = false;
   *aEntry = nullptr;
   if (mLSHE) {
@@ -4104,16 +4190,17 @@ nsDocShell::Stop(uint32_t aStopFlags) {
   // Revoke any pending event related to content viewer restoration
   mRestorePresentationEvent.Revoke();
 
   if (mLoadType == LOAD_ERROR_PAGE) {
     if (mLSHE) {
       // Since error page loads never unset mLSHE, do so now
       SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
     }
+    mActiveEntryIsLoadingFromSessionHistory = false;
 
     mFailedChannel = nullptr;
     mFailedURI = nullptr;
   }
 
   if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
     // Stop the document loading
     if (mContentViewer) {
@@ -5610,16 +5697,17 @@ nsresult nsDocShell::Embed(nsIContentVie
 
     SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
   }
 
   if (StaticPrefs::fission_sessionHistoryInParent()) {
     MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
     mActiveEntry = nullptr;
     mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
+    mActiveEntryIsLoadingFromSessionHistory = !!mLoadingEntry;
     if (mLoadingEntry) {
       mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
       mLoadingEntry.swap(loadingEntry);
     }
     if (mActiveEntry) {
       MOZ_ASSERT(loadingEntry);
       nsID changeID = {};
       if (XRE_IsParentProcess()) {
@@ -6424,16 +6512,18 @@ nsresult nsDocShell::EndPageLoad(nsIWebP
   // itself or one of its children, we can deal with it appropriately.
   if (mLSHE) {
     mLSHE->SetLoadType(LOAD_HISTORY);
 
     // Clear the mLSHE reference to indicate document loading is done one
     // way or another.
     SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
   }
+  mActiveEntryIsLoadingFromSessionHistory = false;
+
   // if there's a refresh header in the channel, this method
   // will set it up for us.
   if (mBrowsingContext->GetIsActive() || !mDisableMetaRefreshWhenInactive)
     RefreshURIFromQueue();
 
   // Test whether this is the top frame or a subframe
   bool isTopFrame = mBrowsingContext->IsTop();
 
@@ -11367,17 +11457,17 @@ nsresult nsDocShell::AddToSessionHistory
   // Title is set in nsDocShell::SetTitle()
   entry->Create(aURI,                 // uri
                 EmptyString(),        // Title
                 inputStream,          // Post data stream
                 cacheKey,             // CacheKey
                 mContentTypeHint,     // Content-type
                 triggeringPrincipal,  // Channel or provided principal
                 principalToInherit, partitionedPrincipalToInherit, csp,
-                HistoryID(), mDynamicallyCreated, originalURI,
+                HistoryID(), GetCreatedDynamically(), originalURI,
                 resultPrincipalURI, loadReplace, referrerInfo, srcdoc,
                 srcdocEntry, baseURI, saveLayoutState, expired);
 
   if (mBrowsingContext->IsTop() && GetSessionHistory()) {
     bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
     Maybe<int32_t> previousEntryIndex;
     Maybe<int32_t> loadedEntryIndex;
     rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -518,16 +518,19 @@ class nsDocShell final : public nsDocLoa
   static bool ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel);
 
   bool IsOSHE(nsISHEntry* aEntry) const { return mOSHE == aEntry; }
 
   mozilla::dom::ChildSHistory* GetSessionHistory() {
     return mBrowsingContext->GetChildSessionHistory();
   }
 
+  // This returns true only when using session history in parent.
+  bool IsLoadingFromSessionHistory();
+
  private:  // member functions
   friend class nsDSURIContentListener;
   friend class FramingChecker;
   friend class OnLinkClickEvent;
   friend class nsIDocShell;
   friend class mozilla::dom::BrowsingContext;
   friend class mozilla::net::DocumentLoadListener;
   friend class nsGlobalWindowOuter;
@@ -1027,17 +1030,19 @@ class nsDocShell final : public nsDocLoa
 
   bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; }
 
   // Handles retrieval of subframe session history for nsDocShell::LoadURI. If a
   // load is requested in a subframe of the current DocShell, the subframe
   // loadType may need to reflect the loadType of the parent document, or in
   // some cases (like reloads), the history load may need to be cancelled. See
   // function comments for in-depth logic descriptions.
-  void MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState);
+  // Returns true if the method itself deals with the load.
+  bool MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState,
+                                  bool aContinueHandlingSubframeHistory);
 
   // If we are passed a named target during InternalLoad, this method handles
   // moving the load to the browsing context the target name resolves to.
   nsresult PerformRetargeting(nsDocShellLoadState* aLoadState);
 
   // Returns one of nsIContentPolicy::TYPE_DOCUMENT,
   // nsIContentPolicy::TYPE_INTERNAL_IFRAME, or
   // nsIContentPolicy::TYPE_INTERNAL_FRAME depending on who is responsible for
@@ -1082,16 +1087,19 @@ class nsDocShell final : public nsDocLoa
 
   void SetTitleOnHistoryEntry();
 
   void SetScrollRestorationIsManualOnHistoryEntry(nsISHEntry* aSHEntry,
                                                   bool aIsManual);
 
   void SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry, uint32_t aCacheKey);
 
+  nsresult LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating,
+                   bool aContinueHandlingSubframeHistory);
+
  private:  // data members
   nsID mHistoryID;
   nsString mTitle;
   nsCString mOriginalUriString;
   nsTObserverArray<nsWeakPtr> mPrivacyObservers;
   nsTObserverArray<nsWeakPtr> mReflowObservers;
   nsTObserverArray<nsWeakPtr> mScrollObservers;
   mozilla::UniquePtr<mozilla::dom::ClientSource> mInitialClientSource;
@@ -1145,16 +1153,18 @@ class nsDocShell final : public nsDocLoa
   // If mLSHE is non-null, non-pushState subframe loads don't create separate
   // root history entries. That is, frames loaded during the parent page
   // load don't generate history entries the way frame navigation after the
   // parent has loaded does. (This isn't the only purpose of mLSHE.)
   nsCOMPtr<nsISHEntry> mLSHE;
 
   // These are only set when fission.sessionHistoryInParent is set.
   mozilla::UniquePtr<mozilla::dom::SessionHistoryInfo> mActiveEntry;
+  bool mActiveEntryIsLoadingFromSessionHistory = false;
+  // mLoadingEntry is set when we're about to start loading.
   mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> mLoadingEntry;
 
   // Holds a weak pointer to a RestorePresentationEvent object if any that
   // holds a weak pointer back to us. We use this pointer to possibly revoke
   // the event whenever necessary.
   nsRevocableEventPtr<RestorePresentationEvent> mRestorePresentationEvent;
 
   // Editor data, if this document is designMode or contentEditable.
@@ -1281,18 +1291,16 @@ class nsDocShell final : public nsDocLoa
   // Indicates that a DocShell in this "docshell tree" is printing
   bool mIsPrintingOrPP : 1;
 
   // Indicates to CreateContentViewer() that it is safe to cache the old
   // presentation of the page, and to SetupNewViewer() that the old viewer
   // should be passed a SHEntry to save itself into.
   bool mSavingOldViewer : 1;
 
-  // @see nsIDocShellHistory::createdDynamically
-  bool mDynamicallyCreated : 1;
   bool mAffectPrivateSessionLifetime : 1;
   bool mInvisible : 1;
   bool mHasLoadedNonBlankURI : 1;
 
   // This flag means that mTiming has been initialized but nulled out.
   // We will check the innerWin's timing before creating a new one
   // in MaybeInitTiming()
   bool mBlankTiming : 1;
--- a/docshell/shistory/SessionHistoryEntry.cpp
+++ b/docshell/shistory/SessionHistoryEntry.cpp
@@ -908,16 +908,21 @@ SessionHistoryEntry::HasDetachedEditor()
   return false;
 }
 
 NS_IMETHODIMP_(bool)
 SessionHistoryEntry::IsDynamicallyAdded() {
   return SharedInfo()->mDynamicallyCreated;
 }
 
+void SessionHistoryEntry::SetIsDynamicallyAdded(bool aDynamic) {
+  MOZ_ASSERT_IF(SharedInfo()->mDynamicallyCreated, aDynamic);
+  SharedInfo()->mDynamicallyCreated = aDynamic;
+}
+
 NS_IMETHODIMP
 SessionHistoryEntry::HasDynamicallyAddedChild(bool* aHasDynamicallyAddedChild) {
   for (const auto& child : mChildren) {
     if (child && child->IsDynamicallyAdded()) {
       *aHasDynamicallyAddedChild = true;
       return NS_OK;
     }
   }
@@ -1108,17 +1113,48 @@ SessionHistoryEntry::GetChildAt(int32_t 
   nsCOMPtr<nsISHEntry> child = mChildren.SafeElementAt(aIndex);
   child.forget(aChild);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(void)
 SessionHistoryEntry::GetChildSHEntryIfHasNoDynamicallyAddedChild(
     int32_t aChildOffset, nsISHEntry** aChild) {
-  MOZ_CRASH("Need to implement this");
+  *aChild = nullptr;
+
+  bool dynamicallyAddedChild = false;
+  HasDynamicallyAddedChild(&dynamicallyAddedChild);
+  if (dynamicallyAddedChild) {
+    return;
+  }
+
+  // If the user did a shift-reload on this frameset page,
+  // we don't want to load the subframes from history.
+  if (IsForceReloadType(mInfo->mLoadType) || mInfo->mLoadType == LOAD_REFRESH) {
+    return;
+  }
+
+  /* Before looking for the subframe's url, check
+   * the expiration status of the parent. If the parent
+   * has expired from cache, then subframes will not be
+   * loaded from history in certain situations.
+   * If the user pressed reload and the parent frame has expired
+   *  from cache, we do not want to load the child frame from history.
+   */
+  if (SharedInfo()->mExpired && (mInfo->mLoadType == LOAD_RELOAD_NORMAL)) {
+    // The parent has expired. Return null.
+    *aChild = nullptr;
+    return;
+  }
+  // Get the child subframe from session history.
+  GetChildAt(aChildOffset, aChild);
+  if (*aChild) {
+    // Set the parent's Load Type on the child
+    (*aChild)->SetLoadType(mInfo->mLoadType);
+  }
 }
 
 NS_IMETHODIMP
 SessionHistoryEntry::ReplaceChild(nsISHEntry* aNewChild) {
   NS_ENSURE_STATE(aNewChild);
 
   nsCOMPtr<SessionHistoryEntry> newChild = do_QueryInterface(aNewChild);
   MOZ_ASSERT(newChild);
--- a/docshell/shistory/SessionHistoryEntry.h
+++ b/docshell/shistory/SessionHistoryEntry.h
@@ -240,16 +240,18 @@ class SessionHistoryEntry : public nsISH
 
   bool ForInitialLoad() { return mForInitialLoad; }
   void SetForInitialLoad(bool aForInitialLoad) {
     mForInitialLoad = aForInitialLoad;
   }
 
   const nsID& DocshellID() const;
 
+  void SetIsDynamicallyAdded(bool aDynamic);
+
   // Get an entry based on LoadingSessionHistoryInfo's mLoadId. Parent process
   // only.
   static SessionHistoryEntry* GetByLoadId(uint64_t aLoadId);
   static void RemoveLoadId(uint64_t aLoadId);
 
   const nsTArray<RefPtr<SessionHistoryEntry>>& Children() { return mChildren; }
 
  private:
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -6949,16 +6949,36 @@ mozilla::ipc::IPCResult ContentParent::R
   SessionHistoryEntry* entry =
       aContext.get_canonical()->GetActiveSessionHistoryEntry();
   if (entry) {
     entry->SetCacheKey(aCacheKey);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentParent::RecvGetLoadingSessionHistoryInfoFromParent(
+    const MaybeDiscarded<BrowsingContext>& aContext,
+    GetLoadingSessionHistoryInfoFromParentResolver&& aResolver) {
+  if (aContext.IsNullOrDiscarded()) {
+    return IPC_OK();
+  }
+
+  Maybe<LoadingSessionHistoryInfo> info;
+  int32_t requestedIndex = -1;
+  int32_t sessionHistoryLength = 0;
+  aContext.get_canonical()->GetLoadingSessionHistoryInfoFromParent(
+      info, &requestedIndex, &sessionHistoryLength);
+  aResolver(
+      Tuple<const mozilla::Maybe<LoadingSessionHistoryInfo>&, const int32_t&,
+            const int32_t&>(info, requestedIndex, sessionHistoryLength));
+
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult ContentParent::RecvSetActiveSessionHistoryEntryForTop(
     const MaybeDiscarded<BrowsingContext>& aContext,
     const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
     uint32_t aLoadType, const nsID& aChangeID) {
   if (!aContext.IsDiscarded()) {
     aContext.get_canonical()->SetActiveSessionHistoryEntryForTop(
         aPreviousScrollPos, &aInfo, aLoadType, aChangeID);
   }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1337,16 +1337,20 @@ class ContentParent final
   mozilla::ipc::IPCResult RecvSessionHistoryEntryCacheKey(
       const MaybeDiscarded<BrowsingContext>& aContext,
       const uint32_t& aCacheKey);
 
   mozilla::ipc::IPCResult
   RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
       const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aName);
 
+  mozilla::ipc::IPCResult RecvGetLoadingSessionHistoryInfoFromParent(
+      const MaybeDiscarded<BrowsingContext>& aContext,
+      GetLoadingSessionHistoryInfoFromParentResolver&& aResolver);
+
   mozilla::ipc::IPCResult RecvSetActiveSessionHistoryEntryForTop(
       const MaybeDiscarded<BrowsingContext>& aContext,
       const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
       uint32_t aLoadType, const nsID& aChangeID);
 
   mozilla::ipc::IPCResult RecvSetActiveSessionHistoryEntryForFrame(
       const MaybeDiscarded<BrowsingContext>& aContext,
       const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -284,16 +284,18 @@ struct DocShellLoadStateInit
   // nsCOMPtr<nsIDocShell> mSourceDocShell;
   // bool mIsSrcDocLoad; // useless without sourcedocshell
   // nsIChannel pendingRedirectedChannel; // sent through other mechanism
 
   uint64_t LoadIdentifier;
 
   bool ChannelInitialized;
 
+  bool TryToReplaceWithSessionHistoryLoad;
+
   LoadingSessionHistoryInfo? loadingSessionHistoryInfo;
 };
 
 struct TimedChannelInfo
 {
   bool timingEnabled;
   int8_t redirectCount;
   int8_t internalRedirectCount;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -125,16 +125,17 @@ using mozilla::ContentBlockingNotifier::
 using mozilla::ContentBlockingNotifier::BlockingDecision from "mozilla/ContentBlockingNotifier.h";
 using mozilla::ContentBlocking::StorageAccessPromptChoices from "mozilla/ContentBlocking.h";
 using JSActorMessageKind from "mozilla/dom/JSActor.h";
 using JSActorMessageMeta from "mozilla/dom/PWindowGlobal.h";
 using mozilla::PermissionDelegateHandler::DelegatedPermissionList from "mozilla/PermissionDelegateIPCUtils.h";
 using refcounted class nsILayoutHistoryState from "nsILayoutHistoryState.h";
 using class mozilla::dom::SessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
 using nsPoint from "mozilla/GfxMessageUtils.h";
+using struct mozilla::dom::LoadingSessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
 
@@ -939,16 +940,19 @@ parent:
                                                        bool aIsManual);
 
     async SessionHistoryEntryCacheKey(MaybeDiscardedBrowsingContext aContext,
                                       uint32_t aCacheKey);
 
     async SessionHistoryEntryStoreWindowNameInContiguousEntries(MaybeDiscardedBrowsingContext aContext,
                                                                 nsString aName);
 
+    async GetLoadingSessionHistoryInfoFromParent(MaybeDiscardedBrowsingContext aContext)
+        returns (LoadingSessionHistoryInfo? aLoadingInfo, int32_t aRequestedIndex, int32_t aLength);
+
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     async CreateGMPService();
 
     async InitStreamFilter(uint64_t channelId, nsString addonId)
         returns (Endpoint<PStreamFilterChild> aEndpoint);
 
     /**