Home | History | Annotate | Download | only in chromedriver
      1 // Copyright (c) 2013 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 "chrome/test/chromedriver/session_commands.h"
      6 
      7 #include <list>
      8 
      9 #include "base/bind.h"
     10 #include "base/callback.h"
     11 #include "base/file_util.h"
     12 #include "base/logging.h"  // For CHECK macros.
     13 #include "base/memory/ref_counted.h"
     14 #include "base/message_loop/message_loop_proxy.h"
     15 #include "base/synchronization/lock.h"
     16 #include "base/synchronization/waitable_event.h"
     17 #include "base/values.h"
     18 #include "chrome/test/chromedriver/basic_types.h"
     19 #include "chrome/test/chromedriver/chrome/automation_extension.h"
     20 #include "chrome/test/chromedriver/chrome/chrome.h"
     21 #include "chrome/test/chromedriver/chrome/geoposition.h"
     22 #include "chrome/test/chromedriver/chrome/status.h"
     23 #include "chrome/test/chromedriver/chrome/web_view.h"
     24 #include "chrome/test/chromedriver/logging.h"
     25 #include "chrome/test/chromedriver/session.h"
     26 #include "chrome/test/chromedriver/util.h"
     27 
     28 namespace {
     29 
     30 const char kWindowHandlePrefix[] = "CDwindow-";
     31 
     32 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
     33   return kWindowHandlePrefix + web_view_id;
     34 }
     35 
     36 bool WindowHandleToWebViewId(const std::string& window_handle,
     37                              std::string* web_view_id) {
     38   if (window_handle.find(kWindowHandlePrefix) != 0u)
     39     return false;
     40   *web_view_id = window_handle.substr(
     41       std::string(kWindowHandlePrefix).length());
     42   return true;
     43 }
     44 
     45 }  // namespace
     46 
     47 Status ExecuteQuit(
     48     bool allow_detach,
     49     Session* session,
     50     const base::DictionaryValue& params,
     51     scoped_ptr<base::Value>* value) {
     52   if (allow_detach && session->detach) {
     53     return Status(kOk);
     54   } else {
     55     session->quit = true;
     56     return session->chrome->Quit();
     57   }
     58 }
     59 
     60 Status ExecuteGetSessionCapabilities(
     61     Session* session,
     62     const base::DictionaryValue& params,
     63     scoped_ptr<base::Value>* value) {
     64   value->reset(session->capabilities->DeepCopy());
     65   return Status(kOk);
     66 }
     67 
     68 Status ExecuteGetCurrentWindowHandle(
     69     Session* session,
     70     const base::DictionaryValue& params,
     71     scoped_ptr<base::Value>* value) {
     72   WebView* web_view = NULL;
     73   Status status = session->GetTargetWindow(&web_view);
     74   if (status.IsError())
     75     return status;
     76 
     77   value->reset(new StringValue(WebViewIdToWindowHandle(web_view->GetId())));
     78   return Status(kOk);
     79 }
     80 
     81 Status ExecuteClose(
     82     Session* session,
     83     const base::DictionaryValue& params,
     84     scoped_ptr<base::Value>* value) {
     85   std::list<std::string> web_view_ids;
     86   Status status = session->chrome->GetWebViewIds(&web_view_ids);
     87   if (status.IsError())
     88     return status;
     89   bool is_last_web_view = web_view_ids.size() == 1u;
     90   web_view_ids.clear();
     91 
     92   WebView* web_view = NULL;
     93   status = session->GetTargetWindow(&web_view);
     94   if (status.IsError())
     95     return status;
     96 
     97   status = session->chrome->CloseWebView(web_view->GetId());
     98   if (status.IsError())
     99     return status;
    100 
    101   status = session->chrome->GetWebViewIds(&web_view_ids);
    102   if ((status.code() == kChromeNotReachable && is_last_web_view) ||
    103       (status.IsOk() && web_view_ids.empty())) {
    104     // If no window is open, close is the equivalent of calling "quit".
    105     session->quit = true;
    106     return session->chrome->Quit();
    107   }
    108 
    109   return status;
    110 }
    111 
    112 Status ExecuteGetWindowHandles(
    113     Session* session,
    114     const base::DictionaryValue& params,
    115     scoped_ptr<base::Value>* value) {
    116   std::list<std::string> web_view_ids;
    117   Status status = session->chrome->GetWebViewIds(&web_view_ids);
    118   if (status.IsError())
    119     return status;
    120   scoped_ptr<base::ListValue> window_ids(new base::ListValue());
    121   for (std::list<std::string>::const_iterator it = web_view_ids.begin();
    122        it != web_view_ids.end(); ++it) {
    123     window_ids->AppendString(WebViewIdToWindowHandle(*it));
    124   }
    125   value->reset(window_ids.release());
    126   return Status(kOk);
    127 }
    128 
    129 Status ExecuteSwitchToWindow(
    130     Session* session,
    131     const base::DictionaryValue& params,
    132     scoped_ptr<base::Value>* value) {
    133   std::string name;
    134   if (!params.GetString("name", &name) || name.empty())
    135     return Status(kUnknownError, "'name' must be a nonempty string");
    136 
    137   std::list<std::string> web_view_ids;
    138   Status status = session->chrome->GetWebViewIds(&web_view_ids);
    139   if (status.IsError())
    140     return status;
    141 
    142   std::string web_view_id;
    143   bool found = false;
    144   if (WindowHandleToWebViewId(name, &web_view_id)) {
    145     // Check if any web_view matches |web_view_id|.
    146     for (std::list<std::string>::const_iterator it = web_view_ids.begin();
    147          it != web_view_ids.end(); ++it) {
    148       if (*it == web_view_id) {
    149         found = true;
    150         break;
    151       }
    152     }
    153   } else {
    154     // Check if any of the tab window names match |name|.
    155     const char* kGetWindowNameScript = "function() { return window.name; }";
    156     base::ListValue args;
    157     for (std::list<std::string>::const_iterator it = web_view_ids.begin();
    158          it != web_view_ids.end(); ++it) {
    159       scoped_ptr<base::Value> result;
    160       WebView* web_view;
    161       status = session->chrome->GetWebViewById(*it, &web_view);
    162       if (status.IsError())
    163         return status;
    164       status = web_view->ConnectIfNecessary();
    165       if (status.IsError())
    166         return status;
    167       status = web_view->CallFunction(
    168           std::string(), kGetWindowNameScript, args, &result);
    169       if (status.IsError())
    170         return status;
    171       std::string window_name;
    172       if (!result->GetAsString(&window_name))
    173         return Status(kUnknownError, "failed to get window name");
    174       if (window_name == name) {
    175         web_view_id = *it;
    176         found = true;
    177         break;
    178       }
    179     }
    180   }
    181 
    182   if (!found)
    183     return Status(kNoSuchWindow);
    184 
    185   if (session->overridden_geoposition) {
    186     WebView* web_view;
    187     status = session->chrome->GetWebViewById(web_view_id, &web_view);
    188     if (status.IsError())
    189       return status;
    190     status = web_view->ConnectIfNecessary();
    191     if (status.IsError())
    192       return status;
    193     status = web_view->OverrideGeolocation(*session->overridden_geoposition);
    194     if (status.IsError())
    195       return status;
    196   }
    197 
    198   session->window = web_view_id;
    199   session->SwitchToTopFrame();
    200   session->mouse_position = WebPoint(0, 0);
    201   return Status(kOk);
    202 }
    203 
    204 Status ExecuteSetTimeout(
    205     Session* session,
    206     const base::DictionaryValue& params,
    207     scoped_ptr<base::Value>* value) {
    208   double ms_double;
    209   if (!params.GetDouble("ms", &ms_double))
    210     return Status(kUnknownError, "'ms' must be a double");
    211   std::string type;
    212   if (!params.GetString("type", &type))
    213     return Status(kUnknownError, "'type' must be a string");
    214 
    215   int ms = static_cast<int>(ms_double);
    216   // TODO(frankf): implicit and script timeout should be cleared
    217   // if negative timeout is specified.
    218   if (type == "implicit")
    219     session->implicit_wait = ms;
    220   else if (type == "script")
    221     session->script_timeout = ms;
    222   else if (type == "page load")
    223     session->page_load_timeout =
    224         ((ms < 0) ? Session::kDefaultPageLoadTimeoutMs : ms);
    225   else
    226     return Status(kUnknownError, "unknown type of timeout:" + type);
    227   return Status(kOk);
    228 }
    229 
    230 Status ExecuteSetScriptTimeout(
    231     Session* session,
    232     const base::DictionaryValue& params,
    233     scoped_ptr<base::Value>* value) {
    234   double ms;
    235   if (!params.GetDouble("ms", &ms) || ms < 0)
    236     return Status(kUnknownError, "'ms' must be a non-negative number");
    237   session->script_timeout = static_cast<int>(ms);
    238   return Status(kOk);
    239 }
    240 
    241 Status ExecuteImplicitlyWait(
    242     Session* session,
    243     const base::DictionaryValue& params,
    244     scoped_ptr<base::Value>* value) {
    245   double ms;
    246   if (!params.GetDouble("ms", &ms) || ms < 0)
    247     return Status(kUnknownError, "'ms' must be a non-negative number");
    248   session->implicit_wait = static_cast<int>(ms);
    249   return Status(kOk);
    250 }
    251 
    252 Status ExecuteIsLoading(
    253     Session* session,
    254     const base::DictionaryValue& params,
    255     scoped_ptr<base::Value>* value) {
    256   WebView* web_view = NULL;
    257   Status status = session->GetTargetWindow(&web_view);
    258   if (status.IsError())
    259     return status;
    260 
    261   status = web_view->ConnectIfNecessary();
    262   if (status.IsError())
    263     return status;
    264 
    265   bool is_pending;
    266   status = web_view->IsPendingNavigation(
    267       session->GetCurrentFrameId(), &is_pending);
    268   if (status.IsError())
    269     return status;
    270   value->reset(new base::FundamentalValue(is_pending));
    271   return Status(kOk);
    272 }
    273 
    274 Status ExecuteGetLocation(
    275     Session* session,
    276     const base::DictionaryValue& params,
    277     scoped_ptr<base::Value>* value) {
    278   if (!session->overridden_geoposition) {
    279     return Status(kUnknownError,
    280                   "Location must be set before it can be retrieved");
    281   }
    282   base::DictionaryValue location;
    283   location.SetDouble("latitude", session->overridden_geoposition->latitude);
    284   location.SetDouble("longitude", session->overridden_geoposition->longitude);
    285   location.SetDouble("accuracy", session->overridden_geoposition->accuracy);
    286   // Set a dummy altitude to make WebDriver clients happy.
    287   // https://code.google.com/p/chromedriver/issues/detail?id=281
    288   location.SetDouble("altitude", 0);
    289   value->reset(location.DeepCopy());
    290   return Status(kOk);
    291 }
    292 
    293 Status ExecuteGetWindowPosition(
    294     Session* session,
    295     const base::DictionaryValue& params,
    296     scoped_ptr<base::Value>* value) {
    297   AutomationExtension* extension = NULL;
    298   Status status = session->chrome->GetAutomationExtension(&extension);
    299   if (status.IsError())
    300     return status;
    301 
    302   int x, y;
    303   status = extension->GetWindowPosition(&x, &y);
    304   if (status.IsError())
    305     return status;
    306 
    307   base::DictionaryValue position;
    308   position.SetInteger("x", x);
    309   position.SetInteger("y", y);
    310   value->reset(position.DeepCopy());
    311   return Status(kOk);
    312 }
    313 
    314 Status ExecuteSetWindowPosition(
    315     Session* session,
    316     const base::DictionaryValue& params,
    317     scoped_ptr<base::Value>* value) {
    318   double x, y;
    319   if (!params.GetDouble("x", &x) || !params.GetDouble("y", &y))
    320     return Status(kUnknownError, "missing or invalid 'x' or 'y'");
    321   AutomationExtension* extension = NULL;
    322   Status status = session->chrome->GetAutomationExtension(&extension);
    323   if (status.IsError())
    324     return status;
    325 
    326   return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
    327 }
    328 
    329 Status ExecuteGetWindowSize(
    330     Session* session,
    331     const base::DictionaryValue& params,
    332     scoped_ptr<base::Value>* value) {
    333   AutomationExtension* extension = NULL;
    334   Status status = session->chrome->GetAutomationExtension(&extension);
    335   if (status.IsError())
    336     return status;
    337 
    338   int width, height;
    339   status = extension->GetWindowSize(&width, &height);
    340   if (status.IsError())
    341     return status;
    342 
    343   base::DictionaryValue size;
    344   size.SetInteger("width", width);
    345   size.SetInteger("height", height);
    346   value->reset(size.DeepCopy());
    347   return Status(kOk);
    348 }
    349 
    350 Status ExecuteSetWindowSize(
    351     Session* session,
    352     const base::DictionaryValue& params,
    353     scoped_ptr<base::Value>* value) {
    354   double width, height;
    355   if (!params.GetDouble("width", &width) ||
    356       !params.GetDouble("height", &height))
    357     return Status(kUnknownError, "missing or invalid 'width' or 'height'");
    358   AutomationExtension* extension = NULL;
    359   Status status = session->chrome->GetAutomationExtension(&extension);
    360   if (status.IsError())
    361     return status;
    362 
    363   return extension->SetWindowSize(
    364       static_cast<int>(width), static_cast<int>(height));
    365 }
    366 
    367 Status ExecuteMaximizeWindow(
    368     Session* session,
    369     const base::DictionaryValue& params,
    370     scoped_ptr<base::Value>* value) {
    371   AutomationExtension* extension = NULL;
    372   Status status = session->chrome->GetAutomationExtension(&extension);
    373   if (status.IsError())
    374     return status;
    375 
    376   return extension->MaximizeWindow();
    377 }
    378 
    379 Status ExecuteGetAvailableLogTypes(
    380     Session* session,
    381     const base::DictionaryValue& params,
    382     scoped_ptr<base::Value>* value) {
    383   scoped_ptr<base::ListValue> types(new base::ListValue());
    384   for (ScopedVector<WebDriverLog>::const_iterator log
    385        = session->devtools_logs.begin();
    386        log != session->devtools_logs.end(); ++log) {
    387     types->AppendString((*log)->GetType());
    388   }
    389   value->reset(types.release());
    390   return Status(kOk);
    391 }
    392 
    393 Status ExecuteGetLog(
    394     Session* session,
    395     const base::DictionaryValue& params,
    396     scoped_ptr<base::Value>* value) {
    397   std::string log_type;
    398   if (!params.GetString("type", &log_type)) {
    399     return Status(kUnknownError, "missing or invalid 'type'");
    400   }
    401   for (ScopedVector<WebDriverLog>::const_iterator log
    402        = session->devtools_logs.begin();
    403        log != session->devtools_logs.end(); ++log) {
    404     if (log_type == (*log)->GetType()) {
    405       scoped_ptr<base::ListValue> log_entries = (*log)->GetAndClearEntries();
    406       value->reset(log_entries.release());
    407       return Status(kOk);
    408     }
    409   }
    410   return Status(kUnknownError, "log type '" + log_type + "' not found");
    411 }
    412 
    413 Status ExecuteUploadFile(
    414     Session* session,
    415     const base::DictionaryValue& params,
    416     scoped_ptr<base::Value>* value) {
    417     std::string base64_zip_data;
    418   if (!params.GetString("file", &base64_zip_data))
    419     return Status(kUnknownError, "missing or invalid 'file'");
    420   std::string zip_data;
    421   if (!Base64Decode(base64_zip_data, &zip_data))
    422     return Status(kUnknownError, "unable to decode 'file'");
    423 
    424   if (!session->temp_dir.IsValid()) {
    425     if (!session->temp_dir.CreateUniqueTempDir())
    426       return Status(kUnknownError, "unable to create temp dir");
    427   }
    428   base::FilePath upload_dir;
    429   if (!file_util::CreateTemporaryDirInDir(
    430           session->temp_dir.path(), FILE_PATH_LITERAL("upload"), &upload_dir)) {
    431     return Status(kUnknownError, "unable to create temp dir");
    432   }
    433   std::string error_msg;
    434   base::FilePath upload;
    435   Status status = UnzipSoleFile(upload_dir, zip_data, &upload);
    436   if (status.IsError())
    437     return Status(kUnknownError, "unable to unzip 'file'", status);
    438 
    439   value->reset(new base::StringValue(upload.value()));
    440   return Status(kOk);
    441 }
    442