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"
22 #include "net/test/embedded_test_server/embedded_test_server.h"
24 // For fine-grained suppression on flaky tests.
26 #include "base/win/windows_version.h"
31 class FrameTreeBrowserTest
: public ContentBrowserTest
{
33 FrameTreeBrowserTest() {}
35 void SetUpOnMainThread() override
{
36 host_resolver()->AddRule("*", "127.0.0.1");
37 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
38 SetupCrossSiteRedirector(embedded_test_server());
42 DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest
);
45 // Ensures FrameTree correctly reflects page structure during navigations.
46 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape
) {
47 GURL base_url
= embedded_test_server()->GetURL("A.com", "/site_isolation/");
49 // Load doc without iframes. Verify FrameTree just has root.
52 NavigateToURL(shell(), base_url
.Resolve("blank.html"));
54 static_cast<WebContentsImpl
*>(shell()->web_contents())->
55 GetFrameTree()->root();
56 EXPECT_EQ(0U, root
->child_count());
58 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
60 // Site-A Root -- Site-A frame1
62 WindowedNotificationObserver
observer1(
63 content::NOTIFICATION_LOAD_STOP
,
64 content::Source
<NavigationController
>(
65 &shell()->web_contents()->GetController()));
66 NavigateToURL(shell(), base_url
.Resolve("frames-X-X.html"));
68 ASSERT_EQ(2U, root
->child_count());
69 EXPECT_EQ(0U, root
->child_at(0)->child_count());
70 EXPECT_EQ(0U, root
->child_at(1)->child_count());
73 // TODO(ajwong): Talk with nasko and merge this functionality with
75 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape2
) {
76 NavigateToURL(shell(),
77 embedded_test_server()->GetURL("/frame_tree/top.html"));
79 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
80 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
82 // Check that the root node is properly created.
83 ASSERT_EQ(3UL, root
->child_count());
84 EXPECT_EQ(std::string(), root
->frame_name());
86 ASSERT_EQ(2UL, root
->child_at(0)->child_count());
87 EXPECT_STREQ("1-1-name", root
->child_at(0)->frame_name().c_str());
89 // Verify the deepest node exists and has the right name.
90 ASSERT_EQ(2UL, root
->child_at(2)->child_count());
91 EXPECT_EQ(1UL, root
->child_at(2)->child_at(1)->child_count());
92 EXPECT_EQ(0UL, root
->child_at(2)->child_at(1)->child_at(0)->child_count());
93 EXPECT_STREQ("3-1-name",
94 root
->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
96 // Navigate to about:blank, which should leave only the root node of the frame
97 // tree in the browser process.
98 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
100 root
= wc
->GetFrameTree()->root();
101 EXPECT_EQ(0UL, root
->child_count());
102 EXPECT_EQ(std::string(), root
->frame_name());
105 // Test that we can navigate away if the previous renderer doesn't clean up its
107 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeAfterCrash
) {
108 NavigateToURL(shell(),
109 embedded_test_server()->GetURL("/frame_tree/top.html"));
111 // Ensure the view and frame are live.
112 RenderViewHost
* rvh
= shell()->web_contents()->GetRenderViewHost();
113 RenderFrameHostImpl
* rfh
=
114 static_cast<RenderFrameHostImpl
*>(rvh
->GetMainFrame());
115 EXPECT_TRUE(rvh
->IsRenderViewLive());
116 EXPECT_TRUE(rfh
->IsRenderFrameLive());
118 // Crash the renderer so that it doesn't send any FrameDetached messages.
119 RenderProcessHostWatcher
crash_observer(
120 shell()->web_contents(),
121 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT
);
122 NavigateToURL(shell(), GURL(kChromeUICrashURL
));
123 crash_observer
.Wait();
125 // The frame tree should be cleared.
126 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
127 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
128 EXPECT_EQ(0UL, root
->child_count());
130 // Ensure the view and frame aren't live anymore.
131 EXPECT_FALSE(rvh
->IsRenderViewLive());
132 EXPECT_FALSE(rfh
->IsRenderFrameLive());
134 // Navigate to a new URL.
135 GURL
url(embedded_test_server()->GetURL("/title1.html"));
136 NavigateToURL(shell(), url
);
137 EXPECT_EQ(0UL, root
->child_count());
138 EXPECT_EQ(url
, root
->current_url());
140 // Ensure the view and frame are live again.
141 EXPECT_TRUE(rvh
->IsRenderViewLive());
142 EXPECT_TRUE(rfh
->IsRenderFrameLive());
145 // Test that we can navigate away if the previous renderer doesn't clean up its
147 // Flaky on Mac. http://crbug.com/452018
148 #if defined(OS_MACOSX)
149 #define MAYBE_NavigateWithLeftoverFrames DISABLED_NavigateWithLeftoverFrames
151 #define MAYBE_NavigateWithLeftoverFrames NavigateWithLeftoverFrames
153 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, MAYBE_NavigateWithLeftoverFrames
) {
155 // Flaky on XP bot http://crbug.com/468713
156 if (base::win::GetVersion() <= base::win::VERSION_XP
)
159 GURL base_url
= embedded_test_server()->GetURL("A.com", "/site_isolation/");
161 NavigateToURL(shell(),
162 embedded_test_server()->GetURL("/frame_tree/top.html"));
164 // Hang the renderer so that it doesn't send any FrameDetached messages.
165 // (This navigation will never complete, so don't wait for it.)
166 shell()->LoadURL(GURL(kChromeUIHangURL
));
168 // Check that the frame tree still has children.
169 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
170 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
171 ASSERT_EQ(3UL, root
->child_count());
173 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
174 // wait for the previous navigation to stop.
175 TestNavigationObserver
tab_observer(wc
, 1);
176 shell()->LoadURL(base_url
.Resolve("blank.html"));
179 // The frame tree should now be cleared.
180 EXPECT_EQ(0UL, root
->child_count());
183 // Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
184 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, IsRenderFrameLive
) {
185 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
186 NavigateToURL(shell(), main_url
);
188 // It is safe to obtain the root frame tree node here, as it doesn't change.
189 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
190 ->GetFrameTree()->root();
192 // The root and subframe should each have a live RenderFrame.
194 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
195 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
196 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
198 // Load a same-site page into iframe and it should still be live.
199 GURL
http_url(embedded_test_server()->GetURL("/title1.html"));
200 NavigateFrameToURL(root
->child_at(0), http_url
);
202 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
203 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
204 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
207 // Ensure that origins are correctly set on navigations.
208 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, OriginSetOnNavigation
) {
209 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
210 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
212 // It is safe to obtain the root frame tree node here, as it doesn't change.
213 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
214 ->GetFrameTree()->root();
216 // Extra '/' is added because the replicated origin is serialized in RFC 6454
217 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
219 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
220 main_url
.GetOrigin().spec());
222 GURL
frame_url(embedded_test_server()->GetURL("/title1.html"));
223 NavigateFrameToURL(root
->child_at(0), frame_url
);
226 root
->child_at(0)->current_replication_state().origin
.string() + '/',
227 frame_url
.GetOrigin().spec());
229 GURL
data_url("data:text/html,foo");
230 EXPECT_TRUE(NavigateToURL(shell(), data_url
));
232 // Navigating to a data URL should set a unique origin. This is represented
233 // as "null" per RFC 6454.
234 EXPECT_EQ(root
->current_replication_state().origin
.string(), "null");
236 // Re-navigating to a normal URL should update the origin.
237 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
238 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
239 main_url
.GetOrigin().spec());
242 // Ensure that sandbox flags are correctly set when child frames are created.
243 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, SandboxFlagsSetForChildFrames
) {
244 GURL
main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
245 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
247 // It is safe to obtain the root frame tree node here, as it doesn't change.
248 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
249 ->GetFrameTree()->root();
251 // Verify that sandbox flags are set properly for all FrameTreeNodes.
252 // First frame is completely sandboxed; second frame uses "allow-scripts",
253 // which resets both SandboxFlags::Scripts and
254 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
255 // third frame has "allow-scripts allow-same-origin".
256 EXPECT_EQ(root
->current_replication_state().sandbox_flags
,
258 EXPECT_EQ(root
->child_at(0)->current_replication_state().sandbox_flags
,
260 EXPECT_EQ(root
->child_at(1)->current_replication_state().sandbox_flags
,
261 SandboxFlags::ALL
& ~SandboxFlags::SCRIPTS
&
262 ~SandboxFlags::AUTOMATIC_FEATURES
);
263 EXPECT_EQ(root
->child_at(2)->current_replication_state().sandbox_flags
,
264 SandboxFlags::ALL
& ~SandboxFlags::SCRIPTS
&
265 ~SandboxFlags::AUTOMATIC_FEATURES
& ~SandboxFlags::ORIGIN
);
267 // Sandboxed frames should set a unique origin unless they have the
268 // "allow-same-origin" directive.
269 EXPECT_EQ(root
->child_at(0)->current_replication_state().origin
.string(),
271 EXPECT_EQ(root
->child_at(1)->current_replication_state().origin
.string(),
274 root
->child_at(2)->current_replication_state().origin
.string() + "/",
275 main_url
.GetOrigin().spec());
277 // Navigating to a different URL should not clear sandbox flags.
278 GURL
frame_url(embedded_test_server()->GetURL("/title1.html"));
279 NavigateFrameToURL(root
->child_at(0), frame_url
);
280 EXPECT_EQ(root
->child_at(0)->current_replication_state().sandbox_flags
,
284 class CrossProcessFrameTreeBrowserTest
: public ContentBrowserTest
{
286 CrossProcessFrameTreeBrowserTest() {}
288 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
289 command_line
->AppendSwitch(switches::kSitePerProcess
);
292 void SetUpOnMainThread() override
{
293 host_resolver()->AddRule("*", "127.0.0.1");
294 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
295 SetupCrossSiteRedirector(embedded_test_server());
299 DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest
);
302 // Ensure that we can complete a cross-process subframe navigation.
303 #if defined(OS_ANDROID)
304 #define MAYBE_CreateCrossProcessSubframeProxies DISABLED_CreateCrossProcessSubframeProxies
306 #define MAYBE_CreateCrossProcessSubframeProxies CreateCrossProcessSubframeProxies
308 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest
,
309 MAYBE_CreateCrossProcessSubframeProxies
) {
310 GURL
main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
311 NavigateToURL(shell(), main_url
);
313 // It is safe to obtain the root frame tree node here, as it doesn't change.
314 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
315 ->GetFrameTree()->root();
317 // There should not be a proxy for the root's own SiteInstance.
318 SiteInstance
* root_instance
= root
->current_frame_host()->GetSiteInstance();
319 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
321 // Load same-site page into iframe.
322 GURL
http_url(embedded_test_server()->GetURL("/title1.html"));
323 NavigateFrameToURL(root
->child_at(0), http_url
);
325 // Load cross-site page into iframe.
327 embedded_test_server()->GetURL("foo.com", "/title2.html"));
328 NavigateFrameToURL(root
->child_at(0), cross_site_url
);
330 // Ensure that we have created a new process for the subframe.
331 ASSERT_EQ(2U, root
->child_count());
332 FrameTreeNode
* child
= root
->child_at(0);
333 SiteInstance
* child_instance
= child
->current_frame_host()->GetSiteInstance();
334 RenderViewHost
* rvh
= child
->current_frame_host()->render_view_host();
335 RenderProcessHost
* rph
= child
->current_frame_host()->GetProcess();
337 EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh
);
338 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance
);
339 EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph
);
341 // Ensure that the root node has a proxy for the child node's SiteInstance.
342 EXPECT_TRUE(root
->render_manager()->GetRenderFrameProxyHost(child_instance
));
344 // Also ensure that the child has a proxy for the root node's SiteInstance.
345 EXPECT_TRUE(child
->render_manager()->GetRenderFrameProxyHost(root_instance
));
347 // The nodes should not have proxies for their own SiteInstance.
348 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
350 child
->render_manager()->GetRenderFrameProxyHost(child_instance
));
352 // Ensure that the RenderViews and RenderFrames are all live.
354 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
356 child
->current_frame_host()->render_view_host()->IsRenderViewLive());
357 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
358 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
361 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest
,
362 OriginSetOnCrossProcessNavigations
) {
363 GURL
main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
364 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
366 // It is safe to obtain the root frame tree node here, as it doesn't change.
367 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
368 ->GetFrameTree()->root();
370 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
371 main_url
.GetOrigin().spec());
373 // First frame is an about:blank frame. Check that its origin is correctly
374 // inherited from the parent.
376 root
->child_at(0)->current_replication_state().origin
.string() + '/',
377 main_url
.GetOrigin().spec());
379 // Second frame loads a same-site page. Its origin should also be the same
382 root
->child_at(1)->current_replication_state().origin
.string() + '/',
383 main_url
.GetOrigin().spec());
385 // Load cross-site page into the first frame.
387 embedded_test_server()->GetURL("foo.com", "/title2.html"));
388 NavigateFrameToURL(root
->child_at(0), cross_site_url
);
391 root
->child_at(0)->current_replication_state().origin
.string() + '/',
392 cross_site_url
.GetOrigin().spec());
394 // The root's origin shouldn't have changed.
395 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
396 main_url
.GetOrigin().spec());
398 GURL
data_url("data:text/html,foo");
399 NavigateFrameToURL(root
->child_at(1), data_url
);
401 // Navigating to a data URL should set a unique origin. This is represented
402 // as "null" per RFC 6454.
403 EXPECT_EQ(root
->child_at(1)->current_replication_state().origin
.string(),
407 } // namespace content