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