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