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
5import command_executor
6from command_executor import Command
7from webelement import WebElement
8
9
10class ChromeDriverException(Exception):
11  pass
12class NoSuchElement(ChromeDriverException):
13  pass
14class NoSuchFrame(ChromeDriverException):
15  pass
16class UnknownCommand(ChromeDriverException):
17  pass
18class StaleElementReference(ChromeDriverException):
19  pass
20class UnknownError(ChromeDriverException):
21  pass
22class JavaScriptError(ChromeDriverException):
23  pass
24class XPathLookupError(ChromeDriverException):
25  pass
26class NoSuchWindow(ChromeDriverException):
27  pass
28class InvalidCookieDomain(ChromeDriverException):
29  pass
30class ScriptTimeout(ChromeDriverException):
31  pass
32class InvalidSelector(ChromeDriverException):
33  pass
34class SessionNotCreatedException(ChromeDriverException):
35  pass
36class NoSuchSession(ChromeDriverException):
37  pass
38
39def _ExceptionForResponse(response):
40  exception_class_map = {
41    7: NoSuchElement,
42    8: NoSuchFrame,
43    9: UnknownCommand,
44    10: StaleElementReference,
45    13: UnknownError,
46    17: JavaScriptError,
47    19: XPathLookupError,
48    23: NoSuchWindow,
49    24: InvalidCookieDomain,
50    28: ScriptTimeout,
51    32: InvalidSelector,
52    33: SessionNotCreatedException,
53    100: NoSuchSession
54  }
55  status = response['status']
56  msg = response['value']['message']
57  return exception_class_map.get(status, ChromeDriverException)(msg)
58
59
60class 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               chrome_switches=None, chrome_extensions=None,
65               chrome_log_path=None):
66    self._executor = command_executor.CommandExecutor(server_url)
67
68    options = {}
69    if android_package:
70      options['androidPackage'] = android_package
71    elif chrome_binary:
72      options['binary'] = chrome_binary
73
74    if chrome_switches:
75      assert type(chrome_switches) is list
76      options['args'] = chrome_switches
77
78    if chrome_extensions:
79      assert type(chrome_extensions) is list
80      options['extensions'] = chrome_extensions
81
82    if chrome_log_path:
83      assert type(chrome_log_path) is str
84      options['logPath'] = chrome_log_path
85
86    params = {
87      'desiredCapabilities': {
88        'chromeOptions': options
89      }
90    }
91
92    self._session_id = self._executor.Execute(
93        Command.NEW_SESSION, params)['sessionId']
94
95  def _WrapValue(self, value):
96    """Wrap value from client side for chromedriver side."""
97    if isinstance(value, dict):
98      converted = {}
99      for key, val in value.items():
100        converted[key] = self._WrapValue(val)
101      return converted
102    elif isinstance(value, WebElement):
103      return {'ELEMENT': value._id}
104    elif isinstance(value, list):
105      return list(self._WrapValue(item) for item in value)
106    else:
107      return value
108
109  def _UnwrapValue(self, value):
110    """Unwrap value from chromedriver side for client side."""
111    if isinstance(value, dict):
112      if (len(value) == 1 and 'ELEMENT' in value
113          and isinstance(value['ELEMENT'], basestring)):
114        return WebElement(self, value['ELEMENT'])
115      else:
116        unwraped = {}
117        for key, val in value.items():
118          unwraped[key] = self._UnwrapValue(val)
119        return unwraped
120    elif isinstance(value, list):
121      return list(self._UnwrapValue(item) for item in value)
122    else:
123      return value
124
125  def ExecuteCommand(self, command, params={}):
126    params['sessionId'] = self._session_id
127    params = self._WrapValue(params)
128    response = self._executor.Execute(command, params)
129    if response['status'] != 0:
130      raise _ExceptionForResponse(response)
131    return self._UnwrapValue(response['value'])
132
133  def GetWindowHandles(self):
134    return self.ExecuteCommand(Command.GET_WINDOW_HANDLES)
135
136  def SwitchToWindow(self, handle_or_name):
137    self.ExecuteCommand(Command.SWITCH_TO_WINDOW, {'name': handle_or_name})
138
139  def GetCurrentWindowHandle(self):
140    return self.ExecuteCommand(Command.GET_CURRENT_WINDOW_HANDLE)
141
142  def CloseWindow(self):
143    self.ExecuteCommand(Command.CLOSE)
144
145  def Load(self, url):
146    self.ExecuteCommand(Command.GET, {'url': url})
147
148  def ExecuteScript(self, script, *args):
149    converted_args = list(args)
150    return self.ExecuteCommand(
151        Command.EXECUTE_SCRIPT, {'script': script, 'args': converted_args})
152
153  def ExecuteAsyncScript(self, script, *args):
154    converted_args = list(args)
155    return self.ExecuteCommand(
156        Command.EXECUTE_ASYNC_SCRIPT,
157        {'script': script, 'args': converted_args})
158
159  def SwitchToFrame(self, id_or_name):
160    self.ExecuteCommand(Command.SWITCH_TO_FRAME, {'id': id_or_name})
161
162  def SwitchToFrameByIndex(self, index):
163    self.SwitchToFrame(index)
164
165  def SwitchToMainFrame(self):
166    self.SwitchToFrame(None)
167
168  def GetTitle(self):
169    return self.ExecuteCommand(Command.GET_TITLE)
170
171  def GetPageSource(self):
172    return self.ExecuteCommand(Command.GET_PAGE_SOURCE)
173
174  def FindElement(self, strategy, target):
175    return self.ExecuteCommand(
176        Command.FIND_ELEMENT, {'using': strategy, 'value': target})
177
178  def FindElements(self, strategy, target):
179    return self.ExecuteCommand(
180        Command.FIND_ELEMENTS, {'using': strategy, 'value': target})
181
182  def SetTimeout(self, type, timeout):
183    return self.ExecuteCommand(
184        Command.SET_TIMEOUT, {'type' : type, 'ms': timeout})
185
186  def GetCurrentUrl(self):
187    return self.ExecuteCommand(Command.GET_CURRENT_URL)
188
189  def GoBack(self):
190    return self.ExecuteCommand(Command.GO_BACK)
191
192  def GoForward(self):
193    return self.ExecuteCommand(Command.GO_FORWARD)
194
195  def Refresh(self):
196    return self.ExecuteCommand(Command.REFRESH)
197
198  def MouseMoveTo(self, element=None, x_offset=None, y_offset=None):
199    params = {}
200    if element is not None:
201      params['element'] = element._id
202    if x_offset is not None:
203      params['xoffset'] = x_offset
204    if y_offset is not None:
205      params['yoffset'] = y_offset
206    self.ExecuteCommand(Command.MOUSE_MOVE_TO, params)
207
208  def MouseClick(self, button=0):
209    self.ExecuteCommand(Command.MOUSE_CLICK, {'button': button})
210
211  def MouseButtonDown(self, button=0):
212    self.ExecuteCommand(Command.MOUSE_BUTTON_DOWN, {'button': button})
213
214  def MouseButtonUp(self, button=0):
215    self.ExecuteCommand(Command.MOUSE_BUTTON_UP, {'button': button})
216
217  def MouseDoubleClick(self, button=0):
218    self.ExecuteCommand(Command.MOUSE_DOUBLE_CLICK, {'button': button})
219
220  def GetCookies(self):
221    return self.ExecuteCommand(Command.GET_COOKIES)
222
223  def AddCookie(self, cookie):
224    self.ExecuteCommand(Command.ADD_COOKIE, {'cookie': cookie})
225
226  def DeleteCookie(self, name):
227    self.ExecuteCommand(Command.DELETE_COOKIE, {'name': name})
228
229  def DeleteAllCookies(self):
230    self.ExecuteCommand(Command.DELETE_ALL_COOKIES)
231
232  def IsAlertOpen(self):
233    return self.ExecuteCommand(Command.GET_ALERT)
234
235  def GetAlertMessage(self):
236    return self.ExecuteCommand(Command.GET_ALERT_TEXT)
237
238  def HandleAlert(self, accept, prompt_text=''):
239    if prompt_text:
240      self.ExecuteCommand(Command.SET_ALERT_VALUE, {'text': prompt_text})
241    if accept:
242      cmd = Command.ACCEPT_ALERT
243    else:
244      cmd = Command.DISMISS_ALERT
245    self.ExecuteCommand(cmd)
246
247  def IsLoading(self):
248    return self.ExecuteCommand(Command.IS_LOADING)
249
250  def GetWindowPosition(self):
251    position = self.ExecuteCommand(Command.GET_WINDOW_POSITION,
252                                   {'windowHandle': 'current'})
253    return [position['x'], position['y']]
254
255  def SetWindowPosition(self, x, y):
256    self.ExecuteCommand(Command.SET_WINDOW_POSITION,
257                        {'windowHandle': 'current', 'x': x, 'y': y})
258
259  def GetWindowSize(self):
260    size = self.ExecuteCommand(Command.GET_WINDOW_SIZE,
261                               {'windowHandle': 'current'})
262    return [size['width'], size['height']]
263
264  def SetWindowSize(self, width, height):
265    self.ExecuteCommand(
266        Command.SET_WINDOW_SIZE,
267        {'windowHandle': 'current', 'width': width, 'height': height})
268
269  def MaximizeWindow(self):
270    self.ExecuteCommand(Command.MAXIMIZE_WINDOW, {'windowHandle': 'current'})
271
272  def Quit(self):
273    """Quits the browser and ends the session."""
274    self.ExecuteCommand(Command.QUIT)
275