frame_tree_browsertest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 "content/browser/frame_host/frame_tree.h"
7#include "content/browser/frame_host/frame_tree_node.h"
8#include "content/browser/renderer_host/render_view_host_impl.h"
9#include "content/browser/web_contents/web_contents_impl.h"
10#include "content/public/browser/notification_service.h"
11#include "content/public/browser/notification_types.h"
12#include "content/public/common/content_switches.h"
13#include "content/public/common/url_constants.h"
14#include "content/public/test/browser_test_utils.h"
15#include "content/public/test/content_browser_test.h"
16#include "content/public/test/content_browser_test_utils.h"
17#include "content/public/test/test_navigation_observer.h"
18#include "content/public/test/test_utils.h"
19#include "content/shell/browser/shell.h"
20#include "content/test/content_browser_test_utils_internal.h"
21#include "net/dns/mock_host_resolver.h"
22
23namespace content {
24
25class FrameTreeBrowserTest : public ContentBrowserTest {
26 public:
27  FrameTreeBrowserTest() {}
28
29 private:
30  DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest);
31};
32
33// Ensures FrameTree correctly reflects page structure during navigations.
34IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) {
35  host_resolver()->AddRule("*", "127.0.0.1");
36  ASSERT_TRUE(test_server()->Start());
37
38  GURL base_url = test_server()->GetURL("files/site_isolation/");
39  GURL::Replacements replace_host;
40  std::string host_str("A.com");  // Must stay in scope with replace_host.
41  replace_host.SetHostStr(host_str);
42  base_url = base_url.ReplaceComponents(replace_host);
43
44  // Load doc without iframes. Verify FrameTree just has root.
45  // Frame tree:
46  //   Site-A Root
47  NavigateToURL(shell(), base_url.Resolve("blank.html"));
48  FrameTreeNode* root =
49      static_cast<WebContentsImpl*>(shell()->web_contents())->
50      GetFrameTree()->root();
51  EXPECT_EQ(0U, root->child_count());
52
53  // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
54  // Frame tree:
55  //   Site-A Root -- Site-A frame1
56  //              \-- Site-A frame2
57  WindowedNotificationObserver observer1(
58      content::NOTIFICATION_LOAD_STOP,
59      content::Source<NavigationController>(
60          &shell()->web_contents()->GetController()));
61  NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"));
62  observer1.Wait();
63  ASSERT_EQ(2U, root->child_count());
64  EXPECT_EQ(0U, root->child_at(0)->child_count());
65  EXPECT_EQ(0U, root->child_at(1)->child_count());
66}
67
68// TODO(ajwong): Talk with nasko and merge this functionality with
69// FrameTreeShape.
70IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) {
71  ASSERT_TRUE(test_server()->Start());
72  NavigateToURL(shell(),
73                test_server()->GetURL("files/frame_tree/top.html"));
74
75  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
76  FrameTreeNode* root = wc->GetFrameTree()->root();
77
78  // Check that the root node is properly created.
79  ASSERT_EQ(3UL, root->child_count());
80  EXPECT_EQ(std::string(), root->frame_name());
81
82  ASSERT_EQ(2UL, root->child_at(0)->child_count());
83  EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
84
85  // Verify the deepest node exists and has the right name.
86  ASSERT_EQ(2UL, root->child_at(2)->child_count());
87  EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
88  EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
89  EXPECT_STREQ("3-1-name",
90      root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
91
92  // Navigate to about:blank, which should leave only the root node of the frame
93  // tree in the browser process.
94  NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
95
96  root = wc->GetFrameTree()->root();
97  EXPECT_EQ(0UL, root->child_count());
98  EXPECT_EQ(std::string(), root->frame_name());
99}
100
101// Test that we can navigate away if the previous renderer doesn't clean up its
102// child frames.
103IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) {
104  ASSERT_TRUE(test_server()->Start());
105  NavigateToURL(shell(),
106                test_server()->GetURL("files/frame_tree/top.html"));
107
108  // Ensure the view and frame are live.
109  RenderViewHost* rvh = shell()->web_contents()->GetRenderViewHost();
110  RenderFrameHostImpl* rfh =
111      static_cast<RenderFrameHostImpl*>(rvh->GetMainFrame());
112  EXPECT_TRUE(rvh->IsRenderViewLive());
113  EXPECT_TRUE(rfh->IsRenderFrameLive());
114
115  // Crash the renderer so that it doesn't send any FrameDetached messages.
116  RenderProcessHostWatcher crash_observer(
117      shell()->web_contents(),
118      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
119  NavigateToURL(shell(), GURL(kChromeUICrashURL));
120  crash_observer.Wait();
121
122  // The frame tree should be cleared.
123  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
124  FrameTreeNode* root = wc->GetFrameTree()->root();
125  EXPECT_EQ(0UL, root->child_count());
126
127  // Ensure the view and frame aren't live anymore.
128  EXPECT_FALSE(rvh->IsRenderViewLive());
129  EXPECT_FALSE(rfh->IsRenderFrameLive());
130
131  // Navigate to a new URL.
132  GURL url(test_server()->GetURL("files/title1.html"));
133  NavigateToURL(shell(), url);
134  EXPECT_EQ(0UL, root->child_count());
135  EXPECT_EQ(url, root->current_url());
136
137  // Ensure the view and frame are live again.
138  EXPECT_TRUE(rvh->IsRenderViewLive());
139  EXPECT_TRUE(rfh->IsRenderFrameLive());
140}
141
142// Test that we can navigate away if the previous renderer doesn't clean up its
143// child frames.
144IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
145  host_resolver()->AddRule("*", "127.0.0.1");
146  ASSERT_TRUE(test_server()->Start());
147
148  GURL base_url = test_server()->GetURL("files/site_isolation/");
149  GURL::Replacements replace_host;
150  std::string host_str("A.com");  // Must stay in scope with replace_host.
151  replace_host.SetHostStr(host_str);
152  base_url = base_url.ReplaceComponents(replace_host);
153
154  NavigateToURL(shell(),
155                test_server()->GetURL("files/frame_tree/top.html"));
156
157  // Hang the renderer so that it doesn't send any FrameDetached messages.
158  // (This navigation will never complete, so don't wait for it.)
159  shell()->LoadURL(GURL(kChromeUIHangURL));
160
161  // Check that the frame tree still has children.
162  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
163  FrameTreeNode* root = wc->GetFrameTree()->root();
164  ASSERT_EQ(3UL, root->child_count());
165
166  // Navigate to a new URL.  We use LoadURL because NavigateToURL will try to
167  // wait for the previous navigation to stop.
168  TestNavigationObserver tab_observer(wc, 1);
169  shell()->LoadURL(base_url.Resolve("blank.html"));
170  tab_observer.Wait();
171
172  // The frame tree should now be cleared.
173  EXPECT_EQ(0UL, root->child_count());
174}
175
176// Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
177IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) {
178  host_resolver()->AddRule("*", "127.0.0.1");
179  ASSERT_TRUE(test_server()->Start());
180  GURL main_url(test_server()->GetURL("files/frame_tree/top.html"));
181  NavigateToURL(shell(), main_url);
182
183  // It is safe to obtain the root frame tree node here, as it doesn't change.
184  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
185                            ->GetFrameTree()->root();
186
187  // The root and subframe should each have a live RenderFrame.
188  EXPECT_TRUE(
189      root->current_frame_host()->render_view_host()->IsRenderViewLive());
190  EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
191  EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
192
193  // Load a same-site page into iframe and it should still be live.
194  GURL http_url(test_server()->GetURL("files/title1.html"));
195  NavigateFrameToURL(root->child_at(0), http_url);
196  EXPECT_TRUE(
197      root->current_frame_host()->render_view_host()->IsRenderViewLive());
198  EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
199  EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
200}
201
202class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest {
203 public:
204  CrossProcessFrameTreeBrowserTest() {}
205
206  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
207    command_line->AppendSwitch(switches::kSitePerProcess);
208  }
209
210 private:
211  DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest);
212};
213
214// Ensure that we can complete a cross-process subframe navigation.
215#if defined(OS_ANDROID)
216#define MAYBE_CreateCrossProcessSubframeProxies DISABLED_CreateCrossProcessSubframeProxies
217#else
218#define MAYBE_CreateCrossProcessSubframeProxies CreateCrossProcessSubframeProxies
219#endif
220IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
221                       MAYBE_CreateCrossProcessSubframeProxies) {
222  host_resolver()->AddRule("*", "127.0.0.1");
223  ASSERT_TRUE(test_server()->Start());
224  GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
225  NavigateToURL(shell(), main_url);
226
227  // It is safe to obtain the root frame tree node here, as it doesn't change.
228  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
229                            ->GetFrameTree()->root();
230
231  // There should not be a proxy for the root's own SiteInstance.
232  SiteInstance* root_instance = root->current_frame_host()->GetSiteInstance();
233  EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance));
234
235  // Load same-site page into iframe.
236  GURL http_url(test_server()->GetURL("files/title1.html"));
237  NavigateFrameToURL(root->child_at(0), http_url);
238
239  // These must stay in scope with replace_host.
240  GURL::Replacements replace_host;
241  std::string foo_com("foo.com");
242
243  // Load cross-site page into iframe.
244  GURL cross_site_url(test_server()->GetURL("files/title2.html"));
245  replace_host.SetHostStr(foo_com);
246  cross_site_url = cross_site_url.ReplaceComponents(replace_host);
247  NavigateFrameToURL(root->child_at(0), cross_site_url);
248
249  // Ensure that we have created a new process for the subframe.
250  ASSERT_EQ(1U, root->child_count());
251  FrameTreeNode* child = root->child_at(0);
252  SiteInstance* child_instance = child->current_frame_host()->GetSiteInstance();
253  RenderViewHost* rvh = child->current_frame_host()->render_view_host();
254  RenderProcessHost* rph = child->current_frame_host()->GetProcess();
255
256  EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh);
257  EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance);
258  EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph);
259
260  // Ensure that the root node has a proxy for the child node's SiteInstance.
261  EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(child_instance));
262
263  // Also ensure that the child has a proxy for the root node's SiteInstance.
264  EXPECT_TRUE(child->render_manager()->GetRenderFrameProxyHost(root_instance));
265
266  // The nodes should not have proxies for their own SiteInstance.
267  EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance));
268  EXPECT_FALSE(
269      child->render_manager()->GetRenderFrameProxyHost(child_instance));
270
271  // Ensure that the RenderViews and RenderFrames are all live.
272  EXPECT_TRUE(
273      root->current_frame_host()->render_view_host()->IsRenderViewLive());
274  EXPECT_TRUE(
275      child->current_frame_host()->render_view_host()->IsRenderViewLive());
276  EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
277  EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
278}
279
280}  // namespace content
281