1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/test/histogram_tester.h"
7 #include "base/time/time.h"
8 #include "content/browser/frame_host/navigation_controller_impl.h"
9 #include "content/browser/frame_host/navigation_entry_impl.h"
10 #include "content/browser/frame_host/navigation_request.h"
11 #include "content/browser/frame_host/navigation_request_info.h"
12 #include "content/browser/frame_host/navigator.h"
13 #include "content/browser/frame_host/navigator_impl.h"
14 #include "content/browser/frame_host/render_frame_host_manager.h"
15 #include "content/browser/site_instance_impl.h"
16 #include "content/common/navigation_params.h"
17 #include "content/public/browser/stream_handle.h"
18 #include "content/public/common/content_switches.h"
19 #include "content/public/common/url_constants.h"
20 #include "content/public/common/url_utils.h"
21 #include "content/test/test_render_frame_host.h"
22 #include "content/test/test_web_contents.h"
23 #include "net/base/load_flags.h"
24 #include "ui/base/page_transition_types.h"
30 // Mocked out stream handle to commit the navigation with.
31 class TestStreamHandle
: public StreamHandle
{
33 TestStreamHandle() : url_("test:stream") {}
35 virtual const GURL
& GetURL() override
{
39 virtual const GURL
& GetOriginalURL() override
{
44 virtual const std::string
& GetMimeType() override
{
49 virtual scoped_refptr
<net::HttpResponseHeaders
>
50 GetResponseHeaders() override
{
55 virtual void AddCloseListener(const base::Closure
& callback
) override
{
62 std::string mime_type_
;
64 DISALLOW_COPY_AND_ASSIGN(TestStreamHandle
);
70 : public RenderViewHostImplTestHarness
{
72 NavigationRequest
* GetNavigationRequestForFrameTreeNode(
73 FrameTreeNode
* frame_tree_node
) const {
74 NavigatorImpl
* navigator
=
75 static_cast<NavigatorImpl
*>(frame_tree_node
->navigator());
76 return navigator
->navigation_request_map_
.get(
77 frame_tree_node
->frame_tree_node_id());
80 void EnableBrowserSideNavigation() {
81 CommandLine::ForCurrentProcess()->AppendSwitch(
82 switches::kEnableBrowserSideNavigation
);
85 void SendRequestNavigation(FrameTreeNode
* node
,
87 SendRequestNavigationWithParameters(
88 node
, url
, Referrer(), ui::PAGE_TRANSITION_LINK
,
89 NavigationController::NO_RELOAD
);
92 void SendRequestNavigationWithParameters(
95 const Referrer
& referrer
,
96 ui::PageTransition transition_type
,
97 NavigationController::ReloadType reload_type
) {
98 scoped_ptr
<NavigationEntryImpl
> entry(
99 NavigationEntryImpl::FromNavigationEntry(
100 NavigationController::CreateNavigationEntry(
106 controller().GetBrowserContext())));
107 static_cast<NavigatorImpl
*>(node
->navigator())->RequestNavigation(
108 node
, *entry
, reload_type
, base::TimeTicks::Now());
112 // PlzNavigate: Test that a proper NavigationRequest is created by
114 // Note that all PlzNavigate methods on the browser side require the use of the
115 // flag kEnableBrowserSideNavigation.
116 TEST_F(NavigatorTest
, BrowserSideNavigationBeginNavigation
) {
117 const GURL
kUrl1("http://www.google.com/");
118 const GURL
kUrl2("http://www.chromium.org/");
119 const GURL
kUrl3("http://www.gmail.com/");
121 contents()->NavigateAndCommit(kUrl1
);
123 EnableBrowserSideNavigation();
126 TestRenderFrameHost
* subframe_rfh
= static_cast<TestRenderFrameHost
*>(
127 contents()->GetFrameTree()->AddFrame(
128 contents()->GetFrameTree()->root(), 14, "Child"));
130 FrameTreeNode
* subframe_node
= subframe_rfh
->frame_tree_node();
131 SendRequestNavigation(subframe_rfh
->frame_tree_node(), kUrl2
);
132 // There is no previous renderer in the subframe, so BeginNavigation is
134 NavigationRequest
* subframe_request
=
135 GetNavigationRequestForFrameTreeNode(subframe_node
);
136 ASSERT_TRUE(subframe_request
);
137 EXPECT_EQ(kUrl2
, subframe_request
->common_params().url
);
138 // First party for cookies url should be that of the main frame.
139 EXPECT_EQ(kUrl1
, subframe_request
->info_for_test()->first_party_for_cookies
);
140 EXPECT_FALSE(subframe_request
->info_for_test()->is_main_frame
);
141 EXPECT_TRUE(subframe_request
->info_for_test()->parent_is_main_frame
);
143 FrameTreeNode
* main_frame_node
=
144 contents()->GetMainFrame()->frame_tree_node();
145 SendRequestNavigation(main_frame_node
, kUrl3
);
146 // Simulate a BeginNavigation IPC on the main frame.
147 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl3
);
148 NavigationRequest
* main_request
=
149 GetNavigationRequestForFrameTreeNode(main_frame_node
);
150 ASSERT_TRUE(main_request
);
151 EXPECT_EQ(kUrl3
, main_request
->common_params().url
);
152 EXPECT_EQ(kUrl3
, main_request
->info_for_test()->first_party_for_cookies
);
153 EXPECT_TRUE(main_request
->info_for_test()->is_main_frame
);
154 EXPECT_FALSE(main_request
->info_for_test()->parent_is_main_frame
);
157 // PlzNavigate: Test that RequestNavigation creates a NavigationRequest and that
158 // RenderFrameHost is not modified when the navigation commits.
159 TEST_F(NavigatorTest
, BrowserSideNavigationRequestNavigationNoLiveRenderer
) {
160 const GURL
kUrl("http://www.google.com/");
162 EnableBrowserSideNavigation();
163 EXPECT_FALSE(main_test_rfh()->render_view_host()->IsRenderViewLive());
164 FrameTreeNode
* node
= main_test_rfh()->frame_tree_node();
165 SendRequestNavigation(node
, kUrl
);
166 NavigationRequest
* main_request
= GetNavigationRequestForFrameTreeNode(node
);
167 // A NavigationRequest should have been generated.
168 EXPECT_TRUE(main_request
!= NULL
);
169 RenderFrameHostImpl
* rfh
= main_test_rfh();
171 // Now commit the same url.
172 scoped_refptr
<ResourceResponse
> response(new ResourceResponse
);
173 node
->navigator()->CommitNavigation(
174 node
, response
.get(), scoped_ptr
<StreamHandle
>(new TestStreamHandle
));
175 main_request
= GetNavigationRequestForFrameTreeNode(node
);
177 // The main RFH should not have been changed, and the renderer should have
179 EXPECT_EQ(rfh
, main_test_rfh());
180 EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive());
181 EXPECT_TRUE(main_test_rfh()->render_view_host()->IsRenderViewLive());
184 // PlzNavigate: Test that a new RenderFrameHost is created when doing a cross
186 TEST_F(NavigatorTest
, BrowserSideNavigationCrossSiteNavigation
) {
187 const GURL
kUrl1("http://www.chromium.org/");
188 const GURL
kUrl2("http://www.google.com/");
190 contents()->NavigateAndCommit(kUrl1
);
191 RenderFrameHostImpl
* rfh
= main_test_rfh();
192 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT
, rfh
->rfh_state());
193 FrameTreeNode
* node
= main_test_rfh()->frame_tree_node();
195 EnableBrowserSideNavigation();
197 // Navigate to a different site.
198 SendRequestNavigation(node
, kUrl2
);
199 main_test_rfh()->SendBeginNavigationWithURL(kUrl2
);
200 NavigationRequest
* main_request
= GetNavigationRequestForFrameTreeNode(node
);
201 ASSERT_TRUE(main_request
);
203 scoped_refptr
<ResourceResponse
> response(new ResourceResponse
);
204 node
->navigator()->CommitNavigation(
205 node
, response
.get(), scoped_ptr
<StreamHandle
>(new TestStreamHandle
));
206 RenderFrameHostImpl
* pending_rfh
=
207 node
->render_manager()->pending_frame_host();
208 ASSERT_TRUE(pending_rfh
);
209 EXPECT_NE(pending_rfh
, rfh
);
210 EXPECT_TRUE(pending_rfh
->IsRenderFrameLive());
211 EXPECT_TRUE(pending_rfh
->render_view_host()->IsRenderViewLive());
214 // PlzNavigate: Test that a navigation is cancelled if another request has been
215 // issued in the meantime.
216 TEST_F(NavigatorTest
, BrowserSideNavigationReplacePendingNavigation
) {
217 const GURL
kUrl0("http://www.wikipedia.org/");
218 const GURL kUrl0_site
= SiteInstance::GetSiteForURL(browser_context(), kUrl0
);
219 const GURL
kUrl1("http://www.chromium.org/");
220 const GURL
kUrl2("http://www.google.com/");
221 const GURL kUrl2_site
= SiteInstance::GetSiteForURL(browser_context(), kUrl2
);
224 contents()->NavigateAndCommit(kUrl0
);
225 FrameTreeNode
* node
= main_test_rfh()->frame_tree_node();
226 EnableBrowserSideNavigation();
227 EXPECT_EQ(kUrl0_site
, main_test_rfh()->GetSiteInstance()->GetSiteURL());
229 // Request navigation to the 1st URL.
230 SendRequestNavigation(node
, kUrl1
);
231 main_test_rfh()->SendBeginNavigationWithURL(kUrl1
);
232 NavigationRequest
* request1
= GetNavigationRequestForFrameTreeNode(node
);
233 ASSERT_TRUE(request1
);
234 EXPECT_EQ(kUrl1
, request1
->common_params().url
);
236 // Request navigation to the 2nd URL; the NavigationRequest must have been
237 // replaced by a new one with a different URL.
238 SendRequestNavigation(node
, kUrl2
);
239 main_test_rfh()->SendBeginNavigationWithURL(kUrl2
);
240 NavigationRequest
* request2
= GetNavigationRequestForFrameTreeNode(node
);
241 ASSERT_TRUE(request2
);
242 EXPECT_EQ(kUrl2
, request2
->common_params().url
);
244 // Confirm that the commit corresonds to the new request.
245 scoped_refptr
<ResourceResponse
> response(new ResourceResponse
);
246 node
->navigator()->CommitNavigation(
247 node
, response
.get(), scoped_ptr
<StreamHandle
>(new TestStreamHandle
));
248 RenderFrameHostImpl
* pending_rfh
=
249 node
->render_manager()->pending_frame_host();
250 ASSERT_TRUE(pending_rfh
);
251 EXPECT_EQ(kUrl2_site
, pending_rfh
->GetSiteInstance()->GetSiteURL());
254 // PlzNavigate: Tests that the navigation histograms are correctly tracked both
255 // when PlzNavigate is enabled and disabled, and also ignores in-tab renderer
256 // initiated navigation for the non-enabled case.
257 // Note: the related histogram, Navigation.TimeToURLJobStart, cannot be tracked
258 // by this test as the IO thread is not running.
259 TEST_F(NavigatorTest
, BrowserSideNavigationHistogramTest
) {
260 const GURL
kUrl0("http://www.google.com/");
261 const GURL
kUrl1("http://www.chromium.org/");
262 base::HistogramTester histo_tester
;
264 // Performs a "normal" non-PlzNavigate navigation
265 contents()->NavigateAndCommit(kUrl0
);
266 histo_tester
.ExpectTotalCount("Navigation.TimeToCommit", 1);
268 // Performs an in-tab renderer initiated navigation
269 int32 new_page_id
= 1 + contents()->GetMaxPageIDForSiteInstance(
270 main_test_rfh()->GetSiteInstance());
271 main_test_rfh()->SendNavigate(new_page_id
, kUrl0
);
272 histo_tester
.ExpectTotalCount("Navigation.TimeToCommit", 1);
274 // Performs a PlzNavigate navigation
275 EnableBrowserSideNavigation();
276 contents()->NavigateAndCommit(kUrl1
);
277 histo_tester
.ExpectTotalCount("Navigation.TimeToCommit", 2);
280 // PlzNavigate: Test that a reload navigation is properly signaled to the
281 // renderer when the navigation can commit.
282 TEST_F(NavigatorTest
, BrowserSideNavigationReload
) {
283 const GURL
kUrl("http://www.google.com/");
284 contents()->NavigateAndCommit(kUrl
);
286 EnableBrowserSideNavigation();
287 FrameTreeNode
* node
= main_test_rfh()->frame_tree_node();
288 SendRequestNavigationWithParameters(
289 node
, kUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
,
290 NavigationController::RELOAD
);
291 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl
);
292 // A NavigationRequest should have been generated.
293 NavigationRequest
* main_request
=
294 GetNavigationRequestForFrameTreeNode(node
);
295 ASSERT_TRUE(main_request
!= NULL
);
296 EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD
,
297 main_request
->common_params().navigation_type
);
298 int page_id
= contents()->GetMaxPageIDForSiteInstance(
299 main_test_rfh()->GetSiteInstance()) + 1;
300 main_test_rfh()->SendNavigate(page_id
, kUrl
);
302 // Now do a shift+reload.
303 SendRequestNavigationWithParameters(
304 node
, kUrl
, Referrer(), ui::PAGE_TRANSITION_LINK
,
305 NavigationController::RELOAD_IGNORING_CACHE
);
306 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl
);
307 // A NavigationRequest should have been generated.
308 main_request
= GetNavigationRequestForFrameTreeNode(node
);
309 ASSERT_TRUE(main_request
!= NULL
);
310 EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE
,
311 main_request
->common_params().navigation_type
);
314 } // namespace content