Home | History | Annotate | Download | only in frame_host
      1 // Copyright 2013 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 "content/browser/frame_host/frame_tree.h"
      6 
      7 #include "base/run_loop.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "content/browser/frame_host/navigator_impl.h"
     10 #include "content/browser/frame_host/render_frame_host_factory.h"
     11 #include "content/browser/frame_host/render_frame_host_impl.h"
     12 #include "content/browser/renderer_host/render_view_host_impl.h"
     13 #include "content/public/test/mock_render_process_host.h"
     14 #include "content/public/test/test_browser_context.h"
     15 #include "content/public/test/test_browser_thread_bundle.h"
     16 #include "content/public/test/test_renderer_host.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 namespace content {
     20 namespace {
     21 
     22 class FrameTreeTest : public RenderViewHostTestHarness {
     23  protected:
     24   // Prints a FrameTree, for easy assertions of the tree hierarchy.
     25   std::string GetTreeState(FrameTree* frame_tree) {
     26     std::string result;
     27     AppendTreeNodeState(frame_tree->root(), &result);
     28     return result;
     29   }
     30 
     31  private:
     32   void AppendTreeNodeState(FrameTreeNode* node, std::string* result) {
     33     result->append(base::Int64ToString(node->frame_id()));
     34     if (!node->frame_name().empty()) {
     35       result->append(" '");
     36       result->append(node->frame_name());
     37       result->append("'");
     38     }
     39     result->append(": [");
     40     const char* separator = "";
     41     for (size_t i = 0; i < node->child_count(); i++) {
     42       result->append(separator);
     43       AppendTreeNodeState(node->child_at(i), result);
     44       separator = ", ";
     45     }
     46     result->append("]");
     47   }
     48 };
     49 
     50 // The root node never changes during navigation even though its
     51 // RenderFrameHost does.
     52 //  - Swapping main frame doesn't change root node.
     53 //  - Swapping back to NULL doesn't crash (easier tear-down for interstitials).
     54 //  - Main frame does not own RenderFrameHost.
     55 TEST_F(FrameTreeTest, RootNode) {
     56   FrameTree frame_tree(new NavigatorImpl(NULL, NULL), NULL, NULL, NULL, NULL);
     57 
     58   // Initial state has empty node.
     59   FrameTreeNode* root = frame_tree.root();
     60   ASSERT_TRUE(root);
     61   EXPECT_FALSE(frame_tree.GetMainFrame());
     62 
     63   // Swap in main frame.
     64   RenderFrameHostImpl* dummy = reinterpret_cast<RenderFrameHostImpl*>(0x1);
     65   frame_tree.SwapMainFrame(dummy);
     66   EXPECT_EQ(root, frame_tree.root());
     67   EXPECT_EQ(dummy, frame_tree.GetMainFrame());
     68 
     69   // Move back to NULL.
     70   frame_tree.SwapMainFrame(NULL);
     71   EXPECT_EQ(root, frame_tree.root());
     72   EXPECT_FALSE(frame_tree.GetMainFrame());
     73 
     74   // Move back to an invalid pointer, let the FrameTree go out of scope. Test
     75   // should not crash because the main frame isn't owned.
     76   frame_tree.SwapMainFrame(dummy);
     77 }
     78 
     79 // Test that swapping the main frame resets the renderer-assigned frame id.
     80 //  - On creation, frame id is unassigned.
     81 //  - After a swap, frame id is unassigned.
     82 TEST_F(FrameTreeTest, FirstNavigationAfterSwap) {
     83   FrameTree frame_tree(new NavigatorImpl(NULL, NULL), NULL, NULL, NULL, NULL);
     84 
     85   EXPECT_TRUE(frame_tree.IsFirstNavigationAfterSwap());
     86   EXPECT_EQ(FrameTreeNode::kInvalidFrameId,
     87             frame_tree.root()->frame_id());
     88   frame_tree.OnFirstNavigationAfterSwap(1);
     89   EXPECT_FALSE(frame_tree.IsFirstNavigationAfterSwap());
     90   EXPECT_EQ(1, frame_tree.root()->frame_id());
     91 
     92   frame_tree.SwapMainFrame(NULL);
     93   EXPECT_TRUE(frame_tree.IsFirstNavigationAfterSwap());
     94   EXPECT_EQ(FrameTreeNode::kInvalidFrameId,
     95             frame_tree.root()->frame_id());
     96 }
     97 
     98 // Exercise tree manipulation routines.
     99 //  - Add a series of nodes and verify tree structure.
    100 //  - Remove a series of nodes and verify tree structure.
    101 TEST_F(FrameTreeTest, Shape) {
    102   FrameTree frame_tree(new NavigatorImpl(NULL, NULL), NULL, NULL, NULL, NULL);
    103 
    104   std::string no_children_node("no children node");
    105   std::string deep_subtree("node with deep subtree");
    106 
    107   // Ensure the top-level node of the FrameTree is initialized by simulating a
    108   // main frame swap here.
    109   scoped_ptr<RenderFrameHostImpl> render_frame_host =
    110       RenderFrameHostFactory::Create(static_cast<RenderViewHostImpl*>(rvh()),
    111                                      NULL,
    112                                      &frame_tree,
    113                                      frame_tree.root(),
    114                                      process()->GetNextRoutingID(),
    115                                      false);
    116   frame_tree.SwapMainFrame(render_frame_host.get());
    117   frame_tree.OnFirstNavigationAfterSwap(5);
    118 
    119   ASSERT_EQ("5: []", GetTreeState(&frame_tree));
    120 
    121   // Simulate attaching a series of frames to build the frame tree.
    122   frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 14, std::string());
    123   frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 15, std::string());
    124   frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 16, std::string());
    125 
    126   frame_tree.AddFrame(process()->GetNextRoutingID(), 14, 244, std::string());
    127   frame_tree.AddFrame(process()->GetNextRoutingID(), 15, 255, no_children_node);
    128   frame_tree.AddFrame(process()->GetNextRoutingID(), 14, 245, std::string());
    129 
    130   ASSERT_EQ("5: [14: [244: [], 245: []], "
    131                 "15: [255 'no children node': []], "
    132                 "16: []]",
    133             GetTreeState(&frame_tree));
    134 
    135   frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 264, std::string());
    136   frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 265, std::string());
    137   frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 266, std::string());
    138   frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 267, deep_subtree);
    139   frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 268, std::string());
    140 
    141   frame_tree.AddFrame(process()->GetNextRoutingID(), 267, 365, std::string());
    142   frame_tree.AddFrame(process()->GetNextRoutingID(), 365, 455, std::string());
    143   frame_tree.AddFrame(process()->GetNextRoutingID(), 455, 555, std::string());
    144   frame_tree.AddFrame(process()->GetNextRoutingID(), 555, 655, std::string());
    145 
    146   // Now that's it's fully built, verify the tree structure is as expected.
    147   ASSERT_EQ("5: [14: [244: [], 245: []], "
    148                 "15: [255 'no children node': []], "
    149                 "16: [264: [], 265: [], 266: [], "
    150                      "267 'node with deep subtree': "
    151                          "[365: [455: [555: [655: []]]]], 268: []]]",
    152             GetTreeState(&frame_tree));
    153 
    154   // Test removing of nodes.
    155   frame_tree.RemoveFrame(NULL, 555, 655);
    156   ASSERT_EQ("5: [14: [244: [], 245: []], "
    157                 "15: [255 'no children node': []], "
    158                 "16: [264: [], 265: [], 266: [], "
    159                      "267 'node with deep subtree': "
    160                          "[365: [455: [555: []]]], 268: []]]",
    161             GetTreeState(&frame_tree));
    162 
    163   frame_tree.RemoveFrame(NULL, 16, 265);
    164   ASSERT_EQ("5: [14: [244: [], 245: []], "
    165                 "15: [255 'no children node': []], "
    166                 "16: [264: [], 266: [], "
    167                      "267 'node with deep subtree': "
    168                          "[365: [455: [555: []]]], 268: []]]",
    169             GetTreeState(&frame_tree));
    170 
    171   frame_tree.RemoveFrame(NULL, 5, 15);
    172   ASSERT_EQ("5: [14: [244: [], 245: []], "
    173                 "16: [264: [], 266: [], "
    174                      "267 'node with deep subtree': "
    175                          "[365: [455: [555: []]]], 268: []]]",
    176             GetTreeState(&frame_tree));
    177 }
    178 
    179 }  // namespace
    180 }  // namespace content
    181