summaryrefslogtreecommitdiff
path: root/chromium/content/browser/frame_host/navigator_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/frame_host/navigator_impl_unittest.cc')
-rw-r--r--chromium/content/browser/frame_host/navigator_impl_unittest.cc1203
1 files changed, 929 insertions, 274 deletions
diff --git a/chromium/content/browser/frame_host/navigator_impl_unittest.cc b/chromium/content/browser/frame_host/navigator_impl_unittest.cc
index 37a0f243d6d..8dc64af604f 100644
--- a/chromium/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/chromium/content/browser/frame_host/navigator_impl_unittest.cc
@@ -3,10 +3,7 @@
// found in the LICENSE file.
#include "base/command_line.h"
-#include "base/guid.h"
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/test/histogram_tester.h"
#include "base/time/time.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
@@ -15,17 +12,17 @@
#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/loader/navigation_url_loader.h"
-#include "content/browser/loader/navigation_url_loader_delegate.h"
-#include "content/browser/loader/navigation_url_loader_factory.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_registry.h"
+#include "content/common/frame_messages.h"
#include "content/common/navigation_params.h"
#include "content/public/browser/stream_handle.h"
#include "content/public/common/content_switches.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/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"
@@ -36,236 +33,339 @@
namespace content {
-namespace {
-
-class TestNavigationURLLoader
- : public NavigationURLLoader,
- public base::SupportsWeakPtr<TestNavigationURLLoader> {
+class NavigatorTestWithBrowserSideNavigation
+ : public RenderViewHostImplTestHarness {
public:
- TestNavigationURLLoader(const CommonNavigationParams& common_params,
- scoped_ptr<NavigationRequestInfo> request_info,
- NavigationURLLoaderDelegate* delegate)
- : common_params_(common_params),
- request_info_(request_info.Pass()),
- delegate_(delegate),
- redirect_count_(0) {
- }
+ // Re-defines the private RenderFrameHostManager::SiteInstanceDescriptor here
+ // to allow access to it from tests.
+ typedef RenderFrameHostManager::SiteInstanceDescriptor SiteInstanceDescriptor;
- // NavigationURLLoader implementation.
- void FollowRedirect() override { redirect_count_++; }
+ void SetUp() override {
+ EnableBrowserSideNavigation();
+ RenderViewHostImplTestHarness::SetUp();
+ }
- const CommonNavigationParams& common_params() const { return common_params_; }
- NavigationRequestInfo* request_info() const { return request_info_.get(); }
+ TestNavigationURLLoader* GetLoaderForNavigationRequest(
+ NavigationRequest* request) const {
+ return static_cast<TestNavigationURLLoader*>(request->loader_for_testing());
+ }
- void CallOnRequestRedirected(
- const net::RedirectInfo& redirect_info,
- const scoped_refptr<ResourceResponse>& response) {
- delegate_->OnRequestRedirected(redirect_info, response);
+ // 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);
}
- void CallOnResponseStarted(
- const scoped_refptr<ResourceResponse>& response,
- scoped_ptr<StreamHandle> body) {
- delegate_->OnResponseStarted(response, body.Pass());
+ // 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();
}
- int redirect_count() { return redirect_count_; }
+ TestRenderFrameHost* GetSpeculativeRenderFrameHost(FrameTreeNode* node) {
+ return static_cast<TestRenderFrameHost*>(
+ node->render_manager()->speculative_render_frame_host_.get());
+ }
- private:
- CommonNavigationParams common_params_;
- scoped_ptr<NavigationRequestInfo> request_info_;
- NavigationURLLoaderDelegate* delegate_;
- int redirect_count_;
-};
+ // 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 FrameMsg_CommitNavigation* commit_message =
+ static_cast<const FrameMsg_CommitNavigation*>(
+ rfh->GetProcess()->sink().GetUniqueMessageMatching(
+ FrameMsg_CommitNavigation::ID));
+ return commit_message &&
+ rfh->GetRoutingID() == commit_message->routing_id();
+ }
-class TestNavigationURLLoaderFactory : public NavigationURLLoaderFactory {
- public:
- // NavigationURLLoaderFactory implementation.
- scoped_ptr<NavigationURLLoader> CreateLoader(
- BrowserContext* browser_context,
- int64 frame_tree_node_id,
- const CommonNavigationParams& common_params,
- scoped_ptr<NavigationRequestInfo> request_info,
- ResourceRequestBody* request_body,
- NavigationURLLoaderDelegate* delegate) override {
- return scoped_ptr<NavigationURLLoader>(new TestNavigationURLLoader(
- common_params, request_info.Pass(), delegate));
+ SiteInstance* ConvertToSiteInstance(RenderFrameHostManager* rfhm,
+ const SiteInstanceDescriptor& descriptor,
+ SiteInstance* candidate_instance) {
+ return rfhm->ConvertToSiteInstance(descriptor, candidate_instance);
}
};
-} // namespace
+// PlzNavigate: Test a complete browser-initiated navigation starting with a
+// non-live renderer.
+TEST_F(NavigatorTestWithBrowserSideNavigation,
+ SimpleBrowserInitiatedNavigationFromNonLiveRenderer) {
+ const GURL kUrl("http://chromium.org/");
-class NavigatorTest : public RenderViewHostImplTestHarness {
- public:
- NavigatorTest() : stream_registry_(new StreamRegistry) {}
+ EXPECT_FALSE(main_test_rfh()->IsRenderFrameLive());
- void SetUp() override {
- RenderViewHostImplTestHarness::SetUp();
- loader_factory_.reset(new TestNavigationURLLoaderFactory);
- NavigationURLLoader::SetFactoryForTesting(loader_factory_.get());
- }
+ // Start a browser-initiated navigation.
+ int32 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());
- void TearDown() override {
- NavigationURLLoader::SetFactoryForTesting(nullptr);
- loader_factory_.reset();
- RenderViewHostImplTestHarness::TearDown();
- }
+ // Have the current RenderFrameHost commit the navigation.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(request)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+ EXPECT_EQ(NavigationRequest::RESPONSE_STARTED, request->state());
+
+ // 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->navigation_request());
+ EXPECT_FALSE(node->render_manager()->pending_frame_host());
- NavigationRequest* GetNavigationRequestForFrameTreeNode(
- FrameTreeNode* frame_tree_node) const {
- NavigatorImpl* navigator =
- static_cast<NavigatorImpl*>(frame_tree_node->navigator());
- return navigator->navigation_request_map_.get(
- frame_tree_node->frame_tree_node_id());
- }
+ // 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());
- TestNavigationURLLoader* GetLoaderForNavigationRequest(
- NavigationRequest* request) const {
- return static_cast<TestNavigationURLLoader*>(request->loader_for_testing());
- }
+ // After a navigation is finished no speculative RenderFrameHost should
+ // exist.
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
- void EnableBrowserSideNavigation() {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kEnableBrowserSideNavigation);
- }
+ // With PlzNavigate enabled a pending RenderFrameHost should never exist.
+ EXPECT_FALSE(node->render_manager()->pending_frame_host());
+}
- void SendRequestNavigation(FrameTreeNode* node,
- const GURL& url) {
- SendRequestNavigationWithParameters(
- node, url, Referrer(), ui::PAGE_TRANSITION_LINK,
- NavigationController::NO_RELOAD);
- }
+// 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");
- void SendRequestNavigationWithParameters(
- FrameTreeNode* node,
- const GURL& url,
- const Referrer& referrer,
- ui::PageTransition transition_type,
- NavigationController::ReloadType reload_type) {
- scoped_ptr<NavigationEntryImpl> entry(
- NavigationEntryImpl::FromNavigationEntry(
- NavigationController::CreateNavigationEntry(
- url,
- referrer,
- transition_type,
- false,
- std::string(),
- controller().GetBrowserContext())));
- static_cast<NavigatorImpl*>(node->navigator())->RequestNavigation(
- node, *entry, reload_type, base::TimeTicks::Now());
- }
+ contents()->NavigateAndCommit(kUrl1);
+ EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive());
- scoped_ptr<StreamHandle> MakeEmptyStream() {
- GURL url(std::string(url::kBlobScheme) + "://" + base::GenerateGUID());
- scoped_refptr<Stream> stream(new Stream(stream_registry_.get(), NULL, url));
- stream->Finalize();
- return stream->CreateHandle();
- }
+ // 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<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(request)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+ EXPECT_EQ(NavigationRequest::RESPONSE_STARTED, request->state());
+
+ // 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(node->navigation_request());
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+ EXPECT_FALSE(node->render_manager()->pending_frame_host());
+}
- private:
- scoped_ptr<StreamRegistry> stream_registry_;
- scoped_ptr<TestNavigationURLLoaderFactory> loader_factory_;
-};
+// 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 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());
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Have the current RenderFrameHost commit the navigation.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(request)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ EXPECT_TRUE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+ EXPECT_EQ(NavigationRequest::RESPONSE_STARTED, request->state());
+
+ // 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(node->navigation_request());
+ 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_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Simulate a beforeUnload denial.
+ main_test_rfh()->SendBeforeUnloadACK(false);
+ EXPECT_FALSE(node->navigation_request());
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+}
// PlzNavigate: Test that a proper NavigationRequest is created by
-// BeginNavigation.
-// Note that all PlzNavigate methods on the browser side require the use of the
-// flag kEnableBrowserSideNavigation.
-TEST_F(NavigatorTest, BrowserSideNavigationBeginNavigation) {
+// 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);
- EnableBrowserSideNavigation();
-
// Add a subframe.
- FrameTreeNode* root = contents()->GetFrameTree()->root();
- TestRenderFrameHost* subframe_rfh = static_cast<TestRenderFrameHost*>(
- contents()->GetFrameTree()->AddFrame(
- root, root->current_frame_host()->GetProcess()->GetID(), 14,
- "Child"));
- EXPECT_TRUE(subframe_rfh);
+ 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();
- SendRequestNavigation(subframe_rfh->frame_tree_node(), kUrl2);
- // There is no previous renderer in the subframe, so BeginNavigation is
- // handled already.
- NavigationRequest* subframe_request =
- GetNavigationRequestForFrameTreeNode(subframe_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->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 (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSitePerProcess)) {
+ 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());
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(root_node));
- SendRequestNavigation(root, kUrl3);
- // Simulate a BeginNavigation IPC on the main frame.
- contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl3);
- NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(root);
+ // Simulate a BeforeUnloadACK IPC on the main frame.
+ main_test_rfh()->SendBeforeUnloadACK(true);
TestNavigationURLLoader* main_loader =
GetLoaderForNavigationRequest(main_request);
- ASSERT_TRUE(main_request);
EXPECT_EQ(kUrl3, main_request->common_params().url);
- EXPECT_EQ(kUrl3, main_loader->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());
+
+ // Main frame navigation to a different site should use a speculative
+ // RenderFrameHost.
+ 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 (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSitePerProcess)) {
+ EXPECT_TRUE(GetSpeculativeRenderFrameHost(subframe_node));
+ } else {
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(subframe_node));
+ }
}
-// PlzNavigate: Test that RequestNavigation creates a NavigationRequest and that
-// RenderFrameHost is not modified when the navigation commits.
-TEST_F(NavigatorTest, BrowserSideNavigationRequestNavigationNoLiveRenderer) {
- const GURL kUrl("http://www.google.com/");
-
- EnableBrowserSideNavigation();
- EXPECT_FALSE(main_test_rfh()->render_view_host()->IsRenderViewLive());
- FrameTreeNode* node = main_test_rfh()->frame_tree_node();
- SendRequestNavigation(node, kUrl);
- NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
- // A NavigationRequest should have been generated.
- EXPECT_TRUE(main_request != NULL);
- RenderFrameHostImpl* rfh = main_test_rfh();
-
- // Now return the response without any redirects. This will cause the
- // navigation to commit at the same URL.
- scoped_refptr<ResourceResponse> response(new ResourceResponse);
- GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
- response, MakeEmptyStream());
- main_request = GetNavigationRequestForFrameTreeNode(node);
-
- // The main RFH should not have been changed, and the renderer should have
- // been initialized.
- EXPECT_EQ(rfh, main_test_rfh());
- EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive());
- EXPECT_TRUE(main_test_rfh()->render_view_host()->IsRenderViewLive());
-}
-
-// PlzNavigate: Test that commiting an HTTP 204 or HTTP 205 response cancels the
-// navigation.
-TEST_F(NavigatorTest, BrowserSideNavigationNoContent) {
+// 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);
- RenderFrameHostImpl* rfh = main_test_rfh();
- EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh->rfh_state());
FrameTreeNode* node = main_test_rfh()->frame_tree_node();
- EnableBrowserSideNavigation();
-
// Navigate to a different site.
- SendRequestNavigation(node, kUrl2);
- main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
- NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
+ 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<ResourceResponse> response(new ResourceResponse);
const char kNoContentHeaders[] = "HTTP/1.1 204 No Content\0\0";
@@ -274,17 +374,23 @@ TEST_F(NavigatorTest, BrowserSideNavigationNoContent) {
GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
response, MakeEmptyStream());
- // There should be no pending RenderFrameHost; the navigation was aborted.
- EXPECT_FALSE(GetNavigationRequestForFrameTreeNode(node));
+ // 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.
- SendRequestNavigation(node, kUrl2);
- main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
- main_request = GetNavigationRequestForFrameTreeNode(node);
+ 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;
@@ -294,187 +400,736 @@ TEST_F(NavigatorTest, BrowserSideNavigationNoContent) {
GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
response, MakeEmptyStream());
- // There should be no pending RenderFrameHost; the navigation was aborted.
- EXPECT_FALSE(GetNavigationRequestForFrameTreeNode(node));
+ // 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(NavigatorTest, BrowserSideNavigationCrossSiteNavigation) {
+TEST_F(NavigatorTestWithBrowserSideNavigation, CrossSiteNavigation) {
const GURL kUrl1("http://www.chromium.org/");
const GURL kUrl2("http://www.google.com/");
contents()->NavigateAndCommit(kUrl1);
- RenderFrameHostImpl* rfh = main_test_rfh();
- EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh->rfh_state());
+ RenderFrameHostImpl* initial_rfh = main_test_rfh();
FrameTreeNode* node = main_test_rfh()->frame_tree_node();
- EnableBrowserSideNavigation();
-
// Navigate to a different site.
- SendRequestNavigation(node, kUrl2);
- main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
- NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
+ process()->sink().ClearMessages();
+ int entry_id = RequestNavigation(node, kUrl2);
+ NavigationRequest* main_request = node->navigation_request();
ASSERT_TRUE(main_request);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Receive the beforeUnload ACK.
+ main_test_rfh()->SendBeforeUnloadACK(true);
+ EXPECT_TRUE(GetSpeculativeRenderFrameHost(node));
+ EXPECT_FALSE(contents()->CrossProcessNavigationPending());
scoped_refptr<ResourceResponse> response(new ResourceResponse);
GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
response, MakeEmptyStream());
- RenderFrameHostImpl* pending_rfh =
- node->render_manager()->pending_frame_host();
- ASSERT_TRUE(pending_rfh);
- EXPECT_NE(pending_rfh, rfh);
- EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
- EXPECT_TRUE(pending_rfh->render_view_host()->IsRenderViewLive());
+ TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+ 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.
-TEST_F(NavigatorTest, BrowserSideNavigationRedirectCrossSite) {
+// 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();
- EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh->rfh_state());
FrameTreeNode* node = main_test_rfh()->frame_tree_node();
- EnableBrowserSideNavigation();
-
// Navigate to a URL on the same site.
- SendRequestNavigation(node, kUrl1);
- main_test_rfh()->SendBeginNavigationWithURL(kUrl1);
- NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
+ 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.
- net::RedirectInfo redirect_info;
- redirect_info.status_code = 302;
- redirect_info.new_method = "GET";
- redirect_info.new_url = kUrl2;
- redirect_info.new_first_party_for_cookies = kUrl2;
- scoped_refptr<ResourceResponse> response(new ResourceResponse);
- GetLoaderForNavigationRequest(main_request)->CallOnRequestRedirected(
- redirect_info, response);
+ GetLoaderForNavigationRequest(main_request)->SimulateServerRedirect(kUrl2);
// The redirect should have been followed.
EXPECT_EQ(1, GetLoaderForNavigationRequest(main_request)->redirect_count());
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
- // Then it commits.
- response = new ResourceResponse;
+ // Have the RenderFrameHost commit the navigation.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
response, MakeEmptyStream());
- RenderFrameHostImpl* pending_rfh =
- node->render_manager()->pending_frame_host();
- ASSERT_TRUE(pending_rfh);
- EXPECT_NE(pending_rfh, rfh);
- EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
- EXPECT_TRUE(pending_rfh->render_view_host()->IsRenderViewLive());
+ 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 cancelled if another request has been
-// issued in the meantime.
-TEST_F(NavigatorTest, BrowserSideNavigationReplacePendingNavigation) {
+// 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 kUrl0_site = SiteInstance::GetSiteForURL(browser_context(), kUrl0);
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();
- EnableBrowserSideNavigation();
- EXPECT_EQ(kUrl0_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
// Request navigation to the 1st URL.
- SendRequestNavigation(node, kUrl1);
- main_test_rfh()->SendBeginNavigationWithURL(kUrl1);
- NavigationRequest* request1 = GetNavigationRequestForFrameTreeNode(node);
+ 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<TestNavigationURLLoader> loader1 =
GetLoaderForNavigationRequest(request1)->AsWeakPtr();
+ EXPECT_TRUE(loader1);
+
+ // Confirm a speculative RenderFrameHost was created.
+ TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+ int32 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.
- SendRequestNavigation(node, kUrl2);
- main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
- NavigationRequest* request2 = GetNavigationRequestForFrameTreeNode(node);
+ 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 the commit corresponds to the new request.
+ // Confirm that a new speculative RenderFrameHost was created.
+ speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+ int32 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<ResourceResponse> response(new ResourceResponse);
GetLoaderForNavigationRequest(request2)->CallOnResponseStarted(
response, MakeEmptyStream());
- RenderFrameHostImpl* pending_rfh =
- node->render_manager()->pending_frame_host();
- ASSERT_TRUE(pending_rfh);
- EXPECT_EQ(kUrl2_site, pending_rfh->GetSiteInstance()->GetSiteURL());
+ 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<TestNavigationURLLoader> 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.
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Have the RenderFrameHost commit the navigation.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(request2)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ 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: Tests that the navigation histograms are correctly tracked both
-// when PlzNavigate is enabled and disabled, and also ignores in-tab renderer
-// initiated navigation for the non-enabled case.
-// Note: the related histogram, Navigation.TimeToURLJobStart, cannot be tracked
-// by this test as the IO thread is not running.
-TEST_F(NavigatorTest, BrowserSideNavigationHistogramTest) {
- const GURL kUrl0("http://www.google.com/");
+// 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/");
- base::HistogramTester histo_tester;
+ const GURL kUrl2("http://www.google.com/");
- // Performs a "normal" non-PlzNavigate navigation
+ // Initialization.
contents()->NavigateAndCommit(kUrl0);
- histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 1);
+ FrameTreeNode* node = main_test_rfh()->frame_tree_node();
- // Performs an in-tab renderer initiated navigation
- int32 new_page_id = 1 + contents()->GetMaxPageIDForSiteInstance(
- main_test_rfh()->GetSiteInstance());
- main_test_rfh()->SendNavigate(new_page_id, kUrl0);
- histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 1);
+ // 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);
+ 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);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
- // Performs a PlzNavigate navigation
- EnableBrowserSideNavigation();
- contents()->NavigateAndCommit(kUrl1);
- histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 2);
+ // Have the RenderFrameHost commit the navigation.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(request2)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ 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_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_TRUE(request2->browser_initiated());
+ EXPECT_FALSE(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<ResourceResponse> 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 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);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+ base::WeakPtr<TestNavigationURLLoader> 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);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Confirm that the first loader got destroyed.
+ EXPECT_FALSE(loader1);
+
+ // Have the RenderFrameHost commit the navigation.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(request2)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ 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
-// renderer when the navigation can commit.
-TEST_F(NavigatorTest, BrowserSideNavigationReload) {
+// 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);
- EnableBrowserSideNavigation();
FrameTreeNode* node = main_test_rfh()->frame_tree_node();
- SendRequestNavigationWithParameters(
- node, kUrl, Referrer(), ui::PAGE_TRANSITION_LINK,
- NavigationController::RELOAD);
- contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl);
+ controller().Reload(false);
+ int entry_id = controller().GetPendingEntry()->GetUniqueID();
// A NavigationRequest should have been generated.
- NavigationRequest* main_request =
- GetNavigationRequestForFrameTreeNode(node);
+ NavigationRequest* main_request = node->navigation_request();
ASSERT_TRUE(main_request != NULL);
EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD,
main_request->common_params().navigation_type);
- int page_id = contents()->GetMaxPageIDForSiteInstance(
- main_test_rfh()->GetSiteInstance()) + 1;
- main_test_rfh()->SendNavigate(page_id, kUrl);
+ 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.
- SendRequestNavigationWithParameters(
- node, kUrl, Referrer(), ui::PAGE_TRANSITION_LINK,
- NavigationController::RELOAD_IGNORING_CACHE);
- contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl);
+ controller().ReloadIgnoringCache(false);
// A NavigationRequest should have been generated.
- main_request = GetNavigationRequestForFrameTreeNode(node);
+ 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);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Receive the beforeUnload ACK.
+ main_test_rfh()->SendBeforeUnloadACK(true);
+ TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+ EXPECT_NE(speculative_rfh, main_test_rfh());
+ EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrl),
+ speculative_rfh->GetSiteInstance()->GetSiteURL());
+ EXPECT_FALSE(node->render_manager()->pending_frame_host());
+ int32 site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
+
+ // Ask Navigator to commit the navigation by simulating a call to
+ // OnResponseStarted.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(node->navigation_request())
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+ 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 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);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ // Receive the beforeUnload ACK.
+ main_test_rfh()->SendBeforeUnloadACK(true);
+ TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ int32 site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
+ EXPECT_NE(init_site_instance_id, site_instance_id);
+ EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
+ ASSERT_TRUE(speculative_rfh);
+ EXPECT_NE(speculative_rfh, main_test_rfh());
+ EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrl),
+ speculative_rfh->GetSiteInstance()->GetSiteURL());
+
+ // 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());
+ speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+
+ // 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(site_instance_id, speculative_rfh->GetSiteInstance()->GetId());
+
+ // Commit the navigation with Navigator by simulating the call to
+ // OnResponseStarted.
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(main_request)
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh));
+ EXPECT_EQ(init_site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
+
+ // Once commit happens the speculative RenderFrameHost is updated to match the
+ // known final SiteInstance.
+ ASSERT_TRUE(speculative_rfh);
+ EXPECT_EQ(SiteInstanceImpl::GetSiteForURL(browser_context(), kUrlRedirect),
+ speculative_rfh->GetSiteInstance()->GetSiteURL());
+ int32 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 a previously swapped out RenderFrameHost is
+// correctly reused when spawning a speculative RenderFrameHost in a navigation
+// using the same SiteInstance.
+TEST_F(NavigatorTestWithBrowserSideNavigation,
+ SpeculativeRendererReuseSwappedOutRFH) {
+ // Navigate to an initial site.
+ const GURL kUrl1("http://wikipedia.org/");
+ contents()->NavigateAndCommit(kUrl1);
+ TestRenderFrameHost* rfh1 = main_test_rfh();
+ FrameTreeNode* node = rfh1->frame_tree_node();
+ RenderFrameHostManager* rfhm = node->render_manager();
+
+ // Increment active frame count to cause the RenderFrameHost to be swapped out
+ // (instead of immediately destroyed).
+ rfh1->GetSiteInstance()->increment_active_frame_count();
+
+ // Navigate to another site to swap out the initial RenderFrameHost.
+ const GURL kUrl2("http://chromium.org/");
+ contents()->NavigateAndCommit(kUrl2);
+ ASSERT_NE(rfh1, main_test_rfh());
+ EXPECT_NE(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
+ EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, main_test_rfh()->rfh_state());
+ EXPECT_TRUE(rfhm->IsOnSwappedOutList(rfh1));
+
+ // Now go back to the initial site so that the swapped out RenderFrameHost
+ // should be reused.
+ process()->sink().ClearMessages();
+ rfh1->GetProcess()->sink().ClearMessages();
+ int entry_id = RequestNavigation(node, kUrl1);
+ EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+ main_test_rfh()->SendBeforeUnloadACK(true);
+ EXPECT_EQ(rfh1, GetSpeculativeRenderFrameHost(node));
+ EXPECT_NE(RenderFrameHostImpl::STATE_DEFAULT,
+ GetSpeculativeRenderFrameHost(node)->rfh_state());
+
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ GetLoaderForNavigationRequest(node->navigation_request())
+ ->CallOnResponseStarted(response, MakeEmptyStream());
+ EXPECT_EQ(rfh1, GetSpeculativeRenderFrameHost(node));
+ EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT,
+ GetSpeculativeRenderFrameHost(node)->rfh_state());
+ EXPECT_TRUE(DidRenderFrameHostRequestCommit(rfh1));
+ EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+
+ rfh1->SendNavigate(1, entry_id, true, kUrl1);
+ EXPECT_EQ(rfh1, main_test_rfh());
+ EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh1->rfh_state());
+ EXPECT_FALSE(rfhm->IsOnSwappedOutList(rfh1));
+}
+
+// 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.
+ int entry_id = RequestNavigation(node, kUrl2);
+ NavigationRequest* navigation_request = node->navigation_request();
+ ASSERT_TRUE(navigation_request);
+ EXPECT_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE,
+ navigation_request->state());
+ main_test_rfh()->SendBeforeUnloadACK(true);
+
+ // The request should not have been sent to the IO thread but committed
+ // immediately.
+ EXPECT_EQ(NavigationRequest::RESPONSE_STARTED,
+ navigation_request->state());
+ EXPECT_FALSE(navigation_request->loader_for_testing());
+ TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+ ASSERT_TRUE(speculative_rfh);
+ 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);
+ navigation_request = node->navigation_request();
+ ASSERT_TRUE(navigation_request);
+ EXPECT_EQ(NavigationRequest::RESPONSE_STARTED,
+ navigation_request->state());
+ EXPECT_FALSE(navigation_request->loader_for_testing());
+ 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<SiteInstance> 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<SiteInstance> 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<SiteInstance> 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<SiteInstance> 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<SiteInstance> 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 content