frame_tree_unittest.cc revision 1675a649fd7a8b3cb80ffddae2dc181f122353c5
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/browser/web_contents/web_contents_impl.h" 14#include "content/common/view_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 24namespace content { 25 26namespace { 27 28// Appends a description of the structure of the frame tree to |result|. 29void AppendTreeNodeState(FrameTreeNode* node, std::string* result) { 30 result->append( 31 base::Int64ToString(node->current_frame_host()->GetRoutingID())); 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// Logs calls to WebContentsObserver along with the state of the frame tree, 48// for later use in EXPECT_EQ(). 49class TreeWalkingWebContentsLogger : public WebContentsObserver { 50 public: 51 explicit TreeWalkingWebContentsLogger(WebContents* web_contents) 52 : WebContentsObserver(web_contents) {} 53 54 virtual ~TreeWalkingWebContentsLogger() { 55 EXPECT_EQ("", log_) << "Activity logged that was not expected"; 56 } 57 58 // Gets and resets the log, which is a string of what happened. 59 std::string GetLog() { 60 std::string result = log_; 61 log_.clear(); 62 return result; 63 } 64 65 // content::WebContentsObserver implementation. 66 virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE { 67 LogWhatHappened("RenderFrameCreated", render_frame_host); 68 } 69 70 virtual void RenderFrameHostChanged(RenderFrameHost* old_host, 71 RenderFrameHost* new_host) OVERRIDE { 72 if (old_host) 73 LogWhatHappened("RenderFrameChanged(old)", old_host); 74 LogWhatHappened("RenderFrameChanged(new)", new_host); 75 } 76 77 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE { 78 LogWhatHappened("RenderFrameDeleted", render_frame_host); 79 } 80 81 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE { 82 LogWhatHappened("RenderProcessGone"); 83 } 84 85 private: 86 void LogWhatHappened(const std::string& event_name) { 87 if (!log_.empty()) { 88 log_.append("\n"); 89 } 90 log_.append(event_name + " -> "); 91 AppendTreeNodeState( 92 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(), 93 &log_); 94 } 95 96 void LogWhatHappened(const std::string& event_name, RenderFrameHost* rfh) { 97 LogWhatHappened( 98 base::StringPrintf("%s(%d)", event_name.c_str(), rfh->GetRoutingID())); 99 } 100 101 std::string log_; 102 103 DISALLOW_COPY_AND_ASSIGN(TreeWalkingWebContentsLogger); 104}; 105 106} // namespace 107 108class FrameTreeTest : public RenderViewHostImplTestHarness { 109 protected: 110 // Prints a FrameTree, for easy assertions of the tree hierarchy. 111 std::string GetTreeState(FrameTree* frame_tree) { 112 std::string result; 113 AppendTreeNodeState(frame_tree->root(), &result); 114 return result; 115 } 116}; 117 118// Exercise tree manipulation routines. 119// - Add a series of nodes and verify tree structure. 120// - Remove a series of nodes and verify tree structure. 121TEST_F(FrameTreeTest, Shape) { 122 // Use the FrameTree of the WebContents so that it has all the delegates it 123 // needs. We may want to consider a test version of this. 124 FrameTree* frame_tree = contents()->GetFrameTree(); 125 FrameTreeNode* root = frame_tree->root(); 126 127 std::string no_children_node("no children node"); 128 std::string deep_subtree("node with deep subtree"); 129 int process_id = root->current_frame_host()->GetProcess()->GetID(); 130 131 ASSERT_EQ("1: []", GetTreeState(frame_tree)); 132 133 // Simulate attaching a series of frames to build the frame tree. 134 frame_tree->AddFrame(root, process_id, 14, std::string()); 135 frame_tree->AddFrame(root, process_id, 15, std::string()); 136 frame_tree->AddFrame(root, process_id, 16, std::string()); 137 138 frame_tree->AddFrame(root->child_at(0), process_id, 244, std::string()); 139 frame_tree->AddFrame(root->child_at(1), process_id, 255, no_children_node); 140 frame_tree->AddFrame(root->child_at(0), process_id, 245, std::string()); 141 142 ASSERT_EQ("1: [14: [244: [], 245: []], " 143 "15: [255 'no children node': []], " 144 "16: []]", 145 GetTreeState(frame_tree)); 146 147 FrameTreeNode* child_16 = root->child_at(2); 148 frame_tree->AddFrame(child_16, process_id, 264, std::string()); 149 frame_tree->AddFrame(child_16, process_id, 265, std::string()); 150 frame_tree->AddFrame(child_16, process_id, 266, std::string()); 151 frame_tree->AddFrame(child_16, process_id, 267, deep_subtree); 152 frame_tree->AddFrame(child_16, process_id, 268, std::string()); 153 154 FrameTreeNode* child_267 = child_16->child_at(3); 155 frame_tree->AddFrame(child_267, process_id, 365, std::string()); 156 frame_tree->AddFrame(child_267->child_at(0), process_id, 455, std::string()); 157 frame_tree->AddFrame(child_267->child_at(0)->child_at(0), process_id, 555, 158 std::string()); 159 frame_tree->AddFrame(child_267->child_at(0)->child_at(0)->child_at(0), 160 process_id, 655, std::string()); 161 162 // Now that's it's fully built, verify the tree structure is as expected. 163 ASSERT_EQ("1: [14: [244: [], 245: []], " 164 "15: [255 'no children node': []], " 165 "16: [264: [], 265: [], 266: [], " 166 "267 'node with deep subtree': " 167 "[365: [455: [555: [655: []]]]], 268: []]]", 168 GetTreeState(frame_tree)); 169 170 FrameTreeNode* child_555 = child_267->child_at(0)->child_at(0)->child_at(0); 171 frame_tree->RemoveFrame(child_555); 172 ASSERT_EQ("1: [14: [244: [], 245: []], " 173 "15: [255 'no children node': []], " 174 "16: [264: [], 265: [], 266: [], " 175 "267 'node with deep subtree': " 176 "[365: [455: []]], 268: []]]", 177 GetTreeState(frame_tree)); 178 179 frame_tree->RemoveFrame(child_16->child_at(1)); 180 ASSERT_EQ("1: [14: [244: [], 245: []], " 181 "15: [255 'no children node': []], " 182 "16: [264: [], 266: [], " 183 "267 'node with deep subtree': " 184 "[365: [455: []]], 268: []]]", 185 GetTreeState(frame_tree)); 186 187 frame_tree->RemoveFrame(root->child_at(1)); 188 ASSERT_EQ("1: [14: [244: [], 245: []], " 189 "16: [264: [], 266: [], " 190 "267 'node with deep subtree': " 191 "[365: [455: []]], 268: []]]", 192 GetTreeState(frame_tree)); 193} 194 195// Do some simple manipulations of the frame tree, making sure that 196// WebContentsObservers see a consistent view of the tree as we go. 197TEST_F(FrameTreeTest, ObserverWalksTreeDuringFrameCreation) { 198 TreeWalkingWebContentsLogger activity(contents()); 199 FrameTree* frame_tree = contents()->GetFrameTree(); 200 FrameTreeNode* root = frame_tree->root(); 201 202 EXPECT_EQ("", activity.GetLog()); 203 204 // Simulate attaching a series of frames to build the frame tree. 205 main_test_rfh()->OnCreateChildFrame(14, std::string()); 206 EXPECT_EQ("RenderFrameCreated(14) -> 1: [14: []]", activity.GetLog()); 207 main_test_rfh()->OnCreateChildFrame(18, std::string()); 208 EXPECT_EQ("RenderFrameCreated(18) -> 1: [14: [], 18: []]", activity.GetLog()); 209 frame_tree->RemoveFrame(root->child_at(0)); 210 EXPECT_EQ("RenderFrameDeleted(14) -> 1: [18: []]", activity.GetLog()); 211 frame_tree->RemoveFrame(root->child_at(0)); 212 EXPECT_EQ("RenderFrameDeleted(18) -> 1: []", activity.GetLog()); 213} 214 215// Make sure that WebContentsObservers see a consistent view of the tree after 216// recovery from a render process crash. 217TEST_F(FrameTreeTest, ObserverWalksTreeAfterCrash) { 218 TreeWalkingWebContentsLogger activity(contents()); 219 220 main_test_rfh()->OnCreateChildFrame(22, std::string()); 221 EXPECT_EQ("RenderFrameCreated(22) -> 1: [22: []]", activity.GetLog()); 222 main_test_rfh()->OnCreateChildFrame(23, std::string()); 223 EXPECT_EQ("RenderFrameCreated(23) -> 1: [22: [], 23: []]", activity.GetLog()); 224 225 // Crash the renderer 226 test_rvh()->OnMessageReceived(ViewHostMsg_RenderProcessGone( 227 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1)); 228 EXPECT_EQ( 229 "RenderFrameDeleted(22) -> 1: []\n" 230 "RenderFrameDeleted(23) -> 1: []\n" 231 "RenderProcessGone -> 1: []", 232 activity.GetLog()); 233} 234 235// Ensure that frames are not added to the tree, if the process passed in 236// is different than the process of the parent node. 237TEST_F(FrameTreeTest, FailAddFrameWithWrongProcessId) { 238 FrameTree* frame_tree = contents()->GetFrameTree(); 239 FrameTreeNode* root = frame_tree->root(); 240 int process_id = root->current_frame_host()->GetProcess()->GetID(); 241 242 ASSERT_EQ("1: []", GetTreeState(frame_tree)); 243 244 // Simulate attaching a frame from mismatched process id. 245 ASSERT_FALSE(frame_tree->AddFrame(root, process_id + 1, 1, std::string())); 246 ASSERT_EQ("1: []", GetTreeState(frame_tree)); 247} 248 249} // namespace content 250