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