Home | History | Annotate | Download | only in test
      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 "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 
     34 static const int kDefaultWsPort = 8880;
     35 
     36 namespace content {
     37 namespace {
     38 
     39 class 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 RenderProcessGone(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.
     81 bool 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.
     89 bool 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 
    120 void 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 
    155 void GetCookiesCallback(std::string* cookies_out,
    156                         base::WaitableEvent* event,
    157                         const std::string& cookies) {
    158   *cookies_out = cookies;
    159   event->Signal();
    160 }
    161 
    162 void 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 
    173 void SetCookieCallback(bool* result,
    174                        base::WaitableEvent* event,
    175                        bool success) {
    176   *result = success;
    177   event->Signal();
    178 }
    179 
    180 void 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 
    195 GURL 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 
    206 void 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 
    217 void 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 
    226 void 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 
    234 void 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 
    255 void 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 
    265 void 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 
    290 namespace internal {
    291 
    292 ToRenderViewHost::ToRenderViewHost(WebContents* web_contents)
    293     : render_view_host_(web_contents->GetRenderViewHost()) {
    294 }
    295 
    296 ToRenderViewHost::ToRenderViewHost(RenderViewHost* render_view_host)
    297     : render_view_host_(render_view_host) {
    298 }
    299 
    300 }  // namespace internal
    301 
    302 bool 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 
    311 bool 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 
    325 bool 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 
    339 bool 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 
    353 bool ExecuteScript(const internal::ToRenderViewHost& adapter,
    354                    const std::string& script) {
    355   return ExecuteScriptInFrame(adapter, std::string(), script);
    356 }
    357 
    358 bool ExecuteScriptAndExtractInt(const internal::ToRenderViewHost& adapter,
    359                                 const std::string& script, int* result) {
    360   return ExecuteScriptInFrameAndExtractInt(adapter, std::string(), script,
    361                                            result);
    362 }
    363 
    364 bool ExecuteScriptAndExtractBool(const internal::ToRenderViewHost& adapter,
    365                                  const std::string& script, bool* result) {
    366   return ExecuteScriptInFrameAndExtractBool(adapter, std::string(), script,
    367                                             result);
    368 }
    369 
    370 bool 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 
    377 std::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 
    391 bool 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 
    407 TitleWatcher::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 
    429 void TitleWatcher::AlsoWaitForTitle(const string16& expected_title) {
    430   expected_titles_.push_back(expected_title);
    431 }
    432 
    433 TitleWatcher::~TitleWatcher() {
    434 }
    435 
    436 const 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 
    445 void 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 
    474 DOMMessageQueue::DOMMessageQueue() : waiting_for_message_(false) {
    475   registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
    476                  NotificationService::AllSources());
    477 }
    478 
    479 DOMMessageQueue::~DOMMessageQueue() {}
    480 
    481 void 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 
    493 void DOMMessageQueue::ClearQueue() {
    494   message_queue_ = std::queue<std::string>();
    495 }
    496 
    497 bool 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