Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(719)

Side by Side Diff: components/html_viewer/html_frame_apptest.cc

Issue 1677293002: Bye bye Mandoline (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: moar Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/html_viewer/html_frame.cc ('k') | components/html_viewer/html_frame_delegate.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include <stddef.h>
6 #include <stdint.h>
7 #include <utility>
8
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/json/json_reader.h"
14 #include "base/macros.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/test/test_timeouts.h"
18 #include "base/time/time.h"
19 #include "base/values.h"
20 #include "build/build_config.h"
21 #include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h"
22 #include "components/mus/public/cpp/tests/window_server_test_base.h"
23 #include "components/mus/public/cpp/window.h"
24 #include "components/mus/public/cpp/window_tree_connection.h"
25 #include "components/web_view/frame.h"
26 #include "components/web_view/frame_connection.h"
27 #include "components/web_view/frame_tree.h"
28 #include "components/web_view/public/interfaces/frame.mojom.h"
29 #include "components/web_view/test_frame_tree_delegate.h"
30 #include "mojo/services/accessibility/public/interfaces/accessibility.mojom.h"
31 #include "net/test/embedded_test_server/embedded_test_server.h"
32
33 using mus::mojom::WindowTreeClientPtr;
34 using mus::WindowServerTestBase;
35 using web_view::Frame;
36 using web_view::FrameConnection;
37 using web_view::FrameTree;
38 using web_view::FrameTreeDelegate;
39 using web_view::mojom::FrameClient;
40
41 namespace mojo {
42
43 namespace {
44
45 const char kAddFrameWithEmptyPageScript[] =
46 "var iframe = document.createElement(\"iframe\");"
47 "iframe.src = \"http://127.0.0.1:%u/empty_page.html\";"
48 "document.body.appendChild(iframe);";
49
50 void OnGotContentHandlerForRoot(bool* got_callback) {
51 *got_callback = true;
52 ignore_result(WindowServerTestBase::QuitRunLoop());
53 }
54
55 mojo::Connection* ConnectionForFrame(Frame* frame) {
56 return static_cast<FrameConnection*>(frame->user_data())->connection();
57 }
58
59 std::string GetFrameText(Connection* connection) {
60 html_viewer::TestHTMLViewerPtr test_html_viewer;
61 connection->ConnectToService(&test_html_viewer);
62 std::string result;
63 test_html_viewer->GetContentAsText([&result](const String& mojo_string) {
64 result = mojo_string;
65 ASSERT_TRUE(WindowServerTestBase::QuitRunLoop());
66 });
67 if (!WindowServerTestBase::DoRunLoopWithTimeout())
68 ADD_FAILURE() << "Timed out waiting for execute to complete";
69 // test_html_viewer.WaitForIncomingResponse();
70 return result;
71 }
72
73 scoped_ptr<base::Value> ExecuteScript(Connection* connection,
74 const std::string& script) {
75 html_viewer::TestHTMLViewerPtr test_html_viewer;
76 connection->ConnectToService(&test_html_viewer);
77 scoped_ptr<base::Value> result;
78 test_html_viewer->ExecuteScript(script, [&result](const String& json_string) {
79 result = base::JSONReader::Read(json_string.To<std::string>());
80 ASSERT_TRUE(WindowServerTestBase::QuitRunLoop());
81 });
82 if (!WindowServerTestBase::DoRunLoopWithTimeout())
83 ADD_FAILURE() << "Timed out waiting for execute to complete";
84 return result;
85 }
86
87 // FrameTreeDelegate that can block waiting for navigation to start.
88 class TestFrameTreeDelegateImpl : public web_view::TestFrameTreeDelegate {
89 public:
90 explicit TestFrameTreeDelegateImpl(mojo::Shell* shell)
91 : TestFrameTreeDelegate(shell), frame_tree_(nullptr) {}
92 ~TestFrameTreeDelegateImpl() override {}
93
94 void set_frame_tree(FrameTree* frame_tree) { frame_tree_ = frame_tree; }
95
96 // Resets the navigation state for |frame|.
97 void ClearGotNavigate(Frame* frame) { frames_navigated_.erase(frame); }
98
99 // Waits until |frame| has |count| children and the last child has navigated.
100 bool WaitForChildFrameCount(Frame* frame, size_t count) {
101 if (DidChildNavigate(frame, count))
102 return true;
103
104 waiting_for_frame_child_count_.reset(new FrameAndChildCount);
105 waiting_for_frame_child_count_->frame = frame;
106 waiting_for_frame_child_count_->count = count;
107
108 return WindowServerTestBase::DoRunLoopWithTimeout();
109 }
110
111 // Returns true if |frame| has navigated. If |frame| hasn't navigated runs
112 // a nested message loop until |frame| navigates.
113 bool WaitForFrameNavigation(Frame* frame) {
114 if (frames_navigated_.count(frame))
115 return true;
116
117 frames_waiting_for_navigate_.insert(frame);
118 return WindowServerTestBase::DoRunLoopWithTimeout();
119 }
120
121 // TestFrameTreeDelegate:
122 void DidStartNavigation(Frame* frame) override {
123 frames_navigated_.insert(frame);
124
125 if (waiting_for_frame_child_count_ &&
126 DidChildNavigate(waiting_for_frame_child_count_->frame,
127 waiting_for_frame_child_count_->count)) {
128 waiting_for_frame_child_count_.reset();
129 ASSERT_TRUE(WindowServerTestBase::QuitRunLoop());
130 }
131
132 if (frames_waiting_for_navigate_.count(frame)) {
133 frames_waiting_for_navigate_.erase(frame);
134 ignore_result(WindowServerTestBase::QuitRunLoop());
135 }
136 }
137
138 private:
139 struct FrameAndChildCount {
140 Frame* frame;
141 size_t count;
142 };
143
144 // Returns true if |frame| has |count| children and the last child frame
145 // has navigated.
146 bool DidChildNavigate(Frame* frame, size_t count) {
147 return ((frame->children().size() == count) &&
148 (frames_navigated_.count(frame->children()[count - 1])));
149 }
150
151 FrameTree* frame_tree_;
152 // Any time DidStartNavigation() is invoked the frame is added here. Frames
153 // are inserted as void* as this does not track destruction of the frames.
154 std::set<void*> frames_navigated_;
155
156 // The set of frames waiting for a navigation to occur.
157 std::set<Frame*> frames_waiting_for_navigate_;
158
159 // Set of frames waiting for a certain number of children and navigation.
160 scoped_ptr<FrameAndChildCount> waiting_for_frame_child_count_;
161
162 DISALLOW_COPY_AND_ASSIGN(TestFrameTreeDelegateImpl);
163 };
164
165 } // namespace
166
167 class HTMLFrameTest : public WindowServerTestBase {
168 public:
169 HTMLFrameTest() {}
170 ~HTMLFrameTest() override {}
171
172 protected:
173 // Creates the frame tree showing an empty page at the root and adds (via
174 // script) a frame showing the same empty page.
175 Frame* LoadEmptyPageAndCreateFrame() {
176 mus::Window* embed_window = window_manager()->NewWindow();
177 frame_tree_delegate_.reset(new TestFrameTreeDelegateImpl(shell()));
178 FrameConnection* root_connection = InitFrameTree(
179 embed_window, "http://127.0.0.1:%u/empty_page2.html");
180 if (!root_connection) {
181 ADD_FAILURE() << "unable to establish root connection";
182 return nullptr;
183 }
184 const std::string frame_text = GetFrameText(root_connection->connection());
185 if (frame_text != "child2") {
186 ADD_FAILURE() << "unexpected text " << frame_text;
187 return nullptr;
188 }
189
190 return CreateEmptyChildFrame(frame_tree_->root());
191 }
192
193 Frame* CreateEmptyChildFrame(Frame* parent) {
194 const size_t initial_frame_count = parent->children().size();
195 // Dynamically add a new frame.
196 ExecuteScript(ConnectionForFrame(parent),
197 AddPortToString(kAddFrameWithEmptyPageScript));
198
199 frame_tree_delegate_->WaitForChildFrameCount(parent,
200 initial_frame_count + 1);
201 if (HasFatalFailure())
202 return nullptr;
203
204 return parent->FindFrame(parent->window()->children().back()->id());
205 }
206
207 std::string AddPortToString(const std::string& string) {
208 const uint16_t assigned_port = http_server_->host_port_pair().port();
209 return base::StringPrintf(string.c_str(), assigned_port);
210 }
211
212 mojo::URLRequestPtr BuildRequestForURL(const std::string& url_string) {
213 mojo::URLRequestPtr request(mojo::URLRequest::New());
214 request->url = mojo::String::From(AddPortToString(url_string));
215 return request;
216 }
217
218 FrameConnection* InitFrameTree(mus::Window* view,
219 const std::string& url_string) {
220 frame_tree_delegate_.reset(new TestFrameTreeDelegateImpl(shell()));
221 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
222 bool got_callback = false;
223 frame_connection->Init(
224 shell(), BuildRequestForURL(url_string),
225 base::Bind(&OnGotContentHandlerForRoot, &got_callback));
226 ignore_result(WindowServerTestBase::DoRunLoopWithTimeout());
227 if (!got_callback)
228 return nullptr;
229 FrameConnection* result = frame_connection.get();
230 FrameClient* frame_client = frame_connection->frame_client();
231 WindowTreeClientPtr tree_client = frame_connection->GetWindowTreeClient();
232 frame_tree_.reset(new FrameTree(
233 result->GetContentHandlerID(), view, std::move(tree_client),
234 frame_tree_delegate_.get(), frame_client, std::move(frame_connection),
235 Frame::ClientPropertyMap(), base::TimeTicks::Now()));
236 frame_tree_delegate_->set_frame_tree(frame_tree_.get());
237 return result;
238 }
239
240 // ViewManagerTest:
241 void SetUp() override {
242 WindowServerTestBase::SetUp();
243
244 // Start a test server.
245 http_server_.reset(new net::EmbeddedTestServer);
246 http_server_->ServeFilesFromSourceDirectory(
247 "components/test/data/html_viewer");
248 ASSERT_TRUE(http_server_->Start());
249 }
250 void TearDown() override {
251 frame_tree_.reset();
252 http_server_.reset();
253 WindowServerTestBase::TearDown();
254 }
255
256 scoped_ptr<net::EmbeddedTestServer> http_server_;
257 scoped_ptr<FrameTree> frame_tree_;
258
259 scoped_ptr<TestFrameTreeDelegateImpl> frame_tree_delegate_;
260
261 private:
262 DISALLOW_COPY_AND_ASSIGN(HTMLFrameTest);
263 };
264
265 // Crashes on linux_chromium_rel_ng only. http://crbug.com/567337
266 #if defined(OS_LINUX)
267 #define MAYBE_PageWithSingleFrame DISABLED_PageWithSingleFrame
268 #else
269 #define MAYBE_PageWithSingleFrame PageWithSingleFrame
270 #endif
271 TEST_F(HTMLFrameTest, MAYBE_PageWithSingleFrame) {
272 mus::Window* embed_window = window_manager()->NewWindow();
273
274 FrameConnection* root_connection = InitFrameTree(
275 embed_window, "http://127.0.0.1:%u/page_with_single_frame.html");
276 ASSERT_TRUE(root_connection);
277
278 ASSERT_EQ("Page with single frame",
279 GetFrameText(root_connection->connection()));
280
281 ASSERT_NO_FATAL_FAILURE(
282 frame_tree_delegate_->WaitForChildFrameCount(frame_tree_->root(), 1u));
283
284 ASSERT_EQ(1u, embed_window->children().size());
285 Frame* child_frame =
286 frame_tree_->root()->FindFrame(embed_window->children()[0]->id());
287 ASSERT_TRUE(child_frame);
288
289 ASSERT_EQ("child",
290 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
291 ->connection()));
292 }
293
294 // Creates two frames. The parent navigates the child frame by way of changing
295 // the location of the child frame.
296 // Crashes on linux_chromium_rel_ng only. http://crbug.com/567337
297 #if defined(OS_LINUX)
298 #define MAYBE_ChangeLocationOfChildFrame DISABLED_ChangeLocationOfChildFrame
299 #else
300 #define MAYBE_ChangeLocationOfChildFrame ChangeLocationOfChildFrame
301 #endif
302 TEST_F(HTMLFrameTest, MAYBE_ChangeLocationOfChildFrame) {
303 mus::Window* embed_window = window_manager()->NewWindow();
304
305 ASSERT_TRUE(InitFrameTree(
306 embed_window, "http://127.0.0.1:%u/page_with_single_frame.html"));
307
308 // page_with_single_frame contains a child frame. The child frame should
309 // create a new View and Frame.
310 ASSERT_NO_FATAL_FAILURE(
311 frame_tree_delegate_->WaitForChildFrameCount(frame_tree_->root(), 1u));
312
313 Frame* child_frame = frame_tree_->root()->children().back();
314
315 ASSERT_EQ("child",
316 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
317 ->connection()));
318
319 // Change the location and wait for the navigation to occur.
320 const char kNavigateFrame[] =
321 "window.frames[0].location = "
322 "'http://127.0.0.1:%u/empty_page2.html'";
323 frame_tree_delegate_->ClearGotNavigate(child_frame);
324 ExecuteScript(ConnectionForFrame(frame_tree_->root()),
325 AddPortToString(kNavigateFrame));
326 ASSERT_TRUE(frame_tree_delegate_->WaitForFrameNavigation(child_frame));
327
328 // There should still only be one frame.
329 ASSERT_EQ(1u, frame_tree_->root()->children().size());
330
331 // The navigation should have changed the text of the frame.
332 ASSERT_TRUE(child_frame->user_data());
333 ASSERT_EQ("child2",
334 GetFrameText(static_cast<FrameConnection*>(child_frame->user_data())
335 ->connection()));
336 }
337
338 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndVerifyParent) {
339 Frame* child_frame = LoadEmptyPageAndCreateFrame();
340 ASSERT_TRUE(child_frame);
341
342 mojo::Connection* child_frame_connection = ConnectionForFrame(child_frame);
343
344 ASSERT_EQ("child", GetFrameText(child_frame_connection));
345 // The child's parent should not be itself:
346 const char kGetWindowParentNameScript[] =
347 "window.parent == window ? 'parent is self' : 'parent not self';";
348 scoped_ptr<base::Value> parent_value(
349 ExecuteScript(child_frame_connection, kGetWindowParentNameScript));
350 ASSERT_TRUE(parent_value->IsType(base::Value::TYPE_LIST));
351 base::ListValue* parent_list;
352 ASSERT_TRUE(parent_value->GetAsList(&parent_list));
353 ASSERT_EQ(1u, parent_list->GetSize());
354 std::string parent_name;
355 ASSERT_TRUE(parent_list->GetString(0u, &parent_name));
356 EXPECT_EQ("parent not self", parent_name);
357 }
358
359 TEST_F(HTMLFrameTest, DynamicallyAddFrameAndSeeNameChange) {
360 Frame* child_frame = LoadEmptyPageAndCreateFrame();
361 ASSERT_TRUE(child_frame);
362
363 mojo::Connection* child_frame_connection = ConnectionForFrame(child_frame);
364
365 // Change the name of the child's window.
366 ExecuteScript(child_frame_connection, "window.name = 'new_child';");
367
368 // Eventually the parent should see the change. There is no convenient way
369 // to observe this change, so we repeatedly ask for it and timeout if we
370 // never get the right value.
371 const base::TimeTicks start_time(base::TimeTicks::Now());
372 std::string find_window_result;
373 do {
374 find_window_result.clear();
375 scoped_ptr<base::Value> script_value(
376 ExecuteScript(ConnectionForFrame(frame_tree_->root()),
377 "window.frames['new_child'] != null ? 'found frame' : "
378 "'unable to find frame';"));
379 if (script_value->IsType(base::Value::TYPE_LIST)) {
380 base::ListValue* script_value_as_list;
381 if (script_value->GetAsList(&script_value_as_list) &&
382 script_value_as_list->GetSize() == 1) {
383 script_value_as_list->GetString(0u, &find_window_result);
384 }
385 }
386 } while (find_window_result != "found frame" &&
387 base::TimeTicks::Now() - start_time <
388 TestTimeouts::action_timeout());
389 EXPECT_EQ("found frame", find_window_result);
390 }
391
392 // Triggers dynamic addition and removal of a frame.
393 TEST_F(HTMLFrameTest, FrameTreeOfThreeLevels) {
394 // Create a child frame, and in that child frame create another child frame.
395 Frame* child_frame = LoadEmptyPageAndCreateFrame();
396 ASSERT_TRUE(child_frame);
397
398 ASSERT_TRUE(CreateEmptyChildFrame(child_frame));
399
400 // Make sure the parent can see the child and child's child. There is no
401 // convenient way to observe this change, so we repeatedly ask for it and
402 // timeout if we never get the right value.
403 const char kGetChildChildFrameCount[] =
404 "if (window.frames.length > 0)"
405 " window.frames[0].frames.length.toString();"
406 "else"
407 " '0';";
408 const base::TimeTicks start_time(base::TimeTicks::Now());
409 std::string child_child_frame_count;
410 do {
411 child_child_frame_count.clear();
412 scoped_ptr<base::Value> script_value(
413 ExecuteScript(ConnectionForFrame(frame_tree_->root()),
414 kGetChildChildFrameCount));
415 if (script_value->IsType(base::Value::TYPE_LIST)) {
416 base::ListValue* script_value_as_list;
417 if (script_value->GetAsList(&script_value_as_list) &&
418 script_value_as_list->GetSize() == 1) {
419 script_value_as_list->GetString(0u, &child_child_frame_count);
420 }
421 }
422 } while (child_child_frame_count != "1" &&
423 base::TimeTicks::Now() - start_time <
424 TestTimeouts::action_timeout());
425 EXPECT_EQ("1", child_child_frame_count);
426
427 // Remove the child's child and make sure the root doesn't see it anymore.
428 const char kRemoveLastIFrame[] =
429 "document.body.removeChild(document.body.lastChild);";
430 ExecuteScript(ConnectionForFrame(child_frame), kRemoveLastIFrame);
431 do {
432 child_child_frame_count.clear();
433 scoped_ptr<base::Value> script_value(
434 ExecuteScript(ConnectionForFrame(frame_tree_->root()),
435 kGetChildChildFrameCount));
436 if (script_value->IsType(base::Value::TYPE_LIST)) {
437 base::ListValue* script_value_as_list;
438 if (script_value->GetAsList(&script_value_as_list) &&
439 script_value_as_list->GetSize() == 1) {
440 script_value_as_list->GetString(0u, &child_child_frame_count);
441 }
442 }
443 } while (child_child_frame_count != "0" &&
444 base::TimeTicks::Now() - start_time <
445 TestTimeouts::action_timeout());
446 ASSERT_EQ("0", child_child_frame_count);
447 }
448
449 // Verifies PostMessage() works across frames.
450 TEST_F(HTMLFrameTest, PostMessage) {
451 Frame* child_frame = LoadEmptyPageAndCreateFrame();
452 ASSERT_TRUE(child_frame);
453
454 mojo::Connection* child_frame_connection = ConnectionForFrame(child_frame);
455 ASSERT_EQ("child", GetFrameText(child_frame_connection));
456
457 // Register an event handler in the child frame.
458 const char kRegisterPostMessageHandler[] =
459 "window.messageData = null;"
460 "function messageFunction(event) {"
461 " window.messageData = event.data;"
462 "}"
463 "window.addEventListener('message', messageFunction, false);";
464 ExecuteScript(child_frame_connection, kRegisterPostMessageHandler);
465
466 // Post a message from the parent to the child.
467 const char kPostMessageFromParent[] =
468 "window.frames[0].postMessage('hello from parent', '*');";
469 ExecuteScript(ConnectionForFrame(frame_tree_->root()),
470 kPostMessageFromParent);
471
472 // Wait for the child frame to see the message.
473 const base::TimeTicks start_time(base::TimeTicks::Now());
474 std::string message_in_child;
475 do {
476 const char kGetMessageData[] = "window.messageData;";
477 scoped_ptr<base::Value> script_value(
478 ExecuteScript(child_frame_connection, kGetMessageData));
479 if (script_value->IsType(base::Value::TYPE_LIST)) {
480 base::ListValue* script_value_as_list;
481 if (script_value->GetAsList(&script_value_as_list) &&
482 script_value_as_list->GetSize() == 1) {
483 script_value_as_list->GetString(0u, &message_in_child);
484 }
485 }
486 } while (message_in_child != "hello from parent" &&
487 base::TimeTicks::Now() - start_time <
488 TestTimeouts::action_timeout());
489 EXPECT_EQ("hello from parent", message_in_child);
490 }
491
492 } // namespace mojo
OLDNEW
« no previous file with comments | « components/html_viewer/html_frame.cc ('k') | components/html_viewer/html_frame_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698