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