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