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