frame_tree_browsertest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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  // Crash the renderer so that it doesn't send any FrameDetached messages.
109  RenderProcessHostWatcher crash_observer(
110      shell()->web_contents(),
111      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
112  NavigateToURL(shell(), GURL(kChromeUICrashURL));
113  crash_observer.Wait();
114
115  // The frame tree should be cleared.
116  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
117  FrameTreeNode* root = wc->GetFrameTree()->root();
118  EXPECT_EQ(0UL, root->child_count());
119
120  // Navigate to a new URL.
121  GURL url(test_server()->GetURL("files/title1.html"));
122  NavigateToURL(shell(), url);
123  EXPECT_EQ(0UL, root->child_count());
124  EXPECT_EQ(url, root->current_url());
125}
126
127// Test that we can navigate away if the previous renderer doesn't clean up its
128// child frames.
129IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
130  host_resolver()->AddRule("*", "127.0.0.1");
131  ASSERT_TRUE(test_server()->Start());
132
133  GURL base_url = test_server()->GetURL("files/site_isolation/");
134  GURL::Replacements replace_host;
135  std::string host_str("A.com");  // Must stay in scope with replace_host.
136  replace_host.SetHostStr(host_str);
137  base_url = base_url.ReplaceComponents(replace_host);
138
139  NavigateToURL(shell(),
140                test_server()->GetURL("files/frame_tree/top.html"));
141
142  // Hang the renderer so that it doesn't send any FrameDetached messages.
143  // (This navigation will never complete, so don't wait for it.)
144  shell()->LoadURL(GURL(kChromeUIHangURL));
145
146  // Check that the frame tree still has children.
147  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
148  FrameTreeNode* root = wc->GetFrameTree()->root();
149  ASSERT_EQ(3UL, root->child_count());
150
151  // Navigate to a new URL.  We use LoadURL because NavigateToURL will try to
152  // wait for the previous navigation to stop.
153  TestNavigationObserver tab_observer(wc, 1);
154  shell()->LoadURL(base_url.Resolve("blank.html"));
155  tab_observer.Wait();
156
157  // The frame tree should now be cleared.
158  EXPECT_EQ(0UL, root->child_count());
159}
160
161class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest {
162 public:
163  CrossProcessFrameTreeBrowserTest() {}
164
165  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
166    command_line->AppendSwitch(switches::kSitePerProcess);
167  }
168
169 private:
170  DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest);
171};
172
173// Ensure that we can complete a cross-process subframe navigation.
174#if defined(OS_ANDROID)
175#define MAYBE_CreateCrossProcessSubframeProxies DISABLED_CreateCrossProcessSubframeProxies
176#else
177#define MAYBE_CreateCrossProcessSubframeProxies CreateCrossProcessSubframeProxies
178#endif
179IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
180                       MAYBE_CreateCrossProcessSubframeProxies) {
181  host_resolver()->AddRule("*", "127.0.0.1");
182  ASSERT_TRUE(test_server()->Start());
183  GURL main_url(test_server()->GetURL("files/site_per_process_main.html"));
184  NavigateToURL(shell(), main_url);
185
186  // It is safe to obtain the root frame tree node here, as it doesn't change.
187  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
188                            ->GetFrameTree()->root();
189
190  // Load same-site page into iframe.
191  GURL http_url(test_server()->GetURL("files/title1.html"));
192  NavigateFrameToURL(root->child_at(0), http_url);
193
194  // These must stay in scope with replace_host.
195  GURL::Replacements replace_host;
196  std::string foo_com("foo.com");
197
198  // Load cross-site page into iframe.
199  GURL cross_site_url(test_server()->GetURL("files/title2.html"));
200  replace_host.SetHostStr(foo_com);
201  cross_site_url = cross_site_url.ReplaceComponents(replace_host);
202  NavigateFrameToURL(root->child_at(0), cross_site_url);
203
204  // Ensure that we have created a new process for the subframe.
205  ASSERT_EQ(1U, root->child_count());
206  FrameTreeNode* child = root->child_at(0);
207  SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
208  RenderViewHost* rvh = child->current_frame_host()->render_view_host();
209  RenderProcessHost* rph = child->current_frame_host()->GetProcess();
210
211  EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh);
212  EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
213  EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph);
214
215  // Ensure that the root node has a proxy for the child node's SiteInstance.
216  EXPECT_TRUE(root->render_manager()->proxy_hosts_[site_instance->GetId()]);
217
218  // Also ensure that the child has a proxy for the root node's SiteInstance.
219  EXPECT_TRUE(child->render_manager()->proxy_hosts_[
220              root->current_frame_host()->GetSiteInstance()->GetId()]);
221}
222
223}  // namespace content
224