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