Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / public / test / browser_test_utils.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/public/test/browser_test_utils.h"
6
7 #include "base/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<base::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<base::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(base::UTF8ToUTF16(frame_xpath),
100                                                 base::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   RenderProcessHostWatcher watcher(
217       rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
218   base::KillProcess(rph->GetHandle(), 0, false);
219   watcher.Wait();
220 }
221
222 void SimulateMouseClick(WebContents* web_contents,
223                         int modifiers,
224                         blink::WebMouseEvent::Button button) {
225   int x = web_contents->GetView()->GetContainerSize().width() / 2;
226   int y = web_contents->GetView()->GetContainerSize().height() / 2;
227   SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
228 }
229
230 void SimulateMouseClickAt(WebContents* web_contents,
231                           int modifiers,
232                           blink::WebMouseEvent::Button button,
233                           const gfx::Point& point) {
234   blink::WebMouseEvent mouse_event;
235   mouse_event.type = blink::WebInputEvent::MouseDown;
236   mouse_event.button = button;
237   mouse_event.x = point.x();
238   mouse_event.y = point.y();
239   mouse_event.modifiers = modifiers;
240   // Mac needs globalX/globalY for events to plugins.
241   gfx::Rect offset;
242   web_contents->GetView()->GetContainerBounds(&offset);
243   mouse_event.globalX = point.x() + offset.x();
244   mouse_event.globalY = point.y() + offset.y();
245   mouse_event.clickCount = 1;
246   web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
247   mouse_event.type = blink::WebInputEvent::MouseUp;
248   web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
249 }
250
251 void SimulateMouseEvent(WebContents* web_contents,
252                         blink::WebInputEvent::Type type,
253                         const gfx::Point& point) {
254   blink::WebMouseEvent mouse_event;
255   mouse_event.type = type;
256   mouse_event.x = point.x();
257   mouse_event.y = point.y();
258   web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
259 }
260
261 void SimulateKeyPress(WebContents* web_contents,
262                       ui::KeyboardCode key_code,
263                       bool control,
264                       bool shift,
265                       bool alt,
266                       bool command) {
267   SimulateKeyPressWithCode(
268       web_contents, key_code, NULL, control, shift, alt, command);
269 }
270
271 void SimulateKeyPressWithCode(WebContents* web_contents,
272                               ui::KeyboardCode key_code,
273                               const char* code,
274                               bool control,
275                               bool shift,
276                               bool alt,
277                               bool command) {
278   ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
279   int native_key_code = key_converter->CodeToNativeKeycode(code);
280
281   int modifiers = 0;
282
283   // The order of these key down events shouldn't matter for our simulation.
284   // For our simulation we can use either the left keys or the right keys.
285   if (control) {
286     modifiers |= blink::WebInputEvent::ControlKey;
287     InjectRawKeyEvent(
288         web_contents,
289         blink::WebInputEvent::RawKeyDown,
290         ui::VKEY_CONTROL,
291         key_converter->CodeToNativeKeycode("ControlLeft"),
292         modifiers);
293   }
294
295   if (shift) {
296     modifiers |= blink::WebInputEvent::ShiftKey;
297     InjectRawKeyEvent(
298         web_contents,
299         blink::WebInputEvent::RawKeyDown,
300         ui::VKEY_SHIFT,
301         key_converter->CodeToNativeKeycode("ShiftLeft"),
302         modifiers);
303   }
304
305   if (alt) {
306     modifiers |= blink::WebInputEvent::AltKey;
307     InjectRawKeyEvent(
308         web_contents,
309         blink::WebInputEvent::RawKeyDown,
310         ui::VKEY_MENU,
311         key_converter->CodeToNativeKeycode("AltLeft"),
312         modifiers);
313   }
314
315   if (command) {
316     modifiers |= blink::WebInputEvent::MetaKey;
317     InjectRawKeyEvent(
318         web_contents,
319         blink::WebInputEvent::RawKeyDown,
320         ui::VKEY_COMMAND,
321         key_converter->CodeToNativeKeycode("OSLeft"),
322         modifiers);
323   }
324
325   InjectRawKeyEvent(
326       web_contents,
327       blink::WebInputEvent::RawKeyDown,
328       key_code,
329       native_key_code,
330       modifiers);
331
332   InjectRawKeyEvent(
333       web_contents,
334       blink::WebInputEvent::Char,
335       key_code,
336       native_key_code,
337       modifiers);
338
339   InjectRawKeyEvent(
340       web_contents,
341       blink::WebInputEvent::KeyUp,
342       key_code,
343       native_key_code,
344       modifiers);
345
346   // The order of these key releases shouldn't matter for our simulation.
347   if (control) {
348     modifiers &= ~blink::WebInputEvent::ControlKey;
349     InjectRawKeyEvent(
350         web_contents,
351         blink::WebInputEvent::KeyUp,
352         ui::VKEY_CONTROL,
353         key_converter->CodeToNativeKeycode("ControlLeft"),
354         modifiers);
355   }
356
357   if (shift) {
358     modifiers &= ~blink::WebInputEvent::ShiftKey;
359     InjectRawKeyEvent(
360         web_contents,
361         blink::WebInputEvent::KeyUp,
362         ui::VKEY_SHIFT,
363         key_converter->CodeToNativeKeycode("ShiftLeft"),
364         modifiers);
365   }
366
367   if (alt) {
368     modifiers &= ~blink::WebInputEvent::AltKey;
369     InjectRawKeyEvent(
370         web_contents,
371         blink::WebInputEvent::KeyUp,
372         ui::VKEY_MENU,
373         key_converter->CodeToNativeKeycode("AltLeft"),
374         modifiers);
375   }
376
377   if (command) {
378     modifiers &= ~blink::WebInputEvent::MetaKey;
379     InjectRawKeyEvent(
380         web_contents,
381         blink::WebInputEvent::KeyUp,
382         ui::VKEY_COMMAND,
383         key_converter->CodeToNativeKeycode("OSLeft"),
384         modifiers);
385   }
386
387   ASSERT_EQ(modifiers, 0);
388 }
389
390 namespace internal {
391
392 ToRenderViewHost::ToRenderViewHost(WebContents* web_contents)
393     : render_view_host_(web_contents->GetRenderViewHost()) {
394 }
395
396 ToRenderViewHost::ToRenderViewHost(RenderViewHost* render_view_host)
397     : render_view_host_(render_view_host) {
398 }
399
400 }  // namespace internal
401
402 bool ExecuteScriptInFrame(const internal::ToRenderViewHost& adapter,
403                           const std::string& frame_xpath,
404                           const std::string& original_script) {
405   std::string script =
406       original_script + ";window.domAutomationController.send(0);";
407   return ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
408                              NULL);
409 }
410
411 bool ExecuteScriptInFrameAndExtractInt(
412     const internal::ToRenderViewHost& adapter,
413     const std::string& frame_xpath,
414     const std::string& script,
415     int* result) {
416   DCHECK(result);
417   scoped_ptr<base::Value> value;
418   if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
419                            &value) || !value.get())
420     return false;
421
422   return value->GetAsInteger(result);
423 }
424
425 bool ExecuteScriptInFrameAndExtractBool(
426     const internal::ToRenderViewHost& adapter,
427     const std::string& frame_xpath,
428     const std::string& script,
429     bool* result) {
430   DCHECK(result);
431   scoped_ptr<base::Value> value;
432   if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
433                            &value) || !value.get())
434     return false;
435
436   return value->GetAsBoolean(result);
437 }
438
439 bool ExecuteScriptInFrameAndExtractString(
440     const internal::ToRenderViewHost& adapter,
441     const std::string& frame_xpath,
442     const std::string& script,
443     std::string* result) {
444   DCHECK(result);
445   scoped_ptr<base::Value> value;
446   if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
447                            &value) || !value.get())
448     return false;
449
450   return value->GetAsString(result);
451 }
452
453 bool ExecuteScript(const internal::ToRenderViewHost& adapter,
454                    const std::string& script) {
455   return ExecuteScriptInFrame(adapter, std::string(), script);
456 }
457
458 bool ExecuteScriptAndExtractInt(const internal::ToRenderViewHost& adapter,
459                                 const std::string& script, int* result) {
460   return ExecuteScriptInFrameAndExtractInt(adapter, std::string(), script,
461                                            result);
462 }
463
464 bool ExecuteScriptAndExtractBool(const internal::ToRenderViewHost& adapter,
465                                  const std::string& script, bool* result) {
466   return ExecuteScriptInFrameAndExtractBool(adapter, std::string(), script,
467                                             result);
468 }
469
470 bool ExecuteScriptAndExtractString(const internal::ToRenderViewHost& adapter,
471                                    const std::string& script,
472                                    std::string* result) {
473   return ExecuteScriptInFrameAndExtractString(adapter, std::string(), script,
474                                               result);
475 }
476
477 bool ExecuteWebUIResourceTest(
478     const internal::ToRenderViewHost& adapter,
479     const std::vector<int>& js_resource_ids) {
480   // Inject WebUI test runner script first prior to other scripts required to
481   // run the test as scripts may depend on it being declared.
482   std::vector<int> ids;
483   ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST);
484   ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end());
485
486   std::string script;
487   for (std::vector<int>::iterator iter = ids.begin();
488        iter != ids.end();
489        ++iter) {
490     ResourceBundle::GetSharedInstance().GetRawDataResource(*iter)
491         .AppendToString(&script);
492     script.append("\n");
493   }
494   if (!content::ExecuteScript(adapter, script))
495     return false;
496
497   content::DOMMessageQueue message_queue;
498   if (!content::ExecuteScript(adapter, "runTests()"))
499     return false;
500
501   std::string message;
502   do {
503     if (!message_queue.WaitForMessage(&message))
504       return false;
505   } while (message.compare("\"PENDING\"") == 0);
506
507   return message.compare("\"SUCCESS\"") == 0;
508 }
509
510 std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
511   std::string cookies;
512   base::WaitableEvent event(true, false);
513   net::URLRequestContextGetter* context_getter =
514       browser_context->GetRequestContext();
515
516   BrowserThread::PostTask(
517       BrowserThread::IO, FROM_HERE,
518       base::Bind(&GetCookiesOnIOThread, url,
519                  make_scoped_refptr(context_getter), &event, &cookies));
520   event.Wait();
521   return cookies;
522 }
523
524 bool SetCookie(BrowserContext* browser_context,
525                const GURL& url,
526                const std::string& value) {
527   bool result = false;
528   base::WaitableEvent event(true, false);
529   net::URLRequestContextGetter* context_getter =
530       browser_context->GetRequestContext();
531
532   BrowserThread::PostTask(
533       BrowserThread::IO, FROM_HERE,
534       base::Bind(&SetCookieOnIOThread, url, value,
535                  make_scoped_refptr(context_getter), &event, &result));
536   event.Wait();
537   return result;
538 }
539
540 TitleWatcher::TitleWatcher(WebContents* web_contents,
541                            const base::string16& expected_title)
542     : WebContentsObserver(web_contents),
543       message_loop_runner_(new MessageLoopRunner) {
544   EXPECT_TRUE(web_contents != NULL);
545   expected_titles_.push_back(expected_title);
546 }
547
548 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
549   expected_titles_.push_back(expected_title);
550 }
551
552 TitleWatcher::~TitleWatcher() {
553 }
554
555 const base::string16& TitleWatcher::WaitAndGetTitle() {
556   TestTitle();
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 RenderProcessHostWatcher::RenderProcessHostWatcher(
605     RenderProcessHost* render_process_host, WatchType type)
606     : render_process_host_(render_process_host),
607       type_(type),
608       message_loop_runner_(new MessageLoopRunner) {
609   render_process_host_->AddObserver(this);
610 }
611
612 RenderProcessHostWatcher::RenderProcessHostWatcher(
613     WebContents* web_contents, WatchType type)
614     : render_process_host_(web_contents->GetRenderProcessHost()),
615       type_(type),
616       message_loop_runner_(new MessageLoopRunner) {
617   render_process_host_->AddObserver(this);
618 }
619
620 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
621   if (render_process_host_)
622     render_process_host_->RemoveObserver(this);
623 }
624
625 void RenderProcessHostWatcher::Wait() {
626   message_loop_runner_->Run();
627 }
628
629 void RenderProcessHostWatcher::RenderProcessExited(
630     RenderProcessHost* host,
631     base::ProcessHandle handle,
632     base::TerminationStatus status,
633     int exit_code) {
634   if (type_ == WATCH_FOR_PROCESS_EXIT)
635     message_loop_runner_->Quit();
636 }
637
638 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
639     RenderProcessHost* host) {
640   render_process_host_ = NULL;
641   if (type_ == WATCH_FOR_HOST_DESTRUCTION)
642     message_loop_runner_->Quit();
643 }
644
645 DOMMessageQueue::DOMMessageQueue() : waiting_for_message_(false) {
646   registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
647                  NotificationService::AllSources());
648 }
649
650 DOMMessageQueue::~DOMMessageQueue() {}
651
652 void DOMMessageQueue::Observe(int type,
653                               const NotificationSource& source,
654                               const NotificationDetails& details) {
655   Details<DomOperationNotificationDetails> dom_op_details(details);
656   Source<RenderViewHost> sender(source);
657   message_queue_.push(dom_op_details->json);
658   if (waiting_for_message_) {
659     waiting_for_message_ = false;
660     message_loop_runner_->Quit();
661   }
662 }
663
664 void DOMMessageQueue::ClearQueue() {
665   message_queue_ = std::queue<std::string>();
666 }
667
668 bool DOMMessageQueue::WaitForMessage(std::string* message) {
669   if (message_queue_.empty()) {
670     waiting_for_message_ = true;
671     // This will be quit when a new message comes in.
672     message_loop_runner_ = new MessageLoopRunner;
673     message_loop_runner_->Run();
674   }
675   // The queue should not be empty, unless we were quit because of a timeout.
676   if (message_queue_.empty())
677     return false;
678   if (message)
679     *message = message_queue_.front();
680   message_queue_.pop();
681   return true;
682 }
683
684 }  // namespace content