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.
5 #include "content/browser/frame_host/frame_tree.h"
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/browser/web_contents/web_contents_impl.h"
14 #include "content/common/frame_messages.h"
15 #include "content/public/browser/web_contents_observer.h"
16 #include "content/public/test/mock_render_process_host.h"
17 #include "content/public/test/test_browser_context.h"
18 #include "content/public/test/test_browser_thread_bundle.h"
19 #include "content/test/test_render_frame_host.h"
20 #include "content/test/test_render_view_host.h"
21 #include "content/test/test_web_contents.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
29 // Appends a description of the structure of the frame tree to |result|.
30 void AppendTreeNodeState(FrameTreeNode
* node
, std::string
* result
) {
32 base::Int64ToString(node
->current_frame_host()->GetRoutingID()));
33 if (!node
->current_frame_host()->IsRenderFrameLive())
34 result
->append("*"); // Asterisk next to dead frames.
36 if (!node
->frame_name().empty()) {
38 result
->append(node
->frame_name());
41 result
->append(": [");
42 const char* separator
= "";
43 for (size_t i
= 0; i
< node
->child_count(); i
++) {
44 result
->append(separator
);
45 AppendTreeNodeState(node
->child_at(i
), result
);
51 // Logs calls to WebContentsObserver along with the state of the frame tree,
52 // for later use in EXPECT_EQ().
53 class TreeWalkingWebContentsLogger
: public WebContentsObserver
{
55 explicit TreeWalkingWebContentsLogger(WebContents
* web_contents
)
56 : WebContentsObserver(web_contents
) {}
58 ~TreeWalkingWebContentsLogger() override
{
59 EXPECT_EQ("", log_
) << "Activity logged that was not expected";
62 // Gets and resets the log, which is a string of what happened.
63 std::string
GetLog() {
64 std::string result
= log_
;
69 // content::WebContentsObserver implementation.
70 void RenderFrameCreated(RenderFrameHost
* render_frame_host
) override
{
71 LogWhatHappened("RenderFrameCreated", render_frame_host
);
74 void RenderFrameHostChanged(RenderFrameHost
* old_host
,
75 RenderFrameHost
* new_host
) override
{
77 LogWhatHappened("RenderFrameHostChanged(old)", old_host
);
78 LogWhatHappened("RenderFrameHostChanged(new)", new_host
);
81 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
{
82 LogWhatHappened("RenderFrameDeleted", render_frame_host
);
85 void RenderProcessGone(base::TerminationStatus status
) override
{
86 LogWhatHappened("RenderProcessGone");
90 void LogWhatHappened(const std::string
& event_name
) {
94 log_
.append(event_name
+ " -> ");
96 static_cast<WebContentsImpl
*>(web_contents())->GetFrameTree()->root(),
100 void LogWhatHappened(const std::string
& event_name
, RenderFrameHost
* rfh
) {
102 base::StringPrintf("%s(%d)", event_name
.c_str(), rfh
->GetRoutingID()));
107 DISALLOW_COPY_AND_ASSIGN(TreeWalkingWebContentsLogger
);
112 class FrameTreeTest
: public RenderViewHostImplTestHarness
{
114 // Prints a FrameTree, for easy assertions of the tree hierarchy.
115 std::string
GetTreeState(FrameTree
* frame_tree
) {
117 AppendTreeNodeState(frame_tree
->root(), &result
);
122 // Exercise tree manipulation routines.
123 // - Add a series of nodes and verify tree structure.
124 // - Remove a series of nodes and verify tree structure.
125 TEST_F(FrameTreeTest
, Shape
) {
126 // Use the FrameTree of the WebContents so that it has all the delegates it
127 // needs. We may want to consider a test version of this.
128 FrameTree
* frame_tree
= contents()->GetFrameTree();
129 FrameTreeNode
* root
= frame_tree
->root();
131 std::string
no_children_node("no children node");
132 std::string
deep_subtree("node with deep subtree");
133 int process_id
= root
->current_frame_host()->GetProcess()->GetID();
135 // Do not navigate each frame separately, since that will clutter the test
136 // itself. Instead, leave them in "not live" state, which is indicated by the
137 // * after the frame id, since this test cares about the shape, not the
139 EXPECT_EQ("1*: []", GetTreeState(frame_tree
));
141 // Simulate attaching a series of frames to build the frame tree.
142 frame_tree
->AddFrame(root
, process_id
, 14, blink::WebTreeScopeType::Document
,
143 std::string(), blink::WebSandboxFlags::None
);
144 frame_tree
->AddFrame(root
, process_id
, 15, blink::WebTreeScopeType::Document
,
145 std::string(), blink::WebSandboxFlags::None
);
146 frame_tree
->AddFrame(root
, process_id
, 16, blink::WebTreeScopeType::Document
,
147 std::string(), blink::WebSandboxFlags::None
);
149 frame_tree
->AddFrame(root
->child_at(0), process_id
, 244,
150 blink::WebTreeScopeType::Document
, std::string(),
151 blink::WebSandboxFlags::None
);
152 frame_tree
->AddFrame(root
->child_at(1), process_id
, 255,
153 blink::WebTreeScopeType::Document
, no_children_node
,
154 blink::WebSandboxFlags::None
);
155 frame_tree
->AddFrame(root
->child_at(0), process_id
, 245,
156 blink::WebTreeScopeType::Document
, std::string(),
157 blink::WebSandboxFlags::None
);
159 EXPECT_EQ("1*: [14*: [244*: [], 245*: []], "
160 "15*: [255* 'no children node': []], "
162 GetTreeState(frame_tree
));
164 FrameTreeNode
* child_16
= root
->child_at(2);
165 frame_tree
->AddFrame(child_16
, process_id
, 264,
166 blink::WebTreeScopeType::Document
, std::string(),
167 blink::WebSandboxFlags::None
);
168 frame_tree
->AddFrame(child_16
, process_id
, 265,
169 blink::WebTreeScopeType::Document
, std::string(),
170 blink::WebSandboxFlags::None
);
171 frame_tree
->AddFrame(child_16
, process_id
, 266,
172 blink::WebTreeScopeType::Document
, std::string(),
173 blink::WebSandboxFlags::None
);
174 frame_tree
->AddFrame(child_16
, process_id
, 267,
175 blink::WebTreeScopeType::Document
, deep_subtree
,
176 blink::WebSandboxFlags::None
);
177 frame_tree
->AddFrame(child_16
, process_id
, 268,
178 blink::WebTreeScopeType::Document
, std::string(),
179 blink::WebSandboxFlags::None
);
181 FrameTreeNode
* child_267
= child_16
->child_at(3);
182 frame_tree
->AddFrame(child_267
, process_id
, 365,
183 blink::WebTreeScopeType::Document
, std::string(),
184 blink::WebSandboxFlags::None
);
185 frame_tree
->AddFrame(child_267
->child_at(0), process_id
, 455,
186 blink::WebTreeScopeType::Document
, std::string(),
187 blink::WebSandboxFlags::None
);
188 frame_tree
->AddFrame(child_267
->child_at(0)->child_at(0), process_id
, 555,
189 blink::WebTreeScopeType::Document
, std::string(),
190 blink::WebSandboxFlags::None
);
191 frame_tree
->AddFrame(child_267
->child_at(0)->child_at(0)->child_at(0),
192 process_id
, 655, blink::WebTreeScopeType::Document
,
193 std::string(), blink::WebSandboxFlags::None
);
195 // Now that's it's fully built, verify the tree structure is as expected.
196 EXPECT_EQ("1*: [14*: [244*: [], 245*: []], "
197 "15*: [255* 'no children node': []], "
198 "16*: [264*: [], 265*: [], 266*: [], "
199 "267* 'node with deep subtree': "
200 "[365*: [455*: [555*: [655*: []]]]], 268*: []]]",
201 GetTreeState(frame_tree
));
203 FrameTreeNode
* child_555
= child_267
->child_at(0)->child_at(0)->child_at(0);
204 frame_tree
->RemoveFrame(child_555
);
205 EXPECT_EQ("1*: [14*: [244*: [], 245*: []], "
206 "15*: [255* 'no children node': []], "
207 "16*: [264*: [], 265*: [], 266*: [], "
208 "267* 'node with deep subtree': "
209 "[365*: [455*: []]], 268*: []]]",
210 GetTreeState(frame_tree
));
212 frame_tree
->RemoveFrame(child_16
->child_at(1));
213 EXPECT_EQ("1*: [14*: [244*: [], 245*: []], "
214 "15*: [255* 'no children node': []], "
215 "16*: [264*: [], 266*: [], "
216 "267* 'node with deep subtree': "
217 "[365*: [455*: []]], 268*: []]]",
218 GetTreeState(frame_tree
));
220 frame_tree
->RemoveFrame(root
->child_at(1));
221 EXPECT_EQ("1*: [14*: [244*: [], 245*: []], "
222 "16*: [264*: [], 266*: [], "
223 "267* 'node with deep subtree': "
224 "[365*: [455*: []]], 268*: []]]",
225 GetTreeState(frame_tree
));
228 // Ensure frames can be found by frame_tree_node_id, routing ID, or name.
229 TEST_F(FrameTreeTest
, FindFrames
) {
230 main_test_rfh()->InitializeRenderFrameIfNeeded();
232 // Add a few child frames to the main frame.
233 FrameTree
* frame_tree
= contents()->GetFrameTree();
234 FrameTreeNode
* root
= frame_tree
->root();
236 main_test_rfh()->OnCreateChildFrame(22, blink::WebTreeScopeType::Document
,
237 "child0", blink::WebSandboxFlags::None
);
238 main_test_rfh()->OnCreateChildFrame(23, blink::WebTreeScopeType::Document
,
239 "child1", blink::WebSandboxFlags::None
);
240 main_test_rfh()->OnCreateChildFrame(24, blink::WebTreeScopeType::Document
,
242 blink::WebSandboxFlags::None
);
243 FrameTreeNode
* child0
= root
->child_at(0);
244 FrameTreeNode
* child1
= root
->child_at(1);
246 FrameTreeNode
* child2
= root
->child_at(2);
248 // Add one grandchild frame.
249 child1
->current_frame_host()->OnCreateChildFrame(
250 33, blink::WebTreeScopeType::Document
, "grandchild",
251 blink::WebSandboxFlags::None
);
252 FrameTreeNode
* grandchild
= child1
->child_at(0);
254 // Ensure they can be found by FTN id.
255 EXPECT_EQ(root
, frame_tree
->FindByID(root
->frame_tree_node_id()));
256 EXPECT_EQ(child0
, frame_tree
->FindByID(child0
->frame_tree_node_id()));
257 EXPECT_EQ(child1
, frame_tree
->FindByID(child1
->frame_tree_node_id()));
258 EXPECT_EQ(child2
, frame_tree
->FindByID(child2
->frame_tree_node_id()));
259 EXPECT_EQ(grandchild
, frame_tree
->FindByID(grandchild
->frame_tree_node_id()));
260 EXPECT_EQ(nullptr, frame_tree
->FindByID(-1));
262 // Ensure they can be found by routing id.
263 int process_id
= main_test_rfh()->GetProcess()->GetID();
264 EXPECT_EQ(root
, frame_tree
->FindByRoutingID(process_id
,
265 main_test_rfh()->GetRoutingID()));
266 EXPECT_EQ(child0
, frame_tree
->FindByRoutingID(process_id
, 22));
267 EXPECT_EQ(child1
, frame_tree
->FindByRoutingID(process_id
, 23));
268 EXPECT_EQ(child2
, frame_tree
->FindByRoutingID(process_id
, 24));
269 EXPECT_EQ(grandchild
, frame_tree
->FindByRoutingID(process_id
, 33));
270 EXPECT_EQ(nullptr, frame_tree
->FindByRoutingID(process_id
, 37));
272 // Ensure they can be found by name, if they have one.
273 EXPECT_EQ(root
, frame_tree
->FindByName(std::string()));
274 EXPECT_EQ(child0
, frame_tree
->FindByName("child0"));
275 EXPECT_EQ(child1
, frame_tree
->FindByName("child1"));
276 EXPECT_EQ(grandchild
, frame_tree
->FindByName("grandchild"));
277 EXPECT_EQ(nullptr, frame_tree
->FindByName("no such frame"));
280 // Check that PreviousSibling() is retrieved correctly.
281 TEST_F(FrameTreeTest
, PreviousSibling
) {
282 main_test_rfh()->InitializeRenderFrameIfNeeded();
284 // Add a few child frames to the main frame.
285 FrameTree
* frame_tree
= contents()->GetFrameTree();
286 FrameTreeNode
* root
= frame_tree
->root();
287 main_test_rfh()->OnCreateChildFrame(22, blink::WebTreeScopeType::Document
,
288 "child0", blink::WebSandboxFlags::None
);
289 main_test_rfh()->OnCreateChildFrame(23, blink::WebTreeScopeType::Document
,
290 "child1", blink::WebSandboxFlags::None
);
291 main_test_rfh()->OnCreateChildFrame(24, blink::WebTreeScopeType::Document
,
292 "child2", blink::WebSandboxFlags::None
);
293 FrameTreeNode
* child0
= root
->child_at(0);
294 FrameTreeNode
* child1
= root
->child_at(1);
295 FrameTreeNode
* child2
= root
->child_at(2);
297 // Add one grandchild frame.
298 child1
->current_frame_host()->OnCreateChildFrame(
299 33, blink::WebTreeScopeType::Document
, "grandchild",
300 blink::WebSandboxFlags::None
);
301 FrameTreeNode
* grandchild
= child1
->child_at(0);
303 EXPECT_EQ(nullptr, root
->PreviousSibling());
304 EXPECT_EQ(nullptr, child0
->PreviousSibling());
305 EXPECT_EQ(child0
, child1
->PreviousSibling());
306 EXPECT_EQ(child1
, child2
->PreviousSibling());
307 EXPECT_EQ(nullptr, grandchild
->PreviousSibling());
310 // Do some simple manipulations of the frame tree, making sure that
311 // WebContentsObservers see a consistent view of the tree as we go.
312 TEST_F(FrameTreeTest
, ObserverWalksTreeDuringFrameCreation
) {
313 TreeWalkingWebContentsLogger
activity(contents());
314 contents()->NavigateAndCommit(GURL("http://www.google.com"));
315 EXPECT_EQ("RenderFrameCreated(1) -> 1: []", activity
.GetLog());
317 FrameTree
* frame_tree
= contents()->GetFrameTree();
318 FrameTreeNode
* root
= frame_tree
->root();
320 // Simulate attaching a series of frames to build the frame tree.
321 main_test_rfh()->OnCreateChildFrame(14, blink::WebTreeScopeType::Document
,
323 blink::WebSandboxFlags::None
);
325 "RenderFrameHostChanged(new)(14) -> 1: []\n"
326 "RenderFrameCreated(14) -> 1: [14: []]",
328 main_test_rfh()->OnCreateChildFrame(18, blink::WebTreeScopeType::Document
,
330 blink::WebSandboxFlags::None
);
332 "RenderFrameHostChanged(new)(18) -> 1: [14: []]\n"
333 "RenderFrameCreated(18) -> 1: [14: [], 18: []]",
335 frame_tree
->RemoveFrame(root
->child_at(0));
336 EXPECT_EQ("RenderFrameDeleted(14) -> 1: [18: []]", activity
.GetLog());
337 frame_tree
->RemoveFrame(root
->child_at(0));
338 EXPECT_EQ("RenderFrameDeleted(18) -> 1: []", activity
.GetLog());
341 // Make sure that WebContentsObservers see a consistent view of the tree after
342 // recovery from a render process crash.
343 TEST_F(FrameTreeTest
, ObserverWalksTreeAfterCrash
) {
344 TreeWalkingWebContentsLogger
activity(contents());
345 contents()->NavigateAndCommit(GURL("http://www.google.com"));
346 EXPECT_EQ("RenderFrameCreated(1) -> 1: []", activity
.GetLog());
348 main_test_rfh()->OnCreateChildFrame(22, blink::WebTreeScopeType::Document
,
350 blink::WebSandboxFlags::None
);
352 "RenderFrameHostChanged(new)(22) -> 1: []\n"
353 "RenderFrameCreated(22) -> 1: [22: []]",
355 main_test_rfh()->OnCreateChildFrame(23, blink::WebTreeScopeType::Document
,
357 blink::WebSandboxFlags::None
);
359 "RenderFrameHostChanged(new)(23) -> 1: [22: []]\n"
360 "RenderFrameCreated(23) -> 1: [22: [], 23: []]",
363 // Crash the renderer
364 main_test_rfh()->GetProcess()->SimulateCrash();
366 "RenderProcessGone -> 1*: [22*: [], 23*: []]\n"
367 "RenderFrameDeleted(23) -> 1*: [22*: [], 23*: []]\n"
368 "RenderFrameDeleted(22) -> 1*: [22*: [], 23*: []]\n"
369 "RenderFrameDeleted(1) -> 1*: []",
373 // Ensure that frames are not added to the tree, if the process passed in
374 // is different than the process of the parent node.
375 TEST_F(FrameTreeTest
, FailAddFrameWithWrongProcessId
) {
376 contents()->NavigateAndCommit(GURL("http://www.google.com"));
377 FrameTree
* frame_tree
= contents()->GetFrameTree();
378 FrameTreeNode
* root
= frame_tree
->root();
379 int process_id
= root
->current_frame_host()->GetProcess()->GetID();
381 ASSERT_EQ("1: []", GetTreeState(frame_tree
));
383 // Simulate attaching a frame from mismatched process id.
384 ASSERT_FALSE(frame_tree
->AddFrame(
385 root
, process_id
+ 1, 1, blink::WebTreeScopeType::Document
, std::string(),
386 blink::WebSandboxFlags::None
));
387 ASSERT_EQ("1: []", GetTreeState(frame_tree
));
390 // Ensure that frames removed while a process has crashed are not preserved in
391 // the global map of id->frame.
392 TEST_F(FrameTreeTest
, ProcessCrashClearsGlobalMap
) {
393 main_test_rfh()->InitializeRenderFrameIfNeeded();
395 // Add a couple child frames to the main frame.
396 FrameTreeNode
* root
= contents()->GetFrameTree()->root();
398 main_test_rfh()->OnCreateChildFrame(22, blink::WebTreeScopeType::Document
,
400 blink::WebSandboxFlags::None
);
401 main_test_rfh()->OnCreateChildFrame(23, blink::WebTreeScopeType::Document
,
403 blink::WebSandboxFlags::None
);
405 // Add one grandchild frame.
406 RenderFrameHostImpl
* child1_rfh
= root
->child_at(0)->current_frame_host();
407 child1_rfh
->OnCreateChildFrame(33, blink::WebTreeScopeType::Document
,
408 std::string(), blink::WebSandboxFlags::None
);
410 // Ensure they can be found by id.
411 int id1
= root
->child_at(0)->frame_tree_node_id();
412 int id2
= root
->child_at(1)->frame_tree_node_id();
413 int id3
= root
->child_at(0)->child_at(0)->frame_tree_node_id();
414 EXPECT_TRUE(FrameTreeNode::GloballyFindByID(id1
));
415 EXPECT_TRUE(FrameTreeNode::GloballyFindByID(id2
));
416 EXPECT_TRUE(FrameTreeNode::GloballyFindByID(id3
));
418 // Crash the renderer.
419 main_test_rfh()->GetProcess()->SimulateCrash();
421 // Ensure they cannot be found by id after the process has crashed.
422 EXPECT_FALSE(FrameTreeNode::GloballyFindByID(id1
));
423 EXPECT_FALSE(FrameTreeNode::GloballyFindByID(id2
));
424 EXPECT_FALSE(FrameTreeNode::GloballyFindByID(id3
));
427 } // namespace content