browser_test_utils.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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_util.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 "net/base/net_util.h"
28#include "net/cookies/cookie_store.h"
29#include "net/test/python_utils.h"
30#include "net/url_request/url_request_context.h"
31#include "net/url_request/url_request_context_getter.h"
32#include "testing/gtest/include/gtest/gtest.h"
33
34static const int kDefaultWsPort = 8880;
35
36namespace content {
37namespace {
38
39class DOMOperationObserver : public NotificationObserver,
40                             public WebContentsObserver {
41 public:
42  explicit DOMOperationObserver(RenderViewHost* rvh)
43      : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
44        did_respond_(false) {
45    registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
46                   Source<RenderViewHost>(rvh));
47    message_loop_runner_ = new MessageLoopRunner;
48  }
49
50  virtual void Observe(int type,
51                       const NotificationSource& source,
52                       const NotificationDetails& details) OVERRIDE {
53    DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
54    Details<DomOperationNotificationDetails> dom_op_details(details);
55    response_ = dom_op_details->json;
56    did_respond_ = true;
57    message_loop_runner_->Quit();
58  }
59
60  // Overridden from WebContentsObserver:
61  virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE {
62    message_loop_runner_->Quit();
63  }
64
65  bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
66    message_loop_runner_->Run();
67    *response = response_;
68    return did_respond_;
69  }
70
71 private:
72  NotificationRegistrar registrar_;
73  std::string response_;
74  bool did_respond_;
75  scoped_refptr<MessageLoopRunner> message_loop_runner_;
76
77  DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
78};
79
80// Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
81bool ExecuteScriptHelper(RenderViewHost* render_view_host,
82                         const std::string& frame_xpath,
83                         const std::string& original_script,
84                         scoped_ptr<Value>* result) WARN_UNUSED_RESULT;
85
86// Executes the passed |original_script| in the frame pointed to by
87// |frame_xpath|.  If |result| is not NULL, stores the value that the evaluation
88// of the script in |result|.  Returns true on success.
89bool ExecuteScriptHelper(RenderViewHost* render_view_host,
90                         const std::string& frame_xpath,
91                         const std::string& original_script,
92                         scoped_ptr<Value>* result) {
93  // TODO(jcampan): we should make the domAutomationController not require an
94  //                automation id.
95  std::string script =
96      "window.domAutomationController.setAutomationId(0);" + original_script;
97  DOMOperationObserver dom_op_observer(render_view_host);
98  render_view_host->ExecuteJavascriptInWebFrame(UTF8ToUTF16(frame_xpath),
99                                                UTF8ToUTF16(script));
100  std::string json;
101  if (!dom_op_observer.WaitAndGetResponse(&json)) {
102    DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
103    return false;
104  }
105
106  // Nothing more to do for callers that ignore the returned JS value.
107  if (!result)
108    return true;
109
110  base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
111  result->reset(reader.ReadToValue(json));
112  if (!result->get()) {
113    DLOG(ERROR) << reader.GetErrorMessage();
114    return false;
115  }
116
117  return true;
118}
119
120void BuildSimpleWebKeyEvent(WebKit::WebInputEvent::Type type,
121                            ui::KeyboardCode key,
122                            bool control,
123                            bool shift,
124                            bool alt,
125                            bool command,
126                            NativeWebKeyboardEvent* event) {
127  event->nativeKeyCode = 0;
128  event->windowsKeyCode = key;
129  event->setKeyIdentifierFromWindowsKeyCode();
130  event->type = type;
131  event->modifiers = 0;
132  event->isSystemKey = false;
133  event->timeStampSeconds = base::Time::Now().ToDoubleT();
134  event->skip_in_browser = true;
135
136  if (type == WebKit::WebInputEvent::Char ||
137      type == WebKit::WebInputEvent::RawKeyDown) {
138    event->text[0] = key;
139    event->unmodifiedText[0] = key;
140  }
141
142  if (control)
143    event->modifiers |= WebKit::WebInputEvent::ControlKey;
144
145  if (shift)
146    event->modifiers |= WebKit::WebInputEvent::ShiftKey;
147
148  if (alt)
149    event->modifiers |= WebKit::WebInputEvent::AltKey;
150
151  if (command)
152    event->modifiers |= WebKit::WebInputEvent::MetaKey;
153}
154
155void GetCookiesCallback(std::string* cookies_out,
156                        base::WaitableEvent* event,
157                        const std::string& cookies) {
158  *cookies_out = cookies;
159  event->Signal();
160}
161
162void GetCookiesOnIOThread(const GURL& url,
163                          net::URLRequestContextGetter* context_getter,
164                          base::WaitableEvent* event,
165                          std::string* cookies) {
166  net::CookieStore* cookie_store =
167      context_getter->GetURLRequestContext()->cookie_store();
168  cookie_store->GetCookiesWithOptionsAsync(
169      url, net::CookieOptions(),
170      base::Bind(&GetCookiesCallback, cookies, event));
171}
172
173void SetCookieCallback(bool* result,
174                       base::WaitableEvent* event,
175                       bool success) {
176  *result = success;
177  event->Signal();
178}
179
180void SetCookieOnIOThread(const GURL& url,
181                         const std::string& value,
182                         net::URLRequestContextGetter* context_getter,
183                         base::WaitableEvent* event,
184                         bool* result) {
185  net::CookieStore* cookie_store =
186      context_getter->GetURLRequestContext()->cookie_store();
187  cookie_store->SetCookieWithOptionsAsync(
188      url, value, net::CookieOptions(),
189      base::Bind(&SetCookieCallback, result, event));
190}
191
192}  // namespace
193
194
195GURL GetFileUrlWithQuery(const base::FilePath& path,
196                         const std::string& query_string) {
197  GURL url = net::FilePathToFileURL(path);
198  if (!query_string.empty()) {
199    GURL::Replacements replacements;
200    replacements.SetQueryStr(query_string);
201    return url.ReplaceComponents(replacements);
202  }
203  return url;
204}
205
206void WaitForLoadStop(WebContents* web_contents) {
207    WindowedNotificationObserver load_stop_observer(
208    NOTIFICATION_LOAD_STOP,
209    Source<NavigationController>(&web_contents->GetController()));
210  // In many cases, the load may have finished before we get here.  Only wait if
211  // the tab still has a pending navigation.
212  if (!web_contents->IsLoading())
213    return;
214  load_stop_observer.Wait();
215}
216
217void CrashTab(WebContents* web_contents) {
218  RenderProcessHost* rph = web_contents->GetRenderProcessHost();
219  WindowedNotificationObserver observer(
220      NOTIFICATION_RENDERER_PROCESS_CLOSED,
221      Source<RenderProcessHost>(rph));
222  base::KillProcess(rph->GetHandle(), 0, false);
223  observer.Wait();
224}
225
226void SimulateMouseClick(WebContents* web_contents,
227                        int modifiers,
228                        WebKit::WebMouseEvent::Button button) {
229  int x = web_contents->GetView()->GetContainerSize().width() / 2;
230  int y = web_contents->GetView()->GetContainerSize().height() / 2;
231  SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
232}
233
234void SimulateMouseClickAt(WebContents* web_contents,
235                          int modifiers,
236                          WebKit::WebMouseEvent::Button button,
237                          const gfx::Point& point) {
238  WebKit::WebMouseEvent mouse_event;
239  mouse_event.type = WebKit::WebInputEvent::MouseDown;
240  mouse_event.button = button;
241  mouse_event.x = point.x();
242  mouse_event.y = point.y();
243  mouse_event.modifiers = modifiers;
244  // Mac needs globalX/globalY for events to plugins.
245  gfx::Rect offset;
246  web_contents->GetView()->GetContainerBounds(&offset);
247  mouse_event.globalX = point.x() + offset.x();
248  mouse_event.globalY = point.y() + offset.y();
249  mouse_event.clickCount = 1;
250  web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
251  mouse_event.type = WebKit::WebInputEvent::MouseUp;
252  web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
253}
254
255void SimulateMouseEvent(WebContents* web_contents,
256                        WebKit::WebInputEvent::Type type,
257                        const gfx::Point& point) {
258  WebKit::WebMouseEvent mouse_event;
259  mouse_event.type = type;
260  mouse_event.x = point.x();
261  mouse_event.y = point.y();
262  web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
263}
264
265void SimulateKeyPress(WebContents* web_contents,
266                      ui::KeyboardCode key,
267                      bool control,
268                      bool shift,
269                      bool alt,
270                      bool command) {
271  NativeWebKeyboardEvent event_down;
272  BuildSimpleWebKeyEvent(
273      WebKit::WebInputEvent::RawKeyDown, key, control, shift, alt, command,
274      &event_down);
275  web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event_down);
276
277  NativeWebKeyboardEvent char_event;
278  BuildSimpleWebKeyEvent(
279      WebKit::WebInputEvent::Char, key, control, shift, alt, command,
280      &char_event);
281  web_contents->GetRenderViewHost()->ForwardKeyboardEvent(char_event);
282
283  NativeWebKeyboardEvent event_up;
284  BuildSimpleWebKeyEvent(
285      WebKit::WebInputEvent::KeyUp, key, control, shift, alt, command,
286      &event_up);
287  web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event_up);
288}
289
290namespace internal {
291
292ToRenderViewHost::ToRenderViewHost(WebContents* web_contents)
293    : render_view_host_(web_contents->GetRenderViewHost()) {
294}
295
296ToRenderViewHost::ToRenderViewHost(RenderViewHost* render_view_host)
297    : render_view_host_(render_view_host) {
298}
299
300}  // namespace internal
301
302bool ExecuteScriptInFrame(const internal::ToRenderViewHost& adapter,
303                          const std::string& frame_xpath,
304                          const std::string& original_script) {
305  std::string script =
306      original_script + ";window.domAutomationController.send(0);";
307  return ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
308                             NULL);
309}
310
311bool ExecuteScriptInFrameAndExtractInt(
312    const internal::ToRenderViewHost& adapter,
313    const std::string& frame_xpath,
314    const std::string& script,
315    int* result) {
316  DCHECK(result);
317  scoped_ptr<Value> value;
318  if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
319                           &value) || !value.get())
320    return false;
321
322  return value->GetAsInteger(result);
323}
324
325bool ExecuteScriptInFrameAndExtractBool(
326    const internal::ToRenderViewHost& adapter,
327    const std::string& frame_xpath,
328    const std::string& script,
329    bool* result) {
330  DCHECK(result);
331  scoped_ptr<Value> value;
332  if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
333                           &value) || !value.get())
334    return false;
335
336  return value->GetAsBoolean(result);
337}
338
339bool ExecuteScriptInFrameAndExtractString(
340    const internal::ToRenderViewHost& adapter,
341    const std::string& frame_xpath,
342    const std::string& script,
343    std::string* result) {
344  DCHECK(result);
345  scoped_ptr<Value> value;
346  if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
347                           &value) || !value.get())
348    return false;
349
350  return value->GetAsString(result);
351}
352
353bool ExecuteScript(const internal::ToRenderViewHost& adapter,
354                   const std::string& script) {
355  return ExecuteScriptInFrame(adapter, std::string(), script);
356}
357
358bool ExecuteScriptAndExtractInt(const internal::ToRenderViewHost& adapter,
359                                const std::string& script, int* result) {
360  return ExecuteScriptInFrameAndExtractInt(adapter, std::string(), script,
361                                           result);
362}
363
364bool ExecuteScriptAndExtractBool(const internal::ToRenderViewHost& adapter,
365                                 const std::string& script, bool* result) {
366  return ExecuteScriptInFrameAndExtractBool(adapter, std::string(), script,
367                                            result);
368}
369
370bool ExecuteScriptAndExtractString(const internal::ToRenderViewHost& adapter,
371                                   const std::string& script,
372                                   std::string* result) {
373  return ExecuteScriptInFrameAndExtractString(adapter, std::string(), script,
374                                              result);
375}
376
377std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
378  std::string cookies;
379  base::WaitableEvent event(true, false);
380  net::URLRequestContextGetter* context_getter =
381      browser_context->GetRequestContext();
382
383  BrowserThread::PostTask(
384      BrowserThread::IO, FROM_HERE,
385      base::Bind(&GetCookiesOnIOThread, url,
386                 make_scoped_refptr(context_getter), &event, &cookies));
387  event.Wait();
388  return cookies;
389}
390
391bool SetCookie(BrowserContext* browser_context,
392               const GURL& url,
393               const std::string& value) {
394  bool result = false;
395  base::WaitableEvent event(true, false);
396  net::URLRequestContextGetter* context_getter =
397      browser_context->GetRequestContext();
398
399  BrowserThread::PostTask(
400      BrowserThread::IO, FROM_HERE,
401      base::Bind(&SetCookieOnIOThread, url, value,
402                 make_scoped_refptr(context_getter), &event, &result));
403  event.Wait();
404  return result;
405}
406
407TitleWatcher::TitleWatcher(WebContents* web_contents,
408                           const string16& expected_title)
409    : web_contents_(web_contents),
410      expected_title_observed_(false),
411      quit_loop_on_observation_(false) {
412  EXPECT_TRUE(web_contents != NULL);
413  expected_titles_.push_back(expected_title);
414  notification_registrar_.Add(this,
415                              NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
416                              Source<WebContents>(web_contents));
417
418  // When navigating through the history, the restored NavigationEntry's title
419  // will be used. If the entry ends up having the same title after we return
420  // to it, as will usually be the case, the
421  // NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED will then be suppressed, since the
422  // NavigationEntry's title hasn't changed.
423  notification_registrar_.Add(
424      this,
425      NOTIFICATION_LOAD_STOP,
426      Source<NavigationController>(&web_contents->GetController()));
427}
428
429void TitleWatcher::AlsoWaitForTitle(const string16& expected_title) {
430  expected_titles_.push_back(expected_title);
431}
432
433TitleWatcher::~TitleWatcher() {
434}
435
436const string16& TitleWatcher::WaitAndGetTitle() {
437  if (expected_title_observed_)
438    return observed_title_;
439  quit_loop_on_observation_ = true;
440  message_loop_runner_ = new MessageLoopRunner;
441  message_loop_runner_->Run();
442  return observed_title_;
443}
444
445void TitleWatcher::Observe(int type,
446                           const NotificationSource& source,
447                           const NotificationDetails& details) {
448  if (type == NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) {
449    WebContents* source_contents = Source<WebContents>(source).ptr();
450    ASSERT_EQ(web_contents_, source_contents);
451  } else if (type == NOTIFICATION_LOAD_STOP) {
452    NavigationController* controller =
453        Source<NavigationController>(source).ptr();
454    ASSERT_EQ(&web_contents_->GetController(), controller);
455  } else {
456    FAIL() << "Unexpected notification received.";
457  }
458
459  std::vector<string16>::const_iterator it =
460      std::find(expected_titles_.begin(),
461                expected_titles_.end(),
462                web_contents_->GetTitle());
463  if (it == expected_titles_.end())
464    return;
465  observed_title_ = *it;
466  expected_title_observed_ = true;
467  if (quit_loop_on_observation_) {
468    // Only call Quit once, on first Observe:
469    quit_loop_on_observation_ = false;
470    message_loop_runner_->Quit();
471  }
472}
473
474DOMMessageQueue::DOMMessageQueue() : waiting_for_message_(false) {
475  registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
476                 NotificationService::AllSources());
477}
478
479DOMMessageQueue::~DOMMessageQueue() {}
480
481void DOMMessageQueue::Observe(int type,
482                              const NotificationSource& source,
483                              const NotificationDetails& details) {
484  Details<DomOperationNotificationDetails> dom_op_details(details);
485  Source<RenderViewHost> sender(source);
486  message_queue_.push(dom_op_details->json);
487  if (waiting_for_message_) {
488    waiting_for_message_ = false;
489    message_loop_runner_->Quit();
490  }
491}
492
493void DOMMessageQueue::ClearQueue() {
494  message_queue_ = std::queue<std::string>();
495}
496
497bool DOMMessageQueue::WaitForMessage(std::string* message) {
498  if (message_queue_.empty()) {
499    waiting_for_message_ = true;
500    // This will be quit when a new message comes in.
501    message_loop_runner_ = new MessageLoopRunner;
502    message_loop_runner_->Run();
503  }
504  // The queue should not be empty, unless we were quit because of a timeout.
505  if (message_queue_.empty())
506    return false;
507  if (message)
508    *message = message_queue_.front();
509  message_queue_.pop();
510  return true;
511}
512
513}  // namespace content
514