Home | History | Annotate | Download | only in test
      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
    736