Upstream version 9.38.198.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 namespace {
26
27 // Appends a description of the structure of the frame tree to |result|.
28 void 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().
48 class 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
105 class 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.
118 TEST_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.
192 TEST_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.
212 TEST_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