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