• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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