Home | History | Annotate | Download | only in webdriver
      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 "chrome/test/webdriver/webdriver_automation.h"
      6 
      7 #if defined(OS_WIN)
      8 #include <windows.h>
      9 #endif
     10 
     11 #include "base/base_paths.h"
     12 #include "base/basictypes.h"
     13 #include "base/callback.h"
     14 #include "base/environment.h"
     15 #include "base/file_util.h"
     16 #include "base/files/file_path.h"
     17 #include "base/json/json_writer.h"
     18 #include "base/memory/ref_counted.h"
     19 #include "base/path_service.h"
     20 #include "base/strings/string_number_conversions.h"
     21 #include "base/strings/string_split.h"
     22 #include "base/strings/stringprintf.h"
     23 #include "base/strings/utf_string_conversions.h"
     24 #include "base/synchronization/waitable_event.h"
     25 #include "base/values.h"
     26 #include "chrome/common/automation_constants.h"
     27 #include "chrome/common/automation_messages.h"
     28 #include "chrome/common/chrome_constants.h"
     29 #include "chrome/common/chrome_switches.h"
     30 #include "chrome/common/url_constants.h"
     31 #include "chrome/test/automation/automation_json_requests.h"
     32 #include "chrome/test/automation/automation_proxy.h"
     33 #include "chrome/test/automation/browser_proxy.h"
     34 #include "chrome/test/automation/proxy_launcher.h"
     35 #include "chrome/test/automation/tab_proxy.h"
     36 #include "chrome/test/base/chrome_process_util.h"
     37 #include "chrome/test/webdriver/frame_path.h"
     38 #include "chrome/test/webdriver/webdriver_basic_types.h"
     39 #include "chrome/test/webdriver/webdriver_error.h"
     40 #include "chrome/test/webdriver/webdriver_util.h"
     41 
     42 #if defined(OS_WIN)
     43 #include "base/win/registry.h"
     44 #include "base/win/windows_version.h"
     45 #endif
     46 
     47 namespace {
     48 
     49 // Iterates through each browser executable path, and checks if the path exists
     50 // in any of the given locations. If found, returns true and sets |browser_exe|.
     51 bool CheckForChromeExe(const std::vector<base::FilePath>& browser_exes,
     52                        const std::vector<base::FilePath>& locations,
     53                        base::FilePath* browser_exe) {
     54   for (size_t i = 0; i < browser_exes.size(); ++i) {
     55     for (size_t j = 0; j < locations.size(); ++j) {
     56       base::FilePath path = locations[j].Append(browser_exes[i]);
     57       if (base::PathExists(path)) {
     58         *browser_exe = path;
     59         return true;
     60       }
     61     }
     62   }
     63   return false;
     64 }
     65 
     66 // Gets the path to the default Chrome executable. Returns true on success.
     67 bool GetDefaultChromeExe(base::FilePath* browser_exe) {
     68   // Instead of using chrome constants, we hardcode these constants here so
     69   // that we can locate chrome or chromium regardless of the branding
     70   // chromedriver is built with. It may be argued that then we need to keep
     71   // these in sync with chrome constants. However, if chrome constants changes,
     72   // we need to look for the previous and new versions to support some
     73   // backwards compatibility.
     74 #if defined(OS_WIN)
     75   base::FilePath browser_exes_array[] = {
     76       base::FilePath(L"chrome.exe")
     77   };
     78 #elif defined(OS_MACOSX)
     79   base::FilePath browser_exes_array[] = {
     80       base::FilePath("Google Chrome.app/Contents/MacOS/Google Chrome"),
     81       base::FilePath("Chromium.app/Contents/MacOS/Chromium")
     82   };
     83 #elif defined(OS_LINUX)
     84   base::FilePath browser_exes_array[] = {
     85       base::FilePath("google-chrome"),
     86       base::FilePath("chrome"),
     87       base::FilePath("chromium"),
     88       base::FilePath("chromium-browser")
     89   };
     90 #endif
     91   std::vector<base::FilePath> browser_exes(
     92       browser_exes_array, browser_exes_array + arraysize(browser_exes_array));
     93 
     94   // Step 1: Check the directory this module resides in. This is done
     95   // before all else so that the tests will pickup the built chrome.
     96   base::FilePath module_dir;
     97   if (PathService::Get(base::DIR_MODULE, &module_dir)) {
     98     for (size_t j = 0; j < browser_exes.size(); ++j) {
     99       base::FilePath path = module_dir.Append(browser_exes[j]);
    100       if (base::PathExists(path)) {
    101         *browser_exe = path;
    102         return true;
    103       }
    104     }
    105   }
    106 
    107   // Step 2: Add all possible install locations, in order they should be
    108   // searched. If a location can only hold a chromium install, add it to
    109   // |chromium_locations|. Since on some platforms we cannot tell by the binary
    110   // name whether it is chrome or chromium, we search these locations last.
    111   // We attempt to run chrome before chromium, if any install can be found.
    112   std::vector<base::FilePath> locations;
    113   std::vector<base::FilePath> chromium_locations;
    114 #if defined(OS_WIN)
    115   // Add the App Paths registry key location.
    116   const wchar_t kSubKey[] =
    117       L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe";
    118   base::win::RegKey key(HKEY_CURRENT_USER, kSubKey, KEY_READ);
    119   std::wstring path;
    120   if (key.ReadValue(L"path", &path) == ERROR_SUCCESS)
    121     locations.push_back(base::FilePath(path));
    122   base::win::RegKey sys_key(HKEY_LOCAL_MACHINE, kSubKey, KEY_READ);
    123   if (sys_key.ReadValue(L"path", &path) == ERROR_SUCCESS)
    124     locations.push_back(base::FilePath(path));
    125 
    126   // Add the user-level location for Chrome.
    127   base::FilePath app_from_google(L"Google\\Chrome\\Application");
    128   base::FilePath app_from_chromium(L"Chromium\\Application");
    129   scoped_ptr<base::Environment> env(base::Environment::Create());
    130   std::string home_dir;
    131   if (env->GetVar("userprofile", &home_dir)) {
    132     base::FilePath default_location(UTF8ToWide(home_dir));
    133     if (base::win::GetVersion() < base::win::VERSION_VISTA) {
    134       default_location = default_location.Append(
    135           L"Local Settings\\Application Data");
    136     } else {
    137       default_location = default_location.Append(L"AppData\\Local");
    138     }
    139     locations.push_back(default_location.Append(app_from_google));
    140     chromium_locations.push_back(default_location.Append(app_from_chromium));
    141   }
    142 
    143   // Add the system-level location for Chrome.
    144   std::string program_dir;
    145   if (env->GetVar("ProgramFiles", &program_dir)) {
    146     locations.push_back(base::FilePath(UTF8ToWide(program_dir))
    147         .Append(app_from_google));
    148     chromium_locations.push_back(base::FilePath(UTF8ToWide(program_dir))
    149         .Append(app_from_chromium));
    150   }
    151   if (env->GetVar("ProgramFiles(x86)", &program_dir)) {
    152     locations.push_back(base::FilePath(UTF8ToWide(program_dir))
    153         .Append(app_from_google));
    154     chromium_locations.push_back(base::FilePath(UTF8ToWide(program_dir))
    155         .Append(app_from_chromium));
    156   }
    157 #elif defined(OS_MACOSX)
    158   std::vector<base::FilePath> app_dirs;
    159   webdriver::GetApplicationDirs(&app_dirs);
    160   locations.insert(locations.end(), app_dirs.begin(), app_dirs.end());
    161 #elif defined(OS_LINUX)
    162   locations.push_back(base::FilePath("/opt/google/chrome"));
    163   locations.push_back(base::FilePath("/usr/local/bin"));
    164   locations.push_back(base::FilePath("/usr/local/sbin"));
    165   locations.push_back(base::FilePath("/usr/bin"));
    166   locations.push_back(base::FilePath("/usr/sbin"));
    167   locations.push_back(base::FilePath("/bin"));
    168   locations.push_back(base::FilePath("/sbin"));
    169 #endif
    170 
    171   // Add the current directory.
    172   base::FilePath current_dir;
    173   if (file_util::GetCurrentDirectory(&current_dir))
    174     locations.push_back(current_dir);
    175 
    176   // Step 3: For each browser exe path, check each location to see if the
    177   // browser is installed there. Check the chromium locations lastly.
    178   return CheckForChromeExe(browser_exes, locations, browser_exe) ||
    179       CheckForChromeExe(browser_exes, chromium_locations, browser_exe);
    180 }
    181 
    182 // Message that duplicates a given message but uses a different type.
    183 class MessageWithAlternateType : public IPC::Message {
    184  public:
    185   MessageWithAlternateType(const IPC::Message& msg, int type)
    186       : IPC::Message(msg) {
    187     header()->type = type;
    188   }
    189   virtual ~MessageWithAlternateType() {}
    190 };
    191 
    192 // Filters incoming and outgoing messages on the IO thread. Translates messages
    193 // from old Chrome versions to the new version. This is needed so that new
    194 // ChromeDriver releases support the current stable and beta channels of Chrome.
    195 // TODO(kkania): Delete this when Chrome v21 is stable.
    196 class BackwardsCompatAutomationMessageFilter
    197     : public IPC::ChannelProxy::MessageFilter,
    198       public IPC::ChannelProxy::OutgoingMessageFilter {
    199  public:
    200   explicit BackwardsCompatAutomationMessageFilter(AutomationProxy* server);
    201 
    202   // Overriden from IPC::ChannelProxy::MessageFiler.
    203   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
    204 
    205   // Overriden from IPC::ChannelProxy::OutgoingMessageFiler.
    206   virtual IPC::Message* Rewrite(IPC::Message* message) OVERRIDE;
    207 
    208  private:
    209   virtual ~BackwardsCompatAutomationMessageFilter();
    210 
    211   // The first version of Chrome using the new IPC automation message set,
    212   // after r137672 changed most of the message IDs.
    213   static const int kNewAutomationVersion = 1142;
    214 
    215   // The first version of Chrome using the new JSON interface which takes a
    216   // browser index instead of a browser handle.
    217   static const int kNewJSONInterfaceVersion = 1195;
    218 
    219   AutomationProxy* server_;
    220   bool old_version_;
    221 
    222   DISALLOW_COPY_AND_ASSIGN(BackwardsCompatAutomationMessageFilter);
    223 };
    224 
    225 BackwardsCompatAutomationMessageFilter::BackwardsCompatAutomationMessageFilter(
    226     AutomationProxy* server)
    227     : server_(server), old_version_(false) {
    228 }
    229 
    230 bool BackwardsCompatAutomationMessageFilter::OnMessageReceived(
    231     const IPC::Message& message) {
    232   const uint32 kOldHelloType = 44,
    233                kOldInitialLoadsCompleteType = 47,
    234                kOldInitialNewTabUILoadCompleteType = 267;
    235   if (message.type() == kOldHelloType) {
    236     old_version_ = true;
    237     std::string server_version;
    238     PickleIterator iter(message);
    239     CHECK(message.ReadString(&iter, &server_version));
    240     server_->SignalAppLaunch(server_version);
    241     return true;
    242   }
    243   if (!old_version_)
    244     return false;
    245 
    246   switch (message.type()) {
    247     case kOldInitialLoadsCompleteType:
    248       server_->SignalInitialLoads();
    249       break;
    250     case kOldInitialNewTabUILoadCompleteType:
    251       server_->SignalNewTabUITab(-1);
    252       break;
    253     default:
    254       return false;
    255   }
    256   return true;
    257 }
    258 
    259 IPC::Message* BackwardsCompatAutomationMessageFilter::Rewrite(
    260     IPC::Message* message) {
    261   int build_no = -1;
    262   std::string version = server_->server_version();
    263   std::vector<std::string> version_parts;
    264   base::SplitString(version, '.', &version_parts);
    265   CHECK(version_parts.size() == 4 &&
    266         base::StringToInt(version_parts[2], &build_no))
    267       << "Can't rewrite message (type: " << message->type()
    268       << ") because unknown server (version: " << version << ")";
    269   CHECK_EQ(static_cast<uint32>(AutomationMsg_SendJSONRequest::ID),
    270            message->type());
    271   int type = AutomationMsg_SendJSONRequest::ID;
    272   // These old message types are determined by inspecting the line number
    273   // of the SendJSONRequest message in older versions of
    274   // automation_messages_internal.h.
    275   if (build_no < kNewAutomationVersion)
    276     type = 1301;
    277   else if (build_no < kNewJSONInterfaceVersion)
    278     type = 863;
    279   IPC::Message* new_message = new MessageWithAlternateType(*message, type);
    280   delete message;
    281   return new_message;
    282 }
    283 
    284 BackwardsCompatAutomationMessageFilter::
    285     ~BackwardsCompatAutomationMessageFilter() {
    286 }
    287 
    288 void AddBackwardsCompatFilter(AutomationProxy* proxy) {
    289   // The filter is ref-counted in AddFilter.
    290   BackwardsCompatAutomationMessageFilter* filter =
    291       new BackwardsCompatAutomationMessageFilter(proxy);
    292   proxy->channel()->AddFilter(filter);
    293   proxy->channel()->set_outgoing_message_filter(filter);
    294 }
    295 
    296 class WebDriverAnonymousProxyLauncher : public AnonymousProxyLauncher {
    297  public:
    298   WebDriverAnonymousProxyLauncher()
    299       : AnonymousProxyLauncher(false) {}
    300   virtual ~WebDriverAnonymousProxyLauncher() {}
    301 
    302   virtual AutomationProxy* CreateAutomationProxy(
    303       base::TimeDelta execution_timeout) OVERRIDE {
    304     AutomationProxy* proxy =
    305         AnonymousProxyLauncher::CreateAutomationProxy(execution_timeout);
    306     AddBackwardsCompatFilter(proxy);
    307     return proxy;
    308   }
    309 };
    310 
    311 class WebDriverNamedProxyLauncher : public NamedProxyLauncher {
    312  public:
    313   WebDriverNamedProxyLauncher(const std::string& channel_id,
    314                               bool launch_browser)
    315       : NamedProxyLauncher(channel_id, launch_browser, false) {}
    316   virtual ~WebDriverNamedProxyLauncher() {}
    317 
    318   virtual AutomationProxy* CreateAutomationProxy(
    319       base::TimeDelta execution_timeout) OVERRIDE {
    320     AutomationProxy* proxy =
    321         NamedProxyLauncher::CreateAutomationProxy(execution_timeout);
    322     // We can only add the filter here if the browser has not already been
    323     // started. Otherwise the filter is not guaranteed to receive the
    324     // first message. The only occasion where we don't launch the browser is
    325     // in PyAuto, in which case the backwards compat filter isn't needed
    326     // anyways because ChromeDriver and Chrome are at the same version there.
    327     if (launch_browser_)
    328       AddBackwardsCompatFilter(proxy);
    329     return proxy;
    330   }
    331 };
    332 
    333 }  // namespace
    334 
    335 namespace webdriver {
    336 
    337 Automation::BrowserOptions::BrowserOptions()
    338     : command(CommandLine::NO_PROGRAM),
    339       detach_process(false),
    340       ignore_certificate_errors(false) {}
    341 
    342 Automation::BrowserOptions::~BrowserOptions() {}
    343 
    344 Automation::Automation(const Logger& logger)
    345     : logger_(logger),
    346       build_no_(0) {}
    347 
    348 Automation::~Automation() {}
    349 
    350 void Automation::Init(
    351     const BrowserOptions& options,
    352     int* build_no,
    353     Error** error) {
    354   // Prepare Chrome's command line.
    355   CommandLine command(CommandLine::NO_PROGRAM);
    356   if (CommandLine::ForCurrentProcess()->HasSwitch("no-sandbox")) {
    357     command.AppendSwitch(switches::kNoSandbox);
    358   }
    359 
    360   const char* excludable_switches[] = {
    361       switches::kDisableHangMonitor,
    362       switches::kDisablePromptOnRepost,
    363       switches::kDomAutomationController,
    364       switches::kFullMemoryCrashReport,
    365       switches::kNoDefaultBrowserCheck,
    366       switches::kNoFirstRun,
    367       switches::kDisableBackgroundNetworking,
    368       switches::kDisableSync,
    369       switches::kDisableTranslate,
    370       switches::kDisableWebResources,
    371       switches::kSbDisableAutoUpdate,
    372       switches::kSbDisableDownloadProtection,
    373       switches::kDisableClientSidePhishingDetection,
    374       switches::kDisableComponentUpdate,
    375       switches::kDisableDefaultApps
    376   };
    377   std::vector<std::string> switches(excludable_switches,
    378       excludable_switches + arraysize(excludable_switches));
    379   for (size_t i = 0; i < switches.size(); ++i) {
    380     const std::string& switch_name = switches[i];
    381     if (options.exclude_switches.find(switch_name) ==
    382         options.exclude_switches.end()) {
    383       command.AppendSwitch(switch_name);
    384     }
    385   }
    386 
    387   command.AppendSwitch(switches::kEnableLogging);
    388   command.AppendSwitchASCII(switches::kLoggingLevel, "1");
    389 
    390 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
    391   command.AppendSwitchASCII(switches::kPasswordStore, "basic");
    392 #endif
    393 #if defined(OS_MACOSX)
    394   command.AppendSwitch(switches::kUseMockKeychain);
    395 #endif
    396   if (options.detach_process)
    397     command.AppendSwitch(switches::kAutomationReinitializeOnChannelError);
    398   if (options.ignore_certificate_errors)
    399     command.AppendSwitch(switches::kIgnoreCertificateErrors);
    400   if (options.user_data_dir.empty())
    401     command.AppendArg(content::kAboutBlankURL);
    402 
    403   command.AppendArguments(options.command, true /* include_program */);
    404 
    405   // Find the Chrome binary.
    406   if (command.GetProgram().empty()) {
    407     base::FilePath browser_exe;
    408     if (!GetDefaultChromeExe(&browser_exe)) {
    409       *error = new Error(kUnknownError, "Could not find default Chrome binary");
    410       return;
    411     }
    412     command.SetProgram(browser_exe);
    413   }
    414   if (!base::PathExists(command.GetProgram())) {
    415     std::string message = base::StringPrintf(
    416         "Could not find Chrome binary at: %" PRFilePath,
    417         command.GetProgram().value().c_str());
    418     *error = new Error(kUnknownError, message);
    419     return;
    420   }
    421   std::string chrome_details = base::StringPrintf(
    422       "Using Chrome binary at: %" PRFilePath,
    423       command.GetProgram().value().c_str());
    424 
    425   // Create the ProxyLauncher and launch Chrome.
    426   // In Chrome 13/14, the only way to detach the browser process is to use a
    427   // named proxy launcher.
    428   // TODO(kkania): Remove this when Chrome 15 is stable.
    429   std::string channel_id = options.channel_id;
    430   bool launch_browser = false;
    431   if (options.detach_process) {
    432     launch_browser = true;
    433     if (!channel_id.empty()) {
    434       *error = new Error(
    435           kUnknownError, "Cannot detach an already running browser process");
    436       return;
    437     }
    438 #if defined(OS_WIN)
    439     channel_id = "chromedriver" + GenerateRandomID();
    440 #elif defined(OS_POSIX)
    441     base::FilePath temp_file;
    442     if (!file_util::CreateTemporaryFile(&temp_file) ||
    443         !base::DeleteFile(temp_file, false /* recursive */)) {
    444       *error = new Error(kUnknownError, "Could not create temporary filename");
    445       return;
    446     }
    447     channel_id = temp_file.value();
    448 #endif
    449   }
    450   if (channel_id.empty()) {
    451     std::string command_line_str;
    452 #if defined(OS_WIN)
    453     command_line_str = WideToUTF8(command.GetCommandLineString());
    454 #elif defined(OS_POSIX)
    455     command_line_str = command.GetCommandLineString();
    456 #endif
    457     logger_.Log(kInfoLogLevel, "Launching chrome: " + command_line_str);
    458     launcher_.reset(new WebDriverAnonymousProxyLauncher());
    459   } else {
    460     logger_.Log(kInfoLogLevel, "Using named testing interface");
    461     launcher_.reset(new WebDriverNamedProxyLauncher(
    462         channel_id, launch_browser));
    463   }
    464   ProxyLauncher::LaunchState launch_props = {
    465       false,  // clear_profile
    466       options.user_data_dir,  // template_user_data
    467       base::Closure(),
    468       command,
    469       true,  // include_testing_id
    470       true   // show_window
    471   };
    472   if (!launcher_->InitializeConnection(launch_props, true)) {
    473     logger_.Log(kSevereLogLevel, "Failed to initialize connection");
    474     *error = new Error(
    475         kUnknownError,
    476         "Unable to either launch or connect to Chrome. Please check that "
    477             "ChromeDriver is up-to-date. " + chrome_details);
    478     return;
    479   }
    480 
    481   launcher_->automation()->set_action_timeout(
    482       base::TimeDelta::FromMilliseconds(base::kNoTimeout));
    483   logger_.Log(kInfoLogLevel, "Connected to Chrome successfully. Version: " +
    484               automation()->server_version());
    485 
    486   *error = DetermineBuildNumber();
    487   if (*error)
    488     return;
    489   *build_no = build_no_;
    490 
    491   // Check the version of Chrome is compatible with this ChromeDriver.
    492   chrome_details += ", version (" + automation()->server_version() + ")";
    493   int version = 0;
    494   automation::Error auto_error;
    495   if (!SendGetChromeDriverAutomationVersion(
    496           automation(), &version, &auto_error)) {
    497     *error = Error::FromAutomationError(auto_error);
    498     return;
    499   }
    500   if (version > automation::kChromeDriverAutomationVersion) {
    501     *error = new Error(
    502         kUnknownError,
    503         "ChromeDriver is not compatible with this version of Chrome. " +
    504             chrome_details);
    505     return;
    506   }
    507 }
    508 
    509 void Automation::Terminate() {
    510   if (launcher_.get() && launcher_->process() != base::kNullProcessHandle) {
    511 #if defined(OS_MACOSX)
    512     // There's currently no way to shutdown gracefully with mac chrome.
    513     // An alert could be open or (open before shutdown is started) and stop
    514     // the whole process. Close any current dialog, try to kill gently, and
    515     // if all else fails, kill it hard.
    516     Error* error = NULL;
    517     AcceptOrDismissAppModalDialog(true /* accept */, &error);
    518     scoped_ptr<Error> scoped_error(error);
    519 
    520     kill(launcher_->process(), SIGTERM);
    521 #else
    522     automation()->Disconnect();
    523 #endif
    524     int exit_code = -1;
    525     if (!launcher_->WaitForBrowserProcessToQuit(
    526             base::TimeDelta::FromSeconds(10), &exit_code)) {
    527       logger_.Log(kWarningLogLevel, "Chrome still running, terminating...");
    528       ChromeProcessList process_pids(GetRunningChromeProcesses(
    529           launcher_->process_id()));
    530       TerminateAllChromeProcesses(process_pids);
    531     }
    532     base::CloseProcessHandle(launcher_->process());
    533     logger_.Log(kInfoLogLevel, "Chrome shutdown");
    534   }
    535 }
    536 
    537 void Automation::ExecuteScript(const WebViewId& view_id,
    538                                const FramePath& frame_path,
    539                                const std::string& script,
    540                                std::string* result,
    541                                Error** error) {
    542   WebViewLocator view_locator;
    543   *error = ConvertViewIdToLocator(view_id, &view_locator);
    544   if (*error)
    545     return;
    546 
    547   automation::Error auto_error;
    548   scoped_ptr<Value> value;
    549   if (!SendExecuteJavascriptJSONRequest(automation(), view_locator,
    550                                         frame_path.value(), script,
    551                                         &value, &auto_error)) {
    552     *error = Error::FromAutomationError(auto_error);
    553     return;
    554   }
    555   if (!value->GetAsString(result))
    556     *error = new Error(kUnknownError, "Execute script did not return string");
    557 }
    558 
    559 void Automation::MouseMoveDeprecated(
    560     const WebViewId& view_id,
    561     const Point& p,
    562     Error** error) {
    563   WebViewLocator view_locator;
    564   *error = ConvertViewIdToLocator(view_id, &view_locator);
    565   if (*error)
    566     return;
    567 
    568   automation::Error auto_error;
    569   if (!SendMouseMoveJSONRequestDeprecated(
    570           automation(), view_locator, p.rounded_x(), p.rounded_y(),
    571           &auto_error)) {
    572     *error = Error::FromAutomationError(auto_error);
    573   }
    574 }
    575 
    576 void Automation::MouseClickDeprecated(
    577     const WebViewId& view_id,
    578     const Point& p,
    579     automation::MouseButton button,
    580     Error** error) {
    581   WebViewLocator view_locator;
    582   *error = ConvertViewIdToLocator(view_id, &view_locator);
    583   if (*error)
    584     return;
    585 
    586   automation::Error auto_error;
    587   if (!SendMouseClickJSONRequestDeprecated(
    588           automation(), view_locator, button, p.rounded_x(),
    589           p.rounded_y(), &auto_error)) {
    590     *error = Error::FromAutomationError(auto_error);
    591   }
    592 }
    593 
    594 void Automation::MouseDragDeprecated(
    595     const WebViewId& view_id,
    596     const Point& start,
    597     const Point& end,
    598     Error** error) {
    599   WebViewLocator view_locator;
    600   *error = ConvertViewIdToLocator(view_id, &view_locator);
    601   if (*error)
    602     return;
    603 
    604   automation::Error auto_error;
    605   if (!SendMouseDragJSONRequestDeprecated(
    606           automation(), view_locator, start.rounded_x(), start.rounded_y(),
    607           end.rounded_x(), end.rounded_y(), &auto_error)) {
    608     *error = Error::FromAutomationError(auto_error);
    609   }
    610 }
    611 
    612 void Automation::MouseButtonUpDeprecated(
    613     const WebViewId& view_id,
    614     const Point& p,
    615     Error** error) {
    616   *error = CheckAdvancedInteractionsSupported();
    617   if (*error)
    618     return;
    619 
    620   WebViewLocator view_locator;
    621   *error = ConvertViewIdToLocator(view_id, &view_locator);
    622   if (*error)
    623     return;
    624 
    625   automation::Error auto_error;
    626   if (!SendMouseButtonUpJSONRequestDeprecated(
    627           automation(), view_locator, p.rounded_x(), p.rounded_y(),
    628           &auto_error)) {
    629     *error = Error::FromAutomationError(auto_error);
    630   }
    631 }
    632 
    633 void Automation::MouseButtonDownDeprecated(
    634     const WebViewId& view_id,
    635     const Point& p,
    636     Error** error) {
    637   *error = CheckAdvancedInteractionsSupported();
    638   if (*error)
    639     return;
    640 
    641   WebViewLocator view_locator;
    642   *error = ConvertViewIdToLocator(view_id, &view_locator);
    643   if (*error)
    644     return;
    645 
    646   automation::Error auto_error;
    647   if (!SendMouseButtonDownJSONRequestDeprecated(
    648           automation(), view_locator, p.rounded_x(), p.rounded_y(),
    649           &auto_error)) {
    650     *error = Error::FromAutomationError(auto_error);
    651   }
    652 }
    653 
    654 void Automation::MouseDoubleClickDeprecated(
    655     const WebViewId& view_id,
    656     const Point& p,
    657     Error** error) {
    658   *error = CheckAdvancedInteractionsSupported();
    659   if (*error)
    660     return;
    661 
    662   WebViewLocator view_locator;
    663   *error = ConvertViewIdToLocator(view_id, &view_locator);
    664   if (*error)
    665     return;
    666 
    667   automation::Error auto_error;
    668   if (!SendMouseDoubleClickJSONRequestDeprecated(
    669           automation(), view_locator, p.rounded_x(), p.rounded_y(),
    670           &auto_error)) {
    671     *error = Error::FromAutomationError(auto_error);
    672   }
    673 }
    674 
    675 void Automation::DragAndDropFilePaths(
    676     const WebViewId& view_id, const Point& location,
    677     const std::vector<base::FilePath::StringType>& paths, Error** error) {
    678   WebViewLocator view_locator;
    679   *error = ConvertViewIdToLocator(view_id, &view_locator);
    680   if (*error) {
    681     return;
    682   }
    683 
    684   automation::Error auto_error;
    685   if (!SendDragAndDropFilePathsJSONRequest(
    686           automation(), view_locator, location.rounded_x(),
    687           location.rounded_y(), paths, &auto_error)) {
    688     *error = Error::FromAutomationError(auto_error);
    689   }
    690 }
    691 
    692 void Automation::SendWebKeyEvent(const WebViewId& view_id,
    693                                  const WebKeyEvent& key_event,
    694                                  Error** error) {
    695   WebViewLocator view_locator;
    696   *error = ConvertViewIdToLocator(view_id, &view_locator);
    697   if (*error)
    698     return;
    699 
    700   automation::Error auto_error;
    701   if (!SendWebKeyEventJSONRequest(
    702           automation(), view_locator, key_event, &auto_error)) {
    703     *error = Error::FromAutomationError(auto_error);
    704   }
    705 }
    706 
    707 void Automation::SendWebMouseEvent(const WebViewId& view_id,
    708                                    const WebMouseEvent& event,
    709                                    Error** error) {
    710   WebViewLocator view_locator;
    711   *error = ConvertViewIdToLocator(view_id, &view_locator);
    712   if (*error)
    713     return;
    714 
    715   automation::Error auto_error;
    716   if (!SendWebMouseEventJSONRequestDeprecated(
    717           automation(), view_locator, event, &auto_error)) {
    718     *error = Error::FromAutomationError(auto_error);
    719   }
    720 }
    721 
    722 void Automation::CaptureEntirePageAsPNG(const WebViewId& view_id,
    723                                         const base::FilePath& path,
    724                                         Error** error) {
    725   WebViewLocator view_locator;
    726   *error = ConvertViewIdToLocator(view_id, &view_locator);
    727   if (*error)
    728     return;
    729 
    730   automation::Error auto_error;
    731   if (!SendCaptureEntirePageJSONRequestDeprecated(
    732           automation(), view_locator, path, &auto_error)) {
    733     *error = Error::FromAutomationError(auto_error);
    734   }
    735 }
    736 
    737 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
    738 void Automation::HeapProfilerDump(const WebViewId& view_id,
    739                                   const std::string& reason,
    740                                   Error** error) {
    741   WebViewLocator view_locator;
    742   *error = ConvertViewIdToLocator(view_id, &view_locator);
    743   if (*error)
    744     return;
    745 
    746   automation::Error auto_error;
    747   if (!SendHeapProfilerDumpJSONRequestDeprecated(
    748           automation(), view_locator, reason, &auto_error)) {
    749     *error = Error::FromAutomationError(auto_error);
    750   }
    751 }
    752 #endif  // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
    753 
    754 void Automation::NavigateToURL(const WebViewId& view_id,
    755                                const std::string& url,
    756                                Error** error) {
    757   WebViewLocator view_locator;
    758   *error = ConvertViewIdToLocator(view_id, &view_locator);
    759   if (*error)
    760     return;
    761 
    762   AutomationMsg_NavigationResponseValues navigate_response;
    763   automation::Error auto_error;
    764   if (!SendNavigateToURLJSONRequest(automation(), view_locator,
    765                                     url, 1, &navigate_response,
    766                                     &auto_error)) {
    767     *error = Error::FromAutomationError(auto_error);
    768     return;
    769   }
    770   // TODO(kkania): Do not rely on this enum.
    771   if (navigate_response == AUTOMATION_MSG_NAVIGATION_ERROR)
    772     *error = new Error(kUnknownError, "Navigation error occurred");
    773 }
    774 
    775 void Automation::NavigateToURLAsync(const WebViewId& view_id,
    776                                     const std::string& url,
    777                                     Error** error) {
    778   WebViewLocator view_locator;
    779   *error = ConvertViewIdToLocator(view_id, &view_locator);
    780   if (*error)
    781     return;
    782 
    783   automation::Error auto_error;
    784   if (!view_id.old_style()) {
    785     AutomationMsg_NavigationResponseValues navigate_response;
    786     if (!SendNavigateToURLJSONRequest(automation(), view_locator, url,
    787                                       0, &navigate_response, &auto_error)) {
    788       *error = Error::FromAutomationError(auto_error);
    789       return;
    790     }
    791   } else {
    792     scoped_refptr<BrowserProxy> browser =
    793         automation()->GetBrowserWindow(view_locator.browser_index());
    794     if (!browser.get()) {
    795       *error = new Error(kUnknownError, "Couldn't obtain browser proxy");
    796       return;
    797     }
    798     scoped_refptr<TabProxy> tab = browser->GetTab(view_locator.tab_index());
    799     if (!tab.get()) {
    800       *error = new Error(kUnknownError, "Couldn't obtain tab proxy");
    801       return;
    802     }
    803     if (!tab->NavigateToURLAsync(GURL(url))) {
    804       *error = new Error(kUnknownError, "Unable to navigate to url");
    805       return;
    806     }
    807   }
    808 }
    809 
    810 void Automation::GoForward(const WebViewId& view_id, Error** error) {
    811   WebViewLocator view_locator;
    812   *error = ConvertViewIdToLocator(view_id, &view_locator);
    813   if (*error)
    814     return;
    815 
    816   automation::Error auto_error;
    817   if (!SendGoForwardJSONRequest(
    818           automation(), view_locator, &auto_error)) {
    819     *error = Error::FromAutomationError(auto_error);
    820   }
    821 }
    822 
    823 void Automation::GoBack(const WebViewId& view_id, Error** error) {
    824   WebViewLocator view_locator;
    825   *error = ConvertViewIdToLocator(view_id, &view_locator);
    826   if (*error)
    827     return;
    828 
    829   automation::Error auto_error;
    830   if (!SendGoBackJSONRequest(automation(), view_locator, &auto_error))
    831     *error = Error::FromAutomationError(auto_error);
    832 }
    833 
    834 void Automation::Reload(const WebViewId& view_id, Error** error) {
    835   WebViewLocator view_locator;
    836   *error = ConvertViewIdToLocator(view_id, &view_locator);
    837   if (*error)
    838     return;
    839 
    840   automation::Error auto_error;
    841   if (!SendReloadJSONRequest(automation(), view_locator, &auto_error))
    842     *error = Error::FromAutomationError(auto_error);
    843 }
    844 
    845 void Automation::GetCookies(const std::string& url,
    846                             scoped_ptr<ListValue>* cookies,
    847                             Error** error) {
    848   automation::Error auto_error;
    849   if (!SendGetCookiesJSONRequest(automation(), url, cookies, &auto_error))
    850     *error = Error::FromAutomationError(auto_error);
    851 }
    852 
    853 void Automation::DeleteCookie(const std::string& url,
    854                               const std::string& cookie_name,
    855                               Error** error) {
    856   automation::Error auto_error;
    857   if (!SendDeleteCookieJSONRequest(
    858           automation(), url, cookie_name, &auto_error)) {
    859     *error = Error::FromAutomationError(auto_error);
    860   }
    861 }
    862 
    863 void Automation::SetCookie(const std::string& url,
    864                            DictionaryValue* cookie_dict,
    865                            Error** error) {
    866   automation::Error auto_error;
    867   if (!SendSetCookieJSONRequest(automation(), url, cookie_dict, &auto_error))
    868     *error = Error::FromAutomationError(auto_error);
    869 }
    870 
    871 void Automation::GetViews(std::vector<WebViewInfo>* views,
    872                           Error** error) {
    873   automation::Error auto_error;
    874   if (build_no_ >= 963) {
    875     if (!SendGetWebViewsJSONRequest(automation(), views, &auto_error))
    876       *error = Error::FromAutomationError(auto_error);
    877   } else {
    878     if (!SendGetTabIdsJSONRequest(automation(), views, &auto_error))
    879       *error = Error::FromAutomationError(auto_error);
    880   }
    881 }
    882 
    883 void Automation::DoesViewExist(
    884     const WebViewId& view_id, bool* does_exist, Error** error) {
    885   automation::Error auto_error;
    886   if (view_id.old_style()) {
    887     if (!SendIsTabIdValidJSONRequest(
    888             automation(), view_id, does_exist, &auto_error))
    889       *error = Error::FromAutomationError(auto_error);
    890   } else {
    891     if (!SendDoesAutomationObjectExistJSONRequest(
    892             automation(), view_id, does_exist, &auto_error))
    893       *error = Error::FromAutomationError(auto_error);
    894   }
    895 }
    896 
    897 void Automation::CloseView(const WebViewId& view_id, Error** error) {
    898   WebViewLocator view_locator;
    899   *error = ConvertViewIdToLocator(view_id, &view_locator);
    900   if (*error)
    901     return;
    902 
    903   automation::Error auto_error;
    904   if (!SendCloseViewJSONRequest(automation(), view_locator, &auto_error))
    905     *error = Error::FromAutomationError(auto_error);
    906 }
    907 
    908 void Automation::SetViewBounds(const WebViewId& view_id,
    909                                const Rect& bounds,
    910                                Error** error) {
    911   automation::Error auto_error;
    912   if (!SendSetViewBoundsJSONRequest(
    913           automation(), view_id, bounds.x(), bounds.y(),
    914           bounds.width(), bounds.height(), &auto_error))
    915     *error = Error::FromAutomationError(auto_error);
    916 }
    917 
    918 void Automation::MaximizeView(const WebViewId& view_id, Error** error) {
    919   *error = CheckMaximizeSupported();
    920   if (*error)
    921     return;
    922 
    923   automation::Error auto_error;
    924   if (!SendMaximizeJSONRequest(
    925           automation(), view_id, &auto_error))
    926     *error = Error::FromAutomationError(auto_error);
    927 }
    928 
    929 void Automation::GetAppModalDialogMessage(std::string* message, Error** error) {
    930   *error = CheckAlertsSupported();
    931   if (*error)
    932     return;
    933 
    934   automation::Error auto_error;
    935   if (!SendGetAppModalDialogMessageJSONRequest(
    936           automation(), message, &auto_error)) {
    937     *error = Error::FromAutomationError(auto_error);
    938   }
    939 }
    940 
    941 void Automation::AcceptOrDismissAppModalDialog(bool accept, Error** error) {
    942   *error = CheckAlertsSupported();
    943   if (*error)
    944     return;
    945 
    946   automation::Error auto_error;
    947   if (!SendAcceptOrDismissAppModalDialogJSONRequest(
    948           automation(), accept, &auto_error)) {
    949     *error = Error::FromAutomationError(auto_error);
    950   }
    951 }
    952 
    953 void Automation::AcceptPromptAppModalDialog(const std::string& prompt_text,
    954                                             Error** error) {
    955   *error = CheckAlertsSupported();
    956   if (*error)
    957     return;
    958 
    959   automation::Error auto_error;
    960   if (!SendAcceptPromptAppModalDialogJSONRequest(
    961           automation(), prompt_text, &auto_error)) {
    962     *error = Error::FromAutomationError(auto_error);
    963   }
    964 }
    965 
    966 void Automation::GetBrowserVersion(std::string* version) {
    967   *version = automation()->server_version();
    968 }
    969 
    970 void Automation::GetChromeDriverAutomationVersion(int* version, Error** error) {
    971   automation::Error auto_error;
    972   if (!SendGetChromeDriverAutomationVersion(automation(), version, &auto_error))
    973     *error = Error::FromAutomationError(auto_error);
    974 }
    975 
    976 void Automation::WaitForAllViewsToStopLoading(Error** error) {
    977   automation::Error auto_error;
    978   if (!SendWaitForAllViewsToStopLoadingJSONRequestDeprecated(
    979           automation(), &auto_error))
    980     *error = Error::FromAutomationError(auto_error);
    981 }
    982 
    983 void Automation::InstallExtension(
    984     const base::FilePath& path, std::string* extension_id, Error** error) {
    985   *error = CheckNewExtensionInterfaceSupported();
    986   if (*error)
    987     return;
    988 
    989   automation::Error auto_error;
    990   if (!SendInstallExtensionJSONRequest(
    991           automation(), path, false /* with_ui */, extension_id,
    992           &auto_error))
    993     *error = Error::FromAutomationError(auto_error);
    994 }
    995 
    996 void Automation::GetExtensionsInfo(
    997     base::ListValue* extensions_list, Error** error) {
    998   *error = CheckNewExtensionInterfaceSupported();
    999   if (*error)
   1000     return;
   1001 
   1002   automation::Error auto_error;
   1003   if (!SendGetExtensionsInfoJSONRequest(
   1004           automation(), extensions_list, &auto_error))
   1005     *error = Error::FromAutomationError(auto_error);
   1006 }
   1007 
   1008 void Automation::IsPageActionVisible(
   1009     const WebViewId& tab_id,
   1010     const std::string& extension_id,
   1011     bool* is_visible,
   1012     Error** error) {
   1013   *error = CheckNewExtensionInterfaceSupported();
   1014   if (*error)
   1015     return;
   1016 
   1017   automation::Error auto_error;
   1018   if (!SendIsPageActionVisibleJSONRequest(
   1019           automation(), tab_id, extension_id, is_visible, &auto_error))
   1020     *error = Error::FromAutomationError(auto_error);
   1021 }
   1022 
   1023 void Automation::SetExtensionState(
   1024     const std::string& extension_id,
   1025     bool enable,
   1026     Error** error) {
   1027   *error = CheckNewExtensionInterfaceSupported();
   1028   if (*error)
   1029     return;
   1030 
   1031   automation::Error auto_error;
   1032   if (!SendSetExtensionStateJSONRequest(
   1033           automation(), extension_id, enable /* enable */,
   1034           false /* allow_in_incognito */, &auto_error))
   1035     *error = Error::FromAutomationError(auto_error);
   1036 }
   1037 
   1038 void Automation::ClickExtensionButton(
   1039     const std::string& extension_id,
   1040     bool browser_action,
   1041     Error** error) {
   1042   automation::Error auto_error;
   1043   if (!SendClickExtensionButtonJSONRequest(
   1044           automation(), extension_id, browser_action, &auto_error))
   1045     *error = Error::FromAutomationError(auto_error);
   1046 }
   1047 
   1048 void Automation::UninstallExtension(
   1049     const std::string& extension_id, Error** error) {
   1050   *error = CheckNewExtensionInterfaceSupported();
   1051   if (*error)
   1052     return;
   1053 
   1054   automation::Error auto_error;
   1055   if (!SendUninstallExtensionJSONRequest(
   1056           automation(), extension_id, &auto_error))
   1057     *error = Error::FromAutomationError(auto_error);
   1058 }
   1059 
   1060 void Automation::SetLocalStatePreference(const std::string& pref,
   1061                                          base::Value* value,
   1062                                          Error** error) {
   1063   scoped_ptr<Value> scoped_value(value);
   1064   // In version 927, SetLocalStatePrefs was changed from taking a browser
   1065   // handle to a browser index.
   1066   if (build_no_ >= 927) {
   1067     automation::Error auto_error;
   1068     if (!SendSetLocalStatePreferenceJSONRequest(
   1069             automation(), pref, scoped_value.release(), &auto_error))
   1070       *error = Error::FromAutomationError(auto_error);
   1071   } else {
   1072     std::string request, response;
   1073     DictionaryValue request_dict;
   1074     request_dict.SetString("command", "SetLocalStatePrefs");
   1075     request_dict.SetString("path", pref);
   1076     request_dict.Set("value", scoped_value.release());
   1077     base::JSONWriter::Write(&request_dict, &request);
   1078     if (!automation()->GetBrowserWindow(0)->SendJSONRequest(
   1079             request, -1, &response)) {
   1080       *error = new Error(kUnknownError, base::StringPrintf(
   1081           "Failed to set local state pref '%s'", pref.c_str()));
   1082     }
   1083   }
   1084 }
   1085 
   1086 void Automation::SetPreference(const std::string& pref,
   1087                                base::Value* value,
   1088                                Error** error) {
   1089   scoped_ptr<Value> scoped_value(value);
   1090   // Chrome 17 is on the 963 branch. The first released 18 build should have
   1091   // the new SetPrefs method which uses a browser index instead of handle.
   1092   if (build_no_ >= 964) {
   1093     automation::Error auto_error;
   1094     if (!SendSetPreferenceJSONRequest(
   1095             automation(), pref, scoped_value.release(), &auto_error))
   1096       *error = Error::FromAutomationError(auto_error);
   1097   } else {
   1098     std::string request, response;
   1099     DictionaryValue request_dict;
   1100     request_dict.SetString("command", "SetPrefs");
   1101     request_dict.SetInteger("windex", 0);
   1102     request_dict.SetString("path", pref);
   1103     request_dict.Set("value", scoped_value.release());
   1104     base::JSONWriter::Write(&request_dict, &request);
   1105     if (!automation()->GetBrowserWindow(0)->SendJSONRequest(
   1106             request, -1, &response)) {
   1107       *error = new Error(kUnknownError, base::StringPrintf(
   1108           "Failed to set pref '%s'", pref.c_str()));
   1109     }
   1110   }
   1111 }
   1112 
   1113 void Automation::GetGeolocation(scoped_ptr<DictionaryValue>* geolocation,
   1114                                 Error** error) {
   1115   *error = CheckGeolocationSupported();
   1116   if (*error)
   1117     return;
   1118 
   1119   if (geolocation_.get()) {
   1120     geolocation->reset(geolocation_->DeepCopy());
   1121   } else {
   1122     *error = new Error(kUnknownError,
   1123                        "Location must be set before it can be retrieved");
   1124   }
   1125 }
   1126 
   1127 void Automation::OverrideGeolocation(const DictionaryValue* geolocation,
   1128                                      Error** error) {
   1129   *error = CheckGeolocationSupported();
   1130   if (*error)
   1131     return;
   1132 
   1133   automation::Error auto_error;
   1134   if (SendOverrideGeolocationJSONRequest(
   1135           automation(), geolocation, &auto_error)) {
   1136     geolocation_.reset(geolocation->DeepCopy());
   1137   } else {
   1138     *error = Error::FromAutomationError(auto_error);
   1139   }
   1140 }
   1141 
   1142 AutomationProxy* Automation::automation() const {
   1143   return launcher_->automation();
   1144 }
   1145 
   1146 Error* Automation::ConvertViewIdToLocator(
   1147     const WebViewId& view_id, WebViewLocator* view_locator) {
   1148   if (view_id.old_style()) {
   1149     int browser_index, tab_index;
   1150     automation::Error auto_error;
   1151     if (!SendGetIndicesFromTabIdJSONRequest(
   1152             automation(), view_id.tab_id(), &browser_index, &tab_index,
   1153             &auto_error))
   1154       return Error::FromAutomationError(auto_error);
   1155     *view_locator = WebViewLocator::ForIndexPair(browser_index, tab_index);
   1156   } else {
   1157     *view_locator = WebViewLocator::ForViewId(view_id.GetId());
   1158   }
   1159   return NULL;
   1160 }
   1161 
   1162 Error* Automation::DetermineBuildNumber() {
   1163   std::string version = automation()->server_version();
   1164   std::vector<std::string> split_version;
   1165   base::SplitString(version, '.', &split_version);
   1166   if (split_version.size() != 4) {
   1167     return new Error(
   1168         kUnknownError, "Browser version has unrecognized format: " + version);
   1169   }
   1170   if (!base::StringToInt(split_version[2], &build_no_)) {
   1171     return new Error(
   1172         kUnknownError, "Browser version has unrecognized format: " + version);
   1173   }
   1174   return NULL;
   1175 }
   1176 
   1177 Error* Automation::CheckVersion(int min_required_build_no,
   1178                                 const std::string& error_msg) {
   1179   if (build_no_ < min_required_build_no)
   1180     return new Error(kUnknownError, error_msg);
   1181   return NULL;
   1182 }
   1183 
   1184 Error* Automation::CheckAlertsSupported() {
   1185   return CheckVersion(
   1186       768, "Alerts are not supported for this version of Chrome");
   1187 }
   1188 
   1189 Error* Automation::CheckAdvancedInteractionsSupported() {
   1190   const char* message =
   1191       "Advanced user interactions are not supported for this version of Chrome";
   1192   return CheckVersion(750, message);
   1193 }
   1194 
   1195 Error* Automation::CheckNewExtensionInterfaceSupported() {
   1196   const char* message =
   1197       "Extension interface is not supported for this version of Chrome";
   1198   return CheckVersion(947, message);
   1199 }
   1200 
   1201 Error* Automation::CheckGeolocationSupported() {
   1202   const char* message =
   1203       "Geolocation automation interface is not supported for this version of "
   1204       "Chrome.";
   1205   return CheckVersion(1119, message);
   1206 }
   1207 
   1208 Error* Automation::CheckMaximizeSupported() {
   1209   const char* message =
   1210       "Maximize automation interface is not supported for this version of "
   1211       "Chrome.";
   1212   return CheckVersion(1160, message);
   1213 }
   1214 
   1215 }  // namespace webdriver
   1216