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