OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import ctypes | 5 import ctypes |
6 import json | 6 import json |
7 | 7 |
| 8 from webelement import WebElement |
| 9 |
8 class ChromeDriverException(Exception): | 10 class ChromeDriverException(Exception): |
9 pass | 11 pass |
10 class NoSuchElement(ChromeDriverException): | 12 class NoSuchElement(ChromeDriverException): |
11 pass | 13 pass |
12 class UnknownCommand(ChromeDriverException): | 14 class UnknownCommand(ChromeDriverException): |
13 pass | 15 pass |
| 16 class StaleElementReference(ChromeDriverException): |
| 17 pass |
14 class UnknownError(ChromeDriverException): | 18 class UnknownError(ChromeDriverException): |
15 pass | 19 pass |
16 class XPathLookupError(ChromeDriverException): | 20 class XPathLookupError(ChromeDriverException): |
17 pass | 21 pass |
18 class InvalidSelector(ChromeDriverException): | 22 class InvalidSelector(ChromeDriverException): |
19 pass | 23 pass |
20 class SessionNotCreatedException(ChromeDriverException): | 24 class SessionNotCreatedException(ChromeDriverException): |
21 pass | 25 pass |
22 class NoSuchSession(ChromeDriverException): | 26 class NoSuchSession(ChromeDriverException): |
23 pass | 27 pass |
24 | 28 |
25 def _ExceptionForResponse(response): | 29 def _ExceptionForResponse(response): |
26 exception_class_map = { | 30 exception_class_map = { |
27 7: NoSuchElement, | 31 7: NoSuchElement, |
28 9: UnknownCommand, | 32 9: UnknownCommand, |
| 33 10: StaleElementReference, |
29 13: UnknownError, | 34 13: UnknownError, |
30 19: XPathLookupError, | 35 19: XPathLookupError, |
31 32: InvalidSelector, | 36 32: InvalidSelector, |
32 33: SessionNotCreatedException, | 37 33: SessionNotCreatedException, |
33 100: NoSuchSession | 38 100: NoSuchSession |
34 } | 39 } |
35 status = response['status'] | 40 status = response['status'] |
36 msg = response['value']['message'] | 41 msg = response['value']['message'] |
37 return exception_class_map.get(status, ChromeDriverException)(msg) | 42 return exception_class_map.get(status, ChromeDriverException)(msg) |
38 | 43 |
39 class ChromeDriver(object): | 44 class ChromeDriver(object): |
40 """Starts and controls a single Chrome instance on this machine.""" | 45 """Starts and controls a single Chrome instance on this machine.""" |
41 | 46 |
42 def __init__(self, lib_path, chrome_binary=None): | 47 def __init__(self, lib_path, chrome_binary=None): |
43 self._lib = ctypes.CDLL(lib_path) | 48 self._lib = ctypes.CDLL(lib_path) |
44 if chrome_binary is None: | 49 if chrome_binary is None: |
45 params = {} | 50 params = {} |
46 else: | 51 else: |
47 params = { | 52 params = { |
48 'desiredCapabilities': { | 53 'desiredCapabilities': { |
49 'chromeOptions': { | 54 'chromeOptions': { |
50 'binary': chrome_binary | 55 'binary': chrome_binary |
51 } | 56 } |
52 } | 57 } |
53 } | 58 } |
54 self._session_id = self._ExecuteCommand('newSession', params)['sessionId'] | 59 self._session_id = self._ExecuteCommand('newSession', params)['sessionId'] |
55 | 60 |
| 61 def _WrapValue(self, value): |
| 62 """Wrap value from client side for chromedriver side.""" |
| 63 if isinstance(value, dict): |
| 64 converted = {} |
| 65 for key, val in value.items(): |
| 66 converted[key] = self._WrapValue(val) |
| 67 return converted |
| 68 elif isinstance(value, WebElement): |
| 69 return {'ELEMENT': value._id} |
| 70 elif isinstance(value, list): |
| 71 return list(self._WrapValue(item) for item in value) |
| 72 else: |
| 73 return value |
| 74 |
| 75 def _UnwrapValue(self, value): |
| 76 """Unwrap value from chromedriver side for client side.""" |
| 77 if isinstance(value, dict): |
| 78 if (len(value) == 1 and 'ELEMENT' in value |
| 79 and isinstance(value['ELEMENT'], basestring)): |
| 80 return WebElement(self, value['ELEMENT']) |
| 81 else: |
| 82 unwraped = {} |
| 83 for key, val in value.items(): |
| 84 unwraped[key] = self._UnwrapValue(val) |
| 85 return unwraped |
| 86 elif isinstance(value, list): |
| 87 return list(self._UnwrapValue(item) for item in value) |
| 88 else: |
| 89 return value |
| 90 |
56 def _ExecuteCommand(self, name, params={}, session_id=''): | 91 def _ExecuteCommand(self, name, params={}, session_id=''): |
57 cmd = { | 92 cmd = { |
58 'name': name, | 93 'name': name, |
59 'parameters': params, | 94 'parameters': params, |
60 'sessionId': session_id | 95 'sessionId': session_id |
61 } | 96 } |
62 cmd_json = json.dumps(cmd) | 97 cmd_json = json.dumps(cmd) |
63 response_data = ctypes.c_char_p() | 98 response_data = ctypes.c_char_p() |
64 response_size = ctypes.c_uint() | 99 response_size = ctypes.c_uint() |
65 self._lib.ExecuteCommand( | 100 self._lib.ExecuteCommand( |
66 ctypes.c_char_p(cmd_json), | 101 ctypes.c_char_p(cmd_json), |
67 ctypes.c_uint(len(cmd_json)), | 102 ctypes.c_uint(len(cmd_json)), |
68 ctypes.byref(response_data), | 103 ctypes.byref(response_data), |
69 ctypes.byref(response_size)) | 104 ctypes.byref(response_size)) |
70 response_json = ctypes.string_at(response_data, response_size.value) | 105 response_json = ctypes.string_at(response_data, response_size.value) |
71 self._lib.Free(response_data) | 106 self._lib.Free(response_data) |
72 response = json.loads(response_json) | 107 response = json.loads(response_json) |
73 if response['status'] != 0: | 108 if response['status'] != 0: |
74 raise _ExceptionForResponse(response) | 109 raise _ExceptionForResponse(response) |
75 return response | 110 return response |
76 | 111 |
77 def _ExecuteSessionCommand(self, name, params={}): | 112 def ExecuteSessionCommand(self, name, params={}): |
78 return self._ExecuteCommand(name, params, self._session_id)['value'] | 113 params = self._WrapValue(params) |
| 114 return self._UnwrapValue( |
| 115 self._ExecuteCommand(name, params, self._session_id)['value']) |
79 | 116 |
80 def Load(self, url): | 117 def Load(self, url): |
81 self._ExecuteSessionCommand('get', {'url': url}) | 118 self.ExecuteSessionCommand('get', {'url': url}) |
82 | 119 |
83 def ExecuteScript(self, script, *args): | 120 def ExecuteScript(self, script, *args): |
84 return self._ExecuteSessionCommand( | 121 converted_args = list(args) |
85 'executeScript', {'script': script, 'args': args}) | 122 return self.ExecuteSessionCommand( |
| 123 'executeScript', {'script': script, 'args': converted_args}) |
86 | 124 |
87 def SwitchToFrame(self, id_or_name): | 125 def SwitchToFrame(self, id_or_name): |
88 self._ExecuteSessionCommand('switchToFrame', {'id': id_or_name}) | 126 self.ExecuteSessionCommand('switchToFrame', {'id': id_or_name}) |
89 | 127 |
90 def SwitchToFrameByIndex(self, index): | 128 def SwitchToFrameByIndex(self, index): |
91 self.SwitchToFrame(index) | 129 self.SwitchToFrame(index) |
92 | 130 |
93 def SwitchToMainFrame(self): | 131 def SwitchToMainFrame(self): |
94 self.SwitchToFrame(None) | 132 self.SwitchToFrame(None) |
95 | 133 |
96 def GetTitle(self): | 134 def GetTitle(self): |
97 return self._ExecuteSessionCommand('getTitle') | 135 return self.ExecuteSessionCommand('getTitle') |
98 | 136 |
99 def FindElement(self, strategy, target): | 137 def FindElement(self, strategy, target): |
100 return self._ExecuteSessionCommand( | 138 return self.ExecuteSessionCommand( |
101 'findElement', {'using': strategy, 'value': target}) | 139 'findElement', {'using': strategy, 'value': target}) |
102 | 140 |
103 def FindElements(self, strategy, target): | 141 def FindElements(self, strategy, target): |
104 return self._ExecuteSessionCommand( | 142 return self.ExecuteSessionCommand( |
105 'findElements', {'using': strategy, 'value': target}) | 143 'findElements', {'using': strategy, 'value': target}) |
106 | 144 |
107 def SetTimeout(self, type, timeout): | 145 def SetTimeout(self, type, timeout): |
108 return self._ExecuteSessionCommand( | 146 return self.ExecuteSessionCommand( |
109 'setTimeout', {'type' : type, 'ms': timeout}) | 147 'setTimeout', {'type' : type, 'ms': timeout}) |
110 | 148 |
111 def GetCurrentUrl(self): | 149 def GetCurrentUrl(self): |
112 return self._ExecuteSessionCommand('getCurrentUrl') | 150 return self.ExecuteSessionCommand('getCurrentUrl') |
113 | 151 |
114 def GoBack(self): | 152 def GoBack(self): |
115 return self._ExecuteSessionCommand('goBack') | 153 return self.ExecuteSessionCommand('goBack') |
116 | 154 |
117 def GoForward(self): | 155 def GoForward(self): |
118 return self._ExecuteSessionCommand('goForward') | 156 return self.ExecuteSessionCommand('goForward') |
119 | 157 |
120 def Refresh(self): | 158 def Refresh(self): |
121 return self._ExecuteSessionCommand('refresh') | 159 return self.ExecuteSessionCommand('refresh') |
122 | 160 |
123 def Quit(self): | 161 def Quit(self): |
124 """Quits the browser and ends the session.""" | 162 """Quits the browser and ends the session.""" |
125 self._ExecuteSessionCommand('quit') | 163 self.ExecuteSessionCommand('quit') |
OLD | NEW |