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.
5 #include "content/public/test/browser_test_utils.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 "grit/webui_resources.h"
33 #include "net/base/filename_util.h"
34 #include "net/cookies/cookie_store.h"
35 #include "net/test/python_utils.h"
36 #include "net/url_request/url_request_context.h"
37 #include "net/url_request/url_request_context_getter.h"
38 #include "testing/gtest/include/gtest/gtest.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/compositor/test/draw_waiter_for_test.h"
41 #include "ui/events/gestures/gesture_configuration.h"
42 #include "ui/events/keycodes/dom4/keycode_converter.h"
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"
54 class DOMOperationObserver
: public NotificationObserver
,
55 public WebContentsObserver
{
57 explicit DOMOperationObserver(RenderViewHost
* rvh
)
58 : WebContentsObserver(WebContents::FromRenderViewHost(rvh
)),
60 registrar_
.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE
,
61 Source
<WebContents
>(web_contents()));
62 message_loop_runner_
= new MessageLoopRunner
;
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
;
72 message_loop_runner_
->Quit();
75 // Overridden from WebContentsObserver:
76 virtual void RenderProcessGone(base::TerminationStatus status
) OVERRIDE
{
77 message_loop_runner_
->Quit();
80 bool WaitAndGetResponse(std::string
* response
) WARN_UNUSED_RESULT
{
81 message_loop_runner_
->Run();
82 *response
= response_
;
87 NotificationRegistrar registrar_
;
88 std::string response_
;
90 scoped_refptr
<MessageLoopRunner
> message_loop_runner_
;
92 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver
);
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
;
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
110 "window.domAutomationController.setAutomationId(0);" + original_script
;
111 DOMOperationObserver
dom_op_observer(render_frame_host
->GetRenderViewHost());
112 render_frame_host
->ExecuteJavaScript(base::UTF8ToUTF16(script
));
114 if (!dom_op_observer
.WaitAndGetResponse(&json
)) {
115 DLOG(ERROR
) << "Cannot communicate with DOMOperationObserver.";
119 // Nothing more to do for callers that ignore the returned JS value.
123 base::JSONReader
reader(base::JSON_ALLOW_TRAILING_COMMAS
);
124 result
->reset(reader
.ReadToValue(json
));
125 if (!result
->get()) {
126 DLOG(ERROR
) << reader
.GetErrorMessage();
133 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type
,
134 ui::KeyboardCode key_code
,
137 NativeWebKeyboardEvent
* event
) {
138 event
->nativeKeyCode
= native_key_code
;
139 event
->windowsKeyCode
= key_code
;
140 event
->setKeyIdentifierFromWindowsKeyCode();
142 event
->modifiers
= modifiers
;
143 event
->isSystemKey
= false;
144 event
->timeStampSeconds
= base::Time::Now().ToDoubleT();
145 event
->skip_in_browser
= true;
147 if (type
== blink::WebInputEvent::Char
||
148 type
== blink::WebInputEvent::RawKeyDown
) {
149 event
->text
[0] = key_code
;
150 event
->unmodifiedText
[0] = key_code
;
154 void InjectRawKeyEvent(WebContents
* web_contents
,
155 blink::WebInputEvent::Type type
,
156 ui::KeyboardCode key_code
,
159 NativeWebKeyboardEvent event
;
160 BuildSimpleWebKeyEvent(type
, key_code
, native_key_code
, modifiers
, &event
);
161 web_contents
->GetRenderViewHost()->ForwardKeyboardEvent(event
);
164 void GetCookiesCallback(std::string
* cookies_out
,
165 base::WaitableEvent
* event
,
166 const std::string
& cookies
) {
167 *cookies_out
= cookies
;
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
));
182 void SetCookieCallback(bool* result
,
183 base::WaitableEvent
* event
,
189 void SetCookieOnIOThread(const GURL
& url
,
190 const std::string
& value
,
191 net::URLRequestContextGetter
* context_getter
,
192 base::WaitableEvent
* event
,
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
));
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
);
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();
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);
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();
241 void WaitForResizeComplete(WebContents
* web_contents
) {
242 aura::Window
* content
= web_contents
->GetContentNativeView();
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();
260 void SimulateMouseClick(WebContents
* web_contents
,
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
));
268 void SimulateMouseClickAt(WebContents
* web_contents
,
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
);
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
);
298 void SimulateTapAt(WebContents
* web_contents
, const gfx::Point
& point
) {
299 blink::WebGestureEvent tap
;
300 tap
.type
= blink::WebGestureEvent::GestureTap
;
303 RenderWidgetHostImpl
* widget_host
=
304 RenderWidgetHostImpl::From(web_contents
->GetRenderViewHost());
305 widget_host
->ForwardGestureEvent(tap
);
308 void SimulateKeyPress(WebContents
* web_contents
,
309 ui::KeyboardCode key_code
,
314 SimulateKeyPressWithCode(
315 web_contents
, key_code
, NULL
, control
, shift
, alt
, command
);
318 void SimulateKeyPressWithCode(WebContents
* web_contents
,
319 ui::KeyboardCode key_code
,
325 int native_key_code
= ui::KeycodeConverter::CodeToNativeKeycode(code
);
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.
332 modifiers
|= blink::WebInputEvent::ControlKey
;
333 InjectRawKeyEvent(web_contents
,
334 blink::WebInputEvent::RawKeyDown
,
336 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"),
341 modifiers
|= blink::WebInputEvent::ShiftKey
;
342 InjectRawKeyEvent(web_contents
,
343 blink::WebInputEvent::RawKeyDown
,
345 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"),
350 modifiers
|= blink::WebInputEvent::AltKey
;
351 InjectRawKeyEvent(web_contents
,
352 blink::WebInputEvent::RawKeyDown
,
354 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"),
359 modifiers
|= blink::WebInputEvent::MetaKey
;
360 InjectRawKeyEvent(web_contents
,
361 blink::WebInputEvent::RawKeyDown
,
363 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"),
369 blink::WebInputEvent::RawKeyDown
,
376 blink::WebInputEvent::Char
,
383 blink::WebInputEvent::KeyUp
,
388 // The order of these key releases shouldn't matter for our simulation.
390 modifiers
&= ~blink::WebInputEvent::ControlKey
;
391 InjectRawKeyEvent(web_contents
,
392 blink::WebInputEvent::KeyUp
,
394 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"),
399 modifiers
&= ~blink::WebInputEvent::ShiftKey
;
400 InjectRawKeyEvent(web_contents
,
401 blink::WebInputEvent::KeyUp
,
403 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"),
408 modifiers
&= ~blink::WebInputEvent::AltKey
;
409 InjectRawKeyEvent(web_contents
,
410 blink::WebInputEvent::KeyUp
,
412 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"),
417 modifiers
&= ~blink::WebInputEvent::MetaKey
;
418 InjectRawKeyEvent(web_contents
,
419 blink::WebInputEvent::KeyUp
,
421 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"),
425 ASSERT_EQ(modifiers
, 0);
430 ToRenderFrameHost::ToRenderFrameHost(WebContents
* web_contents
)
431 : render_frame_host_(web_contents
->GetMainFrame()) {
434 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost
* render_view_host
)
435 : render_frame_host_(render_view_host
->GetMainFrame()) {
438 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost
* render_frame_host
)
439 : render_frame_host_(render_frame_host
) {
442 } // namespace internal
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
);
451 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost
& adapter
,
452 const std::string
& script
, int* result
) {
454 scoped_ptr
<base::Value
> value
;
455 if (!ExecuteScriptHelper(adapter
.render_frame_host(), script
, &value
) ||
460 return value
->GetAsInteger(result
);
463 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost
& adapter
,
464 const std::string
& script
, bool* result
) {
466 scoped_ptr
<base::Value
> value
;
467 if (!ExecuteScriptHelper(adapter
.render_frame_host(), script
, &value
) ||
472 return value
->GetAsBoolean(result
);
475 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost
& adapter
,
476 const std::string
& script
,
477 std::string
* result
) {
479 scoped_ptr
<base::Value
> value
;
480 if (!ExecuteScriptHelper(adapter
.render_frame_host(), script
, &value
) ||
485 return value
->GetAsString(result
);
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
);
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();
508 bool FrameMatchesName(const std::string
& name
, RenderFrameHost
* frame
) {
509 return frame
->GetFrameName() == name
;
512 bool FrameIsChildOfMainFrame(RenderFrameHost
* frame
) {
513 return frame
->GetParent() && !frame
->GetParent()->GetParent();
516 bool FrameHasSourceUrl(const GURL
& url
, RenderFrameHost
* frame
) {
517 return frame
->GetLastCommittedURL() == url
;
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());
529 for (std::vector
<int>::iterator iter
= ids
.begin();
532 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter
)
533 .AppendToString(&script
);
536 if (!ExecuteScript(web_contents
, script
))
539 DOMMessageQueue message_queue
;
540 if (!ExecuteScript(web_contents
, "runTests()"))
545 if (!message_queue
.WaitForMessage(&message
))
547 } while (message
.compare("\"PENDING\"") == 0);
549 return message
.compare("\"SUCCESS\"") == 0;
552 std::string
GetCookies(BrowserContext
* browser_context
, const GURL
& url
) {
554 base::WaitableEvent
event(true, false);
555 net::URLRequestContextGetter
* context_getter
=
556 browser_context
->GetRequestContext();
558 BrowserThread::PostTask(
559 BrowserThread::IO
, FROM_HERE
,
560 base::Bind(&GetCookiesOnIOThread
, url
,
561 make_scoped_refptr(context_getter
), &event
, &cookies
));
566 bool SetCookie(BrowserContext
* browser_context
,
568 const std::string
& value
) {
570 base::WaitableEvent
event(true, false);
571 net::URLRequestContextGetter
* context_getter
=
572 browser_context
->GetRequestContext();
574 BrowserThread::PostTask(
575 BrowserThread::IO
, FROM_HERE
,
576 base::Bind(&SetCookieOnIOThread
, url
, value
,
577 make_scoped_refptr(context_getter
), &event
, &result
));
582 void FetchHistogramsFromChildProcesses() {
583 scoped_refptr
<content::MessageLoopRunner
> runner
= new MessageLoopRunner
;
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());
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
);
604 void TitleWatcher::AlsoWaitForTitle(const base::string16
& expected_title
) {
605 expected_titles_
.push_back(expected_title
);
608 TitleWatcher::~TitleWatcher() {
611 const base::string16
& TitleWatcher::WaitAndGetTitle() {
613 message_loop_runner_
->Run();
614 return observed_title_
;
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.
625 void TitleWatcher::TitleWasSet(NavigationEntry
* entry
, bool explicit_set
) {
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())
637 observed_title_
= *it
;
638 message_loop_runner_
->Quit();
641 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
642 WebContents
* web_contents
)
643 : WebContentsObserver(web_contents
),
644 message_loop_runner_(new MessageLoopRunner
) {
645 EXPECT_TRUE(web_contents
!= NULL
);
648 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
651 void WebContentsDestroyedWatcher::Wait() {
652 message_loop_runner_
->Run();
655 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
656 message_loop_runner_
->Quit();
659 RenderProcessHostWatcher::RenderProcessHostWatcher(
660 RenderProcessHost
* render_process_host
, WatchType type
)
661 : render_process_host_(render_process_host
),
663 message_loop_runner_(new MessageLoopRunner
) {
664 render_process_host_
->AddObserver(this);
667 RenderProcessHostWatcher::RenderProcessHostWatcher(
668 WebContents
* web_contents
, WatchType type
)
669 : render_process_host_(web_contents
->GetRenderProcessHost()),
671 message_loop_runner_(new MessageLoopRunner
) {
672 render_process_host_
->AddObserver(this);
675 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
676 if (render_process_host_
)
677 render_process_host_
->RemoveObserver(this);
680 void RenderProcessHostWatcher::Wait() {
681 message_loop_runner_
->Run();
684 void RenderProcessHostWatcher::RenderProcessExited(
685 RenderProcessHost
* host
,
686 base::ProcessHandle handle
,
687 base::TerminationStatus status
,
689 if (type_
== WATCH_FOR_PROCESS_EXIT
)
690 message_loop_runner_
->Quit();
693 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
694 RenderProcessHost
* host
) {
695 render_process_host_
= NULL
;
696 if (type_
== WATCH_FOR_HOST_DESTRUCTION
)
697 message_loop_runner_
->Quit();
700 DOMMessageQueue::DOMMessageQueue() {
701 registrar_
.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE
,
702 NotificationService::AllSources());
705 DOMMessageQueue::~DOMMessageQueue() {}
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();
716 void DOMMessageQueue::ClearQueue() {
717 message_queue_
= std::queue
<std::string
>();
720 bool DOMMessageQueue::WaitForMessage(std::string
* 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();
727 // The queue should not be empty, unless we were quit because of a timeout.
728 if (message_queue_
.empty())
730 *message
= message_queue_
.front();
731 message_queue_
.pop();
735 } // namespace content