Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / content / browser / frame_host / navigator_impl_unittest.cc
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.
4
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"
36
37 namespace content {
38
39 namespace {
40
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) {
52   }
53
54   // NavigationURLLoader implementation.
55   void FollowRedirect() override { redirect_count_++; }
56
57   const CommonNavigationParams& common_params() const { return common_params_; }
58   NavigationRequestInfo* request_info() const { return request_info_.get(); }
59
60   void CallOnRequestRedirected(
61       const net::RedirectInfo& redirect_info,
62       const scoped_refptr<ResourceResponse>& response) {
63     delegate_->OnRequestRedirected(redirect_info, response);
64   }
65
66   void CallOnResponseStarted(
67       const scoped_refptr<ResourceResponse>& response,
68       scoped_ptr<StreamHandle> body) {
69     delegate_->OnResponseStarted(response, body.Pass());
70   }
71
72   int redirect_count() { return redirect_count_; }
73
74  private:
75   CommonNavigationParams common_params_;
76   scoped_ptr<NavigationRequestInfo> request_info_;
77   NavigationURLLoaderDelegate* delegate_;
78   int redirect_count_;
79 };
80
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));
93   }
94 };
95
96 }  // namespace
97
98 class NavigatorTest : public RenderViewHostImplTestHarness {
99  public:
100   NavigatorTest() : stream_registry_(new StreamRegistry) {}
101
102   void SetUp() override {
103     RenderViewHostImplTestHarness::SetUp();
104     loader_factory_.reset(new TestNavigationURLLoaderFactory);
105     NavigationURLLoader::SetFactoryForTesting(loader_factory_.get());
106   }
107
108   void TearDown() override {
109     NavigationURLLoader::SetFactoryForTesting(nullptr);
110     loader_factory_.reset();
111     RenderViewHostImplTestHarness::TearDown();
112   }
113
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());
120   }
121
122   TestNavigationURLLoader* GetLoaderForNavigationRequest(
123       NavigationRequest* request) const {
124     return static_cast<TestNavigationURLLoader*>(request->loader_for_testing());
125   }
126
127   void EnableBrowserSideNavigation() {
128     CommandLine::ForCurrentProcess()->AppendSwitch(
129         switches::kEnableBrowserSideNavigation);
130   }
131
132   void SendRequestNavigation(FrameTreeNode* node,
133                              const GURL& url) {
134     SendRequestNavigationWithParameters(
135         node, url, Referrer(), ui::PAGE_TRANSITION_LINK,
136         NavigationController::NO_RELOAD);
137   }
138
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());
156   }
157
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();
163   }
164
165  private:
166   scoped_ptr<StreamRegistry> stream_registry_;
167   scoped_ptr<TestNavigationURLLoaderFactory> loader_factory_;
168 };
169
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/");
178
179   contents()->NavigateAndCommit(kUrl1);
180
181   EnableBrowserSideNavigation();
182
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);
190
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);
206
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);
219 }
220
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/");
225
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();
234
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);
241
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());
247 }
248
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/");
254
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();
260
261   EnableBrowserSideNavigation();
262
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);
268
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());
276
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());
280
281   // Now, repeat the test with 205 Reset Content.
282
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);
288
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());
296
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());
300 }
301
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/");
307
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();
312
313   EnableBrowserSideNavigation();
314
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);
320
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());
330 }
331
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/");
336
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();
341
342   EnableBrowserSideNavigation();
343
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);
349
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);
359
360   // The redirect should have been followed.
361   EXPECT_EQ(1, GetLoaderForNavigationRequest(main_request)->redirect_count());
362
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());
373 }
374
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);
383
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());
389
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();
398
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);
406
407   // Confirm that the first loader got destroyed.
408   EXPECT_FALSE(loader1);
409
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());
418 }
419
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;
429
430   // Performs a "normal" non-PlzNavigate navigation
431   contents()->NavigateAndCommit(kUrl0);
432   histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 1);
433
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);
439
440   // Performs a PlzNavigate navigation
441   EnableBrowserSideNavigation();
442   contents()->NavigateAndCommit(kUrl1);
443   histo_tester.ExpectTotalCount("Navigation.TimeToCommit", 2);
444 }
445
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);
451
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);
467
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);
478 }
479
480 }  // namespace content