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 "net/base/filename_util.h" 33 #include "net/cookies/cookie_store.h" 34 #include "net/test/python_utils.h" 35 #include "net/url_request/url_request_context.h" 36 #include "net/url_request/url_request_context_getter.h" 37 #include "testing/gtest/include/gtest/gtest.h" 38 #include "ui/base/resource/resource_bundle.h" 39 #include "ui/compositor/test/draw_waiter_for_test.h" 40 #include "ui/events/gestures/gesture_configuration.h" 41 #include "ui/events/keycodes/dom4/keycode_converter.h" 42 #include "ui/resources/grit/webui_resources.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 51 namespace content { 52 namespace { 53 54 class 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. 96 bool 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. 104 bool 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->ExecuteJavaScriptForTests(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 133 void 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 154 void 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 164 void GetCookiesCallback(std::string* cookies_out, 165 base::WaitableEvent* event, 166 const std::string& cookies) { 167 *cookies_out = cookies; 168 event->Signal(); 169 } 170 171 void 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 182 void SetCookieCallback(bool* result, 183 base::WaitableEvent* event, 184 bool success) { 185 *result = success; 186 event->Signal(); 187 } 188 189 void 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 204 GURL 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 215 void 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 226 void 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) 235 bool 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 241 void 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 260 void 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 268 void 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 288 void 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 298 void 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 308 void 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 318 void 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 int native_key_code = ui::KeycodeConverter::CodeToNativeKeycode(code); 326 327 int modifiers = 0; 328 329 // The order of these key down events shouldn't matter for our simulation. 330 // For our simulation we can use either the left keys or the right keys. 331 if (control) { 332 modifiers |= blink::WebInputEvent::ControlKey; 333 InjectRawKeyEvent(web_contents, 334 blink::WebInputEvent::RawKeyDown, 335 ui::VKEY_CONTROL, 336 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"), 337 modifiers); 338 } 339 340 if (shift) { 341 modifiers |= blink::WebInputEvent::ShiftKey; 342 InjectRawKeyEvent(web_contents, 343 blink::WebInputEvent::RawKeyDown, 344 ui::VKEY_SHIFT, 345 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"), 346 modifiers); 347 } 348 349 if (alt) { 350 modifiers |= blink::WebInputEvent::AltKey; 351 InjectRawKeyEvent(web_contents, 352 blink::WebInputEvent::RawKeyDown, 353 ui::VKEY_MENU, 354 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"), 355 modifiers); 356 } 357 358 if (command) { 359 modifiers |= blink::WebInputEvent::MetaKey; 360 InjectRawKeyEvent(web_contents, 361 blink::WebInputEvent::RawKeyDown, 362 ui::VKEY_COMMAND, 363 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"), 364 modifiers); 365 } 366 367 InjectRawKeyEvent( 368 web_contents, 369 blink::WebInputEvent::RawKeyDown, 370 key_code, 371 native_key_code, 372 modifiers); 373 374 InjectRawKeyEvent( 375 web_contents, 376 blink::WebInputEvent::Char, 377 key_code, 378 native_key_code, 379 modifiers); 380 381 InjectRawKeyEvent( 382 web_contents, 383 blink::WebInputEvent::KeyUp, 384 key_code, 385 native_key_code, 386 modifiers); 387 388 // The order of these key releases shouldn't matter for our simulation. 389 if (control) { 390 modifiers &= ~blink::WebInputEvent::ControlKey; 391 InjectRawKeyEvent(web_contents, 392 blink::WebInputEvent::KeyUp, 393 ui::VKEY_CONTROL, 394 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"), 395 modifiers); 396 } 397 398 if (shift) { 399 modifiers &= ~blink::WebInputEvent::ShiftKey; 400 InjectRawKeyEvent(web_contents, 401 blink::WebInputEvent::KeyUp, 402 ui::VKEY_SHIFT, 403 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"), 404 modifiers); 405 } 406 407 if (alt) { 408 modifiers &= ~blink::WebInputEvent::AltKey; 409 InjectRawKeyEvent(web_contents, 410 blink::WebInputEvent::KeyUp, 411 ui::VKEY_MENU, 412 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"), 413 modifiers); 414 } 415 416 if (command) { 417 modifiers &= ~blink::WebInputEvent::MetaKey; 418 InjectRawKeyEvent(web_contents, 419 blink::WebInputEvent::KeyUp, 420 ui::VKEY_COMMAND, 421 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"), 422 modifiers); 423 } 424 425 ASSERT_EQ(modifiers, 0); 426 } 427 428 namespace internal { 429 430 ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents) 431 : render_frame_host_(web_contents->GetMainFrame()) { 432 } 433 434 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host) 435 : render_frame_host_(render_view_host->GetMainFrame()) { 436 } 437 438 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host) 439 : render_frame_host_(render_frame_host) { 440 } 441 442 } // namespace internal 443 444 bool ExecuteScript(const internal::ToRenderFrameHost& adapter, 445 const std::string& script) { 446 std::string new_script = 447 script + ";window.domAutomationController.send(0);"; 448 return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL); 449 } 450 451 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost& adapter, 452 const std::string& script, int* result) { 453 DCHECK(result); 454 scoped_ptr<base::Value> value; 455 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) || 456 !value.get()) { 457 return false; 458 } 459 460 return value->GetAsInteger(result); 461 } 462 463 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost& adapter, 464 const std::string& script, bool* result) { 465 DCHECK(result); 466 scoped_ptr<base::Value> value; 467 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) || 468 !value.get()) { 469 return false; 470 } 471 472 return value->GetAsBoolean(result); 473 } 474 475 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost& adapter, 476 const std::string& script, 477 std::string* result) { 478 DCHECK(result); 479 scoped_ptr<base::Value> value; 480 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) || 481 !value.get()) { 482 return false; 483 } 484 485 return value->GetAsString(result); 486 } 487 488 namespace { 489 void AddToSetIfFrameMatchesPredicate( 490 std::set<RenderFrameHost*>* frame_set, 491 const base::Callback<bool(RenderFrameHost*)>& predicate, 492 RenderFrameHost* host) { 493 if (predicate.Run(host)) 494 frame_set->insert(host); 495 } 496 } 497 498 RenderFrameHost* FrameMatchingPredicate( 499 WebContents* web_contents, 500 const base::Callback<bool(RenderFrameHost*)>& predicate) { 501 std::set<RenderFrameHost*> frame_set; 502 web_contents->ForEachFrame( 503 base::Bind(&AddToSetIfFrameMatchesPredicate, &frame_set, predicate)); 504 DCHECK_EQ(1U, frame_set.size()); 505 return *frame_set.begin(); 506 } 507 508 bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) { 509 return frame->GetFrameName() == name; 510 } 511 512 bool FrameIsChildOfMainFrame(RenderFrameHost* frame) { 513 return frame->GetParent() && !frame->GetParent()->GetParent(); 514 } 515 516 bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) { 517 return frame->GetLastCommittedURL() == url; 518 } 519 520 bool ExecuteWebUIResourceTest(WebContents* web_contents, 521 const std::vector<int>& js_resource_ids) { 522 // Inject WebUI test runner script first prior to other scripts required to 523 // run the test as scripts may depend on it being declared. 524 std::vector<int> ids; 525 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST); 526 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end()); 527 528 std::string script; 529 for (std::vector<int>::iterator iter = ids.begin(); 530 iter != ids.end(); 531 ++iter) { 532 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter) 533 .AppendToString(&script); 534 script.append("\n"); 535 } 536 if (!ExecuteScript(web_contents, script)) 537 return false; 538 539 DOMMessageQueue message_queue; 540 if (!ExecuteScript(web_contents, "runTests()")) 541 return false; 542 543 std::string message; 544 do { 545 if (!message_queue.WaitForMessage(&message)) 546 return false; 547 } while (message.compare("\"PENDING\"") == 0); 548 549 return message.compare("\"SUCCESS\"") == 0; 550 } 551 552 std::string GetCookies(BrowserContext* browser_context, const GURL& url) { 553 std::string cookies; 554 base::WaitableEvent event(true, false); 555 net::URLRequestContextGetter* context_getter = 556 browser_context->GetRequestContext(); 557 558 BrowserThread::PostTask( 559 BrowserThread::IO, FROM_HERE, 560 base::Bind(&GetCookiesOnIOThread, url, 561 make_scoped_refptr(context_getter), &event, &cookies)); 562 event.Wait(); 563 return cookies; 564 } 565 566 bool SetCookie(BrowserContext* browser_context, 567 const GURL& url, 568 const std::string& value) { 569 bool result = false; 570 base::WaitableEvent event(true, false); 571 net::URLRequestContextGetter* context_getter = 572 browser_context->GetRequestContext(); 573 574 BrowserThread::PostTask( 575 BrowserThread::IO, FROM_HERE, 576 base::Bind(&SetCookieOnIOThread, url, value, 577 make_scoped_refptr(context_getter), &event, &result)); 578 event.Wait(); 579 return result; 580 } 581 582 void FetchHistogramsFromChildProcesses() { 583 scoped_refptr<content::MessageLoopRunner> runner = new MessageLoopRunner; 584 585 FetchHistogramsAsynchronously( 586 base::MessageLoop::current(), 587 runner->QuitClosure(), 588 // If this call times out, it means that a child process is not 589 // responding, which is something we should not ignore. The timeout is 590 // set to be longer than the normal browser test timeout so that it will 591 // be prempted by the normal timeout. 592 TestTimeouts::action_max_timeout()); 593 runner->Run(); 594 } 595 596 TitleWatcher::TitleWatcher(WebContents* web_contents, 597 const base::string16& expected_title) 598 : WebContentsObserver(web_contents), 599 message_loop_runner_(new MessageLoopRunner) { 600 EXPECT_TRUE(web_contents != NULL); 601 expected_titles_.push_back(expected_title); 602 } 603 604 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) { 605 expected_titles_.push_back(expected_title); 606 } 607 608 TitleWatcher::~TitleWatcher() { 609 } 610 611 const base::string16& TitleWatcher::WaitAndGetTitle() { 612 TestTitle(); 613 message_loop_runner_->Run(); 614 return observed_title_; 615 } 616 617 void TitleWatcher::DidStopLoading(RenderViewHost* render_view_host) { 618 // When navigating through the history, the restored NavigationEntry's title 619 // will be used. If the entry ends up having the same title after we return 620 // to it, as will usually be the case, then WebContentsObserver::TitleSet 621 // will then be suppressed, since the NavigationEntry's title hasn't changed. 622 TestTitle(); 623 } 624 625 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) { 626 TestTitle(); 627 } 628 629 void TitleWatcher::TestTitle() { 630 std::vector<base::string16>::const_iterator it = 631 std::find(expected_titles_.begin(), 632 expected_titles_.end(), 633 web_contents()->GetTitle()); 634 if (it == expected_titles_.end()) 635 return; 636 637 observed_title_ = *it; 638 message_loop_runner_->Quit(); 639 } 640 641 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher( 642 WebContents* web_contents) 643 : WebContentsObserver(web_contents), 644 message_loop_runner_(new MessageLoopRunner) { 645 EXPECT_TRUE(web_contents != NULL); 646 } 647 648 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() { 649 } 650 651 void WebContentsDestroyedWatcher::Wait() { 652 message_loop_runner_->Run(); 653 } 654 655 void WebContentsDestroyedWatcher::WebContentsDestroyed() { 656 message_loop_runner_->Quit(); 657 } 658 659 RenderProcessHostWatcher::RenderProcessHostWatcher( 660 RenderProcessHost* render_process_host, WatchType type) 661 : render_process_host_(render_process_host), 662 type_(type), 663 message_loop_runner_(new MessageLoopRunner) { 664 render_process_host_->AddObserver(this); 665 } 666 667 RenderProcessHostWatcher::RenderProcessHostWatcher( 668 WebContents* web_contents, WatchType type) 669 : render_process_host_(web_contents->GetRenderProcessHost()), 670 type_(type), 671 message_loop_runner_(new MessageLoopRunner) { 672 render_process_host_->AddObserver(this); 673 } 674 675 RenderProcessHostWatcher::~RenderProcessHostWatcher() { 676 if (render_process_host_) 677 render_process_host_->RemoveObserver(this); 678 } 679 680 void RenderProcessHostWatcher::Wait() { 681 message_loop_runner_->Run(); 682 } 683 684 void RenderProcessHostWatcher::RenderProcessExited( 685 RenderProcessHost* host, 686 base::ProcessHandle handle, 687 base::TerminationStatus status, 688 int exit_code) { 689 if (type_ == WATCH_FOR_PROCESS_EXIT) 690 message_loop_runner_->Quit(); 691 } 692 693 void RenderProcessHostWatcher::RenderProcessHostDestroyed( 694 RenderProcessHost* host) { 695 render_process_host_ = NULL; 696 if (type_ == WATCH_FOR_HOST_DESTRUCTION) 697 message_loop_runner_->Quit(); 698 } 699 700 DOMMessageQueue::DOMMessageQueue() { 701 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE, 702 NotificationService::AllSources()); 703 } 704 705 DOMMessageQueue::~DOMMessageQueue() {} 706 707 void DOMMessageQueue::Observe(int type, 708 const NotificationSource& source, 709 const NotificationDetails& details) { 710 Details<DomOperationNotificationDetails> dom_op_details(details); 711 message_queue_.push(dom_op_details->json); 712 if (message_loop_runner_.get()) 713 message_loop_runner_->Quit(); 714 } 715 716 void DOMMessageQueue::ClearQueue() { 717 message_queue_ = std::queue<std::string>(); 718 } 719 720 bool DOMMessageQueue::WaitForMessage(std::string* message) { 721 DCHECK(message); 722 if (message_queue_.empty()) { 723 // This will be quit when a new message comes in. 724 message_loop_runner_ = new MessageLoopRunner; 725 message_loop_runner_->Run(); 726 } 727 // The queue should not be empty, unless we were quit because of a timeout. 728 if (message_queue_.empty()) 729 return false; 730 *message = message_queue_.front(); 731 message_queue_.pop(); 732 return true; 733 } 734 735 } // namespace content 736