blob: 27b8167814993d8bb3f8fc91c2ac06d8667d770e [file] [log] [blame]
[email protected]9b159a52013-10-03 17:24:551// 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
[email protected]d4a8ca482013-10-30 21:06:405#include "content/browser/frame_host/frame_tree.h"
[email protected]9b159a52013-10-03 17:24:556
7#include "base/run_loop.h"
[email protected]7cc7ebd2013-10-08 00:59:008#include "base/strings/string_number_conversions.h"
[email protected]190b8c52013-11-09 01:35:449#include "content/browser/frame_host/navigator.h"
[email protected]2a18ee222013-11-21 07:52:4410#include "content/browser/frame_host/render_frame_host_factory.h"
[email protected]d4a8ca482013-10-30 21:06:4011#include "content/browser/frame_host/render_frame_host_impl.h"
[email protected]9b159a52013-10-03 17:24:5512#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
19namespace content {
20namespace {
21
22class FrameTreeTest : public RenderViewHostTestHarness {
[email protected]7cc7ebd2013-10-08 00:59:0023 protected:
24 // Prints a FrameTree, for easy assertions of the tree hierarchy.
25 std::string GetTreeState(FrameTree* frame_tree) {
26 std::string result;
[email protected]fa944cb82013-11-15 17:51:2127 AppendTreeNodeState(frame_tree->root(), &result);
[email protected]7cc7ebd2013-10-08 00:59:0028 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 }
[email protected]9b159a52013-10-03 17:24:5548};
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.
55TEST_F(FrameTreeTest, RootNode) {
[email protected]fa944cb82013-11-15 17:51:2156 FrameTree frame_tree(new Navigator(NULL, NULL), NULL, NULL, NULL);
[email protected]9b159a52013-10-03 17:24:5557
58 // Initial state has empty node.
[email protected]fa944cb82013-11-15 17:51:2159 FrameTreeNode* root = frame_tree.root();
[email protected]9b159a52013-10-03 17:24:5560 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);
[email protected]fa944cb82013-11-15 17:51:2166 EXPECT_EQ(root, frame_tree.root());
[email protected]9b159a52013-10-03 17:24:5567 EXPECT_EQ(dummy, frame_tree.GetMainFrame());
68
69 // Move back to NULL.
70 frame_tree.SwapMainFrame(NULL);
[email protected]fa944cb82013-11-15 17:51:2171 EXPECT_EQ(root, frame_tree.root());
[email protected]9b159a52013-10-03 17:24:5572 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.
82TEST_F(FrameTreeTest, FirstNavigationAfterSwap) {
[email protected]fa944cb82013-11-15 17:51:2183 FrameTree frame_tree(new Navigator(NULL, NULL), NULL, NULL, NULL);
[email protected]9b159a52013-10-03 17:24:5584
85 EXPECT_TRUE(frame_tree.IsFirstNavigationAfterSwap());
86 EXPECT_EQ(FrameTreeNode::kInvalidFrameId,
[email protected]fa944cb82013-11-15 17:51:2187 frame_tree.root()->frame_id());
[email protected]9b159a52013-10-03 17:24:5588 frame_tree.OnFirstNavigationAfterSwap(1);
89 EXPECT_FALSE(frame_tree.IsFirstNavigationAfterSwap());
[email protected]fa944cb82013-11-15 17:51:2190 EXPECT_EQ(1, frame_tree.root()->frame_id());
[email protected]9b159a52013-10-03 17:24:5591
92 frame_tree.SwapMainFrame(NULL);
93 EXPECT_TRUE(frame_tree.IsFirstNavigationAfterSwap());
94 EXPECT_EQ(FrameTreeNode::kInvalidFrameId,
[email protected]fa944cb82013-11-15 17:51:2195 frame_tree.root()->frame_id());
[email protected]9b159a52013-10-03 17:24:5596}
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.
101TEST_F(FrameTreeTest, Shape) {
[email protected]fa944cb82013-11-15 17:51:21102 FrameTree frame_tree(new Navigator(NULL, NULL), NULL, NULL, NULL);
[email protected]190b8c52013-11-09 01:35:44103
[email protected]9b159a52013-10-03 17:24:55104 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.
[email protected]2a18ee222013-11-21 07:52:44109 scoped_ptr<RenderFrameHostImpl> render_frame_host =
110 RenderFrameHostFactory::Create(static_cast<RenderViewHostImpl*>(rvh()),
111 &frame_tree,
112 process()->GetNextRoutingID(),
113 false);
114 frame_tree.SwapMainFrame(render_frame_host.get());
[email protected]9b159a52013-10-03 17:24:55115 frame_tree.OnFirstNavigationAfterSwap(5);
116
[email protected]7cc7ebd2013-10-08 00:59:00117 ASSERT_EQ("5: []", GetTreeState(&frame_tree));
118
[email protected]9b159a52013-10-03 17:24:55119 // Simulate attaching a series of frames to build the frame tree.
120 frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 14, std::string());
121 frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 15, std::string());
122 frame_tree.AddFrame(process()->GetNextRoutingID(), 5, 16, std::string());
123
124 frame_tree.AddFrame(process()->GetNextRoutingID(), 14, 244, std::string());
[email protected]7cc7ebd2013-10-08 00:59:00125 frame_tree.AddFrame(process()->GetNextRoutingID(), 15, 255, no_children_node);
[email protected]9b159a52013-10-03 17:24:55126 frame_tree.AddFrame(process()->GetNextRoutingID(), 14, 245, std::string());
127
[email protected]7cc7ebd2013-10-08 00:59:00128 ASSERT_EQ("5: [14: [244: [], 245: []], "
129 "15: [255 'no children node': []], "
130 "16: []]",
131 GetTreeState(&frame_tree));
[email protected]9b159a52013-10-03 17:24:55132
133 frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 264, std::string());
134 frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 265, std::string());
135 frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 266, std::string());
136 frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 267, deep_subtree);
137 frame_tree.AddFrame(process()->GetNextRoutingID(), 16, 268, std::string());
138
139 frame_tree.AddFrame(process()->GetNextRoutingID(), 267, 365, std::string());
140 frame_tree.AddFrame(process()->GetNextRoutingID(), 365, 455, std::string());
141 frame_tree.AddFrame(process()->GetNextRoutingID(), 455, 555, std::string());
142 frame_tree.AddFrame(process()->GetNextRoutingID(), 555, 655, std::string());
143
[email protected]7cc7ebd2013-10-08 00:59:00144 // Now that's it's fully built, verify the tree structure is as expected.
145 ASSERT_EQ("5: [14: [244: [], 245: []], "
146 "15: [255 'no children node': []], "
147 "16: [264: [], 265: [], 266: [], "
148 "267 'node with deep subtree': "
149 "[365: [455: [555: [655: []]]]], 268: []]]",
150 GetTreeState(&frame_tree));
[email protected]9b159a52013-10-03 17:24:55151
152 // Test removing of nodes.
153 frame_tree.RemoveFrame(555, 655);
[email protected]7cc7ebd2013-10-08 00:59:00154 ASSERT_EQ("5: [14: [244: [], 245: []], "
155 "15: [255 'no children node': []], "
156 "16: [264: [], 265: [], 266: [], "
157 "267 'node with deep subtree': "
158 "[365: [455: [555: []]]], 268: []]]",
159 GetTreeState(&frame_tree));
[email protected]9b159a52013-10-03 17:24:55160
161 frame_tree.RemoveFrame(16, 265);
[email protected]7cc7ebd2013-10-08 00:59:00162 ASSERT_EQ("5: [14: [244: [], 245: []], "
163 "15: [255 'no children node': []], "
164 "16: [264: [], 266: [], "
165 "267 'node with deep subtree': "
166 "[365: [455: [555: []]]], 268: []]]",
167 GetTreeState(&frame_tree));
[email protected]9b159a52013-10-03 17:24:55168
169 frame_tree.RemoveFrame(5, 15);
[email protected]7cc7ebd2013-10-08 00:59:00170 ASSERT_EQ("5: [14: [244: [], 245: []], "
171 "16: [264: [], 266: [], "
172 "267 'node with deep subtree': "
173 "[365: [455: [555: []]]], 268: []]]",
174 GetTreeState(&frame_tree));
[email protected]9b159a52013-10-03 17:24:55175}
176
177} // namespace
178} // namespace content