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/net/url_request_mock_util.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/search_engines/ui_thread_search_terms_data.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"
56 using content::BrowserThread
;
57 using content::NavigationController
;
58 using net::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('details-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 return IsDisplayingText(browser
, net::ErrorToShortString(error_code
));
93 // Checks that the local error page is being displayed, without remotely
94 // retrieved navigation corrections, and with the specified error code.
95 void ExpectDisplayingLocalErrorPage(Browser
* browser
, net::Error error_code
) {
96 // Expand the help box so innerText will include text below the fold.
97 ToggleHelpBox(browser
);
99 EXPECT_TRUE(IsDisplayingNetError(browser
, error_code
));
101 // Locally generated error pages should not have navigation corrections.
102 EXPECT_FALSE(IsDisplayingText(browser
, "http://correction1/"));
103 EXPECT_FALSE(IsDisplayingText(browser
, "http://correction2/"));
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
);
115 // Checks that an error page with information retrieved from the navigation
116 // correction service is being displayed, with the specified specified error
118 void ExpectDisplayingNavigationCorrections(Browser
* browser
,
119 net::Error error_code
) {
120 // Expand the help box so innerText will include text below the fold.
121 ToggleHelpBox(browser
);
123 EXPECT_TRUE(IsDisplayingNetError(browser
, error_code
));
125 // Check that the mock navigation corrections are displayed.
126 EXPECT_TRUE(IsDisplayingText(browser
, "http://correction1/"));
127 EXPECT_TRUE(IsDisplayingText(browser
, "http://correction2/"));
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
);
139 std::string
GetLoadStaleButtonLabel() {
140 return l10n_util::GetStringUTF8(IDS_ERRORPAGES_BUTTON_LOAD_STALE
);
143 void AddInterceptorForURL(
145 scoped_ptr
<net::URLRequestInterceptor
> handler
) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
147 net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
148 url
, handler
.Pass());
151 // An interceptor that fails a configurable number of requests, then succeeds
152 // all requests after that, keeping count of failures and successes.
153 class FailFirstNRequestsInterceptor
: public net::URLRequestInterceptor
{
155 explicit FailFirstNRequestsInterceptor(int requests_to_fail
)
156 : requests_(0), failures_(0), requests_to_fail_(requests_to_fail
) {}
157 ~FailFirstNRequestsInterceptor() override
{}
159 // net::URLRequestInterceptor implementation
160 net::URLRequestJob
* MaybeInterceptRequest(
161 net::URLRequest
* request
,
162 net::NetworkDelegate
* network_delegate
) const override
{
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
165 if (failures_
< requests_to_fail_
) {
167 // Note: net::ERR_CONNECTION_RESET does not summon the Link Doctor; see
168 // NetErrorHelperCore::GetErrorPageURL.
169 return new URLRequestFailedJob(request
,
171 net::ERR_CONNECTION_RESET
);
173 return new URLRequestTestJob(request
, network_delegate
,
174 URLRequestTestJob::test_headers(),
175 URLRequestTestJob::test_data_1(),
180 int requests() const { return requests_
; }
181 int failures() const { return failures_
; }
184 // These are mutable because MaybeCreateJob is const but we want this state
186 mutable int requests_
;
187 mutable int failures_
;
188 int requests_to_fail_
;
190 DISALLOW_COPY_AND_ASSIGN(FailFirstNRequestsInterceptor
);
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.
196 class LinkDoctorInterceptor
: public net::URLRequestInterceptor
{
198 LinkDoctorInterceptor() : num_requests_(0),
199 requests_to_wait_for_(-1),
200 weak_factory_(this) {
203 ~LinkDoctorInterceptor() override
{}
205 // net::URLRequestInterceptor implementation
206 net::URLRequestJob
* MaybeInterceptRequest(
207 net::URLRequest
* request
,
208 net::NetworkDelegate
* network_delegate
) const override
{
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
211 BrowserThread::PostTask(
212 BrowserThread::UI
, FROM_HERE
,
213 base::Bind(&LinkDoctorInterceptor::RequestCreated
,
214 weak_factory_
.GetWeakPtr()));
216 base::FilePath root_http
;
217 PathService::Get(chrome::DIR_TEST_DATA
, &root_http
);
218 return new net::URLRequestMockHTTPJob(
221 root_http
.AppendASCII("mock-link-doctor.json"),
222 BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
223 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN
));
226 void WaitForRequests(int requests_to_wait_for
) {
227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
228 DCHECK_EQ(-1, requests_to_wait_for_
);
231 if (requests_to_wait_for
>= num_requests_
)
234 requests_to_wait_for_
= requests_to_wait_for
;
235 run_loop_
.reset(new base::RunLoop());
238 requests_to_wait_for_
= -1;
239 EXPECT_EQ(num_requests_
, requests_to_wait_for
);
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_
;
251 void RequestCreated() {
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
255 if (num_requests_
== requests_to_wait_for_
)
259 // These are only used on the UI thread.
261 int requests_to_wait_for_
;
262 scoped_ptr
<base::RunLoop
> run_loop_
;
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_
;
268 DISALLOW_COPY_AND_ASSIGN(LinkDoctorInterceptor
);
271 void InstallMockInterceptors(
272 const GURL
& search_url
,
273 scoped_ptr
<net::URLRequestInterceptor
> link_doctor_interceptor
) {
274 chrome_browser_net::SetUrlRequestMocksEnabled(true);
276 AddInterceptorForURL(google_util::LinkDoctorBaseURL(),
277 link_doctor_interceptor
.Pass());
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::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
283 search_url
.scheme(), search_url
.host(),
284 net::URLRequestMockHTTPJob::CreateInterceptorForSingleFile(
285 root_http
.AppendASCII("title3.html"),
286 BrowserThread::GetBlockingPool()));
289 class ErrorPageTest
: public InProcessBrowserTest
{
291 enum HistoryNavigationDirection
{
292 HISTORY_NAVIGATE_BACK
,
293 HISTORY_NAVIGATE_FORWARD
,
296 ErrorPageTest() : link_doctor_interceptor_(NULL
) {}
297 ~ErrorPageTest() override
{}
299 // Navigates the active tab to a mock url created for the file at |file_path|.
300 // Needed for StaleCacheStatus and StaleCacheStatusFailedCorrections tests.
301 void SetUpCommandLine(base::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 net::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 " loadTimeData.valueExists('staleLoadButton') ? '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 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 UIThreadSearchTermsData
search_terms_data(browser()->profile());
417 BrowserThread::PostTask(
418 BrowserThread::IO
, FROM_HERE
,
419 base::Bind(&InstallMockInterceptors
,
420 GURL(search_terms_data
.GoogleBaseURLValue()),
421 base::Passed(&owned_interceptor
)));
424 // Returns a GURL that results in a DNS error.
425 GURL
GetDnsErrorURL() const {
426 return URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
);
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
,
434 HistoryNavigationDirection direction
) {
435 content::TitleWatcher
title_watcher(
436 browser()->tab_strip_model()->GetActiveWebContents(),
437 base::ASCIIToUTF16(expected_title
));
439 NavigateHistory(num_navigations
, direction
);
441 EXPECT_EQ(title_watcher
.WaitAndGetTitle(),
442 base::ASCIIToUTF16(expected_title
));
445 void NavigateHistory(int num_navigations
,
446 HistoryNavigationDirection direction
) {
447 content::TestNavigationObserver
test_navigation_observer(
448 browser()->tab_strip_model()->GetActiveWebContents(),
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
);
457 test_navigation_observer
.Wait();
460 LinkDoctorInterceptor
* link_doctor_interceptor_
;
463 class TestFailProvisionalLoadObserver
: public content::WebContentsObserver
{
465 explicit TestFailProvisionalLoadObserver(content::WebContents
* contents
)
466 : content::WebContentsObserver(contents
) {}
467 ~TestFailProvisionalLoadObserver() override
{}
469 // This method is invoked when the provisional load failed.
470 void DidFailProvisionalLoad(
471 content::RenderFrameHost
* render_frame_host
,
472 const GURL
& validated_url
,
474 const base::string16
& error_description
) override
{
475 fail_url_
= validated_url
;
478 const GURL
& fail_url() const { return fail_url_
; }
483 DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver
);
486 void InterceptNetworkTransactions(net::URLRequestContextGetter
* getter
,
488 DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::IO
));
489 net::HttpCache
* cache(
490 getter
->GetURLRequestContext()->http_transaction_factory()->GetCache());
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());
499 // Test that a DNS error occuring in the main frame redirects to an error page.
500 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_Basic
) {
501 // The first navigation should fail, and the second one should be the error
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());
509 // Test that a DNS error occuring in the main frame does not result in an
510 // additional session history entry.
511 IN_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());
520 // Test that a DNS error occuring in the main frame does not result in an
521 // additional session history entry.
522 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2
) {
523 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
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());
530 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
532 GoBackAndWaitForNavigations(2);
533 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
534 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
536 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
537 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
540 // Test that a DNS error occuring in the main frame does not result in an
541 // additional session history entry.
542 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2AndForward
) {
543 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
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());
550 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
552 GoBackAndWaitForNavigations(2);
553 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
554 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
556 GoBackAndWaitForTitle("Title Of Awesomeness", 1);
558 GoForwardAndWaitForNavigations(2);
559 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
560 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
563 // Test that a DNS error occuring in the main frame does not result in an
564 // additional session history entry.
565 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_GoBack2Forward2
) {
566 NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
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());
573 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
575 GoBackAndWaitForNavigations(2);
576 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
577 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
579 GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
581 GoForwardAndWaitForNavigations(2);
582 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
583 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
585 GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
586 EXPECT_EQ(3, link_doctor_interceptor()->num_requests());
589 // Test that the search button on a DNS error page works.
590 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_DoSearch
) {
591 // The first navigation should fail, and the second one should be the error
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());
598 content::WebContents
* web_contents
=
599 browser()->tab_strip_model()->GetActiveWebContents();
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(
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();"));
612 EXPECT_EQ(base::ASCIIToUTF16("Title Of More Awesomeness"),
613 title_watcher
.WaitAndGetTitle());
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());
621 // Check the path and query string.
623 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
624 browser()->tab_strip_model()->GetActiveWebContents(),
625 "domAutomationController.send(window.location.href);",
627 EXPECT_EQ("/search", GURL(url
).path());
628 EXPECT_EQ("q=search%20query", GURL(url
).query());
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());
636 // Test that the reload button on a DNS error page works.
637 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_DoReload
) {
638 // The first navigation should fail, and the second one should be the error
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());
645 content::WebContents
* web_contents
=
646 browser()->tab_strip_model()->GetActiveWebContents();
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();"));
657 ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED
);
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());
666 // Test that clicking links on a DNS error page works.
667 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, DNSError_DoClickLink
) {
668 // The first navigation should fail, and the second one should be the error
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());
675 content::WebContents
* web_contents
=
676 browser()->tab_strip_model()->GetActiveWebContents();
678 // Simulate a click on a link.
680 content::TitleWatcher
title_watcher(
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());
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
700 link_doctor_interceptor()->WaitForRequests(2);
701 EXPECT_EQ(2, link_doctor_interceptor()->num_requests());
704 // Test that a DNS error occuring in an iframe does not result in showing
705 // navigation corrections.
706 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, IFrameDNSError_Basic
) {
707 NavigateToURLAndWaitForTitle(
708 net::URLRequestMockHTTPJob::GetMockUrl(
709 base::FilePath(FILE_PATH_LITERAL("iframe_dns_error.html"))),
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".
715 browser()->tab_strip_model()->GetActiveWebContents()->
716 GetController().GetEntryCount());
717 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
720 // This test fails regularly on win_rel trybots. See crbug.com/121540
722 #define MAYBE_IFrameDNSError_GoBack DISABLED_IFrameDNSError_GoBack
724 #define MAYBE_IFrameDNSError_GoBack IFrameDNSError_GoBack
726 // Test that a DNS error occuring in an iframe does not result in an
727 // additional session history entry.
728 IN_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());
735 // This test fails regularly on win_rel trybots. See crbug.com/121540
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
741 #define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
743 // Test that a DNS error occuring in an iframe does not result in an
744 // additional session history entry.
745 IN_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());
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.
757 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, IFrameDNSError_JavaScript
) {
758 content::WebContents
* wc
=
759 browser()->tab_strip_model()->GetActiveWebContents();
761 URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
);
763 // Load a regular web page, in which we will inject an iframe.
764 NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
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());
770 std::string script
= "var frame = document.createElement('iframe');"
771 "frame.src = '" + fail_url
.spec() + "';"
772 "document.body.appendChild(frame);";
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();
781 // Ensure we saw the expected failure.
782 EXPECT_EQ(fail_url
, fail_observer
.fail_url());
784 // Failed initial navigation of an iframe shouldn't be adding any history
786 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
789 // Do the same test, but with an iframe that doesn't have initial URL
791 script
= "var frame = document.createElement('iframe');"
792 "frame.id = 'target_frame';"
793 "document.body.appendChild(frame);";
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();
802 script
= "var f = document.getElementById('target_frame');"
803 "f.src = '" + fail_url
.spec() + "';";
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();
812 EXPECT_EQ(fail_url
, fail_observer
.fail_url());
813 EXPECT_EQ(2, wc
->GetController().GetEntryCount());
815 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
818 // Checks that navigation corrections are not loaded when we receive an actual
820 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, Page404
) {
821 NavigateToURLAndWaitForTitle(
822 net::URLRequestMockHTTPJob::GetMockUrl(
823 base::FilePath(FILE_PATH_LITERAL("page404.html"))),
826 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
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.
832 IN_PROC_BROWSER_TEST_F(ErrorPageTest
, StaleCacheStatus
) {
833 ASSERT_TRUE(test_server()->Start());
834 // Load cache with entry with "nocache" set, to create stale
836 GURL
test_url(test_server()->GetURL("files/nocache.html"));
837 NavigateToURLAndWaitForTitle(test_url
, "Nocache Test Page", 1);
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
,
848 // With no navigation corrections to load, there's only one navigation.
849 ui_test_utils::NavigateToURL(browser(), test_url
);
850 EXPECT_TRUE(ProbeStaleCopyValue(true));
851 EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
852 EXPECT_NE(base::ASCIIToUTF16("Nocache Test Page"),
853 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
855 // Confirm that loading the stale copy from the cache works.
856 content::TestNavigationObserver
same_tab_observer(
857 browser()->tab_strip_model()->GetActiveWebContents(), 1);
858 ASSERT_TRUE(ReloadStaleCopyFromCache());
859 same_tab_observer
.Wait();
860 EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
861 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
863 // Clear the cache and reload the same URL; confirm the error page is told
864 // that there is no cached copy.
865 BrowsingDataRemover
* remover
=
866 BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
867 remover
->Remove(BrowsingDataRemover::REMOVE_CACHE
,
868 BrowsingDataHelper::UNPROTECTED_WEB
);
869 ui_test_utils::NavigateToURL(browser(), test_url
);
870 EXPECT_TRUE(ProbeStaleCopyValue(false));
871 EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
872 EXPECT_EQ(0, link_doctor_interceptor()->num_requests());
875 class ErrorPageAutoReloadTest
: public InProcessBrowserTest
{
877 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
878 command_line
->AppendSwitch(switches::kEnableOfflineAutoReload
);
881 void InstallInterceptor(const GURL
& url
, int requests_to_fail
) {
882 interceptor_
= new FailFirstNRequestsInterceptor(requests_to_fail
);
883 scoped_ptr
<net::URLRequestInterceptor
> owned_interceptor(interceptor_
);
885 // Tests don't need to wait for this task to complete before using the
886 // filter; any requests that might be affected by it will end up in the IO
887 // thread's message loop after this posted task anyway.
889 // Ownership of the interceptor is passed to an object the IO thread, but a
890 // pointer is kept in the test fixture. As soon as anything calls
891 // URLRequestFilter::ClearHandlers(), |interceptor_| can become invalid.
892 BrowserThread::PostTask(
893 BrowserThread::IO
, FROM_HERE
,
894 base::Bind(&AddInterceptorForURL
, url
,
895 base::Passed(&owned_interceptor
)));
898 void NavigateToURLAndWaitForTitle(const GURL
& url
,
899 const std::string
& expected_title
,
900 int num_navigations
) {
901 content::TitleWatcher
title_watcher(
902 browser()->tab_strip_model()->GetActiveWebContents(),
903 base::ASCIIToUTF16(expected_title
));
905 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
906 browser(), url
, num_navigations
);
908 EXPECT_EQ(base::ASCIIToUTF16(expected_title
),
909 title_watcher
.WaitAndGetTitle());
912 FailFirstNRequestsInterceptor
* interceptor() {
917 FailFirstNRequestsInterceptor
* interceptor_
;
920 IN_PROC_BROWSER_TEST_F(ErrorPageAutoReloadTest
, AutoReload
) {
921 GURL
test_url("http://error.page.auto.reload");
922 const int kRequestsToFail
= 2;
923 InstallInterceptor(test_url
, kRequestsToFail
);
924 NavigateToURLAndWaitForTitle(test_url
, "Test One", kRequestsToFail
+ 1);
925 // Note that the interceptor updates these variables on the IO thread,
926 // but this function reads them on the main thread. The requests have to be
927 // created (on the IO thread) before NavigateToURLAndWaitForTitle returns or
928 // this becomes racey.
929 EXPECT_EQ(kRequestsToFail
, interceptor()->failures());
930 EXPECT_EQ(kRequestsToFail
+ 1, interceptor()->requests());
933 IN_PROC_BROWSER_TEST_F(ErrorPageAutoReloadTest
, ManualReloadNotSuppressed
) {
934 GURL
test_url("http://error.page.auto.reload");
935 const int kRequestsToFail
= 3;
936 InstallInterceptor(test_url
, kRequestsToFail
);
937 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
938 browser(), test_url
, 2);
940 EXPECT_EQ(2, interceptor()->failures());
941 EXPECT_EQ(2, interceptor()->requests());
943 ToggleHelpBox(browser());
944 EXPECT_TRUE(IsDisplayingText(browser(), "error.page.auto.reload"));
946 content::WebContents
* web_contents
=
947 browser()->tab_strip_model()->GetActiveWebContents();
948 content::TestNavigationObserver
nav_observer(web_contents
, 1);
949 web_contents
->GetMainFrame()->ExecuteJavaScript(
950 base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
952 EXPECT_FALSE(IsDisplayingText(browser(), "error.page.auto.reload"));
955 // Interceptor that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
956 class AddressUnreachableInterceptor
: public net::URLRequestInterceptor
{
958 AddressUnreachableInterceptor() {}
959 ~AddressUnreachableInterceptor() override
{}
961 // net::URLRequestInterceptor:
962 net::URLRequestJob
* MaybeInterceptRequest(
963 net::URLRequest
* request
,
964 net::NetworkDelegate
* network_delegate
) const override
{
965 return new URLRequestFailedJob(request
,
967 net::ERR_ADDRESS_UNREACHABLE
);
971 DISALLOW_COPY_AND_ASSIGN(AddressUnreachableInterceptor
);
974 // A test fixture that returns ERR_ADDRESS_UNREACHABLE for all navigation
975 // correction requests. ERR_NAME_NOT_RESOLVED is more typical, but need to use
976 // a different error for the correction service and the original page to
977 // validate the right page is being displayed.
978 class ErrorPageNavigationCorrectionsFailTest
: public ErrorPageTest
{
980 // InProcessBrowserTest:
981 void SetUpOnMainThread() override
{
982 BrowserThread::PostTask(
983 BrowserThread::IO
, FROM_HERE
,
984 base::Bind(&ErrorPageNavigationCorrectionsFailTest::AddFilters
));
987 void TearDownOnMainThread() override
{
988 BrowserThread::PostTask(
989 BrowserThread::IO
, FROM_HERE
,
990 base::Bind(&ErrorPageNavigationCorrectionsFailTest::RemoveFilters
));
994 // Adds a filter that causes all correction service requests to fail with
995 // ERR_ADDRESS_UNREACHABLE.
997 // Also adds the net::URLRequestFailedJob filter.
998 static void AddFilters() {
999 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1000 URLRequestFailedJob::AddUrlHandler();
1002 net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
1003 google_util::LinkDoctorBaseURL(),
1004 scoped_ptr
<net::URLRequestInterceptor
>(
1005 new AddressUnreachableInterceptor()));
1008 static void RemoveFilters() {
1009 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1010 net::URLRequestFilter::GetInstance()->ClearHandlers();
1014 // Make sure that when corrections fail to load, the network error page is
1015 // successfully loaded.
1016 IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest
,
1017 FetchCorrectionsFails
) {
1018 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1020 URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED
),
1023 // Verify that the expected error page is being displayed.
1024 ExpectDisplayingLocalErrorPage(browser(), net::ERR_NAME_NOT_RESOLVED
);
1027 // Checks that when an error occurs and a corrections fail to load, the stale
1028 // cache status of the page is correctly transferred, and we can load the
1029 // stale copy from the javascript. Most logic copied from StaleCacheStatus
1031 IN_PROC_BROWSER_TEST_F(ErrorPageNavigationCorrectionsFailTest
,
1032 StaleCacheStatusFailedCorrections
) {
1033 ASSERT_TRUE(test_server()->Start());
1034 // Load cache with entry with "nocache" set, to create stale
1036 GURL
test_url(test_server()->GetURL("files/nocache.html"));
1037 NavigateToURLAndWaitForTitle(test_url
, "Nocache Test Page", 1);
1039 // Reload same URL after forcing an error from the the network layer;
1040 // confirm that the error page is told the cached copy exists.
1041 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
=
1042 browser()->profile()->GetRequestContext();
1043 BrowserThread::PostTask(
1044 BrowserThread::IO
, FROM_HERE
,
1045 base::Bind(&InterceptNetworkTransactions
, url_request_context_getter
,
1046 net::ERR_CONNECTION_FAILED
));
1048 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1049 browser(), test_url
, 2);
1050 EXPECT_TRUE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
1051 EXPECT_TRUE(ProbeStaleCopyValue(true));
1053 // Confirm that loading the stale copy from the cache works.
1054 content::TestNavigationObserver
same_tab_observer(
1055 browser()->tab_strip_model()->GetActiveWebContents(), 1);
1056 ASSERT_TRUE(ReloadStaleCopyFromCache());
1057 same_tab_observer
.Wait();
1058 EXPECT_EQ(base::ASCIIToUTF16("Nocache Test Page"),
1059 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
1061 // Clear the cache and reload the same URL; confirm the error page is told
1062 // that there is no cached copy.
1063 BrowsingDataRemover
* remover
=
1064 BrowsingDataRemover::CreateForUnboundedRange(browser()->profile());
1065 remover
->Remove(BrowsingDataRemover::REMOVE_CACHE
,
1066 BrowsingDataHelper::UNPROTECTED_WEB
);
1067 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
1068 browser(), test_url
, 2);
1069 EXPECT_TRUE(ProbeStaleCopyValue(false));
1070 EXPECT_FALSE(IsDisplayingText(browser(), GetLoadStaleButtonLabel()));
1073 // A test fixture that simulates failing requests for an IDN domain name.
1074 class ErrorPageForIDNTest
: public InProcessBrowserTest
{
1076 // Target hostname in different forms.
1077 static const char kHostname
[];
1078 static const char kHostnameJSUnicode
[];
1080 // InProcessBrowserTest:
1081 void SetUpOnMainThread() override
{
1082 // Clear AcceptLanguages to force punycode decoding.
1083 browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages
,
1085 BrowserThread::PostTask(
1086 BrowserThread::IO
, FROM_HERE
,
1087 base::Bind(&ErrorPageForIDNTest::AddFilters
));
1090 void TearDownOnMainThread() override
{
1091 BrowserThread::PostTask(
1092 BrowserThread::IO
, FROM_HERE
,
1093 base::Bind(&ErrorPageForIDNTest::RemoveFilters
));
1097 static void AddFilters() {
1098 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1099 URLRequestFailedJob::AddUrlHandlerForHostname(kHostname
);
1102 static void RemoveFilters() {
1103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1104 net::URLRequestFilter::GetInstance()->ClearHandlers();
1108 const char ErrorPageForIDNTest::kHostname
[] =
1109 "xn--d1abbgf6aiiy.xn--p1ai";
1110 const char ErrorPageForIDNTest::kHostnameJSUnicode
[] =
1111 "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442."
1114 // Make sure error page shows correct unicode for IDN.
1115 IN_PROC_BROWSER_TEST_F(ErrorPageForIDNTest
, IDN
) {
1116 // ERR_UNSAFE_PORT will not trigger navigation corrections.
1117 ui_test_utils::NavigateToURL(
1119 URLRequestFailedJob::GetMockHttpUrlForHostname(net::ERR_UNSAFE_PORT
,
1122 ToggleHelpBox(browser());
1123 EXPECT_TRUE(IsDisplayingText(browser(), kHostnameJSUnicode
));