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