errorpage_browsertest.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/utf_string_conversions.h"
7#include "chrome/browser/google/google_util.h"
8#include "chrome/browser/net/url_request_mock_util.h"
9#include "chrome/browser/ui/browser.h"
10#include "chrome/browser/ui/browser_commands.h"
11#include "chrome/browser/ui/tabs/tab_strip_model.h"
12#include "chrome/test/base/in_process_browser_test.h"
13#include "chrome/test/base/ui_test_utils.h"
14#include "content/public/browser/web_contents.h"
15#include "content/public/test/browser_test_utils.h"
16#include "content/public/test/test_navigation_observer.h"
17#include "content/test/net/url_request_failed_job.h"
18#include "content/test/net/url_request_mock_http_job.h"
19#include "net/base/net_errors.h"
20#include "net/url_request/url_request_filter.h"
21#include "net/url_request/url_request_job_factory.h"
22
23using content::BrowserThread;
24using content::NavigationController;
25using content::URLRequestFailedJob;
26
27namespace {
28
29class ErrorPageTest : public InProcessBrowserTest {
30 public:
31  enum HistoryNavigationDirection {
32    HISTORY_NAVIGATE_BACK,
33    HISTORY_NAVIGATE_FORWARD,
34  };
35
36  // Navigates the active tab to a mock url created for the file at |file_path|.
37  void NavigateToFileURL(const base::FilePath::StringType& file_path) {
38    ui_test_utils::NavigateToURL(
39        browser(),
40        content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path)));
41  }
42
43  // Navigates to the given URL and waits for |num_navigations| to occur, and
44  // the title to change to |expected_title|.
45  void NavigateToURLAndWaitForTitle(const GURL& url,
46                                    const std::string& expected_title,
47                                    int num_navigations) {
48    content::TitleWatcher title_watcher(
49        browser()->tab_strip_model()->GetActiveWebContents(),
50        ASCIIToUTF16(expected_title));
51
52    ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
53        browser(), url, num_navigations);
54
55    EXPECT_EQ(ASCIIToUTF16(expected_title), title_watcher.WaitAndGetTitle());
56  }
57
58  // Navigates back in the history and waits for |num_navigations| to occur, and
59  // the title to change to |expected_title|.
60  void GoBackAndWaitForTitle(const std::string& expected_title,
61                             int num_navigations) {
62    NavigateHistoryAndWaitForTitle(expected_title,
63                                   num_navigations,
64                                   HISTORY_NAVIGATE_BACK);
65  }
66
67  // Navigates forward in the history and waits for |num_navigations| to occur,
68  // and the title to change to |expected_title|.
69  void GoForwardAndWaitForTitle(const std::string& expected_title,
70                                int num_navigations) {
71    NavigateHistoryAndWaitForTitle(expected_title,
72                                   num_navigations,
73                                   HISTORY_NAVIGATE_FORWARD);
74  }
75
76 protected:
77  virtual void SetUpOnMainThread() OVERRIDE {
78    BrowserThread::PostTask(
79        BrowserThread::IO, FROM_HERE,
80        base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
81  }
82
83  // Returns a GURL that results in a DNS error.
84  GURL GetDnsErrorURL() const {
85    return URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
86  }
87
88 private:
89  // Navigates the browser the indicated direction in the history and waits for
90  // |num_navigations| to occur and the title to change to |expected_title|.
91  void NavigateHistoryAndWaitForTitle(const std::string& expected_title,
92                                      int num_navigations,
93                                      HistoryNavigationDirection direction) {
94    content::TitleWatcher title_watcher(
95        browser()->tab_strip_model()->GetActiveWebContents(),
96        ASCIIToUTF16(expected_title));
97
98    content::TestNavigationObserver test_navigation_observer(
99        content::Source<NavigationController>(
100              &browser()->tab_strip_model()->GetActiveWebContents()->
101                  GetController()),
102        num_navigations);
103    if (direction == HISTORY_NAVIGATE_BACK) {
104      chrome::GoBack(browser(), CURRENT_TAB);
105    } else if (direction == HISTORY_NAVIGATE_FORWARD) {
106      chrome::GoForward(browser(), CURRENT_TAB);
107    } else {
108      FAIL();
109    }
110    test_navigation_observer.WaitForObservation(
111        base::Bind(&content::RunMessageLoop),
112        base::Bind(&MessageLoop::Quit,
113                   base::Unretained(MessageLoopForUI::current())));
114
115    EXPECT_EQ(title_watcher.WaitAndGetTitle(), ASCIIToUTF16(expected_title));
116  }
117};
118
119// See crbug.com/109669
120#if defined(USE_AURA) || defined(OS_WIN) || defined(OS_MACOSX)
121#define MAYBE_DNSError_Basic DISABLED_DNSError_Basic
122#else
123#define MAYBE_DNSError_Basic DNSError_Basic
124#endif
125// Test that a DNS error occuring in the main frame redirects to an error page.
126IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_DNSError_Basic) {
127  // The first navigation should fail, and the second one should be the error
128  // page.
129  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
130}
131
132// See crbug.com/109669
133#if defined(USE_AURA) || defined(OS_WIN) || defined(OS_MACOSX)
134#define MAYBE_DNSError_GoBack1 DISABLED_DNSError_GoBack1
135#else
136#define MAYBE_DNSError_GoBack1 DNSError_GoBack1
137#endif
138
139// Test that a DNS error occuring in the main frame does not result in an
140// additional session history entry.
141IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_DNSError_GoBack1) {
142  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
143  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
144  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
145}
146
147// See crbug.com/109669
148#if defined(USE_AURA) || defined(OS_WIN) || defined(OS_MACOSX)
149#define MAYBE_DNSError_GoBack2 DISABLED_DNSError_GoBack2
150#else
151#define MAYBE_DNSError_GoBack2 DNSError_GoBack2
152#endif
153// Test that a DNS error occuring in the main frame does not result in an
154// additional session history entry.
155IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2) {
156  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
157
158  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
159  NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
160
161  GoBackAndWaitForTitle("Mock Link Doctor", 2);
162  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
163}
164
165// See crbug.com/109669
166#if defined(USE_AURA) || defined(OS_WIN) || defined(OS_MACOSX)
167#define MAYBE_DNSError_GoBack2AndForward DISABLED_DNSError_GoBack2AndForward
168#else
169#define MAYBE_DNSError_GoBack2AndForward DNSError_GoBack2AndForward
170#endif
171// Test that a DNS error occuring in the main frame does not result in an
172// additional session history entry.
173IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2AndForward) {
174  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
175
176  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
177  NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
178
179  GoBackAndWaitForTitle("Mock Link Doctor", 2);
180  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
181
182  GoForwardAndWaitForTitle("Mock Link Doctor", 2);
183}
184
185// See crbug.com/109669
186#if defined(USE_AURA) || defined(OS_WIN) || defined(OS_MACOSX)
187#define MAYBE_DNSError_GoBack2Forward2 DISABLED_DNSError_GoBack2Forward2
188#else
189#define MAYBE_DNSError_GoBack2Forward2 DNSError_GoBack2Forward2
190#endif
191// Test that a DNS error occuring in the main frame does not result in an
192// additional session history entry.
193IN_PROC_BROWSER_TEST_F(ErrorPageTest, DNSError_GoBack2Forward2) {
194  NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
195
196  NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
197  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
198
199  GoBackAndWaitForTitle("Mock Link Doctor", 2);
200  GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
201
202  GoForwardAndWaitForTitle("Mock Link Doctor", 2);
203  GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
204}
205
206// Test that a DNS error occuring in an iframe.
207IN_PROC_BROWSER_TEST_F(ErrorPageTest, IFrameDNSError_Basic) {
208  NavigateToURLAndWaitForTitle(
209      content::URLRequestMockHTTPJob::GetMockUrl(
210          base::FilePath(FILE_PATH_LITERAL("iframe_dns_error.html"))),
211      "Blah",
212      1);
213}
214
215// This test fails regularly on win_rel trybots. See crbug.com/121540
216#if defined(OS_WIN)
217#define MAYBE_IFrameDNSError_GoBack DISABLED_IFrameDNSError_GoBack
218#else
219#define MAYBE_IFrameDNSError_GoBack IFrameDNSError_GoBack
220#endif
221// Test that a DNS error occuring in an iframe does not result in an
222// additional session history entry.
223IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_IFrameDNSError_GoBack) {
224  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
225  NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
226  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
227}
228
229// This test fails regularly on win_rel trybots. See crbug.com/121540
230#if defined(OS_WIN)
231#define MAYBE_IFrameDNSError_GoBackAndForward DISABLED_IFrameDNSError_GoBackAndForward
232#else
233#define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
234#endif
235// Test that a DNS error occuring in an iframe does not result in an
236// additional session history entry.
237IN_PROC_BROWSER_TEST_F(ErrorPageTest, MAYBE_IFrameDNSError_GoBackAndForward) {
238  NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
239  NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
240  GoBackAndWaitForTitle("Title Of Awesomeness", 1);
241  GoForwardAndWaitForTitle("Blah", 1);
242}
243
244// Checks that the Link Doctor is not loaded when we receive an actual 404 page.
245IN_PROC_BROWSER_TEST_F(ErrorPageTest, Page404) {
246  NavigateToURLAndWaitForTitle(
247      content::URLRequestMockHTTPJob::GetMockUrl(
248          base::FilePath(FILE_PATH_LITERAL("page404.html"))),
249      "SUCCESS",
250      1);
251}
252
253// Protocol handler that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
254class AddressUnreachableProtocolHandler
255    : public net::URLRequestJobFactory::ProtocolHandler {
256 public:
257  AddressUnreachableProtocolHandler() {}
258  virtual ~AddressUnreachableProtocolHandler() {}
259
260  // net::URLRequestJobFactory::ProtocolHandler:
261  virtual net::URLRequestJob* MaybeCreateJob(
262      net::URLRequest* request,
263      net::NetworkDelegate* network_delegate) const OVERRIDE {
264    return new URLRequestFailedJob(request,
265                                   network_delegate,
266                                   net::ERR_ADDRESS_UNREACHABLE);
267  }
268
269 private:
270  DISALLOW_COPY_AND_ASSIGN(AddressUnreachableProtocolHandler);
271};
272
273// A test fixture that returns ERR_ADDRESS_UNREACHABLE for all Link Doctor
274// requests.  ERR_NAME_NOT_RESOLVED is more typical, but need to use a different
275// error for the Link Doctor and the original page to validate the right page
276// is being displayed.
277class ErrorPageLinkDoctorFailTest : public InProcessBrowserTest {
278 public:
279  // InProcessBrowserTest:
280  virtual void SetUpOnMainThread() OVERRIDE {
281    BrowserThread::PostTask(
282        BrowserThread::IO, FROM_HERE,
283        base::Bind(&ErrorPageLinkDoctorFailTest::AddFilters));
284  }
285
286  virtual void CleanUpOnMainThread() OVERRIDE {
287    BrowserThread::PostTask(
288        BrowserThread::IO, FROM_HERE,
289        base::Bind(&ErrorPageLinkDoctorFailTest::RemoveFilters));
290  }
291
292 private:
293  // Adds a filter that causes all requests for the Link Doctor's scheme and
294  // host to fail with ERR_ADDRESS_UNREACHABLE.  Since the Link Doctor adds
295  // query strings, it's not enough to just fail exact matches.
296  //
297  // Also adds the content::URLRequestFailedJob filter.
298  static void AddFilters() {
299    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
300    content::URLRequestFailedJob::AddUrlHandler();
301
302    net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
303        google_util::LinkDoctorBaseURL().scheme(),
304        google_util::LinkDoctorBaseURL().host(),
305        scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
306            new AddressUnreachableProtocolHandler()));
307  }
308
309  static void RemoveFilters() {
310    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
311    net::URLRequestFilter::GetInstance()->ClearHandlers();
312  }
313};
314
315// Make sure that when the Link Doctor fails to load, the network error page is
316// successfully loaded.
317IN_PROC_BROWSER_TEST_F(ErrorPageLinkDoctorFailTest, LinkDoctorFail) {
318  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
319      browser(),
320      URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED),
321      2);
322
323  // Verify that the expected error page is being displayed.  Do this by making
324  // sure the original error code (ERR_NAME_NOT_RESOLVED) is displayed.
325  bool result = false;
326  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
327      browser()->tab_strip_model()->GetActiveWebContents(),
328      "var textContent = document.body.textContent;"
329      "var hasError = textContent.indexOf('ERR_NAME_NOT_RESOLVED') >= 0;"
330      "domAutomationController.send(hasError);",
331      &result));
332  EXPECT_TRUE(result);
333}
334
335}  // namespace
336