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/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/histogram_fetcher.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_frame_host.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/test/test_utils.h"
31 #include "net/base/filename_util.h"
32 #include "net/cookies/cookie_store.h"
33 #include "net/test/embedded_test_server/embedded_test_server.h"
34 #include "net/test/embedded_test_server/http_request.h"
35 #include "net/test/embedded_test_server/http_response.h"
36 #include "net/test/python_utils.h"
37 #include "net/url_request/url_request_context.h"
38 #include "net/url_request/url_request_context_getter.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40 #include "ui/base/resource/resource_bundle.h"
41 #include "ui/compositor/test/draw_waiter_for_test.h"
42 #include "ui/events/gesture_detection/gesture_configuration.h"
43 #include "ui/events/keycodes/dom4/keycode_converter.h"
44 #include "ui/resources/grit/webui_resources.h"
47 #include "ui/aura/test/window_event_dispatcher_test_api.h"
48 #include "ui/aura/window.h"
49 #include "ui/aura/window_event_dispatcher.h"
50 #include "ui/aura/window_tree_host.h"
56 class DOMOperationObserver : public NotificationObserver,
57 public WebContentsObserver {
59 explicit DOMOperationObserver(RenderViewHost* rvh)
60 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
62 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
63 Source<WebContents>(web_contents()));
64 message_loop_runner_ = new MessageLoopRunner;
67 void Observe(int type,
68 const NotificationSource& source,
69 const NotificationDetails& details) override {
70 DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
71 Details<DomOperationNotificationDetails> dom_op_details(details);
72 response_ = dom_op_details->json;
74 message_loop_runner_->Quit();
77 // Overridden from WebContentsObserver:
78 void RenderProcessGone(base::TerminationStatus status) override {
79 message_loop_runner_->Quit();
82 bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
83 message_loop_runner_->Run();
84 *response = response_;
89 NotificationRegistrar registrar_;
90 std::string response_;
92 scoped_refptr<MessageLoopRunner> message_loop_runner_;
94 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
97 class InterstitialObserver : public content::WebContentsObserver {
99 InterstitialObserver(content::WebContents* web_contents,
100 const base::Closure& attach_callback,
101 const base::Closure& detach_callback)
102 : WebContentsObserver(web_contents),
103 attach_callback_(attach_callback),
104 detach_callback_(detach_callback) {
106 ~InterstitialObserver() override {}
108 // WebContentsObserver methods:
109 void DidAttachInterstitialPage() override { attach_callback_.Run(); }
110 void DidDetachInterstitialPage() override { detach_callback_.Run(); }
113 base::Closure attach_callback_;
114 base::Closure detach_callback_;
116 DISALLOW_COPY_AND_ASSIGN(InterstitialObserver);
119 // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
120 bool ExecuteScriptHelper(
121 RenderFrameHost* render_frame_host,
122 const std::string& original_script,
123 scoped_ptr<base::Value>* result) WARN_UNUSED_RESULT;
125 // Executes the passed |original_script| in the frame specified by
126 // |render_frame_host|. If |result| is not NULL, stores the value that the
127 // evaluation of the script in |result|. Returns true on success.
128 bool ExecuteScriptHelper(RenderFrameHost* render_frame_host,
129 const std::string& original_script,
130 scoped_ptr<base::Value>* result) {
131 // TODO(jcampan): we should make the domAutomationController not require an
134 "window.domAutomationController.setAutomationId(0);" + original_script;
135 DOMOperationObserver dom_op_observer(render_frame_host->GetRenderViewHost());
136 render_frame_host->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script));
138 if (!dom_op_observer.WaitAndGetResponse(&json)) {
139 DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
143 // Nothing more to do for callers that ignore the returned JS value.
147 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
148 result->reset(reader.ReadToValue(json));
149 if (!result->get()) {
150 DLOG(ERROR) << reader.GetErrorMessage();
157 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type,
158 ui::KeyboardCode key_code,
161 NativeWebKeyboardEvent* event) {
162 event->nativeKeyCode = native_key_code;
163 event->windowsKeyCode = key_code;
164 event->setKeyIdentifierFromWindowsKeyCode();
166 event->modifiers = modifiers;
167 event->isSystemKey = false;
168 event->timeStampSeconds = base::Time::Now().ToDoubleT();
169 event->skip_in_browser = true;
171 if (type == blink::WebInputEvent::Char ||
172 type == blink::WebInputEvent::RawKeyDown) {
173 event->text[0] = key_code;
174 event->unmodifiedText[0] = key_code;
178 void InjectRawKeyEvent(WebContents* web_contents,
179 blink::WebInputEvent::Type type,
180 ui::KeyboardCode key_code,
183 NativeWebKeyboardEvent event;
184 BuildSimpleWebKeyEvent(type, key_code, native_key_code, modifiers, &event);
185 web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event);
188 void GetCookiesCallback(std::string* cookies_out,
189 base::WaitableEvent* event,
190 const std::string& cookies) {
191 *cookies_out = cookies;
195 void GetCookiesOnIOThread(const GURL& url,
196 net::URLRequestContextGetter* context_getter,
197 base::WaitableEvent* event,
198 std::string* cookies) {
199 net::CookieStore* cookie_store =
200 context_getter->GetURLRequestContext()->cookie_store();
201 cookie_store->GetCookiesWithOptionsAsync(
202 url, net::CookieOptions(),
203 base::Bind(&GetCookiesCallback, cookies, event));
206 void SetCookieCallback(bool* result,
207 base::WaitableEvent* event,
213 void SetCookieOnIOThread(const GURL& url,
214 const std::string& value,
215 net::URLRequestContextGetter* context_getter,
216 base::WaitableEvent* event,
218 net::CookieStore* cookie_store =
219 context_getter->GetURLRequestContext()->cookie_store();
220 cookie_store->SetCookieWithOptionsAsync(
221 url, value, net::CookieOptions(),
222 base::Bind(&SetCookieCallback, result, event));
225 scoped_ptr<net::test_server::HttpResponse> CrossSiteRedirectResponseHandler(
226 const GURL& server_base_url,
227 const net::test_server::HttpRequest& request) {
228 std::string prefix("/cross-site/");
229 if (!StartsWithASCII(request.relative_url, prefix, true))
230 return scoped_ptr<net::test_server::HttpResponse>();
232 std::string params = request.relative_url.substr(prefix.length());
234 // A hostname to redirect to must be included in the URL, therefore at least
235 // one '/' character is expected.
236 size_t slash = params.find('/');
237 if (slash == std::string::npos)
238 return scoped_ptr<net::test_server::HttpResponse>();
240 // Replace the host of the URL with the one passed in the URL.
241 std::string host = params.substr(0, slash);
242 GURL::Replacements replace_host;
243 replace_host.SetHostStr(host);
244 GURL redirect_server = server_base_url.ReplaceComponents(replace_host);
246 // Append the real part of the path to the new URL.
247 std::string path = params.substr(slash + 1);
248 GURL redirect_target(redirect_server.Resolve(path));
249 DCHECK(redirect_target.is_valid());
251 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
252 new net::test_server::BasicHttpResponse);
253 http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
254 http_response->AddCustomHeader("Location", redirect_target.spec());
255 return http_response.Pass();
261 GURL GetFileUrlWithQuery(const base::FilePath& path,
262 const std::string& query_string) {
263 GURL url = net::FilePathToFileURL(path);
264 if (!query_string.empty()) {
265 GURL::Replacements replacements;
266 replacements.SetQueryStr(query_string);
267 return url.ReplaceComponents(replacements);
272 void WaitForLoadStop(WebContents* web_contents) {
273 // In many cases, the load may have finished before we get here. Only wait if
274 // the tab still has a pending navigation.
275 if (web_contents->IsLoading()) {
276 WindowedNotificationObserver load_stop_observer(
277 NOTIFICATION_LOAD_STOP,
278 Source<NavigationController>(&web_contents->GetController()));
279 load_stop_observer.Wait();
283 void CrashTab(WebContents* web_contents) {
284 RenderProcessHost* rph = web_contents->GetRenderProcessHost();
285 RenderProcessHostWatcher watcher(
286 rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
287 base::KillProcess(rph->GetHandle(), 0, false);
291 #if defined(USE_AURA)
292 bool IsResizeComplete(aura::test::WindowEventDispatcherTestApi* dispatcher_test,
293 RenderWidgetHostImpl* widget_host) {
294 return !dispatcher_test->HoldingPointerMoves() &&
295 !widget_host->resize_ack_pending_for_testing();
298 void WaitForResizeComplete(WebContents* web_contents) {
299 aura::Window* content = web_contents->GetContentNativeView();
303 aura::WindowTreeHost* window_host = content->GetHost();
304 aura::WindowEventDispatcher* dispatcher = window_host->dispatcher();
305 aura::test::WindowEventDispatcherTestApi dispatcher_test(dispatcher);
306 RenderWidgetHostImpl* widget_host =
307 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
308 if (!IsResizeComplete(&dispatcher_test, widget_host)) {
309 WindowedNotificationObserver resize_observer(
310 NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
311 base::Bind(IsResizeComplete, &dispatcher_test, widget_host));
312 resize_observer.Wait();
317 void SimulateMouseClick(WebContents* web_contents,
319 blink::WebMouseEvent::Button button) {
320 int x = web_contents->GetContainerBounds().width() / 2;
321 int y = web_contents->GetContainerBounds().height() / 2;
322 SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
325 void SimulateMouseClickAt(WebContents* web_contents,
327 blink::WebMouseEvent::Button button,
328 const gfx::Point& point) {
329 blink::WebMouseEvent mouse_event;
330 mouse_event.type = blink::WebInputEvent::MouseDown;
331 mouse_event.button = button;
332 mouse_event.x = point.x();
333 mouse_event.y = point.y();
334 mouse_event.modifiers = modifiers;
335 // Mac needs globalX/globalY for events to plugins.
336 gfx::Rect offset = web_contents->GetContainerBounds();
337 mouse_event.globalX = point.x() + offset.x();
338 mouse_event.globalY = point.y() + offset.y();
339 mouse_event.clickCount = 1;
340 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
341 mouse_event.type = blink::WebInputEvent::MouseUp;
342 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
345 void SimulateMouseEvent(WebContents* web_contents,
346 blink::WebInputEvent::Type type,
347 const gfx::Point& point) {
348 blink::WebMouseEvent mouse_event;
349 mouse_event.type = type;
350 mouse_event.x = point.x();
351 mouse_event.y = point.y();
352 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
355 void SimulateTapAt(WebContents* web_contents, const gfx::Point& point) {
356 blink::WebGestureEvent tap;
357 tap.type = blink::WebGestureEvent::GestureTap;
360 RenderWidgetHostImpl* widget_host =
361 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
362 widget_host->ForwardGestureEvent(tap);
365 void SimulateKeyPress(WebContents* web_contents,
366 ui::KeyboardCode key_code,
371 SimulateKeyPressWithCode(
372 web_contents, key_code, NULL, control, shift, alt, command);
375 void SimulateKeyPressWithCode(WebContents* web_contents,
376 ui::KeyboardCode key_code,
382 int native_key_code = ui::KeycodeConverter::CodeToNativeKeycode(code);
386 // The order of these key down events shouldn't matter for our simulation.
387 // For our simulation we can use either the left keys or the right keys.
389 modifiers |= blink::WebInputEvent::ControlKey;
390 InjectRawKeyEvent(web_contents,
391 blink::WebInputEvent::RawKeyDown,
393 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"),
398 modifiers |= blink::WebInputEvent::ShiftKey;
399 InjectRawKeyEvent(web_contents,
400 blink::WebInputEvent::RawKeyDown,
402 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"),
407 modifiers |= blink::WebInputEvent::AltKey;
408 InjectRawKeyEvent(web_contents,
409 blink::WebInputEvent::RawKeyDown,
411 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"),
416 modifiers |= blink::WebInputEvent::MetaKey;
417 InjectRawKeyEvent(web_contents,
418 blink::WebInputEvent::RawKeyDown,
420 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"),
426 blink::WebInputEvent::RawKeyDown,
433 blink::WebInputEvent::Char,
440 blink::WebInputEvent::KeyUp,
445 // The order of these key releases shouldn't matter for our simulation.
447 modifiers &= ~blink::WebInputEvent::ControlKey;
448 InjectRawKeyEvent(web_contents,
449 blink::WebInputEvent::KeyUp,
451 ui::KeycodeConverter::CodeToNativeKeycode("ControlLeft"),
456 modifiers &= ~blink::WebInputEvent::ShiftKey;
457 InjectRawKeyEvent(web_contents,
458 blink::WebInputEvent::KeyUp,
460 ui::KeycodeConverter::CodeToNativeKeycode("ShiftLeft"),
465 modifiers &= ~blink::WebInputEvent::AltKey;
466 InjectRawKeyEvent(web_contents,
467 blink::WebInputEvent::KeyUp,
469 ui::KeycodeConverter::CodeToNativeKeycode("AltLeft"),
474 modifiers &= ~blink::WebInputEvent::MetaKey;
475 InjectRawKeyEvent(web_contents,
476 blink::WebInputEvent::KeyUp,
478 ui::KeycodeConverter::CodeToNativeKeycode("OSLeft"),
482 ASSERT_EQ(modifiers, 0);
487 ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents)
488 : render_frame_host_(web_contents->GetMainFrame()) {
491 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host)
492 : render_frame_host_(render_view_host->GetMainFrame()) {
495 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host)
496 : render_frame_host_(render_frame_host) {
499 } // namespace internal
501 bool ExecuteScript(const internal::ToRenderFrameHost& adapter,
502 const std::string& script) {
503 std::string new_script =
504 script + ";window.domAutomationController.send(0);";
505 return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL);
508 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost& adapter,
509 const std::string& script, int* result) {
511 scoped_ptr<base::Value> value;
512 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
517 return value->GetAsInteger(result);
520 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost& adapter,
521 const std::string& script, bool* result) {
523 scoped_ptr<base::Value> value;
524 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
529 return value->GetAsBoolean(result);
532 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost& adapter,
533 const std::string& script,
534 std::string* result) {
536 scoped_ptr<base::Value> value;
537 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
542 return value->GetAsString(result);
546 void AddToSetIfFrameMatchesPredicate(
547 std::set<RenderFrameHost*>* frame_set,
548 const base::Callback<bool(RenderFrameHost*)>& predicate,
549 RenderFrameHost* host) {
550 if (predicate.Run(host))
551 frame_set->insert(host);
555 RenderFrameHost* FrameMatchingPredicate(
556 WebContents* web_contents,
557 const base::Callback<bool(RenderFrameHost*)>& predicate) {
558 std::set<RenderFrameHost*> frame_set;
559 web_contents->ForEachFrame(
560 base::Bind(&AddToSetIfFrameMatchesPredicate, &frame_set, predicate));
561 DCHECK_EQ(1U, frame_set.size());
562 return *frame_set.begin();
565 bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) {
566 return frame->GetFrameName() == name;
569 bool FrameIsChildOfMainFrame(RenderFrameHost* frame) {
570 return frame->GetParent() && !frame->GetParent()->GetParent();
573 bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) {
574 return frame->GetLastCommittedURL() == url;
577 bool ExecuteWebUIResourceTest(WebContents* web_contents,
578 const std::vector<int>& js_resource_ids) {
579 // Inject WebUI test runner script first prior to other scripts required to
580 // run the test as scripts may depend on it being declared.
581 std::vector<int> ids;
582 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST);
583 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end());
586 for (std::vector<int>::iterator iter = ids.begin();
589 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter)
590 .AppendToString(&script);
593 if (!ExecuteScript(web_contents, script))
596 DOMMessageQueue message_queue;
597 if (!ExecuteScript(web_contents, "runTests()"))
602 if (!message_queue.WaitForMessage(&message))
604 } while (message.compare("\"PENDING\"") == 0);
606 return message.compare("\"SUCCESS\"") == 0;
609 std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
611 base::WaitableEvent event(true, false);
612 net::URLRequestContextGetter* context_getter =
613 browser_context->GetRequestContext();
615 BrowserThread::PostTask(
616 BrowserThread::IO, FROM_HERE,
617 base::Bind(&GetCookiesOnIOThread, url,
618 make_scoped_refptr(context_getter), &event, &cookies));
623 bool SetCookie(BrowserContext* browser_context,
625 const std::string& value) {
627 base::WaitableEvent event(true, false);
628 net::URLRequestContextGetter* context_getter =
629 browser_context->GetRequestContext();
631 BrowserThread::PostTask(
632 BrowserThread::IO, FROM_HERE,
633 base::Bind(&SetCookieOnIOThread, url, value,
634 make_scoped_refptr(context_getter), &event, &result));
639 void FetchHistogramsFromChildProcesses() {
640 scoped_refptr<content::MessageLoopRunner> runner = new MessageLoopRunner;
642 FetchHistogramsAsynchronously(
643 base::MessageLoop::current(),
644 runner->QuitClosure(),
645 // If this call times out, it means that a child process is not
646 // responding, which is something we should not ignore. The timeout is
647 // set to be longer than the normal browser test timeout so that it will
648 // be prempted by the normal timeout.
649 TestTimeouts::action_max_timeout());
653 void SetupCrossSiteRedirector(
654 net::test_server::EmbeddedTestServer* embedded_test_server) {
655 embedded_test_server->RegisterRequestHandler(
656 base::Bind(&CrossSiteRedirectResponseHandler,
657 embedded_test_server->base_url()));
660 void WaitForInterstitialAttach(content::WebContents* web_contents) {
661 if (web_contents->ShowingInterstitialPage())
663 scoped_refptr<content::MessageLoopRunner> loop_runner(
664 new content::MessageLoopRunner);
665 InterstitialObserver observer(web_contents,
666 loop_runner->QuitClosure(),
671 void WaitForInterstitialDetach(content::WebContents* web_contents) {
672 RunTaskAndWaitForInterstitialDetach(web_contents, base::Closure());
675 void RunTaskAndWaitForInterstitialDetach(content::WebContents* web_contents,
676 const base::Closure& task) {
677 if (!web_contents || !web_contents->ShowingInterstitialPage())
679 scoped_refptr<content::MessageLoopRunner> loop_runner(
680 new content::MessageLoopRunner);
681 InterstitialObserver observer(web_contents,
683 loop_runner->QuitClosure());
686 // At this point, web_contents may have been deleted.
690 TitleWatcher::TitleWatcher(WebContents* web_contents,
691 const base::string16& expected_title)
692 : WebContentsObserver(web_contents),
693 message_loop_runner_(new MessageLoopRunner) {
694 EXPECT_TRUE(web_contents != NULL);
695 expected_titles_.push_back(expected_title);
698 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
699 expected_titles_.push_back(expected_title);
702 TitleWatcher::~TitleWatcher() {
705 const base::string16& TitleWatcher::WaitAndGetTitle() {
707 message_loop_runner_->Run();
708 return observed_title_;
711 void TitleWatcher::DidStopLoading(RenderViewHost* render_view_host) {
712 // When navigating through the history, the restored NavigationEntry's title
713 // will be used. If the entry ends up having the same title after we return
714 // to it, as will usually be the case, then WebContentsObserver::TitleSet
715 // will then be suppressed, since the NavigationEntry's title hasn't changed.
719 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
723 void TitleWatcher::TestTitle() {
724 std::vector<base::string16>::const_iterator it =
725 std::find(expected_titles_.begin(),
726 expected_titles_.end(),
727 web_contents()->GetTitle());
728 if (it == expected_titles_.end())
731 observed_title_ = *it;
732 message_loop_runner_->Quit();
735 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
736 WebContents* web_contents)
737 : WebContentsObserver(web_contents),
738 message_loop_runner_(new MessageLoopRunner) {
739 EXPECT_TRUE(web_contents != NULL);
742 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
745 void WebContentsDestroyedWatcher::Wait() {
746 message_loop_runner_->Run();
749 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
750 message_loop_runner_->Quit();
753 RenderProcessHostWatcher::RenderProcessHostWatcher(
754 RenderProcessHost* render_process_host, WatchType type)
755 : render_process_host_(render_process_host),
757 message_loop_runner_(new MessageLoopRunner) {
758 render_process_host_->AddObserver(this);
761 RenderProcessHostWatcher::RenderProcessHostWatcher(
762 WebContents* web_contents, WatchType type)
763 : render_process_host_(web_contents->GetRenderProcessHost()),
765 message_loop_runner_(new MessageLoopRunner) {
766 render_process_host_->AddObserver(this);
769 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
770 if (render_process_host_)
771 render_process_host_->RemoveObserver(this);
774 void RenderProcessHostWatcher::Wait() {
775 message_loop_runner_->Run();
778 void RenderProcessHostWatcher::RenderProcessExited(
779 RenderProcessHost* host,
780 base::TerminationStatus status,
782 if (type_ == WATCH_FOR_PROCESS_EXIT)
783 message_loop_runner_->Quit();
786 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
787 RenderProcessHost* host) {
788 render_process_host_ = NULL;
789 if (type_ == WATCH_FOR_HOST_DESTRUCTION)
790 message_loop_runner_->Quit();
793 DOMMessageQueue::DOMMessageQueue() {
794 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
795 NotificationService::AllSources());
798 DOMMessageQueue::~DOMMessageQueue() {}
800 void DOMMessageQueue::Observe(int type,
801 const NotificationSource& source,
802 const NotificationDetails& details) {
803 Details<DomOperationNotificationDetails> dom_op_details(details);
804 message_queue_.push(dom_op_details->json);
805 if (message_loop_runner_.get())
806 message_loop_runner_->Quit();
809 void DOMMessageQueue::ClearQueue() {
810 message_queue_ = std::queue<std::string>();
813 bool DOMMessageQueue::WaitForMessage(std::string* message) {
815 if (message_queue_.empty()) {
816 // This will be quit when a new message comes in.
817 message_loop_runner_ = new MessageLoopRunner;
818 message_loop_runner_->Run();
820 // The queue should not be empty, unless we were quit because of a timeout.
821 if (message_queue_.empty())
823 *message = message_queue_.front();
824 message_queue_.pop();
828 } // namespace content