Revert "Reland c91b178b07b0d - Delete dead signin code (SigninGlobalError)"
[chromium-blink-merge.git] / components / html_viewer / html_frame_apptest.cc
blob7a205e929ba3f72004c4d959594041f65bc6c34e
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/auto_reset.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/run_loop.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/values.h"
13 #include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h"
14 #include "components/view_manager/public/cpp/tests/view_manager_test_base.h"
15 #include "components/view_manager/public/cpp/view.h"
16 #include "components/view_manager/public/cpp/view_manager.h"
17 #include "mandoline/tab/frame.h"
18 #include "mandoline/tab/frame_connection.h"
19 #include "mandoline/tab/frame_tree.h"
20 #include "mandoline/tab/public/interfaces/frame_tree.mojom.h"
21 #include "mandoline/tab/test_frame_tree_delegate.h"
22 #include "mojo/application/public/cpp/application_impl.h"
23 #include "net/test/spawned_test_server/spawned_test_server.h"
24 #include "third_party/mojo_services/src/accessibility/public/interfaces/accessibility.mojom.h"
26 using mandoline::Frame;
27 using mandoline::FrameConnection;
28 using mandoline::FrameTree;
29 using mandoline::FrameTreeClient;
31 namespace mojo {
33 namespace {
35 const char kAddFrameWithEmptyPageScript[] =
36 "var iframe = document.createElement(\"iframe\");"
37 "iframe.src = \"http://127.0.0.1:%u/files/empty_page.html\";"
38 "document.body.appendChild(iframe);";
40 mojo::ApplicationConnection* ApplicationConnectionForFrame(Frame* frame) {
41 return static_cast<FrameConnection*>(frame->user_data())
42 ->application_connection();
45 std::string GetFrameText(ApplicationConnection* connection) {
46 html_viewer::TestHTMLViewerPtr test_html_viewer;
47 connection->ConnectToService(&test_html_viewer);
48 std::string result;
49 test_html_viewer->GetContentAsText([&result](const String& mojo_string) {
50 result = mojo_string;
51 ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop());
52 });
53 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
54 ADD_FAILURE() << "Timed out waiting for execute to complete";
55 // test_html_viewer.WaitForIncomingResponse();
56 return result;
59 scoped_ptr<base::Value> ExecuteScript(ApplicationConnection* connection,
60 const std::string& script) {
61 html_viewer::TestHTMLViewerPtr test_html_viewer;
62 connection->ConnectToService(&test_html_viewer);
63 scoped_ptr<base::Value> result;
64 test_html_viewer->ExecuteScript(script, [&result](const String& json_string) {
65 result = base::JSONReader::Read(json_string.To<std::string>());
66 ASSERT_TRUE(ViewManagerTestBase::QuitRunLoop());
67 });
68 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
69 ADD_FAILURE() << "Timed out waiting for execute to complete";
70 return result.Pass();
73 // FrameTreeDelegate that can block waiting for navigation to start.
74 class TestFrameTreeDelegateImpl : public mandoline::TestFrameTreeDelegate {
75 public:
76 explicit TestFrameTreeDelegateImpl(mojo::ApplicationImpl* app)
77 : app_(app),
78 frame_tree_(nullptr),
79 waiting_for_navigate_(false),
80 got_navigate_(false) {}
81 ~TestFrameTreeDelegateImpl() override {}
83 void set_frame_tree(FrameTree* frame_tree) { frame_tree_ = frame_tree; }
85 void clear_got_navigate() { got_navigate_ = false; }
87 bool waiting_for_navigate() const { return waiting_for_navigate_; }
89 // Waits for a navigation to occur. This immediately returns true if a
90 // navigation has already occurred. In other words, take care when using this,
91 // you may need to clear_got_navigate() before calling this.
92 bool WaitForNavigateFrame() {
93 if (waiting_for_navigate_)
94 return false;
96 if (got_navigate_)
97 return true;
99 base::AutoReset<bool> resetter(&waiting_for_navigate_, true);
100 return ViewManagerTestBase::DoRunLoopWithTimeout() && got_navigate_;
103 // TestFrameTreeDelegate:
104 bool CanNavigateFrame(
105 Frame* target,
106 mojo::URLRequestPtr request,
107 mandoline::FrameTreeClient** frame_tree_client,
108 scoped_ptr<mandoline::FrameUserData>* frame_user_data,
109 mojo::ViewManagerClientPtr* view_manager_client) override {
110 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
111 frame_connection->Init(app_, request.Pass(), view_manager_client);
112 *frame_tree_client = frame_connection->frame_tree_client();
113 *frame_user_data = frame_connection.Pass();
115 return true;
118 void DidStartNavigation(Frame* frame) override {
119 got_navigate_ = true;
121 if (waiting_for_navigate_)
122 ignore_result(ViewManagerTestBase::QuitRunLoop());
125 private:
126 mojo::ApplicationImpl* app_;
127 FrameTree* frame_tree_;
128 bool waiting_for_navigate_;
129 bool got_navigate_;
131 DISALLOW_COPY_AND_ASSIGN(TestFrameTreeDelegateImpl);
134 } // namespace
136 class HTMLFrameTest : public ViewManagerTestBase {
137 public:
138 HTMLFrameTest() {}
139 ~HTMLFrameTest() override {}
141 protected:
142 // Creates the frame tree showing an empty page at the root and adds (via
143 // script) a frame showing the same empty page.
144 Frame* LoadEmptyPageAndCreateFrame() {
145 View* embed_view = window_manager()->CreateView();
146 frame_tree_delegate_.reset(
147 new TestFrameTreeDelegateImpl(application_impl()));
148 FrameConnection* root_connection =
149 InitFrameTree(embed_view, "http://127.0.0.1:%u/files/empty_page2.html");
150 const std::string frame_text =
151 GetFrameText(root_connection->application_connection());
152 if (frame_text != "child2") {
153 ADD_FAILURE() << "unexpected text " << frame_text;
154 return nullptr;
157 return CreateEmptyChildFrame(frame_tree_->root());
160 Frame* CreateEmptyChildFrame(Frame* parent) {
161 const size_t initial_frame_count = parent->children().size();
162 // Dynamically add a new frame.
163 ExecuteScript(ApplicationConnectionForFrame(parent),
164 AddPortToString(kAddFrameWithEmptyPageScript));
166 // Wait for the frame to appear.
167 if ((parent->children().size() != initial_frame_count + 1u ||
168 !parent->children().back()->user_data()) &&
169 !WaitForNavigateFrame()) {
170 ADD_FAILURE() << "timed out waiting for child";
171 return nullptr;
174 if (parent->view()->children().size() != initial_frame_count + 1u) {
175 ADD_FAILURE() << "unexpected number of children "
176 << parent->view()->children().size();
177 return nullptr;
180 return parent->FindFrame(parent->view()->children().back()->id());
183 std::string AddPortToString(const std::string& string) {
184 const uint16_t assigned_port = http_server_->host_port_pair().port();
185 return base::StringPrintf(string.c_str(), assigned_port);
188 mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) {
189 mojo::URLRequestPtr request(mojo::URLRequest::New());
190 request->url = mojo::String::From(AddPortToString(url_string));
191 return request.Pass();
194 FrameConnection* InitFrameTree(View* view, const std::string& url_string) {
195 frame_tree_delegate_.reset(
196 new TestFrameTreeDelegateImpl(application_impl()));
197 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
198 FrameConnection* result = frame_connection.get();
199 ViewManagerClientPtr view_manager_client;
200 frame_connection->Init(application_impl(), BuildRequestForURL(url_string),
201 &view_manager_client);
202 FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client();
203 frame_tree_.reset(new FrameTree(view, frame_tree_delegate_.get(),
204 frame_tree_client,
205 frame_connection.Pass()));
206 frame_tree_delegate_->set_frame_tree(frame_tree_.get());
207 view->Embed(view_manager_client.Pass());
208 return result;
211 bool WaitForNavigateFrame() {
212 if (frame_tree_delegate_->waiting_for_navigate())
213 return false;
215 frame_tree_delegate_->clear_got_navigate();
216 return frame_tree_delegate_->WaitForNavigateFrame();
219 // ViewManagerTest:
220 void SetUp() override {
221 ViewManagerTestBase::SetUp();
223 // Make it so we get OnEmbedForDescendant().
224 window_manager()->SetEmbedRoot();
226 // Start a test server.
227 http_server_.reset(new net::SpawnedTestServer(
228 net::SpawnedTestServer::TYPE_HTTP, net::SpawnedTestServer::kLocalhost,
229 base::FilePath(FILE_PATH_LITERAL("components/test/data/html_viewer"))));
230 ASSERT_TRUE(http_server_->Start());
232 void TearDown() override {
233 frame_tree_.reset();
234 http_server_.reset();
235 ViewManagerTestBase::TearDown();
238 scoped_ptr<net::SpawnedTestServer> http_server_;
239 scoped_ptr<FrameTree> frame_tree_;
241 scoped_ptr<TestFrameTreeDelegateImpl> frame_tree_delegate_;
243 private:
244 DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest);
247 TEST_F(HTMLFrameTest, PageWithSingleFrame) {
248 View* embed_view = window_manager()->CreateView();
250 FrameConnection* root_connection = InitFrameTree(
251 embed_view, "http://127.0.0.1:%u/files/page_with_single_frame.html");
253 ASSERT_EQ("Page with single frame",
254 GetFrameText(root_connection->application_connection()));
256 // page_with_single_frame contains a child frame. The child frame should
257 // create a new View and Frame.
258 if (frame_tree_->root()->children().empty() ||
259 !frame_tree_->root()->children().back()->user_data()) {
260 ASSERT_TRUE(WaitForNavigateFrame());
263 ASSERT_EQ(1u, embed_view->children().size());
264 Frame* child_frame =
265 frame_tree_->root()->FindFrame(embed_view->children()[0]->id());
266 ASSERT_TRUE(child_frame);
268 ASSERT_EQ("child",
269 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
270 ->application_connection()));
273 // Creates two frames. The parent navigates the child frame by way of changing
274 // the location of the child frame.
275 TEST_F(HTMLFrameTest, ChangeLocationOfChildFrame) {
276 View* embed_view = window_manager()->CreateView();
278 InitFrameTree(embed_view,
279 "http://127.0.0.1:%u/files/page_with_single_frame.html");
281 // page_with_single_frame contains a child frame. The child frame should
282 // create a new View and Frame.
283 if (frame_tree_->root()->children().empty() ||
284 !frame_tree_->root()->children().back()->user_data()) {
285 ASSERT_TRUE(WaitForNavigateFrame());
288 ASSERT_EQ(
289 "child",
290 GetFrameText(static_cast<FrameConnection*>(
291 frame_tree_->root()->children().back()->user_data())
292 ->application_connection()));
294 // Change the location and wait for the navigation to occur.
295 const char kNavigateFrame[] =
296 "window.frames[0].location = "
297 "'http://127.0.0.1:%u/files/empty_page2.html'";
298 frame_tree_delegate_->clear_got_navigate();
299 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
300 AddPortToString(kNavigateFrame));
301 ASSERT_TRUE(WaitForNavigateFrame());
303 // The navigation should have changed the text of the frame.
304 ASSERT_EQ(1u, frame_tree_->root()->children().size());
305 Frame* child_frame = frame_tree_->root()->children()[0];
306 ASSERT_TRUE(child_frame->user_data());
307 ASSERT_EQ("child2",
308 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
309 ->application_connection()));
312 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndVerifyParent) {
313 Frame* child_frame = LoadEmptyPageAndCreateFrame();
314 ASSERT_TRUE(child_frame);
316 mojo::ApplicationConnection* child_frame_connection =
317 ApplicationConnectionForFrame(child_frame);
319 ASSERT_EQ("child", GetFrameText(child_frame_connection));
320 // The child's parent should not be itself:
321 const char kGetWindowParentNameScript[] =
322 "window.parent == window ? 'parent is self' : 'parent not self';";
323 scoped_ptr<base::Value> parent_value(
324 ExecuteScript(child_frame_connection, kGetWindowParentNameScript));
325 ASSERT_TRUE(parent_value->IsType(base::Value::TYPE_LIST));
326 base::ListValue* parent_list;
327 ASSERT_TRUE(parent_value->GetAsList(&parent_list));
328 ASSERT_EQ(1u, parent_list->GetSize());
329 std::string parent_name;
330 ASSERT_TRUE(parent_list->GetString(0u, &parent_name));
331 EXPECT_EQ("parent not self", parent_name);
334 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndSeeNameChange) {
335 Frame* child_frame = LoadEmptyPageAndCreateFrame();
336 ASSERT_TRUE(child_frame);
338 mojo::ApplicationConnection* child_frame_connection =
339 ApplicationConnectionForFrame(child_frame);
341 // Change the name of the child's window.
342 ExecuteScript(child_frame_connection, "window.name = 'new_child';");
344 // Eventually the parent should see the change. There is no convenient way
345 // to observe this change, so we repeatedly ask for it and timeout if we
346 // never get the right value.
347 const base::TimeTicks start_time(base::TimeTicks::Now());
348 std::string find_window_result;
349 do {
350 find_window_result.clear();
351 scoped_ptr<base::Value> script_value(
352 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
353 "window.frames['new_child'] != null ? 'found frame' : "
354 "'unable to find frame';"));
355 if (script_value->IsType(base::Value::TYPE_LIST)) {
356 base::ListValue* script_value_as_list;
357 if (script_value->GetAsList(&script_value_as_list) &&
358 script_value_as_list->GetSize() == 1) {
359 script_value_as_list->GetString(0u, &find_window_result);
362 } while (find_window_result != "found frame" &&
363 base::TimeTicks::Now() - start_time <
364 TestTimeouts::action_timeout());
365 EXPECT_EQ("found frame", find_window_result);
368 // Triggers dynamic addition and removal of a frame.
369 TEST_F(HTMLFrameTest, FrameTreeOfThreeLevels) {
370 // Create a child frame, and in that child frame create another child frame.
371 Frame* child_frame = LoadEmptyPageAndCreateFrame();
372 ASSERT_TRUE(child_frame);
374 ASSERT_TRUE(CreateEmptyChildFrame(child_frame));
376 // Make sure the parent can see the child and child's child. There is no
377 // convenient way to observe this change, so we repeatedly ask for it and
378 // timeout if we never get the right value.
379 const char kGetChildChildFrameCount[] =
380 "if (window.frames.length > 0)"
381 " window.frames[0].frames.length.toString();"
382 "else"
383 " '0';";
384 const base::TimeTicks start_time(base::TimeTicks::Now());
385 std::string child_child_frame_count;
386 do {
387 child_child_frame_count.clear();
388 scoped_ptr<base::Value> script_value(
389 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
390 kGetChildChildFrameCount));
391 if (script_value->IsType(base::Value::TYPE_LIST)) {
392 base::ListValue* script_value_as_list;
393 if (script_value->GetAsList(&script_value_as_list) &&
394 script_value_as_list->GetSize() == 1) {
395 script_value_as_list->GetString(0u, &child_child_frame_count);
398 } while (child_child_frame_count != "1" &&
399 base::TimeTicks::Now() - start_time <
400 TestTimeouts::action_timeout());
401 EXPECT_EQ("1", child_child_frame_count);
403 // Remove the child's child and make sure the root doesn't see it anymore.
404 const char kRemoveLastIFrame[] =
405 "document.body.removeChild(document.body.lastChild);";
406 ExecuteScript(ApplicationConnectionForFrame(child_frame), kRemoveLastIFrame);
407 do {
408 child_child_frame_count.clear();
409 scoped_ptr<base::Value> script_value(
410 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
411 kGetChildChildFrameCount));
412 if (script_value->IsType(base::Value::TYPE_LIST)) {
413 base::ListValue* script_value_as_list;
414 if (script_value->GetAsList(&script_value_as_list) &&
415 script_value_as_list->GetSize() == 1) {
416 script_value_as_list->GetString(0u, &child_child_frame_count);
419 } while (child_child_frame_count != "0" &&
420 base::TimeTicks::Now() - start_time <
421 TestTimeouts::action_timeout());
422 ASSERT_EQ("0", child_child_frame_count);
425 // Verifies PostMessage() works across frames.
426 TEST_F(HTMLFrameTest, PostMessage) {
427 Frame* child_frame = LoadEmptyPageAndCreateFrame();
428 ASSERT_TRUE(child_frame);
430 mojo::ApplicationConnection* child_frame_connection =
431 ApplicationConnectionForFrame(child_frame);
432 ASSERT_EQ("child", GetFrameText(child_frame_connection));
434 // Register an event handler in the child frame.
435 const char kRegisterPostMessageHandler[] =
436 "window.messageData = null;"
437 "function messageFunction(event) {"
438 " window.messageData = event.data;"
440 "window.addEventListener('message', messageFunction, false);";
441 ExecuteScript(child_frame_connection, kRegisterPostMessageHandler);
443 frame_tree_delegate_->clear_got_navigate();
445 // Post a message from the parent to the child.
446 const char kPostMessageFromParent[] =
447 "window.frames[0].postMessage('hello from parent', '*');";
448 ExecuteScript(ApplicationConnectionForFrame(frame_tree_->root()),
449 kPostMessageFromParent);
451 // Wait for the child frame to see the message.
452 const base::TimeTicks start_time(base::TimeTicks::Now());
453 std::string message_in_child;
454 do {
455 const char kGetMessageData[] = "window.messageData;";
456 scoped_ptr<base::Value> script_value(
457 ExecuteScript(child_frame_connection, kGetMessageData));
458 if (script_value->IsType(base::Value::TYPE_LIST)) {
459 base::ListValue* script_value_as_list;
460 if (script_value->GetAsList(&script_value_as_list) &&
461 script_value_as_list->GetSize() == 1) {
462 script_value_as_list->GetString(0u, &message_in_child);
465 } while (message_in_child != "hello from parent" &&
466 base::TimeTicks::Now() - start_time <
467 TestTimeouts::action_timeout());
468 EXPECT_EQ("hello from parent", message_in_child);
471 } // namespace mojo