browser_test_utils.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright (c) 2012 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/public/test/browser_test_utils.h" 6 7#include "base/command_line.h" 8#include "base/json/json_reader.h" 9#include "base/path_service.h" 10#include "base/process/kill.h" 11#include "base/rand_util.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/utf_string_conversions.h" 14#include "base/synchronization/waitable_event.h" 15#include "base/test/test_timeouts.h" 16#include "base/values.h" 17#include "content/browser/renderer_host/render_widget_host_impl.h" 18#include "content/browser/web_contents/web_contents_view.h" 19#include "content/common/input/synthetic_web_input_event_builders.h" 20#include "content/public/browser/browser_context.h" 21#include "content/public/browser/dom_operation_notification_details.h" 22#include "content/public/browser/notification_service.h" 23#include "content/public/browser/notification_types.h" 24#include "content/public/browser/render_frame_host.h" 25#include "content/public/browser/render_process_host.h" 26#include "content/public/browser/render_view_host.h" 27#include "content/public/browser/web_contents.h" 28#include "content/public/browser/web_contents_observer.h" 29#include "content/public/test/test_utils.h" 30#include "grit/webui_resources.h" 31#include "net/base/filename_util.h" 32#include "net/cookies/cookie_store.h" 33#include "net/test/python_utils.h" 34#include "net/url_request/url_request_context.h" 35#include "net/url_request/url_request_context_getter.h" 36#include "testing/gtest/include/gtest/gtest.h" 37#include "ui/base/resource/resource_bundle.h" 38#include "ui/events/gestures/gesture_configuration.h" 39#include "ui/events/keycodes/dom4/keycode_converter.h" 40 41namespace content { 42namespace { 43 44class DOMOperationObserver : public NotificationObserver, 45 public WebContentsObserver { 46 public: 47 explicit DOMOperationObserver(RenderViewHost* rvh) 48 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)), 49 did_respond_(false) { 50 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE, 51 Source<WebContents>(web_contents())); 52 message_loop_runner_ = new MessageLoopRunner; 53 } 54 55 virtual void Observe(int type, 56 const NotificationSource& source, 57 const NotificationDetails& details) OVERRIDE { 58 DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE); 59 Details<DomOperationNotificationDetails> dom_op_details(details); 60 response_ = dom_op_details->json; 61 did_respond_ = true; 62 message_loop_runner_->Quit(); 63 } 64 65 // Overridden from WebContentsObserver: 66 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE { 67 message_loop_runner_->Quit(); 68 } 69 70 bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT { 71 message_loop_runner_->Run(); 72 *response = response_; 73 return did_respond_; 74 } 75 76 private: 77 NotificationRegistrar registrar_; 78 std::string response_; 79 bool did_respond_; 80 scoped_refptr<MessageLoopRunner> message_loop_runner_; 81 82 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver); 83}; 84 85// Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute. 86bool ExecuteScriptHelper( 87 RenderFrameHost* render_frame_host, 88 const std::string& original_script, 89 scoped_ptr<base::Value>* result) WARN_UNUSED_RESULT; 90 91// Executes the passed |original_script| in the frame specified by 92// |render_frame_host|. If |result| is not NULL, stores the value that the 93// evaluation of the script in |result|. Returns true on success. 94bool ExecuteScriptHelper(RenderFrameHost* render_frame_host, 95 const std::string& original_script, 96 scoped_ptr<base::Value>* result) { 97 // TODO(jcampan): we should make the domAutomationController not require an 98 // automation id. 99 std::string script = 100 "window.domAutomationController.setAutomationId(0);" + original_script; 101 DOMOperationObserver dom_op_observer(render_frame_host->GetRenderViewHost()); 102 render_frame_host->ExecuteJavaScript(base::UTF8ToUTF16(script)); 103 std::string json; 104 if (!dom_op_observer.WaitAndGetResponse(&json)) { 105 DLOG(ERROR) << "Cannot communicate with DOMOperationObserver."; 106 return false; 107 } 108 109 // Nothing more to do for callers that ignore the returned JS value. 110 if (!result) 111 return true; 112 113 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS); 114 result->reset(reader.ReadToValue(json)); 115 if (!result->get()) { 116 DLOG(ERROR) << reader.GetErrorMessage(); 117 return false; 118 } 119 120 return true; 121} 122 123void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type, 124 ui::KeyboardCode key_code, 125 int native_key_code, 126 int modifiers, 127 NativeWebKeyboardEvent* event) { 128 event->nativeKeyCode = native_key_code; 129 event->windowsKeyCode = key_code; 130 event->setKeyIdentifierFromWindowsKeyCode(); 131 event->type = type; 132 event->modifiers = modifiers; 133 event->isSystemKey = false; 134 event->timeStampSeconds = base::Time::Now().ToDoubleT(); 135 event->skip_in_browser = true; 136 137 if (type == blink::WebInputEvent::Char || 138 type == blink::WebInputEvent::RawKeyDown) { 139 event->text[0] = key_code; 140 event->unmodifiedText[0] = key_code; 141 } 142} 143 144void InjectRawKeyEvent(WebContents* web_contents, 145 blink::WebInputEvent::Type type, 146 ui::KeyboardCode key_code, 147 int native_key_code, 148 int modifiers) { 149 NativeWebKeyboardEvent event; 150 BuildSimpleWebKeyEvent(type, key_code, native_key_code, modifiers, &event); 151 web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event); 152} 153 154void GetCookiesCallback(std::string* cookies_out, 155 base::WaitableEvent* event, 156 const std::string& cookies) { 157 *cookies_out = cookies; 158 event->Signal(); 159} 160 161void GetCookiesOnIOThread(const GURL& url, 162 net::URLRequestContextGetter* context_getter, 163 base::WaitableEvent* event, 164 std::string* cookies) { 165 net::CookieStore* cookie_store = 166 context_getter->GetURLRequestContext()->cookie_store(); 167 cookie_store->GetCookiesWithOptionsAsync( 168 url, net::CookieOptions(), 169 base::Bind(&GetCookiesCallback, cookies, event)); 170} 171 172void SetCookieCallback(bool* result, 173 base::WaitableEvent* event, 174 bool success) { 175 *result = success; 176 event->Signal(); 177} 178 179void SetCookieOnIOThread(const GURL& url, 180 const std::string& value, 181 net::URLRequestContextGetter* context_getter, 182 base::WaitableEvent* event, 183 bool* result) { 184 net::CookieStore* cookie_store = 185 context_getter->GetURLRequestContext()->cookie_store(); 186 cookie_store->SetCookieWithOptionsAsync( 187 url, value, net::CookieOptions(), 188 base::Bind(&SetCookieCallback, result, event)); 189} 190 191} // namespace 192 193 194GURL GetFileUrlWithQuery(const base::FilePath& path, 195 const std::string& query_string) { 196 GURL url = net::FilePathToFileURL(path); 197 if (!query_string.empty()) { 198 GURL::Replacements replacements; 199 replacements.SetQueryStr(query_string); 200 return url.ReplaceComponents(replacements); 201 } 202 return url; 203} 204 205void WaitForLoadStop(WebContents* web_contents) { 206 WindowedNotificationObserver load_stop_observer( 207 NOTIFICATION_LOAD_STOP, 208 Source<NavigationController>(&web_contents->GetController())); 209 // In many cases, the load may have finished before we get here. Only wait if 210 // the tab still has a pending navigation. 211 if (!web_contents->IsLoading()) 212 return; 213 load_stop_observer.Wait(); 214} 215 216void CrashTab(WebContents* web_contents) { 217 RenderProcessHost* rph = web_contents->GetRenderProcessHost(); 218 RenderProcessHostWatcher watcher( 219 rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); 220 base::KillProcess(rph->GetHandle(), 0, false); 221 watcher.Wait(); 222} 223 224void SimulateMouseClick(WebContents* web_contents, 225 int modifiers, 226 blink::WebMouseEvent::Button button) { 227 int x = web_contents->GetContainerBounds().width() / 2; 228 int y = web_contents->GetContainerBounds().height() / 2; 229 SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y)); 230} 231 232void SimulateMouseClickAt(WebContents* web_contents, 233 int modifiers, 234 blink::WebMouseEvent::Button button, 235 const gfx::Point& point) { 236 blink::WebMouseEvent mouse_event; 237 mouse_event.type = blink::WebInputEvent::MouseDown; 238 mouse_event.button = button; 239 mouse_event.x = point.x(); 240 mouse_event.y = point.y(); 241 mouse_event.modifiers = modifiers; 242 // Mac needs globalX/globalY for events to plugins. 243 gfx::Rect offset = web_contents->GetContainerBounds(); 244 mouse_event.globalX = point.x() + offset.x(); 245 mouse_event.globalY = point.y() + offset.y(); 246 mouse_event.clickCount = 1; 247 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); 248 mouse_event.type = blink::WebInputEvent::MouseUp; 249 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); 250} 251 252void SimulateMouseEvent(WebContents* web_contents, 253 blink::WebInputEvent::Type type, 254 const gfx::Point& point) { 255 blink::WebMouseEvent mouse_event; 256 mouse_event.type = type; 257 mouse_event.x = point.x(); 258 mouse_event.y = point.y(); 259 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); 260} 261 262void SimulateTapAt(WebContents* web_contents, const gfx::Point& point) { 263 const double kTapDurationSeconds = 264 0.5 * (ui::GestureConfiguration:: 265 min_touch_down_duration_in_seconds_for_click() + 266 ui::GestureConfiguration:: 267 max_touch_down_duration_in_seconds_for_click()); 268 SyntheticWebTouchEvent touch; 269 touch.timeStampSeconds = 0; 270 touch.PressPoint(point.x(), point.y()); 271 RenderWidgetHostImpl* widget_host = 272 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost()); 273 widget_host->ForwardTouchEvent(touch); 274 touch.timeStampSeconds = kTapDurationSeconds; 275 touch.ReleasePoint(0); 276 widget_host->ForwardTouchEvent(touch); 277} 278 279void SimulateKeyPress(WebContents* web_contents, 280 ui::KeyboardCode key_code, 281 bool control, 282 bool shift, 283 bool alt, 284 bool command) { 285 SimulateKeyPressWithCode( 286 web_contents, key_code, NULL, control, shift, alt, command); 287} 288 289void SimulateKeyPressWithCode(WebContents* web_contents, 290 ui::KeyboardCode key_code, 291 const char* code, 292 bool control, 293 bool shift, 294 bool alt, 295 bool command) { 296 ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance(); 297 int native_key_code = key_converter->CodeToNativeKeycode(code); 298 299 int modifiers = 0; 300 301 // The order of these key down events shouldn't matter for our simulation. 302 // For our simulation we can use either the left keys or the right keys. 303 if (control) { 304 modifiers |= blink::WebInputEvent::ControlKey; 305 InjectRawKeyEvent( 306 web_contents, 307 blink::WebInputEvent::RawKeyDown, 308 ui::VKEY_CONTROL, 309 key_converter->CodeToNativeKeycode("ControlLeft"), 310 modifiers); 311 } 312 313 if (shift) { 314 modifiers |= blink::WebInputEvent::ShiftKey; 315 InjectRawKeyEvent( 316 web_contents, 317 blink::WebInputEvent::RawKeyDown, 318 ui::VKEY_SHIFT, 319 key_converter->CodeToNativeKeycode("ShiftLeft"), 320 modifiers); 321 } 322 323 if (alt) { 324 modifiers |= blink::WebInputEvent::AltKey; 325 InjectRawKeyEvent( 326 web_contents, 327 blink::WebInputEvent::RawKeyDown, 328 ui::VKEY_MENU, 329 key_converter->CodeToNativeKeycode("AltLeft"), 330 modifiers); 331 } 332 333 if (command) { 334 modifiers |= blink::WebInputEvent::MetaKey; 335 InjectRawKeyEvent( 336 web_contents, 337 blink::WebInputEvent::RawKeyDown, 338 ui::VKEY_COMMAND, 339 key_converter->CodeToNativeKeycode("OSLeft"), 340 modifiers); 341 } 342 343 InjectRawKeyEvent( 344 web_contents, 345 blink::WebInputEvent::RawKeyDown, 346 key_code, 347 native_key_code, 348 modifiers); 349 350 InjectRawKeyEvent( 351 web_contents, 352 blink::WebInputEvent::Char, 353 key_code, 354 native_key_code, 355 modifiers); 356 357 InjectRawKeyEvent( 358 web_contents, 359 blink::WebInputEvent::KeyUp, 360 key_code, 361 native_key_code, 362 modifiers); 363 364 // The order of these key releases shouldn't matter for our simulation. 365 if (control) { 366 modifiers &= ~blink::WebInputEvent::ControlKey; 367 InjectRawKeyEvent( 368 web_contents, 369 blink::WebInputEvent::KeyUp, 370 ui::VKEY_CONTROL, 371 key_converter->CodeToNativeKeycode("ControlLeft"), 372 modifiers); 373 } 374 375 if (shift) { 376 modifiers &= ~blink::WebInputEvent::ShiftKey; 377 InjectRawKeyEvent( 378 web_contents, 379 blink::WebInputEvent::KeyUp, 380 ui::VKEY_SHIFT, 381 key_converter->CodeToNativeKeycode("ShiftLeft"), 382 modifiers); 383 } 384 385 if (alt) { 386 modifiers &= ~blink::WebInputEvent::AltKey; 387 InjectRawKeyEvent( 388 web_contents, 389 blink::WebInputEvent::KeyUp, 390 ui::VKEY_MENU, 391 key_converter->CodeToNativeKeycode("AltLeft"), 392 modifiers); 393 } 394 395 if (command) { 396 modifiers &= ~blink::WebInputEvent::MetaKey; 397 InjectRawKeyEvent( 398 web_contents, 399 blink::WebInputEvent::KeyUp, 400 ui::VKEY_COMMAND, 401 key_converter->CodeToNativeKeycode("OSLeft"), 402 modifiers); 403 } 404 405 ASSERT_EQ(modifiers, 0); 406} 407 408namespace internal { 409 410ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents) 411 : render_frame_host_(web_contents->GetMainFrame()) { 412} 413 414ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host) 415 : render_frame_host_(render_view_host->GetMainFrame()) { 416} 417 418ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host) 419 : render_frame_host_(render_frame_host) { 420} 421 422} // namespace internal 423 424bool ExecuteScript(const internal::ToRenderFrameHost& adapter, 425 const std::string& script) { 426 std::string new_script = 427 script + ";window.domAutomationController.send(0);"; 428 return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL); 429} 430 431bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost& adapter, 432 const std::string& script, int* result) { 433 DCHECK(result); 434 scoped_ptr<base::Value> value; 435 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) || 436 !value.get()) { 437 return false; 438 } 439 440 return value->GetAsInteger(result); 441} 442 443bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost& adapter, 444 const std::string& script, bool* result) { 445 DCHECK(result); 446 scoped_ptr<base::Value> value; 447 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) || 448 !value.get()) { 449 return false; 450 } 451 452 return value->GetAsBoolean(result); 453} 454 455bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost& adapter, 456 const std::string& script, 457 std::string* result) { 458 DCHECK(result); 459 scoped_ptr<base::Value> value; 460 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) || 461 !value.get()) { 462 return false; 463 } 464 465 return value->GetAsString(result); 466} 467 468namespace { 469void AddToSetIfFrameMatchesPredicate( 470 std::set<RenderFrameHost*>* frame_set, 471 const base::Callback<bool(RenderFrameHost*)>& predicate, 472 RenderFrameHost* host) { 473 if (predicate.Run(host)) 474 frame_set->insert(host); 475} 476} 477 478RenderFrameHost* FrameMatchingPredicate( 479 WebContents* web_contents, 480 const base::Callback<bool(RenderFrameHost*)>& predicate) { 481 std::set<RenderFrameHost*> frame_set; 482 web_contents->ForEachFrame( 483 base::Bind(&AddToSetIfFrameMatchesPredicate, &frame_set, predicate)); 484 DCHECK_EQ(1U, frame_set.size()); 485 return *frame_set.begin(); 486} 487 488bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) { 489 return frame->GetFrameName() == name; 490} 491 492bool FrameIsChildOfMainFrame(RenderFrameHost* frame) { 493 return frame->GetParent() && !frame->GetParent()->GetParent(); 494} 495 496bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) { 497 return frame->GetLastCommittedURL() == url; 498} 499 500bool ExecuteWebUIResourceTest(WebContents* web_contents, 501 const std::vector<int>& js_resource_ids) { 502 // Inject WebUI test runner script first prior to other scripts required to 503 // run the test as scripts may depend on it being declared. 504 std::vector<int> ids; 505 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST); 506 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end()); 507 508 std::string script; 509 for (std::vector<int>::iterator iter = ids.begin(); 510 iter != ids.end(); 511 ++iter) { 512 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter) 513 .AppendToString(&script); 514 script.append("\n"); 515 } 516 if (!ExecuteScript(web_contents, script)) 517 return false; 518 519 DOMMessageQueue message_queue; 520 if (!ExecuteScript(web_contents, "runTests()")) 521 return false; 522 523 std::string message; 524 do { 525 if (!message_queue.WaitForMessage(&message)) 526 return false; 527 } while (message.compare("\"PENDING\"") == 0); 528 529 return message.compare("\"SUCCESS\"") == 0; 530} 531 532std::string GetCookies(BrowserContext* browser_context, const GURL& url) { 533 std::string cookies; 534 base::WaitableEvent event(true, false); 535 net::URLRequestContextGetter* context_getter = 536 browser_context->GetRequestContext(); 537 538 BrowserThread::PostTask( 539 BrowserThread::IO, FROM_HERE, 540 base::Bind(&GetCookiesOnIOThread, url, 541 make_scoped_refptr(context_getter), &event, &cookies)); 542 event.Wait(); 543 return cookies; 544} 545 546bool SetCookie(BrowserContext* browser_context, 547 const GURL& url, 548 const std::string& value) { 549 bool result = false; 550 base::WaitableEvent event(true, false); 551 net::URLRequestContextGetter* context_getter = 552 browser_context->GetRequestContext(); 553 554 BrowserThread::PostTask( 555 BrowserThread::IO, FROM_HERE, 556 base::Bind(&SetCookieOnIOThread, url, value, 557 make_scoped_refptr(context_getter), &event, &result)); 558 event.Wait(); 559 return result; 560} 561 562TitleWatcher::TitleWatcher(WebContents* web_contents, 563 const base::string16& expected_title) 564 : WebContentsObserver(web_contents), 565 message_loop_runner_(new MessageLoopRunner) { 566 EXPECT_TRUE(web_contents != NULL); 567 expected_titles_.push_back(expected_title); 568} 569 570void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) { 571 expected_titles_.push_back(expected_title); 572} 573 574TitleWatcher::~TitleWatcher() { 575} 576 577const base::string16& TitleWatcher::WaitAndGetTitle() { 578 TestTitle(); 579 message_loop_runner_->Run(); 580 return observed_title_; 581} 582 583void TitleWatcher::DidStopLoading(RenderViewHost* render_view_host) { 584 // When navigating through the history, the restored NavigationEntry's title 585 // will be used. If the entry ends up having the same title after we return 586 // to it, as will usually be the case, then WebContentsObserver::TitleSet 587 // will then be suppressed, since the NavigationEntry's title hasn't changed. 588 TestTitle(); 589} 590 591void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) { 592 TestTitle(); 593} 594 595void TitleWatcher::TestTitle() { 596 std::vector<base::string16>::const_iterator it = 597 std::find(expected_titles_.begin(), 598 expected_titles_.end(), 599 web_contents()->GetTitle()); 600 if (it == expected_titles_.end()) 601 return; 602 603 observed_title_ = *it; 604 message_loop_runner_->Quit(); 605} 606 607WebContentsDestroyedWatcher::WebContentsDestroyedWatcher( 608 WebContents* web_contents) 609 : WebContentsObserver(web_contents), 610 message_loop_runner_(new MessageLoopRunner) { 611 EXPECT_TRUE(web_contents != NULL); 612} 613 614WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() { 615} 616 617void WebContentsDestroyedWatcher::Wait() { 618 message_loop_runner_->Run(); 619} 620 621void WebContentsDestroyedWatcher::WebContentsDestroyed() { 622 message_loop_runner_->Quit(); 623} 624 625RenderProcessHostWatcher::RenderProcessHostWatcher( 626 RenderProcessHost* render_process_host, WatchType type) 627 : render_process_host_(render_process_host), 628 type_(type), 629 message_loop_runner_(new MessageLoopRunner) { 630 render_process_host_->AddObserver(this); 631} 632 633RenderProcessHostWatcher::RenderProcessHostWatcher( 634 WebContents* web_contents, WatchType type) 635 : render_process_host_(web_contents->GetRenderProcessHost()), 636 type_(type), 637 message_loop_runner_(new MessageLoopRunner) { 638 render_process_host_->AddObserver(this); 639} 640 641RenderProcessHostWatcher::~RenderProcessHostWatcher() { 642 if (render_process_host_) 643 render_process_host_->RemoveObserver(this); 644} 645 646void RenderProcessHostWatcher::Wait() { 647 message_loop_runner_->Run(); 648} 649 650void RenderProcessHostWatcher::RenderProcessExited( 651 RenderProcessHost* host, 652 base::ProcessHandle handle, 653 base::TerminationStatus status, 654 int exit_code) { 655 if (type_ == WATCH_FOR_PROCESS_EXIT) 656 message_loop_runner_->Quit(); 657} 658 659void RenderProcessHostWatcher::RenderProcessHostDestroyed( 660 RenderProcessHost* host) { 661 render_process_host_ = NULL; 662 if (type_ == WATCH_FOR_HOST_DESTRUCTION) 663 message_loop_runner_->Quit(); 664} 665 666DOMMessageQueue::DOMMessageQueue() { 667 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE, 668 NotificationService::AllSources()); 669} 670 671DOMMessageQueue::~DOMMessageQueue() {} 672 673void DOMMessageQueue::Observe(int type, 674 const NotificationSource& source, 675 const NotificationDetails& details) { 676 Details<DomOperationNotificationDetails> dom_op_details(details); 677 message_queue_.push(dom_op_details->json); 678 if (message_loop_runner_) 679 message_loop_runner_->Quit(); 680} 681 682void DOMMessageQueue::ClearQueue() { 683 message_queue_ = std::queue<std::string>(); 684} 685 686bool DOMMessageQueue::WaitForMessage(std::string* message) { 687 DCHECK(message); 688 if (message_queue_.empty()) { 689 // This will be quit when a new message comes in. 690 message_loop_runner_ = new MessageLoopRunner; 691 message_loop_runner_->Run(); 692 } 693 // The queue should not be empty, unless we were quit because of a timeout. 694 if (message_queue_.empty()) 695 return false; 696 *message = message_queue_.front(); 697 message_queue_.pop(); 698 return true; 699} 700 701} // namespace content 702