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.
6 #include "base/prefs/pref_service.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/google/google_util.h"
10 #include "chrome/browser/net/url_request_mock_util.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/test/base/in_process_browser_test.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_observer.h"
22 #include "content/public/test/browser_test_utils.h"
23 #include "content/public/test/test_navigation_observer.h"
24 #include "content/test/net/url_request_failed_job.h"
25 #include "content/test/net/url_request_mock_http_job.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_util.h"
28 #include "net/url_request/url_request_filter.h"
29 #include "net/url_request/url_request_job_factory.h"
31 using content::BrowserThread
;
32 using content::NavigationController
;
33 using content::URLRequestFailedJob
;
37 class ErrorPageTest
: public InProcessBrowserTest
{
39 enum HistoryNavigationDirection
{
40 HISTORY_NAVIGATE_BACK
,
41 HISTORY_NAVIGATE_FORWARD
,
44 // Navigates the active tab to a mock url created for the file at |file_path|.
45 void NavigateToFileURL(const base::FilePath::StringType
& file_path
) {
46 ui_test_utils::NavigateToURL(
48 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path
)));
51 // Navigates to the given URL and waits for |num_navigations| to occur, and
52 // the title to change to |expected_title|.
53 void NavigateToURLAndWaitForTitle(const GURL
& url
,
54 const std::string
& expected_title
,
55 int num_navigations
) {
56 content::TitleWatcher
title_watcher(
57 browser()->tab_strip_model()->GetActiveWebContents(),
58 base::ASCIIToUTF16(expected_title
));
60 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
61 browser(), url
, num_navigations
);
63 EXPECT_EQ(base::ASCIIToUTF16(expected_title
),
64 title_watcher
.WaitAndGetTitle());
67 // Navigates back in the history and waits for |num_navigations| to occur, and
68 // the title to change to |expected_title|.
69 void GoBackAndWaitForTitle(const std::string
& expected_title
,
70 int num_navigations
) {
71 NavigateHistoryAndWaitForTitle(expected_title
,
73 HISTORY_NAVIGATE_BACK
);
76 // Navigates forward in the history and waits for |num_navigations| to occur,
77 // and the title to change to |expected_title|.
78 void GoForwardAndWaitForTitle(const std::string
& expected_title
,
79 int num_navigations
) {
80 NavigateHistoryAndWaitForTitle(expected_title
,
82 HISTORY_NAVIGATE_FORWARD
);
86 virtual void SetUpOnMainThread() OVERRIDE
{
87 BrowserThread::PostTask(
88 BrowserThread::IO
, FROM_HERE
,
89 base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled
, true));
92 // Returns a GURL that results in a DNS error.
93 GURL
GetDnsErrorURL() const {
94 return URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
);
98 // Navigates the browser the indicated direction in the history and waits for
99 // |num_navigations| to occur and the title to change to |expected_title|.
100 void NavigateHistoryAndWaitForTitle(const std::string
& expected_title
,
102 HistoryNavigationDirection direction
) {
103 content::TitleWatcher
title_watcher(
104 browser()->tab_strip_model()->GetActiveWebContents(),
105 base::ASCIIToUTF16(expected_title
));
107 content::TestNavigationObserver
test_navigation_observer(
108 browser()->tab_strip_model()->GetActiveWebContents(),
110 if (direction
== HISTORY_NAVIGATE_BACK
) {
111 chrome::GoBack(browser(), CURRENT_TAB
);
112 } else if (direction
== HISTORY_NAVIGATE_FORWARD
) {
113 chrome::GoForward(browser(), CURRENT_TAB
);
117 test_navigation_observer
.Wait();
119 EXPECT_EQ(title_watcher
.WaitAndGetTitle(),
120 base::ASCIIToUTF16(expected_title
));
125 class TestFailProvisionalLoadObserver
: public content::WebContentsObserver
{
127 explicit TestFailProvisionalLoadObserver(content::WebContents
* contents
)
128 : content::WebContentsObserver(contents
) {}
129 virtual ~TestFailProvisionalLoadObserver() {}
131 // This method is invoked when the provisional load failed.
132 virtual void DidFailProvisionalLoad(
134 const base::string16
& frame_unique_name
,
136 const GURL
& validated_url
,
138 const base::string16
& error_description
,
139 content::RenderViewHost
* render_view_host
) OVERRIDE
{
140 fail_url_
= validated_url
;
143 const GURL
& fail_url() const { return fail_url_
; }
148 DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver
);
151 // See crbug.com/109669
152 #if defined(USE_AURA) || defined(OS_WIN)
153 #define MAYBE_DNSError_Basic DISABLED_DNSError_Basic
155 #define MAYBE_DNSError_Basic DNSError_Basic
157 // Test that a DNS error occuring in the main frame redirects to an error page.
158 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, MAYBE_DNSError_Basic
) {
159 // The first navigation should fail, and the second one should be the error
161 NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
164 // See crbug.com/109669
165 #if defined(USE_AURA)
166 #define MAYBE_DNSError_GoBack1 DISABLED_DNSError_GoBack1
168 #define MAYBE_DNSError_GoBack1 DNSError_GoBack1
171 // Test that a DNS error occuring in the main frame does not result in an
172 // additional session history entry.
173 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, MAYBE_DNSError_GoBack1
) {
174 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
175 NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
176 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
179 // See crbug.com/109669
180 #if defined(USE_AURA)
181 #define MAYBE_DNSError_GoBack2 DISABLED_DNSError_GoBack2
183 #define MAYBE_DNSError_GoBack2 DNSError_GoBack2
185 // Test that a DNS error occuring in the main frame does not result in an
186 // additional session history entry.
187 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2
) {
188 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
190 NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
191 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
193 GoBackAndWaitForTitle("Mock Link Doctor", 2);
194 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
197 // See crbug.com/109669
198 #if defined(USE_AURA)
199 #define MAYBE_DNSError_GoBack2AndForward DISABLED_DNSError_GoBack2AndForward
201 #define MAYBE_DNSError_GoBack2AndForward DNSError_GoBack2AndForward
203 // Test that a DNS error occuring in the main frame does not result in an
204 // additional session history entry.
205 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2AndForward
) {
206 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
208 NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
209 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
211 GoBackAndWaitForTitle("Mock Link Doctor", 2);
212 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
214 GoForwardAndWaitForTitle("Mock Link Doctor", 2);
217 // See crbug.com/109669
218 #if defined(USE_AURA)
219 #define MAYBE_DNSError_GoBack2Forward2 DISABLED_DNSError_GoBack2Forward2
221 #define MAYBE_DNSError_GoBack2Forward2 DNSError_GoBack2Forward2
223 // Test that a DNS error occuring in the main frame does not result in an
224 // additional session history entry.
225 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2Forward2
) {
226 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
228 NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
229 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
231 GoBackAndWaitForTitle("Mock Link Doctor", 2);
232 GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
234 GoForwardAndWaitForTitle("Mock Link Doctor", 2);
235 GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
238 // Test that a DNS error occuring in an iframe.
239 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, IFrameDNSError_Basic
) {
240 NavigateToURLAndWaitForTitle(
241 content::URLRequestMockHTTPJob::GetMockUrl(
242 base::FilePath(FILE_PATH_LITERAL("iframe_dns_error.html"))),
245 // We expect to have two history entries, since we started off with navigation
246 // to "about:blank" and then navigated to "iframe_dns_error.html".
248 browser()->tab_strip_model()->GetActiveWebContents()->
249 GetController().GetEntryCount());
252 // This test fails regularly on win_rel trybots. See crbug.com/121540
254 #define MAYBE_IFrameDNSError_GoBack DISABLED_IFrameDNSError_GoBack
256 #define MAYBE_IFrameDNSError_GoBack IFrameDNSError_GoBack
258 // Test that a DNS error occuring in an iframe does not result in an
259 // additional session history entry.
260 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, MAYBE_IFrameDNSError_GoBack
) {
261 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
262 NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
263 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
266 // This test fails regularly on win_rel trybots. See crbug.com/121540
268 // This fails on linux_aura bringup: http://crbug.com/163931
269 #if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
270 #define MAYBE_IFrameDNSError_GoBackAndForward DISABLED_IFrameDNSError_GoBackAndForward
272 #define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
274 // Test that a DNS error occuring in an iframe does not result in an
275 // additional session history entry.
276 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, MAYBE_IFrameDNSError_GoBackAndForward
) {
277 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
278 NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
279 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
280 GoForwardAndWaitForTitle("Blah", 1);
283 // Test that a DNS error occuring in an iframe, once the main document is
284 // completed loading, does not result in an additional session history entry.
285 // To ensure that the main document has completed loading, JavaScript is used to
286 // inject an iframe after loading is done.
287 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, IFrameDNSError_JavaScript
) {
288 content::WebContents
* wc
=
289 browser()->tab_strip_model()->GetActiveWebContents();
291 URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
);
293 // Load a regular web page, in which we will inject an iframe.
294 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
296 // We expect to have two history entries, since we started off with navigation
297 // to "about:blank" and then navigated to "title2.html".
298 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
300 std::string script
= "var frame = document.createElement('iframe');"
301 "frame.src = '" + fail_url
.spec() + "';"
302 "document.body.appendChild(frame);";
304 TestFailProvisionalLoadObserver
fail_observer(wc
);
305 content::WindowedNotificationObserver
load_observer(
306 content::NOTIFICATION_LOAD_STOP
,
307 content::Source
<NavigationController
>(&wc
->GetController()));
308 wc
->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
309 base::string16(), base::ASCIIToUTF16(script
));
310 load_observer
.Wait();
312 // Ensure we saw the expected failure.
313 EXPECT_EQ(fail_url
, fail_observer
.fail_url());
315 // Failed initial navigation of an iframe shouldn't be adding any history
317 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
320 // Do the same test, but with an iframe that doesn't have initial URL
322 script
= "var frame = document.createElement('iframe');"
323 "frame.id = 'target_frame';"
324 "document.body.appendChild(frame);";
326 content::WindowedNotificationObserver
load_observer(
327 content::NOTIFICATION_LOAD_STOP
,
328 content::Source
<NavigationController
>(&wc
->GetController()));
329 wc
->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
330 base::string16(), base::ASCIIToUTF16(script
));
331 load_observer
.Wait();
334 script
= "var f = document.getElementById('target_frame');"
335 "f.src = '" + fail_url
.spec() + "';";
337 TestFailProvisionalLoadObserver
fail_observer(wc
);
338 content::WindowedNotificationObserver
load_observer(
339 content::NOTIFICATION_LOAD_STOP
,
340 content::Source
<NavigationController
>(&wc
->GetController()));
341 wc
->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
342 base::string16(), base::ASCIIToUTF16(script
));
343 load_observer
.Wait();
345 EXPECT_EQ(fail_url
, fail_observer
.fail_url());
346 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
350 // Checks that the Link Doctor is not loaded when we receive an actual 404 page.
351 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, Page404
) {
352 NavigateToURLAndWaitForTitle(
353 content::URLRequestMockHTTPJob::GetMockUrl(
354 base::FilePath(FILE_PATH_LITERAL("page404.html"))),
359 // Returns Javascript code that executes plain text search for the page.
360 // Pass into content::ExecuteScriptAndExtractBool as |script| parameter.
361 std::string
GetTextContentContainsStringScript(
362 const std::string
& value_to_search
) {
363 return base::StringPrintf(
364 "var textContent = document.body.textContent;"
365 "var hasError = textContent.indexOf('%s') >= 0;"
366 "domAutomationController.send(hasError);",
367 value_to_search
.c_str());
370 // Protocol handler that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
371 class AddressUnreachableProtocolHandler
372 : public net::URLRequestJobFactory::ProtocolHandler
{
374 AddressUnreachableProtocolHandler() {}
375 virtual ~AddressUnreachableProtocolHandler() {}
377 // net::URLRequestJobFactory::ProtocolHandler:
378 virtual net::URLRequestJob
* MaybeCreateJob(
379 net::URLRequest
* request
,
380 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
381 return new URLRequestFailedJob(request
,
383 net::ERR_ADDRESS_UNREACHABLE
);
387 DISALLOW_COPY_AND_ASSIGN(AddressUnreachableProtocolHandler
);
390 // A test fixture that returns ERR_ADDRESS_UNREACHABLE for all Link Doctor
391 // requests. ERR_NAME_NOT_RESOLVED is more typical, but need to use a different
392 // error for the Link Doctor and the original page to validate the right page
393 // is being displayed.
394 class ErrorPageLinkDoctorFailTest
: public InProcessBrowserTest
{
396 // InProcessBrowserTest:
397 virtual void SetUpOnMainThread() OVERRIDE
{
398 BrowserThread::PostTask(
399 BrowserThread::IO
, FROM_HERE
,
400 base::Bind(&ErrorPageLinkDoctorFailTest::AddFilters
));
403 virtual void CleanUpOnMainThread() OVERRIDE
{
404 BrowserThread::PostTask(
405 BrowserThread::IO
, FROM_HERE
,
406 base::Bind(&ErrorPageLinkDoctorFailTest::RemoveFilters
));
410 // Adds a filter that causes all requests for the Link Doctor's scheme and
411 // host to fail with ERR_ADDRESS_UNREACHABLE. Since the Link Doctor adds
412 // query strings, it's not enough to just fail exact matches.
414 // Also adds the content::URLRequestFailedJob filter.
415 static void AddFilters() {
416 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
417 content::URLRequestFailedJob::AddUrlHandler();
419 net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
420 google_util::LinkDoctorBaseURL().scheme(),
421 google_util::LinkDoctorBaseURL().host(),
422 scoped_ptr
<net::URLRequestJobFactory::ProtocolHandler
>(
423 new AddressUnreachableProtocolHandler()));
426 static void RemoveFilters() {
427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
428 net::URLRequestFilter::GetInstance()->ClearHandlers();
432 // Make sure that when the Link Doctor fails to load, the network error page is
433 // successfully loaded.
434 IN_PROC_BROWSER_TEST_F(ErrorPageLinkDoctorFailTest
, LinkDoctorFail
) {
435 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
437 URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
),
440 // Verify that the expected error page is being displayed. Do this by making
441 // sure the original error code (ERR_NAME_NOT_RESOLVED) is displayed.
443 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
444 browser()->tab_strip_model()->GetActiveWebContents(),
445 GetTextContentContainsStringScript("ERR_NAME_NOT_RESOLVED"),
450 // A test fixture that simulates failing requests for an IDN domain name.
451 class ErrorPageForIDNTest
: public InProcessBrowserTest
{
453 // Target hostname in different forms.
454 static const char kHostname
[];
455 static const char kHostnameJSUnicode
[];
457 // InProcessBrowserTest:
458 virtual void SetUpOnMainThread() OVERRIDE
{
459 // Clear AcceptLanguages to force punycode decoding.
460 browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages
,
462 BrowserThread::PostTask(
463 BrowserThread::IO
, FROM_HERE
,
464 base::Bind(&ErrorPageForIDNTest::AddFilters
));
467 virtual void CleanUpOnMainThread() OVERRIDE
{
468 BrowserThread::PostTask(
469 BrowserThread::IO
, FROM_HERE
,
470 base::Bind(&ErrorPageForIDNTest::RemoveFilters
));
474 static void AddFilters() {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
476 content::URLRequestFailedJob::AddUrlHandlerForHostname(kHostname
);
479 static void RemoveFilters() {
480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
481 net::URLRequestFilter::GetInstance()->ClearHandlers();
485 const char ErrorPageForIDNTest::kHostname
[] =
486 "xn--d1abbgf6aiiy.xn--p1ai";
487 const char ErrorPageForIDNTest::kHostnameJSUnicode
[] =
488 "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442."
491 // Make sure error page shows correct unicode for IDN.
492 IN_PROC_BROWSER_TEST_F(ErrorPageForIDNTest
, IDN
) {
493 // ERR_UNSAFE_PORT will not trigger the link doctor.
494 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
496 URLRequestFailedJob::GetMockHttpUrlForHostname(net::ERR_UNSAFE_PORT
,
501 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
502 browser()->tab_strip_model()->GetActiveWebContents(),
503 GetTextContentContainsStringScript(kHostnameJSUnicode
),