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