Bug 1318767 - Part 3: Use SessionStore to recover from missing GroupedSHistory segments, r=ehsan, r=mconley
authorMichael Layzell <michael@thelayzells.com>
Tue, 13 Dec 2016 16:32:54 +0800
changeset 449580 44a95445e11d8eff6ca5c10f1b2b913a9f44eeb0
parent 449579 447db63f9b10e75fd48a42a38ce9298ddd839930
child 449581 3360b8d2f04bc8439c101c3fa892eecd7465e073
push id38613
push userbmo:till@tillschneidereit.net
push dateWed, 14 Dec 2016 16:03:56 +0000
reviewersehsan, mconley
bugs1318767
milestone53.0a1
Bug 1318767 - Part 3: Use SessionStore to recover from missing GroupedSHistory segments, r=ehsan, r=mconley MozReview-Commit-ID: FDmQIjiHox7
browser/base/content/browser.js
docshell/shistory/nsIGroupedSHistory.idl
dom/base/GroupedSHistory.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsIFrameLoader.idl
xpfe/appshell/nsIXULBrowserWindow.idl
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4664,16 +4664,27 @@ var XULBrowserWindow = {
       { URI: gBrowser.currentURI },
       loadingDone ? nsIWebProgressListener.STATE_STOP : nsIWebProgressListener.STATE_START,
       aStatus
     );
     // status message and progress value are undefined if we're done with loading
     if (loadingDone)
       return;
     this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
+  },
+
+  navigateAndRestoreByIndex: function XWB_navigateAndRestoreByIndex(aBrowser, aIndex) {
+    let tab = gBrowser.getTabForBrowser(aBrowser);
+    if (tab) {
+      SessionStore.navigateAndRestore(tab, {}, aIndex);
+      return;
+    }
+
+    throw new Error("Trying to navigateAndRestore a browser which was " +
+                    "not attached to this tabbrowser is unsupported");
   }
 };
 
 var LinkTargetDisplay = {
   get DELAY_SHOW() {
      delete this.DELAY_SHOW;
      return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
   },
--- a/docshell/shistory/nsIGroupedSHistory.idl
+++ b/docshell/shistory/nsIGroupedSHistory.idl
@@ -34,20 +34,24 @@ interface nsIGroupedSHistory : nsISuppor
    */
   void onPartialSessionHistoryChange(in nsIPartialSHistory aPartialHistory);
 
   /**
    * Find the proper partial session history and navigate to the entry
    * corresponding to the given global index. Note it doesn't swap frameloaders,
    * but rather return the target loader for the caller to swap.
    *
-   * @param aGlobalIndex        The global index to navigate to.
-   * @param aTargetLoaderToSwap The owner frameloader of the to-be-navigate
-   *                            partial session history.
+   * This function may throw NS_ERROR_NOT_AVAILABLE if the frameloader to swap
+   * to is dead.
+   *
+   * @param  aGlobalIndex
+   *         The global index to navigate to.
+   * @return The frameloader which needs to be swapped in, or null if no
+   *         frameloader needs to be swapped.
    */
-  void gotoIndex(in unsigned long aGlobalIndex, out nsIFrameLoader aTargetLoaderToSwap);
+  nsIFrameLoader gotoIndex(in unsigned long aGlobalIndex);
 
   /**
    * Close the FrameLoaderOwners of the inactive PartialSHistories in this GlobalSHistory.
    * This does not remove the PartialSHistories from the GroupedSHistory.
    */
   void closeInactiveFrameLoaderOwners();
 };
--- a/dom/base/GroupedSHistory.cpp
+++ b/dom/base/GroupedSHistory.cpp
@@ -94,16 +94,19 @@ GroupedSHistory::OnPartialSessionHistory
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GroupedSHistory::GotoIndex(uint32_t aGlobalIndex,
                            nsIFrameLoader** aTargetLoaderToSwap)
 {
+  MOZ_ASSERT(aTargetLoaderToSwap);
+  *aTargetLoaderToSwap = nullptr;
+
   nsCOMPtr<nsIPartialSHistory> currentPartialHistory =
     mPartialHistories[mIndexOfActivePartialHistory];
   if (!currentPartialHistory) {
     // Cycle collected?
     return NS_ERROR_UNEXPECTED;
   }
 
   for (uint32_t i = 0; i < mPartialHistories.Length(); i++) {
@@ -113,25 +116,36 @@ GroupedSHistory::GotoIndex(uint32_t aGlo
       return NS_ERROR_UNEXPECTED;
     }
 
     // Examine index range.
     uint32_t offset = partialHistory->GetGlobalIndexOffset();
     uint32_t count = partialHistory->GetCount();
     if (offset <= aGlobalIndex && (offset + count) > aGlobalIndex) {
       uint32_t targetIndex = aGlobalIndex - offset;
-      partialHistory->GetOwnerFrameLoader(aTargetLoaderToSwap);
+
+      // Check if we are trying to swap to a dead frameloader, and return
+      // NS_ERROR_NOT_AVAILABLE if we are.
+      nsCOMPtr<nsIFrameLoader> frameLoader;
+      partialHistory->GetOwnerFrameLoader(getter_AddRefs(frameLoader));
+      if (!frameLoader || frameLoader->GetIsDead()) {
+        return NS_ERROR_NOT_AVAILABLE;
+      }
+
       if ((size_t)mIndexOfActivePartialHistory == i) {
         return NS_OK;
       }
       mIndexOfActivePartialHistory = i;
       if (NS_FAILED(currentPartialHistory->OnDeactive()) ||
           NS_FAILED(partialHistory->OnActive(mCount, targetIndex))) {
         return NS_ERROR_FAILURE;
       }
+
+      // Return the target frameloader to the caller.
+      frameLoader.forget(aTargetLoaderToSwap);
       return NS_OK;
     }
   }
 
   // Index not found.
   NS_WARNING("Out of index request!");
   return NS_ERROR_FAILURE;
 }
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -510,27 +510,80 @@ public:
   RequestGroupedHistoryNavigationHelper(nsFrameLoader* aThis,
                                         uint32_t aGlobalIndex,
                                         Promise* aPromise)
     : mThis(aThis), mGlobalIndex(aGlobalIndex), mPromise(aPromise) {}
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
+    if (NS_WARN_IF(!mThis->mOwnerContent)) {
+      mPromise->MaybeRejectWithUndefined();
+      return;
+    }
+
     // Navigate the loader to the new index
     nsCOMPtr<nsIFrameLoader> otherLoader;
     nsresult rv = mThis->mGroupedSessionHistory->
                     GotoIndex(mGlobalIndex, getter_AddRefs(otherLoader));
-    if (NS_WARN_IF(!otherLoader || NS_FAILED(rv))) {
+
+    // Check if the gotoIndex failed because the target frameloader is dead. We
+    // need to perform a navigateAndRestoreByIndex and then return to recover.
+    if (rv == NS_ERROR_NOT_AVAILABLE) {
+      // Get the nsIXULBrowserWindow so that we can call NavigateAndRestoreByIndex on it.
+      nsCOMPtr<nsIDocShell> docShell = mThis->mOwnerContent->OwnerDoc()->GetDocShell();
+      if (NS_WARN_IF(!docShell)) {
+        mPromise->MaybeRejectWithUndefined();
+        return;
+      }
+
+      nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+      docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+      if (NS_WARN_IF(!treeOwner)) {
+        mPromise->MaybeRejectWithUndefined();
+        return;
+      }
+
+      nsCOMPtr<nsIXULWindow> window = do_GetInterface(treeOwner);
+      if (NS_WARN_IF(!window)) {
+        mPromise->MaybeRejectWithUndefined();
+        return;
+      }
+
+      nsCOMPtr<nsIXULBrowserWindow> xbw;
+      window->GetXULBrowserWindow(getter_AddRefs(xbw));
+      if (NS_WARN_IF(!xbw)) {
+        mPromise->MaybeRejectWithUndefined();
+        return;
+      }
+
+      nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(mThis->mOwnerContent);
+      if (NS_WARN_IF(!ourBrowser)) {
+        mPromise->MaybeRejectWithUndefined();
+        return;
+      }
+
+      rv = xbw->NavigateAndRestoreByIndex(ourBrowser, mGlobalIndex);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        mPromise->MaybeRejectWithUndefined();
+        return;
+      }
+      mPromise->MaybeResolveWithUndefined();
+      return;
+    }
+
+    // Check for any other type of failure
+    if (NS_WARN_IF(NS_FAILED(rv))) {
       mPromise->MaybeRejectWithUndefined();
       return;
     }
+
+    // Perform the swap.
     nsFrameLoader* other = static_cast<nsFrameLoader*>(otherLoader.get());
-
-    if (other == mThis) {
+    if (!other || other == mThis) {
       mPromise->MaybeRejectWithUndefined();
       return;
     }
 
     // Swap the browsers and fire the BrowserChangedProcess event.
     if (mThis->SwapBrowsersAndNotify(other)) {
       mPromise->MaybeResolveWithUndefined();
     } else {
@@ -3570,14 +3623,21 @@ nsFrameLoader::PopulateUserContextIdFrom
       aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::GetIsDead(bool* aIsDead)
+{
+  *aIsDead = mDestroyCalled;
+  return NS_OK;
+}
+
 nsIMessageSender*
 nsFrameLoader::GetProcessMessageManager() const
 {
   return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager()
                         : nullptr;
 };
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -253,16 +253,21 @@ interface nsIFrameLoader : nsISupports
    */
   readonly attribute nsIPartialSHistory partialSessionHistory;
 
   /**
    * The grouped session history composed of multiple session history objects
    * across root docshells.
    */
   readonly attribute nsIGroupedSHistory groupedSessionHistory;
+
+  /**
+   * Is `true` if the frameloader is dead (destroy has been called on it)
+   */
+  [infallible] readonly attribute boolean isDead;
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
--- a/xpfe/appshell/nsIXULBrowserWindow.idl
+++ b/xpfe/appshell/nsIXULBrowserWindow.idl
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIURI.idl"
 #include "nsIDOMNode.idl"
 
+interface nsIBrowser;
 interface nsIRequest;
 interface nsIDOMElement;
 interface nsIInputStream;
 interface nsIDocShell;
 interface nsITabParent;
 interface mozIDOMWindowProxy;
 
 /**
@@ -68,10 +69,19 @@ interface nsIXULBrowserWindow : nsISuppo
    */
   void showTooltip(in long x, in long y, in AString tooltip, in AString direction);
   void hideTooltip();
 
   /**
    * Return the number of tabs in this window.
    */
   uint32_t getTabCount();
+
+  /**
+   * Navigate the browser to the given history index after restoring the full history
+   * from SessionStore. If the browser is currently in GroupedSHistory mode, it will
+   * be reverted to a non-grouped history mode. If a process change is required to
+   * perform the load, this will also occur.
+   */
+  void navigateAndRestoreByIndex(in nsIBrowser aBrowser, in long aIndex);
+
 };