Retry: Win Video Capture: Create an STA |video_capture_thread_| from MediaStreamManager.
[chromium-blink-merge.git] / content / browser / frame_host / navigator_impl_unittest.cc
blob37a0f243d6dbd01a074760fd51caeba647bfa7db
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/guid.h"
7 #include "base/macros.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/test/histogram_tester.h"
10 #include "base/time/time.h"
11 #include "content/browser/frame_host/navigation_controller_impl.h"
12 #include "content/browser/frame_host/navigation_entry_impl.h"
13 #include "content/browser/frame_host/navigation_request.h"
14 #include "content/browser/frame_host/navigation_request_info.h"
15 #include "content/browser/frame_host/navigator.h"
16 #include "content/browser/frame_host/navigator_impl.h"
17 #include "content/browser/frame_host/render_frame_host_manager.h"
18 #include "content/browser/loader/navigation_url_loader.h"
19 #include "content/browser/loader/navigation_url_loader_delegate.h"
20 #include "content/browser/loader/navigation_url_loader_factory.h"
21 #include "content/browser/site_instance_impl.h"
22 #include "content/browser/streams/stream.h"
23 #include "content/browser/streams/stream_registry.h"
24 #include "content/common/navigation_params.h"
25 #include "content/public/browser/stream_handle.h"
26 #include "content/public/common/content_switches.h"
27 #include "content/public/common/url_constants.h"
28 #include "content/public/common/url_utils.h"
29 #include "content/test/test_render_frame_host.h"
30 #include "content/test/test_web_contents.h"
31 #include "net/base/load_flags.h"
32 #include "net/http/http_response_headers.h"
33 #include "net/url_request/redirect_info.h"
34 #include "ui/base/page_transition_types.h"
35 #include "url/url_constants.h"
37 namespace content {
39 namespace {
41 class TestNavigationURLLoader
42 : public NavigationURLLoader,
43 public base::SupportsWeakPtr<TestNavigationURLLoader> {
44 public:
45 TestNavigationURLLoader(const CommonNavigationParams& common_params,
46 scoped_ptr<NavigationRequestInfo> request_info,
47 NavigationURLLoaderDelegate* delegate)
48 : common_params_(common_params),
49 request_info_(request_info.Pass()),
50 delegate_(delegate),
51 redirect_count_(0) {
54 // NavigationURLLoader implementation.
55 void FollowRedirect() override { redirect_count_++; }
57 const CommonNavigationParams& common_params() const { return common_params_; }
58 NavigationRequestInfo* request_info() const { return request_info_.get(); }
60 void CallOnRequestRedirected(
61 const net::RedirectInfo& redirect_info,
62 const scoped_refptr<ResourceResponse>& response) {
63 delegate_->OnRequestRedirected(redirect_info, response);
66 void CallOnResponseStarted(
67 const scoped_refptr<ResourceResponse>& response,
68 scoped_ptr<StreamHandle> body) {
69 delegate_->OnResponseStarted(response, body.Pass());
72 int redirect_count() { return redirect_count_; }
74 private:
75 CommonNavigationParams common_params_;
76 scoped_ptr<NavigationRequestInfo> request_info_;
77 NavigationURLLoaderDelegate* delegate_;
78 int redirect_count_;
81 class TestNavigationURLLoaderFactory : public NavigationURLLoaderFactory {
82 public:
83 // NavigationURLLoaderFactory implementation.
84 scoped_ptr<NavigationURLLoader> CreateLoader(
85 BrowserContext* browser_context,
86 int64 frame_tree_node_id,
87 const CommonNavigationParams& common_params,
88 scoped_ptr<NavigationRequestInfo> request_info,
89 ResourceRequestBody* request_body,
90 NavigationURLLoaderDelegate* delegate) override {
91 return scoped_ptr<NavigationURLLoader>(new TestNavigationURLLoader(
92 common_params, request_info.Pass(), delegate));
96 } // namespace
98 class NavigatorTest : public RenderViewHostImplTestHarness {
99 public:
100 NavigatorTest() : stream_registry_(new StreamRegistry) {}
102 void SetUp() override {
103 RenderViewHostImplTestHarness::SetUp();
104 loader_factory_.reset(new TestNavigationURLLoaderFactory);
105 NavigationURLLoader::SetFactoryForTesting(loader_factory_.get());
108 void TearDown() override {
109 NavigationURLLoader::SetFactoryForTesting(nullptr);
110 loader_factory_.reset();
111 RenderViewHostImplTestHarness::TearDown();
114 NavigationRequest* GetNavigationRequestForFrameTreeNode(
115 FrameTreeNode* frame_tree_node) const {
116 NavigatorImpl* navigator =
117 static_cast<NavigatorImpl*>(frame_tree_node->navigator());
118 return navigator->navigation_request_map_.get(
119 frame_tree_node->frame_tree_node_id());
122 TestNavigationURLLoader* GetLoaderForNavigationRequest(
123 NavigationRequest* request) const {
124 return static_cast<TestNavigationURLLoader*>(request->loader_for_testing());
127 void EnableBrowserSideNavigation() {
128 CommandLine::ForCurrentProcess()->AppendSwitch(
129 switches::kEnableBrowserSideNavigation);
132 void SendRequestNavigation(FrameTreeNode* node,
133 const GURL& url) {
134 SendRequestNavigationWithParameters(
135 node, url, Referrer(), ui::PAGE_TRANSITION_LINK,
136 NavigationController::NO_RELOAD);
139 void SendRequestNavigationWithParameters(
140 FrameTreeNode* node,
141 const GURL& url,
142 const Referrer& referrer,
143 ui::PageTransition transition_type,
144 NavigationController::ReloadType reload_type) {
145 scoped_ptr<NavigationEntryImpl> entry(
146 NavigationEntryImpl::FromNavigationEntry(
147 NavigationController::CreateNavigationEntry(
148 url,
149 referrer,
150 transition_type,
151 false,
152 std::string(),
153 controller().GetBrowserContext())));
154 static_cast<NavigatorImpl*>(node->navigator())->RequestNavigation(
155 node, *entry, reload_type, base::TimeTicks::Now());
158 scoped_ptr<StreamHandle> MakeEmptyStream() {
159 GURL url(std::string(url::kBlobScheme) + "://" + base::GenerateGUID());
160 scoped_refptr<Stream> stream(new Stream(stream_registry_.get(), NULL, url));
161 stream->Finalize();
162 return stream->CreateHandle();
165 private:
166 scoped_ptr<StreamRegistry> stream_registry_;
167 scoped_ptr<TestNavigationURLLoaderFactory> loader_factory_;
170 // PlzNavigate: Test that a proper NavigationRequest is created by
171 // BeginNavigation.
172 // Note that all PlzNavigate methods on the browser side require the use of the
173 // flag kEnableBrowserSideNavigation.
174 TEST_F(NavigatorTest, BrowserSideNavigationBeginNavigation) {
175 const GURL kUrl1("http://www.google.com/");
176 const GURL kUrl2("http://www.chromium.org/");
177 const GURL kUrl3("http://www.gmail.com/");
179 contents()->NavigateAndCommit(kUrl1);
181 EnableBrowserSideNavigation();
183 // Add a subframe.
184 FrameTreeNode* root = contents()->GetFrameTree()->root();
185 TestRenderFrameHost* subframe_rfh = static_cast<TestRenderFrameHost*>(
186 contents()->GetFrameTree()->AddFrame(
187 root, root->current_frame_host()->GetProcess()->GetID(), 14,
188 "Child"));
189 EXPECT_TRUE(subframe_rfh);
191 FrameTreeNode* subframe_node = subframe_rfh->frame_tree_node();
192 SendRequestNavigation(subframe_rfh->frame_tree_node(), kUrl2);
193 // There is no previous renderer in the subframe, so BeginNavigation is
194 // handled already.
195 NavigationRequest* subframe_request =
196 GetNavigationRequestForFrameTreeNode(subframe_node);
197 TestNavigationURLLoader* subframe_loader =
198 GetLoaderForNavigationRequest(subframe_request);
199 ASSERT_TRUE(subframe_request);
200 EXPECT_EQ(kUrl2, subframe_request->common_params().url);
201 EXPECT_EQ(kUrl2, subframe_loader->common_params().url);
202 // First party for cookies url should be that of the main frame.
203 EXPECT_EQ(kUrl1, subframe_loader->request_info()->first_party_for_cookies);
204 EXPECT_FALSE(subframe_loader->request_info()->is_main_frame);
205 EXPECT_TRUE(subframe_loader->request_info()->parent_is_main_frame);
207 SendRequestNavigation(root, kUrl3);
208 // Simulate a BeginNavigation IPC on the main frame.
209 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl3);
210 NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(root);
211 TestNavigationURLLoader* main_loader =
212 GetLoaderForNavigationRequest(main_request);
213 ASSERT_TRUE(main_request);
214 EXPECT_EQ(kUrl3, main_request->common_params().url);
215 EXPECT_EQ(kUrl3, main_loader->common_params().url);
216 EXPECT_EQ(kUrl3, main_loader->request_info()->first_party_for_cookies);
217 EXPECT_TRUE(main_loader->request_info()->is_main_frame);
218 EXPECT_FALSE(main_loader->request_info()->parent_is_main_frame);
221 // PlzNavigate: Test that RequestNavigation creates a NavigationRequest and that
222 // RenderFrameHost is not modified when the navigation commits.
223 TEST_F(NavigatorTest, BrowserSideNavigationRequestNavigationNoLiveRenderer) {
224 const GURL kUrl("http://www.google.com/");
226 EnableBrowserSideNavigation();
227 EXPECT_FALSE(main_test_rfh()->render_view_host()->IsRenderViewLive());
228 FrameTreeNode* node = main_test_rfh()->frame_tree_node();
229 SendRequestNavigation(node, kUrl);
230 NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
231 // A NavigationRequest should have been generated.
232 EXPECT_TRUE(main_request != NULL);
233 RenderFrameHostImpl* rfh = main_test_rfh();
235 // Now return the response without any redirects. This will cause the
236 // navigation to commit at the same URL.
237 scoped_refptr<ResourceResponse> response(new ResourceResponse);
238 GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
239 response, MakeEmptyStream());
240 main_request = GetNavigationRequestForFrameTreeNode(node);
242 // The main RFH should not have been changed, and the renderer should have
243 // been initialized.
244 EXPECT_EQ(rfh, main_test_rfh());
245 EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive());
246 EXPECT_TRUE(main_test_rfh()->render_view_host()->IsRenderViewLive());
249 // PlzNavigate: Test that commiting an HTTP 204 or HTTP 205 response cancels the
250 // navigation.
251 TEST_F(NavigatorTest, BrowserSideNavigationNoContent) {
252 const GURL kUrl1("http://www.chromium.org/");
253 const GURL kUrl2("http://www.google.com/");
255 // Load a URL.
256 contents()->NavigateAndCommit(kUrl1);
257 RenderFrameHostImpl* rfh = main_test_rfh();
258 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh->rfh_state());
259 FrameTreeNode* node = main_test_rfh()->frame_tree_node();
261 EnableBrowserSideNavigation();
263 // Navigate to a different site.
264 SendRequestNavigation(node, kUrl2);
265 main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
266 NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
267 ASSERT_TRUE(main_request);
269 // Commit an HTTP 204 response.
270 scoped_refptr<ResourceResponse> response(new ResourceResponse);
271 const char kNoContentHeaders[] = "HTTP/1.1 204 No Content\0\0";
272 response->head.headers = new net::HttpResponseHeaders(
273 std::string(kNoContentHeaders, arraysize(kNoContentHeaders)));
274 GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
275 response, MakeEmptyStream());
277 // There should be no pending RenderFrameHost; the navigation was aborted.
278 EXPECT_FALSE(GetNavigationRequestForFrameTreeNode(node));
279 EXPECT_FALSE(node->render_manager()->pending_frame_host());
281 // Now, repeat the test with 205 Reset Content.
283 // Navigate to a different site again.
284 SendRequestNavigation(node, kUrl2);
285 main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
286 main_request = GetNavigationRequestForFrameTreeNode(node);
287 ASSERT_TRUE(main_request);
289 // Commit an HTTP 205 response.
290 response = new ResourceResponse;
291 const char kResetContentHeaders[] = "HTTP/1.1 205 Reset Content\0\0";
292 response->head.headers = new net::HttpResponseHeaders(
293 std::string(kResetContentHeaders, arraysize(kResetContentHeaders)));
294 GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
295 response, MakeEmptyStream());
297 // There should be no pending RenderFrameHost; the navigation was aborted.
298 EXPECT_FALSE(GetNavigationRequestForFrameTreeNode(node));
299 EXPECT_FALSE(node->render_manager()->pending_frame_host());
302 // PlzNavigate: Test that a new RenderFrameHost is created when doing a cross
303 // site navigation.
304 TEST_F(NavigatorTest, BrowserSideNavigationCrossSiteNavigation) {
305 const GURL kUrl1("http://www.chromium.org/");
306 const GURL kUrl2("http://www.google.com/");
308 contents()->NavigateAndCommit(kUrl1);
309 RenderFrameHostImpl* rfh = main_test_rfh();
310 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh->rfh_state());
311 FrameTreeNode* node = main_test_rfh()->frame_tree_node();
313 EnableBrowserSideNavigation();
315 // Navigate to a different site.
316 SendRequestNavigation(node, kUrl2);
317 main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
318 NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
319 ASSERT_TRUE(main_request);
321 scoped_refptr<ResourceResponse> response(new ResourceResponse);
322 GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
323 response, MakeEmptyStream());
324 RenderFrameHostImpl* pending_rfh =
325 node->render_manager()->pending_frame_host();
326 ASSERT_TRUE(pending_rfh);
327 EXPECT_NE(pending_rfh, rfh);
328 EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
329 EXPECT_TRUE(pending_rfh->render_view_host()->IsRenderViewLive());
332 // PlzNavigate: Test that redirects are followed.
333 TEST_F(NavigatorTest, BrowserSideNavigationRedirectCrossSite) {
334 const GURL kUrl1("http://www.chromium.org/");
335 const GURL kUrl2("http://www.google.com/");
337 contents()->NavigateAndCommit(kUrl1);
338 RenderFrameHostImpl* rfh = main_test_rfh();
339 EXPECT_EQ(RenderFrameHostImpl::STATE_DEFAULT, rfh->rfh_state());
340 FrameTreeNode* node = main_test_rfh()->frame_tree_node();
342 EnableBrowserSideNavigation();
344 // Navigate to a URL on the same site.
345 SendRequestNavigation(node, kUrl1);
346 main_test_rfh()->SendBeginNavigationWithURL(kUrl1);
347 NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
348 ASSERT_TRUE(main_request);
350 // It then redirects to another site.
351 net::RedirectInfo redirect_info;
352 redirect_info.status_code = 302;
353 redirect_info.new_method = "GET";
354 redirect_info.new_url = kUrl2;
355 redirect_info.new_first_party_for_cookies = kUrl2;
356 scoped_refptr<ResourceResponse> response(new ResourceResponse);
357 GetLoaderForNavigationRequest(main_request)->CallOnRequestRedirected(
358 redirect_info, response);
360 // The redirect should have been followed.
361 EXPECT_EQ(1, GetLoaderForNavigationRequest(main_request)->redirect_count());
363 // Then it commits.
364 response = new ResourceResponse;
365 GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
366 response, MakeEmptyStream());
367 RenderFrameHostImpl* pending_rfh =
368 node->render_manager()->pending_frame_host();
369 ASSERT_TRUE(pending_rfh);
370 EXPECT_NE(pending_rfh, rfh);
371 EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
372 EXPECT_TRUE(pending_rfh->render_view_host()->IsRenderViewLive());
375 // PlzNavigate: Test that a navigation is cancelled if another request has been
376 // issued in the meantime.
377 TEST_F(NavigatorTest, BrowserSideNavigationReplacePendingNavigation) {
378 const GURL kUrl0("http://www.wikipedia.org/");
379 const GURL kUrl0_site = SiteInstance::GetSiteForURL(browser_context(), kUrl0);
380 const GURL kUrl1("http://www.chromium.org/");
381 const GURL kUrl2("http://www.google.com/");
382 const GURL kUrl2_site = SiteInstance::GetSiteForURL(browser_context(), kUrl2);
384 // Initialization.
385 contents()->NavigateAndCommit(kUrl0);
386 FrameTreeNode* node = main_test_rfh()->frame_tree_node();
387 EnableBrowserSideNavigation();
388 EXPECT_EQ(kUrl0_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
390 // Request navigation to the 1st URL.
391 SendRequestNavigation(node, kUrl1);
392 main_test_rfh()->SendBeginNavigationWithURL(kUrl1);
393 NavigationRequest* request1 = GetNavigationRequestForFrameTreeNode(node);
394 ASSERT_TRUE(request1);
395 EXPECT_EQ(kUrl1, request1->common_params().url);
396 base::WeakPtr<TestNavigationURLLoader> loader1 =
397 GetLoaderForNavigationRequest(request1)->AsWeakPtr();
399 // Request navigation to the 2nd URL; the NavigationRequest must have been
400 // replaced by a new one with a different URL.
401 SendRequestNavigation(node, kUrl2);
402 main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
403 NavigationRequest* request2 = GetNavigationRequestForFrameTreeNode(node);
404 ASSERT_TRUE(request2);
405 EXPECT_EQ(kUrl2, request2->common_params().url);
407 // Confirm that the first loader got destroyed.
408 EXPECT_FALSE(loader1);
410 // Confirm that the commit corresponds to the new request.
411 scoped_refptr<ResourceResponse> response(new ResourceResponse);
412 GetLoaderForNavigationRequest(request2)->CallOnResponseStarted(
413 response, MakeEmptyStream());
414 RenderFrameHostImpl* pending_rfh =
415 node->render_manager()->pending_frame_host();
416 ASSERT_TRUE(pending_rfh);
417 EXPECT_EQ(kUrl2_site, pending_rfh->GetSiteInstance()->GetSiteURL());
420 // PlzNavigate: Tests that the navigation histograms are correctly tracked both
421 // when PlzNavigate is enabled and disabled, and also ignores in-tab renderer
422 // initiated navigation for the non-enabled case.
423 // Note: the related histogram, Navigation.TimeToURLJobStart, cannot be tracked
424 // by this test as the IO thread is not running.
425 TEST_F(NavigatorTest, BrowserSideNavigationHistogramTest) {
426 const GURL kUrl0("http://www.google.com/");
427 const GURL kUrl1("http://www.chromium.org/");
428 base::HistogramTester histo_tester;
430 // Performs a "normal" non-PlzNavigate navigation
431 contents()->NavigateAndCommit(kUrl0);
432 histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 1);
434 // Performs an in-tab renderer initiated navigation
435 int32 new_page_id = 1 + contents()->GetMaxPageIDForSiteInstance(
436 main_test_rfh()->GetSiteInstance());
437 main_test_rfh()->SendNavigate(new_page_id, kUrl0);
438 histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 1);
440 // Performs a PlzNavigate navigation
441 EnableBrowserSideNavigation();
442 contents()->NavigateAndCommit(kUrl1);
443 histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 2);
446 // PlzNavigate: Test that a reload navigation is properly signaled to the
447 // renderer when the navigation can commit.
448 TEST_F(NavigatorTest, BrowserSideNavigationReload) {
449 const GURL kUrl("http://www.google.com/");
450 contents()->NavigateAndCommit(kUrl);
452 EnableBrowserSideNavigation();
453 FrameTreeNode* node = main_test_rfh()->frame_tree_node();
454 SendRequestNavigationWithParameters(
455 node, kUrl, Referrer(), ui::PAGE_TRANSITION_LINK,
456 NavigationController::RELOAD);
457 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl);
458 // A NavigationRequest should have been generated.
459 NavigationRequest* main_request =
460 GetNavigationRequestForFrameTreeNode(node);
461 ASSERT_TRUE(main_request != NULL);
462 EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD,
463 main_request->common_params().navigation_type);
464 int page_id = contents()->GetMaxPageIDForSiteInstance(
465 main_test_rfh()->GetSiteInstance()) + 1;
466 main_test_rfh()->SendNavigate(page_id, kUrl);
468 // Now do a shift+reload.
469 SendRequestNavigationWithParameters(
470 node, kUrl, Referrer(), ui::PAGE_TRANSITION_LINK,
471 NavigationController::RELOAD_IGNORING_CACHE);
472 contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl);
473 // A NavigationRequest should have been generated.
474 main_request = GetNavigationRequestForFrameTreeNode(node);
475 ASSERT_TRUE(main_request != NULL);
476 EXPECT_EQ(FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE,
477 main_request->common_params().navigation_type);
480 } // namespace content