Home | History | Annotate | Download | only in client
      1 # Copyright 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 import command_executor
      6 from command_executor import Command
      7 from webelement import WebElement
      8 
      9 
     10 class ChromeDriverException(Exception):
     11   pass
     12 class NoSuchElement(ChromeDriverException):
     13   pass
     14 class NoSuchFrame(ChromeDriverException):
     15   pass
     16 class UnknownCommand(ChromeDriverException):
     17   pass
     18 class StaleElementReference(ChromeDriverException):
     19   pass
     20 class UnknownError(ChromeDriverException):
     21   pass
     22 class JavaScriptError(ChromeDriverException):
     23   pass
     24 class XPathLookupError(ChromeDriverException):
     25   pass
     26 class NoSuchWindow(ChromeDriverException):
     27   pass
     28 class InvalidCookieDomain(ChromeDriverException):
     29   pass
     30 class ScriptTimeout(ChromeDriverException):
     31   pass
     32 class InvalidSelector(ChromeDriverException):
     33   pass
     34 class SessionNotCreatedException(ChromeDriverException):
     35   pass
     36 class NoSuchSession(ChromeDriverException):
     37   pass
     38 
     39 def _ExceptionForResponse(response):
     40   exception_class_map = {
     41     6: NoSuchSession,
     42     7: NoSuchElement,
     43     8: NoSuchFrame,
     44     9: UnknownCommand,
     45     10: StaleElementReference,
     46     13: UnknownError,
     47     17: JavaScriptError,
     48     19: XPathLookupError,
     49     23: NoSuchWindow,
     50     24: InvalidCookieDomain,
     51     28: ScriptTimeout,
     52     32: InvalidSelector,
     53     33: SessionNotCreatedException
     54   }
     55   status = response['status']
     56   msg = response['value']['message']
     57   return exception_class_map.get(status, ChromeDriverException)(msg)
     58 
     59 
     60 class ChromeDriver(object):
     61   """Starts and controls a single Chrome instance on this machine."""
     62 
     63   def __init__(self, server_url, chrome_binary=None, android_package=None,
     64                android_activity=None, android_process=None,
     65                android_use_running_app=None, chrome_switches=None,
     66                chrome_extensions=None, chrome_log_path=None,
     67                debugger_address=None, browser_log_level=None,
     68                mobile_emulation=None, experimental_options=None):
     69     self._executor = command_executor.CommandExecutor(server_url)
     70 
     71     options = {}
     72 
     73     if experimental_options:
     74       assert isinstance(experimental_options, dict)
     75       options = experimental_options.copy()
     76 
     77     if android_package:
     78       options['androidPackage'] = android_package
     79       if android_activity:
     80         options['androidActivity'] = android_activity
     81       if android_process:
     82         options['androidProcess'] = android_process
     83       if android_use_running_app:
     84         options['androidUseRunningApp'] = android_use_running_app
     85     elif chrome_binary:
     86       options['binary'] = chrome_binary
     87 
     88     if chrome_switches:
     89       assert type(chrome_switches) is list
     90       options['args'] = chrome_switches
     91 
     92     if mobile_emulation:
     93       assert type(mobile_emulation) is dict
     94       options['mobileEmulation'] = mobile_emulation
     95 
     96     if chrome_extensions:
     97       assert type(chrome_extensions) is list
     98       options['extensions'] = chrome_extensions
     99 
    100     if chrome_log_path:
    101       assert type(chrome_log_path) is str
    102       options['logPath'] = chrome_log_path
    103 
    104     if debugger_address:
    105       assert type(debugger_address) is str
    106       options['debuggerAddress'] = debugger_address
    107 
    108     logging_prefs = {}
    109     log_levels = ['ALL', 'DEBUG', 'INFO', 'WARNING', 'SEVERE', 'OFF']
    110     if browser_log_level:
    111       assert browser_log_level in log_levels
    112       logging_prefs['browser'] = browser_log_level
    113 
    114     params = {
    115       'desiredCapabilities': {
    116         'chromeOptions': options,
    117         'loggingPrefs': logging_prefs
    118       }
    119     }
    120 
    121     response = self._ExecuteCommand(Command.NEW_SESSION, params)
    122     self._session_id = response['sessionId']
    123     self.capabilities = self._UnwrapValue(response['value'])
    124 
    125   def _WrapValue(self, value):
    126     """Wrap value from client side for chromedriver side."""
    127     if isinstance(value, dict):
    128       converted = {}
    129       for key, val in value.items():
    130         converted[key] = self._WrapValue(val)
    131       return converted
    132     elif isinstance(value, WebElement):
    133       return {'ELEMENT': value._id}
    134     elif isinstance(value, list):
    135       return list(self._WrapValue(item) for item in value)
    136     else:
    137       return value
    138 
    139   def _UnwrapValue(self, value):
    140     """Unwrap value from chromedriver side for client side."""
    141     if isinstance(value, dict):
    142       if (len(value) == 1 and 'ELEMENT' in value
    143           and isinstance(value['ELEMENT'], basestring)):
    144         return WebElement(self, value['ELEMENT'])
    145       else:
    146         unwraped = {}
    147         for key, val in value.items():
    148           unwraped[key] = self._UnwrapValue(val)
    149         return unwraped
    150     elif isinstance(value, list):
    151       return list(self._UnwrapValue(item) for item in value)
    152     else:
    153       return value
    154 
    155   def _ExecuteCommand(self, command, params={}):
    156     params = self._WrapValue(params)
    157     response = self._executor.Execute(command, params)
    158     if response['status'] != 0:
    159       raise _ExceptionForResponse(response)
    160     return response
    161 
    162   def ExecuteCommand(self, command, params={}):
    163     params['sessionId'] = self._session_id
    164     response = self._ExecuteCommand(command, params)
    165     return self._UnwrapValue(response['value'])
    166 
    167   def GetWindowHandles(self):
    168     return self.ExecuteCommand(Command.GET_WINDOW_HANDLES)
    169 
    170   def SwitchToWindow(self, handle_or_name):
    171     self.ExecuteCommand(Command.SWITCH_TO_WINDOW, {'name': handle_or_name})
    172 
    173   def GetCurrentWindowHandle(self):
    174     return self.ExecuteCommand(Command.GET_CURRENT_WINDOW_HANDLE)
    175 
    176   def CloseWindow(self):
    177     self.ExecuteCommand(Command.CLOSE)
    178 
    179   def Load(self, url):
    180     self.ExecuteCommand(Command.GET, {'url': url})
    181 
    182   def LaunchApp(self, app_id):
    183     self.ExecuteCommand(Command.LAUNCH_APP, {'id': app_id})
    184 
    185   def ExecuteScript(self, script, *args):
    186     converted_args = list(args)
    187     return self.ExecuteCommand(
    188         Command.EXECUTE_SCRIPT, {'script': script, 'args': converted_args})
    189 
    190   def ExecuteAsyncScript(self, script, *args):
    191     converted_args = list(args)
    192     return self.ExecuteCommand(
    193         Command.EXECUTE_ASYNC_SCRIPT,
    194         {'script': script, 'args': converted_args})
    195 
    196   def SwitchToFrame(self, id_or_name):
    197     self.ExecuteCommand(Command.SWITCH_TO_FRAME, {'id': id_or_name})
    198 
    199   def SwitchToFrameByIndex(self, index):
    200     self.SwitchToFrame(index)
    201 
    202   def SwitchToMainFrame(self):
    203     self.SwitchToFrame(None)
    204 
    205   def SwitchToParentFrame(self):
    206     self.ExecuteCommand(Command.SWITCH_TO_PARENT_FRAME)
    207 
    208   def GetTitle(self):
    209     return self.ExecuteCommand(Command.GET_TITLE)
    210 
    211   def GetPageSource(self):
    212     return self.ExecuteCommand(Command.GET_PAGE_SOURCE)
    213 
    214   def FindElement(self, strategy, target):
    215     return self.ExecuteCommand(
    216         Command.FIND_ELEMENT, {'using': strategy, 'value': target})
    217 
    218   def FindElements(self, strategy, target):
    219     return self.ExecuteCommand(
    220         Command.FIND_ELEMENTS, {'using': strategy, 'value': target})
    221 
    222   def SetTimeout(self, type, timeout):
    223     return self.ExecuteCommand(
    224         Command.SET_TIMEOUT, {'type' : type, 'ms': timeout})
    225 
    226   def GetCurrentUrl(self):
    227     return self.ExecuteCommand(Command.GET_CURRENT_URL)
    228 
    229   def GoBack(self):
    230     return self.ExecuteCommand(Command.GO_BACK)
    231 
    232   def GoForward(self):
    233     return self.ExecuteCommand(Command.GO_FORWARD)
    234 
    235   def Refresh(self):
    236     return self.ExecuteCommand(Command.REFRESH)
    237 
    238   def MouseMoveTo(self, element=None, x_offset=None, y_offset=None):
    239     params = {}
    240     if element is not None:
    241       params['element'] = element._id
    242     if x_offset is not None:
    243       params['xoffset'] = x_offset
    244     if y_offset is not None:
    245       params['yoffset'] = y_offset
    246     self.ExecuteCommand(Command.MOUSE_MOVE_TO, params)
    247 
    248   def MouseClick(self, button=0):
    249     self.ExecuteCommand(Command.MOUSE_CLICK, {'button': button})
    250 
    251   def MouseButtonDown(self, button=0):
    252     self.ExecuteCommand(Command.MOUSE_BUTTON_DOWN, {'button': button})
    253 
    254   def MouseButtonUp(self, button=0):
    255     self.ExecuteCommand(Command.MOUSE_BUTTON_UP, {'button': button})
    256 
    257   def MouseDoubleClick(self, button=0):
    258     self.ExecuteCommand(Command.MOUSE_DOUBLE_CLICK, {'button': button})
    259 
    260   def TouchDown(self, x, y):
    261     self.ExecuteCommand(Command.TOUCH_DOWN, {'x': x, 'y': y})
    262 
    263   def TouchUp(self, x, y):
    264     self.ExecuteCommand(Command.TOUCH_UP, {'x': x, 'y': y})
    265 
    266   def TouchMove(self, x, y):
    267     self.ExecuteCommand(Command.TOUCH_MOVE, {'x': x, 'y': y})
    268 
    269   def TouchFlick(self, element, xoffset, yoffset, speed):
    270     params = {
    271         'element': element._id,
    272         'xoffset': xoffset,
    273         'yoffset': yoffset,
    274         'speed': speed
    275     }
    276     self.ExecuteCommand(Command.TOUCH_FLICK, params)
    277 
    278   def GetCookies(self):
    279     return self.ExecuteCommand(Command.GET_COOKIES)
    280 
    281   def AddCookie(self, cookie):
    282     self.ExecuteCommand(Command.ADD_COOKIE, {'cookie': cookie})
    283 
    284   def DeleteCookie(self, name):
    285     self.ExecuteCommand(Command.DELETE_COOKIE, {'name': name})
    286 
    287   def DeleteAllCookies(self):
    288     self.ExecuteCommand(Command.DELETE_ALL_COOKIES)
    289 
    290   def IsAlertOpen(self):
    291     return self.ExecuteCommand(Command.GET_ALERT)
    292 
    293   def GetAlertMessage(self):
    294     return self.ExecuteCommand(Command.GET_ALERT_TEXT)
    295 
    296   def HandleAlert(self, accept, prompt_text=''):
    297     if prompt_text:
    298       self.ExecuteCommand(Command.SET_ALERT_VALUE, {'text': prompt_text})
    299     if accept:
    300       cmd = Command.ACCEPT_ALERT
    301     else:
    302       cmd = Command.DISMISS_ALERT
    303     self.ExecuteCommand(cmd)
    304 
    305   def IsLoading(self):
    306     return self.ExecuteCommand(Command.IS_LOADING)
    307 
    308   def GetWindowPosition(self):
    309     position = self.ExecuteCommand(Command.GET_WINDOW_POSITION,
    310                                    {'windowHandle': 'current'})
    311     return [position['x'], position['y']]
    312 
    313   def SetWindowPosition(self, x, y):
    314     self.ExecuteCommand(Command.SET_WINDOW_POSITION,
    315                         {'windowHandle': 'current', 'x': x, 'y': y})
    316 
    317   def GetWindowSize(self):
    318     size = self.ExecuteCommand(Command.GET_WINDOW_SIZE,
    319                                {'windowHandle': 'current'})
    320     return [size['width'], size['height']]
    321 
    322   def SetWindowSize(self, width, height):
    323     self.ExecuteCommand(
    324         Command.SET_WINDOW_SIZE,
    325         {'windowHandle': 'current', 'width': width, 'height': height})
    326 
    327   def MaximizeWindow(self):
    328     self.ExecuteCommand(Command.MAXIMIZE_WINDOW, {'windowHandle': 'current'})
    329 
    330   def Quit(self):
    331     """Quits the browser and ends the session."""
    332     self.ExecuteCommand(Command.QUIT)
    333 
    334   def GetLog(self, type):
    335     return self.ExecuteCommand(Command.GET_LOG, {'type': type})
    336 
    337   def GetAvailableLogTypes(self):
    338     return self.ExecuteCommand(Command.GET_AVAILABLE_LOG_TYPES)
    339 
    340   def IsAutoReporting(self):
    341     return self.ExecuteCommand(Command.IS_AUTO_REPORTING)
    342 
    343   def SetAutoReporting(self, enabled):
    344     self.ExecuteCommand(Command.SET_AUTO_REPORTING, {'enabled': enabled})
    345