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/command_line.h" 6#include "base/strings/stringprintf.h" 7#include "base/strings/utf_string_conversions.h" 8#include "content/browser/frame_host/frame_tree.h" 9#include "content/browser/renderer_host/render_view_host_impl.h" 10#include "content/browser/web_contents/web_contents_impl.h" 11#include "content/public/browser/navigation_entry.h" 12#include "content/public/browser/notification_observer.h" 13#include "content/public/browser/notification_service.h" 14#include "content/public/browser/notification_types.h" 15#include "content/public/browser/web_contents_observer.h" 16#include "content/public/common/content_switches.h" 17#include "content/public/common/url_constants.h" 18#include "content/public/test/browser_test_utils.h" 19#include "content/public/test/test_navigation_observer.h" 20#include "content/public/test/test_utils.h" 21#include "content/shell/browser/shell.h" 22#include "content/shell/browser/shell_content_browser_client.h" 23#include "content/test/content_browser_test.h" 24#include "content/test/content_browser_test_utils.h" 25#include "net/base/escape.h" 26#include "net/dns/mock_host_resolver.h" 27 28namespace content { 29 30class SitePerProcessWebContentsObserver: public WebContentsObserver { 31 public: 32 explicit SitePerProcessWebContentsObserver(WebContents* web_contents) 33 : WebContentsObserver(web_contents), 34 navigation_succeeded_(true) {} 35 virtual ~SitePerProcessWebContentsObserver() {} 36 37 virtual void DidFailProvisionalLoad( 38 int64 frame_id, 39 const base::string16& frame_unique_name, 40 bool is_main_frame, 41 const GURL& validated_url, 42 int error_code, 43 const base::string16& error_description, 44 RenderViewHost* render_view_host) OVERRIDE { 45 navigation_url_ = validated_url; 46 navigation_succeeded_ = false; 47 } 48 49 virtual void DidCommitProvisionalLoadForFrame( 50 int64 frame_id, 51 const base::string16& frame_unique_name, 52 bool is_main_frame, 53 const GURL& url, 54 PageTransition transition_type, 55 RenderViewHost* render_view_host) OVERRIDE{ 56 navigation_url_ = url; 57 navigation_succeeded_ = true; 58 } 59 60 const GURL& navigation_url() const { 61 return navigation_url_; 62 } 63 64 int navigation_succeeded() const { return navigation_succeeded_; } 65 66 private: 67 GURL navigation_url_; 68 bool navigation_succeeded_; 69 70 DISALLOW_COPY_AND_ASSIGN(SitePerProcessWebContentsObserver); 71}; 72 73class RedirectNotificationObserver : public NotificationObserver { 74 public: 75 // Register to listen for notifications of the given type from either a 76 // specific source, or from all sources if |source| is 77 // NotificationService::AllSources(). 78 RedirectNotificationObserver(int notification_type, 79 const NotificationSource& source); 80 virtual ~RedirectNotificationObserver(); 81 82 // Wait until the specified notification occurs. If the notification was 83 // emitted between the construction of this object and this call then it 84 // returns immediately. 85 void Wait(); 86 87 // Returns NotificationService::AllSources() if we haven't observed a 88 // notification yet. 89 const NotificationSource& source() const { 90 return source_; 91 } 92 93 const NotificationDetails& details() const { 94 return details_; 95 } 96 97 // NotificationObserver: 98 virtual void Observe(int type, 99 const NotificationSource& source, 100 const NotificationDetails& details) OVERRIDE; 101 102 private: 103 bool seen_; 104 bool seen_twice_; 105 bool running_; 106 NotificationRegistrar registrar_; 107 108 NotificationSource source_; 109 NotificationDetails details_; 110 scoped_refptr<MessageLoopRunner> message_loop_runner_; 111 112 DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver); 113}; 114 115RedirectNotificationObserver::RedirectNotificationObserver( 116 int notification_type, 117 const NotificationSource& source) 118 : seen_(false), 119 running_(false), 120 source_(NotificationService::AllSources()) { 121 registrar_.Add(this, notification_type, source); 122} 123 124RedirectNotificationObserver::~RedirectNotificationObserver() {} 125 126void RedirectNotificationObserver::Wait() { 127 if (seen_ && seen_twice_) 128 return; 129 130 running_ = true; 131 message_loop_runner_ = new MessageLoopRunner; 132 message_loop_runner_->Run(); 133 EXPECT_TRUE(seen_); 134} 135 136void RedirectNotificationObserver::Observe( 137 int type, 138 const NotificationSource& source, 139 const NotificationDetails& details) { 140 source_ = source; 141 details_ = details; 142 seen_twice_ = seen_; 143 seen_ = true; 144 if (!running_) 145 return; 146 147 message_loop_runner_->Quit(); 148 running_ = false; 149} 150 151class SitePerProcessBrowserTest : public ContentBrowserTest { 152 protected: 153 bool NavigateIframeToURL(Shell* window, 154 const GURL& url, 155 std::string iframe_id) { 156 std::string script = base::StringPrintf( 157 "var iframes = document.getElementById('%s');iframes.src='%s';", 158 iframe_id.c_str(), url.spec().c_str()); 159 WindowedNotificationObserver load_observer( 160 NOTIFICATION_LOAD_STOP, 161 Source<NavigationController>( 162 &shell()->web_contents()->GetController())); 163 bool result = ExecuteScript(window->web_contents(), script); 164 load_observer.Wait(); 165 return result; 166 } 167 168 void NavigateToURLContentInitiated(Shell* window, 169 const GURL& url, 170 bool should_replace_current_entry) { 171 std::string script; 172 if (should_replace_current_entry) 173 script = base::StringPrintf("location.replace('%s')", url.spec().c_str()); 174 else 175 script = base::StringPrintf("location.href = '%s'", url.spec().c_str()); 176 TestNavigationObserver load_observer(shell()->web_contents(), 1); 177 bool result = ExecuteScript(window->web_contents(), script); 178 EXPECT_TRUE(result); 179 load_observer.Wait(); 180 } 181 182 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 183 command_line->AppendSwitch(switches::kSitePerProcess); 184 } 185}; 186 187// TODO(nasko): Disable this test until out-of-process iframes is ready and the 188// security checks are back in place. 189IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DISABLED_CrossSiteIframe) { 190 ASSERT_TRUE(test_server()->Start()); 191 net::SpawnedTestServer https_server( 192 net::SpawnedTestServer::TYPE_HTTPS, 193 net::SpawnedTestServer::kLocalhost, 194 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 195 ASSERT_TRUE(https_server.Start()); 196 GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); 197 198 NavigateToURL(shell(), main_url); 199 200 SitePerProcessWebContentsObserver observer(shell()->web_contents()); 201 { 202 // Load same-site page into Iframe. 203 GURL http_url(test_server()->GetURL("files/title1.html")); 204 EXPECT_TRUE(NavigateIframeToURL(shell(), http_url, "test")); 205 EXPECT_EQ(observer.navigation_url(), http_url); 206 EXPECT_TRUE(observer.navigation_succeeded()); 207 } 208 209 { 210 // Load cross-site page into Iframe. 211 GURL https_url(https_server.GetURL("files/title1.html")); 212 EXPECT_TRUE(NavigateIframeToURL(shell(), https_url, "test")); 213 EXPECT_EQ(observer.navigation_url(), https_url); 214 EXPECT_FALSE(observer.navigation_succeeded()); 215 } 216} 217 218// TODO(nasko): Disable this test until out-of-process iframes is ready and the 219// security checks are back in place. 220IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 221 DISABLED_CrossSiteIframeRedirectOnce) { 222 ASSERT_TRUE(test_server()->Start()); 223 net::SpawnedTestServer https_server( 224 net::SpawnedTestServer::TYPE_HTTPS, 225 net::SpawnedTestServer::kLocalhost, 226 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 227 ASSERT_TRUE(https_server.Start()); 228 229 GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); 230 GURL http_url(test_server()->GetURL("files/title1.html")); 231 GURL https_url(https_server.GetURL("files/title1.html")); 232 233 NavigateToURL(shell(), main_url); 234 235 SitePerProcessWebContentsObserver observer(shell()->web_contents()); 236 { 237 // Load cross-site client-redirect page into Iframe. 238 // Should be blocked. 239 GURL client_redirect_https_url(https_server.GetURL( 240 "client-redirect?files/title1.html")); 241 EXPECT_TRUE(NavigateIframeToURL(shell(), 242 client_redirect_https_url, "test")); 243 // DidFailProvisionalLoad when navigating to client_redirect_https_url. 244 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url); 245 EXPECT_FALSE(observer.navigation_succeeded()); 246 } 247 248 { 249 // Load cross-site server-redirect page into Iframe, 250 // which redirects to same-site page. 251 GURL server_redirect_http_url(https_server.GetURL( 252 "server-redirect?" + http_url.spec())); 253 EXPECT_TRUE(NavigateIframeToURL(shell(), 254 server_redirect_http_url, "test")); 255 EXPECT_EQ(observer.navigation_url(), http_url); 256 EXPECT_TRUE(observer.navigation_succeeded()); 257 } 258 259 { 260 // Load cross-site server-redirect page into Iframe, 261 // which redirects to cross-site page. 262 GURL server_redirect_http_url(https_server.GetURL( 263 "server-redirect?files/title1.html")); 264 EXPECT_TRUE(NavigateIframeToURL(shell(), 265 server_redirect_http_url, "test")); 266 // DidFailProvisionalLoad when navigating to https_url. 267 EXPECT_EQ(observer.navigation_url(), https_url); 268 EXPECT_FALSE(observer.navigation_succeeded()); 269 } 270 271 { 272 // Load same-site server-redirect page into Iframe, 273 // which redirects to cross-site page. 274 GURL server_redirect_http_url(test_server()->GetURL( 275 "server-redirect?" + https_url.spec())); 276 EXPECT_TRUE(NavigateIframeToURL(shell(), 277 server_redirect_http_url, "test")); 278 279 EXPECT_EQ(observer.navigation_url(), https_url); 280 EXPECT_FALSE(observer.navigation_succeeded()); 281 } 282 283 { 284 // Load same-site client-redirect page into Iframe, 285 // which redirects to cross-site page. 286 GURL client_redirect_http_url(test_server()->GetURL( 287 "client-redirect?" + https_url.spec())); 288 289 RedirectNotificationObserver load_observer2( 290 NOTIFICATION_LOAD_STOP, 291 Source<NavigationController>( 292 &shell()->web_contents()->GetController())); 293 294 EXPECT_TRUE(NavigateIframeToURL(shell(), 295 client_redirect_http_url, "test")); 296 297 // Same-site Client-Redirect Page should be loaded successfully. 298 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); 299 EXPECT_TRUE(observer.navigation_succeeded()); 300 301 // Redirecting to Cross-site Page should be blocked. 302 load_observer2.Wait(); 303 EXPECT_EQ(observer.navigation_url(), https_url); 304 EXPECT_FALSE(observer.navigation_succeeded()); 305 } 306 307 { 308 // Load same-site server-redirect page into Iframe, 309 // which redirects to same-site page. 310 GURL server_redirect_http_url(test_server()->GetURL( 311 "server-redirect?files/title1.html")); 312 EXPECT_TRUE(NavigateIframeToURL(shell(), 313 server_redirect_http_url, "test")); 314 EXPECT_EQ(observer.navigation_url(), http_url); 315 EXPECT_TRUE(observer.navigation_succeeded()); 316 } 317 318 { 319 // Load same-site client-redirect page into Iframe, 320 // which redirects to same-site page. 321 GURL client_redirect_http_url(test_server()->GetURL( 322 "client-redirect?" + http_url.spec())); 323 RedirectNotificationObserver load_observer2( 324 NOTIFICATION_LOAD_STOP, 325 Source<NavigationController>( 326 &shell()->web_contents()->GetController())); 327 328 EXPECT_TRUE(NavigateIframeToURL(shell(), 329 client_redirect_http_url, "test")); 330 331 // Same-site Client-Redirect Page should be loaded successfully. 332 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); 333 EXPECT_TRUE(observer.navigation_succeeded()); 334 335 // Redirecting to Same-site Page should be loaded successfully. 336 load_observer2.Wait(); 337 EXPECT_EQ(observer.navigation_url(), http_url); 338 EXPECT_TRUE(observer.navigation_succeeded()); 339 } 340} 341 342// TODO(nasko): Disable this test until out-of-process iframes is ready and the 343// security checks are back in place. 344IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 345 DISABLED_CrossSiteIframeRedirectTwice) { 346 ASSERT_TRUE(test_server()->Start()); 347 net::SpawnedTestServer https_server( 348 net::SpawnedTestServer::TYPE_HTTPS, 349 net::SpawnedTestServer::kLocalhost, 350 base::FilePath(FILE_PATH_LITERAL("content/test/data"))); 351 ASSERT_TRUE(https_server.Start()); 352 353 GURL main_url(test_server()->GetURL("files/site_per_process_main.html")); 354 GURL http_url(test_server()->GetURL("files/title1.html")); 355 GURL https_url(https_server.GetURL("files/title1.html")); 356 357 NavigateToURL(shell(), main_url); 358 359 SitePerProcessWebContentsObserver observer(shell()->web_contents()); 360 { 361 // Load client-redirect page pointing to a cross-site client-redirect page, 362 // which eventually redirects back to same-site page. 363 GURL client_redirect_https_url(https_server.GetURL( 364 "client-redirect?" + http_url.spec())); 365 GURL client_redirect_http_url(test_server()->GetURL( 366 "client-redirect?" + client_redirect_https_url.spec())); 367 368 // We should wait until second client redirect get cancelled. 369 RedirectNotificationObserver load_observer2( 370 NOTIFICATION_LOAD_STOP, 371 Source<NavigationController>( 372 &shell()->web_contents()->GetController())); 373 374 EXPECT_TRUE(NavigateIframeToURL(shell(), client_redirect_http_url, "test")); 375 376 // DidFailProvisionalLoad when navigating to client_redirect_https_url. 377 load_observer2.Wait(); 378 EXPECT_EQ(observer.navigation_url(), client_redirect_https_url); 379 EXPECT_FALSE(observer.navigation_succeeded()); 380 } 381 382 { 383 // Load server-redirect page pointing to a cross-site server-redirect page, 384 // which eventually redirect back to same-site page. 385 GURL server_redirect_https_url(https_server.GetURL( 386 "server-redirect?" + http_url.spec())); 387 GURL server_redirect_http_url(test_server()->GetURL( 388 "server-redirect?" + server_redirect_https_url.spec())); 389 EXPECT_TRUE(NavigateIframeToURL(shell(), 390 server_redirect_http_url, "test")); 391 EXPECT_EQ(observer.navigation_url(), http_url); 392 EXPECT_TRUE(observer.navigation_succeeded()); 393 } 394 395 { 396 // Load server-redirect page pointing to a cross-site server-redirect page, 397 // which eventually redirects back to cross-site page. 398 GURL server_redirect_https_url(https_server.GetURL( 399 "server-redirect?" + https_url.spec())); 400 GURL server_redirect_http_url(test_server()->GetURL( 401 "server-redirect?" + server_redirect_https_url.spec())); 402 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test")); 403 404 // DidFailProvisionalLoad when navigating to https_url. 405 EXPECT_EQ(observer.navigation_url(), https_url); 406 EXPECT_FALSE(observer.navigation_succeeded()); 407 } 408 409 { 410 // Load server-redirect page pointing to a cross-site client-redirect page, 411 // which eventually redirects back to same-site page. 412 GURL client_redirect_http_url(https_server.GetURL( 413 "client-redirect?" + http_url.spec())); 414 GURL server_redirect_http_url(test_server()->GetURL( 415 "server-redirect?" + client_redirect_http_url.spec())); 416 EXPECT_TRUE(NavigateIframeToURL(shell(), server_redirect_http_url, "test")); 417 418 // DidFailProvisionalLoad when navigating to client_redirect_http_url. 419 EXPECT_EQ(observer.navigation_url(), client_redirect_http_url); 420 EXPECT_FALSE(observer.navigation_succeeded()); 421 } 422} 423 424// Ensures FrameTree correctly reflects page structure during navigations. 425IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 426 FrameTreeShape) { 427 host_resolver()->AddRule("*", "127.0.0.1"); 428 ASSERT_TRUE(test_server()->Start()); 429 430 GURL base_url = test_server()->GetURL("files/site_isolation/"); 431 GURL::Replacements replace_host; 432 std::string host_str("A.com"); // Must stay in scope with replace_host. 433 replace_host.SetHostStr(host_str); 434 base_url = base_url.ReplaceComponents(replace_host); 435 436 // Load doc without iframes. Verify FrameTree just has root. 437 // Frame tree: 438 // Site-A Root 439 NavigateToURL(shell(), base_url.Resolve("blank.html")); 440 FrameTreeNode* root = 441 static_cast<WebContentsImpl*>(shell()->web_contents())-> 442 GetFrameTree()->root(); 443 EXPECT_EQ(0U, root->child_count()); 444 445 // Add 2 same-site frames. Verify 3 nodes in tree with proper names. 446 // Frame tree: 447 // Site-A Root -- Site-A frame1 448 // \-- Site-A frame2 449 WindowedNotificationObserver observer1( 450 content::NOTIFICATION_LOAD_STOP, 451 content::Source<NavigationController>( 452 &shell()->web_contents()->GetController())); 453 NavigateToURL(shell(), base_url.Resolve("frames-X-X.html")); 454 observer1.Wait(); 455 ASSERT_EQ(2U, root->child_count()); 456 EXPECT_EQ(0U, root->child_at(0)->child_count()); 457 EXPECT_EQ(0U, root->child_at(1)->child_count()); 458} 459 460// TODO(ajwong): Talk with nasko and merge this functionality with 461// FrameTreeShape. 462IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 463 FrameTreeShape2) { 464 host_resolver()->AddRule("*", "127.0.0.1"); 465 ASSERT_TRUE(test_server()->Start()); 466 467 NavigateToURL(shell(), 468 test_server()->GetURL("files/frame_tree/top.html")); 469 470 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); 471 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 472 wc->GetRenderViewHost()); 473 FrameTreeNode* root = wc->GetFrameTree()->root(); 474 475 // Check that the root node is properly created with the frame id of the 476 // initial navigation. 477 ASSERT_EQ(3UL, root->child_count()); 478 EXPECT_EQ(std::string(), root->frame_name()); 479 EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); 480 481 ASSERT_EQ(2UL, root->child_at(0)->child_count()); 482 EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str()); 483 484 // Verify the deepest node exists and has the right name. 485 ASSERT_EQ(2UL, root->child_at(2)->child_count()); 486 EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count()); 487 EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count()); 488 EXPECT_STREQ("3-1-id", 489 root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str()); 490 491 // Navigate to about:blank, which should leave only the root node of the frame 492 // tree in the browser process. 493 NavigateToURL(shell(), test_server()->GetURL("files/title1.html")); 494 495 root = wc->GetFrameTree()->root(); 496 EXPECT_EQ(0UL, root->child_count()); 497 EXPECT_EQ(std::string(), root->frame_name()); 498 EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); 499} 500 501// Test that we can navigate away if the previous renderer doesn't clean up its 502// child frames. 503IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameTreeAfterCrash) { 504 ASSERT_TRUE(test_server()->Start()); 505 NavigateToURL(shell(), 506 test_server()->GetURL("files/frame_tree/top.html")); 507 508 // Crash the renderer so that it doesn't send any FrameDetached messages. 509 WindowedNotificationObserver crash_observer( 510 NOTIFICATION_RENDERER_PROCESS_CLOSED, 511 NotificationService::AllSources()); 512 NavigateToURL(shell(), GURL(kChromeUICrashURL)); 513 crash_observer.Wait(); 514 515 // The frame tree should be cleared, and the frame ID should be reset. 516 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); 517 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( 518 wc->GetRenderViewHost()); 519 FrameTreeNode* root = wc->GetFrameTree()->root(); 520 EXPECT_EQ(0UL, root->child_count()); 521 EXPECT_EQ(FrameTreeNode::kInvalidFrameId, root->frame_id()); 522 EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); 523 524 // Navigate to a new URL. 525 NavigateToURL(shell(), test_server()->GetURL("files/title1.html")); 526 527 // The frame ID should now be set. 528 EXPECT_EQ(0UL, root->child_count()); 529 EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id()); 530 EXPECT_EQ(rvh->main_frame_id(), root->frame_id()); 531} 532 533// Test that we can navigate away if the previous renderer doesn't clean up its 534// child frames. 535IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateWithLeftoverFrames) { 536 host_resolver()->AddRule("*", "127.0.0.1"); 537 ASSERT_TRUE(test_server()->Start()); 538 539 GURL base_url = test_server()->GetURL("files/site_isolation/"); 540 GURL::Replacements replace_host; 541 std::string host_str("A.com"); // Must stay in scope with replace_host. 542 replace_host.SetHostStr(host_str); 543 base_url = base_url.ReplaceComponents(replace_host); 544 545 NavigateToURL(shell(), 546 test_server()->GetURL("files/frame_tree/top.html")); 547 548 // Hang the renderer so that it doesn't send any FrameDetached messages. 549 // (This navigation will never complete, so don't wait for it.) 550 shell()->LoadURL(GURL(kChromeUIHangURL)); 551 552 // Check that the frame tree still has children. 553 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); 554 FrameTreeNode* root = wc->GetFrameTree()->root(); 555 ASSERT_EQ(3UL, root->child_count()); 556 557 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to 558 // wait for the previous navigation to stop. 559 TestNavigationObserver tab_observer(wc, 1); 560 shell()->LoadURL(base_url.Resolve("blank.html")); 561 tab_observer.Wait(); 562 563 // The frame tree should now be cleared, and the frame ID should be valid. 564 EXPECT_EQ(0UL, root->child_count()); 565 EXPECT_NE(FrameTreeNode::kInvalidFrameId, root->frame_id()); 566} 567 568// Tests that the |should_replace_current_entry| flag persists correctly across 569// request transfers that began with a cross-process navigation. 570IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 571 ReplaceEntryCrossProcessThenTranfers) { 572 const NavigationController& controller = 573 shell()->web_contents()->GetController(); 574 host_resolver()->AddRule("*", "127.0.0.1"); 575 ASSERT_TRUE(test_server()->Start()); 576 577 // These must all stay in scope with replace_host. 578 GURL::Replacements replace_host; 579 std::string a_com("A.com"); 580 std::string b_com("B.com"); 581 582 // Navigate to a starting URL, so there is a history entry to replace. 583 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); 584 NavigateToURL(shell(), url1); 585 586 // Force all future navigations to transfer. Note that this includes same-site 587 // navigiations which may cause double process swaps (via OpenURL and then via 588 // transfer). This test intentionally exercises that case. 589 ShellContentBrowserClient::SetSwapProcessesForRedirect(true); 590 591 // Navigate to a page on A.com with entry replacement. This navigation is 592 // cross-site, so the renderer will send it to the browser via OpenURL to give 593 // to a new process. It will then be transferred into yet another process due 594 // to the call above. 595 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); 596 replace_host.SetHostStr(a_com); 597 url2 = url2.ReplaceComponents(replace_host); 598 NavigateToURLContentInitiated(shell(), url2, true); 599 600 // There should be one history entry. url2 should have replaced url1. 601 EXPECT_TRUE(controller.GetPendingEntry() == NULL); 602 EXPECT_EQ(1, controller.GetEntryCount()); 603 EXPECT_EQ(0, controller.GetCurrentEntryIndex()); 604 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); 605 606 // Now navigate as before to a page on B.com, but normally (without 607 // replacement). This will still perform a double process-swap as above, via 608 // OpenURL and then transfer. 609 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3"); 610 replace_host.SetHostStr(b_com); 611 url3 = url3.ReplaceComponents(replace_host); 612 NavigateToURLContentInitiated(shell(), url3, false); 613 614 // There should be two history entries. url2 should have replaced url1. url2 615 // should not have replaced url3. 616 EXPECT_TRUE(controller.GetPendingEntry() == NULL); 617 EXPECT_EQ(2, controller.GetEntryCount()); 618 EXPECT_EQ(1, controller.GetCurrentEntryIndex()); 619 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); 620 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL()); 621} 622 623// Tests that the |should_replace_current_entry| flag persists correctly across 624// request transfers that began with a content-initiated in-process 625// navigation. This test is the same as the test above, except transfering from 626// in-process. 627IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 628 ReplaceEntryInProcessThenTranfers) { 629 const NavigationController& controller = 630 shell()->web_contents()->GetController(); 631 ASSERT_TRUE(test_server()->Start()); 632 633 // Navigate to a starting URL, so there is a history entry to replace. 634 GURL url = test_server()->GetURL("files/site_isolation/blank.html?1"); 635 NavigateToURL(shell(), url); 636 637 // Force all future navigations to transfer. Note that this includes same-site 638 // navigiations which may cause double process swaps (via OpenURL and then via 639 // transfer). All navigations in this test are same-site, so it only swaps 640 // processes via request transfer. 641 ShellContentBrowserClient::SetSwapProcessesForRedirect(true); 642 643 // Navigate in-process with entry replacement. It will then be transferred 644 // into a new one due to the call above. 645 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); 646 NavigateToURLContentInitiated(shell(), url2, true); 647 648 // There should be one history entry. url2 should have replaced url1. 649 EXPECT_TRUE(controller.GetPendingEntry() == NULL); 650 EXPECT_EQ(1, controller.GetEntryCount()); 651 EXPECT_EQ(0, controller.GetCurrentEntryIndex()); 652 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); 653 654 // Now navigate as before, but without replacement. 655 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3"); 656 NavigateToURLContentInitiated(shell(), url3, false); 657 658 // There should be two history entries. url2 should have replaced url1. url2 659 // should not have replaced url3. 660 EXPECT_TRUE(controller.GetPendingEntry() == NULL); 661 EXPECT_EQ(2, controller.GetEntryCount()); 662 EXPECT_EQ(1, controller.GetCurrentEntryIndex()); 663 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL()); 664 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL()); 665} 666 667// Tests that the |should_replace_current_entry| flag persists correctly across 668// request transfers that cross processes twice from renderer policy. 669IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, 670 ReplaceEntryCrossProcessTwice) { 671 const NavigationController& controller = 672 shell()->web_contents()->GetController(); 673 host_resolver()->AddRule("*", "127.0.0.1"); 674 ASSERT_TRUE(test_server()->Start()); 675 676 // These must all stay in scope with replace_host. 677 GURL::Replacements replace_host; 678 std::string a_com("A.com"); 679 std::string b_com("B.com"); 680 681 // Navigate to a starting URL, so there is a history entry to replace. 682 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); 683 NavigateToURL(shell(), url1); 684 685 // Navigate to a page on A.com which redirects to B.com with entry 686 // replacement. This will switch processes via OpenURL twice. First to A.com, 687 // and second in response to the server redirect to B.com. The second swap is 688 // also renderer-initiated via OpenURL because decidePolicyForNavigation is 689 // currently applied on redirects. 690 GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2"); 691 replace_host.SetHostStr(b_com); 692 url2b = url2b.ReplaceComponents(replace_host); 693 GURL url2a = test_server()->GetURL( 694 "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false)); 695 replace_host.SetHostStr(a_com); 696 url2a = url2a.ReplaceComponents(replace_host); 697 NavigateToURLContentInitiated(shell(), url2a, true); 698 699 // There should be one history entry. url2b should have replaced url1. 700 EXPECT_TRUE(controller.GetPendingEntry() == NULL); 701 EXPECT_EQ(1, controller.GetEntryCount()); 702 EXPECT_EQ(0, controller.GetCurrentEntryIndex()); 703 EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL()); 704 705 // Now repeat without replacement. 706 GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3"); 707 replace_host.SetHostStr(b_com); 708 url3b = url3b.ReplaceComponents(replace_host); 709 GURL url3a = test_server()->GetURL( 710 "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false)); 711 replace_host.SetHostStr(a_com); 712 url3a = url3a.ReplaceComponents(replace_host); 713 NavigateToURLContentInitiated(shell(), url3a, false); 714 715 // There should be two history entries. url2b should have replaced url1. url2b 716 // should not have replaced url3b. 717 EXPECT_TRUE(controller.GetPendingEntry() == NULL); 718 EXPECT_EQ(2, controller.GetEntryCount()); 719 EXPECT_EQ(1, controller.GetCurrentEntryIndex()); 720 EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL()); 721 EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL()); 722} 723 724} // namespace content 725