Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / content / browser / frame_host / frame_tree_unittest.cc
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
24 namespace content {
25
26 namespace {
27
28 // Appends a description of the structure of the frame tree to |result|.
29 void 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().
49 class 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
108 class 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.
121 TEST_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.
197 TEST_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.
217 TEST_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.
237 TEST_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