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:
DOMOperationObserver(RenderViewHost * rvh)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
Observe(int type,const NotificationSource & source,const NotificationDetails & details)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:
RenderProcessGone(base::TerminationStatus status)62 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
63 message_loop_runner_->Quit();
64 }
65
WaitAndGetResponse(std::string * response)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.
ExecuteScriptHelper(RenderViewHost * render_view_host,const std::string & frame_xpath,const std::string & original_script,scoped_ptr<Value> * result)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
BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type,ui::KeyboardCode key_code,int native_key_code,int modifiers,NativeWebKeyboardEvent * event)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
InjectRawKeyEvent(WebContents * web_contents,blink::WebInputEvent::Type type,ui::KeyboardCode key_code,int native_key_code,int modifiers)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
GetCookiesCallback(std::string * cookies_out,base::WaitableEvent * event,const std::string & cookies)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
GetCookiesOnIOThread(const GURL & url,net::URLRequestContextGetter * context_getter,base::WaitableEvent * event,std::string * cookies)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
SetCookieCallback(bool * result,base::WaitableEvent * event,bool success)170 void SetCookieCallback(bool* result,
171 base::WaitableEvent* event,
172 bool success) {
173 *result = success;
174 event->Signal();
175 }
176
SetCookieOnIOThread(const GURL & url,const std::string & value,net::URLRequestContextGetter * context_getter,base::WaitableEvent * event,bool * result)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
GetFileUrlWithQuery(const base::FilePath & path,const std::string & query_string)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
WaitForLoadStop(WebContents * web_contents)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
CrashTab(WebContents * web_contents)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
SimulateMouseClick(WebContents * web_contents,int modifiers,blink::WebMouseEvent::Button button)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
SimulateMouseClickAt(WebContents * web_contents,int modifiers,blink::WebMouseEvent::Button button,const gfx::Point & point)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
SimulateMouseEvent(WebContents * web_contents,blink::WebInputEvent::Type type,const gfx::Point & point)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
SimulateKeyPress(WebContents * web_contents,ui::KeyboardCode key_code,bool control,bool shift,bool alt,bool command)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
SimulateKeyPressWithCode(WebContents * web_contents,ui::KeyboardCode key_code,const char * code,bool control,bool shift,bool alt,bool command)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
ToRenderViewHost(WebContents * web_contents)393 ToRenderViewHost::ToRenderViewHost(WebContents* web_contents)
394 : render_view_host_(web_contents->GetRenderViewHost()) {
395 }
396
ToRenderViewHost(RenderViewHost * render_view_host)397 ToRenderViewHost::ToRenderViewHost(RenderViewHost* render_view_host)
398 : render_view_host_(render_view_host) {
399 }
400
401 } // namespace internal
402
ExecuteScriptInFrame(const internal::ToRenderViewHost & adapter,const std::string & frame_xpath,const std::string & original_script)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
ExecuteScriptInFrameAndExtractInt(const internal::ToRenderViewHost & adapter,const std::string & frame_xpath,const std::string & script,int * result)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
ExecuteScriptInFrameAndExtractBool(const internal::ToRenderViewHost & adapter,const std::string & frame_xpath,const std::string & script,bool * result)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
ExecuteScriptInFrameAndExtractString(const internal::ToRenderViewHost & adapter,const std::string & frame_xpath,const std::string & script,std::string * result)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
ExecuteScript(const internal::ToRenderViewHost & adapter,const std::string & script)454 bool ExecuteScript(const internal::ToRenderViewHost& adapter,
455 const std::string& script) {
456 return ExecuteScriptInFrame(adapter, std::string(), script);
457 }
458
ExecuteScriptAndExtractInt(const internal::ToRenderViewHost & adapter,const std::string & script,int * result)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
ExecuteScriptAndExtractBool(const internal::ToRenderViewHost & adapter,const std::string & script,bool * result)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
ExecuteScriptAndExtractString(const internal::ToRenderViewHost & adapter,const std::string & script,std::string * result)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
ExecuteWebUIResourceTest(const internal::ToRenderViewHost & adapter,const std::vector<int> & js_resource_ids)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
GetCookies(BrowserContext * browser_context,const GURL & url)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
SetCookie(BrowserContext * browser_context,const GURL & url,const std::string & value)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
TitleWatcher(WebContents * web_contents,const base::string16 & expected_title)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
AlsoWaitForTitle(const base::string16 & expected_title)549 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
550 expected_titles_.push_back(expected_title);
551 }
552
~TitleWatcher()553 TitleWatcher::~TitleWatcher() {
554 }
555
WaitAndGetTitle()556 const base::string16& TitleWatcher::WaitAndGetTitle() {
557 message_loop_runner_->Run();
558 return observed_title_;
559 }
560
DidStopLoading(RenderViewHost * render_view_host)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
TitleWasSet(NavigationEntry * entry,bool explicit_set)569 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
570 TestTitle();
571 }
572
TestTitle()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
WebContentsDestroyedWatcher(WebContents * web_contents)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
~WebContentsDestroyedWatcher()592 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
593 }
594
Wait()595 void WebContentsDestroyedWatcher::Wait() {
596 message_loop_runner_->Run();
597 }
598
WebContentsDestroyed(WebContents * web_contents)599 void WebContentsDestroyedWatcher::WebContentsDestroyed(
600 WebContents* web_contents) {
601 message_loop_runner_->Quit();
602 }
603
DOMMessageQueue()604 DOMMessageQueue::DOMMessageQueue() : waiting_for_message_(false) {
605 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
606 NotificationService::AllSources());
607 }
608
~DOMMessageQueue()609 DOMMessageQueue::~DOMMessageQueue() {}
610
Observe(int type,const NotificationSource & source,const NotificationDetails & details)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
ClearQueue()623 void DOMMessageQueue::ClearQueue() {
624 message_queue_ = std::queue<std::string>();
625 }
626
WaitForMessage(std::string * message)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