Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / errorpage_browsertest.cc
index 15a1590..61d09f1 100644 (file)
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/path_service.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/google/google_util.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_remover.h"
+#include "chrome/browser/google/google_profile_helper.h"
 #include "chrome/browser/net/url_request_mock_util.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/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/google/core/browser/google_util.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
-#include "content/test/net/url_request_failed_job.h"
-#include "content/test/net/url_request_mock_http_job.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/http/failing_http_transaction_factory.h"
+#include "net/http/http_cache.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+#include "net/test/url_request/url_request_failed_job.h"
+#include "net/test/url_request/url_request_mock_http_job.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_filter.h"
-#include "net/url_request/url_request_job_factory.h"
+#include "net/url_request/url_request_interceptor.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "ui/base/l10n/l10n_util.h"
 
 using content::BrowserThread;
 using content::NavigationController;
-using content::URLRequestFailedJob;
+using net::URLRequestFailedJob;
+using net::URLRequestTestJob;
 
 namespace {
 
+// Returns true if |text| is displayed on the page |browser| is currently
+// displaying.  Uses "innerText", so will miss hidden text, and whitespace
+// space handling may be weird.
+bool WARN_UNUSED_RESULT IsDisplayingText(Browser* browser,
+                                         const std::string& text) {
+  std::string command = base::StringPrintf(
+      "var textContent = document.body.innerText;"
+      "var hasText = textContent.indexOf('%s') >= 0;"
+      "domAutomationController.send(hasText);",
+      text.c_str());
+  bool result = false;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      browser->tab_strip_model()->GetActiveWebContents(), command, &result));
+  return result;
+}
+
+// Expands the more box on the currently displayed error page.
+void ToggleHelpBox(Browser* browser) {
+  EXPECT_TRUE(content::ExecuteScript(
+      browser->tab_strip_model()->GetActiveWebContents(),
+      "document.getElementById('details-button').click();"));
+}
+
+// Returns true if |browser| is displaying the text representation of
+// |error_code| on the current page.
+bool WARN_UNUSED_RESULT IsDisplayingNetError(Browser* browser,
+                                             net::Error error_code) {
+  return IsDisplayingText(browser, net::ErrorToShortString(error_code));
+}
+
+// Checks that the local error page is being displayed, without remotely
+// retrieved navigation corrections, and with the specified error code.
+void ExpectDisplayingLocalErrorPage(Browser* browser, net::Error error_code) {
+  // Expand the help box so innerText will include text below the fold.
+  ToggleHelpBox(browser);
+
+  EXPECT_TRUE(IsDisplayingNetError(browser, error_code));
+
+  // Locally generated error pages should not have navigation corrections.
+  EXPECT_FALSE(IsDisplayingText(browser, "http://correction1/"));
+  EXPECT_FALSE(IsDisplayingText(browser, "http://correction2/"));
+
+  // Locally generated error pages should not have a populated search box.
+  bool search_box_populated = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      browser->tab_strip_model()->GetActiveWebContents(),
+      "var searchText = document.getElementById('search-box').value;"
+          "domAutomationController.send(searchText == 'search query');",
+      &search_box_populated));
+  EXPECT_FALSE(search_box_populated);
+}
+
+// Checks that an error page with information retrieved from the navigation
+// correction service is being displayed, with the specified specified error
+// code.
+void ExpectDisplayingNavigationCorrections(Browser* browser,
+                                           net::Error error_code) {
+  // Expand the help box so innerText will include text below the fold.
+  ToggleHelpBox(browser);
+
+  EXPECT_TRUE(IsDisplayingNetError(browser, error_code));
+
+  // Check that the mock navigation corrections are displayed.
+  EXPECT_TRUE(IsDisplayingText(browser, "http://correction1/"));
+  EXPECT_TRUE(IsDisplayingText(browser, "http://correction2/"));
+
+  // Check that the search box is populated correctly.
+  bool search_box_populated = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      browser->tab_strip_model()->GetActiveWebContents(),
+      "var searchText = document.getElementById('search-box').value;"
+          "domAutomationController.send(searchText == 'search query');",
+      &search_box_populated));
+  EXPECT_TRUE(search_box_populated);
+}
+
+std::string GetLoadStaleButtonLabel() {
+  return l10n_util::GetStringUTF8(IDS_ERRORPAGES_BUTTON_LOAD_STALE);
+}
+
+void AddInterceptorForURL(
+    const GURL& url,
+    scoped_ptr<net::URLRequestInterceptor> handler) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
+      url, handler.Pass());
+}
+
+// An interceptor that fails a configurable number of requests, then succeeds
+// all requests after that, keeping count of failures and successes.
+class FailFirstNRequestsInterceptor : public net::URLRequestInterceptor {
+ public:
+  explicit FailFirstNRequestsInterceptor(int requests_to_fail)
+      : requests_(0), failures_(0), requests_to_fail_(requests_to_fail) {}
+  virtual ~FailFirstNRequestsInterceptor() {}
+
+  // net::URLRequestInterceptor implementation
+  virtual net::URLRequestJob* MaybeInterceptRequest(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const OVERRIDE {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    requests_++;
+    if (failures_ < requests_to_fail_) {
+      failures_++;
+      // Note: net::ERR_CONNECTION_RESET does not summon the Link Doctor; see
+      // NetErrorHelperCore::GetErrorPageURL.
+      return new URLRequestFailedJob(request,
+                                     network_delegate,
+                                     net::ERR_CONNECTION_RESET);
+    } else {
+      return new URLRequestTestJob(request, network_delegate,
+                                   URLRequestTestJob::test_headers(),
+                                   URLRequestTestJob::test_data_1(),
+                                   true);
+    }
+  }
+
+  int requests() const { return requests_; }
+  int failures() const { return failures_; }
+
+ private:
+  // These are mutable because MaybeCreateJob is const but we want this state
+  // for testing.
+  mutable int requests_;
+  mutable int failures_;
+  int requests_to_fail_;
+
+  DISALLOW_COPY_AND_ASSIGN(FailFirstNRequestsInterceptor);
+};
+
+// An interceptor that serves LinkDoctor responses.  It also allows waiting
+// until a certain number of requests have been sent.
+// TODO(mmenke):  Wait until responses have been received instead.
+class LinkDoctorInterceptor : public net::URLRequestInterceptor {
+ public:
+  LinkDoctorInterceptor() : num_requests_(0),
+                            requests_to_wait_for_(-1),
+                            weak_factory_(this) {
+  }
+
+  virtual ~LinkDoctorInterceptor() {}
+
+  // net::URLRequestInterceptor implementation
+  virtual net::URLRequestJob* MaybeInterceptRequest(
+      net::URLRequest* request,
+      net::NetworkDelegate* network_delegate) const OVERRIDE {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&LinkDoctorInterceptor::RequestCreated,
+                   weak_factory_.GetWeakPtr()));
+
+    base::FilePath root_http;
+    PathService::Get(chrome::DIR_TEST_DATA, &root_http);
+    return new net::URLRequestMockHTTPJob(
+        request,
+        network_delegate,
+        root_http.AppendASCII("mock-link-doctor.json"),
+        BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
+            base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+  }
+
+  void WaitForRequests(int requests_to_wait_for) {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    DCHECK_EQ(-1, requests_to_wait_for_);
+    DCHECK(!run_loop_);
+
+    if (requests_to_wait_for >= num_requests_)
+      return;
+
+    requests_to_wait_for_ = requests_to_wait_for;
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+    run_loop_.reset();
+    requests_to_wait_for_ = -1;
+    EXPECT_EQ(num_requests_, requests_to_wait_for);
+  }
+
+  // It is up to the caller to wait until all relevant requests has been
+  // created, either through calling WaitForRequests or some other manner,
+  // before calling this method.
+  int num_requests() const {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+    return num_requests_;
+  }
+
+ private:
+  void RequestCreated() {
+    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+    num_requests_++;
+    if (num_requests_ == requests_to_wait_for_)
+      run_loop_->Quit();
+  }
+
+  // These are only used on the UI thread.
+  int num_requests_;
+  int requests_to_wait_for_;
+  scoped_ptr<base::RunLoop> run_loop_;
+
+  // This prevents any risk of flake if any test doesn't wait for a request
+  // it sent.  Mutable so it can be accessed from a const function.
+  mutable base::WeakPtrFactory<LinkDoctorInterceptor> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(LinkDoctorInterceptor);
+};
+
+void InstallMockInterceptors(
+    const GURL& search_url,
+    scoped_ptr<net::URLRequestInterceptor> link_doctor_interceptor) {
+  chrome_browser_net::SetUrlRequestMocksEnabled(true);
+
+  AddInterceptorForURL(google_util::LinkDoctorBaseURL(),
+                       link_doctor_interceptor.Pass());
+
+  // Add a mock for the search engine the error page will use.
+  base::FilePath root_http;
+  PathService::Get(chrome::DIR_TEST_DATA, &root_http);
+  net::URLRequestMockHTTPJob::AddHostnameToFileHandler(
+      search_url.host(),
+      root_http.AppendASCII("title3.html"),
+      BrowserThread::GetBlockingPool());
+}
+
 class ErrorPageTest : public InProcessBrowserTest {
  public:
   enum HistoryNavigationDirection {
@@ -41,11 +292,20 @@ class ErrorPageTest : public InProcessBrowserTest {
     HISTORY_NAVIGATE_FORWARD,
   };
 
+  ErrorPageTest() : link_doctor_interceptor_(NULL) {}
+  virtual ~ErrorPageTest() {}
+
+  // Navigates the active tab to a mock url created for the file at |file_path|.
+  // Needed for StaleCacheStatus and StaleCacheStatusFailedCorrections tests.
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    command_line->AppendSwitch(switches::kEnableOfflineLoadStaleCache);
+  }
+
   // Navigates the active tab to a mock url created for the file at |file_path|.
   void NavigateToFileURL(const base::FilePath::StringType& file_path) {
     ui_test_utils::NavigateToURL(
         browser(),
-        content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path)));
+        net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path)));
   }
 
   // Navigates to the given URL and waits for |num_navigations| to occur, and
@@ -55,12 +315,13 @@ class ErrorPageTest : public InProcessBrowserTest {
                                     int num_navigations) {
     content::TitleWatcher title_watcher(
         browser()->tab_strip_model()->GetActiveWebContents(),
-        ASCIIToUTF16(expected_title));
+        base::ASCIIToUTF16(expected_title));
 
     ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
         browser(), url, num_navigations);
 
-    EXPECT_EQ(ASCIIToUTF16(expected_title), title_watcher.WaitAndGetTitle());
+    EXPECT_EQ(base::ASCIIToUTF16(expected_title),
+              title_watcher.WaitAndGetTitle());
   }
 
   // Navigates back in the history and waits for |num_navigations| to occur, and
@@ -81,11 +342,83 @@ class ErrorPageTest : public InProcessBrowserTest {
                                    HISTORY_NAVIGATE_FORWARD);
   }
 
+  void GoBackAndWaitForNavigations(int num_navigations) {
+    NavigateHistory(num_navigations, HISTORY_NAVIGATE_BACK);
+  }
+
+  void GoForwardAndWaitForNavigations(int num_navigations) {
+    NavigateHistory(num_navigations, HISTORY_NAVIGATE_FORWARD);
+  }
+
+  // Confirms that the javascript variable indicating whether or not we have
+  // a stale copy in the cache has been set to |expected|, and that the
+  // stale load button is or isn't there based on the same expectation.
+  testing::AssertionResult ProbeStaleCopyValue(bool expected) {
+    const char* js_cache_probe =
+        "try {\n"
+        "    domAutomationController.send(\n"
+        "        'staleLoadButton' in templateData ? 'yes' : 'no');\n"
+        "} catch (e) {\n"
+        "    domAutomationController.send(e.message);\n"
+        "}\n";
+
+    std::string result;
+    bool ret =
+        content::ExecuteScriptAndExtractString(
+            browser()->tab_strip_model()->GetActiveWebContents(),
+            js_cache_probe,
+            &result);
+    if (!ret) {
+      return testing::AssertionFailure()
+          << "Failing return from ExecuteScriptAndExtractString.";
+    }
+
+    if ((expected && "yes" == result) || (!expected && "no" == result))
+      return testing::AssertionSuccess();
+
+    return testing::AssertionFailure() << "Cache probe result is " << result;
+  }
+
+  testing::AssertionResult ReloadStaleCopyFromCache() {
+    const char* js_reload_script =
+        "try {\n"
+        "    document.getElementById('stale-load-button').click();\n"
+        "    domAutomationController.send('success');\n"
+        "} catch (e) {\n"
+        "    domAutomationController.send(e.message);\n"
+        "}\n";
+
+    std::string result;
+    bool ret = content::ExecuteScriptAndExtractString(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        js_reload_script,
+        &result);
+    EXPECT_TRUE(ret);
+    if (!ret)
+      return testing::AssertionFailure();
+    return ("success" == result ? testing::AssertionSuccess() :
+            (testing::AssertionFailure() << "Exception message is " << result));
+  }
+
+  LinkDoctorInterceptor* link_doctor_interceptor() {
+    return link_doctor_interceptor_;
+  }
+
  protected:
   virtual void SetUpOnMainThread() OVERRIDE {
+    link_doctor_interceptor_ = new LinkDoctorInterceptor();
+    scoped_ptr<net::URLRequestInterceptor> owned_interceptor(
+        link_doctor_interceptor_);
+    // Ownership of the |interceptor_| is passed to an object the IO thread, but
+    // a pointer is kept in the test fixture.  As soon as anything calls
+    // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
-        base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
+        base::Bind(&InstallMockInterceptors,
+                   google_util::GetGoogleSearchURL(
+                       google_profile_helper::GetGoogleHomePageURL(
+                           browser()->profile())),
+                   base::Passed(&owned_interceptor)));
   }
 
   // Returns a GURL that results in a DNS error.
@@ -101,8 +434,16 @@ class ErrorPageTest : public InProcessBrowserTest {
                                       HistoryNavigationDirection direction) {
     content::TitleWatcher title_watcher(
         browser()->tab_strip_model()->GetActiveWebContents(),
-        ASCIIToUTF16(expected_title));
+        base::ASCIIToUTF16(expected_title));
+
+    NavigateHistory(num_navigations, direction);
 
+    EXPECT_EQ(title_watcher.WaitAndGetTitle(),
+              base::ASCIIToUTF16(expected_title));
+  }
+
+  void NavigateHistory(int num_navigations,
+                       HistoryNavigationDirection direction) {
     content::TestNavigationObserver test_navigation_observer(
         browser()->tab_strip_model()->GetActiveWebContents(),
         num_navigations);
@@ -114,11 +455,10 @@ class ErrorPageTest : public InProcessBrowserTest {
       FAIL();
     }
     test_navigation_observer.Wait();
-
-    EXPECT_EQ(title_watcher.WaitAndGetTitle(), ASCIIToUTF16(expected_title));
   }
-};
 
+  LinkDoctorInterceptor* link_doctor_interceptor_;
+};
 
 class TestFailProvisionalLoadObserver : public content::WebContentsObserver {
  public:
@@ -128,13 +468,10 @@ class TestFailProvisionalLoadObserver : public content::WebContentsObserver {
 
   // This method is invoked when the provisional load failed.
   virtual void DidFailProvisionalLoad(
-      int64 frame_id,
-      const string16& frame_unique_name,
-      bool is_main_frame,
+      content::RenderFrameHost* render_frame_host,
       const GURL& validated_url,
       int error_code,
-      const string16& error_description,
-      content::RenderViewHost* render_view_host) OVERRIDE {
+      const base::string16& error_description) OVERRIDE {
     fail_url_ = validated_url;
   }
 
@@ -146,97 +483,229 @@ class TestFailProvisionalLoadObserver : public content::WebContentsObserver {
   DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver);
 };
 
-// See crbug.com/109669
-#if defined(USE_AURA) || defined(OS_WIN)
-#define MAYBE_DNSError_Basic DISABLED_DNSError_Basic
-#else
-#define MAYBE_DNSError_Basic DNSError_Basic
-#endif
+void InterceptNetworkTransactions(net::URLRequestContextGetter* getter,
+                                  net::Error error) {
+  DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO));
+  net::HttpCache* cache(
+      getter->GetURLRequestContext()->http_transaction_factory()->GetCache());
+  DCHECK(cache);
+  scoped_ptr<net::HttpTransactionFactory> factory(
+      new net::FailingHttpTransactionFactory(cache->GetSession(), error));
+  // Throw away old version; since this is a a browser test, we don't
+  // need to restore the old state.
+  cache->SetHttpNetworkTransactionFactoryForTesting(factory.Pass());
+}
+
 // Test that a DNS error occuring in the main frame redirects to an error page.
-IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_DNSError_Basic) {
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_Basic) {
   // The first navigation should fail, and the second one should be the error
   // page.
-  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
 }
 
-// See crbug.com/109669
-#if defined(USE_AURA)
-#define MAYBE_DNSError_GoBack1 DISABLED_DNSError_GoBack1
-#else
-#define MAYBE_DNSError_GoBack1 DNSError_GoBack1
-#endif
-
 // Test that a DNS error occuring in the main frame does not result in an
 // additional session history entry.
-IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_DNSError_GoBack1) {
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack1) {
   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
-  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
 }
 
-// See crbug.com/109669
-#if defined(USE_AURA)
-#define MAYBE_DNSError_GoBack2 DISABLED_DNSError_GoBack2
-#else
-#define MAYBE_DNSError_GoBack2 DNSError_GoBack2
-#endif
 // Test that a DNS error occuring in the main frame does not result in an
 // additional session history entry.
 IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2) {
   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
 
-  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
+
   NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
 
-  GoBackAndWaitForTitle("Mock Link Doctor", 2);
+  GoBackAndWaitForNavigations(2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
+
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
+  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
 }
 
-// See crbug.com/109669
-#if defined(USE_AURA)
-#define MAYBE_DNSError_GoBack2AndForward DISABLED_DNSError_GoBack2AndForward
-#else
-#define MAYBE_DNSError_GoBack2AndForward DNSError_GoBack2AndForward
-#endif
 // Test that a DNS error occuring in the main frame does not result in an
 // additional session history entry.
 IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2AndForward) {
   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
 
-  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
+
   NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
 
-  GoBackAndWaitForTitle("Mock Link Doctor", 2);
+  GoBackAndWaitForNavigations(2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
+
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
 
-  GoForwardAndWaitForTitle("Mock Link Doctor", 2);
+  GoForwardAndWaitForNavigations(2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
 }
 
-// See crbug.com/109669
-#if defined(USE_AURA)
-#define MAYBE_DNSError_GoBack2Forward2 DISABLED_DNSError_GoBack2Forward2
-#else
-#define MAYBE_DNSError_GoBack2Forward2 DNSError_GoBack2Forward2
-#endif
 // Test that a DNS error occuring in the main frame does not result in an
 // additional session history entry.
 IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2Forward2) {
   NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
 
-  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
+
   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
 
-  GoBackAndWaitForTitle("Mock Link Doctor", 2);
+  GoBackAndWaitForNavigations(2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
+
   GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
 
-  GoForwardAndWaitForTitle("Mock Link Doctor", 2);
+  GoForwardAndWaitForNavigations(2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
+
   GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
+  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
+}
+
+// Test that the search button on a DNS error page works.
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_DoSearch) {
+  // The first navigation should fail, and the second one should be the error
+  // page.
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Do a search and make sure the browser ends up at the right page.
+  content::TestNavigationObserver nav_observer(web_contents, 1);
+  content::TitleWatcher title_watcher(
+      web_contents,
+      base::ASCIIToUTF16("Title Of More Awesomeness"));
+  // Can't use content::ExecuteScript because it waits for scripts to send
+  // notification that they've run, and scripts that trigger a navigation may
+  // not send that notification.
+  web_contents->GetMainFrame()->ExecuteJavaScript(
+      base::ASCIIToUTF16("document.getElementById('search-button').click();"));
+  nav_observer.Wait();
+  EXPECT_EQ(base::ASCIIToUTF16("Title Of More Awesomeness"),
+            title_watcher.WaitAndGetTitle());
+
+  // There should have been another Link Doctor request, for tracking purposes.
+  // Have to wait for it, since the search page does not depend on having
+  // sent the tracking request.
+  link_doctor_interceptor()->WaitForRequests(2);
+  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
+
+  // Check the path and query string.
+  std::string url;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+                  browser()->tab_strip_model()->GetActiveWebContents(),
+                  "domAutomationController.send(window.location.href);",
+                  &url));
+  EXPECT_EQ("/search", GURL(url).path());
+  EXPECT_EQ("q=search%20query", GURL(url).query());
+
+  // Go back to the error page, to make sure the history is correct.
+  GoBackAndWaitForNavigations(2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
+}
+
+// Test that the reload button on a DNS error page works.
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_DoReload) {
+  // The first navigation should fail, and the second one should be the error
+  // page.
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Clicking the reload button should load the error page again, and there
+  // should be two commits, as before.
+  content::TestNavigationObserver nav_observer(web_contents, 2);
+  // Can't use content::ExecuteScript because it waits for scripts to send
+  // notification that they've run, and scripts that trigger a navigation may
+  // not send that notification.
+  web_contents->GetMainFrame()->ExecuteJavaScript(
+      base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
+  nav_observer.Wait();
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+
+  // There should have two more requests to the correction service:  One for the
+  // new error page, and one for tracking purposes.  Have to make sure to wait
+  // for the tracking request, since the new error page does not depend on it.
+  link_doctor_interceptor()->WaitForRequests(3);
+  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
+}
+
+// Test that clicking links on a DNS error page works.
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_DoClickLink) {
+  // The first navigation should fail, and the second one should be the error
+  // page.
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+       browser(), GetDnsErrorURL(), 2);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Simulate a click on a link.
+
+  content::TitleWatcher title_watcher(
+      web_contents,
+      base::ASCIIToUTF16("Title Of Awesomeness"));
+  std::string link_selector =
+      "document.querySelector('a[href=\"http://mock.http/title2.html\"]')";
+  // The tracking request is triggered by onmousedown, so it catches middle
+  // mouse button clicks, as well as left clicks.
+  web_contents->GetMainFrame()->ExecuteJavaScript(
+      base::ASCIIToUTF16(link_selector + ".onmousedown();"));
+  // Can't use content::ExecuteScript because it waits for scripts to send
+  // notification that they've run, and scripts that trigger a navigation may
+  // not send that notification.
+  web_contents->GetMainFrame()->ExecuteJavaScript(
+      base::ASCIIToUTF16(link_selector + ".click();"));
+  EXPECT_EQ(base::ASCIIToUTF16("Title Of Awesomeness"),
+            title_watcher.WaitAndGetTitle());
+
+  // There should have been a tracking request to the correction service.  Have
+  // to make sure to wait the tracking request, since the new page does not
+  // depend on it.
+  link_doctor_interceptor()->WaitForRequests(2);
+  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
 }
 
-// Test that a DNS error occuring in an iframe.
+// Test that a DNS error occuring in an iframe does not result in showing
+// navigation corrections.
 IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_Basic) {
   NavigateToURLAndWaitForTitle(
-      content::URLRequestMockHTTPJob::GetMockUrl(
+      net::URLRequestMockHTTPJob::GetMockUrl(
           base::FilePath(FILE_PATH_LITERAL("iframe_dns_error.html"))),
       "Blah",
       1);
@@ -245,6 +714,7 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_Basic) {
   EXPECT_EQ(2,
       browser()->tab_strip_model()->GetActiveWebContents()->
           GetController().GetEntryCount());
+  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
 }
 
 // This test fails regularly on win_rel trybots. See crbug.com/121540
@@ -259,10 +729,13 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_IFrameDNSError_GoBack) {
   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
   NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
+  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
 }
 
 // This test fails regularly on win_rel trybots. See crbug.com/121540
-#if defined(OS_WIN)
+//
+// This fails on linux_aura bringup: http://crbug.com/163931
+#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
 #define MAYBE_IFrameDNSError_GoBackAndForward DISABLED_IFrameDNSError_GoBackAndForward
 #else
 #define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
@@ -274,6 +747,7 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_IFrameDNSError_GoBackAndForward) {
   NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
   GoForwardAndWaitForTitle("Blah", 1);
+  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
 }
 
 // Test that a DNS error occuring in an iframe, once the main document is
@@ -301,8 +775,7 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_JavaScript) {
     content::WindowedNotificationObserver load_observer(
         content::NOTIFICATION_LOAD_STOP,
         content::Source<NavigationController>(&wc->GetController()));
-    wc->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
-        string16(), ASCIIToUTF16(script));
+    wc->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
     load_observer.Wait();
 
     // Ensure we saw the expected failure.
@@ -322,8 +795,7 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_JavaScript) {
     content::WindowedNotificationObserver load_observer(
         content::NOTIFICATION_LOAD_STOP,
         content::Source<NavigationController>(&wc->GetController()));
-    wc->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
-        string16(), ASCIIToUTF16(script));
+    wc->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
     load_observer.Wait();
   }
 
@@ -334,44 +806,140 @@ IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_JavaScript) {
     content::WindowedNotificationObserver load_observer(
         content::NOTIFICATION_LOAD_STOP,
         content::Source<NavigationController>(&wc->GetController()));
-    wc->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
-        string16(), ASCIIToUTF16(script));
+    wc->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
     load_observer.Wait();
 
     EXPECT_EQ(fail_url, fail_observer.fail_url());
     EXPECT_EQ(2, wc->GetController().GetEntryCount());
   }
+  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
 }
 
-// Checks that the Link Doctor is not loaded when we receive an actual 404 page.
+// Checks that navigation corrections are not loaded when we receive an actual
+// 404 page.
 IN_PROC_BROWSER_TEST_F(ErrorPageTest, Page404) {
   NavigateToURLAndWaitForTitle(
-      content::URLRequestMockHTTPJob::GetMockUrl(
+      net::URLRequestMockHTTPJob::GetMockUrl(
           base::FilePath(FILE_PATH_LITERAL("page404.html"))),
       "SUCCESS",
       1);
+  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
 }
 
-// Returns Javascript code that executes plain text search for the page.
-// Pass into content::ExecuteScriptAndExtractBool as |script| parameter.
-std::string GetTextContentContainsStringScript(
-    const std::string& value_to_search) {
-  return base::StringPrintf(
-      "var textContent = document.body.textContent;"
-      "var hasError = textContent.indexOf('%s') >= 0;"
-      "domAutomationController.send(hasError);",
-      value_to_search.c_str());
+// Checks that when an error occurs, the stale cache status of the page
+// is correctly transferred, and that stale cached copied can be loaded
+// from the javascript.
+IN_PROC_BROWSER_TEST_F(ErrorPageTest, StaleCacheStatus) {
+  ASSERT_TRUE(test_server()->Start());
+  // Load cache with entry with "nocache" set, to create stale
+  // cache.
+  GURL test_url(test_server()->GetURL("files/nocache.html"));
+  NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1);
+
+  // Reload same URL after forcing an error from the the network layer;
+  // confirm that the error page is told the cached copy exists.
+  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
+      browser()->profile()->GetRequestContext();
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&InterceptNetworkTransactions, url_request_context_getter,
+                 net::ERR_FAILED));
+
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+      // With no navigation corrections to load, there's only one navigation.
+      browser(), test_url, 1);
+  EXPECT_TRUE(ProbeStaleCopyValue(true));
+  EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
+  EXPECT_NE(base::ASCIIToUTF16("Nocache Test Page"),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+
+  // Confirm that loading the stale copy from the cache works.
+  content::TestNavigationObserver same_tab_observer(
+      browser()->tab_strip_model()->GetActiveWebContents(), 1);
+  ASSERT_TRUE(ReloadStaleCopyFromCache());
+  same_tab_observer.Wait();
+  EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+
+  // Clear the cache and reload the same URL; confirm the error page is told
+  // that there is no cached copy.
+  BrowsingDataRemover* remover =
+      BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
+  remover->Remove(BrowsingDataRemover::REMOVE_CACHE,
+                  BrowsingDataHelper::UNPROTECTED_WEB);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+      browser(), test_url, 1);
+  EXPECT_TRUE(ProbeStaleCopyValue(false));
+  EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
+  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
+}
+
+class ErrorPageAutoReloadTest : public InProcessBrowserTest {
+ public:
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    command_line->AppendSwitch(switches::kEnableOfflineAutoReload);
+  }
+
+  void InstallInterceptor(const GURL& url, int requests_to_fail) {
+    interceptor_ = new FailFirstNRequestsInterceptor(requests_to_fail);
+    scoped_ptr<net::URLRequestInterceptor> owned_interceptor(interceptor_);
+
+    // Tests don't need to wait for this task to complete before using the
+    // filter; any requests that might be affected by it will end up in the IO
+    // thread's message loop after this posted task anyway.
+    //
+    // Ownership of the interceptor is passed to an object the IO thread, but a
+    // pointer is kept in the test fixture.  As soon as anything calls
+    // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&AddInterceptorForURL, url,
+                   base::Passed(&owned_interceptor)));
+  }
+
+  void NavigateToURLAndWaitForTitle(const GURL& url,
+                                    const std::string& expected_title,
+                                    int num_navigations) {
+    content::TitleWatcher title_watcher(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        base::ASCIIToUTF16(expected_title));
+
+    ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+        browser(), url, num_navigations);
+
+    EXPECT_EQ(base::ASCIIToUTF16(expected_title),
+              title_watcher.WaitAndGetTitle());
+  }
+
+  FailFirstNRequestsInterceptor* interceptor() {
+    return interceptor_;
+  }
+
+ private:
+  FailFirstNRequestsInterceptor* interceptor_;
+};
+
+IN_PROC_BROWSER_TEST_F(ErrorPageAutoReloadTest, AutoReload) {
+  GURL test_url("http://error.page.auto.reload");
+  const int kRequestsToFail = 2;
+  InstallInterceptor(test_url, kRequestsToFail);
+  NavigateToURLAndWaitForTitle(test_url, "Test One", kRequestsToFail + 1);
+  // Note that the interceptor updates these variables on the IO thread,
+  // but this function reads them on the main thread. The requests have to be
+  // created (on the IO thread) before NavigateToURLAndWaitForTitle returns or
+  // this becomes racey.
+  EXPECT_EQ(kRequestsToFail, interceptor()->failures());
+  EXPECT_EQ(kRequestsToFail + 1, interceptor()->requests());
 }
 
-// Protocol handler that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
-class AddressUnreachableProtocolHandler
-    : public net::URLRequestJobFactory::ProtocolHandler {
+// Interceptor that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
+class AddressUnreachableInterceptor : public net::URLRequestInterceptor {
  public:
-  AddressUnreachableProtocolHandler() {}
-  virtual ~AddressUnreachableProtocolHandler() {}
+  AddressUnreachableInterceptor() {}
+  virtual ~AddressUnreachableInterceptor() {}
 
-  // net::URLRequestJobFactory::ProtocolHandler:
-  virtual net::URLRequestJob* MaybeCreateJob(
+  // net::URLRequestInterceptor:
+  virtual net::URLRequestJob* MaybeInterceptRequest(
       net::URLRequest* request,
       net::NetworkDelegate* network_delegate) const OVERRIDE {
     return new URLRequestFailedJob(request,
@@ -380,43 +948,41 @@ class AddressUnreachableProtocolHandler
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(AddressUnreachableProtocolHandler);
+  DISALLOW_COPY_AND_ASSIGN(AddressUnreachableInterceptor);
 };
 
-// A test fixture that returns ERR_ADDRESS_UNREACHABLE for all Link Doctor
-// requests.  ERR_NAME_NOT_RESOLVED is more typical, but need to use a different
-// error for the Link Doctor and the original page to validate the right page
-// is being displayed.
-class ErrorPageLinkDoctorFailTest : public InProcessBrowserTest {
+// A test fixture that returns ERR_ADDRESS_UNREACHABLE for all navigation
+// correction requests.  ERR_NAME_NOT_RESOLVED is more typical, but need to use
+// a different error for the correction service and the original page to
+// validate the right page is being displayed.
+class ErrorPageNavigationCorrectionsFailTest : public ErrorPageTest {
  public:
   // InProcessBrowserTest:
   virtual void SetUpOnMainThread() OVERRIDE {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
-        base::Bind(&ErrorPageLinkDoctorFailTest::AddFilters));
+        base::Bind(&ErrorPageNavigationCorrectionsFailTest::AddFilters));
   }
 
-  virtual void CleanUpOnMainThread() OVERRIDE {
+  virtual void TearDownOnMainThread() OVERRIDE {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
-        base::Bind(&ErrorPageLinkDoctorFailTest::RemoveFilters));
+        base::Bind(&ErrorPageNavigationCorrectionsFailTest::RemoveFilters));
   }
 
  private:
-  // Adds a filter that causes all requests for the Link Doctor's scheme and
-  // host to fail with ERR_ADDRESS_UNREACHABLE.  Since the Link Doctor adds
-  // query strings, it's not enough to just fail exact matches.
+  // Adds a filter that causes all correction service requests to fail with
+  // ERR_ADDRESS_UNREACHABLE.
   //
-  // Also adds the content::URLRequestFailedJob filter.
+  // Also adds the net::URLRequestFailedJob filter.
   static void AddFilters() {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    content::URLRequestFailedJob::AddUrlHandler();
+    URLRequestFailedJob::AddUrlHandler();
 
-    net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
-        google_util::LinkDoctorBaseURL().scheme(),
-        google_util::LinkDoctorBaseURL().host(),
-        scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
-            new AddressUnreachableProtocolHandler()));
+    net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
+        google_util::LinkDoctorBaseURL(),
+        scoped_ptr<net::URLRequestInterceptor>(
+            new AddressUnreachableInterceptor()));
   }
 
   static void RemoveFilters() {
@@ -425,22 +991,63 @@ class ErrorPageLinkDoctorFailTest : public InProcessBrowserTest {
   }
 };
 
-// Make sure that when the Link Doctor fails to load, the network error page is
+// Make sure that when corrections fail to load, the network error page is
 // successfully loaded.
-IN_PROC_BROWSER_TEST_F(ErrorPageLinkDoctorFailTest, LinkDoctorFail) {
+IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest,
+                       FetchCorrectionsFails) {
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
       browser(),
       URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED),
       2);
 
-  // Verify that the expected error page is being displayed.  Do this by making
-  // sure the original error code (ERR_NAME_NOT_RESOLVED) is displayed.
-  bool result = false;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      GetTextContentContainsStringScript("ERR_NAME_NOT_RESOLVED"),
-      &result));
-  EXPECT_TRUE(result);
+  // Verify that the expected error page is being displayed.
+  ExpectDisplayingLocalErrorPage(browser(), net::ERR_NAME_NOT_RESOLVED);
+}
+
+// Checks that when an error occurs and a corrections fail to load, the stale
+// cache status of the page is correctly transferred, and we can load the
+// stale copy from the javascript.  Most logic copied from StaleCacheStatus
+// above.
+IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest,
+                       StaleCacheStatusFailedCorrections) {
+  ASSERT_TRUE(test_server()->Start());
+  // Load cache with entry with "nocache" set, to create stale
+  // cache.
+  GURL test_url(test_server()->GetURL("files/nocache.html"));
+  NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1);
+
+  // Reload same URL after forcing an error from the the network layer;
+  // confirm that the error page is told the cached copy exists.
+  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
+      browser()->profile()->GetRequestContext();
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&InterceptNetworkTransactions, url_request_context_getter,
+                 net::ERR_CONNECTION_FAILED));
+
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+      browser(), test_url, 2);
+  EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
+  EXPECT_TRUE(ProbeStaleCopyValue(true));
+
+  // Confirm that loading the stale copy from the cache works.
+  content::TestNavigationObserver same_tab_observer(
+      browser()->tab_strip_model()->GetActiveWebContents(), 1);
+  ASSERT_TRUE(ReloadStaleCopyFromCache());
+  same_tab_observer.Wait();
+  EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+
+  // Clear the cache and reload the same URL; confirm the error page is told
+  // that there is no cached copy.
+  BrowsingDataRemover* remover =
+      BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
+  remover->Remove(BrowsingDataRemover::REMOVE_CACHE,
+                  BrowsingDataHelper::UNPROTECTED_WEB);
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+      browser(), test_url, 2);
+  EXPECT_TRUE(ProbeStaleCopyValue(false));
+  EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
 }
 
 // A test fixture that simulates failing requests for an IDN domain name.
@@ -460,7 +1067,7 @@ class ErrorPageForIDNTest : public InProcessBrowserTest {
         base::Bind(&ErrorPageForIDNTest::AddFilters));
   }
 
-  virtual void CleanUpOnMainThread() OVERRIDE {
+  virtual void TearDownOnMainThread() OVERRIDE {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&ErrorPageForIDNTest::RemoveFilters));
@@ -469,7 +1076,7 @@ class ErrorPageForIDNTest : public InProcessBrowserTest {
  private:
   static void AddFilters() {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    content::URLRequestFailedJob::AddUrlHandlerForHostname(kHostname);
+    URLRequestFailedJob::AddUrlHandlerForHostname(kHostname);
   }
 
   static void RemoveFilters() {
@@ -486,19 +1093,15 @@ const char ErrorPageForIDNTest::kHostnameJSUnicode[] =
 
 // Make sure error page shows correct unicode for IDN.
 IN_PROC_BROWSER_TEST_F(ErrorPageForIDNTest, IDN) {
-  // ERR_UNSAFE_PORT will not trigger the link doctor.
+  // ERR_UNSAFE_PORT will not trigger navigation corrections.
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
       browser(),
       URLRequestFailedJob::GetMockHttpUrlForHostname(net::ERR_UNSAFE_PORT,
                                                      kHostname),
       1);
 
-  bool result = false;
-  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      GetTextContentContainsStringScript(kHostnameJSUnicode),
-      &result));
-  EXPECT_TRUE(result);
+  ToggleHelpBox(browser());
+  EXPECT_TRUE(IsDisplayingText(browser(), kHostnameJSUnicode));
 }
 
 }  // namespace