Introduce NavigateToExistingPendingEntry in NavigationController

Following the introduction of NavigateWithoutEntry in
https://chromium-review.googlesource.com/c/chromium/src/+/904988, this
CL introduces a new function in NavigationController,
NavigateToExistingPendingEntry. This function should be used when requesting a
navigation to an existing NavigationEntry.

Bug: 803365, 803859
Change-Id: I2b1901c0e63934aac4df9837104dcb3f8f28fdc0
Reviewed-on: https://chromium-review.googlesource.com/1089052
Reviewed-by: Charlie Reis <[email protected]>
Commit-Queue: Camille Lamy <[email protected]>
Cr-Commit-Position: refs/heads/master@{#573693}
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 3423efe3..4e51a98 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -494,7 +494,7 @@
     pending_entry_index_ = current_index;
     pending_entry_->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
 
-    NavigateToPendingEntry(reload_type, nullptr /* navigation_ui_data */);
+    NavigateToExistingPendingEntry(reload_type);
   }
 }
 
@@ -697,7 +697,7 @@
   pending_entry_index_ = index;
   pending_entry_->SetTransitionType(ui::PageTransitionFromInt(
       pending_entry_->GetTransitionType() | ui::PAGE_TRANSITION_FORWARD_BACK));
-  NavigateToPendingEntry(ReloadType::NONE, nullptr /* navigation_ui_data */);
+  NavigateToExistingPendingEntry(ReloadType::NONE);
 }
 
 void NavigationControllerImpl::GoToOffset(int offset) {
@@ -2134,10 +2134,11 @@
   }
 }
 
-void NavigationControllerImpl::NavigateToPendingEntry(
-    ReloadType reload_type,
-    std::unique_ptr<NavigationUIData> navigation_ui_data) {
+void NavigationControllerImpl::NavigateToExistingPendingEntry(
+    ReloadType reload_type) {
   DCHECK(pending_entry_);
+  DCHECK(IsInitialNavigation() || pending_entry_index_ != -1);
+  DCHECK(!IsRendererDebugURL(pending_entry_->GetURL()));
   needs_reload_ = false;
 
   // If we were navigating to a slow-to-commit page, and the user performs
@@ -2146,8 +2147,7 @@
   // navigation, and won't send a message to stop the throbber. To prevent this
   // from happening, we drop the navigation here and stop the slow-to-commit
   // page from loading (which would normally happen during the navigation).
-  if (pending_entry_index_ != -1 &&
-      pending_entry_index_ == last_committed_entry_index_ &&
+  if (pending_entry_index_ == last_committed_entry_index_ &&
       pending_entry_->restore_type() == RestoreType::NONE &&
       pending_entry_->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK) {
     delegate_->Stop();
@@ -2169,65 +2169,15 @@
     return;
   }
 
-  // If an interstitial page is showing, the previous renderer is blocked and
-  // cannot make new requests.  Unblock (and disable) it to allow this
-  // navigation to succeed.  The interstitial will stay visible until the
-  // resulting DidNavigate.
-  if (delegate_->GetInterstitialPage()) {
-    static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())
-        ->CancelForNavigation();
-  }
-
-  // Convert navigations to the current URL to a reload.
-  if (ShouldTreatNavigationAsReload(
-          pending_entry_->GetURL(), pending_entry_->GetVirtualURL(),
-          pending_entry_->GetBaseURLForDataURL(),
-          pending_entry_->GetTransitionType(),
-          pending_entry_->frame_tree_node_id() == -1 /* is_main_frame */,
-          pending_entry_->GetHasPostData() /* is _post */,
-          reload_type != ReloadType::NONE /* is_reload */,
-          pending_entry_index_ != -1 /* is_navigation_to_existing_entry */,
-          transient_entry_index_ != -1 /* has_interstitial */,
-          GetLastCommittedEntry())) {
-    reload_type = ReloadType::NORMAL;
-  }
-
-  // Any renderer-side debug URLs or javascript: URLs should be ignored if the
-  // renderer process is not live, unless it is the initial navigation of the
-  // tab.
-  // TODO(clamy): Remove this as it is duplicated in HandleRendererDebugURL.
-  if (IsRendererDebugURL(pending_entry_->GetURL())) {
-    // TODO(creis): Find the RVH for the correct frame.
-    if (!delegate_->GetRenderViewHost()->IsRenderViewLive() &&
-        !IsInitialNavigation()) {
-      DiscardNonCommittedEntries();
-      return;
-    }
-  }
-
-  // This call does not support re-entrancy.  See http://crbug.com/347742.
-  CHECK(!in_navigate_to_pending_entry_);
-  in_navigate_to_pending_entry_ = true;
-  bool success = NavigateToPendingEntryInternal(reload_type,
-                                                std::move(navigation_ui_data));
-  in_navigate_to_pending_entry_ = false;
-
-  if (!success)
-    DiscardNonCommittedEntries();
-}
-
-bool NavigationControllerImpl::NavigateToPendingEntryInternal(
-    ReloadType reload_type,
-    std::unique_ptr<NavigationUIData> navigation_ui_data) {
-  DCHECK(pending_entry_);
   FrameTreeNode* root = delegate_->GetFrameTree()->root();
 
   // Compare FrameNavigationEntries to see which frames in the tree need to be
   // navigated.
-  FrameLoadVector same_document_loads;
-  FrameLoadVector different_document_loads;
+  std::vector<std::unique_ptr<NavigationRequest>> same_document_loads;
+  std::vector<std::unique_ptr<NavigationRequest>> different_document_loads;
   if (GetLastCommittedEntry()) {
-    FindFramesToNavigate(root, &same_document_loads, &different_document_loads);
+    FindFramesToNavigate(root, reload_type, &same_document_loads,
+                         &different_document_loads);
   }
 
   if (same_document_loads.empty() && different_document_loads.empty()) {
@@ -2238,62 +2188,55 @@
     // thing to do. In the second case, we should have been able to find a
     // frame to navigate based on names if this were a same document
     // navigation, so we can safely assume this is the different document case.
-    different_document_loads.push_back(
-        std::make_pair(root, pending_entry_->GetFrameEntry(root)));
+    std::unique_ptr<NavigationRequest> navigation_request =
+        CreateNavigationRequest(
+            root, *pending_entry_, pending_entry_->GetFrameEntry(root),
+            reload_type, false /* is_same_document_history_load */,
+            false /* is_history_navigation_in_new_child */, nullptr, nullptr);
+    if (!navigation_request) {
+      // This navigation cannot start (e.g. the URL is invalid), delete the
+      // pending NavigationEntry.
+      DiscardPendingEntry(false);
+      return;
+    }
+    different_document_loads.push_back(std::move(navigation_request));
   }
 
-  // If all the frame loads fail, we will discard the pending entry.
-  bool success = false;
+  // If an interstitial page is showing, the previous renderer is blocked and
+  // cannot make new requests.  Unblock (and disable) it to allow this
+  // navigation to succeed.  The interstitial will stay visible until the
+  // resulting DidNavigate.
+  // TODO(clamy): See if this can be removed now that PlzNavigate has shipped.
+  // See https://crbug.com/849250
+  if (delegate_->GetInterstitialPage()) {
+    static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())
+        ->CancelForNavigation();
+  }
+
+  // This call does not support re-entrancy.  See http://crbug.com/347742.
+  CHECK(!in_navigate_to_pending_entry_);
+  in_navigate_to_pending_entry_ = true;
 
   // Send all the same document frame loads before the different document loads.
-  for (const auto& item : same_document_loads) {
-    FrameTreeNode* frame = item.first;
-    DCHECK(!IsRendererDebugURL(item.second->url()));
-    std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
-        frame, *pending_entry_, item.second, reload_type,
-        true /* is_same_document_history_load */,
-        false /* is_history_navigation_in_new_child */, nullptr, nullptr);
-    if (request) {
-      success = true;
-      frame->navigator()->Navigate(std::move(request), reload_type,
-                                   pending_entry_->restore_type());
-    }
+  for (auto& item : same_document_loads) {
+    FrameTreeNode* frame = item->frame_tree_node();
+    frame->navigator()->Navigate(std::move(item), reload_type,
+                                 pending_entry_->restore_type());
   }
-  for (const auto& item : different_document_loads) {
-    FrameTreeNode* frame = item.first;
-    if (IsRendererDebugURL(item.second->url())) {
-      HandleRendererDebugURL(frame, item.second->url());
-      if (!item.second->url().SchemeIs(url::kJavaScriptScheme)) {
-        // Note: navigations to Javascript URLs should not result in a pending
-        // NavigationEntry. This is why they should return false from this
-        // function.
-        // TODO(clamy): Clean this up as part of the NavigationController
-        // refactoring. Ideally, navigations to JavaScript URLs should not
-        // arrive here.
-        success = true;
-      }
-    } else {
-      std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
-          frame, *pending_entry_, item.second, reload_type,
-          false /* is_same_document_history_load */,
-          false /* is_history_navigation_in_new_child */, nullptr,
-          // The NavigationUIData has only been initialized for main frames. Do
-          // not pass it to subframes.
-          frame->IsMainFrame() ? std::move(navigation_ui_data) : nullptr);
-      if (request) {
-        success = true;
-        frame->navigator()->Navigate(std::move(request), reload_type,
-                                     pending_entry_->restore_type());
-      }
-    }
+  for (auto& item : different_document_loads) {
+    FrameTreeNode* frame = item->frame_tree_node();
+    frame->navigator()->Navigate(std::move(item), reload_type,
+                                 pending_entry_->restore_type());
   }
-  return success;
+
+  in_navigate_to_pending_entry_ = false;
 }
 
 void NavigationControllerImpl::FindFramesToNavigate(
     FrameTreeNode* frame,
-    FrameLoadVector* same_document_loads,
-    FrameLoadVector* different_document_loads) {
+    ReloadType reload_type,
+    std::vector<std::unique_ptr<NavigationRequest>>* same_document_loads,
+    std::vector<std::unique_ptr<NavigationRequest>>* different_document_loads) {
   DCHECK(pending_entry_);
   DCHECK_GE(last_committed_entry_index_, 0);
   FrameNavigationEntry* new_item = pending_entry_->GetFrameEntry(frame);
@@ -2321,7 +2264,16 @@
         new_item->document_sequence_number() ==
             old_item->document_sequence_number() &&
         !frame->current_frame_host()->GetLastCommittedURL().is_empty()) {
-      same_document_loads->push_back(std::make_pair(frame, new_item));
+      std::unique_ptr<NavigationRequest> navigation_request =
+          CreateNavigationRequest(
+              frame, *pending_entry_, new_item, reload_type,
+              true /* is_same_document_history_load */,
+              false /* is_history_navigation_in_new_child */, nullptr, nullptr);
+      if (navigation_request) {
+        // Only add the request if was properly created. It's possible for the
+        // creation to fail in certain cases, e.g. when the URL is invalid.
+        same_document_loads->push_back(std::move(navigation_request));
+      }
 
       // TODO(avi, creis): This is a bug; we should not return here. Rather, we
       // should continue on and navigate all child frames which have also
@@ -2338,7 +2290,16 @@
       // different way that will one day allow us to fix this.
       return;
     } else {
-      different_document_loads->push_back(std::make_pair(frame, new_item));
+      std::unique_ptr<NavigationRequest> navigation_request =
+          CreateNavigationRequest(
+              frame, *pending_entry_, new_item, reload_type,
+              false /* is_same_document_history_load */,
+              false /* is_history_navigation_in_new_child */, nullptr, nullptr);
+      if (navigation_request) {
+        // Only add the request if was properly created. It's possible for the
+        // creation to fail in certain cases, e.g. when the URL is invalid.
+        different_document_loads->push_back(std::move(navigation_request));
+      }
       // For a different document, the subframes will be destroyed, so there's
       // no need to consider them.
       return;
@@ -2346,7 +2307,7 @@
   }
 
   for (size_t i = 0; i < frame->child_count(); i++) {
-    FindFramesToNavigate(frame->child_at(i), same_document_loads,
+    FindFramesToNavigate(frame->child_at(i), reload_type, same_document_loads,
                          different_document_loads);
   }
 }
@@ -2702,11 +2663,11 @@
   // Explicitly use NavigateToPendingEntry so that the renderer uses the
   // cached state.
   if (pending_entry_) {
-    NavigateToPendingEntry(ReloadType::NONE, nullptr /* navigation_ui_data */);
+    NavigateToExistingPendingEntry(ReloadType::NONE);
   } else if (last_committed_entry_index_ != -1) {
     pending_entry_ = entries_[last_committed_entry_index_].get();
     pending_entry_index_ = last_committed_entry_index_;
-    NavigateToPendingEntry(ReloadType::NONE, nullptr /* navigation_ui_data */);
+    NavigateToExistingPendingEntry(ReloadType::NONE);
   } else {
     // If there is something to reload, the successful reload will clear the
     // |needs_reload_| flag. Otherwise, just do it here.