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/strings/string_util.h" 6 #include "base/strings/stringprintf.h" 7 #include "base/strings/utf_string_conversions.h" 8 #include "content/browser/download/download_manager_impl.h" 9 #include "content/browser/web_contents/web_contents_impl.h" 10 #include "content/public/browser/browser_context.h" 11 #include "content/public/browser/browser_thread.h" 12 #include "content/public/browser/web_contents.h" 13 #include "content/public/common/url_constants.h" 14 #include "content/public/test/browser_test_utils.h" 15 #include "content/public/test/content_browser_test.h" 16 #include "content/public/test/content_browser_test_utils.h" 17 #include "content/public/test/test_utils.h" 18 #include "content/shell/browser/shell.h" 19 #include "content/shell/browser/shell_content_browser_client.h" 20 #include "content/shell/browser/shell_network_delegate.h" 21 #include "content/test/net/url_request_failed_job.h" 22 #include "content/test/net/url_request_mock_http_job.h" 23 #include "net/base/net_errors.h" 24 #include "net/test/embedded_test_server/embedded_test_server.h" 25 #include "net/test/embedded_test_server/http_request.h" 26 #include "net/test/embedded_test_server/http_response.h" 27 28 using base::ASCIIToUTF16; 29 30 namespace content { 31 32 class ResourceDispatcherHostBrowserTest : public ContentBrowserTest, 33 public DownloadManager::Observer { 34 public: 35 ResourceDispatcherHostBrowserTest() : got_downloads_(false) {} 36 37 protected: 38 virtual void SetUpOnMainThread() OVERRIDE { 39 base::FilePath path = GetTestFilePath("", ""); 40 BrowserThread::PostTask( 41 BrowserThread::IO, FROM_HERE, 42 base::Bind(&URLRequestMockHTTPJob::AddUrlHandler, path)); 43 BrowserThread::PostTask( 44 BrowserThread::IO, FROM_HERE, 45 base::Bind(&URLRequestFailedJob::AddUrlHandler)); 46 } 47 48 virtual void OnDownloadCreated( 49 DownloadManager* manager, 50 DownloadItem* item) OVERRIDE { 51 if (!got_downloads_) 52 got_downloads_ = !!manager->InProgressCount(); 53 } 54 55 GURL GetMockURL(const std::string& file) { 56 return URLRequestMockHTTPJob::GetMockUrl( 57 base::FilePath().AppendASCII(file)); 58 } 59 60 void CheckTitleTest(const GURL& url, 61 const std::string& expected_title) { 62 base::string16 expected_title16(ASCIIToUTF16(expected_title)); 63 TitleWatcher title_watcher(shell()->web_contents(), expected_title16); 64 NavigateToURL(shell(), url); 65 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 66 } 67 68 bool GetPopupTitle(const GURL& url, base::string16* title) { 69 NavigateToURL(shell(), url); 70 71 ShellAddedObserver new_shell_observer; 72 73 // Create dynamic popup. 74 if (!ExecuteScript(shell()->web_contents(), "OpenPopup();")) 75 return false; 76 77 Shell* new_shell = new_shell_observer.GetShell(); 78 *title = new_shell->web_contents()->GetTitle(); 79 return true; 80 } 81 82 std::string GetCookies(const GURL& url) { 83 return content::GetCookies( 84 shell()->web_contents()->GetBrowserContext(), url); 85 } 86 87 bool got_downloads() const { return got_downloads_; } 88 89 private: 90 bool got_downloads_; 91 }; 92 93 // Test title for content created by javascript window.open(). 94 // See http://crbug.com/5988 95 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DynamicTitle1) { 96 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 97 98 GURL url(embedded_test_server()->GetURL("/dynamic1.html")); 99 base::string16 title; 100 ASSERT_TRUE(GetPopupTitle(url, &title)); 101 EXPECT_TRUE(StartsWith(title, ASCIIToUTF16("My Popup Title"), true)) 102 << "Actual title: " << title; 103 } 104 105 // Test title for content created by javascript window.open(). 106 // See http://crbug.com/5988 107 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DynamicTitle2) { 108 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 109 110 GURL url(embedded_test_server()->GetURL("/dynamic2.html")); 111 base::string16 title; 112 ASSERT_TRUE(GetPopupTitle(url, &title)); 113 EXPECT_TRUE(StartsWith(title, ASCIIToUTF16("My Dynamic Title"), true)) 114 << "Actual title: " << title; 115 } 116 117 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 118 SniffHTMLWithNoContentType) { 119 CheckTitleTest(GetMockURL("content-sniffer-test0.html"), 120 "Content Sniffer Test 0"); 121 } 122 123 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 124 RespectNoSniffDirective) { 125 CheckTitleTest(GetMockURL("nosniff-test.html"), 126 "mock.http/nosniff-test.html"); 127 } 128 129 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 130 DoNotSniffHTMLFromTextPlain) { 131 CheckTitleTest(GetMockURL("content-sniffer-test1.html"), 132 "mock.http/content-sniffer-test1.html"); 133 } 134 135 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 136 DoNotSniffHTMLFromImageGIF) { 137 CheckTitleTest(GetMockURL("content-sniffer-test2.html"), 138 "mock.http/content-sniffer-test2.html"); 139 } 140 141 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 142 SniffNoContentTypeNoData) { 143 // Make sure no downloads start. 144 BrowserContext::GetDownloadManager( 145 shell()->web_contents()->GetBrowserContext())->AddObserver(this); 146 CheckTitleTest(GetMockURL("content-sniffer-test3.html"), 147 "Content Sniffer Test 3"); 148 EXPECT_EQ(1u, Shell::windows().size()); 149 ASSERT_FALSE(got_downloads()); 150 } 151 152 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 153 ContentDispositionEmpty) { 154 CheckTitleTest(GetMockURL("content-disposition-empty.html"), "success"); 155 } 156 157 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 158 ContentDispositionInline) { 159 CheckTitleTest(GetMockURL("content-disposition-inline.html"), "success"); 160 } 161 162 // Test for bug #1091358. 163 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, SyncXMLHttpRequest) { 164 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 165 NavigateToURL( 166 shell(), embedded_test_server()->GetURL("/sync_xmlhttprequest.html")); 167 168 // Let's check the XMLHttpRequest ran successfully. 169 bool success = false; 170 EXPECT_TRUE(ExecuteScriptAndExtractBool( 171 shell()->web_contents(), 172 "window.domAutomationController.send(DidSyncRequestSucceed());", 173 &success)); 174 EXPECT_TRUE(success); 175 } 176 177 // If this flakes, use http://crbug.com/62776. 178 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 179 SyncXMLHttpRequest_Disallowed) { 180 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 181 NavigateToURL( 182 shell(), 183 embedded_test_server()->GetURL("/sync_xmlhttprequest_disallowed.html")); 184 185 // Let's check the XMLHttpRequest ran successfully. 186 bool success = false; 187 EXPECT_TRUE(ExecuteScriptAndExtractBool( 188 shell()->web_contents(), 189 "window.domAutomationController.send(DidSucceed());", 190 &success)); 191 EXPECT_TRUE(success); 192 } 193 194 // Test for bug #1159553 -- A synchronous xhr (whose content-type is 195 // downloadable) would trigger download and hang the renderer process, 196 // if executed while navigating to a new page. 197 // Disabled on Mac: see http://crbug.com/56264 198 #if defined(OS_MACOSX) 199 #define MAYBE_SyncXMLHttpRequest_DuringUnload \ 200 DISABLED_SyncXMLHttpRequest_DuringUnload 201 #else 202 #define MAYBE_SyncXMLHttpRequest_DuringUnload SyncXMLHttpRequest_DuringUnload 203 #endif 204 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 205 MAYBE_SyncXMLHttpRequest_DuringUnload) { 206 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 207 BrowserContext::GetDownloadManager( 208 shell()->web_contents()->GetBrowserContext())->AddObserver(this); 209 210 CheckTitleTest( 211 embedded_test_server()->GetURL("/sync_xmlhttprequest_during_unload.html"), 212 "sync xhr on unload"); 213 214 // Navigate to a new page, to dispatch unload event and trigger xhr. 215 // (the bug would make this step hang the renderer). 216 CheckTitleTest( 217 embedded_test_server()->GetURL("/title2.html"), "Title Of Awesomeness"); 218 219 ASSERT_FALSE(got_downloads()); 220 } 221 222 // Tests that onunload is run for cross-site requests. (Bug 1114994) 223 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 224 CrossSiteOnunloadCookie) { 225 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 226 227 GURL url = embedded_test_server()->GetURL("/onunload_cookie.html"); 228 CheckTitleTest(url, "set cookie on unload"); 229 230 // Navigate to a new cross-site page, to dispatch unload event and set the 231 // cookie. 232 CheckTitleTest(GetMockURL("content-sniffer-test0.html"), 233 "Content Sniffer Test 0"); 234 235 // Check that the cookie was set. 236 EXPECT_EQ("onunloadCookie=foo", GetCookies(url)); 237 } 238 239 // If this flakes, use http://crbug.com/130404 240 // Tests that onunload is run for cross-site requests to URLs that complete 241 // without network loads (e.g., about:blank, data URLs). 242 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 243 DISABLED_CrossSiteImmediateLoadOnunloadCookie) { 244 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 245 246 GURL url = embedded_test_server()->GetURL("/onunload_cookie.html"); 247 CheckTitleTest(url, "set cookie on unload"); 248 249 // Navigate to a cross-site page that loads immediately without making a 250 // network request. The unload event should still be run. 251 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); 252 253 // Check that the cookie was set. 254 EXPECT_EQ("onunloadCookie=foo", GetCookies(url)); 255 } 256 257 namespace { 258 259 // Handles |request| by serving a redirect response. 260 scoped_ptr<net::test_server::HttpResponse> NoContentResponseHandler( 261 const std::string& path, 262 const net::test_server::HttpRequest& request) { 263 if (!StartsWithASCII(path, request.relative_url, true)) 264 return scoped_ptr<net::test_server::HttpResponse>(); 265 266 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 267 new net::test_server::BasicHttpResponse); 268 http_response->set_code(net::HTTP_NO_CONTENT); 269 return http_response.PassAs<net::test_server::HttpResponse>(); 270 } 271 272 } // namespace 273 274 // Tests that the unload handler is not run for 204 responses. 275 // If this flakes use http://crbug.com/80596. 276 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 277 CrossSiteNoUnloadOn204) { 278 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 279 280 // Start with a URL that sets a cookie in its unload handler. 281 GURL url = embedded_test_server()->GetURL("/onunload_cookie.html"); 282 CheckTitleTest(url, "set cookie on unload"); 283 284 // Navigate to a cross-site URL that returns a 204 No Content response. 285 const char kNoContentPath[] = "/nocontent"; 286 embedded_test_server()->RegisterRequestHandler( 287 base::Bind(&NoContentResponseHandler, kNoContentPath)); 288 NavigateToURL(shell(), embedded_test_server()->GetURL(kNoContentPath)); 289 290 // Check that the unload cookie was not set. 291 EXPECT_EQ("", GetCookies(url)); 292 } 293 294 #if !defined(OS_MACOSX) 295 // Tests that the onbeforeunload and onunload logic is short-circuited if the 296 // old renderer is gone. In that case, we don't want to wait for the old 297 // renderer to run the handlers. 298 // We need to disable this on Mac because the crash causes the OS CrashReporter 299 // process to kick in to analyze the poor dead renderer. Unfortunately, if the 300 // app isn't stripped of debug symbols, this takes about five minutes to 301 // complete and isn't conducive to quick turnarounds. As we don't currently 302 // strip the app on the build bots, this is bad times. 303 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CrossSiteAfterCrash) { 304 // Make sure we have a live process before trying to kill it. 305 NavigateToURL(shell(), GURL("about:blank")); 306 307 // Cause the renderer to crash. 308 RenderProcessHostWatcher crash_observer( 309 shell()->web_contents(), 310 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); 311 NavigateToURL(shell(), GURL(kChromeUICrashURL)); 312 // Wait for browser to notice the renderer crash. 313 crash_observer.Wait(); 314 315 // Navigate to a new cross-site page. The browser should not wait around for 316 // the old renderer's on{before}unload handlers to run. 317 CheckTitleTest(GetMockURL("content-sniffer-test0.html"), 318 "Content Sniffer Test 0"); 319 } 320 #endif // !defined(OS_MACOSX) 321 322 // Tests that cross-site navigations work when the new page does not go through 323 // the BufferedEventHandler (e.g., non-http{s} URLs). (Bug 1225872) 324 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 325 CrossSiteNavigationNonBuffered) { 326 // Start with an HTTP page. 327 CheckTitleTest(GetMockURL("content-sniffer-test0.html"), 328 "Content Sniffer Test 0"); 329 330 // Now load a file:// page, which does not use the BufferedEventHandler. 331 // Make sure that the page loads and displays a title, and doesn't get stuck. 332 GURL url = GetTestUrl("", "title2.html"); 333 CheckTitleTest(url, "Title Of Awesomeness"); 334 } 335 336 // Tests that a cross-site navigation to an error page (resulting in the link 337 // doctor page) still runs the onunload handler and can support navigations 338 // away from the link doctor page. (Bug 1235537) 339 // Flaky: http://crbug.com/100823 340 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 341 CrossSiteNavigationErrorPage) { 342 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 343 344 GURL url(embedded_test_server()->GetURL("/onunload_cookie.html")); 345 CheckTitleTest(url, "set cookie on unload"); 346 347 // Navigate to a new cross-site URL that results in an error. 348 // TODO(creis): If this causes crashes or hangs, it might be for the same 349 // reason as ErrorPageTest::DNSError. See bug 1199491 and 350 // http://crbug.com/22877. 351 GURL failed_url = URLRequestFailedJob::GetMockHttpUrl( 352 net::ERR_NAME_NOT_RESOLVED); 353 NavigateToURL(shell(), failed_url); 354 355 EXPECT_NE(ASCIIToUTF16("set cookie on unload"), 356 shell()->web_contents()->GetTitle()); 357 358 // Check that the cookie was set, meaning that the onunload handler ran. 359 EXPECT_EQ("onunloadCookie=foo", GetCookies(url)); 360 361 // Check that renderer-initiated navigations still work. In a previous bug, 362 // the ResourceDispatcherHost would think that such navigations were 363 // cross-site, because we didn't clean up from the previous request. Since 364 // WebContentsImpl was in the NORMAL state, it would ignore the attempt to run 365 // the onunload handler, and the navigation would fail. We can't test by 366 // redirecting to javascript:window.location='someURL', since javascript: 367 // URLs are prohibited by policy from interacting with sensitive chrome 368 // pages of which the error page is one. Instead, use automation to kick 369 // off the navigation, and wait to see that the tab loads. 370 base::string16 expected_title16(ASCIIToUTF16("Title Of Awesomeness")); 371 TitleWatcher title_watcher(shell()->web_contents(), expected_title16); 372 373 bool success; 374 GURL test_url(embedded_test_server()->GetURL("/title2.html")); 375 std::string redirect_script = "window.location='" + 376 test_url.possibly_invalid_spec() + "';" + 377 "window.domAutomationController.send(true);"; 378 EXPECT_TRUE(ExecuteScriptAndExtractBool( 379 shell()->web_contents(), 380 redirect_script, 381 &success)); 382 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 383 } 384 385 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 386 CrossSiteNavigationErrorPage2) { 387 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 388 389 GURL url(embedded_test_server()->GetURL("/title2.html")); 390 CheckTitleTest(url, "Title Of Awesomeness"); 391 392 // Navigate to a new cross-site URL that results in an error. 393 // TODO(creis): If this causes crashes or hangs, it might be for the same 394 // reason as ErrorPageTest::DNSError. See bug 1199491 and 395 // http://crbug.com/22877. 396 GURL failed_url = URLRequestFailedJob::GetMockHttpUrl( 397 net::ERR_NAME_NOT_RESOLVED); 398 399 NavigateToURL(shell(), failed_url); 400 EXPECT_NE(ASCIIToUTF16("Title Of Awesomeness"), 401 shell()->web_contents()->GetTitle()); 402 403 // Repeat navigation. We are testing that this completes. 404 NavigateToURL(shell(), failed_url); 405 EXPECT_NE(ASCIIToUTF16("Title Of Awesomeness"), 406 shell()->web_contents()->GetTitle()); 407 } 408 409 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 410 CrossOriginRedirectBlocked) { 411 // We expect the following URL requests from this test: 412 // 1- http://mock.http/cross-origin-redirect-blocked.html 413 // 2- http://mock.http/redirect-to-title2.html 414 // 3- http://mock.http/title2.html 415 // 416 // If the redirect in #2 were not blocked, we'd also see a request 417 // for http://mock.http:4000/title2.html, and the title would be different. 418 CheckTitleTest(GetMockURL("cross-origin-redirect-blocked.html"), 419 "Title Of More Awesomeness"); 420 } 421 422 // Tests that ResourceRequestInfoImpl is updated correctly on failed 423 // requests, to prevent calling Read on a request that has already failed. 424 // See bug 40250. 425 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, 426 CrossSiteFailedRequest) { 427 // Visit another URL first to trigger a cross-site navigation. 428 NavigateToURL(shell(), GetTestUrl("", "simple_page.html")); 429 430 // Visit a URL that fails without calling ResourceDispatcherHost::Read. 431 GURL broken_url("chrome://theme"); 432 NavigateToURL(shell(), broken_url); 433 } 434 435 namespace { 436 437 scoped_ptr<net::test_server::HttpResponse> HandleRedirectRequest( 438 const std::string& request_path, 439 const net::test_server::HttpRequest& request) { 440 if (!StartsWithASCII(request.relative_url, request_path, true)) 441 return scoped_ptr<net::test_server::HttpResponse>(); 442 443 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 444 new net::test_server::BasicHttpResponse); 445 http_response->set_code(net::HTTP_FOUND); 446 http_response->AddCustomHeader( 447 "Location", request.relative_url.substr(request_path.length())); 448 return http_response.PassAs<net::test_server::HttpResponse>(); 449 } 450 451 } // namespace 452 453 // Test that we update the cookie policy URLs correctly when transferring 454 // navigations. 455 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CookiePolicy) { 456 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 457 embedded_test_server()->RegisterRequestHandler( 458 base::Bind(&HandleRedirectRequest, "/redirect?")); 459 460 std::string set_cookie_url(base::StringPrintf( 461 "http://localhost:%d/set_cookie.html", embedded_test_server()->port())); 462 GURL url(embedded_test_server()->GetURL("/redirect?" + set_cookie_url)); 463 464 ShellContentBrowserClient::SetSwapProcessesForRedirect(true); 465 ShellNetworkDelegate::SetAcceptAllCookies(false); 466 467 CheckTitleTest(url, "cookie set"); 468 } 469 470 } // namespace content 471