Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / errorpage_browsertest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/bind.h"
6 #include "base/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"
30
31 using content::BrowserThread;
32 using content::NavigationController;
33 using content::URLRequestFailedJob;
34
35 namespace {
36
37 class ErrorPageTest : public InProcessBrowserTest {
38  public:
39   enum HistoryNavigationDirection {
40     HISTORY_NAVIGATE_BACK,
41     HISTORY_NAVIGATE_FORWARD,
42   };
43
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(
47         browser(),
48         content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(file_path)));
49   }
50
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));
59
60     ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
61         browser(), url, num_navigations);
62
63     EXPECT_EQ(base::ASCIIToUTF16(expected_title),
64               title_watcher.WaitAndGetTitle());
65   }
66
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,
72                                    num_navigations,
73                                    HISTORY_NAVIGATE_BACK);
74   }
75
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,
81                                    num_navigations,
82                                    HISTORY_NAVIGATE_FORWARD);
83   }
84
85  protected:
86   virtual void SetUpOnMainThread() OVERRIDE {
87     BrowserThread::PostTask(
88         BrowserThread::IO, FROM_HERE,
89         base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
90   }
91
92   // Returns a GURL that results in a DNS error.
93   GURL GetDnsErrorURL() const {
94     return URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
95   }
96
97  private:
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,
101                                       int num_navigations,
102                                       HistoryNavigationDirection direction) {
103     content::TitleWatcher title_watcher(
104         browser()->tab_strip_model()->GetActiveWebContents(),
105         base::ASCIIToUTF16(expected_title));
106
107     content::TestNavigationObserver test_navigation_observer(
108         browser()->tab_strip_model()->GetActiveWebContents(),
109         num_navigations);
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);
114     } else {
115       FAIL();
116     }
117     test_navigation_observer.Wait();
118
119     EXPECT_EQ(title_watcher.WaitAndGetTitle(),
120               base::ASCIIToUTF16(expected_title));
121   }
122 };
123
124
125 class TestFailProvisionalLoadObserver : public content::WebContentsObserver {
126  public:
127   explicit TestFailProvisionalLoadObserver(content::WebContents* contents)
128       : content::WebContentsObserver(contents) {}
129   virtual ~TestFailProvisionalLoadObserver() {}
130
131   // This method is invoked when the provisional load failed.
132   virtual void DidFailProvisionalLoad(
133       int64 frame_id,
134       const base::string16& frame_unique_name,
135       bool is_main_frame,
136       const GURL& validated_url,
137       int error_code,
138       const base::string16& error_description,
139       content::RenderViewHost* render_view_host) OVERRIDE {
140     fail_url_ = validated_url;
141   }
142
143   const GURL& fail_url() const { return fail_url_; }
144
145  private:
146   GURL fail_url_;
147
148   DISALLOW_COPY_AND_ASSIGN(TestFailProvisionalLoadObserver);
149 };
150
151 // See crbug.com/109669
152 #if defined(USE_AURA) || defined(OS_WIN)
153 #define MAYBE_DNSError_Basic DISABLED_DNSError_Basic
154 #else
155 #define MAYBE_DNSError_Basic DNSError_Basic
156 #endif
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
160   // page.
161   NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
162 }
163
164 // See crbug.com/109669
165 #if defined(USE_AURA)
166 #define MAYBE_DNSError_GoBack1 DISABLED_DNSError_GoBack1
167 #else
168 #define MAYBE_DNSError_GoBack1 DNSError_GoBack1
169 #endif
170
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);
177 }
178
179 // See crbug.com/109669
180 #if defined(USE_AURA)
181 #define MAYBE_DNSError_GoBack2 DISABLED_DNSError_GoBack2
182 #else
183 #define MAYBE_DNSError_GoBack2 DNSError_GoBack2
184 #endif
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"));
189
190   NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
191   NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
192
193   GoBackAndWaitForTitle("Mock Link Doctor", 2);
194   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
195 }
196
197 // See crbug.com/109669
198 #if defined(USE_AURA)
199 #define MAYBE_DNSError_GoBack2AndForward DISABLED_DNSError_GoBack2AndForward
200 #else
201 #define MAYBE_DNSError_GoBack2AndForward DNSError_GoBack2AndForward
202 #endif
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"));
207
208   NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
209   NavigateToFileURL(FILE_PATH_LITERAL("title3.html"));
210
211   GoBackAndWaitForTitle("Mock Link Doctor", 2);
212   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
213
214   GoForwardAndWaitForTitle("Mock Link Doctor", 2);
215 }
216
217 // See crbug.com/109669
218 #if defined(USE_AURA)
219 #define MAYBE_DNSError_GoBack2Forward2 DISABLED_DNSError_GoBack2Forward2
220 #else
221 #define MAYBE_DNSError_GoBack2Forward2 DNSError_GoBack2Forward2
222 #endif
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"));
227
228   NavigateToURLAndWaitForTitle(GetDnsErrorURL(), "Mock Link Doctor", 2);
229   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
230
231   GoBackAndWaitForTitle("Mock Link Doctor", 2);
232   GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
233
234   GoForwardAndWaitForTitle("Mock Link Doctor", 2);
235   GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
236 }
237
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"))),
243       "Blah",
244       1);
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".
247   EXPECT_EQ(2,
248       browser()->tab_strip_model()->GetActiveWebContents()->
249           GetController().GetEntryCount());
250 }
251
252 // This test fails regularly on win_rel trybots. See crbug.com/121540
253 #if defined(OS_WIN)
254 #define MAYBE_IFrameDNSError_GoBack DISABLED_IFrameDNSError_GoBack
255 #else
256 #define MAYBE_IFrameDNSError_GoBack IFrameDNSError_GoBack
257 #endif
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);
264 }
265
266 // This test fails regularly on win_rel trybots. See crbug.com/121540
267 //
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
271 #else
272 #define MAYBE_IFrameDNSError_GoBackAndForward IFrameDNSError_GoBackAndForward
273 #endif
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);
281 }
282
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();
290   GURL fail_url =
291       URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED);
292
293   // Load a regular web page, in which we will inject an iframe.
294   NavigateToFileURL(FILE_PATH_LITERAL("title2.html"));
295
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());
299
300   std::string script = "var frame = document.createElement('iframe');"
301                        "frame.src = '" + fail_url.spec() + "';"
302                        "document.body.appendChild(frame);";
303   {
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();
311
312     // Ensure we saw the expected failure.
313     EXPECT_EQ(fail_url, fail_observer.fail_url());
314
315     // Failed initial navigation of an iframe shouldn't be adding any history
316     // entries.
317     EXPECT_EQ(2, wc->GetController().GetEntryCount());
318   }
319
320   // Do the same test, but with an iframe that doesn't have initial URL
321   // assigned.
322   script = "var frame = document.createElement('iframe');"
323            "frame.id = 'target_frame';"
324            "document.body.appendChild(frame);";
325   {
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();
332   }
333
334   script = "var f = document.getElementById('target_frame');"
335            "f.src = '" + fail_url.spec() + "';";
336   {
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();
344
345     EXPECT_EQ(fail_url, fail_observer.fail_url());
346     EXPECT_EQ(2, wc->GetController().GetEntryCount());
347   }
348 }
349
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"))),
355       "SUCCESS",
356       1);
357 }
358
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());
368 }
369
370 // Protocol handler that fails all requests with net::ERR_ADDRESS_UNREACHABLE.
371 class AddressUnreachableProtocolHandler
372     : public net::URLRequestJobFactory::ProtocolHandler {
373  public:
374   AddressUnreachableProtocolHandler() {}
375   virtual ~AddressUnreachableProtocolHandler() {}
376
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,
382                                    network_delegate,
383                                    net::ERR_ADDRESS_UNREACHABLE);
384   }
385
386  private:
387   DISALLOW_COPY_AND_ASSIGN(AddressUnreachableProtocolHandler);
388 };
389
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 {
395  public:
396   // InProcessBrowserTest:
397   virtual void SetUpOnMainThread() OVERRIDE {
398     BrowserThread::PostTask(
399         BrowserThread::IO, FROM_HERE,
400         base::Bind(&ErrorPageLinkDoctorFailTest::AddFilters));
401   }
402
403   virtual void CleanUpOnMainThread() OVERRIDE {
404     BrowserThread::PostTask(
405         BrowserThread::IO, FROM_HERE,
406         base::Bind(&ErrorPageLinkDoctorFailTest::RemoveFilters));
407   }
408
409  private:
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.
413   //
414   // Also adds the content::URLRequestFailedJob filter.
415   static void AddFilters() {
416     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
417     content::URLRequestFailedJob::AddUrlHandler();
418
419     net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
420         google_util::LinkDoctorBaseURL().scheme(),
421         google_util::LinkDoctorBaseURL().host(),
422         scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
423             new AddressUnreachableProtocolHandler()));
424   }
425
426   static void RemoveFilters() {
427     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
428     net::URLRequestFilter::GetInstance()->ClearHandlers();
429   }
430 };
431
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(
436       browser(),
437       URLRequestFailedJob::GetMockHttpUrl(net::ERR_NAME_NOT_RESOLVED),
438       2);
439
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.
442   bool result = false;
443   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
444       browser()->tab_strip_model()->GetActiveWebContents(),
445       GetTextContentContainsStringScript("ERR_NAME_NOT_RESOLVED"),
446       &result));
447   EXPECT_TRUE(result);
448 }
449
450 // A test fixture that simulates failing requests for an IDN domain name.
451 class ErrorPageForIDNTest : public InProcessBrowserTest {
452  public:
453   // Target hostname in different forms.
454   static const char kHostname[];
455   static const char kHostnameJSUnicode[];
456
457   // InProcessBrowserTest:
458   virtual void SetUpOnMainThread() OVERRIDE {
459     // Clear AcceptLanguages to force punycode decoding.
460     browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages,
461                                                 std::string());
462     BrowserThread::PostTask(
463         BrowserThread::IO, FROM_HERE,
464         base::Bind(&ErrorPageForIDNTest::AddFilters));
465   }
466
467   virtual void CleanUpOnMainThread() OVERRIDE {
468     BrowserThread::PostTask(
469         BrowserThread::IO, FROM_HERE,
470         base::Bind(&ErrorPageForIDNTest::RemoveFilters));
471   }
472
473  private:
474   static void AddFilters() {
475     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
476     content::URLRequestFailedJob::AddUrlHandlerForHostname(kHostname);
477   }
478
479   static void RemoveFilters() {
480     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
481     net::URLRequestFilter::GetInstance()->ClearHandlers();
482   }
483 };
484
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."
489     "\\u0440\\u0444";
490
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(
495       browser(),
496       URLRequestFailedJob::GetMockHttpUrlForHostname(net::ERR_UNSAFE_PORT,
497                                                      kHostname),
498       1);
499
500   bool result = false;
501   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
502       browser()->tab_strip_model()->GetActiveWebContents(),
503       GetTextContentContainsStringScript(kHostnameJSUnicode),
504       &result));
505   EXPECT_TRUE(result);
506 }
507
508 }  // namespace