Mark error pages as such on Ctrl-Shift-T reload.

When reloading a page whose url leads to an error page, a different
pathway is taken in NavigationControllerImpl. We must be careful to
check for unreachable urls in
NavigationControllerImpl::RendererDidNavigateToExistingPage() in this
case, and mark them as error pages.

BUG=458036

Review URL: https://codereview.chromium.org/917043004

Cr-Commit-Position: refs/heads/master@{#317046}
diff --git a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
index 378739d..a16336c1 100644
--- a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
+++ b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/process/kill.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
 #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
@@ -168,6 +169,57 @@
   EXPECT_FLOAT_EQ(new_zoom_level, zoom_controller->GetZoomLevel());
 }
 
+IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
+                       ErrorPagesCanZoomAfterTabRestore) {
+  // This url is meant to cause a network error page to be loaded.
+  // Tests can't reach the network, so this test should continue
+  // to work even if the domain listed is someday registered.
+  GURL url("http://kjfhkjsdf.com");
+
+  TabStripModel* tab_strip = browser()->tab_strip_model();
+  ASSERT_TRUE(tab_strip);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  {
+    content::WebContents* web_contents = tab_strip->GetActiveWebContents();
+
+    EXPECT_EQ(
+        content::PAGE_TYPE_ERROR,
+        web_contents->GetController().GetLastCommittedEntry()->GetPageType());
+
+    content::WebContentsDestroyedWatcher destroyed_watcher(web_contents);
+    tab_strip->CloseWebContentsAt(tab_strip->active_index(),
+                                  TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
+    destroyed_watcher.Wait();
+  }
+  EXPECT_EQ(1, tab_strip->count());
+
+  content::WebContentsAddedObserver new_web_contents_observer;
+  chrome::RestoreTab(browser());
+  content::WebContents* web_contents =
+      new_web_contents_observer.GetWebContents();
+  content::WaitForLoadStop(web_contents);
+
+  EXPECT_EQ(2, tab_strip->count());
+
+  EXPECT_EQ(
+      content::PAGE_TYPE_ERROR,
+      web_contents->GetController().GetLastCommittedEntry()->GetPageType());
+
+  ZoomController* zoom_controller =
+      ZoomController::FromWebContents(web_contents);
+
+  double old_zoom_level = zoom_controller->GetZoomLevel();
+  double new_zoom_level = old_zoom_level + 0.5;
+
+  // The following attempt to change the zoom level for an error page should
+  // fail.
+  zoom_controller->SetZoomLevel(new_zoom_level);
+  EXPECT_FLOAT_EQ(new_zoom_level, zoom_controller->GetZoomLevel());
+}
+
 IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, Observe) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index b358e55..faa0bbd 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -1057,10 +1057,6 @@
        pending_entry_->site_instance() == rfh->GetSiteInstance())) {
     new_entry = new NavigationEntryImpl(*pending_entry_);
 
-    // Don't use the page type from the pending entry. Some interstitial page
-    // may have set the type to interstitial. Once we commit, however, the page
-    // type must always be normal.
-    new_entry->set_page_type(PAGE_TYPE_NORMAL);
     update_virtual_url = new_entry->update_virtual_url_with_url();
   } else {
     new_entry = new NavigationEntryImpl;
@@ -1081,8 +1077,11 @@
     update_virtual_url = needs_update;
   }
 
-  if (params.url_is_unreachable)
-    new_entry->set_page_type(PAGE_TYPE_ERROR);
+  // Don't use the page type from the pending entry. Some interstitial page
+  // may have set the type to interstitial. Once we commit, however, the page
+  // type must always be normal or error.
+  new_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                     : PAGE_TYPE_NORMAL);
   new_entry->SetURL(params.url);
   if (update_virtual_url)
     UpdateVirtualURLToURL(new_entry, params.url);
@@ -1133,6 +1132,8 @@
   NavigationEntryImpl* entry = entries_[entry_index].get();
 
   // The URL may have changed due to redirects.
+  entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                 : PAGE_TYPE_NORMAL);
   entry->SetURL(params.url);
   entry->SetReferrer(params.referrer);
   if (entry->update_virtual_url_with_url())
@@ -1185,6 +1186,8 @@
   existing_entry->set_unique_id(pending_entry_->GetUniqueID());
 
   // The URL may have changed due to redirects.
+  existing_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                          : PAGE_TYPE_NORMAL);
   if (existing_entry->update_virtual_url_with_url())
     UpdateVirtualURLToURL(existing_entry, params.url);
   existing_entry->SetURL(params.url);
@@ -1211,6 +1214,8 @@
   // entry and it will be the same page as the new navigation (minus the
   // reference fragments, of course).  We'll update the URL of the existing
   // entry without pruning the forward history.
+  existing_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                          : PAGE_TYPE_NORMAL);
   existing_entry->SetURL(params.url);
   if (existing_entry->update_virtual_url_with_url())
     UpdateVirtualURLToURL(existing_entry, params.url);
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 2a36a47..8642679 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -30,6 +30,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/page_state.h"
+#include "content/public/common/page_type.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_notification_tracker.h"
@@ -4537,4 +4538,61 @@
   EXPECT_EQ(0, delegate->repost_form_warning_count());
 }
 
+TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
+  GURL url("http://foo");
+  FrameHostMsg_DidCommitProvisionalLoad_Params params;
+  params.page_id = 1;
+  params.url = url;
+  params.transition = ui::PAGE_TRANSITION_LINK;
+  params.gesture = NavigationGestureUser;
+  params.page_state = PageState::CreateFromURL(url);
+  params.was_within_same_page = false;
+  params.is_post = true;
+  params.post_id = 2;
+  params.url_is_unreachable = true;
+  // Navigate to new page
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details.type);
+  }
+
+  // Navigate to existing page.
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
+  }
+
+  // Navigate to same page.
+  // Note: The call to LoadURL() creates a pending entry in order to trigger the
+  // same-page transition.
+  controller_impl().LoadURL(
+      url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  params.transition = ui::PAGE_TRANSITION_TYPED;
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, details.type);
+  }
+
+  // Navigate in page.
+  params.url = GURL("http://foo#foo");
+  params.transition = ui::PAGE_TRANSITION_LINK;
+  params.was_within_same_page = true;
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, details.type);
+  }
+}
+
 }  // namespace content