Home | History | Annotate | Download | only in frame_host
      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