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 "content/browser/frame_host/frame_tree.h"
6 #include "content/browser/frame_host/frame_tree_node.h"
7 #include "content/browser/renderer_host/render_view_host_impl.h"
8 #include "content/browser/web_contents/web_contents_impl.h"
9 #include "content/public/browser/notification_service.h"
10 #include "content/public/browser/notification_types.h"
11 #include "content/public/common/url_constants.h"
12 #include "content/public/test/browser_test_utils.h"
13 #include "content/public/test/content_browser_test.h"
14 #include "content/public/test/content_browser_test_utils.h"
15 #include "content/public/test/test_navigation_observer.h"
16 #include "content/public/test/test_utils.h"
17 #include "content/shell/browser/shell.h"
18 #include "content/test/content_browser_test_utils_internal.h"
19 #include "net/dns/mock_host_resolver.h"
20 #include "net/test/embedded_test_server/embedded_test_server.h"
21 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
23 // For fine-grained suppression on flaky tests.
25 #include "base/win/windows_version.h"
30 class FrameTreeBrowserTest
: public ContentBrowserTest
{
32 FrameTreeBrowserTest() {}
34 void SetUpOnMainThread() override
{
35 host_resolver()->AddRule("*", "127.0.0.1");
36 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
37 SetupCrossSiteRedirector(embedded_test_server());
41 DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest
);
44 // Ensures FrameTree correctly reflects page structure during navigations.
45 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape
) {
46 GURL base_url
= embedded_test_server()->GetURL("A.com", "/site_isolation/");
48 // Load doc without iframes. Verify FrameTree just has root.
51 NavigateToURL(shell(), base_url
.Resolve("blank.html"));
53 static_cast<WebContentsImpl
*>(shell()->web_contents())->
54 GetFrameTree()->root();
55 EXPECT_EQ(0U, root
->child_count());
57 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
59 // Site-A Root -- Site-A frame1
61 WindowedNotificationObserver
observer1(
62 content::NOTIFICATION_LOAD_STOP
,
63 content::Source
<NavigationController
>(
64 &shell()->web_contents()->GetController()));
65 NavigateToURL(shell(), base_url
.Resolve("frames-X-X.html"));
67 ASSERT_EQ(2U, root
->child_count());
68 EXPECT_EQ(0U, root
->child_at(0)->child_count());
69 EXPECT_EQ(0U, root
->child_at(1)->child_count());
72 // TODO(ajwong): Talk with nasko and merge this functionality with
74 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeShape2
) {
75 NavigateToURL(shell(),
76 embedded_test_server()->GetURL("/frame_tree/top.html"));
78 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
79 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
81 // Check that the root node is properly created.
82 ASSERT_EQ(3UL, root
->child_count());
83 EXPECT_EQ(std::string(), root
->frame_name());
85 ASSERT_EQ(2UL, root
->child_at(0)->child_count());
86 EXPECT_STREQ("1-1-name", root
->child_at(0)->frame_name().c_str());
88 // Verify the deepest node exists and has the right name.
89 ASSERT_EQ(2UL, root
->child_at(2)->child_count());
90 EXPECT_EQ(1UL, root
->child_at(2)->child_at(1)->child_count());
91 EXPECT_EQ(0UL, root
->child_at(2)->child_at(1)->child_at(0)->child_count());
92 EXPECT_STREQ("3-1-name",
93 root
->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
95 // Navigate to about:blank, which should leave only the root node of the frame
96 // tree in the browser process.
97 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
99 root
= wc
->GetFrameTree()->root();
100 EXPECT_EQ(0UL, root
->child_count());
101 EXPECT_EQ(std::string(), root
->frame_name());
104 // Test that we can navigate away if the previous renderer doesn't clean up its
106 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, FrameTreeAfterCrash
) {
107 NavigateToURL(shell(),
108 embedded_test_server()->GetURL("/frame_tree/top.html"));
110 // Ensure the view and frame are live.
111 RenderViewHost
* rvh
= shell()->web_contents()->GetRenderViewHost();
112 RenderFrameHostImpl
* rfh
=
113 static_cast<RenderFrameHostImpl
*>(rvh
->GetMainFrame());
114 EXPECT_TRUE(rvh
->IsRenderViewLive());
115 EXPECT_TRUE(rfh
->IsRenderFrameLive());
117 // Crash the renderer so that it doesn't send any FrameDetached messages.
118 RenderProcessHostWatcher
crash_observer(
119 shell()->web_contents(),
120 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT
);
121 NavigateToURL(shell(), GURL(kChromeUICrashURL
));
122 crash_observer
.Wait();
124 // The frame tree should be cleared.
125 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
126 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
127 EXPECT_EQ(0UL, root
->child_count());
129 // Ensure the view and frame aren't live anymore.
130 EXPECT_FALSE(rvh
->IsRenderViewLive());
131 EXPECT_FALSE(rfh
->IsRenderFrameLive());
133 // Navigate to a new URL.
134 GURL
url(embedded_test_server()->GetURL("/title1.html"));
135 NavigateToURL(shell(), url
);
136 EXPECT_EQ(0UL, root
->child_count());
137 EXPECT_EQ(url
, root
->current_url());
139 // Ensure the view and frame are live again.
140 EXPECT_TRUE(rvh
->IsRenderViewLive());
141 EXPECT_TRUE(rfh
->IsRenderFrameLive());
144 // Test that we can navigate away if the previous renderer doesn't clean up its
146 // Flaky on Mac. http://crbug.com/452018
147 #if defined(OS_MACOSX)
148 #define MAYBE_NavigateWithLeftoverFrames DISABLED_NavigateWithLeftoverFrames
150 #define MAYBE_NavigateWithLeftoverFrames NavigateWithLeftoverFrames
152 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, MAYBE_NavigateWithLeftoverFrames
) {
154 // Flaky on XP bot http://crbug.com/468713
155 if (base::win::GetVersion() <= base::win::VERSION_XP
)
158 GURL base_url
= embedded_test_server()->GetURL("A.com", "/site_isolation/");
160 NavigateToURL(shell(),
161 embedded_test_server()->GetURL("/frame_tree/top.html"));
163 // Hang the renderer so that it doesn't send any FrameDetached messages.
164 // (This navigation will never complete, so don't wait for it.)
165 shell()->LoadURL(GURL(kChromeUIHangURL
));
167 // Check that the frame tree still has children.
168 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(shell()->web_contents());
169 FrameTreeNode
* root
= wc
->GetFrameTree()->root();
170 ASSERT_EQ(3UL, root
->child_count());
172 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
173 // wait for the previous navigation to stop.
174 TestNavigationObserver
tab_observer(wc
, 1);
175 shell()->LoadURL(base_url
.Resolve("blank.html"));
178 // The frame tree should now be cleared.
179 EXPECT_EQ(0UL, root
->child_count());
182 // Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
183 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, IsRenderFrameLive
) {
184 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
185 NavigateToURL(shell(), main_url
);
187 // It is safe to obtain the root frame tree node here, as it doesn't change.
188 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
189 ->GetFrameTree()->root();
191 // The root and subframe should each have a live RenderFrame.
193 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
194 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
195 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
197 // Load a same-site page into iframe and it should still be live.
198 GURL
http_url(embedded_test_server()->GetURL("/title1.html"));
199 NavigateFrameToURL(root
->child_at(0), http_url
);
201 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
202 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
203 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
206 // Ensure that origins are correctly set on navigations.
207 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, OriginSetOnNavigation
) {
208 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
209 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
211 // It is safe to obtain the root frame tree node here, as it doesn't change.
212 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
213 ->GetFrameTree()->root();
215 // Extra '/' is added because the replicated origin is serialized in RFC 6454
216 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
218 EXPECT_EQ(root
->current_replication_state().origin
.Serialize() + '/',
219 main_url
.GetOrigin().spec());
221 GURL
frame_url(embedded_test_server()->GetURL("/title1.html"));
222 NavigateFrameToURL(root
->child_at(0), frame_url
);
225 root
->child_at(0)->current_replication_state().origin
.Serialize() + '/',
226 frame_url
.GetOrigin().spec());
228 GURL
data_url("data:text/html,foo");
229 EXPECT_TRUE(NavigateToURL(shell(), data_url
));
231 // Navigating to a data URL should set a unique origin. This is represented
232 // as "null" per RFC 6454.
233 EXPECT_EQ(root
->current_replication_state().origin
.Serialize(), "null");
235 // Re-navigating to a normal URL should update the origin.
236 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
237 EXPECT_EQ(root
->current_replication_state().origin
.Serialize() + '/',
238 main_url
.GetOrigin().spec());
241 // Ensure that sandbox flags are correctly set when child frames are created.
242 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, SandboxFlagsSetForChildFrames
) {
243 GURL
main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
244 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
246 // It is safe to obtain the root frame tree node here, as it doesn't change.
247 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
248 ->GetFrameTree()->root();
250 // Verify that sandbox flags are set properly for all FrameTreeNodes.
251 // First frame is completely sandboxed; second frame uses "allow-scripts",
252 // which resets both SandboxFlags::Scripts and
253 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
254 // third frame has "allow-scripts allow-same-origin".
255 EXPECT_EQ(root
->current_replication_state().sandbox_flags
,
256 blink::WebSandboxFlags::None
);
257 EXPECT_EQ(root
->child_at(0)->current_replication_state().sandbox_flags
,
258 blink::WebSandboxFlags::All
);
259 EXPECT_EQ(root
->child_at(1)->current_replication_state().sandbox_flags
,
260 blink::WebSandboxFlags::All
& ~blink::WebSandboxFlags::Scripts
&
261 ~blink::WebSandboxFlags::AutomaticFeatures
);
262 EXPECT_EQ(root
->child_at(2)->current_replication_state().sandbox_flags
,
263 blink::WebSandboxFlags::All
& ~blink::WebSandboxFlags::Scripts
&
264 ~blink::WebSandboxFlags::AutomaticFeatures
&
265 ~blink::WebSandboxFlags::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
.Serialize(),
271 EXPECT_EQ(root
->child_at(1)->current_replication_state().origin
.Serialize(),
274 root
->child_at(2)->current_replication_state().origin
.Serialize() + "/",
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
,
281 blink::WebSandboxFlags::All
);
284 // Ensure that a popup opened from a subframe sets its opener to the subframe's
285 // FrameTreeNode, and that the opener is cleared if the subframe is destroyed.
286 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest
, SubframeOpenerSetForNewWindow
) {
287 GURL
main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
288 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
290 // It is safe to obtain the root frame tree node here, as it doesn't change.
291 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
295 // Open a new window from a subframe.
296 ShellAddedObserver new_shell_observer
;
297 GURL
popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
298 EXPECT_TRUE(ExecuteScript(root
->child_at(0)->current_frame_host(),
299 "window.open('" + popup_url
.spec() + "');"));
300 Shell
* new_shell
= new_shell_observer
.GetShell();
301 WebContents
* new_contents
= new_shell
->web_contents();
302 WaitForLoadStop(new_contents
);
304 // Check that the new window's opener points to the correct subframe on
306 FrameTreeNode
* popup_root
=
307 static_cast<WebContentsImpl
*>(new_contents
)->GetFrameTree()->root();
308 EXPECT_EQ(root
->child_at(0), popup_root
->opener());
310 // Close the original window. This should clear the new window's opener.
312 EXPECT_EQ(nullptr, popup_root
->opener());
315 class CrossProcessFrameTreeBrowserTest
: public ContentBrowserTest
{
317 CrossProcessFrameTreeBrowserTest() {}
319 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
320 IsolateAllSitesForTesting(command_line
);
323 void SetUpOnMainThread() override
{
324 host_resolver()->AddRule("*", "127.0.0.1");
325 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
326 SetupCrossSiteRedirector(embedded_test_server());
330 DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest
);
333 // Ensure that we can complete a cross-process subframe navigation.
334 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest
,
335 CreateCrossProcessSubframeProxies
) {
336 GURL
main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
337 NavigateToURL(shell(), main_url
);
339 // It is safe to obtain the root frame tree node here, as it doesn't change.
340 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
341 ->GetFrameTree()->root();
343 // There should not be a proxy for the root's own SiteInstance.
344 SiteInstance
* root_instance
= root
->current_frame_host()->GetSiteInstance();
345 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
347 // Load same-site page into iframe.
348 GURL
http_url(embedded_test_server()->GetURL("/title1.html"));
349 NavigateFrameToURL(root
->child_at(0), http_url
);
351 // Load cross-site page into iframe.
353 embedded_test_server()->GetURL("foo.com", "/title2.html"));
354 NavigateFrameToURL(root
->child_at(0), cross_site_url
);
356 // Ensure that we have created a new process for the subframe.
357 ASSERT_EQ(2U, root
->child_count());
358 FrameTreeNode
* child
= root
->child_at(0);
359 SiteInstance
* child_instance
= child
->current_frame_host()->GetSiteInstance();
360 RenderViewHost
* rvh
= child
->current_frame_host()->render_view_host();
361 RenderProcessHost
* rph
= child
->current_frame_host()->GetProcess();
363 EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh
);
364 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance
);
365 EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph
);
367 // Ensure that the root node has a proxy for the child node's SiteInstance.
368 EXPECT_TRUE(root
->render_manager()->GetRenderFrameProxyHost(child_instance
));
370 // Also ensure that the child has a proxy for the root node's SiteInstance.
371 EXPECT_TRUE(child
->render_manager()->GetRenderFrameProxyHost(root_instance
));
373 // The nodes should not have proxies for their own SiteInstance.
374 EXPECT_FALSE(root
->render_manager()->GetRenderFrameProxyHost(root_instance
));
376 child
->render_manager()->GetRenderFrameProxyHost(child_instance
));
378 // Ensure that the RenderViews and RenderFrames are all live.
380 root
->current_frame_host()->render_view_host()->IsRenderViewLive());
382 child
->current_frame_host()->render_view_host()->IsRenderViewLive());
383 EXPECT_TRUE(root
->current_frame_host()->IsRenderFrameLive());
384 EXPECT_TRUE(root
->child_at(0)->current_frame_host()->IsRenderFrameLive());
387 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest
,
388 OriginSetOnCrossProcessNavigations
) {
389 GURL
main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
390 EXPECT_TRUE(NavigateToURL(shell(), main_url
));
392 // It is safe to obtain the root frame tree node here, as it doesn't change.
393 FrameTreeNode
* root
= static_cast<WebContentsImpl
*>(shell()->web_contents())
394 ->GetFrameTree()->root();
396 EXPECT_EQ(root
->current_replication_state().origin
.Serialize() + '/',
397 main_url
.GetOrigin().spec());
399 // First frame is an about:blank frame. Check that its origin is correctly
400 // inherited from the parent.
402 root
->child_at(0)->current_replication_state().origin
.Serialize() + '/',
403 main_url
.GetOrigin().spec());
405 // Second frame loads a same-site page. Its origin should also be the same
408 root
->child_at(1)->current_replication_state().origin
.Serialize() + '/',
409 main_url
.GetOrigin().spec());
411 // Load cross-site page into the first frame.
413 embedded_test_server()->GetURL("foo.com", "/title2.html"));
414 NavigateFrameToURL(root
->child_at(0), cross_site_url
);
417 root
->child_at(0)->current_replication_state().origin
.Serialize() + '/',
418 cross_site_url
.GetOrigin().spec());
420 // The root's origin shouldn't have changed.
421 EXPECT_EQ(root
->current_replication_state().origin
.Serialize() + '/',
422 main_url
.GetOrigin().spec());
424 GURL
data_url("data:text/html,foo");
425 NavigateFrameToURL(root
->child_at(1), data_url
);
427 // Navigating to a data URL should set a unique origin. This is represented
428 // as "null" per RFC 6454.
429 EXPECT_EQ(root
->child_at(1)->current_replication_state().origin
.Serialize(),
433 } // namespace content