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