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