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/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/test/base/in_process_browser_test.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "components/google/core/browser/google_util.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_types.h"
33 #include "content/public/browser/render_frame_host.h"
34 #include "content/public/browser/render_view_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_observer.h"
37 #include "content/public/test/browser_test_utils.h"
38 #include "content/public/test/test_navigation_observer.h"
39 #include "content/test/net/url_request_failed_job.h"
40 #include "content/test/net/url_request_mock_http_job.h"
41 #include "grit/generated_resources.h"
42 #include "net/base/net_errors.h"
43 #include "net/base/net_util.h"
44 #include "net/http/failing_http_transaction_factory.h"
45 #include "net/http/http_cache.h"
46 #include "net/test/spawned_test_server/spawned_test_server.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"
56 using content::BrowserThread
;
57 using content::NavigationController
;
58 using content::URLRequestFailedJob
;
59 using net::URLRequestTestJob
;
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.
66 bool 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);",
74 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
75 browser
->tab_strip_model()->GetActiveWebContents(), command
, &result
));
79 // Expands the more box on the currently displayed error page.
80 void ToggleHelpBox(Browser
* browser
) {
81 EXPECT_TRUE(content::ExecuteScript(
82 browser
->tab_strip_model()->GetActiveWebContents(),
83 "document.getElementById('more-less-button').click();"));
86 // Returns true if |browser| is displaying the text representation of
87 // |error_code| on the current page.
88 bool WARN_UNUSED_RESULT
IsDisplayingNetError(Browser
* browser
,
89 net::Error error_code
) {
90 // Get the error as a string, and remove the leading "net::", which is not
91 // included on error pages.
92 std::string
error_string(net::ErrorToString(error_code
));
93 DCHECK(StartsWithASCII(error_string
, "net::", true));
94 error_string
.erase(0, 5);
96 return IsDisplayingText(browser
, error_string
);
99 // Checks that the local error page is being displayed, without remotely
100 // retrieved navigation corrections, and with the specified error code.
101 void ExpectDisplayingLocalErrorPage(Browser
* browser
, net::Error error_code
) {
102 // Expand the help box so innerText will include text below the fold.
103 ToggleHelpBox(browser
);
105 EXPECT_TRUE(IsDisplayingNetError(browser
, error_code
));
107 // Locally generated error pages should not have navigation corrections.
108 EXPECT_FALSE(IsDisplayingText(browser
, "http://correction1/"));
109 EXPECT_FALSE(IsDisplayingText(browser
, "http://correction2/"));
111 // Locally generated error pages should not have a populated search box.
112 bool search_box_populated
= false;
113 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
114 browser
->tab_strip_model()->GetActiveWebContents(),
115 "var searchText = document.getElementById('search-box').value;"
116 "domAutomationController.send(searchText == 'search query');",
117 &search_box_populated
));
118 EXPECT_FALSE(search_box_populated
);
121 // Checks that an error page with information retrieved from the navigation
122 // correction service is being displayed, with the specified specified error
124 void ExpectDisplayingNavigationCorrections(Browser
* browser
,
125 net::Error error_code
) {
126 // Expand the help box so innerText will include text below the fold.
127 ToggleHelpBox(browser
);
129 EXPECT_TRUE(IsDisplayingNetError(browser
, error_code
));
131 // Check that the mock navigation corrections are displayed.
132 EXPECT_TRUE(IsDisplayingText(browser
, "http://correction1/"));
133 EXPECT_TRUE(IsDisplayingText(browser
, "http://correction2/"));
135 // Check that the search box is populated correctly.
136 bool search_box_populated
= false;
137 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
138 browser
->tab_strip_model()->GetActiveWebContents(),
139 "var searchText = document.getElementById('search-box').value;"
140 "domAutomationController.send(searchText == 'search query');",
141 &search_box_populated
));
142 EXPECT_TRUE(search_box_populated
);
145 std::string
GetLoadStaleButtonLabel() {
146 return l10n_util::GetStringUTF8(IDS_ERRORPAGES_BUTTON_LOAD_STALE
);
149 void AddInterceptorForURL(
151 scoped_ptr
<net::URLRequestInterceptor
> handler
) {
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
153 net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
154 url
, handler
.Pass());
157 // An interceptor that fails a configurable number of requests, then succeeds
158 // all requests after that, keeping count of failures and successes.
159 class FailFirstNRequestsInterceptor
: public net::URLRequestInterceptor
{
161 explicit FailFirstNRequestsInterceptor(int requests_to_fail
)
162 : requests_(0), failures_(0), requests_to_fail_(requests_to_fail
) {}
163 virtual ~FailFirstNRequestsInterceptor() {}
165 // net::URLRequestInterceptor implementation
166 virtual net::URLRequestJob
* MaybeInterceptRequest(
167 net::URLRequest
* request
,
168 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
171 if (failures_
< requests_to_fail_
) {
173 // Note: net::ERR_CONNECTION_RESET does not summon the Link Doctor; see
174 // NetErrorHelperCore::GetErrorPageURL.
175 return new URLRequestFailedJob(request
,
177 net::ERR_CONNECTION_RESET
);
179 return new URLRequestTestJob(request
, network_delegate
,
180 URLRequestTestJob::test_headers(),
181 URLRequestTestJob::test_data_1(),
186 int requests() const { return requests_
; }
187 int failures() const { return failures_
; }
190 // These are mutable because MaybeCreateJob is const but we want this state
192 mutable int requests_
;
193 mutable int failures_
;
194 int requests_to_fail_
;
196 DISALLOW_COPY_AND_ASSIGN(FailFirstNRequestsInterceptor
);
199 // An interceptor that serves LinkDoctor responses. It also allows waiting
200 // until a certain number of requests have been sent.
201 // TODO(mmenke): Wait until responses have been received instead.
202 class LinkDoctorInterceptor
: public net::URLRequestInterceptor
{
204 LinkDoctorInterceptor() : num_requests_(0),
205 requests_to_wait_for_(-1),
206 weak_factory_(this) {
209 virtual ~LinkDoctorInterceptor() {}
211 // net::URLRequestInterceptor implementation
212 virtual net::URLRequestJob
* MaybeInterceptRequest(
213 net::URLRequest
* request
,
214 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
217 BrowserThread::PostTask(
218 BrowserThread::UI
, FROM_HERE
,
219 base::Bind(&LinkDoctorInterceptor::RequestCreated
,
220 weak_factory_
.GetWeakPtr()));
222 base::FilePath root_http
;
223 PathService::Get(chrome::DIR_TEST_DATA
, &root_http
);
224 return new content::URLRequestMockHTTPJob(
225 request
, network_delegate
,
226 root_http
.AppendASCII("mock-link-doctor.json"));
229 void WaitForRequests(int requests_to_wait_for
) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
231 DCHECK_EQ(-1, requests_to_wait_for_
);
234 if (requests_to_wait_for
>= num_requests_
)
237 requests_to_wait_for_
= requests_to_wait_for
;
238 run_loop_
.reset(new base::RunLoop());
241 requests_to_wait_for_
= -1;
242 EXPECT_EQ(num_requests_
, requests_to_wait_for
);
245 // It is up to the caller to wait until all relevant requests has been
246 // created, either through calling WaitForRequests or some other manner,
247 // before calling this method.
248 int num_requests() const {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
250 return num_requests_
;
254 void RequestCreated() {
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
258 if (num_requests_
== requests_to_wait_for_
)
262 // These are only used on the UI thread.
264 int requests_to_wait_for_
;
265 scoped_ptr
<base::RunLoop
> run_loop_
;
267 // This prevents any risk of flake if any test doesn't wait for a request
268 // it sent. Mutable so it can be accessed from a const function.
269 mutable base::WeakPtrFactory
<LinkDoctorInterceptor
> weak_factory_
;
271 DISALLOW_COPY_AND_ASSIGN(LinkDoctorInterceptor
);
274 void InstallMockInterceptors(
275 const GURL
& search_url
,
276 scoped_ptr
<net::URLRequestInterceptor
> link_doctor_interceptor
) {
277 chrome_browser_net::SetUrlRequestMocksEnabled(true);
279 AddInterceptorForURL(google_util::LinkDoctorBaseURL(),
280 link_doctor_interceptor
.Pass());
282 // Add a mock for the search engine the error page will use.
283 base::FilePath root_http
;
284 PathService::Get(chrome::DIR_TEST_DATA
, &root_http
);
285 content::URLRequestMockHTTPJob::AddHostnameToFileHandler(
286 search_url
.host(), root_http
.AppendASCII("title3.html"));
289 class ErrorPageTest
: public InProcessBrowserTest
{
291 enum HistoryNavigationDirection
{
292 HISTORY_NAVIGATE_BACK
,
293 HISTORY_NAVIGATE_FORWARD
,
296 ErrorPageTest() : link_doctor_interceptor_(NULL
) {}
297 virtual ~ErrorPageTest() {}
299 // Navigates the active tab to a mock url created for the file at |file_path|.
300 // Needed for StaleCacheStatus and StaleCacheStatusFailedCorrections tests.
301 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
302 command_line
->AppendSwitch(switches::kEnableOfflineLoadStaleCache
);
305 // Navigates the active tab to a mock url created for the file at |file_path|.
306 void NavigateToFileURL(const base::FilePath::StringType
& file_path
) {
307 ui_test_utils::NavigateToURL(
309 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path
)));
312 // Navigates to the given URL and waits for |num_navigations| to occur, and
313 // the title to change to |expected_title|.
314 void NavigateToURLAndWaitForTitle(const GURL
& url
,
315 const std::string
& expected_title
,
316 int num_navigations
) {
317 content::TitleWatcher
title_watcher(
318 browser()->tab_strip_model()->GetActiveWebContents(),
319 base::ASCIIToUTF16(expected_title
));
321 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
322 browser(), url
, num_navigations
);
324 EXPECT_EQ(base::ASCIIToUTF16(expected_title
),
325 title_watcher
.WaitAndGetTitle());
328 // Navigates back in the history and waits for |num_navigations| to occur, and
329 // the title to change to |expected_title|.
330 void GoBackAndWaitForTitle(const std::string
& expected_title
,
331 int num_navigations
) {
332 NavigateHistoryAndWaitForTitle(expected_title
,
334 HISTORY_NAVIGATE_BACK
);
337 // Navigates forward in the history and waits for |num_navigations| to occur,
338 // and the title to change to |expected_title|.
339 void GoForwardAndWaitForTitle(const std::string
& expected_title
,
340 int num_navigations
) {
341 NavigateHistoryAndWaitForTitle(expected_title
,
343 HISTORY_NAVIGATE_FORWARD
);
346 void GoBackAndWaitForNavigations(int num_navigations
) {
347 NavigateHistory(num_navigations
, HISTORY_NAVIGATE_BACK
);
350 void GoForwardAndWaitForNavigations(int num_navigations
) {
351 NavigateHistory(num_navigations
, HISTORY_NAVIGATE_FORWARD
);
354 // Confirms that the javascript variable indicating whether or not we have
355 // a stale copy in the cache has been set to |expected|, and that the
356 // stale load button is or isn't there based on the same expectation.
357 testing::AssertionResult
ProbeStaleCopyValue(bool expected
) {
358 const char* js_cache_probe
=
360 " domAutomationController.send(\n"
361 " 'staleLoadButton' in templateData ? 'yes' : 'no');\n"
363 " domAutomationController.send(e.message);\n"
368 content::ExecuteScriptAndExtractString(
369 browser()->tab_strip_model()->GetActiveWebContents(),
373 return testing::AssertionFailure()
374 << "Failing return from ExecuteScriptAndExtractString.";
377 if ((expected
&& "yes" == result
) || (!expected
&& "no" == result
))
378 return testing::AssertionSuccess();
380 return testing::AssertionFailure() << "Cache probe result is " << result
;
383 testing::AssertionResult
ReloadStaleCopyFromCache() {
384 const char* js_reload_script
=
386 " document.getElementById('stale-load-button').click();\n"
387 " domAutomationController.send('success');\n"
389 " domAutomationController.send(e.message);\n"
393 bool ret
= content::ExecuteScriptAndExtractString(
394 browser()->tab_strip_model()->GetActiveWebContents(),
399 return testing::AssertionFailure();
400 return ("success" == result
? testing::AssertionSuccess() :
401 (testing::AssertionFailure() << "Exception message is " << result
));
404 LinkDoctorInterceptor
* link_doctor_interceptor() {
405 return link_doctor_interceptor_
;
409 virtual void SetUpOnMainThread() OVERRIDE
{
410 link_doctor_interceptor_
= new LinkDoctorInterceptor();
411 scoped_ptr
<net::URLRequestInterceptor
> owned_interceptor(
412 link_doctor_interceptor_
);
413 // Ownership of the |interceptor_| is passed to an object the IO thread, but
414 // a pointer is kept in the test fixture. As soon as anything calls
415 // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
416 BrowserThread::PostTask(
417 BrowserThread::IO
, FROM_HERE
,
418 base::Bind(&InstallMockInterceptors
,
419 google_util::GetGoogleSearchURL(
420 google_profile_helper::GetGoogleHomePageURL(
421 browser()->profile())),
422 base::Passed(&owned_interceptor
)));
425 // Returns a GURL that results in a DNS error.
426 GURL
GetDnsErrorURL() const {
427 return URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
);
431 // Navigates the browser the indicated direction in the history and waits for
432 // |num_navigations| to occur and the title to change to |expected_title|.
433 void NavigateHistoryAndWaitForTitle(const std::string
& expected_title
,
435 HistoryNavigationDirection direction
) {
436 content::TitleWatcher
title_watcher(
437 browser()->tab_strip_model()->GetActiveWebContents(),
438 base::ASCIIToUTF16(expected_title
));
440 NavigateHistory(num_navigations
, direction
);
442 EXPECT_EQ(title_watcher
.WaitAndGetTitle(),
443 base::ASCIIToUTF16(expected_title
));
446 void NavigateHistory(int num_navigations
,
447 HistoryNavigationDirection direction
) {
448 content::TestNavigationObserver
test_navigation_observer(
449 browser()->tab_strip_model()->GetActiveWebContents(),
451 if (direction
== HISTORY_NAVIGATE_BACK
) {
452 chrome::GoBack(browser(), CURRENT_TAB
);
453 } else if (direction
== HISTORY_NAVIGATE_FORWARD
) {
454 chrome::GoForward(browser(), CURRENT_TAB
);
458 test_navigation_observer
.Wait();
461 LinkDoctorInterceptor
* link_doctor_interceptor_
;
464 class TestFailProvisionalLoadObserver
: public content::WebContentsObserver
{
466 explicit TestFailProvisionalLoadObserver(content::WebContents
* contents
)
467 : content::WebContentsObserver(contents
) {}
468 virtual ~TestFailProvisionalLoadObserver() {}
470 // This method is invoked when the provisional load failed.
471 virtual void DidFailProvisionalLoad(
472 content::RenderFrameHost
* render_frame_host
,
473 const GURL
& validated_url
,
475 const base::string16
& error_description
) OVERRIDE
{
476 fail_url_
= validated_url
;
479 const GURL
& fail_url() const { return fail_url_
; }
484 DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver
);
487 void InterceptNetworkTransactions(net::URLRequestContextGetter
* getter
,
489 DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO
));
490 net::HttpCache
* cache(
491 getter
->GetURLRequestContext()->http_transaction_factory()->GetCache());
493 scoped_ptr
<net::HttpTransactionFactory
> factory(
494 new net::FailingHttpTransactionFactory(cache
->GetSession(), error
));
495 // Throw away old version; since this is a a browser test, we don't
496 // need to restore the old state.
497 cache
->SetHttpNetworkTransactionFactoryForTesting(factory
.Pass());
500 // Test that a DNS error occuring in the main frame redirects to an error page.
501 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_Basic
) {
502 // The first navigation should fail, and the second one should be the error
504 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
505 browser(), GetDnsErrorURL(), 2);
506 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
507 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
510 // Test that a DNS error occuring in the main frame does not result in an
511 // additional session history entry.
512 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack1
) {
513 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
514 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
515 browser(), GetDnsErrorURL(), 2);
516 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
517 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
518 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
521 // Test that a DNS error occuring in the main frame does not result in an
522 // additional session history entry.
523 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2
) {
524 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
526 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
527 browser(), GetDnsErrorURL(), 2);
528 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
529 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
531 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
533 GoBackAndWaitForNavigations(2);
534 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
535 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
537 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
538 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
541 // Test that a DNS error occuring in the main frame does not result in an
542 // additional session history entry.
543 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2AndForward
) {
544 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
546 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
547 browser(), GetDnsErrorURL(), 2);
548 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
549 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
551 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
553 GoBackAndWaitForNavigations(2);
554 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
555 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
557 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
559 GoForwardAndWaitForNavigations(2);
560 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
561 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
564 // Test that a DNS error occuring in the main frame does not result in an
565 // additional session history entry.
566 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2Forward2
) {
567 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
569 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
570 browser(), GetDnsErrorURL(), 2);
571 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
572 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
574 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
576 GoBackAndWaitForNavigations(2);
577 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
578 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
580 GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
582 GoForwardAndWaitForNavigations(2);
583 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
584 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
586 GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
587 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
590 // Test that the search button on a DNS error page works.
591 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_DoSearch
) {
592 // The first navigation should fail, and the second one should be the error
594 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
595 browser(), GetDnsErrorURL(), 2);
596 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
597 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
599 content::WebContents
* web_contents
=
600 browser()->tab_strip_model()->GetActiveWebContents();
602 // Do a search and make sure the browser ends up at the right page.
603 content::TestNavigationObserver
nav_observer(web_contents
, 1);
604 content::TitleWatcher
title_watcher(
606 base::ASCIIToUTF16("Title Of More Awesomeness"));
607 // Can't use content::ExecuteScript because it waits for scripts to send
608 // notification that they've run, and scripts that trigger a navigation may
609 // not send that notification.
610 web_contents
->GetMainFrame()->ExecuteJavaScript(
611 base::ASCIIToUTF16("document.getElementById('search-button').click();"));
613 EXPECT_EQ(base::ASCIIToUTF16("Title Of More Awesomeness"),
614 title_watcher
.WaitAndGetTitle());
616 // There should have been another Link Doctor request, for tracking purposes.
617 // Have to wait for it, since the search page does not depend on having
618 // sent the tracking request.
619 link_doctor_interceptor()->WaitForRequests(2);
620 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
622 // Check the path and query string.
624 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
625 browser()->tab_strip_model()->GetActiveWebContents(),
626 "domAutomationController.send(window.location.href);",
628 EXPECT_EQ("/search", GURL(url
).path());
629 EXPECT_EQ("q=search%20query", GURL(url
).query());
631 // Go back to the error page, to make sure the history is correct.
632 GoBackAndWaitForNavigations(2);
633 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
634 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
637 // Test that the reload button on a DNS error page works.
638 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_DoReload
) {
639 // The first navigation should fail, and the second one should be the error
641 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
642 browser(), GetDnsErrorURL(), 2);
643 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
644 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
646 content::WebContents
* web_contents
=
647 browser()->tab_strip_model()->GetActiveWebContents();
649 // Clicking the reload button should load the error page again, and there
650 // should be two commits, as before.
651 content::TestNavigationObserver
nav_observer(web_contents
, 2);
652 // Can't use content::ExecuteScript because it waits for scripts to send
653 // notification that they've run, and scripts that trigger a navigation may
654 // not send that notification.
655 web_contents
->GetMainFrame()->ExecuteJavaScript(
656 base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
658 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
660 // There should have two more requests to the correction service: One for the
661 // new error page, and one for tracking purposes. Have to make sure to wait
662 // for the tracking request, since the new error page does not depend on it.
663 link_doctor_interceptor()->WaitForRequests(3);
664 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
667 // Test that clicking links on a DNS error page works.
668 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_DoClickLink
) {
669 // The first navigation should fail, and the second one should be the error
671 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
672 browser(), GetDnsErrorURL(), 2);
673 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
674 EXPECT_EQ(1, link_doctor_interceptor()->num_requests());
676 content::WebContents
* web_contents
=
677 browser()->tab_strip_model()->GetActiveWebContents();
679 // Simulate a click on a link.
681 content::TitleWatcher
title_watcher(
683 base::ASCIIToUTF16("Title Of Awesomeness"));
684 std::string link_selector
=
685 "document.querySelector('a[href=\"http://mock.http/title2.html\"]')";
686 // The tracking request is triggered by onmousedown, so it catches middle
687 // mouse button clicks, as well as left clicks.
688 web_contents
->GetMainFrame()->ExecuteJavaScript(
689 base::ASCIIToUTF16(link_selector
+ ".onmousedown();"));
690 // Can't use content::ExecuteScript because it waits for scripts to send
691 // notification that they've run, and scripts that trigger a navigation may
692 // not send that notification.
693 web_contents
->GetMainFrame()->ExecuteJavaScript(
694 base::ASCIIToUTF16(link_selector
+ ".click();"));
695 EXPECT_EQ(base::ASCIIToUTF16("Title Of Awesomeness"),
696 title_watcher
.WaitAndGetTitle());
698 // There should have been a tracking request to the correction service. Have
699 // to make sure to wait the tracking request, since the new page does not
701 link_doctor_interceptor()->WaitForRequests(2);
702 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
705 // Test that a DNS error occuring in an iframe does not result in showing
706 // navigation corrections.
707 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, IFrameDNSError_Basic
) {
708 NavigateToURLAndWaitForTitle(
709 content::URLRequestMockHTTPJob::GetMockUrl(
710 base::FilePath(FILE_PATH_LITERAL("iframe_dns_error.html"))),
713 // We expect to have two history entries, since we started off with navigation
714 // to "about:blank" and then navigated to "iframe_dns_error.html".
716 browser()->tab_strip_model()->GetActiveWebContents()->
717 GetController().GetEntryCount());
718 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
721 // This test fails regularly on win_rel trybots. See crbug.com/121540
723 #define MAYBE_IFrameDNSError_GoBack DISABLED_IFrameDNSError_GoBack
725 #define MAYBE_IFrameDNSError_GoBack IFrameDNSError_GoBack
727 // Test that a DNS error occuring in an iframe does not result in an
728 // additional session history entry.
729 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, MAYBE_IFrameDNSError_GoBack
) {
730 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
731 NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
732 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
733 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
736 // This test fails regularly on win_rel trybots. See crbug.com/121540
738 // This fails on linux_aura bringup: http://crbug.com/163931
739 #if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA))
740 #define MAYBE_IFrameDNSError_GoBackAndForward DISABLED_IFrameDNSError_GoBackAndForward
742 #define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
744 // Test that a DNS error occuring in an iframe does not result in an
745 // additional session history entry.
746 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, MAYBE_IFrameDNSError_GoBackAndForward
) {
747 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
748 NavigateToFileURL(FILE_PATH_LITERAL("iframe_dns_error.html"));
749 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
750 GoForwardAndWaitForTitle("Blah", 1);
751 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
754 // Test that a DNS error occuring in an iframe, once the main document is
755 // completed loading, does not result in an additional session history entry.
756 // To ensure that the main document has completed loading, JavaScript is used to
757 // inject an iframe after loading is done.
758 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, IFrameDNSError_JavaScript
) {
759 content::WebContents
* wc
=
760 browser()->tab_strip_model()->GetActiveWebContents();
762 URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
);
764 // Load a regular web page, in which we will inject an iframe.
765 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
767 // We expect to have two history entries, since we started off with navigation
768 // to "about:blank" and then navigated to "title2.html".
769 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
771 std::string script
= "var frame = document.createElement('iframe');"
772 "frame.src = '" + fail_url
.spec() + "';"
773 "document.body.appendChild(frame);";
775 TestFailProvisionalLoadObserver
fail_observer(wc
);
776 content::WindowedNotificationObserver
load_observer(
777 content::NOTIFICATION_LOAD_STOP
,
778 content::Source
<NavigationController
>(&wc
->GetController()));
779 wc
->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script
));
780 load_observer
.Wait();
782 // Ensure we saw the expected failure.
783 EXPECT_EQ(fail_url
, fail_observer
.fail_url());
785 // Failed initial navigation of an iframe shouldn't be adding any history
787 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
790 // Do the same test, but with an iframe that doesn't have initial URL
792 script
= "var frame = document.createElement('iframe');"
793 "frame.id = 'target_frame';"
794 "document.body.appendChild(frame);";
796 content::WindowedNotificationObserver
load_observer(
797 content::NOTIFICATION_LOAD_STOP
,
798 content::Source
<NavigationController
>(&wc
->GetController()));
799 wc
->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script
));
800 load_observer
.Wait();
803 script
= "var f = document.getElementById('target_frame');"
804 "f.src = '" + fail_url
.spec() + "';";
806 TestFailProvisionalLoadObserver
fail_observer(wc
);
807 content::WindowedNotificationObserver
load_observer(
808 content::NOTIFICATION_LOAD_STOP
,
809 content::Source
<NavigationController
>(&wc
->GetController()));
810 wc
->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script
));
811 load_observer
.Wait();
813 EXPECT_EQ(fail_url
, fail_observer
.fail_url());
814 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
816 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
819 // Checks that navigation corrections are not loaded when we receive an actual
821 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, Page404
) {
822 NavigateToURLAndWaitForTitle(
823 content::URLRequestMockHTTPJob::GetMockUrl(
824 base::FilePath(FILE_PATH_LITERAL("page404.html"))),
827 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
830 // Checks that when an error occurs, the stale cache status of the page
831 // is correctly transferred, and that stale cached copied can be loaded
832 // from the javascript.
833 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, StaleCacheStatus
) {
834 ASSERT_TRUE(test_server()->Start());
835 // Load cache with entry with "nocache" set, to create stale
837 GURL
test_url(test_server()->GetURL("files/nocache.html"));
838 NavigateToURLAndWaitForTitle(test_url
, "Nocache Test Page", 1);
840 // Reload same URL after forcing an error from the the network layer;
841 // confirm that the error page is told the cached copy exists.
842 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
=
843 browser()->profile()->GetRequestContext();
844 BrowserThread::PostTask(
845 BrowserThread::IO
, FROM_HERE
,
846 base::Bind(&InterceptNetworkTransactions
, url_request_context_getter
,
849 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
850 // With no navigation corrections to load, there's only one navigation.
851 browser(), test_url
, 1);
852 EXPECT_TRUE(ProbeStaleCopyValue(true));
853 EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
854 EXPECT_NE(base::ASCIIToUTF16("Nocache Test Page"),
855 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
857 // Confirm that loading the stale copy from the cache works.
858 content::TestNavigationObserver
same_tab_observer(
859 browser()->tab_strip_model()->GetActiveWebContents(), 1);
860 ASSERT_TRUE(ReloadStaleCopyFromCache());
861 same_tab_observer
.Wait();
862 EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
863 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
865 // Clear the cache and reload the same URL; confirm the error page is told
866 // that there is no cached copy.
867 BrowsingDataRemover
* remover
=
868 BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
869 remover
->Remove(BrowsingDataRemover::REMOVE_CACHE
,
870 BrowsingDataHelper::UNPROTECTED_WEB
);
871 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
872 browser(), test_url
, 1);
873 EXPECT_TRUE(ProbeStaleCopyValue(false));
874 EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
875 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
878 class ErrorPageAutoReloadTest
: public InProcessBrowserTest
{
880 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
881 command_line
->AppendSwitch(switches::kEnableOfflineAutoReload
);
884 void InstallInterceptor(const GURL
& url
, int requests_to_fail
) {
885 interceptor_
= new FailFirstNRequestsInterceptor(requests_to_fail
);
886 scoped_ptr
<net::URLRequestInterceptor
> owned_interceptor(interceptor_
);
888 // Tests don't need to wait for this task to complete before using the
889 // filter; any requests that might be affected by it will end up in the IO
890 // thread's message loop after this posted task anyway.
892 // Ownership of the interceptor is passed to an object the IO thread, but a
893 // pointer is kept in the test fixture. As soon as anything calls
894 // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
895 BrowserThread::PostTask(
896 BrowserThread::IO
, FROM_HERE
,
897 base::Bind(&AddInterceptorForURL
, url
,
898 base::Passed(&owned_interceptor
)));
901 void NavigateToURLAndWaitForTitle(const GURL
& url
,
902 const std::string
& expected_title
,
903 int num_navigations
) {
904 content::TitleWatcher
title_watcher(
905 browser()->tab_strip_model()->GetActiveWebContents(),
906 base::ASCIIToUTF16(expected_title
));
908 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
909 browser(), url
, num_navigations
);
911 EXPECT_EQ(base::ASCIIToUTF16(expected_title
),
912 title_watcher
.WaitAndGetTitle());
915 FailFirstNRequestsInterceptor
* interceptor() {
920 FailFirstNRequestsInterceptor
* interceptor_
;
923 IN_PROC_BROWSER_TEST_F(ErrorPageAutoReloadTest
, AutoReload
) {
924 GURL
test_url("http://error.page.auto.reload");
925 const int kRequestsToFail
= 2;
926 InstallInterceptor(test_url
, kRequestsToFail
);
927 NavigateToURLAndWaitForTitle(test_url
, "Test One", kRequestsToFail
+ 1);
928 // Note that the interceptor updates these variables on the IO thread,
929 // but this function reads them on the main thread. The requests have to be
930 // created (on the IO thread) before NavigateToURLAndWaitForTitle returns or
931 // this becomes racey.
932 EXPECT_EQ(kRequestsToFail
, interceptor()->failures());
933 EXPECT_EQ(kRequestsToFail
+ 1, interceptor()->requests());
936 // Interceptor that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
937 class AddressUnreachableInterceptor
: public net::URLRequestInterceptor
{
939 AddressUnreachableInterceptor() {}
940 virtual ~AddressUnreachableInterceptor() {}
942 // net::URLRequestInterceptor:
943 virtual net::URLRequestJob
* MaybeInterceptRequest(
944 net::URLRequest
* request
,
945 net::NetworkDelegate
* network_delegate
) const OVERRIDE
{
946 return new URLRequestFailedJob(request
,
948 net::ERR_ADDRESS_UNREACHABLE
);
952 DISALLOW_COPY_AND_ASSIGN(AddressUnreachableInterceptor
);
955 // A test fixture that returns ERR_ADDRESS_UNREACHABLE for all navigation
956 // correction requests. ERR_NAME_NOT_RESOLVED is more typical, but need to use
957 // a different error for the correction service and the original page to
958 // validate the right page is being displayed.
959 class ErrorPageNavigationCorrectionsFailTest
: public ErrorPageTest
{
961 // InProcessBrowserTest:
962 virtual void SetUpOnMainThread() OVERRIDE
{
963 BrowserThread::PostTask(
964 BrowserThread::IO
, FROM_HERE
,
965 base::Bind(&ErrorPageNavigationCorrectionsFailTest::AddFilters
));
968 virtual void TearDownOnMainThread() OVERRIDE
{
969 BrowserThread::PostTask(
970 BrowserThread::IO
, FROM_HERE
,
971 base::Bind(&ErrorPageNavigationCorrectionsFailTest::RemoveFilters
));
975 // Adds a filter that causes all correction service requests to fail with
976 // ERR_ADDRESS_UNREACHABLE.
978 // Also adds the content::URLRequestFailedJob filter.
979 static void AddFilters() {
980 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
981 content::URLRequestFailedJob::AddUrlHandler();
983 net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
984 google_util::LinkDoctorBaseURL(),
985 scoped_ptr
<net::URLRequestInterceptor
>(
986 new AddressUnreachableInterceptor()));
989 static void RemoveFilters() {
990 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
991 net::URLRequestFilter::GetInstance()->ClearHandlers();
995 // Make sure that when corrections fail to load, the network error page is
996 // successfully loaded.
997 IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest
,
998 FetchCorrectionsFails
) {
999 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1001 URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
),
1004 // Verify that the expected error page is being displayed.
1005 ExpectDisplayingLocalErrorPage(browser(), net::ERR_NAME_NOT_RESOLVED
);
1008 // Checks that when an error occurs and a corrections fail to load, the stale
1009 // cache status of the page is correctly transferred, and we can load the
1010 // stale copy from the javascript. Most logic copied from StaleCacheStatus
1012 IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest
,
1013 StaleCacheStatusFailedCorrections
) {
1014 ASSERT_TRUE(test_server()->Start());
1015 // Load cache with entry with "nocache" set, to create stale
1017 GURL
test_url(test_server()->GetURL("files/nocache.html"));
1018 NavigateToURLAndWaitForTitle(test_url
, "Nocache Test Page", 1);
1020 // Reload same URL after forcing an error from the the network layer;
1021 // confirm that the error page is told the cached copy exists.
1022 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
=
1023 browser()->profile()->GetRequestContext();
1024 BrowserThread::PostTask(
1025 BrowserThread::IO
, FROM_HERE
,
1026 base::Bind(&InterceptNetworkTransactions
, url_request_context_getter
,
1027 net::ERR_CONNECTION_FAILED
));
1029 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1030 browser(), test_url
, 2);
1031 EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
1032 EXPECT_TRUE(ProbeStaleCopyValue(true));
1034 // Confirm that loading the stale copy from the cache works.
1035 content::TestNavigationObserver
same_tab_observer(
1036 browser()->tab_strip_model()->GetActiveWebContents(), 1);
1037 ASSERT_TRUE(ReloadStaleCopyFromCache());
1038 same_tab_observer
.Wait();
1039 EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
1040 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
1042 // Clear the cache and reload the same URL; confirm the error page is told
1043 // that there is no cached copy.
1044 BrowsingDataRemover
* remover
=
1045 BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
1046 remover
->Remove(BrowsingDataRemover::REMOVE_CACHE
,
1047 BrowsingDataHelper::UNPROTECTED_WEB
);
1048 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1049 browser(), test_url
, 2);
1050 EXPECT_TRUE(ProbeStaleCopyValue(false));
1051 EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
1054 // A test fixture that simulates failing requests for an IDN domain name.
1055 class ErrorPageForIDNTest
: public InProcessBrowserTest
{
1057 // Target hostname in different forms.
1058 static const char kHostname
[];
1059 static const char kHostnameJSUnicode
[];
1061 // InProcessBrowserTest:
1062 virtual void SetUpOnMainThread() OVERRIDE
{
1063 // Clear AcceptLanguages to force punycode decoding.
1064 browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages
,
1066 BrowserThread::PostTask(
1067 BrowserThread::IO
, FROM_HERE
,
1068 base::Bind(&ErrorPageForIDNTest::AddFilters
));
1071 virtual void TearDownOnMainThread() OVERRIDE
{
1072 BrowserThread::PostTask(
1073 BrowserThread::IO
, FROM_HERE
,
1074 base::Bind(&ErrorPageForIDNTest::RemoveFilters
));
1078 static void AddFilters() {
1079 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1080 content::URLRequestFailedJob::AddUrlHandlerForHostname(kHostname
);
1083 static void RemoveFilters() {
1084 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1085 net::URLRequestFilter::GetInstance()->ClearHandlers();
1089 const char ErrorPageForIDNTest::kHostname
[] =
1090 "xn--d1abbgf6aiiy.xn--p1ai";
1091 const char ErrorPageForIDNTest::kHostnameJSUnicode
[] =
1092 "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442."
1095 // Make sure error page shows correct unicode for IDN.
1096 IN_PROC_BROWSER_TEST_F(ErrorPageForIDNTest
, IDN
) {
1097 // ERR_UNSAFE_PORT will not trigger navigation corrections.
1098 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1100 URLRequestFailedJob::GetMockHttpUrlForHostname(net::ERR_UNSAFE_PORT
,
1104 ToggleHelpBox(browser());
1105 EXPECT_TRUE(IsDisplayingText(browser(), kHostnameJSUnicode
));