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"
26 class FrameTreeBrowserTest
: public ContentBrowserTest
{
28 FrameTreeBrowserTest() {}
30 void SetUpOnMainThread() override
{
31 host_resolver()->AddRule("*", "127.0.0.1");
32 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
33 SetupCrossSiteRedirector(embedded_test_server());
37 DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest
);
40 // Ensures FrameTree correctly reflects page structure during navigations.
41 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape
) {
42 GURL base_url
= embedded_test_server()->GetURL("A.com", "/site_isolation/");
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 NavigateToURL(shell(),
72 embedded_test_server()->GetURL("/frame_tree/top.html"));
74 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
75 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
77 // Check that the root node is properly created.
78 ASSERT_EQ(3UL, root
->child_count());
79 EXPECT_EQ(std::string(), root
->frame_name());
81 ASSERT_EQ(2UL, root
->child_at(0)->child_count());
82 EXPECT_STREQ("1-1-name", root
->child_at(0)->frame_name().c_str());
84 // Verify the deepest node exists and has the right name.
85 ASSERT_EQ(2UL, root
->child_at(2)->child_count());
86 EXPECT_EQ(1UL, root
->child_at(2)->child_at(1)->child_count());
87 EXPECT_EQ(0UL, root
->child_at(2)->child_at(1)->child_at(0)->child_count());
88 EXPECT_STREQ("3-1-name",
89 root
->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
91 // Navigate to about:blank, which should leave only the root node of the frame
92 // tree in the browser process.
93 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
95 root
= wc
->GetFrameTree()->root();
96 EXPECT_EQ(0UL, root
->child_count());
97 EXPECT_EQ(std::string(), root
->frame_name());
100 // Test that we can navigate away if the previous renderer doesn't clean up its
102 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeAfterCrash
) {
103 NavigateToURL(shell(),
104 embedded_test_server()->GetURL("/frame_tree/top.html"));
106 // Ensure the view and frame are live.
107 RenderViewHost
* rvh
= shell()->web_contents()->GetRenderViewHost();
108 RenderFrameHostImpl
* rfh
=
109 static_cast<RenderFrameHostImpl
*>(rvh
->GetMainFrame());
110 EXPECT_TRUE(rvh
->IsRenderViewLive());
111 EXPECT_TRUE(rfh
->IsRenderFrameLive());
113 // Crash the renderer so that it doesn't send any FrameDetached messages.
114 RenderProcessHostWatcher
crash_observer(
115 shell()->web_contents(),
116 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT
);
117 NavigateToURL(shell(), GURL(kChromeUICrashURL
));
118 crash_observer
.Wait();
120 // The frame tree should be cleared.
121 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
122 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
123 EXPECT_EQ(0UL, root
->child_count());
125 // Ensure the view and frame aren't live anymore.
126 EXPECT_FALSE(rvh
->IsRenderViewLive());
127 EXPECT_FALSE(rfh
->IsRenderFrameLive());
129 // Navigate to a new URL.
130 GURL
url(embedded_test_server()->GetURL("/title1.html"));
131 NavigateToURL(shell(), url
);
132 EXPECT_EQ(0UL, root
->child_count());
133 EXPECT_EQ(url
, root
->current_url());
135 // Ensure the view and frame are live again.
136 EXPECT_TRUE(rvh
->IsRenderViewLive());
137 EXPECT_TRUE(rfh
->IsRenderFrameLive());
140 // Test that we can navigate away if the previous renderer doesn't clean up its
142 // Flaky on Mac. http://crbug.com/452018
143 #if defined(OS_MACOSX)
144 #define MAYBE_NavigateWithLeftoverFrames DISABLED_NavigateWithLeftoverFrames
146 #define MAYBE_NavigateWithLeftoverFrames NavigateWithLeftoverFrames
148 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, MAYBE_NavigateWithLeftoverFrames
) {
149 GURL base_url
= embedded_test_server()->GetURL("A.com", "/site_isolation/");
151 NavigateToURL(shell(),
152 embedded_test_server()->GetURL("/frame_tree/top.html"));
154 // Hang the renderer so that it doesn't send any FrameDetached messages.
155 // (This navigation will never complete, so don't wait for it.)
156 shell()->LoadURL(GURL(kChromeUIHangURL
));
158 // Check that the frame tree still has children.
159 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
160 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
161 ASSERT_EQ(3UL, root
->child_count());
163 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
164 // wait for the previous navigation to stop.
165 TestNavigationObserver
tab_observer(wc
, 1);
166 shell()->LoadURL(base_url
.Resolve("blank.html"));
169 // The frame tree should now be cleared.
170 EXPECT_EQ(0UL, root
->child_count());
173 // Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
174 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, IsRenderFrameLive
) {
175 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
176 NavigateToURL(shell(), main_url
);
178 // It is safe to obtain the root frame tree node here, as it doesn't change.
179 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
180 ->GetFrameTree()->root();
182 // The root and subframe should each have a live RenderFrame.
184 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
185 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
186 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
188 // Load a same-site page into iframe and it should still be live.
189 GURL
http_url(embedded_test_server()->GetURL("/title1.html"));
190 NavigateFrameToURL(root
->child_at(0), http_url
);
192 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
193 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
194 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
197 // Ensure that origins are correctly set on navigations.
198 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, OriginSetOnNavigation
) {
199 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
200 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
202 // It is safe to obtain the root frame tree node here, as it doesn't change.
203 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
204 ->GetFrameTree()->root();
206 // Extra '/' is added because the replicated origin is serialized in RFC 6454
207 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
209 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
210 main_url
.GetOrigin().spec());
212 GURL
frame_url(embedded_test_server()->GetURL("/title1.html"));
213 NavigateFrameToURL(root
->child_at(0), frame_url
);
216 root
->child_at(0)->current_replication_state().origin
.string() + '/',
217 frame_url
.GetOrigin().spec());
219 GURL
data_url("data:text/html,foo");
220 EXPECT_TRUE(NavigateToURL(shell(), data_url
));
222 // Navigating to a data URL should set a unique origin. This is represented
223 // as "null" per RFC 6454.
224 EXPECT_EQ(root
->current_replication_state().origin
.string(), "null");
226 // Re-navigating to a normal URL should update the origin.
227 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
228 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
229 main_url
.GetOrigin().spec());
232 // Ensure that sandbox flags are correctly set when child frames are created.
233 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, SandboxFlagsSetForChildFrames
) {
234 GURL
main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
235 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
237 // It is safe to obtain the root frame tree node here, as it doesn't change.
238 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
239 ->GetFrameTree()->root();
241 // Verify that sandbox flags are set properly for all FrameTreeNodes.
242 // First frame is completely sandboxed; second frame uses "allow-scripts",
243 // which resets both SandboxFlags::Scripts and
244 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
245 // third frame has "allow-scripts allow-same-origin".
246 EXPECT_EQ(root
->current_replication_state().sandbox_flags
,
248 EXPECT_EQ(root
->child_at(0)->current_replication_state().sandbox_flags
,
250 EXPECT_EQ(root
->child_at(1)->current_replication_state().sandbox_flags
,
251 SandboxFlags::ALL
& ~SandboxFlags::SCRIPTS
&
252 ~SandboxFlags::AUTOMATIC_FEATURES
);
253 EXPECT_EQ(root
->child_at(2)->current_replication_state().sandbox_flags
,
254 SandboxFlags::ALL
& ~SandboxFlags::SCRIPTS
&
255 ~SandboxFlags::AUTOMATIC_FEATURES
& ~SandboxFlags::ORIGIN
);
257 // Sandboxed frames should set a unique origin unless they have the
258 // "allow-same-origin" directive.
259 EXPECT_EQ(root
->child_at(0)->current_replication_state().origin
.string(),
261 EXPECT_EQ(root
->child_at(1)->current_replication_state().origin
.string(),
264 root
->child_at(2)->current_replication_state().origin
.string() + "/",
265 main_url
.GetOrigin().spec());
267 // Navigating to a different URL should not clear sandbox flags.
268 GURL
frame_url(embedded_test_server()->GetURL("/title1.html"));
269 NavigateFrameToURL(root
->child_at(0), frame_url
);
270 EXPECT_EQ(root
->child_at(0)->current_replication_state().sandbox_flags
,
274 class CrossProcessFrameTreeBrowserTest
: public ContentBrowserTest
{
276 CrossProcessFrameTreeBrowserTest() {}
278 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
279 command_line
->AppendSwitch(switches::kSitePerProcess
);
282 void SetUpOnMainThread() override
{
283 host_resolver()->AddRule("*", "127.0.0.1");
284 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
285 SetupCrossSiteRedirector(embedded_test_server());
289 DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest
);
292 // Ensure that we can complete a cross-process subframe navigation.
293 #if defined(OS_ANDROID)
294 #define MAYBE_CreateCrossProcessSubframeProxies DISABLED_CreateCrossProcessSubframeProxies
296 #define MAYBE_CreateCrossProcessSubframeProxies CreateCrossProcessSubframeProxies
298 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest
,
299 MAYBE_CreateCrossProcessSubframeProxies
) {
300 GURL
main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
301 NavigateToURL(shell(), main_url
);
303 // It is safe to obtain the root frame tree node here, as it doesn't change.
304 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
305 ->GetFrameTree()->root();
307 // There should not be a proxy for the root's own SiteInstance.
308 SiteInstance
* root_instance
= root
->current_frame_host()->GetSiteInstance();
309 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
311 // Load same-site page into iframe.
312 GURL
http_url(embedded_test_server()->GetURL("/title1.html"));
313 NavigateFrameToURL(root
->child_at(0), http_url
);
315 // Load cross-site page into iframe.
317 embedded_test_server()->GetURL("foo.com", "/title2.html"));
318 NavigateFrameToURL(root
->child_at(0), cross_site_url
);
320 // Ensure that we have created a new process for the subframe.
321 ASSERT_EQ(2U, root
->child_count());
322 FrameTreeNode
* child
= root
->child_at(0);
323 SiteInstance
* child_instance
= child
->current_frame_host()->GetSiteInstance();
324 RenderViewHost
* rvh
= child
->current_frame_host()->render_view_host();
325 RenderProcessHost
* rph
= child
->current_frame_host()->GetProcess();
327 EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh
);
328 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance
);
329 EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph
);
331 // Ensure that the root node has a proxy for the child node's SiteInstance.
332 EXPECT_TRUE(root
->render_manager()->GetRenderFrameProxyHost(child_instance
));
334 // Also ensure that the child has a proxy for the root node's SiteInstance.
335 EXPECT_TRUE(child
->render_manager()->GetRenderFrameProxyHost(root_instance
));
337 // The nodes should not have proxies for their own SiteInstance.
338 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
340 child
->render_manager()->GetRenderFrameProxyHost(child_instance
));
342 // Ensure that the RenderViews and RenderFrames are all live.
344 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
346 child
->current_frame_host()->render_view_host()->IsRenderViewLive());
347 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
348 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
351 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest
,
352 OriginSetOnCrossProcessNavigations
) {
353 GURL
main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
354 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
356 // It is safe to obtain the root frame tree node here, as it doesn't change.
357 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
358 ->GetFrameTree()->root();
360 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
361 main_url
.GetOrigin().spec());
363 // First frame is an about:blank frame. Check that its origin is correctly
364 // inherited from the parent.
366 root
->child_at(0)->current_replication_state().origin
.string() + '/',
367 main_url
.GetOrigin().spec());
369 // Second frame loads a same-site page. Its origin should also be the same
372 root
->child_at(1)->current_replication_state().origin
.string() + '/',
373 main_url
.GetOrigin().spec());
375 // Load cross-site page into the first frame.
377 embedded_test_server()->GetURL("foo.com", "/title2.html"));
378 NavigateFrameToURL(root
->child_at(0), cross_site_url
);
381 root
->child_at(0)->current_replication_state().origin
.string() + '/',
382 cross_site_url
.GetOrigin().spec());
384 // The root's origin shouldn't have changed.
385 EXPECT_EQ(root
->current_replication_state().origin
.string() + '/',
386 main_url
.GetOrigin().spec());
388 GURL
data_url("data:text/html,foo");
389 NavigateFrameToURL(root
->child_at(1), data_url
);
391 // Navigating to a data URL should set a unique origin. This is represented
392 // as "null" per RFC 6454.
393 EXPECT_EQ(root
->child_at(1)->current_replication_state().origin
.string(),
397 } // namespace content