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 "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"
25 class FrameTreeBrowserTest
: public ContentBrowserTest
{
27 FrameTreeBrowserTest() {}
30 DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest
);
33 // Ensures FrameTree correctly reflects page structure during navigations.
34 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape
) {
35 host_resolver()->AddRule("*", "127.0.0.1");
36 ASSERT_TRUE(test_server()->Start());
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
);
44 // Load doc without iframes. Verify FrameTree just has root.
47 NavigateToURL(shell(), base_url
.Resolve("blank.html"));
49 static_cast<WebContentsImpl
*>(shell()->web_contents())->
50 GetFrameTree()->root();
51 EXPECT_EQ(0U, root
->child_count());
53 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
55 // Site-A Root -- Site-A frame1
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"));
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());
68 // TODO(ajwong): Talk with nasko and merge this functionality with
70 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape2
) {
71 ASSERT_TRUE(test_server()->Start());
72 NavigateToURL(shell(),
73 test_server()->GetURL("files/frame_tree/top.html"));
75 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
76 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
78 // Check that the root node is properly created.
79 ASSERT_EQ(3UL, root
->child_count());
80 EXPECT_EQ(std::string(), root
->frame_name());
82 ASSERT_EQ(2UL, root
->child_at(0)->child_count());
83 EXPECT_STREQ("1-1-name", root
->child_at(0)->frame_name().c_str());
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());
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"));
96 root
= wc
->GetFrameTree()->root();
97 EXPECT_EQ(0UL, root
->child_count());
98 EXPECT_EQ(std::string(), root
->frame_name());
101 // Test that we can navigate away if the previous renderer doesn't clean up its
103 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeAfterCrash
) {
104 ASSERT_TRUE(test_server()->Start());
105 NavigateToURL(shell(),
106 test_server()->GetURL("files/frame_tree/top.html"));
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());
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();
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());
127 // Ensure the view and frame aren't live anymore.
128 EXPECT_FALSE(rvh
->IsRenderViewLive());
129 EXPECT_FALSE(rfh
->IsRenderFrameLive());
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());
137 // Ensure the view and frame are live again.
138 EXPECT_TRUE(rvh
->IsRenderViewLive());
139 EXPECT_TRUE(rfh
->IsRenderFrameLive());
142 // Test that we can navigate away if the previous renderer doesn't clean up its
144 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, NavigateWithLeftoverFrames
) {
145 host_resolver()->AddRule("*", "127.0.0.1");
146 ASSERT_TRUE(test_server()->Start());
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
);
154 NavigateToURL(shell(),
155 test_server()->GetURL("files/frame_tree/top.html"));
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
));
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());
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"));
172 // The frame tree should now be cleared.
173 EXPECT_EQ(0UL, root
->child_count());
176 // Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
177 IN_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
);
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();
187 // The root and subframe should each have a live RenderFrame.
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());
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
);
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());
202 class CrossProcessFrameTreeBrowserTest
: public ContentBrowserTest
{
204 CrossProcessFrameTreeBrowserTest() {}
206 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
207 command_line
->AppendSwitch(switches::kSitePerProcess
);
211 DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest
);
214 // Ensure that we can complete a cross-process subframe navigation.
215 #if defined(OS_ANDROID)
216 #define MAYBE_CreateCrossProcessSubframeProxies DISABLED_CreateCrossProcessSubframeProxies
218 #define MAYBE_CreateCrossProcessSubframeProxies CreateCrossProcessSubframeProxies
220 IN_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
);
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();
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
));
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
);
239 // These must stay in scope with replace_host.
240 GURL::Replacements replace_host
;
241 std::string
foo_com("foo.com");
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
);
249 // Ensure that we have created a new process for the subframe.
250 ASSERT_EQ(2U, 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();
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
);
260 // Ensure that the root node has a proxy for the child node's SiteInstance.
261 EXPECT_TRUE(root
->render_manager()->GetRenderFrameProxyHost(child_instance
));
263 // Also ensure that the child has a proxy for the root node's SiteInstance.
264 EXPECT_TRUE(child
->render_manager()->GetRenderFrameProxyHost(root_instance
));
266 // The nodes should not have proxies for their own SiteInstance.
267 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
269 child
->render_manager()->GetRenderFrameProxyHost(child_instance
));
271 // Ensure that the RenderViews and RenderFrames are all live.
273 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
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());
280 } // namespace content