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 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->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 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 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 428namespace internal { 429 430ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents) 431 : render_frame_host_(web_contents->GetMainFrame()) { 432} 433 434ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host) 435 : render_frame_host_(render_view_host->GetMainFrame()) { 436} 437 438ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host) 439 : render_frame_host_(render_frame_host) { 440} 441 442} // namespace internal 443 444bool 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 451bool 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 463bool 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 475bool 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 488namespace { 489void 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 498RenderFrameHost* 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 508bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) { 509 return frame->GetFrameName() == name; 510} 511 512bool FrameIsChildOfMainFrame(RenderFrameHost* frame) { 513 return frame->GetParent() && !frame->GetParent()->GetParent(); 514} 515 516bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) { 517 return frame->GetLastCommittedURL() == url; 518} 519 520bool 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 552std::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 566bool 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 582void 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 596TitleWatcher::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 604void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) { 605 expected_titles_.push_back(expected_title); 606} 607 608TitleWatcher::~TitleWatcher() { 609} 610 611const base::string16& TitleWatcher::WaitAndGetTitle() { 612 TestTitle(); 613 message_loop_runner_->Run(); 614 return observed_title_; 615} 616 617void 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 625void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) { 626 TestTitle(); 627} 628 629void 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 641WebContentsDestroyedWatcher::WebContentsDestroyedWatcher( 642 WebContents* web_contents) 643 : WebContentsObserver(web_contents), 644 message_loop_runner_(new MessageLoopRunner) { 645 EXPECT_TRUE(web_contents != NULL); 646} 647 648WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() { 649} 650 651void WebContentsDestroyedWatcher::Wait() { 652 message_loop_runner_->Run(); 653} 654 655void WebContentsDestroyedWatcher::WebContentsDestroyed() { 656 message_loop_runner_->Quit(); 657} 658 659RenderProcessHostWatcher::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 667RenderProcessHostWatcher::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 675RenderProcessHostWatcher::~RenderProcessHostWatcher() { 676 if (render_process_host_) 677 render_process_host_->RemoveObserver(this); 678} 679 680void RenderProcessHostWatcher::Wait() { 681 message_loop_runner_->Run(); 682} 683 684void 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 693void RenderProcessHostWatcher::RenderProcessHostDestroyed( 694 RenderProcessHost* host) { 695 render_process_host_ = NULL; 696 if (type_ == WATCH_FOR_HOST_DESTRUCTION) 697 message_loop_runner_->Quit(); 698} 699 700DOMMessageQueue::DOMMessageQueue() { 701 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE, 702 NotificationService::AllSources()); 703} 704 705DOMMessageQueue::~DOMMessageQueue() {} 706 707void 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 716void DOMMessageQueue::ClearQueue() { 717 message_queue_ = std::queue<std::string>(); 718} 719 720bool 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