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 23 namespace content { 24 25 class 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. 34 IN_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. 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")); 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. 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")); 107 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()); 114 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(); 121 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()); 126 127 // Ensure the view and frame aren't live anymore. 128 EXPECT_FALSE(rvh->IsRenderViewLive()); 129 EXPECT_FALSE(rfh->IsRenderFrameLive()); 130 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()); 136 137 // Ensure the view and frame are live again. 138 EXPECT_TRUE(rvh->IsRenderViewLive()); 139 EXPECT_TRUE(rfh->IsRenderFrameLive()); 140 } 141 142 // Test that we can navigate away if the previous renderer doesn't clean up its 143 // child frames. 144 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) { 145 host_resolver()->AddRule("*", "127.0.0.1"); 146 ASSERT_TRUE(test_server()->Start()); 147 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); 153 154 NavigateToURL(shell(), 155 test_server()->GetURL("files/frame_tree/top.html")); 156 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)); 160 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()); 165 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")); 170 tab_observer.Wait(); 171 172 // The frame tree should now be cleared. 173 EXPECT_EQ(0UL, root->child_count()); 174 } 175 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); 182 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(); 186 187 // The root and subframe should each have a live RenderFrame. 188 EXPECT_TRUE( 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()); 192 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); 196 EXPECT_TRUE( 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()); 200 } 201 202 class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest { 203 public: 204 CrossProcessFrameTreeBrowserTest() {} 205 206 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { 207 command_line->AppendSwitch(switches::kSitePerProcess); 208 } 209 210 private: 211 DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest); 212 }; 213 214 // Ensure that we can complete a cross-process subframe navigation. 215 #if defined(OS_ANDROID) 216 #define MAYBE_CreateCrossProcessSubframeProxies DISABLED_CreateCrossProcessSubframeProxies 217 #else 218 #define MAYBE_CreateCrossProcessSubframeProxies CreateCrossProcessSubframeProxies 219 #endif 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); 226 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(); 230 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)); 234 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); 238 239 // These must stay in scope with replace_host. 240 GURL::Replacements replace_host; 241 std::string foo_com("foo.com"); 242 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); 248 249 // Ensure that we have created a new process for the subframe. 250 ASSERT_EQ(1U, 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(); 255 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); 259 260 // Ensure that the root node has a proxy for the child node's SiteInstance. 261 EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(child_instance)); 262 263 // Also ensure that the child has a proxy for the root node's SiteInstance. 264 EXPECT_TRUE(child->render_manager()->GetRenderFrameProxyHost(root_instance)); 265 266 // The nodes should not have proxies for their own SiteInstance. 267 EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance)); 268 EXPECT_FALSE( 269 child->render_manager()->GetRenderFrameProxyHost(child_instance)); 270 271 // Ensure that the RenderViews and RenderFrames are all live. 272 EXPECT_TRUE( 273 root->current_frame_host()->render_view_host()->IsRenderViewLive()); 274 EXPECT_TRUE( 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()); 278 } 279 280 } // namespace content 281