// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/macros.h" #include "base/time/time.h" #include "build/build_config.h" #include "content/browser/frame_host/navigation_controller_impl.h" #include "content/browser/frame_host/navigation_entry_impl.h" #include "content/browser/frame_host/navigation_request.h" #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/frame_host/navigator_impl.h" #include "content/browser/frame_host/render_frame_host_manager.h" #include "content/browser/site_instance_impl.h" #include "content/browser/streams/stream.h" #include "content/common/frame_messages.h" #include "content/common/navigation_params.h" #include "content/common/site_isolation_policy.h" #include "content/public/browser/stream_handle.h" #include "content/public/common/url_constants.h" #include "content/public/common/url_utils.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_utils.h" #include "content/test/browser_side_navigation_test_utils.h" #include "content/test/test_navigation_url_loader.h" #include "content/test/test_render_frame_host.h" #include "content/test/test_web_contents.h" #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/url_request/redirect_info.h" #include "ui/base/page_transition_types.h" #include "url/url_constants.h" #if !defined(OS_ANDROID) #include "content/browser/compositor/test/no_transport_image_transport_factory.h" #endif namespace content { class NavigatorTestWithBrowserSideNavigation : public RenderViewHostImplTestHarness { public: // Re-defines the private RenderFrameHostManager::SiteInstanceDescriptor here // to allow access to it from tests. typedef RenderFrameHostManager::SiteInstanceDescriptor SiteInstanceDescriptor; void SetUp() override { #if !defined(OS_ANDROID) ImageTransportFactory::InitializeForUnitTests( scoped_ptr( new NoTransportImageTransportFactory)); #endif EnableBrowserSideNavigation(); RenderViewHostImplTestHarness::SetUp(); } void TearDown() override { RenderViewHostImplTestHarness::TearDown(); #if !defined(OS_ANDROID) ImageTransportFactory::Terminate(); #endif } TestNavigationURLLoader* GetLoaderForNavigationRequest( NavigationRequest* request) const { return static_cast(request->loader_for_testing()); } // Requests a navigation of the specified FrameTreeNode to the specified URL; // returns the unique ID of the pending NavigationEntry. int RequestNavigation(FrameTreeNode* node, const GURL& url) { return RequestNavigationWithParameters(node, url, Referrer(), ui::PAGE_TRANSITION_LINK); } // Requests a navigation of the specified FrameTreeNode to the specified URL, // using other specified parameters; returns the unique ID of the pending // NavigationEntry. int RequestNavigationWithParameters( FrameTreeNode* node, const GURL& url, const Referrer& referrer, ui::PageTransition transition_type) { NavigationController::LoadURLParams load_params(url); load_params.frame_tree_node_id = node->frame_tree_node_id(); load_params.referrer = referrer; load_params.transition_type = transition_type; controller().LoadURLWithParams(load_params); return controller().GetPendingEntry()->GetUniqueID(); } TestRenderFrameHost* GetSpeculativeRenderFrameHost(FrameTreeNode* node) { return static_cast( node->render_manager()->speculative_render_frame_host_.get()); } // Checks if this RenderFrameHost sent a single FrameMsg_CommitNavigation // since the last clearing of the sink. // Note: caller must invoke ClearMessages on the sink at some point before // the tracked commit happens to clear up commit messages from previous // navigations. bool DidRenderFrameHostRequestCommit(TestRenderFrameHost* rfh) { const IPC::Message* message = rfh->GetProcess()->sink().GetUniqueMessageMatching( FrameMsg_CommitNavigation::ID); return message && rfh->GetRoutingID() == message->routing_id(); } SiteInstance* ConvertToSiteInstance(RenderFrameHostManager* rfhm, const SiteInstanceDescriptor& descriptor, SiteInstance* candidate_instance) { return rfhm->ConvertToSiteInstance(descriptor, candidate_instance); } }; // PlzNavigate: Test a complete browser-initiated navigation starting with a // non-live renderer. TEST_F(NavigatorTestWithBrowserSideNavigation, SimpleBrowserInitiatedNavigationFromNonLiveRenderer) { const GURL kUrl("http://chromium.org/"); EXPECT_FALSE(main_test_rfh()->IsRenderFrameLive()); // Start a browser-initiated navigation. int32_t site_instance_id = main_test_rfh()->GetSiteInstance()->GetId(); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); int entry_id = RequestNavigation(node, kUrl); NavigationRequest* request = node->navigation_request(); ASSERT_TRUE(request); EXPECT_EQ(kUrl, request->common_params().url); EXPECT_TRUE(request->browser_initiated()); // As there's no live renderer the navigation should not wait for a // beforeUnload ACK from the renderer and start right away. EXPECT_EQ(NavigationRequest::STARTED, request->state()); ASSERT_TRUE(GetLoaderForNavigationRequest(request)); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); EXPECT_FALSE(node->render_manager()->pending_frame_host()); // Have the current RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request) ->CallOnResponseStarted(response, MakeEmptyStream()); EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh())); EXPECT_TRUE(main_test_rfh()->is_loading()); EXPECT_FALSE(node->navigation_request()); // Commit the navigation. main_test_rfh()->SendNavigate(0, entry_id, true, kUrl); EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state()); EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrl), main_test_rfh()->GetSiteInstance()->GetSiteURL()); EXPECT_EQ(kUrl, contents()->GetLastCommittedURL()); EXPECT_FALSE(node->render_manager()->pending_frame_host()); // The main RenderFrameHost should not have been changed, and the renderer // should have been initialized. EXPECT_EQ(site_instance_id, main_test_rfh()->GetSiteInstance()->GetId()); EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive()); // After a navigation is finished no speculative RenderFrameHost should // exist. EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); // With PlzNavigate enabled a pending RenderFrameHost should never exist. EXPECT_FALSE(node->render_manager()->pending_frame_host()); } // PlzNavigate: Test a complete renderer-initiated same-site navigation. TEST_F(NavigatorTestWithBrowserSideNavigation, SimpleRendererInitiatedSameSiteNavigation) { const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.chromium.org/Home"); contents()->NavigateAndCommit(kUrl1); EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive()); // Start a renderer-initiated non-user-initiated navigation. process()->sink().ClearMessages(); main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, false); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); NavigationRequest* request = node->navigation_request(); ASSERT_TRUE(request); // The navigation is immediately started as there's no need to wait for // beforeUnload to be executed. EXPECT_EQ(NavigationRequest::STARTED, request->state()); EXPECT_FALSE(request->begin_params().has_user_gesture); EXPECT_EQ(kUrl2, request->common_params().url); EXPECT_FALSE(request->browser_initiated()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); // Have the current RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request) ->CallOnResponseStarted(response, MakeEmptyStream()); EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh())); EXPECT_TRUE(main_test_rfh()->is_loading()); EXPECT_FALSE(node->navigation_request()); // Commit the navigation. main_test_rfh()->SendNavigate(1, 0, true, kUrl2); EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state()); EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrl2), main_test_rfh()->GetSiteInstance()->GetSiteURL()); EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); EXPECT_FALSE(node->render_manager()->pending_frame_host()); } // PlzNavigate: Test a complete renderer-initiated navigation that should be // cross-site but does not result in a SiteInstance swap because its // renderer-initiated. TEST_F(NavigatorTestWithBrowserSideNavigation, SimpleRendererInitiatedCrossSiteNavigation) { const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com"); contents()->NavigateAndCommit(kUrl1); EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive()); int32_t site_instance_id_1 = main_test_rfh()->GetSiteInstance()->GetId(); // Start a renderer-initiated non-user-initiated navigation. process()->sink().ClearMessages(); main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, false); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); NavigationRequest* request = node->navigation_request(); ASSERT_TRUE(request); // The navigation is immediately started as there's no need to wait for // beforeUnload to be executed. EXPECT_EQ(NavigationRequest::STARTED, request->state()); EXPECT_FALSE(request->begin_params().has_user_gesture); EXPECT_EQ(kUrl2, request->common_params().url); EXPECT_FALSE(request->browser_initiated()); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // Have the current RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request) ->CallOnResponseStarted(response, MakeEmptyStream()); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE( DidRenderFrameHostRequestCommit(GetSpeculativeRenderFrameHost(node))); } else { EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh())); } EXPECT_TRUE(main_test_rfh()->is_loading()); EXPECT_FALSE(node->navigation_request()); // Commit the navigation. main_test_rfh()->SendNavigate(1, 0, true, kUrl2); EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state()); EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); EXPECT_FALSE(node->render_manager()->pending_frame_host()); // The SiteInstance did not change. EXPECT_EQ(site_instance_id_1, main_test_rfh()->GetSiteInstance()->GetId()); } // PlzNavigate: Test that a beforeUnload denial cancels the navigation. TEST_F(NavigatorTestWithBrowserSideNavigation, BeforeUnloadDenialCancelNavigation) { const GURL kUrl1("http://www.google.com/"); const GURL kUrl2("http://www.chromium.org/"); contents()->NavigateAndCommit(kUrl1); // Start a new navigation. FrameTreeNode* node = main_test_rfh()->frame_tree_node(); RequestNavigation(node, kUrl2); NavigationRequest* request = node->navigation_request(); ASSERT_TRUE(request); EXPECT_TRUE(request->browser_initiated()); EXPECT_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE, request->state()); EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); RenderFrameDeletedObserver rfh_deleted_observer( GetSpeculativeRenderFrameHost(node)); // Simulate a beforeUnload denial. main_test_rfh()->SendBeforeUnloadACK(false); EXPECT_FALSE(node->navigation_request()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); EXPECT_TRUE(rfh_deleted_observer.deleted()); } // PlzNavigate: Test that a proper NavigationRequest is created by // RequestNavigation. TEST_F(NavigatorTestWithBrowserSideNavigation, BeginNavigation) { const GURL kUrl1("http://www.google.com/"); const GURL kUrl2("http://www.chromium.org/"); const GURL kUrl3("http://www.gmail.com/"); contents()->NavigateAndCommit(kUrl1); // Add a subframe. FrameTreeNode* root_node = contents()->GetFrameTree()->root(); TestRenderFrameHost* subframe_rfh = main_test_rfh()->AppendChild("Child"); ASSERT_TRUE(subframe_rfh); // Start a navigation at the subframe. FrameTreeNode* subframe_node = subframe_rfh->frame_tree_node(); RequestNavigation(subframe_node, kUrl2); NavigationRequest* subframe_request = subframe_node->navigation_request(); TestNavigationURLLoader* subframe_loader = GetLoaderForNavigationRequest(subframe_request); // Subframe navigations should start right away as they don't have to request // beforeUnload to run at the renderer. ASSERT_TRUE(subframe_request); ASSERT_TRUE(subframe_loader); EXPECT_EQ(NavigationRequest::STARTED, subframe_request->state()); EXPECT_EQ(kUrl2, subframe_request->common_params().url); EXPECT_EQ(kUrl2, subframe_loader->request_info()->common_params.url); // First party for cookies url should be that of the main frame. EXPECT_EQ(kUrl1, subframe_loader->request_info()->first_party_for_cookies); EXPECT_FALSE(subframe_loader->request_info()->is_main_frame); EXPECT_TRUE(subframe_loader->request_info()->parent_is_main_frame); EXPECT_TRUE(subframe_request->browser_initiated()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(root_node)); // Subframe navigations should never create a speculative RenderFrameHost, // unless site-per-process is enabled. In that case, as the subframe // navigation is to a different site and is still ongoing, it should have one. if (AreAllSitesIsolatedForTesting()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(subframe_node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(subframe_node)); } // Now start a navigation at the root node. RequestNavigation(root_node, kUrl3); NavigationRequest* main_request = root_node->navigation_request(); ASSERT_TRUE(main_request); EXPECT_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE, main_request->state()); // Main frame navigation to a different site should use a speculative // RenderFrameHost. EXPECT_TRUE(GetSpeculativeRenderFrameHost(root_node)); // Simulate a BeforeUnloadACK IPC on the main frame. main_test_rfh()->SendBeforeUnloadACK(true); TestNavigationURLLoader* main_loader = GetLoaderForNavigationRequest(main_request); EXPECT_EQ(kUrl3, main_request->common_params().url); EXPECT_EQ(kUrl3, main_loader->request_info()->common_params.url); EXPECT_EQ(kUrl3, main_loader->request_info()->first_party_for_cookies); EXPECT_TRUE(main_loader->request_info()->is_main_frame); EXPECT_FALSE(main_loader->request_info()->parent_is_main_frame); EXPECT_TRUE(main_request->browser_initiated()); // BeforeUnloadACK was received from the renderer so the navigation should // have started. EXPECT_EQ(NavigationRequest::STARTED, main_request->state()); EXPECT_TRUE(GetSpeculativeRenderFrameHost(root_node)); // As the main frame hasn't yet committed the subframe still exists. Thus, the // above situation regarding subframe navigations is valid here. if (AreAllSitesIsolatedForTesting()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(subframe_node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(subframe_node)); } } // PlzNavigate: Test that committing an HTTP 204 or HTTP 205 response cancels // the navigation. TEST_F(NavigatorTestWithBrowserSideNavigation, NoContent) { const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); // Load a URL. contents()->NavigateAndCommit(kUrl1); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Navigate to a different site. process()->sink().ClearMessages(); RequestNavigation(node, kUrl2); main_test_rfh()->SendBeforeUnloadACK(true); NavigationRequest* main_request = node->navigation_request(); ASSERT_TRUE(main_request); // Navigations to a different site do create a speculative RenderFrameHost. EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); // Commit an HTTP 204 response. scoped_refptr response(new ResourceResponse); const char kNoContentHeaders[] = "HTTP/1.1 204 No Content\0\0"; response->head.headers = new net::HttpResponseHeaders( std::string(kNoContentHeaders, arraysize(kNoContentHeaders))); GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted( response, MakeEmptyStream()); // There should be no pending nor speculative RenderFrameHost; the navigation // was aborted. EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh())); EXPECT_FALSE(node->navigation_request()); EXPECT_FALSE(node->render_manager()->pending_frame_host()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); // Now, repeat the test with 205 Reset Content. // Navigate to a different site again. process()->sink().ClearMessages(); RequestNavigation(node, kUrl2); main_test_rfh()->SendBeforeUnloadACK(true); main_request = node->navigation_request(); ASSERT_TRUE(main_request); EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); // Commit an HTTP 205 response. response = new ResourceResponse; const char kResetContentHeaders[] = "HTTP/1.1 205 Reset Content\0\0"; response->head.headers = new net::HttpResponseHeaders( std::string(kResetContentHeaders, arraysize(kResetContentHeaders))); GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted( response, MakeEmptyStream()); // There should be no pending nor speculative RenderFrameHost; the navigation // was aborted. EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh())); EXPECT_FALSE(node->navigation_request()); EXPECT_FALSE(node->render_manager()->pending_frame_host()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // PlzNavigate: Test that a new RenderFrameHost is created when doing a cross // site navigation. TEST_F(NavigatorTestWithBrowserSideNavigation, CrossSiteNavigation) { const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); contents()->NavigateAndCommit(kUrl1); RenderFrameHostImpl* initial_rfh = main_test_rfh(); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Navigate to a different site. process()->sink().ClearMessages(); int entry_id = RequestNavigation(node, kUrl2); NavigationRequest* main_request = node->navigation_request(); ASSERT_TRUE(main_request); TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); // Receive the beforeUnload ACK. main_test_rfh()->SendBeforeUnloadACK(true); EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node)); EXPECT_FALSE(contents()->CrossProcessNavigationPending()); scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted( response, MakeEmptyStream()); EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node)); EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh)); EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh())); EXPECT_TRUE(contents()->CrossProcessNavigationPending()); speculative_rfh->SendNavigate(0, entry_id, true, kUrl2); RenderFrameHostImpl* final_rfh = main_test_rfh(); EXPECT_EQ(speculative_rfh, final_rfh); EXPECT_NE(initial_rfh, final_rfh); EXPECT_TRUE(final_rfh->IsRenderFrameLive()); EXPECT_TRUE(final_rfh->render_view_host()->IsRenderViewLive()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // PlzNavigate: Test that redirects are followed and the speculative // RenderFrameHost logic behaves as expected. TEST_F(NavigatorTestWithBrowserSideNavigation, RedirectCrossSite) { const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); contents()->NavigateAndCommit(kUrl1); RenderFrameHostImpl* rfh = main_test_rfh(); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Navigate to a URL on the same site. process()->sink().ClearMessages(); int entry_id = RequestNavigation(node, kUrl1); main_test_rfh()->SendBeforeUnloadACK(true); NavigationRequest* main_request = node->navigation_request(); ASSERT_TRUE(main_request); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); // It then redirects to another site. GetLoaderForNavigationRequest(main_request)->SimulateServerRedirect(kUrl2); // The redirect should have been followed. EXPECT_EQ(1, GetLoaderForNavigationRequest(main_request)->redirect_count()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); // Have the RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted( response, MakeEmptyStream()); TestRenderFrameHost* final_speculative_rfh = GetSpeculativeRenderFrameHost(node); EXPECT_TRUE(final_speculative_rfh); EXPECT_TRUE(DidRenderFrameHostRequestCommit(final_speculative_rfh)); // Commit the navigation. final_speculative_rfh->SendNavigate(0, entry_id, true, kUrl2); RenderFrameHostImpl* final_rfh = main_test_rfh(); ASSERT_TRUE(final_rfh); EXPECT_NE(rfh, final_rfh); EXPECT_EQ(final_speculative_rfh, final_rfh); EXPECT_TRUE(final_rfh->IsRenderFrameLive()); EXPECT_TRUE(final_rfh->render_view_host()->IsRenderViewLive()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // PlzNavigate: Test that a navigation is canceled if another browser-initiated // request has been issued in the meantime. Also confirms that the speculative // RenderFrameHost is correctly updated in the process. TEST_F(NavigatorTestWithBrowserSideNavigation, BrowserInitiatedNavigationCancel) { const GURL kUrl0("http://www.wikipedia.org/"); const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl1_site = SiteInstance::GetSiteForURL(browser_context(), kUrl1); const GURL kUrl2("http://www.google.com/"); const GURL kUrl2_site = SiteInstance::GetSiteForURL(browser_context(), kUrl2); // Initialization. contents()->NavigateAndCommit(kUrl0); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Request navigation to the 1st URL. process()->sink().ClearMessages(); RequestNavigation(node, kUrl1); main_test_rfh()->SendBeforeUnloadACK(true); NavigationRequest* request1 = node->navigation_request(); ASSERT_TRUE(request1); EXPECT_EQ(kUrl1, request1->common_params().url); EXPECT_TRUE(request1->browser_initiated()); base::WeakPtr loader1 = GetLoaderForNavigationRequest(request1)->AsWeakPtr(); EXPECT_TRUE(loader1); // Confirm a speculative RenderFrameHost was created. TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); int32_t site_instance_id_1 = speculative_rfh->GetSiteInstance()->GetId(); EXPECT_EQ(kUrl1_site, speculative_rfh->GetSiteInstance()->GetSiteURL()); // Request navigation to the 2nd URL; the NavigationRequest must have been // replaced by a new one with a different URL. int entry_id = RequestNavigation(node, kUrl2); main_test_rfh()->SendBeforeUnloadACK(true); NavigationRequest* request2 = node->navigation_request(); ASSERT_TRUE(request2); EXPECT_EQ(kUrl2, request2->common_params().url); EXPECT_TRUE(request2->browser_initiated()); // Confirm that the first loader got destroyed. EXPECT_FALSE(loader1); // Confirm that a new speculative RenderFrameHost was created. speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); int32_t site_instance_id_2 = speculative_rfh->GetSiteInstance()->GetId(); EXPECT_NE(site_instance_id_1, site_instance_id_2); // Have the RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request2)->CallOnResponseStarted( response, MakeEmptyStream()); EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh)); EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh())); // Commit the navigation. speculative_rfh->SendNavigate(0, entry_id, true, kUrl2); // Confirm that the commit corresponds to the new request. ASSERT_TRUE(main_test_rfh()); EXPECT_EQ(kUrl2_site, main_test_rfh()->GetSiteInstance()->GetSiteURL()); EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL()); // Confirm that the committed RenderFrameHost is the latest speculative one. EXPECT_EQ(site_instance_id_2, main_test_rfh()->GetSiteInstance()->GetId()); } // PlzNavigate: Test that a browser-initiated navigation is canceled if a // renderer-initiated user-initiated request has been issued in the meantime. TEST_F(NavigatorTestWithBrowserSideNavigation, RendererUserInitiatedNavigationCancel) { const GURL kUrl0("http://www.wikipedia.org/"); const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); // Initialization. contents()->NavigateAndCommit(kUrl0); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Start a browser-initiated navigation to the 1st URL and receive its // beforeUnload ACK. process()->sink().ClearMessages(); RequestNavigation(node, kUrl1); main_test_rfh()->SendBeforeUnloadACK(true); NavigationRequest* request1 = node->navigation_request(); ASSERT_TRUE(request1); EXPECT_EQ(kUrl1, request1->common_params().url); EXPECT_TRUE(request1->browser_initiated()); base::WeakPtr loader1 = GetLoaderForNavigationRequest(request1)->AsWeakPtr(); EXPECT_TRUE(loader1); // Confirm that a speculative RenderFrameHost was created. ASSERT_TRUE(GetSpeculativeRenderFrameHost(node)); // Now receive a renderer-initiated user-initiated request. It should replace // the current NavigationRequest. main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, true); NavigationRequest* request2 = node->navigation_request(); ASSERT_TRUE(request2); EXPECT_EQ(kUrl2, request2->common_params().url); EXPECT_FALSE(request2->browser_initiated()); EXPECT_TRUE(request2->begin_params().has_user_gesture); // Confirm that the first loader got destroyed. EXPECT_FALSE(loader1); // Confirm that the speculative RenderFrameHost was destroyed in the non // SitePerProcess case. if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // Have the RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request2) ->CallOnResponseStarted(response, MakeEmptyStream()); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE( DidRenderFrameHostRequestCommit(GetSpeculativeRenderFrameHost(node))); } else { EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh())); } // Commit the navigation. main_test_rfh()->SendNavigate(1, 0, true, kUrl2); // Confirm that the commit corresponds to the new request. ASSERT_TRUE(main_test_rfh()); EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL()); } // PlzNavigate: Test that a renderer-initiated user-initiated navigation is NOT // canceled if a renderer-initiated non-user-initiated request is issued in the // meantime. TEST_F(NavigatorTestWithBrowserSideNavigation, RendererNonUserInitiatedNavigationDoesntCancelRendererUserInitiated) { const GURL kUrl0("http://www.wikipedia.org/"); const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); // Initialization. contents()->NavigateAndCommit(kUrl0); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Start a renderer-initiated user-initiated navigation to the 1st URL. process()->sink().ClearMessages(); main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1, true); NavigationRequest* request1 = node->navigation_request(); ASSERT_TRUE(request1); EXPECT_EQ(kUrl1, request1->common_params().url); EXPECT_FALSE(request1->browser_initiated()); EXPECT_TRUE(request1->begin_params().has_user_gesture); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // Now receive a renderer-initiated non-user-initiated request. Nothing should // change. main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, false); NavigationRequest* request2 = node->navigation_request(); ASSERT_TRUE(request2); EXPECT_EQ(request1, request2); EXPECT_EQ(kUrl1, request2->common_params().url); EXPECT_FALSE(request2->browser_initiated()); EXPECT_TRUE(request2->begin_params().has_user_gesture); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // Have the RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request2) ->CallOnResponseStarted(response, MakeEmptyStream()); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE( DidRenderFrameHostRequestCommit(GetSpeculativeRenderFrameHost(node))); } else { EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh())); } // Commit the navigation. main_test_rfh()->SendNavigate(1, 0, true, kUrl1); EXPECT_EQ(kUrl1, contents()->GetLastCommittedURL()); } // PlzNavigate: Test that a browser-initiated navigation is NOT canceled if a // renderer-initiated non-user-initiated request is issued in the meantime. TEST_F(NavigatorTestWithBrowserSideNavigation, RendererNonUserInitiatedNavigationDoesntCancelBrowserInitiated) { const GURL kUrl0("http://www.wikipedia.org/"); const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); // Initialization. contents()->NavigateAndCommit(kUrl0); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Start a browser-initiated navigation to the 1st URL. process()->sink().ClearMessages(); int entry_id = RequestNavigation(node, kUrl1); NavigationRequest* request1 = node->navigation_request(); ASSERT_TRUE(request1); EXPECT_EQ(kUrl1, request1->common_params().url); EXPECT_TRUE(request1->browser_initiated()); EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); // Now receive a renderer-initiated non-user-initiated request. Nothing should // change. main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, false); NavigationRequest* request2 = node->navigation_request(); ASSERT_TRUE(request2); EXPECT_EQ(request1, request2); EXPECT_EQ(kUrl1, request2->common_params().url); EXPECT_TRUE(request2->browser_initiated()); EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); // Now receive the beforeUnload ACK from the still ongoing navigation. main_test_rfh()->SendBeforeUnloadACK(true); TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); // Have the RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request2) ->CallOnResponseStarted(response, MakeEmptyStream()); EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh)); EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh())); // Commit the navigation. speculative_rfh->SendNavigate(0, entry_id, true, kUrl1); EXPECT_EQ(kUrl1, contents()->GetLastCommittedURL()); } // PlzNavigate: Test that a renderer-initiated non-user-initiated navigation is // canceled if a another similar request is issued in the meantime. TEST_F(NavigatorTestWithBrowserSideNavigation, RendererNonUserInitiatedNavigationCancelSimilarNavigation) { const GURL kUrl0("http://www.wikipedia.org/"); const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); // Initialization. contents()->NavigateAndCommit(kUrl0); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); int32_t site_instance_id_0 = main_test_rfh()->GetSiteInstance()->GetId(); // Start a renderer-initiated non-user-initiated navigation to the 1st URL. process()->sink().ClearMessages(); main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1, false); NavigationRequest* request1 = node->navigation_request(); ASSERT_TRUE(request1); EXPECT_EQ(kUrl1, request1->common_params().url); EXPECT_FALSE(request1->browser_initiated()); EXPECT_FALSE(request1->begin_params().has_user_gesture); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } base::WeakPtr loader1 = GetLoaderForNavigationRequest(request1)->AsWeakPtr(); EXPECT_TRUE(loader1); // Now receive a 2nd similar request that should replace the current one. main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, false); NavigationRequest* request2 = node->navigation_request(); EXPECT_EQ(kUrl2, request2->common_params().url); EXPECT_FALSE(request2->browser_initiated()); EXPECT_FALSE(request2->begin_params().has_user_gesture); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE(GetSpeculativeRenderFrameHost(node)); } else { EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // Confirm that the first loader got destroyed. EXPECT_FALSE(loader1); // Have the RenderFrameHost commit the navigation. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(request2) ->CallOnResponseStarted(response, MakeEmptyStream()); if (SiteIsolationPolicy::AreCrossProcessFramesPossible()) { EXPECT_TRUE( DidRenderFrameHostRequestCommit(GetSpeculativeRenderFrameHost(node))); } else { EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh())); } // Commit the navigation. main_test_rfh()->SendNavigate(1, 0, true, kUrl2); EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL()); // The SiteInstance did not change. EXPECT_EQ(site_instance_id_0, main_test_rfh()->GetSiteInstance()->GetId()); } // PlzNavigate: Test that a reload navigation is properly signaled to the // RenderFrame when the navigation can commit. A speculative RenderFrameHost // should not be created at any step. TEST_F(NavigatorTestWithBrowserSideNavigation, Reload) { const GURL kUrl("http://www.google.com/"); contents()->NavigateAndCommit(kUrl); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); controller().Reload(false); int entry_id = controller().GetPendingEntry()->GetUniqueID(); // A NavigationRequest should have been generated. NavigationRequest* main_request = node->navigation_request(); ASSERT_TRUE(main_request != NULL); EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD, main_request->common_params().navigation_type); main_test_rfh()->PrepareForCommit(); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); main_test_rfh()->SendNavigate(0, entry_id, false, kUrl); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); // Now do a shift+reload. controller().ReloadIgnoringCache(false); // A NavigationRequest should have been generated. main_request = node->navigation_request(); ASSERT_TRUE(main_request != NULL); EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE, main_request->common_params().navigation_type); main_test_rfh()->PrepareForCommit(); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // PlzNavigate: Confirm that a speculative RenderFrameHost is used when // navigating from one site to another. TEST_F(NavigatorTestWithBrowserSideNavigation, SpeculativeRendererWorksBaseCase) { // Navigate to an initial site. const GURL kUrlInit("http://wikipedia.org/"); contents()->NavigateAndCommit(kUrlInit); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Begin navigating to another site. const GURL kUrl("http://google.com/"); process()->sink().ClearMessages(); int entry_id = RequestNavigation(node, kUrl); TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); EXPECT_NE(speculative_rfh, main_test_rfh()); // Receive the beforeUnload ACK. main_test_rfh()->SendBeforeUnloadACK(true); EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node)); EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrl), speculative_rfh->GetSiteInstance()->GetSiteURL()); EXPECT_FALSE(node->render_manager()->pending_frame_host()); int32_t site_instance_id = speculative_rfh->GetSiteInstance()->GetId(); // Ask Navigator to commit the navigation by simulating a call to // OnResponseStarted. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(node->navigation_request()) ->CallOnResponseStarted(response, MakeEmptyStream()); EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node)); EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh)); EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId()); EXPECT_FALSE(node->render_manager()->pending_frame_host()); // Invoke OnDidCommitProvisionalLoad. speculative_rfh->SendNavigate(0, entry_id, true, kUrl); EXPECT_EQ(site_instance_id, main_test_rfh()->GetSiteInstance()->GetId()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); EXPECT_FALSE(node->render_manager()->pending_frame_host()); } // PlzNavigate: Confirm that a speculative RenderFrameHost is thrown away when // the final URL's site differs from the initial one due to redirects. TEST_F(NavigatorTestWithBrowserSideNavigation, SpeculativeRendererDiscardedAfterRedirectToAnotherSite) { // Navigate to an initial site. const GURL kUrlInit("http://wikipedia.org/"); contents()->NavigateAndCommit(kUrlInit); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); int32_t init_site_instance_id = main_test_rfh()->GetSiteInstance()->GetId(); // Begin navigating to another site. const GURL kUrl("http://google.com/"); process()->sink().ClearMessages(); int entry_id = RequestNavigation(node, kUrl); TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); int32_t site_instance_id = speculative_rfh->GetSiteInstance()->GetId(); RenderFrameDeletedObserver rfh_deleted_observer(speculative_rfh); EXPECT_NE(init_site_instance_id, site_instance_id); EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId()); EXPECT_NE(speculative_rfh, main_test_rfh()); EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrl), speculative_rfh->GetSiteInstance()->GetSiteURL()); // Receive the beforeUnload ACK. main_test_rfh()->SendBeforeUnloadACK(true); EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node)); // It then redirects to yet another site. NavigationRequest* main_request = node->navigation_request(); ASSERT_TRUE(main_request); const GURL kUrlRedirect("https://www.google.com/"); GetLoaderForNavigationRequest(main_request) ->SimulateServerRedirect(kUrlRedirect); EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId()); // For now, ensure that the speculative RenderFrameHost does not change after // the redirect. // TODO(carlosk): once the speculative RenderFrameHost updates with redirects // this next check will be changed to verify that it actually happens. EXPECT_EQ(speculative_rfh, GetSpeculativeRenderFrameHost(node)); EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId()); EXPECT_FALSE(rfh_deleted_observer.deleted()); // Commit the navigation with Navigator by simulating the call to // OnResponseStarted. scoped_refptr response(new ResourceResponse); GetLoaderForNavigationRequest(main_request) ->CallOnResponseStarted(response, MakeEmptyStream()); speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh)); EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId()); EXPECT_TRUE(rfh_deleted_observer.deleted()); // Once commit happens the speculative RenderFrameHost is updated to match the // known final SiteInstance. EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrlRedirect), speculative_rfh->GetSiteInstance()->GetSiteURL()); int32_t redirect_site_instance_id = speculative_rfh->GetSiteInstance()->GetId(); EXPECT_NE(init_site_instance_id, redirect_site_instance_id); EXPECT_NE(site_instance_id, redirect_site_instance_id); // Invoke OnDidCommitProvisionalLoad. speculative_rfh->SendNavigate(0, entry_id, true, kUrlRedirect); // Check that the speculative RenderFrameHost was swapped in. EXPECT_EQ(redirect_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // PlzNavigate: Verify that data urls are properly handled. TEST_F(NavigatorTestWithBrowserSideNavigation, DataUrls) { const GURL kUrl1("http://wikipedia.org/"); const GURL kUrl2("data:text/html,test"); // Navigate to an initial site. contents()->NavigateAndCommit(kUrl1); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Navigate to a data url. The request should not have been sent to the IO // thread but committed immediately. int entry_id = RequestNavigation(node, kUrl2); TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node); ASSERT_TRUE(speculative_rfh); EXPECT_TRUE(speculative_rfh->is_loading()); EXPECT_FALSE(node->navigation_request()); speculative_rfh->SendNavigate(0, entry_id, true, kUrl2); EXPECT_EQ(main_test_rfh(), speculative_rfh); // Go back to the initial site. contents()->NavigateAndCommit(kUrl1); // Do a renderer-initiated navigation to a data url. The request should not be // sent to the IO thread, nor committed. TestRenderFrameHost* main_rfh = main_test_rfh(); main_rfh->SendRendererInitiatedNavigationRequest(kUrl2, true); EXPECT_TRUE(main_rfh->is_loading()); EXPECT_FALSE(node->navigation_request()); EXPECT_FALSE(GetSpeculativeRenderFrameHost(node)); } // Tests several cases for converting SiteInstanceDescriptors into // SiteInstances: // 1) Pointer to the current SiteInstance. // 2) Pointer to an unrelated SiteInstance. // 3) Same-site URL, related. // 4) Cross-site URL, related. // 5) Same-site URL, unrelated (with and without candidate SiteInstances). // 6) Cross-site URL, unrelated (with candidate SiteInstance). TEST_F(NavigatorTestWithBrowserSideNavigation, SiteInstanceDescriptionConversion) { // Navigate to set a current SiteInstance on the RenderFrameHost. GURL kUrl1("http://a.com"); contents()->NavigateAndCommit(kUrl1); SiteInstance* current_instance = main_test_rfh()->GetSiteInstance(); ASSERT_TRUE(current_instance); // 1) Convert a descriptor pointing to the current instance. RenderFrameHostManager* rfhm = main_test_rfh()->frame_tree_node()->render_manager(); { SiteInstanceDescriptor descriptor(current_instance); SiteInstance* converted_instance = ConvertToSiteInstance(rfhm, descriptor, nullptr); EXPECT_EQ(current_instance, converted_instance); } // 2) Convert a descriptor pointing an instance unrelated to the current one, // with a different site. GURL kUrl2("http://b.com"); scoped_refptr unrelated_instance( SiteInstance::CreateForURL(browser_context(), kUrl2)); EXPECT_FALSE( current_instance->IsRelatedSiteInstance(unrelated_instance.get())); { SiteInstanceDescriptor descriptor(unrelated_instance.get()); SiteInstance* converted_instance = ConvertToSiteInstance(rfhm, descriptor, nullptr); EXPECT_EQ(unrelated_instance.get(), converted_instance); } // 3) Convert a descriptor of a related instance with the same site as the // current one. GURL kUrlSameSiteAs1("http://www.a.com/foo"); { SiteInstanceDescriptor descriptor(browser_context(), kUrlSameSiteAs1, true); SiteInstance* converted_instance = ConvertToSiteInstance(rfhm, descriptor, nullptr); EXPECT_EQ(current_instance, converted_instance); } // 4) Convert a descriptor of a related instance with a site different from // the current one. GURL kUrlSameSiteAs2("http://www.b.com/foo"); scoped_refptr related_instance; { SiteInstanceDescriptor descriptor(browser_context(), kUrlSameSiteAs2, true); related_instance = ConvertToSiteInstance(rfhm, descriptor, nullptr); // Should return a new instance, related to the current, set to the new site // URL. EXPECT_TRUE( current_instance->IsRelatedSiteInstance(related_instance.get())); EXPECT_NE(current_instance, related_instance.get()); EXPECT_NE(unrelated_instance.get(), related_instance.get()); EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlSameSiteAs2), related_instance->GetSiteURL()); } // 5) Convert a descriptor of an unrelated instance with the same site as the // current one, several times, with and without candidate sites. { SiteInstanceDescriptor descriptor(browser_context(), kUrlSameSiteAs1, false); scoped_refptr converted_instance_1 = ConvertToSiteInstance(rfhm, descriptor, nullptr); // Should return a new instance, unrelated to the current one, set to the // provided site URL. EXPECT_FALSE( current_instance->IsRelatedSiteInstance(converted_instance_1.get())); EXPECT_NE(current_instance, converted_instance_1.get()); EXPECT_NE(unrelated_instance.get(), converted_instance_1.get()); EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlSameSiteAs1), converted_instance_1->GetSiteURL()); // Does the same but this time using unrelated_instance as a candidate, // which has a different site. scoped_refptr converted_instance_2 = ConvertToSiteInstance(rfhm, descriptor, unrelated_instance.get()); // Should return yet another new instance, unrelated to the current one, set // to the same site URL. EXPECT_FALSE( current_instance->IsRelatedSiteInstance(converted_instance_2.get())); EXPECT_NE(current_instance, converted_instance_2.get()); EXPECT_NE(unrelated_instance.get(), converted_instance_2.get()); EXPECT_NE(converted_instance_1.get(), converted_instance_2.get()); EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlSameSiteAs1), converted_instance_2->GetSiteURL()); // Converts once more but with |converted_instance_1| as a candidate. SiteInstance* converted_instance_3 = ConvertToSiteInstance(rfhm, descriptor, converted_instance_1.get()); // Should return |converted_instance_1| because its site matches and it is // unrelated to the current SiteInstance. EXPECT_EQ(converted_instance_1.get(), converted_instance_3); } // 6) Convert a descriptor of an unrelated instance with the same site of // related_instance and using it as a candidate. { SiteInstanceDescriptor descriptor(browser_context(), kUrlSameSiteAs2, false); scoped_refptr converted_instance_1 = ConvertToSiteInstance(rfhm, descriptor, related_instance.get()); // Should return a new instance, unrelated to the current, set to the // provided site URL. EXPECT_FALSE( current_instance->IsRelatedSiteInstance(converted_instance_1.get())); EXPECT_NE(related_instance.get(), converted_instance_1.get()); EXPECT_NE(unrelated_instance.get(), converted_instance_1.get()); EXPECT_EQ(SiteInstance::GetSiteForURL(browser_context(), kUrlSameSiteAs2), converted_instance_1->GetSiteURL()); SiteInstance* converted_instance_2 = ConvertToSiteInstance(rfhm, descriptor, unrelated_instance.get()); // Should return |unrelated_instance| because its site matches and it is // unrelated to the current SiteInstance. EXPECT_EQ(unrelated_instance.get(), converted_instance_2); } } namespace { void SetWithinPage(const GURL& url, FrameHostMsg_DidCommitProvisionalLoad_Params* params) { params->was_within_same_page = true; params->url = url; params->origin = url::Origin(url); } } // A renderer process might try and claim that a cross site navigation was // within the same page by setting was_within_same_page = true for // FrameHostMsg_DidCommitProvisionalLoad. Such case should be detected on the // browser side and the renderer process should be killed. TEST_F(NavigatorTestWithBrowserSideNavigation, CrossSiteClaimWithinPage) { const GURL kUrl1("http://www.chromium.org/"); const GURL kUrl2("http://www.google.com/"); contents()->NavigateAndCommit(kUrl1); FrameTreeNode* node = main_test_rfh()->frame_tree_node(); // Navigate to a different site. int entry_id = RequestNavigation(node, kUrl2); main_test_rfh()->PrepareForCommit(); // Claim that the navigation was within same page. int bad_msg_count = process()->bad_msg_count(); GetSpeculativeRenderFrameHost(node)->SendNavigateWithModificationCallback( 0, entry_id, true, kUrl2, base::Bind(SetWithinPage, kUrl1)); EXPECT_EQ(process()->bad_msg_count(), bad_msg_count + 1); } } // namespace content