| OLD | NEW |
| (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 | |
| OLD | NEW |