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