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/files/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/capabilities.h"
     20 #include "chrome/test/chromedriver/chrome/automation_extension.h"
     21 #include "chrome/test/chromedriver/chrome/browser_info.h"
     22 #include "chrome/test/chromedriver/chrome/chrome.h"
     23 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
     24 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
     25 #include "chrome/test/chromedriver/chrome/device_manager.h"
     26 #include "chrome/test/chromedriver/chrome/devtools_event_listener.h"
     27 #include "chrome/test/chromedriver/chrome/geoposition.h"
     28 #include "chrome/test/chromedriver/chrome/status.h"
     29 #include "chrome/test/chromedriver/chrome/web_view.h"
     30 #include "chrome/test/chromedriver/chrome_launcher.h"
     31 #include "chrome/test/chromedriver/command_listener.h"
     32 #include "chrome/test/chromedriver/logging.h"
     33 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
     34 #include "chrome/test/chromedriver/session.h"
     35 #include "chrome/test/chromedriver/util.h"
     36 #include "chrome/test/chromedriver/version.h"
     37 
     38 namespace {
     39 
     40 const char kWindowHandlePrefix[] = "CDwindow-";
     41 
     42 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
     43   return kWindowHandlePrefix + web_view_id;
     44 }
     45 
     46 bool WindowHandleToWebViewId(const std::string& window_handle,
     47                              std::string* web_view_id) {
     48   if (window_handle.find(kWindowHandlePrefix) != 0u)
     49     return false;
     50   *web_view_id = window_handle.substr(
     51       std::string(kWindowHandlePrefix).length());
     52   return true;
     53 }
     54 
     55 }  // namespace
     56 
     57 InitSessionParams::InitSessionParams(
     58     scoped_refptr<URLRequestContextGetter> context_getter,
     59     const SyncWebSocketFactory& socket_factory,
     60     DeviceManager* device_manager,
     61     PortServer* port_server,
     62     PortManager* port_manager)
     63     : context_getter(context_getter),
     64       socket_factory(socket_factory),
     65       device_manager(device_manager),
     66       port_server(port_server),
     67       port_manager(port_manager) {}
     68 
     69 InitSessionParams::~InitSessionParams() {}
     70 
     71 namespace {
     72 
     73 scoped_ptr<base::DictionaryValue> CreateCapabilities(Chrome* chrome) {
     74   scoped_ptr<base::DictionaryValue> caps(new base::DictionaryValue());
     75   caps->SetString("browserName", "chrome");
     76   caps->SetString("version", chrome->GetBrowserInfo()->browser_version);
     77   caps->SetString("chrome.chromedriverVersion", kChromeDriverVersion);
     78   caps->SetString("platform", chrome->GetOperatingSystemName());
     79   caps->SetBoolean("javascriptEnabled", true);
     80   caps->SetBoolean("takesScreenshot", true);
     81   caps->SetBoolean("takesHeapSnapshot", true);
     82   caps->SetBoolean("handlesAlerts", true);
     83   caps->SetBoolean("databaseEnabled", false);
     84   caps->SetBoolean("locationContextEnabled", true);
     85   caps->SetBoolean("mobileEmulationEnabled",
     86                    chrome->IsMobileEmulationEnabled());
     87   caps->SetBoolean("applicationCacheEnabled", false);
     88   caps->SetBoolean("browserConnectionEnabled", false);
     89   caps->SetBoolean("cssSelectorsEnabled", true);
     90   caps->SetBoolean("webStorageEnabled", true);
     91   caps->SetBoolean("rotatable", false);
     92   caps->SetBoolean("acceptSslCerts", true);
     93   caps->SetBoolean("nativeEvents", true);
     94   scoped_ptr<base::DictionaryValue> chrome_caps(new base::DictionaryValue());
     95   if (chrome->GetAsDesktop()) {
     96     chrome_caps->SetString(
     97         "userDataDir",
     98         chrome->GetAsDesktop()->command().GetSwitchValueNative(
     99             "user-data-dir"));
    100   }
    101   caps->Set("chrome", chrome_caps.release());
    102   return caps.Pass();
    103 }
    104 
    105 Status InitSessionHelper(
    106     const InitSessionParams& bound_params,
    107     Session* session,
    108     const base::DictionaryValue& params,
    109     scoped_ptr<base::Value>* value) {
    110   session->driver_log.reset(
    111       new WebDriverLog(WebDriverLog::kDriverType, Log::kAll));
    112   const base::DictionaryValue* desired_caps;
    113   if (!params.GetDictionary("desiredCapabilities", &desired_caps))
    114     return Status(kUnknownError, "cannot find dict 'desiredCapabilities'");
    115 
    116   Capabilities capabilities;
    117   Status status = capabilities.Parse(*desired_caps);
    118   if (status.IsError())
    119     return status;
    120 
    121   Log::Level driver_level = Log::kWarning;
    122   if (capabilities.logging_prefs.count(WebDriverLog::kDriverType))
    123     driver_level = capabilities.logging_prefs[WebDriverLog::kDriverType];
    124   session->driver_log->set_min_level(driver_level);
    125 
    126   // Create Log's and DevToolsEventListener's for ones that are DevTools-based.
    127   // Session will own the Log's, Chrome will own the listeners.
    128   // Also create |CommandListener|s for the appropriate logs.
    129   ScopedVector<DevToolsEventListener> devtools_event_listeners;
    130   ScopedVector<CommandListener> command_listeners;
    131   status = CreateLogs(capabilities,
    132                       session,
    133                       &session->devtools_logs,
    134                       &devtools_event_listeners,
    135                       &command_listeners);
    136   if (status.IsError())
    137     return status;
    138 
    139   // |session| will own the |CommandListener|s.
    140   session->command_listeners.swap(command_listeners);
    141 
    142   status = LaunchChrome(bound_params.context_getter.get(),
    143                         bound_params.socket_factory,
    144                         bound_params.device_manager,
    145                         bound_params.port_server,
    146                         bound_params.port_manager,
    147                         capabilities,
    148                         devtools_event_listeners,
    149                         &session->chrome);
    150   if (status.IsError())
    151     return status;
    152 
    153   std::list<std::string> web_view_ids;
    154   status = session->chrome->GetWebViewIds(&web_view_ids);
    155   if (status.IsError() || web_view_ids.empty()) {
    156     return status.IsError() ? status :
    157         Status(kUnknownError, "unable to discover open window in chrome");
    158   }
    159 
    160   session->window = web_view_ids.front();
    161   session->detach = capabilities.detach;
    162   session->force_devtools_screenshot = capabilities.force_devtools_screenshot;
    163   session->capabilities = CreateCapabilities(session->chrome.get());
    164   value->reset(session->capabilities->DeepCopy());
    165   return Status(kOk);
    166 }
    167 
    168 }  // namespace
    169 
    170 Status ExecuteInitSession(
    171     const InitSessionParams& bound_params,
    172     Session* session,
    173     const base::DictionaryValue& params,
    174     scoped_ptr<base::Value>* value) {
    175   Status status = InitSessionHelper(bound_params, session, params, value);
    176   if (status.IsError()) {
    177     session->quit = true;
    178     if (session->chrome != NULL)
    179       session->chrome->Quit();
    180   }
    181   return status;
    182 }
    183 
    184 Status ExecuteQuit(
    185     bool allow_detach,
    186     Session* session,
    187     const base::DictionaryValue& params,
    188     scoped_ptr<base::Value>* value) {
    189   session->quit = true;
    190   if (allow_detach && session->detach)
    191     return Status(kOk);
    192   else
    193     return session->chrome->Quit();
    194 }
    195 
    196 Status ExecuteGetSessionCapabilities(
    197     Session* session,
    198     const base::DictionaryValue& params,
    199     scoped_ptr<base::Value>* value) {
    200   value->reset(session->capabilities->DeepCopy());
    201   return Status(kOk);
    202 }
    203 
    204 Status ExecuteGetCurrentWindowHandle(
    205     Session* session,
    206     const base::DictionaryValue& params,
    207     scoped_ptr<base::Value>* value) {
    208   WebView* web_view = NULL;
    209   Status status = session->GetTargetWindow(&web_view);
    210   if (status.IsError())
    211     return status;
    212 
    213   value->reset(
    214       new base::StringValue(WebViewIdToWindowHandle(web_view->GetId())));
    215   return Status(kOk);
    216 }
    217 
    218 Status ExecuteLaunchApp(
    219     Session* session,
    220     const base::DictionaryValue& params,
    221     scoped_ptr<base::Value>* value) {
    222   std::string id;
    223   if (!params.GetString("id", &id))
    224     return Status(kUnknownError, "'id' must be a string");
    225 
    226   if (!session->chrome->GetAsDesktop())
    227     return Status(kUnknownError,
    228                   "apps can only be launched on desktop platforms");
    229 
    230   AutomationExtension* extension = NULL;
    231   Status status =
    232       session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
    233   if (status.IsError())
    234     return status;
    235 
    236   return extension->LaunchApp(id);
    237 }
    238 
    239 Status ExecuteClose(
    240     Session* session,
    241     const base::DictionaryValue& params,
    242     scoped_ptr<base::Value>* value) {
    243   std::list<std::string> web_view_ids;
    244   Status status = session->chrome->GetWebViewIds(&web_view_ids);
    245   if (status.IsError())
    246     return status;
    247   bool is_last_web_view = web_view_ids.size() == 1u;
    248   web_view_ids.clear();
    249 
    250   WebView* web_view = NULL;
    251   status = session->GetTargetWindow(&web_view);
    252   if (status.IsError())
    253     return status;
    254 
    255   status = session->chrome->CloseWebView(web_view->GetId());
    256   if (status.IsError())
    257     return status;
    258 
    259   status = session->chrome->GetWebViewIds(&web_view_ids);
    260   if ((status.code() == kChromeNotReachable && is_last_web_view) ||
    261       (status.IsOk() && web_view_ids.empty())) {
    262     // If no window is open, close is the equivalent of calling "quit".
    263     session->quit = true;
    264     return session->chrome->Quit();
    265   }
    266 
    267   return status;
    268 }
    269 
    270 Status ExecuteGetWindowHandles(
    271     Session* session,
    272     const base::DictionaryValue& params,
    273     scoped_ptr<base::Value>* value) {
    274   std::list<std::string> web_view_ids;
    275   Status status = session->chrome->GetWebViewIds(&web_view_ids);
    276   if (status.IsError())
    277     return status;
    278   scoped_ptr<base::ListValue> window_ids(new base::ListValue());
    279   for (std::list<std::string>::const_iterator it = web_view_ids.begin();
    280        it != web_view_ids.end(); ++it) {
    281     window_ids->AppendString(WebViewIdToWindowHandle(*it));
    282   }
    283   value->reset(window_ids.release());
    284   return Status(kOk);
    285 }
    286 
    287 Status ExecuteSwitchToWindow(
    288     Session* session,
    289     const base::DictionaryValue& params,
    290     scoped_ptr<base::Value>* value) {
    291   std::string name;
    292   if (!params.GetString("name", &name) || name.empty())
    293     return Status(kUnknownError, "'name' must be a nonempty string");
    294 
    295   std::list<std::string> web_view_ids;
    296   Status status = session->chrome->GetWebViewIds(&web_view_ids);
    297   if (status.IsError())
    298     return status;
    299 
    300   std::string web_view_id;
    301   bool found = false;
    302   if (WindowHandleToWebViewId(name, &web_view_id)) {
    303     // Check if any web_view matches |web_view_id|.
    304     for (std::list<std::string>::const_iterator it = web_view_ids.begin();
    305          it != web_view_ids.end(); ++it) {
    306       if (*it == web_view_id) {
    307         found = true;
    308         break;
    309       }
    310     }
    311   } else {
    312     // Check if any of the tab window names match |name|.
    313     const char* kGetWindowNameScript = "function() { return window.name; }";
    314     base::ListValue args;
    315     for (std::list<std::string>::const_iterator it = web_view_ids.begin();
    316          it != web_view_ids.end(); ++it) {
    317       scoped_ptr<base::Value> result;
    318       WebView* web_view;
    319       status = session->chrome->GetWebViewById(*it, &web_view);
    320       if (status.IsError())
    321         return status;
    322       status = web_view->ConnectIfNecessary();
    323       if (status.IsError())
    324         return status;
    325       status = web_view->CallFunction(
    326           std::string(), kGetWindowNameScript, args, &result);
    327       if (status.IsError())
    328         return status;
    329       std::string window_name;
    330       if (!result->GetAsString(&window_name))
    331         return Status(kUnknownError, "failed to get window name");
    332       if (window_name == name) {
    333         web_view_id = *it;
    334         found = true;
    335         break;
    336       }
    337     }
    338   }
    339 
    340   if (!found)
    341     return Status(kNoSuchWindow);
    342 
    343   if (session->overridden_geoposition) {
    344     WebView* web_view;
    345     status = session->chrome->GetWebViewById(web_view_id, &web_view);
    346     if (status.IsError())
    347       return status;
    348     status = web_view->ConnectIfNecessary();
    349     if (status.IsError())
    350       return status;
    351     status = web_view->OverrideGeolocation(*session->overridden_geoposition);
    352     if (status.IsError())
    353       return status;
    354   }
    355 
    356   session->window = web_view_id;
    357   session->SwitchToTopFrame();
    358   session->mouse_position = WebPoint(0, 0);
    359   return Status(kOk);
    360 }
    361 
    362 Status ExecuteSetTimeout(
    363     Session* session,
    364     const base::DictionaryValue& params,
    365     scoped_ptr<base::Value>* value) {
    366   double ms_double;
    367   if (!params.GetDouble("ms", &ms_double))
    368     return Status(kUnknownError, "'ms' must be a double");
    369   std::string type;
    370   if (!params.GetString("type", &type))
    371     return Status(kUnknownError, "'type' must be a string");
    372 
    373   base::TimeDelta timeout =
    374       base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
    375   // TODO(frankf): implicit and script timeout should be cleared
    376   // if negative timeout is specified.
    377   if (type == "implicit") {
    378     session->implicit_wait = timeout;
    379   } else if (type == "script") {
    380     session->script_timeout = timeout;
    381   } else if (type == "page load") {
    382     session->page_load_timeout =
    383         ((timeout < base::TimeDelta()) ? Session::kDefaultPageLoadTimeout
    384                                        : timeout);
    385   } else {
    386     return Status(kUnknownError, "unknown type of timeout:" + type);
    387   }
    388   return Status(kOk);
    389 }
    390 
    391 Status ExecuteSetScriptTimeout(
    392     Session* session,
    393     const base::DictionaryValue& params,
    394     scoped_ptr<base::Value>* value) {
    395   double ms;
    396   if (!params.GetDouble("ms", &ms) || ms < 0)
    397     return Status(kUnknownError, "'ms' must be a non-negative number");
    398   session->script_timeout =
    399       base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
    400   return Status(kOk);
    401 }
    402 
    403 Status ExecuteImplicitlyWait(
    404     Session* session,
    405     const base::DictionaryValue& params,
    406     scoped_ptr<base::Value>* value) {
    407   double ms;
    408   if (!params.GetDouble("ms", &ms) || ms < 0)
    409     return Status(kUnknownError, "'ms' must be a non-negative number");
    410   session->implicit_wait =
    411       base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
    412   return Status(kOk);
    413 }
    414 
    415 Status ExecuteIsLoading(
    416     Session* session,
    417     const base::DictionaryValue& params,
    418     scoped_ptr<base::Value>* value) {
    419   WebView* web_view = NULL;
    420   Status status = session->GetTargetWindow(&web_view);
    421   if (status.IsError())
    422     return status;
    423 
    424   status = web_view->ConnectIfNecessary();
    425   if (status.IsError())
    426     return status;
    427 
    428   bool is_pending;
    429   status = web_view->IsPendingNavigation(
    430       session->GetCurrentFrameId(), &is_pending);
    431   if (status.IsError())
    432     return status;
    433   value->reset(new base::FundamentalValue(is_pending));
    434   return Status(kOk);
    435 }
    436 
    437 Status ExecuteGetLocation(
    438     Session* session,
    439     const base::DictionaryValue& params,
    440     scoped_ptr<base::Value>* value) {
    441   if (!session->overridden_geoposition) {
    442     return Status(kUnknownError,
    443                   "Location must be set before it can be retrieved");
    444   }
    445   base::DictionaryValue location;
    446   location.SetDouble("latitude", session->overridden_geoposition->latitude);
    447   location.SetDouble("longitude", session->overridden_geoposition->longitude);
    448   location.SetDouble("accuracy", session->overridden_geoposition->accuracy);
    449   // Set a dummy altitude to make WebDriver clients happy.
    450   // https://code.google.com/p/chromedriver/issues/detail?id=281
    451   location.SetDouble("altitude", 0);
    452   value->reset(location.DeepCopy());
    453   return Status(kOk);
    454 }
    455 
    456 Status ExecuteGetWindowPosition(
    457     Session* session,
    458     const base::DictionaryValue& params,
    459     scoped_ptr<base::Value>* value) {
    460   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
    461   if (!desktop) {
    462     return Status(
    463         kUnknownError,
    464         "command only supported for desktop Chrome without debuggerAddress");
    465   }
    466 
    467   AutomationExtension* extension = NULL;
    468   Status status = desktop->GetAutomationExtension(&extension);
    469   if (status.IsError())
    470     return status;
    471 
    472   int x, y;
    473   status = extension->GetWindowPosition(&x, &y);
    474   if (status.IsError())
    475     return status;
    476 
    477   base::DictionaryValue position;
    478   position.SetInteger("x", x);
    479   position.SetInteger("y", y);
    480   value->reset(position.DeepCopy());
    481   return Status(kOk);
    482 }
    483 
    484 Status ExecuteSetWindowPosition(
    485     Session* session,
    486     const base::DictionaryValue& params,
    487     scoped_ptr<base::Value>* value) {
    488   double x = 0;
    489   double y = 0;
    490   if (!params.GetDouble("x", &x) || !params.GetDouble("y", &y))
    491     return Status(kUnknownError, "missing or invalid 'x' or 'y'");
    492 
    493   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
    494   if (!desktop) {
    495     return Status(
    496         kUnknownError,
    497         "command only supported for desktop Chrome without debuggerAddress");
    498   }
    499 
    500   AutomationExtension* extension = NULL;
    501   Status status = desktop->GetAutomationExtension(&extension);
    502   if (status.IsError())
    503     return status;
    504 
    505   return extension->SetWindowPosition(static_cast<int>(x), static_cast<int>(y));
    506 }
    507 
    508 Status ExecuteGetWindowSize(
    509     Session* session,
    510     const base::DictionaryValue& params,
    511     scoped_ptr<base::Value>* value) {
    512   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
    513   if (!desktop) {
    514     return Status(
    515         kUnknownError,
    516         "command only supported for desktop Chrome without debuggerAddress");
    517   }
    518 
    519   AutomationExtension* extension = NULL;
    520   Status status = desktop->GetAutomationExtension(&extension);
    521   if (status.IsError())
    522     return status;
    523 
    524   int width, height;
    525   status = extension->GetWindowSize(&width, &height);
    526   if (status.IsError())
    527     return status;
    528 
    529   base::DictionaryValue size;
    530   size.SetInteger("width", width);
    531   size.SetInteger("height", height);
    532   value->reset(size.DeepCopy());
    533   return Status(kOk);
    534 }
    535 
    536 Status ExecuteSetWindowSize(
    537     Session* session,
    538     const base::DictionaryValue& params,
    539     scoped_ptr<base::Value>* value) {
    540   double width = 0;
    541   double height = 0;
    542   if (!params.GetDouble("width", &width) ||
    543       !params.GetDouble("height", &height))
    544     return Status(kUnknownError, "missing or invalid 'width' or 'height'");
    545 
    546   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
    547   if (!desktop) {
    548     return Status(
    549         kUnknownError,
    550         "command only supported for desktop Chrome without debuggerAddress");
    551   }
    552 
    553   AutomationExtension* extension = NULL;
    554   Status status = desktop->GetAutomationExtension(&extension);
    555   if (status.IsError())
    556     return status;
    557 
    558   return extension->SetWindowSize(
    559       static_cast<int>(width), static_cast<int>(height));
    560 }
    561 
    562 Status ExecuteMaximizeWindow(
    563     Session* session,
    564     const base::DictionaryValue& params,
    565     scoped_ptr<base::Value>* value) {
    566   ChromeDesktopImpl* desktop = session->chrome->GetAsDesktop();
    567   if (!desktop) {
    568     return Status(
    569         kUnknownError,
    570         "command only supported for desktop Chrome without debuggerAddress");
    571   }
    572 
    573   AutomationExtension* extension = NULL;
    574   Status status = desktop->GetAutomationExtension(&extension);
    575   if (status.IsError())
    576     return status;
    577 
    578   return extension->MaximizeWindow();
    579 }
    580 
    581 Status ExecuteGetAvailableLogTypes(
    582     Session* session,
    583     const base::DictionaryValue& params,
    584     scoped_ptr<base::Value>* value) {
    585   scoped_ptr<base::ListValue> types(new base::ListValue());
    586   std::vector<WebDriverLog*> logs = session->GetAllLogs();
    587   for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
    588        log != logs.end();
    589        ++log) {
    590     types->AppendString((*log)->type());
    591   }
    592   *value = types.Pass();
    593   return Status(kOk);
    594 }
    595 
    596 Status ExecuteGetLog(
    597     Session* session,
    598     const base::DictionaryValue& params,
    599     scoped_ptr<base::Value>* value) {
    600   std::string log_type;
    601   if (!params.GetString("type", &log_type)) {
    602     return Status(kUnknownError, "missing or invalid 'type'");
    603   }
    604   std::vector<WebDriverLog*> logs = session->GetAllLogs();
    605   for (std::vector<WebDriverLog*>::const_iterator log = logs.begin();
    606        log != logs.end();
    607        ++log) {
    608     if (log_type == (*log)->type()) {
    609       *value = (*log)->GetAndClearEntries();
    610       return Status(kOk);
    611     }
    612   }
    613   return Status(kUnknownError, "log type '" + log_type + "' not found");
    614 }
    615 
    616 Status ExecuteUploadFile(
    617     Session* session,
    618     const base::DictionaryValue& params,
    619     scoped_ptr<base::Value>* value) {
    620     std::string base64_zip_data;
    621   if (!params.GetString("file", &base64_zip_data))
    622     return Status(kUnknownError, "missing or invalid 'file'");
    623   std::string zip_data;
    624   if (!Base64Decode(base64_zip_data, &zip_data))
    625     return Status(kUnknownError, "unable to decode 'file'");
    626 
    627   if (!session->temp_dir.IsValid()) {
    628     if (!session->temp_dir.CreateUniqueTempDir())
    629       return Status(kUnknownError, "unable to create temp dir");
    630   }
    631   base::FilePath upload_dir;
    632   if (!base::CreateTemporaryDirInDir(session->temp_dir.path(),
    633                                      FILE_PATH_LITERAL("upload"),
    634                                      &upload_dir)) {
    635     return Status(kUnknownError, "unable to create temp dir");
    636   }
    637   std::string error_msg;
    638   base::FilePath upload;
    639   Status status = UnzipSoleFile(upload_dir, zip_data, &upload);
    640   if (status.IsError())
    641     return Status(kUnknownError, "unable to unzip 'file'", status);
    642 
    643   value->reset(new base::StringValue(upload.value()));
    644   return Status(kOk);
    645 }
    646 
    647 Status ExecuteIsAutoReporting(
    648     Session* session,
    649     const base::DictionaryValue& params,
    650     scoped_ptr<base::Value>* value) {
    651   value->reset(new base::FundamentalValue(session->auto_reporting_enabled));
    652   return Status(kOk);
    653 }
    654 
    655 Status ExecuteSetAutoReporting(
    656     Session* session,
    657     const base::DictionaryValue& params,
    658     scoped_ptr<base::Value>* value) {
    659   bool enabled;
    660   if (!params.GetBoolean("enabled", &enabled))
    661     return Status(kUnknownError, "missing parameter 'enabled'");
    662   session->auto_reporting_enabled = enabled;
    663   return Status(kOk);
    664 }
    665