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