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.
5 import command_executor
6 from command_executor
import Command
7 from webelement
import WebElement
10 class ChromeDriverException(Exception):
12 class NoSuchElement(ChromeDriverException
):
14 class NoSuchFrame(ChromeDriverException
):
16 class UnknownCommand(ChromeDriverException
):
18 class StaleElementReference(ChromeDriverException
):
20 class UnknownError(ChromeDriverException
):
22 class JavaScriptError(ChromeDriverException
):
24 class XPathLookupError(ChromeDriverException
):
26 class NoSuchWindow(ChromeDriverException
):
28 class InvalidCookieDomain(ChromeDriverException
):
30 class ScriptTimeout(ChromeDriverException
):
32 class InvalidSelector(ChromeDriverException
):
34 class SessionNotCreatedException(ChromeDriverException
):
36 class NoSuchSession(ChromeDriverException
):
39 def _ExceptionForResponse(response
):
40 exception_class_map
= {
45 10: StaleElementReference
,
50 24: InvalidCookieDomain
,
53 33: SessionNotCreatedException
55 status
= response
['status']
56 msg
= response
['value']['message']
57 return exception_class_map
.get(status
, ChromeDriverException
)(msg
)
60 class ChromeDriver(object):
61 """Starts and controls a single Chrome instance on this machine."""
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 performance_log_level
=None, mobile_emulation
=None,
69 experimental_options
=None, download_dir
=None):
70 self
._executor
= command_executor
.CommandExecutor(server_url
)
74 if experimental_options
:
75 assert isinstance(experimental_options
, dict)
76 options
= experimental_options
.copy()
79 options
['androidPackage'] = android_package
81 options
['androidActivity'] = android_activity
83 options
['androidProcess'] = android_process
84 if android_use_running_app
:
85 options
['androidUseRunningApp'] = android_use_running_app
87 options
['binary'] = chrome_binary
90 assert type(chrome_switches
) is list
91 options
['args'] = chrome_switches
94 assert type(mobile_emulation
) is dict
95 options
['mobileEmulation'] = mobile_emulation
98 assert type(chrome_extensions
) is list
99 options
['extensions'] = chrome_extensions
102 assert type(chrome_log_path
) is str
103 options
['logPath'] = chrome_log_path
106 assert type(debugger_address
) is str
107 options
['debuggerAddress'] = debugger_address
110 log_levels
= ['ALL', 'DEBUG', 'INFO', 'WARNING', 'SEVERE', 'OFF']
111 if browser_log_level
:
112 assert browser_log_level
in log_levels
113 logging_prefs
['browser'] = browser_log_level
114 if performance_log_level
:
115 assert performance_log_level
in log_levels
116 logging_prefs
['performance'] = performance_log_level
120 if 'prefs' not in options
:
121 options
['prefs'] = {}
122 if 'download' not in options
['prefs']:
123 options
['prefs']['download'] = {}
124 options
['prefs']['download']['default_directory'] = download_dir
127 'desiredCapabilities': {
128 'chromeOptions': options
,
129 'loggingPrefs': logging_prefs
133 response
= self
._ExecuteCommand
(Command
.NEW_SESSION
, params
)
134 self
._session
_id
= response
['sessionId']
135 self
.capabilities
= self
._UnwrapValue
(response
['value'])
137 def _WrapValue(self
, value
):
138 """Wrap value from client side for chromedriver side."""
139 if isinstance(value
, dict):
141 for key
, val
in value
.items():
142 converted
[key
] = self
._WrapValue
(val
)
144 elif isinstance(value
, WebElement
):
145 return {'ELEMENT': value
._id
}
146 elif isinstance(value
, list):
147 return list(self
._WrapValue
(item
) for item
in value
)
151 def _UnwrapValue(self
, value
):
152 """Unwrap value from chromedriver side for client side."""
153 if isinstance(value
, dict):
154 if (len(value
) == 1 and 'ELEMENT' in value
155 and isinstance(value
['ELEMENT'], basestring
)):
156 return WebElement(self
, value
['ELEMENT'])
159 for key
, val
in value
.items():
160 unwraped
[key
] = self
._UnwrapValue
(val
)
162 elif isinstance(value
, list):
163 return list(self
._UnwrapValue
(item
) for item
in value
)
167 def _ExecuteCommand(self
, command
, params
={}):
168 params
= self
._WrapValue
(params
)
169 response
= self
._executor
.Execute(command
, params
)
170 if response
['status'] != 0:
171 raise _ExceptionForResponse(response
)
174 def ExecuteCommand(self
, command
, params
={}):
175 params
['sessionId'] = self
._session
_id
176 response
= self
._ExecuteCommand
(command
, params
)
177 return self
._UnwrapValue
(response
['value'])
179 def GetWindowHandles(self
):
180 return self
.ExecuteCommand(Command
.GET_WINDOW_HANDLES
)
182 def SwitchToWindow(self
, handle_or_name
):
183 self
.ExecuteCommand(Command
.SWITCH_TO_WINDOW
, {'name': handle_or_name
})
185 def GetCurrentWindowHandle(self
):
186 return self
.ExecuteCommand(Command
.GET_CURRENT_WINDOW_HANDLE
)
188 def CloseWindow(self
):
189 self
.ExecuteCommand(Command
.CLOSE
)
192 self
.ExecuteCommand(Command
.GET
, {'url': url
})
194 def LaunchApp(self
, app_id
):
195 self
.ExecuteCommand(Command
.LAUNCH_APP
, {'id': app_id
})
197 def ExecuteScript(self
, script
, *args
):
198 converted_args
= list(args
)
199 return self
.ExecuteCommand(
200 Command
.EXECUTE_SCRIPT
, {'script': script
, 'args': converted_args
})
202 def ExecuteAsyncScript(self
, script
, *args
):
203 converted_args
= list(args
)
204 return self
.ExecuteCommand(
205 Command
.EXECUTE_ASYNC_SCRIPT
,
206 {'script': script
, 'args': converted_args
})
208 def SwitchToFrame(self
, id_or_name
):
209 self
.ExecuteCommand(Command
.SWITCH_TO_FRAME
, {'id': id_or_name
})
211 def SwitchToFrameByIndex(self
, index
):
212 self
.SwitchToFrame(index
)
214 def SwitchToMainFrame(self
):
215 self
.SwitchToFrame(None)
217 def SwitchToParentFrame(self
):
218 self
.ExecuteCommand(Command
.SWITCH_TO_PARENT_FRAME
)
220 def GetSessions(self
):
221 return self
.ExecuteCommand(Command
.GET_SESSIONS
)
224 return self
.ExecuteCommand(Command
.GET_TITLE
)
226 def GetPageSource(self
):
227 return self
.ExecuteCommand(Command
.GET_PAGE_SOURCE
)
229 def FindElement(self
, strategy
, target
):
230 return self
.ExecuteCommand(
231 Command
.FIND_ELEMENT
, {'using': strategy
, 'value': target
})
233 def FindElements(self
, strategy
, target
):
234 return self
.ExecuteCommand(
235 Command
.FIND_ELEMENTS
, {'using': strategy
, 'value': target
})
237 def SetTimeout(self
, type, timeout
):
238 return self
.ExecuteCommand(
239 Command
.SET_TIMEOUT
, {'type' : type, 'ms': timeout
})
241 def GetCurrentUrl(self
):
242 return self
.ExecuteCommand(Command
.GET_CURRENT_URL
)
245 return self
.ExecuteCommand(Command
.GO_BACK
)
248 return self
.ExecuteCommand(Command
.GO_FORWARD
)
251 return self
.ExecuteCommand(Command
.REFRESH
)
253 def MouseMoveTo(self
, element
=None, x_offset
=None, y_offset
=None):
255 if element
is not None:
256 params
['element'] = element
._id
257 if x_offset
is not None:
258 params
['xoffset'] = x_offset
259 if y_offset
is not None:
260 params
['yoffset'] = y_offset
261 self
.ExecuteCommand(Command
.MOUSE_MOVE_TO
, params
)
263 def MouseClick(self
, button
=0):
264 self
.ExecuteCommand(Command
.MOUSE_CLICK
, {'button': button
})
266 def MouseButtonDown(self
, button
=0):
267 self
.ExecuteCommand(Command
.MOUSE_BUTTON_DOWN
, {'button': button
})
269 def MouseButtonUp(self
, button
=0):
270 self
.ExecuteCommand(Command
.MOUSE_BUTTON_UP
, {'button': button
})
272 def MouseDoubleClick(self
, button
=0):
273 self
.ExecuteCommand(Command
.MOUSE_DOUBLE_CLICK
, {'button': button
})
275 def TouchDown(self
, x
, y
):
276 self
.ExecuteCommand(Command
.TOUCH_DOWN
, {'x': x
, 'y': y
})
278 def TouchUp(self
, x
, y
):
279 self
.ExecuteCommand(Command
.TOUCH_UP
, {'x': x
, 'y': y
})
281 def TouchMove(self
, x
, y
):
282 self
.ExecuteCommand(Command
.TOUCH_MOVE
, {'x': x
, 'y': y
})
284 def TouchScroll(self
, element
, xoffset
, yoffset
):
285 params
= {'element': element
._id
, 'xoffset': xoffset
, 'yoffset': yoffset
}
286 self
.ExecuteCommand(Command
.TOUCH_SCROLL
, params
)
288 def TouchFlick(self
, element
, xoffset
, yoffset
, speed
):
290 'element': element
._id
,
295 self
.ExecuteCommand(Command
.TOUCH_FLICK
, params
)
297 def TouchPinch(self
, x
, y
, scale
):
298 params
= {'x': x
, 'y': y
, 'scale': scale
}
299 self
.ExecuteCommand(Command
.TOUCH_PINCH
, params
)
301 def GetCookies(self
):
302 return self
.ExecuteCommand(Command
.GET_COOKIES
)
304 def AddCookie(self
, cookie
):
305 self
.ExecuteCommand(Command
.ADD_COOKIE
, {'cookie': cookie
})
307 def DeleteCookie(self
, name
):
308 self
.ExecuteCommand(Command
.DELETE_COOKIE
, {'name': name
})
310 def DeleteAllCookies(self
):
311 self
.ExecuteCommand(Command
.DELETE_ALL_COOKIES
)
313 def IsAlertOpen(self
):
314 return self
.ExecuteCommand(Command
.GET_ALERT
)
316 def GetAlertMessage(self
):
317 return self
.ExecuteCommand(Command
.GET_ALERT_TEXT
)
319 def HandleAlert(self
, accept
, prompt_text
=''):
321 self
.ExecuteCommand(Command
.SET_ALERT_VALUE
, {'text': prompt_text
})
323 cmd
= Command
.ACCEPT_ALERT
325 cmd
= Command
.DISMISS_ALERT
326 self
.ExecuteCommand(cmd
)
329 return self
.ExecuteCommand(Command
.IS_LOADING
)
331 def GetWindowPosition(self
):
332 position
= self
.ExecuteCommand(Command
.GET_WINDOW_POSITION
,
333 {'windowHandle': 'current'})
334 return [position
['x'], position
['y']]
336 def SetWindowPosition(self
, x
, y
):
337 self
.ExecuteCommand(Command
.SET_WINDOW_POSITION
,
338 {'windowHandle': 'current', 'x': x
, 'y': y
})
340 def GetWindowSize(self
):
341 size
= self
.ExecuteCommand(Command
.GET_WINDOW_SIZE
,
342 {'windowHandle': 'current'})
343 return [size
['width'], size
['height']]
345 def SetWindowSize(self
, width
, height
):
347 Command
.SET_WINDOW_SIZE
,
348 {'windowHandle': 'current', 'width': width
, 'height': height
})
350 def MaximizeWindow(self
):
351 self
.ExecuteCommand(Command
.MAXIMIZE_WINDOW
, {'windowHandle': 'current'})
354 """Quits the browser and ends the session."""
355 self
.ExecuteCommand(Command
.QUIT
)
357 def GetLog(self
, type):
358 return self
.ExecuteCommand(Command
.GET_LOG
, {'type': type})
360 def GetAvailableLogTypes(self
):
361 return self
.ExecuteCommand(Command
.GET_AVAILABLE_LOG_TYPES
)
363 def IsAutoReporting(self
):
364 return self
.ExecuteCommand(Command
.IS_AUTO_REPORTING
)
366 def SetAutoReporting(self
, enabled
):
367 self
.ExecuteCommand(Command
.SET_AUTO_REPORTING
, {'enabled': enabled
})
369 def SetNetworkConditions(self
, latency
, download_throughput
,
370 upload_throughput
, offline
=False):
371 # Until http://crbug.com/456324 is resolved, we'll always set 'offline' to
372 # False, as going "offline" will sever Chromedriver's connection to Chrome.
374 'network_conditions': {
377 'download_throughput': download_throughput
,
378 'upload_throughput': upload_throughput
381 self
.ExecuteCommand(Command
.SET_NETWORK_CONDITIONS
, params
)
383 def SetNetworkConditionsName(self
, network_name
):
385 Command
.SET_NETWORK_CONDITIONS
, {'network_name': network_name
})
387 def GetNetworkConditions(self
):
388 conditions
= self
.ExecuteCommand(Command
.GET_NETWORK_CONDITIONS
)
390 'latency': conditions
['latency'],
391 'download_throughput': conditions
['download_throughput'],
392 'upload_throughput': conditions
['upload_throughput'],
393 'offline': conditions
['offline']
396 def DeleteNetworkConditions(self
):
397 self
.ExecuteCommand(Command
.DELETE_NETWORK_CONDITIONS
)