Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / components / html_viewer / html_frame_apptest.cc
blob7d5fce137dc55cb76efd26e67fd38f56c82cb88a
1 // Copyright 2015 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 "base/bind.h"
6 #include "base/command_line.h"
7 #include "base/json/json_reader.h"
8 #include "base/run_loop.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/test/test_timeouts.h"
11 #include "base/values.h"
12 #include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h"
13 #include "components/view_manager/public/cpp/tests/view_manager_test_base.h"
14 #include "components/view_manager/public/cpp/view.h"
15 #include "components/view_manager/public/cpp/view_manager.h"
16 #include "mandoline/tab/frame.h"
17 #include "mandoline/tab/frame_connection.h"
18 #include "mandoline/tab/frame_tree.h"
19 #include "mandoline/tab/public/interfaces/frame_tree.mojom.h"
20 #include "mojo/application/public/cpp/application_impl.h"
21 #include "net/test/spawned_test_server/spawned_test_server.h"
22 #include "third_party/mojo_services/src/accessibility/public/interfaces/accessibility.mojom.h"
24 using mandoline::Frame;
25 using mandoline::FrameConnection;
26 using mandoline::FrameTree;
27 using mandoline::FrameTreeClient;
29 namespace mojo {
31 namespace {
33 // Switch to enable out of process iframes.
34 const char kOOPIF[] = "oopifs";
36 const char kAddFrameWithEmptyPageScript[] =
37 "var iframe = document.createElement(\"iframe\");"
38 "iframe.src = \"http://127.0.0.1:%u/files/empty_page.html\";"
39 "document.body.appendChild(iframe);";
41 bool EnableOOPIFs() {
42 return base::CommandLine::ForCurrentProcess()->HasSwitch(kOOPIF);
45 mojo::ApplicationConnection* ApplicationConnectionForFrame(Frame* frame) {
46 return static_cast<FrameConnection*>(frame->user_data())
47 ->application_connection();
50 std::string GetFrameText(ApplicationConnection* connection) {
51 html_viewer::TestHTMLViewerPtr test_html_viewer;
52 connection->ConnectToService(&test_html_viewer);
53 std::string result;
54 test_html_viewer->GetContentAsText([&result](const String& mojo_string) {
55 result = mojo_string;
56 ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop());
57 });
58 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
59 ADD_FAILURE() << "Timed out waiting for execute to complete";
60 // test_html_viewer.WaitForIncomingResponse();
61 return result;
64 scoped_ptr<base::Value> ExecuteScript(ApplicationConnection* connection,
65 const std::string& script) {
66 html_viewer::TestHTMLViewerPtr test_html_viewer;
67 connection->ConnectToService(&test_html_viewer);
68 scoped_ptr<base::Value> result;
69 test_html_viewer->ExecuteScript(script, [&result](const String& json_string) {
70 result = base::JSONReader::Read(json_string.To<std::string>());
71 ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop());
72 });
73 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
74 ADD_FAILURE() << "Timed out waiting for execute to complete";
75 return result.Pass();
78 } // namespace
80 class HTMLFrameTest : public ViewManagerTestBase {
81 public:
82 HTMLFrameTest() {}
83 ~HTMLFrameTest() override {}
85 protected:
86 // Creates the frame tree showing an empty page at the root and adds (via
87 // script) a frame showing the same empty page.
88 Frame* LoadEmptyPageAndCreateFrame() {
89 View* embed_view = window_manager()->CreateView();
90 FrameConnection* root_connection =
91 InitFrameTree(embed_view, "http://127.0.0.1:%u/files/empty_page2.html");
92 const std::string frame_text =
93 GetFrameText(root_connection->application_connection());
94 if (frame_text != "child") {
95 ADD_FAILURE() << "unexpected text " << frame_text;
96 return nullptr;
99 return CreateEmptyChildFrame(frame_tree_->root());
102 Frame* CreateEmptyChildFrame(Frame* parent) {
103 const size_t initial_frame_count = parent->children().size();
104 // Dynamically add a new frame.
105 ExecuteScript(ApplicationConnectionForFrame(parent),
106 AddPortToString(kAddFrameWithEmptyPageScript));
108 // Wait for the frame to appear.
109 if ((parent->children().size() != initial_frame_count + 1u ||
110 !parent->children().back()->user_data()) &&
111 !WaitForEmbedForDescendant()) {
112 ADD_FAILURE() << "timed out waiting for child";
113 return nullptr;
116 if (parent->view()->children().size() != initial_frame_count + 1u) {
117 ADD_FAILURE() << "unexpected number of children "
118 << parent->view()->children().size();
119 return nullptr;
122 return parent->FindFrame(parent->view()->children().back()->id());
125 std::string AddPortToString(const std::string& string) {
126 const uint16_t assigned_port = http_server_->host_port_pair().port();
127 return base::StringPrintf(string.c_str(), assigned_port);
130 mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) {
131 mojo::URLRequestPtr request(mojo::URLRequest::New());
132 request->url = mojo::String::From(AddPortToString(url_string));
133 return request.Pass();
136 FrameConnection* InitFrameTree(View* view, const std::string& url_string) {
137 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
138 FrameConnection* result = frame_connection.get();
139 ViewManagerClientPtr view_manager_client;
140 frame_connection->Init(application_impl(), BuildRequestForURL(url_string),
141 &view_manager_client);
142 FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
143 frame_tree_.reset(new FrameTree(view, nullptr, frame_tree_client,
144 frame_connection.Pass()));
145 view->Embed(view_manager_client.Pass());
146 return result;
149 bool WaitForEmbedForDescendant() {
150 if (embed_run_loop_)
151 return false;
152 embed_run_loop_.reset(new base::RunLoop);
153 embed_run_loop_->Run();
154 embed_run_loop_.reset();
155 return true;
158 // ViewManagerTest:
159 void SetUp() override {
160 ViewManagerTestBase::SetUp();
162 // Make it so we get OnEmbedForDescendant().
163 window_manager()->SetEmbedRoot();
165 // Start a test server.
166 http_server_.reset(new net::SpawnedTestServer(
167 net::SpawnedTestServer::TYPE_HTTP, net::SpawnedTestServer::kLocalhost,
168 base::FilePath(FILE_PATH_LITERAL("components/test/data/html_viewer"))));
169 ASSERT_TRUE(http_server_->Start());
171 void TearDown() override {
172 frame_tree_.reset();
173 http_server_.reset();
174 ViewManagerTestBase::TearDown();
176 void OnEmbedForDescendant(View* view,
177 URLRequestPtr request,
178 ViewManagerClientPtr* client) override {
179 Frame* frame = Frame::FindFirstFrameAncestor(view);
180 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
181 frame_connection->Init(application_impl(), request.Pass(), client);
182 FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
183 frame_tree_->CreateOrReplaceFrame(frame, view, frame_tree_client,
184 frame_connection.Pass());
185 if (embed_run_loop_)
186 embed_run_loop_->Quit();
189 scoped_ptr<net::SpawnedTestServer> http_server_;
190 scoped_ptr<FrameTree> frame_tree_;
192 private:
193 // A runloop specifically for OnEmbedForDescendant(). We use a separate
194 // runloop here as it's possible at the time OnEmbedForDescendant() is invoked
195 // a separate RunLoop is already running that we shouldn't quit.
196 scoped_ptr<base::RunLoop> embed_run_loop_;
198 DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest);
201 TEST_F(HTMLFrameTest, PageWithSingleFrame) {
202 if (!EnableOOPIFs())
203 return;
205 View* embed_view = window_manager()->CreateView();
207 FrameConnection* root_connection = InitFrameTree(
208 embed_view, "http://127.0.0.1:%u/files/page_with_single_frame.html");
210 ASSERT_EQ("Page with single frame",
211 GetFrameText(root_connection->application_connection()));
213 // page_with_single_frame contains a child frame. The child frame should
214 // create a new View and Frame.
215 if (frame_tree_->root()->children().empty() ||
216 !frame_tree_->root()->children().back()->user_data()) {
217 ASSERT_TRUE(WaitForEmbedForDescendant());
220 ASSERT_EQ(1u, embed_view->children().size());
221 Frame* child_frame =
222 frame_tree_->root()->FindFrame(embed_view->children()[0]->id());
223 ASSERT_TRUE(child_frame);
225 ASSERT_EQ("child",
226 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
227 ->application_connection()));
230 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndVerifyParent) {
231 if (!EnableOOPIFs())
232 return;
234 Frame* child_frame = LoadEmptyPageAndCreateFrame();
235 ASSERT_TRUE(child_frame);
237 mojo::ApplicationConnection* child_frame_connection =
238 ApplicationConnectionForFrame(child_frame);
240 ASSERT_EQ("child", GetFrameText(child_frame_connection));
241 // The child's parent should not be itself:
242 const char kGetWindowParentNameScript[] =
243 "window.parent == window ? 'parent is self' : 'parent not self';";
244 scoped_ptr<base::Value> parent_value(
245 ExecuteScript(child_frame_connection, kGetWindowParentNameScript));
246 ASSERT_TRUE(parent_value->IsType(base::Value::TYPE_LIST));
247 base::ListValue* parent_list;
248 ASSERT_TRUE(parent_value->GetAsList(&parent_list));
249 ASSERT_EQ(1u, parent_list->GetSize());
250 std::string parent_name;
251 ASSERT_TRUE(parent_list->GetString(0u, &parent_name));
252 EXPECT_EQ("parent not self", parent_name);
255 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndSeeNameChange) {
256 if (!EnableOOPIFs())
257 return;
259 Frame* child_frame = LoadEmptyPageAndCreateFrame();
260 ASSERT_TRUE(child_frame);
262 mojo::ApplicationConnection* child_frame_connection =
263 ApplicationConnectionForFrame(child_frame);
265 // Change the name of the child's window.
266 ExecuteScript(child_frame_connection, "window.name = 'new_child';");
268 // Eventually the parent should see the change. There is no convenient way
269 // to observe this change, so we repeatedly ask for it and timeout if we
270 // never get the right value.
271 const base::TimeTicks start_time(base::TimeTicks::Now());
272 std::string find_window_result;
273 do {
274 find_window_result.clear();
275 scoped_ptr<base::Value> script_value(
276 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
277 "window.frames['new_child'] != null ? 'found frame' : "
278 "'unable to find frame';"));
279 if (script_value->IsType(base::Value::TYPE_LIST)) {
280 base::ListValue* script_value_as_list;
281 if (script_value->GetAsList(&script_value_as_list) &&
282 script_value_as_list->GetSize() == 1) {
283 script_value_as_list->GetString(0u, &find_window_result);
286 } while (find_window_result != "found frame" &&
287 base::TimeTicks::Now() - start_time <
288 TestTimeouts::action_timeout());
289 EXPECT_EQ("found frame", find_window_result);
292 // Triggers dynamic addition and removal of a frame.
293 TEST_F(HTMLFrameTest, FrameTreeOfThreeLevels) {
294 if (!EnableOOPIFs())
295 return;
297 // Create a child frame, and in that child frame create another child frame.
298 Frame* child_frame = LoadEmptyPageAndCreateFrame();
299 ASSERT_TRUE(child_frame);
301 ASSERT_TRUE(CreateEmptyChildFrame(child_frame));
303 // Make sure the parent can see the child and child's child. There is no
304 // convenient way to observe this change, so we repeatedly ask for it and
305 // timeout if we never get the right value.
306 const char kGetChildChildFrameCount[] =
307 "if (window.frames.length > 0)"
308 " window.frames[0].frames.length.toString();"
309 "else"
310 " '0';";
311 const base::TimeTicks start_time(base::TimeTicks::Now());
312 std::string child_child_frame_count;
313 do {
314 child_child_frame_count.clear();
315 scoped_ptr<base::Value> script_value(
316 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
317 kGetChildChildFrameCount));
318 if (script_value->IsType(base::Value::TYPE_LIST)) {
319 base::ListValue* script_value_as_list;
320 if (script_value->GetAsList(&script_value_as_list) &&
321 script_value_as_list->GetSize() == 1) {
322 script_value_as_list->GetString(0u, &child_child_frame_count);
325 } while (child_child_frame_count != "1" &&
326 base::TimeTicks::Now() - start_time <
327 TestTimeouts::action_timeout());
328 EXPECT_EQ("1", child_child_frame_count);
330 // Remove the child's child and make sure the root doesn't see it anymore.
331 const char kRemoveLastIFrame[] =
332 "document.body.removeChild(document.body.lastChild);";
333 ExecuteScript(ApplicationConnectionForFrame(child_frame), kRemoveLastIFrame);
334 do {
335 child_child_frame_count.clear();
336 scoped_ptr<base::Value> script_value(
337 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
338 kGetChildChildFrameCount));
339 if (script_value->IsType(base::Value::TYPE_LIST)) {
340 base::ListValue* script_value_as_list;
341 if (script_value->GetAsList(&script_value_as_list) &&
342 script_value_as_list->GetSize() == 1) {
343 script_value_as_list->GetString(0u, &child_child_frame_count);
346 } while (child_child_frame_count != "0" &&
347 base::TimeTicks::Now() - start_time <
348 TestTimeouts::action_timeout());
349 ASSERT_EQ("0", child_child_frame_count);
352 } // namespace mojo