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