1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "base/command_line.h"
7#include "base/compiler_specific.h"
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/memory/weak_ptr.h"
11#include "base/path_service.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/stringprintf.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/synchronization/lock.h"
16#include "chrome/browser/browsing_data/browsing_data_helper.h"
17#include "chrome/browser/browsing_data/browsing_data_remover.h"
18#include "chrome/browser/google/google_profile_helper.h"
19#include "chrome/browser/net/url_request_mock_util.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/ui/browser.h"
22#include "chrome/browser/ui/browser_commands.h"
23#include "chrome/browser/ui/tabs/tab_strip_model.h"
24#include "chrome/common/chrome_paths.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/common/pref_names.h"
27#include "chrome/grit/generated_resources.h"
28#include "chrome/test/base/in_process_browser_test.h"
29#include "chrome/test/base/ui_test_utils.h"
30#include "components/google/core/browser/google_util.h"
31#include "content/public/browser/browser_thread.h"
32#include "content/public/browser/notification_service.h"
33#include "content/public/browser/notification_types.h"
34#include "content/public/browser/render_frame_host.h"
35#include "content/public/browser/render_view_host.h"
36#include "content/public/browser/web_contents.h"
37#include "content/public/browser/web_contents_observer.h"
38#include "content/public/test/browser_test_utils.h"
39#include "content/public/test/test_navigation_observer.h"
40#include "net/base/net_errors.h"
41#include "net/base/net_util.h"
42#include "net/http/failing_http_transaction_factory.h"
43#include "net/http/http_cache.h"
44#include "net/test/spawned_test_server/spawned_test_server.h"
45#include "net/test/url_request/url_request_failed_job.h"
46#include "net/test/url_request/url_request_mock_http_job.h"
47#include "net/url_request/url_request_context.h"
48#include "net/url_request/url_request_context_getter.h"
49#include "net/url_request/url_request_filter.h"
50#include "net/url_request/url_request_interceptor.h"
51#include "net/url_request/url_request_job.h"
52#include "net/url_request/url_request_test_job.h"
53#include "net/url_request/url_request_test_util.h"
54#include "ui/base/l10n/l10n_util.h"
55
56using content::BrowserThread;
57using content::NavigationController;
58using net::URLRequestFailedJob;
59using net::URLRequestTestJob;
60
61namespace {
62
63// Returns true if |text| is displayed on the page |browser| is currently
64// displaying.  Uses "innerText", so will miss hidden text, and whitespace
65// space handling may be weird.
66bool WARN_UNUSED_RESULT IsDisplayingText(Browser* browser,
67                                         const std::string& text) {
68  std::string command = base::StringPrintf(
69      "var textContent = document.body.innerText;"
70      "var hasText = textContent.indexOf('%s') >= 0;"
71      "domAutomationController.send(hasText);",
72      text.c_str());
73  bool result = false;
74  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
75      browser->tab_strip_model()->GetActiveWebContents(), command, &result));
76  return result;
77}
78
79// Expands the more box on the currently displayed error page.
80void ToggleHelpBox(Browser* browser) {
81  EXPECT_TRUE(content::ExecuteScript(
82      browser->tab_strip_model()->GetActiveWebContents(),
83      "document.getElementById('details-button').click();"));
84}
85
86// Returns true if |browser| is displaying the text representation of
87// |error_code| on the current page.
88bool WARN_UNUSED_RESULT IsDisplayingNetError(Browser* browser,
89                                             net::Error error_code) {
90  return IsDisplayingText(browser, net::ErrorToShortString(error_code));
91}
92
93// Checks that the local error page is being displayed, without remotely
94// retrieved navigation corrections, and with the specified error code.
95void ExpectDisplayingLocalErrorPage(Browser* browser, net::Error error_code) {
96  // Expand the help box so innerText will include text below the fold.
97  ToggleHelpBox(browser);
98
99  EXPECT_TRUE(IsDisplayingNetError(browser, error_code));
100
101  // Locally generated error pages should not have navigation corrections.
102  EXPECT_FALSE(IsDisplayingText(browser, "http://correction1/"));
103  EXPECT_FALSE(IsDisplayingText(browser, "http://correction2/"));
104
105  // Locally generated error pages should not have a populated search box.
106  bool search_box_populated = false;
107  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
108      browser->tab_strip_model()->GetActiveWebContents(),
109      "var searchText = document.getElementById('search-box').value;"
110          "domAutomationController.send(searchText == 'search query');",
111      &search_box_populated));
112  EXPECT_FALSE(search_box_populated);
113}
114
115// Checks that an error page with information retrieved from the navigation
116// correction service is being displayed, with the specified specified error
117// code.
118void ExpectDisplayingNavigationCorrections(Browser* browser,
119                                           net::Error error_code) {
120  // Expand the help box so innerText will include text below the fold.
121  ToggleHelpBox(browser);
122
123  EXPECT_TRUE(IsDisplayingNetError(browser, error_code));
124
125  // Check that the mock navigation corrections are displayed.
126  EXPECT_TRUE(IsDisplayingText(browser, "http://correction1/"));
127  EXPECT_TRUE(IsDisplayingText(browser, "http://correction2/"));
128
129  // Check that the search box is populated correctly.
130  bool search_box_populated = false;
131  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
132      browser->tab_strip_model()->GetActiveWebContents(),
133      "var searchText = document.getElementById('search-box').value;"
134          "domAutomationController.send(searchText == 'search query');",
135      &search_box_populated));
136  EXPECT_TRUE(search_box_populated);
137}
138
139std::string GetLoadStaleButtonLabel() {
140  return l10n_util::GetStringUTF8(IDS_ERRORPAGES_BUTTON_LOAD_STALE);
141}
142
143void AddInterceptorForURL(
144    const GURL& url,
145    scoped_ptr<net::URLRequestInterceptor> handler) {
146  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
147  net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
148      url, handler.Pass());
149}
150
151// An interceptor that fails a configurable number of requests, then succeeds
152// all requests after that, keeping count of failures and successes.
153class FailFirstNRequestsInterceptor : public net::URLRequestInterceptor {
154 public:
155  explicit FailFirstNRequestsInterceptor(int requests_to_fail)
156      : requests_(0), failures_(0), requests_to_fail_(requests_to_fail) {}
157  virtual ~FailFirstNRequestsInterceptor() {}
158
159  // net::URLRequestInterceptor implementation
160  virtual net::URLRequestJob* MaybeInterceptRequest(
161      net::URLRequest* request,
162      net::NetworkDelegate* network_delegate) const OVERRIDE {
163    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
164    requests_++;
165    if (failures_ < requests_to_fail_) {
166      failures_++;
167      // Note: net::ERR_CONNECTION_RESET does not summon the Link Doctor; see
168      // NetErrorHelperCore::GetErrorPageURL.
169      return new URLRequestFailedJob(request,
170                                     network_delegate,
171                                     net::ERR_CONNECTION_RESET);
172    } else {
173      return new URLRequestTestJob(request, network_delegate,
174                                   URLRequestTestJob::test_headers(),
175                                   URLRequestTestJob::test_data_1(),
176                                   true);
177    }
178  }
179
180  int requests() const { return requests_; }
181  int failures() const { return failures_; }
182
183 private:
184  // These are mutable because MaybeCreateJob is const but we want this state
185  // for testing.
186  mutable int requests_;
187  mutable int failures_;
188  int requests_to_fail_;
189
190  DISALLOW_COPY_AND_ASSIGN(FailFirstNRequestsInterceptor);
191};
192
193// An interceptor that serves LinkDoctor responses.  It also allows waiting
194// until a certain number of requests have been sent.
195// TODO(mmenke):  Wait until responses have been received instead.
196class LinkDoctorInterceptor : public net::URLRequestInterceptor {
197 public:
198  LinkDoctorInterceptor() : num_requests_(0),
199                            requests_to_wait_for_(-1),
200                            weak_factory_(this) {
201  }
202
203  virtual ~LinkDoctorInterceptor() {}
204
205  // net::URLRequestInterceptor implementation
206  virtual net::URLRequestJob* MaybeInterceptRequest(
207      net::URLRequest* request,
208      net::NetworkDelegate* network_delegate) const OVERRIDE {
209    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
210
211    BrowserThread::PostTask(
212        BrowserThread::UI, FROM_HERE,
213        base::Bind(&LinkDoctorInterceptor::RequestCreated,
214                   weak_factory_.GetWeakPtr()));
215
216    base::FilePath root_http;
217    PathService::Get(chrome::DIR_TEST_DATA, &root_http);
218    return new net::URLRequestMockHTTPJob(
219        request,
220        network_delegate,
221        root_http.AppendASCII("mock-link-doctor.json"),
222        BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
223            base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
224  }
225
226  void WaitForRequests(int requests_to_wait_for) {
227    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
228    DCHECK_EQ(-1, requests_to_wait_for_);
229    DCHECK(!run_loop_);
230
231    if (requests_to_wait_for >= num_requests_)
232      return;
233
234    requests_to_wait_for_ = requests_to_wait_for;
235    run_loop_.reset(new base::RunLoop());
236    run_loop_->Run();
237    run_loop_.reset();
238    requests_to_wait_for_ = -1;
239    EXPECT_EQ(num_requests_, requests_to_wait_for);
240  }
241
242  // It is up to the caller to wait until all relevant requests has been
243  // created, either through calling WaitForRequests or some other manner,
244  // before calling this method.
245  int num_requests() const {
246    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247    return num_requests_;
248  }
249
250 private:
251  void RequestCreated() {
252    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253
254    num_requests_++;
255    if (num_requests_ == requests_to_wait_for_)
256      run_loop_->Quit();
257  }
258
259  // These are only used on the UI thread.
260  int num_requests_;
261  int requests_to_wait_for_;
262  scoped_ptr<base::RunLoop> run_loop_;
263
264  // This prevents any risk of flake if any test doesn't wait for a request
265  // it sent.  Mutable so it can be accessed from a const function.
266  mutable base::WeakPtrFactory<LinkDoctorInterceptor> weak_factory_;
267
268  DISALLOW_COPY_AND_ASSIGN(LinkDoctorInterceptor);
269};
270
271void InstallMockInterceptors(
272    const GURL& search_url,
273    scoped_ptr<net::URLRequestInterceptor> link_doctor_interceptor) {
274  chrome_browser_net::SetUrlRequestMocksEnabled(true);
275
276  AddInterceptorForURL(google_util::LinkDoctorBaseURL(),
277                       link_doctor_interceptor.Pass());
278
279  // Add a mock for the search engine the error page will use.
280  base::FilePath root_http;
281  PathService::Get(chrome::DIR_TEST_DATA, &root_http);
282  net::URLRequestMockHTTPJob::AddHostnameToFileHandler(
283      search_url.host(),
284      root_http.AppendASCII("title3.html"),
285      BrowserThread::GetBlockingPool());
286}
287
288class ErrorPageTest : public InProcessBrowserTest {
289 public:
290  enum HistoryNavigationDirection {
291    HISTORY_NAVIGATE_BACK,
292    HISTORY_NAVIGATE_FORWARD,
293  };
294
295  ErrorPageTest() : link_doctor_interceptor_(NULL) {}
296  virtual ~ErrorPageTest() {}
297
298  // Navigates the active tab to a mock url created for the file at |file_path|.
299  // Needed for StaleCacheStatus and StaleCacheStatusFailedCorrections tests.
300  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
301    command_line->AppendSwitch(switches::kEnableOfflineLoadStaleCache);
302  }
303
304  // Navigates the active tab to a mock url created for the file at |file_path|.
305  void NavigateToFileURL(const base::FilePath::StringType& file_path) {
306    ui_test_utils::NavigateToURL(
307        browser(),
308        net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path)));
309  }
310
311  // Navigates to the given URL and waits for |num_navigations| to occur, and
312  // the title to change to |expected_title|.
313  void NavigateToURLAndWaitForTitle(const GURL& url,
314                                    const std::string& expected_title,
315                                    int num_navigations) {
316    content::TitleWatcher title_watcher(
317        browser()->tab_strip_model()->GetActiveWebContents(),
318        base::ASCIIToUTF16(expected_title));
319
320    ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
321        browser(), url, num_navigations);
322
323    EXPECT_EQ(base::ASCIIToUTF16(expected_title),
324              title_watcher.WaitAndGetTitle());
325  }
326
327  // Navigates back in the history and waits for |num_navigations| to occur, and
328  // the title to change to |expected_title|.
329  void GoBackAndWaitForTitle(const std::string& expected_title,
330                             int num_navigations) {
331    NavigateHistoryAndWaitForTitle(expected_title,
332                                   num_navigations,
333                                   HISTORY_NAVIGATE_BACK);
334  }
335
336  // Navigates forward in the history and waits for |num_navigations| to occur,
337  // and the title to change to |expected_title|.
338  void GoForwardAndWaitForTitle(const std::string& expected_title,
339                                int num_navigations) {
340    NavigateHistoryAndWaitForTitle(expected_title,
341                                   num_navigations,
342                                   HISTORY_NAVIGATE_FORWARD);
343  }
344
345  void GoBackAndWaitForNavigations(int num_navigations) {
346    NavigateHistory(num_navigations, HISTORY_NAVIGATE_BACK);
347  }
348
349  void GoForwardAndWaitForNavigations(int num_navigations) {
350    NavigateHistory(num_navigations, HISTORY_NAVIGATE_FORWARD);
351  }
352
353  // Confirms that the javascript variable indicating whether or not we have
354  // a stale copy in the cache has been set to |expected|, and that the
355  // stale load button is or isn't there based on the same expectation.
356  testing::AssertionResult ProbeStaleCopyValue(bool expected) {
357    const char* js_cache_probe =
358        "try {\n"
359        "    domAutomationController.send(\n"
360        "        'staleLoadButton' in templateData ? 'yes' : 'no');\n"
361        "} catch (e) {\n"
362        "    domAutomationController.send(e.message);\n"
363        "}\n";
364
365    std::string result;
366    bool ret =
367        content::ExecuteScriptAndExtractString(
368            browser()->tab_strip_model()->GetActiveWebContents(),
369            js_cache_probe,
370            &result);
371    if (!ret) {
372      return testing::AssertionFailure()
373          << "Failing return from ExecuteScriptAndExtractString.";
374    }
375
376    if ((expected && "yes" == result) || (!expected && "no" == result))
377      return testing::AssertionSuccess();
378
379    return testing::AssertionFailure() << "Cache probe result is " << result;
380  }
381
382  testing::AssertionResult ReloadStaleCopyFromCache() {
383    const char* js_reload_script =
384        "try {\n"
385        "    document.getElementById('stale-load-button').click();\n"
386        "    domAutomationController.send('success');\n"
387        "} catch (e) {\n"
388        "    domAutomationController.send(e.message);\n"
389        "}\n";
390
391    std::string result;
392    bool ret = content::ExecuteScriptAndExtractString(
393        browser()->tab_strip_model()->GetActiveWebContents(),
394        js_reload_script,
395        &result);
396    EXPECT_TRUE(ret);
397    if (!ret)
398      return testing::AssertionFailure();
399    return ("success" == result ? testing::AssertionSuccess() :
400            (testing::AssertionFailure() << "Exception message is " << result));
401  }
402
403  LinkDoctorInterceptor* link_doctor_interceptor() {
404    return link_doctor_interceptor_;
405  }
406
407 protected:
408  virtual void SetUpOnMainThread() OVERRIDE {
409    link_doctor_interceptor_ = new LinkDoctorInterceptor();
410    scoped_ptr<net::URLRequestInterceptor> owned_interceptor(
411        link_doctor_interceptor_);
412    // Ownership of the |interceptor_| is passed to an object the IO thread, but
413    // a pointer is kept in the test fixture.  As soon as anything calls
414    // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
415    BrowserThread::PostTask(
416        BrowserThread::IO, FROM_HERE,
417        base::Bind(&InstallMockInterceptors,
418                   google_util::GetGoogleSearchURL(
419                       google_profile_helper::GetGoogleHomePageURL(
420                           browser()->profile())),
421                   base::Passed(&owned_interceptor)));
422  }
423
424  // Returns a GURL that results in a DNS error.
425  GURL GetDnsErrorURL() const {
426    return URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
427  }
428
429 private:
430  // Navigates the browser the indicated direction in the history and waits for
431  // |num_navigations| to occur and the title to change to |expected_title|.
432  void NavigateHistoryAndWaitForTitle(const std::string& expected_title,
433                                      int num_navigations,
434                                      HistoryNavigationDirection direction) {
435    content::TitleWatcher title_watcher(
436        browser()->tab_strip_model()->GetActiveWebContents(),
437        base::ASCIIToUTF16(expected_title));
438
439    NavigateHistory(num_navigations, direction);
440
441    EXPECT_EQ(title_watcher.WaitAndGetTitle(),
442              base::ASCIIToUTF16(expected_title));
443  }
444
445  void NavigateHistory(int num_navigations,
446                       HistoryNavigationDirection direction) {
447    content::TestNavigationObserver test_navigation_observer(
448        browser()->tab_strip_model()->GetActiveWebContents(),
449        num_navigations);
450    if (direction == HISTORY_NAVIGATE_BACK) {
451      chrome::GoBack(browser(), CURRENT_TAB);
452    } else if (direction == HISTORY_NAVIGATE_FORWARD) {
453      chrome::GoForward(browser(), CURRENT_TAB);
454    } else {
455      FAIL();
456    }
457    test_navigation_observer.Wait();
458  }
459
460  LinkDoctorInterceptor* link_doctor_interceptor_;
461};
462
463class TestFailProvisionalLoadObserver : public content::WebContentsObserver {
464 public:
465  explicit TestFailProvisionalLoadObserver(content::WebContents* contents)
466      : content::WebContentsObserver(contents) {}
467  virtual ~TestFailProvisionalLoadObserver() {}
468
469  // This method is invoked when the provisional load failed.
470  virtual void DidFailProvisionalLoad(
471      content::RenderFrameHost* render_frame_host,
472      const GURL& validated_url,
473      int error_code,
474      const base::string16& error_description) OVERRIDE {
475    fail_url_ = validated_url;
476  }
477
478  const GURL& fail_url() const { return fail_url_; }
479
480 private:
481  GURL fail_url_;
482
483  DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver);
484};
485
486void InterceptNetworkTransactions(net::URLRequestContextGetter* getter,
487                                  net::Error error) {
488  DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO));
489  net::HttpCache* cache(
490      getter->GetURLRequestContext()->http_transaction_factory()->GetCache());
491  DCHECK(cache);
492  scoped_ptr<net::HttpTransactionFactory> factory(
493      new net::FailingHttpTransactionFactory(cache->GetSession(), error));
494  // Throw away old version; since this is a a browser test, we don't
495  // need to restore the old state.
496  cache->SetHttpNetworkTransactionFactoryForTesting(factory.Pass());
497}
498
499// Test that a DNS error occuring in the main frame redirects to an error page.
500IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_Basic) {
501  // The first navigation should fail, and the second one should be the error
502  // page.
503  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
504       browser(), GetDnsErrorURL(), 2);
505  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
506  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
507}
508
509// Test that a DNS error occuring in the main frame does not result in an
510// additional session history entry.
511IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack1) {
512  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
513  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
514       browser(), GetDnsErrorURL(), 2);
515  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
516  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
517  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
518}
519
520// Test that a DNS error occuring in the main frame does not result in an
521// additional session history entry.
522IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2) {
523  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
524
525  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
526       browser(), GetDnsErrorURL(), 2);
527  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
528  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
529
530  NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
531
532  GoBackAndWaitForNavigations(2);
533  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
534  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
535
536  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
537  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
538}
539
540// Test that a DNS error occuring in the main frame does not result in an
541// additional session history entry.
542IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2AndForward) {
543  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
544
545  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
546       browser(), GetDnsErrorURL(), 2);
547  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
548  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
549
550  NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
551
552  GoBackAndWaitForNavigations(2);
553  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
554  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
555
556  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
557
558  GoForwardAndWaitForNavigations(2);
559  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
560  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
561}
562
563// Test that a DNS error occuring in the main frame does not result in an
564// additional session history entry.
565IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2Forward2) {
566  NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
567
568  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
569       browser(), GetDnsErrorURL(), 2);
570  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
571  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
572
573  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
574
575  GoBackAndWaitForNavigations(2);
576  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
577  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
578
579  GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
580
581  GoForwardAndWaitForNavigations(2);
582  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
583  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
584
585  GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
586  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
587}
588
589// Test that the search button on a DNS error page works.
590IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_DoSearch) {
591  // The first navigation should fail, and the second one should be the error
592  // page.
593  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
594       browser(), GetDnsErrorURL(), 2);
595  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
596  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
597
598  content::WebContents* web_contents =
599      browser()->tab_strip_model()->GetActiveWebContents();
600
601  // Do a search and make sure the browser ends up at the right page.
602  content::TestNavigationObserver nav_observer(web_contents, 1);
603  content::TitleWatcher title_watcher(
604      web_contents,
605      base::ASCIIToUTF16("Title Of More Awesomeness"));
606  // Can't use content::ExecuteScript because it waits for scripts to send
607  // notification that they've run, and scripts that trigger a navigation may
608  // not send that notification.
609  web_contents->GetMainFrame()->ExecuteJavaScript(
610      base::ASCIIToUTF16("document.getElementById('search-button').click();"));
611  nav_observer.Wait();
612  EXPECT_EQ(base::ASCIIToUTF16("Title Of More Awesomeness"),
613            title_watcher.WaitAndGetTitle());
614
615  // There should have been another Link Doctor request, for tracking purposes.
616  // Have to wait for it, since the search page does not depend on having
617  // sent the tracking request.
618  link_doctor_interceptor()->WaitForRequests(2);
619  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
620
621  // Check the path and query string.
622  std::string url;
623  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
624                  browser()->tab_strip_model()->GetActiveWebContents(),
625                  "domAutomationController.send(window.location.href);",
626                  &url));
627  EXPECT_EQ("/search", GURL(url).path());
628  EXPECT_EQ("q=search%20query", GURL(url).query());
629
630  // Go back to the error page, to make sure the history is correct.
631  GoBackAndWaitForNavigations(2);
632  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
633  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
634}
635
636// Test that the reload button on a DNS error page works.
637IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_DoReload) {
638  // The first navigation should fail, and the second one should be the error
639  // page.
640  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
641       browser(), GetDnsErrorURL(), 2);
642  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
643  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
644
645  content::WebContents* web_contents =
646      browser()->tab_strip_model()->GetActiveWebContents();
647
648  // Clicking the reload button should load the error page again, and there
649  // should be two commits, as before.
650  content::TestNavigationObserver nav_observer(web_contents, 2);
651  // Can't use content::ExecuteScript because it waits for scripts to send
652  // notification that they've run, and scripts that trigger a navigation may
653  // not send that notification.
654  web_contents->GetMainFrame()->ExecuteJavaScript(
655      base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
656  nav_observer.Wait();
657  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
658
659  // There should have two more requests to the correction service:  One for the
660  // new error page, and one for tracking purposes.  Have to make sure to wait
661  // for the tracking request, since the new error page does not depend on it.
662  link_doctor_interceptor()->WaitForRequests(3);
663  EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
664}
665
666// Test that clicking links on a DNS error page works.
667IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_DoClickLink) {
668  // The first navigation should fail, and the second one should be the error
669  // page.
670  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
671       browser(), GetDnsErrorURL(), 2);
672  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
673  EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
674
675  content::WebContents* web_contents =
676      browser()->tab_strip_model()->GetActiveWebContents();
677
678  // Simulate a click on a link.
679
680  content::TitleWatcher title_watcher(
681      web_contents,
682      base::ASCIIToUTF16("Title Of Awesomeness"));
683  std::string link_selector =
684      "document.querySelector('a[href=\"http://mock.http/title2.html\"]')";
685  // The tracking request is triggered by onmousedown, so it catches middle
686  // mouse button clicks, as well as left clicks.
687  web_contents->GetMainFrame()->ExecuteJavaScript(
688      base::ASCIIToUTF16(link_selector + ".onmousedown();"));
689  // Can't use content::ExecuteScript because it waits for scripts to send
690  // notification that they've run, and scripts that trigger a navigation may
691  // not send that notification.
692  web_contents->GetMainFrame()->ExecuteJavaScript(
693      base::ASCIIToUTF16(link_selector + ".click();"));
694  EXPECT_EQ(base::ASCIIToUTF16("Title Of Awesomeness"),
695            title_watcher.WaitAndGetTitle());
696
697  // There should have been a tracking request to the correction service.  Have
698  // to make sure to wait the tracking request, since the new page does not
699  // depend on it.
700  link_doctor_interceptor()->WaitForRequests(2);
701  EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
702}
703
704// Test that a DNS error occuring in an iframe does not result in showing
705// navigation corrections.
706IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_Basic) {
707  NavigateToURLAndWaitForTitle(
708      net::URLRequestMockHTTPJob::GetMockUrl(
709          base::FilePath(FILE_PATH_LITERAL("iframe_dns_error.html"))),
710      "Blah",
711      1);
712  // We expect to have two history entries, since we started off with navigation
713  // to "about:blank" and then navigated to "iframe_dns_error.html".
714  EXPECT_EQ(2,
715      browser()->tab_strip_model()->GetActiveWebContents()->
716          GetController().GetEntryCount());
717  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
718}
719
720// This test fails regularly on win_rel trybots. See crbug.com/121540
721#if defined(OS_WIN)
722#define MAYBE_IFrameDNSError_GoBack DISABLED_IFrameDNSError_GoBack
723#else
724#define MAYBE_IFrameDNSError_GoBack IFrameDNSError_GoBack
725#endif
726// Test that a DNS error occuring in an iframe does not result in an
727// additional session history entry.
728IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_IFrameDNSError_GoBack) {
729  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
730  NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
731  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
732  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
733}
734
735// This test fails regularly on win_rel trybots. See crbug.com/121540
736//
737// This fails on linux_aura bringup: http://crbug.com/163931
738#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
739#define MAYBE_IFrameDNSError_GoBackAndForward DISABLED_IFrameDNSError_GoBackAndForward
740#else
741#define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
742#endif
743// Test that a DNS error occuring in an iframe does not result in an
744// additional session history entry.
745IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_IFrameDNSError_GoBackAndForward) {
746  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
747  NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
748  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
749  GoForwardAndWaitForTitle("Blah", 1);
750  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
751}
752
753// Test that a DNS error occuring in an iframe, once the main document is
754// completed loading, does not result in an additional session history entry.
755// To ensure that the main document has completed loading, JavaScript is used to
756// inject an iframe after loading is done.
757IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_JavaScript) {
758  content::WebContents* wc =
759      browser()->tab_strip_model()->GetActiveWebContents();
760  GURL fail_url =
761      URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
762
763  // Load a regular web page, in which we will inject an iframe.
764  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
765
766  // We expect to have two history entries, since we started off with navigation
767  // to "about:blank" and then navigated to "title2.html".
768  EXPECT_EQ(2, wc->GetController().GetEntryCount());
769
770  std::string script = "var frame = document.createElement('iframe');"
771                       "frame.src = '" + fail_url.spec() + "';"
772                       "document.body.appendChild(frame);";
773  {
774    TestFailProvisionalLoadObserver fail_observer(wc);
775    content::WindowedNotificationObserver load_observer(
776        content::NOTIFICATION_LOAD_STOP,
777        content::Source<NavigationController>(&wc->GetController()));
778    wc->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
779    load_observer.Wait();
780
781    // Ensure we saw the expected failure.
782    EXPECT_EQ(fail_url, fail_observer.fail_url());
783
784    // Failed initial navigation of an iframe shouldn't be adding any history
785    // entries.
786    EXPECT_EQ(2, wc->GetController().GetEntryCount());
787  }
788
789  // Do the same test, but with an iframe that doesn't have initial URL
790  // assigned.
791  script = "var frame = document.createElement('iframe');"
792           "frame.id = 'target_frame';"
793           "document.body.appendChild(frame);";
794  {
795    content::WindowedNotificationObserver load_observer(
796        content::NOTIFICATION_LOAD_STOP,
797        content::Source<NavigationController>(&wc->GetController()));
798    wc->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
799    load_observer.Wait();
800  }
801
802  script = "var f = document.getElementById('target_frame');"
803           "f.src = '" + fail_url.spec() + "';";
804  {
805    TestFailProvisionalLoadObserver fail_observer(wc);
806    content::WindowedNotificationObserver load_observer(
807        content::NOTIFICATION_LOAD_STOP,
808        content::Source<NavigationController>(&wc->GetController()));
809    wc->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
810    load_observer.Wait();
811
812    EXPECT_EQ(fail_url, fail_observer.fail_url());
813    EXPECT_EQ(2, wc->GetController().GetEntryCount());
814  }
815  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
816}
817
818// Checks that navigation corrections are not loaded when we receive an actual
819// 404 page.
820IN_PROC_BROWSER_TEST_F(ErrorPageTest, Page404) {
821  NavigateToURLAndWaitForTitle(
822      net::URLRequestMockHTTPJob::GetMockUrl(
823          base::FilePath(FILE_PATH_LITERAL("page404.html"))),
824      "SUCCESS",
825      1);
826  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
827}
828
829// Checks that when an error occurs, the stale cache status of the page
830// is correctly transferred, and that stale cached copied can be loaded
831// from the javascript.
832IN_PROC_BROWSER_TEST_F(ErrorPageTest, StaleCacheStatus) {
833  ASSERT_TRUE(test_server()->Start());
834  // Load cache with entry with "nocache" set, to create stale
835  // cache.
836  GURL test_url(test_server()->GetURL("files/nocache.html"));
837  NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1);
838
839  // Reload same URL after forcing an error from the the network layer;
840  // confirm that the error page is told the cached copy exists.
841  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
842      browser()->profile()->GetRequestContext();
843  BrowserThread::PostTask(
844      BrowserThread::IO, FROM_HERE,
845      base::Bind(&InterceptNetworkTransactions, url_request_context_getter,
846                 net::ERR_FAILED));
847
848  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
849      // With no navigation corrections to load, there's only one navigation.
850      browser(), test_url, 1);
851  EXPECT_TRUE(ProbeStaleCopyValue(true));
852  EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
853  EXPECT_NE(base::ASCIIToUTF16("Nocache Test Page"),
854            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
855
856  // Confirm that loading the stale copy from the cache works.
857  content::TestNavigationObserver same_tab_observer(
858      browser()->tab_strip_model()->GetActiveWebContents(), 1);
859  ASSERT_TRUE(ReloadStaleCopyFromCache());
860  same_tab_observer.Wait();
861  EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
862            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
863
864  // Clear the cache and reload the same URL; confirm the error page is told
865  // that there is no cached copy.
866  BrowsingDataRemover* remover =
867      BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
868  remover->Remove(BrowsingDataRemover::REMOVE_CACHE,
869                  BrowsingDataHelper::UNPROTECTED_WEB);
870  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
871      browser(), test_url, 1);
872  EXPECT_TRUE(ProbeStaleCopyValue(false));
873  EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
874  EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
875}
876
877class ErrorPageAutoReloadTest : public InProcessBrowserTest {
878 public:
879  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
880    command_line->AppendSwitch(switches::kEnableOfflineAutoReload);
881  }
882
883  void InstallInterceptor(const GURL& url, int requests_to_fail) {
884    interceptor_ = new FailFirstNRequestsInterceptor(requests_to_fail);
885    scoped_ptr<net::URLRequestInterceptor> owned_interceptor(interceptor_);
886
887    // Tests don't need to wait for this task to complete before using the
888    // filter; any requests that might be affected by it will end up in the IO
889    // thread's message loop after this posted task anyway.
890    //
891    // Ownership of the interceptor is passed to an object the IO thread, but a
892    // pointer is kept in the test fixture.  As soon as anything calls
893    // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
894    BrowserThread::PostTask(
895        BrowserThread::IO, FROM_HERE,
896        base::Bind(&AddInterceptorForURL, url,
897                   base::Passed(&owned_interceptor)));
898  }
899
900  void NavigateToURLAndWaitForTitle(const GURL& url,
901                                    const std::string& expected_title,
902                                    int num_navigations) {
903    content::TitleWatcher title_watcher(
904        browser()->tab_strip_model()->GetActiveWebContents(),
905        base::ASCIIToUTF16(expected_title));
906
907    ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
908        browser(), url, num_navigations);
909
910    EXPECT_EQ(base::ASCIIToUTF16(expected_title),
911              title_watcher.WaitAndGetTitle());
912  }
913
914  FailFirstNRequestsInterceptor* interceptor() {
915    return interceptor_;
916  }
917
918 private:
919  FailFirstNRequestsInterceptor* interceptor_;
920};
921
922IN_PROC_BROWSER_TEST_F(ErrorPageAutoReloadTest, AutoReload) {
923  GURL test_url("http://error.page.auto.reload");
924  const int kRequestsToFail = 2;
925  InstallInterceptor(test_url, kRequestsToFail);
926  NavigateToURLAndWaitForTitle(test_url, "Test One", kRequestsToFail + 1);
927  // Note that the interceptor updates these variables on the IO thread,
928  // but this function reads them on the main thread. The requests have to be
929  // created (on the IO thread) before NavigateToURLAndWaitForTitle returns or
930  // this becomes racey.
931  EXPECT_EQ(kRequestsToFail, interceptor()->failures());
932  EXPECT_EQ(kRequestsToFail + 1, interceptor()->requests());
933}
934
935// Interceptor that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
936class AddressUnreachableInterceptor : public net::URLRequestInterceptor {
937 public:
938  AddressUnreachableInterceptor() {}
939  virtual ~AddressUnreachableInterceptor() {}
940
941  // net::URLRequestInterceptor:
942  virtual net::URLRequestJob* MaybeInterceptRequest(
943      net::URLRequest* request,
944      net::NetworkDelegate* network_delegate) const OVERRIDE {
945    return new URLRequestFailedJob(request,
946                                   network_delegate,
947                                   net::ERR_ADDRESS_UNREACHABLE);
948  }
949
950 private:
951  DISALLOW_COPY_AND_ASSIGN(AddressUnreachableInterceptor);
952};
953
954// A test fixture that returns ERR_ADDRESS_UNREACHABLE for all navigation
955// correction requests.  ERR_NAME_NOT_RESOLVED is more typical, but need to use
956// a different error for the correction service and the original page to
957// validate the right page is being displayed.
958class ErrorPageNavigationCorrectionsFailTest : public ErrorPageTest {
959 public:
960  // InProcessBrowserTest:
961  virtual void SetUpOnMainThread() OVERRIDE {
962    BrowserThread::PostTask(
963        BrowserThread::IO, FROM_HERE,
964        base::Bind(&ErrorPageNavigationCorrectionsFailTest::AddFilters));
965  }
966
967  virtual void TearDownOnMainThread() OVERRIDE {
968    BrowserThread::PostTask(
969        BrowserThread::IO, FROM_HERE,
970        base::Bind(&ErrorPageNavigationCorrectionsFailTest::RemoveFilters));
971  }
972
973 private:
974  // Adds a filter that causes all correction service requests to fail with
975  // ERR_ADDRESS_UNREACHABLE.
976  //
977  // Also adds the net::URLRequestFailedJob filter.
978  static void AddFilters() {
979    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
980    URLRequestFailedJob::AddUrlHandler();
981
982    net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
983        google_util::LinkDoctorBaseURL(),
984        scoped_ptr<net::URLRequestInterceptor>(
985            new AddressUnreachableInterceptor()));
986  }
987
988  static void RemoveFilters() {
989    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
990    net::URLRequestFilter::GetInstance()->ClearHandlers();
991  }
992};
993
994// Make sure that when corrections fail to load, the network error page is
995// successfully loaded.
996IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest,
997                       FetchCorrectionsFails) {
998  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
999      browser(),
1000      URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED),
1001      2);
1002
1003  // Verify that the expected error page is being displayed.
1004  ExpectDisplayingLocalErrorPage(browser(), net::ERR_NAME_NOT_RESOLVED);
1005}
1006
1007// Checks that when an error occurs and a corrections fail to load, the stale
1008// cache status of the page is correctly transferred, and we can load the
1009// stale copy from the javascript.  Most logic copied from StaleCacheStatus
1010// above.
1011IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest,
1012                       StaleCacheStatusFailedCorrections) {
1013  ASSERT_TRUE(test_server()->Start());
1014  // Load cache with entry with "nocache" set, to create stale
1015  // cache.
1016  GURL test_url(test_server()->GetURL("files/nocache.html"));
1017  NavigateToURLAndWaitForTitle(test_url, "Nocache Test Page", 1);
1018
1019  // Reload same URL after forcing an error from the the network layer;
1020  // confirm that the error page is told the cached copy exists.
1021  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
1022      browser()->profile()->GetRequestContext();
1023  BrowserThread::PostTask(
1024      BrowserThread::IO, FROM_HERE,
1025      base::Bind(&InterceptNetworkTransactions, url_request_context_getter,
1026                 net::ERR_CONNECTION_FAILED));
1027
1028  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1029      browser(), test_url, 2);
1030  EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
1031  EXPECT_TRUE(ProbeStaleCopyValue(true));
1032
1033  // Confirm that loading the stale copy from the cache works.
1034  content::TestNavigationObserver same_tab_observer(
1035      browser()->tab_strip_model()->GetActiveWebContents(), 1);
1036  ASSERT_TRUE(ReloadStaleCopyFromCache());
1037  same_tab_observer.Wait();
1038  EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
1039            browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
1040
1041  // Clear the cache and reload the same URL; confirm the error page is told
1042  // that there is no cached copy.
1043  BrowsingDataRemover* remover =
1044      BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
1045  remover->Remove(BrowsingDataRemover::REMOVE_CACHE,
1046                  BrowsingDataHelper::UNPROTECTED_WEB);
1047  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1048      browser(), test_url, 2);
1049  EXPECT_TRUE(ProbeStaleCopyValue(false));
1050  EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
1051}
1052
1053// A test fixture that simulates failing requests for an IDN domain name.
1054class ErrorPageForIDNTest : public InProcessBrowserTest {
1055 public:
1056  // Target hostname in different forms.
1057  static const char kHostname[];
1058  static const char kHostnameJSUnicode[];
1059
1060  // InProcessBrowserTest:
1061  virtual void SetUpOnMainThread() OVERRIDE {
1062    // Clear AcceptLanguages to force punycode decoding.
1063    browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages,
1064                                                std::string());
1065    BrowserThread::PostTask(
1066        BrowserThread::IO, FROM_HERE,
1067        base::Bind(&ErrorPageForIDNTest::AddFilters));
1068  }
1069
1070  virtual void TearDownOnMainThread() OVERRIDE {
1071    BrowserThread::PostTask(
1072        BrowserThread::IO, FROM_HERE,
1073        base::Bind(&ErrorPageForIDNTest::RemoveFilters));
1074  }
1075
1076 private:
1077  static void AddFilters() {
1078    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1079    URLRequestFailedJob::AddUrlHandlerForHostname(kHostname);
1080  }
1081
1082  static void RemoveFilters() {
1083    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1084    net::URLRequestFilter::GetInstance()->ClearHandlers();
1085  }
1086};
1087
1088const char ErrorPageForIDNTest::kHostname[] =
1089    "xn--d1abbgf6aiiy.xn--p1ai";
1090const char ErrorPageForIDNTest::kHostnameJSUnicode[] =
1091    "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442."
1092    "\\u0440\\u0444";
1093
1094// Make sure error page shows correct unicode for IDN.
1095IN_PROC_BROWSER_TEST_F(ErrorPageForIDNTest, IDN) {
1096  // ERR_UNSAFE_PORT will not trigger navigation corrections.
1097  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1098      browser(),
1099      URLRequestFailedJob::GetMockHttpUrlForHostname(net::ERR_UNSAFE_PORT,
1100                                                     kHostname),
1101      1);
1102
1103  ToggleHelpBox(browser());
1104  EXPECT_TRUE(IsDisplayingText(browser(), kHostnameJSUnicode));
1105}
1106
1107}  // namespace
1108