OLD | NEW |
| (Empty) |
1 #library('webdriver'); | |
2 #import('dart:json'); | |
3 #import('dart:uri'); | |
4 #import('dart:io'); | |
5 #import('dart:math'); | |
6 #source('base64decoder.dart'); | |
7 | |
8 /** | |
9 * WebDriver bindings for Dart. | |
10 * | |
11 * These bindings are based on the WebDriver JSON wire protocol spec | |
12 * (http://code.google.com/p/selenium/wiki/JsonWireProtocol). Not | |
13 * all of these commands are implemented yet by WebDriver itself. | |
14 * Nontheless this is a complete implementation of the spec as the | |
15 * unsupported commands may be supported in the future. Currently, | |
16 * there are known issues with local and session storage, script | |
17 * execution, and log access. | |
18 * | |
19 * To use these bindings, the Selenium standalone server must be running. | |
20 * You can download it at http://code.google.com/p/selenium/downloads/list. | |
21 * | |
22 * There are a number of commands that use ids to access page elements. | |
23 * These ids are not the HTML ids; they are opaque ids internal to | |
24 * WebDriver. To get the id for an element you would first need to do | |
25 * a search, get the results, and extract the WebDriver id from the returned | |
26 * [Map] using the 'ELEMENT' key. For example: | |
27 * | |
28 * String id; | |
29 * WebDriverSession session; | |
30 * Future f = web_driver.newSession('chrome'); | |
31 * f.chain((_session) { | |
32 * session = _session; | |
33 * return session.setUrl('http://my.web.site.com'); | |
34 * }).chain((_) { | |
35 * return session.findElement('id', 'username'); | |
36 * }).chain((element) { | |
37 * id = element['ELEMENT']; | |
38 * return session.sendKeyStrokesToElement(id, | |
39 * [ 'j', 'o', 'e', ' ', 'u', 's', 'e', 'r' ]); | |
40 * }).chain((_) { | |
41 * return session.submit(id); | |
42 * }).chain((_) { | |
43 * return session.close(); | |
44 * }).then((_) { | |
45 * session = null; | |
46 * }); | |
47 */ | |
48 | |
49 void writeStringToFile(String fileName, String contents) { | |
50 var file = new File(fileName); | |
51 var ostream = file.openOutputStream(FileMode.WRITE); | |
52 ostream.writeString(contents); | |
53 ostream.close(); | |
54 } | |
55 | |
56 void writeBytesToFile(String fileName, List<int> contents) { | |
57 var file = new File(fileName); | |
58 var ostream = file.openOutputStream(FileMode.WRITE); | |
59 ostream.write(contents); | |
60 ostream.close(); | |
61 } | |
62 | |
63 class WebDriverError { | |
64 static List _errorTypes = null; | |
65 static List _errorDetails = null; | |
66 int statusCode; | |
67 String type; | |
68 String message; | |
69 String details; | |
70 String results; | |
71 | |
72 WebDriverError(this.statusCode, this.message, [this.results = '']) { | |
73 /** These correspond to WebDrive exception types. */ | |
74 if (_errorTypes == null) { | |
75 _errorTypes = [ | |
76 null, | |
77 'IndexOutOfBounds', | |
78 'NoCollection', | |
79 'NoString', | |
80 'NoStringLength', | |
81 'NoStringWrapper', | |
82 'NoSuchDriver', | |
83 'NoSuchElement', | |
84 'NoSuchFrame', | |
85 'UnknownCommand', | |
86 'ObsoleteElement', | |
87 'ElementNotDisplayed', | |
88 'InvalidElementState', | |
89 'Unhandled', | |
90 'Expected', | |
91 'ElementNotSelectable', | |
92 'NoSuchDocument', | |
93 'UnexpectedJavascript', | |
94 'NoScriptResult', | |
95 'XPathLookup', | |
96 'NoSuchCollection', | |
97 'TimeOut', | |
98 'NullPointer', | |
99 'NoSuchWindow', | |
100 'InvalidCookieDomain', | |
101 'UnableToSetCookie', | |
102 'UnexpectedAlertOpen', | |
103 'NoAlertOpen', | |
104 'ScriptTimeout', | |
105 'InvalidElementCoordinates', | |
106 'IMENotAvailable', | |
107 'IMEEngineActivationFailed', | |
108 'InvalidSelector', | |
109 'SessionNotCreatedException', | |
110 'MoveTargetOutOfBounds' | |
111 ]; | |
112 // Explanations of the eror types. In thoses cases where the | |
113 // explanation is the same as the type (e.g. NoCollection), that is an | |
114 // error type used by an old version of the IE driver and is deprecated. | |
115 _errorDetails = [ | |
116 null, | |
117 'IndexOutOfBounds', | |
118 'NoCollection', | |
119 'NoString', | |
120 'NoStringLength', | |
121 'NoStringWrapper', | |
122 'NoSuchDriver', | |
123 'An element could not be located on the page using the given ' | |
124 'search parameters.', | |
125 'A request to switch to a frame could not be satisfied because the ' | |
126 'frame could not be found.', | |
127 'The requested resource could not be found, or a request was ' | |
128 'received using an HTTP method that is not supported by the ' | |
129 'mapped resource.', | |
130 'An element command failed because the referenced element is no ' | |
131 'longer attached to the DOM.', | |
132 'An element command could not be completed because the element ' | |
133 'is not visible on the page.', | |
134 'An element command could not be completed because the element is in ' | |
135 'an invalid state (e.g. attempting to click a disabled element).', | |
136 'An unknown server-side error occurred while processing the command.', | |
137 'Expected', | |
138 'An attempt was made to select an element that cannot be selected.', | |
139 'NoSuchDocument', | |
140 'An error occurred while executing user supplied JavaScript.', | |
141 'NoScriptResult', | |
142 'An error occurred while searching for an element by XPath.', | |
143 'NoSuchCollection', | |
144 'An operation did not complete before its timeout expired.', | |
145 'NullPointer', | |
146 'A request to switch to a different window could not be satisfied ' | |
147 'because the window could not be found.', | |
148 'An illegal attempt was made to set a cookie under a different ' | |
149 'domain than the current page.', | |
150 'A request to set a cookie\'s value could not be satisfied.', | |
151 'A modal dialog was open, blocking this operation.', | |
152 'An attempt was made to operate on a modal dialog when one was ' | |
153 'not open.', | |
154 'A script did not complete before its timeout expired.', | |
155 'The coordinates provided to an interactions operation are invalid.', | |
156 'IME was not available.', | |
157 'An IME engine could not be started.', | |
158 'Argument was an invalid selector (e.g. XPath/CSS).', | |
159 'A new session could not be created.', | |
160 'Target provided for a move action is out of bounds.' | |
161 ]; | |
162 } | |
163 if (statusCode < 0 || statusCode > 32) { | |
164 type = 'External'; | |
165 details = ''; | |
166 } else { | |
167 type = _errorTypes[statusCode]; | |
168 details = _errorDetails[statusCode]; | |
169 } | |
170 } | |
171 | |
172 String toString() { | |
173 return '$statusCode $type: $message $results\n$details'; | |
174 } | |
175 } | |
176 | |
177 /** | |
178 * Base class for all WebDriver request classes. This class wraps up | |
179 * an URL prefix (host, port, and initial path), and provides a client | |
180 * function for doing HTTP requests with JSON payloads. | |
181 */ | |
182 class WebDriverBase { | |
183 | |
184 String _host; | |
185 int _port; | |
186 String _path; | |
187 String _url; | |
188 | |
189 String get path => _path; | |
190 String get url => _url; | |
191 | |
192 /** | |
193 * The default URL for WebDriver remote server is | |
194 * http://localhost:4444/wd/hub. | |
195 */ | |
196 WebDriverBase.fromUrl([this._url = 'http://localhost:4444/wd/hub']) { | |
197 // Break out the URL components. | |
198 var re = const RegExp('[^:/]+://([^/]+)(/.*)'); | |
199 var matches = re.firstMatch(_url); | |
200 _host = matches[1]; | |
201 _path = matches[2]; | |
202 var idx = _host.indexOf(':'); | |
203 if (idx >= 0) { | |
204 _port = parseInt(_host.substring(idx+1)); | |
205 _host = _host.substring(0, idx); | |
206 } else { | |
207 _port = 80; | |
208 } | |
209 } | |
210 | |
211 WebDriverBase([ | |
212 this._host = 'localhost', | |
213 this._port = 4444, | |
214 this._path = '/wd/hub']) { | |
215 _url = 'http://$_host:$_port$_path'; | |
216 } | |
217 | |
218 /** | |
219 * Execute a request to the WebDriver server. [http_method] should be | |
220 * one of 'GET', 'POST', or 'DELETE'. [command] is the text to append | |
221 * to the base URL path to get the full URL. [params] are the additional | |
222 * parameters. If a [List] or [Map] they will be posted as JSON parameters. | |
223 * If a number or string, "/params" is appended to the URL. | |
224 */ | |
225 void _serverRequest(String http_method, String command, Completer completer, | |
226 [List successCodes, Map params, Function customHandler]) { | |
227 var status = 0; | |
228 var results = null; | |
229 var message = null; | |
230 if (successCodes == null) { | |
231 successCodes = [ 200, 204 ]; | |
232 } | |
233 try { | |
234 if (params != null && params is List && http_method != 'POST') { | |
235 throw new Exception( | |
236 'The http method called for ${command} is ${http_method} but it has ' | |
237 'to be POST if you want to pass the JSON params ' | |
238 '${JSON.stringify(params)}'); | |
239 } | |
240 | |
241 var path = command; | |
242 if (params != null && (params is num || params is String)) { | |
243 path = '$path/$params'; | |
244 } | |
245 | |
246 var client = new HttpClient(); | |
247 var connection = client.open(http_method, _host, _port, path); | |
248 | |
249 connection.onRequest = (r) { | |
250 r.headers.add(HttpHeaders.ACCEPT, "application/json"); | |
251 r.headers.add( | |
252 HttpHeaders.CONTENT_TYPE, 'application/json;charset=UTF-8'); | |
253 OutputStream s = r.outputStream; | |
254 if (params != null && params is Map) { | |
255 s.writeString(JSON.stringify(params)); | |
256 } | |
257 s.close(); | |
258 }; | |
259 connection.onError = (e) { | |
260 if (completer != null) { | |
261 completer.completeException(new WebDriverError(-1, e)); | |
262 completer = null; | |
263 } | |
264 }; | |
265 connection.followRedirects = false; | |
266 connection.onResponse = (r) { | |
267 StringInputStream s = new StringInputStream(r.inputStream); | |
268 StringBuffer sbuf = new StringBuffer(); | |
269 s.onData = () { | |
270 var data = s.read(); | |
271 if (data != null) { | |
272 sbuf.add(data); | |
273 } | |
274 }; | |
275 s.onClosed = () { | |
276 var value = null; | |
277 results = sbuf.toString().trim(); | |
278 // For some reason we get a bunch of NULs on the end | |
279 // of the text and the JSON parser blows up on these, so | |
280 // strip them. We have to do this the hard way as | |
281 // replaceAll('\0', '') does not work. | |
282 // These NULs can be seen in the TCP packet, so it is not | |
283 // an issue with character encoding; it seems to be a bug | |
284 // in WebDriver stack. | |
285 for (var i = results.length; --i >= 0;) { | |
286 var code = results.charCodeAt(i); | |
287 if (code != 0) { | |
288 results = results.substring(0, i+1); | |
289 break; | |
290 } | |
291 } | |
292 if (successCodes.indexOf(r.statusCode) < 0) { | |
293 throw 'Unexpected response ${r.statusCode}'; | |
294 } | |
295 if (status == 0 && results.length > 0) { | |
296 // 4xx responses send plain text; others send JSON. | |
297 if (r.statusCode < 400) { | |
298 results = JSON.parse(results); | |
299 status = results['status']; | |
300 } | |
301 if (results is Map && (results as Map).containsKey('value')) { | |
302 value = results['value']; | |
303 } | |
304 if (value is Map && value.containsKey('message')) { | |
305 message = value['message']; | |
306 } | |
307 } | |
308 if (status == 0) { | |
309 if (customHandler != null) { | |
310 customHandler(r, value); | |
311 } else if (completer != null) { | |
312 completer.complete(value); | |
313 } | |
314 } | |
315 }; | |
316 }; | |
317 } catch (e, s) { | |
318 completer.completeException( | |
319 new WebDriverError(-1, e), s); | |
320 completer = null; | |
321 } | |
322 } | |
323 | |
324 Future _simpleCommand(method, extraPath, [successCodes, params]) { | |
325 var completer = new Completer(); | |
326 _serverRequest(method, '${_path}/$extraPath', completer, | |
327 successCodes, params: params); | |
328 return completer.future; | |
329 } | |
330 | |
331 Future _get(extraPath, [successCodes]) => | |
332 _simpleCommand('GET', extraPath, successCodes); | |
333 | |
334 Future _post(extraPath, [successCodes, params]) => | |
335 _simpleCommand('POST', extraPath, successCodes, params); | |
336 | |
337 Future _delete(extraPath, [successCodes]) => | |
338 _simpleCommand('DELETE', extraPath, successCodes); | |
339 } | |
340 | |
341 class WebDriver extends WebDriverBase { | |
342 | |
343 WebDriver(host, port, path) : super(host, port, path); | |
344 | |
345 /** | |
346 * Create a new session. The server will attempt to create a session that | |
347 * most closely matches the desired and required capabilities. Required | |
348 * capabilities have higher priority than desired capabilities and must be | |
349 * set for the session to be created. | |
350 * | |
351 * The capabilities are: | |
352 * | |
353 * - browserName (String) The name of the browser being used; should be one | |
354 * of chrome|firefox|htmlunit|internet explorer|iphone. | |
355 * | |
356 * - version (String) The browser version, or the empty string if unknown. | |
357 * | |
358 * - platform (String) A key specifying which platform the browser is | |
359 * running on. This value should be one of WINDOWS|XP|VISTA|MAC|LINUX|UNIX. | |
360 * When requesting a new session, the client may specify ANY to indicate | |
361 * any available platform may be used. | |
362 * | |
363 * - javascriptEnabled (bool) Whether the session supports executing user | |
364 * supplied JavaScript in the context of the current page. | |
365 * | |
366 * - takesScreenshot (bool) Whether the session supports taking screenshots | |
367 * of the current page. | |
368 * | |
369 * - handlesAlerts (bool) Whether the session can interact with modal popups, | |
370 * such as window.alert and window.confirm. | |
371 * | |
372 * - databaseEnabled (bool) Whether the session can interact database storage. | |
373 * | |
374 * - locationContextEnabled (bool) Whether the session can set and query the | |
375 * browser's location context. | |
376 * | |
377 * - applicationCacheEnabled (bool) Whether the session can interact with | |
378 * the application cache. | |
379 * | |
380 * - browserConnectionEnabled (bool) Whether the session can query for the | |
381 * browser's connectivity and disable it if desired. | |
382 * | |
383 * - cssSelectorsEnabled (bool) Whether the session supports CSS selectors | |
384 * when searching for elements. | |
385 * | |
386 * - webStorageEnabled (bool) Whether the session supports interactions with | |
387 * storage objects. | |
388 * | |
389 * - rotatable (bool) Whether the session can rotate the current page's | |
390 * current layout between portrait and landscape orientations (only applies | |
391 * to mobile platforms). | |
392 * | |
393 * - acceptSslCerts (bool) Whether the session should accept all SSL certs | |
394 * by default. | |
395 * | |
396 * - nativeEvents (bool) Whether the session is capable of generating native | |
397 * events when simulating user input. | |
398 * | |
399 * - proxy (proxy object) Details of any proxy to use. If no proxy is | |
400 * specified, whatever the system's current or default state is used. | |
401 * | |
402 * The format of the proxy object is: | |
403 * | |
404 * - proxyType (String) The type of proxy being used. Possible values are: | |
405 * | |
406 * direct - A direct connection - no proxy in use, | |
407 * | |
408 * manual - Manual proxy settings configured, | |
409 * | |
410 * pac - Proxy autoconfiguration from a URL), | |
411 * | |
412 * autodetect (proxy autodetection, probably with WPAD), | |
413 * | |
414 * system - Use system settings | |
415 * | |
416 * - proxyAutoconfigUrl (String) Required if proxyType == pac, Ignored | |
417 * otherwise. Specifies the URL to be used for proxy autoconfiguration. | |
418 * | |
419 * - ftpProxy, httpProxy, sslProxy (String) (Optional, Ignored if | |
420 * proxyType != manual) Specifies the proxies to be used for FTP, HTTP | |
421 * and HTTPS requests respectively. Behaviour is undefined if a request | |
422 * is made, where the proxy for the particular protocol is undefined, | |
423 * if proxyType is manual. | |
424 * | |
425 * Potential Errors: SessionNotCreatedException (if a required capability | |
426 * could not be set). | |
427 */ | |
428 Future<WebDriverSession> newSession([ | |
429 browser = 'chrome', Map additional_capabilities]) { | |
430 var completer = new Completer(); | |
431 if (additional_capabilities == null) { | |
432 additional_capabilities = {}; | |
433 } | |
434 | |
435 additional_capabilities['browserName'] = browser; | |
436 | |
437 _serverRequest('POST', '${_path}/session', null, [ 302 ], | |
438 customHandler: (r, v) { | |
439 var url = r.headers.value(HttpHeaders.LOCATION); | |
440 var session = new WebDriverSession.fromUrl(url); | |
441 completer.complete(session); | |
442 }, params: { 'desiredCapabilities': additional_capabilities }); | |
443 return completer.future; | |
444 } | |
445 | |
446 /** Get the set of currently active sessions. */ | |
447 Future<List<WebDriverSession>> getSessions() { | |
448 var completer = new Completer(); | |
449 _get('sessions', (result) { | |
450 var _sessions = []; | |
451 for (var session in result) { | |
452 _sessions.add(new WebDriverSession.fromUrl( | |
453 '${this._path}/session/${session["id"]}')); | |
454 } | |
455 completer.complete(_sessions); | |
456 }); | |
457 return completer.future; | |
458 } | |
459 | |
460 /** Query the server's current status. */ | |
461 Future<Map> getStatus() => _get('status'); | |
462 } | |
463 | |
464 class WebDriverWindow extends WebDriverBase { | |
465 WebDriverWindow.fromUrl(url) : super.fromUrl(url); | |
466 | |
467 /** Get the window size. */ | |
468 Future<Map> getSize() => _get('size'); | |
469 | |
470 /** | |
471 * Set the window size. | |
472 * | |
473 * Potential Errors: | |
474 * NoSuchWindow - If the specified window cannot be found. | |
475 */ | |
476 Future<String> setSize(int width, int height) => | |
477 _post('size', params: { 'width': width, 'height': height }); | |
478 | |
479 /** Get the window position. */ | |
480 Future<Map> getPosition() => _get('position'); | |
481 | |
482 /** | |
483 * Set the window position. | |
484 * | |
485 * Potential Errors: NoSuchWindow. | |
486 */ | |
487 Future setPosition(int x, int y) => | |
488 _post('position', params: { 'x': x, 'y': y }); | |
489 | |
490 /** Maximize the specified window if not already maximized. */ | |
491 Future maximize() => _post('maximize'); | |
492 } | |
493 | |
494 class WebDriverSession extends WebDriverBase { | |
495 WebDriverSession.fromUrl(url) : super.fromUrl(url); | |
496 | |
497 /** Close the session. */ | |
498 Future close() => _delete(''); | |
499 | |
500 /** Get the session capabilities. See [newSession] for details. */ | |
501 Future<Map> getCapabilities() => _get(''); | |
502 | |
503 /** | |
504 * Configure the amount of time in milliseconds that a script can execute | |
505 * for before it is aborted and a Timeout error is returned to the client. | |
506 */ | |
507 Future setScriptTimeout(t) => | |
508 _post('timeouts', params: { 'type': 'script', 'ms': t }); | |
509 | |
510 /*Future<String> setImplicitWaitTimeout(t) => | |
511 simplePost('timeouts', { 'type': 'implicit', 'ms': t });*/ | |
512 | |
513 /** | |
514 * Configure the amount of time in milliseconds that a page can load for | |
515 * before it is aborted and a Timeout error is returned to the client. | |
516 */ | |
517 Future setPageLoadTimeout(t) => | |
518 _post('timeouts', params: { 'type': 'page load', 'ms': t }); | |
519 | |
520 /** | |
521 * Set the amount of time, in milliseconds, that asynchronous scripts | |
522 * executed by /session/:sessionId/execute_async are permitted to run | |
523 * before they are aborted and a Timeout error is returned to the client. | |
524 */ | |
525 Future setAsyncScriptTimeout(t) => | |
526 _post('timeouts/async_script', params: { 'ms': t }); | |
527 | |
528 /** | |
529 * Set the amount of time the driver should wait when searching for elements. | |
530 * When searching for a single element, the driver should poll the page until | |
531 * an element is found or the timeout expires, whichever occurs first. When | |
532 * searching for multiple elements, the driver should poll the page until at | |
533 * least one element is found or the timeout expires, at which point it should | |
534 * return an empty list. | |
535 * | |
536 * If this command is never sent, the driver should default to an implicit | |
537 * wait of 0ms. | |
538 */ | |
539 Future setImplicitWaitTimeout(t) => | |
540 _post('timeouts/implicit_wait', params: { 'ms': t }); | |
541 | |
542 /** | |
543 * Retrieve the current window handle. | |
544 * | |
545 * Potential Errors: NoSuchWindow. | |
546 */ | |
547 Future<String> getWindowHandle() => _get('window_handle'); | |
548 | |
549 /** | |
550 * Retrieve a [WebDriverWindow] for the specified window. We don't | |
551 * have to use a Future here but do so to be consistent. | |
552 */ | |
553 Future<WebDriverWindow> getWindow([handle = 'current']) { | |
554 var completer = new Completer(); | |
555 completer.complete(new WebDriverWindow.fromUrl('${_url}/window/$handle')); | |
556 return completer.future; | |
557 } | |
558 | |
559 /** Retrieve the list of all window handles available to the session. */ | |
560 Future<List<String>> getWindowHandles() => _get('window_handles'); | |
561 | |
562 /** | |
563 * Retrieve the URL of the current page. | |
564 * | |
565 * Potential Errors: NoSuchWindow. | |
566 */ | |
567 Future<String> getUrl() => _get('url'); | |
568 | |
569 /** | |
570 * Navigate to a new URL. | |
571 * | |
572 * Potential Errors: NoSuchWindow. | |
573 */ | |
574 Future setUrl(String url) => _post('url', params: { 'url': url }); | |
575 | |
576 /** | |
577 * Navigate forwards in the browser history, if possible. | |
578 * | |
579 * Potential Errors: NoSuchWindow. | |
580 */ | |
581 Future navigateForward() => _post('forward'); | |
582 | |
583 /** | |
584 * Navigate backwards in the browser history, if possible. | |
585 * | |
586 * Potential Errors: NoSuchWindow. | |
587 */ | |
588 Future navigateBack() => _post('back'); | |
589 | |
590 /** | |
591 * Refresh the current page. | |
592 * | |
593 * Potential Errors: NoSuchWindow. | |
594 */ | |
595 Future refresh() => _post('refresh'); | |
596 | |
597 /** | |
598 * Inject a snippet of JavaScript into the page for execution in the context | |
599 * of the currently selected frame. The executed script is assumed to be | |
600 * synchronous and the result of evaluating the script is returned to the | |
601 * client. | |
602 * | |
603 * The script argument defines the script to execute in the form of a | |
604 * function body. The value returned by that function will be returned to | |
605 * the client. The function will be invoked with the provided args array | |
606 * and the values may be accessed via the arguments object in the order | |
607 * specified. | |
608 * | |
609 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects | |
610 * that define a WebElement reference will be converted to the corresponding | |
611 * DOM element. Likewise, any WebElements in the script result will be | |
612 * returned to the client as WebElement JSON objects. | |
613 * | |
614 * Potential Errors: NoSuchWindow, StaleElementReference, JavaScriptError. | |
615 */ | |
616 Future execute(String script, [List args]) => | |
617 _post('execute', params: { 'script': script, 'args': args }); | |
618 | |
619 /** | |
620 * Inject a snippet of JavaScript into the page for execution in the context | |
621 * of the currently selected frame. The executed script is assumed to be | |
622 * asynchronous and must signal that it is done by invoking the provided | |
623 * callback, which is always provided as the final argument to the function. | |
624 * The value to this callback will be returned to the client. | |
625 * | |
626 * Asynchronous script commands may not span page loads. If an unload event | |
627 * is fired while waiting for a script result, an error should be returned | |
628 * to the client. | |
629 * | |
630 * The script argument defines the script to execute in the form of a function | |
631 * body. The function will be invoked with the provided args array and the | |
632 * values may be accessed via the arguments object in the order specified. | |
633 * The final argument will always be a callback function that must be invoked | |
634 * to signal that the script has finished. | |
635 * | |
636 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects | |
637 * that define a WebElement reference will be converted to the corresponding | |
638 * DOM element. Likewise, any WebElements in the script result will be | |
639 * returned to the client as WebElement JSON objects. | |
640 * | |
641 * Potential Errors: NoSuchWindow, StaleElementReference, Timeout (controlled | |
642 * by the [setAsyncScriptTimeout] command), JavaScriptError (if the script | |
643 * callback is not invoked before the timout expires). | |
644 */ | |
645 Future executeAsync(String script, [List args]) => | |
646 _post('execute_async', params: { 'script': script, 'args': args }); | |
647 | |
648 /** | |
649 * Take a screenshot of the current page (PNG). | |
650 * | |
651 * Potential Errors: NoSuchWindow. | |
652 */ | |
653 Future<List<int>> getScreenshot([fname]) { | |
654 var completer = new Completer(); | |
655 var result = _serverRequest('GET', '$_path/screenshot', completer, | |
656 customHandler: (r, v) { | |
657 var image = Base64Decoder.decode(v); | |
658 if (fname != null) { | |
659 writeBytesToFile(fname, image); | |
660 } | |
661 completer.complete(image); | |
662 }); | |
663 return completer.future; | |
664 } | |
665 | |
666 /** | |
667 * List all available IME (Input Method Editor) engines on the machine. | |
668 * To use an engine, it has to be present in this list. | |
669 * | |
670 * Potential Errors: ImeNotAvailableException. | |
671 */ | |
672 Future<List<String>> getAvailableImeEngines() => | |
673 _get('ime/available_engines'); | |
674 | |
675 /** | |
676 * Get the name of the active IME engine. The name string is | |
677 * platform specific. | |
678 * | |
679 * Potential Errors: ImeNotAvailableException. | |
680 */ | |
681 Future<String> getActiveImeEngine() => _get('ime/active_engine'); | |
682 | |
683 /** | |
684 * Indicates whether IME input is active at the moment (not if | |
685 * it's available). | |
686 * | |
687 * Potential Errors: ImeNotAvailableException. | |
688 */ | |
689 Future<bool> getIsImeActive() => _get('ime/activated'); | |
690 | |
691 /** | |
692 * De-activates the currently-active IME engine. | |
693 * | |
694 * Potential Errors: ImeNotAvailableException. | |
695 */ | |
696 Future deactivateIme() => _post('ime/deactivate'); | |
697 | |
698 /** | |
699 * Make an engine that is available (appears on the list returned by | |
700 * getAvailableEngines) active. After this call, the engine will be added | |
701 * to the list of engines loaded in the IME daemon and the input sent using | |
702 * sendKeys will be converted by the active engine. Note that this is a | |
703 * platform-independent method of activating IME (the platform-specific way | |
704 * being using keyboard shortcuts). | |
705 * | |
706 * Potential Errors: ImeActivationFailedException, ImeNotAvailableException. | |
707 */ | |
708 Future activateIme(String engine) => | |
709 _post('ime/activate', params: { 'engine': engine }); | |
710 | |
711 /** | |
712 * Change focus to another frame on the page. If the frame id is null, | |
713 * the server should switch to the page's default content. | |
714 * [id] is the Identifier for the frame to change focus to, and can be | |
715 * a string, number, null, or JSON Object. | |
716 * | |
717 * Potential Errors: NoSuchWindow, NoSuchFrame. | |
718 */ | |
719 Future setFrameFocus(id) => _post('frame', params: { 'id': id }); | |
720 | |
721 /** | |
722 * Change focus to another window. The window to change focus to may be | |
723 * specified by [name], which is its server assigned window handle, or | |
724 * the value of its name attribute. | |
725 * | |
726 * Potential Errors: NoSuchWindow. | |
727 */ | |
728 Future setWindowFocus(name) => | |
729 _post('window', params: { 'name': name }); | |
730 | |
731 /** | |
732 * Close the current window. | |
733 * | |
734 * Potential Errors: NoSuchWindow. | |
735 */ | |
736 Future closeWindow() => _delete('window'); | |
737 | |
738 /** | |
739 * Retrieve all cookies visible to the current page. | |
740 * | |
741 * The returned List contains Maps with the following keys: | |
742 * | |
743 * 'name' - The name of the cookie. | |
744 * | |
745 * 'value' - The cookie value. | |
746 * | |
747 * The following keys may optionally be present: | |
748 * | |
749 * 'path' - The cookie path. | |
750 * | |
751 * 'domain' - The domain the cookie is visible to. | |
752 * | |
753 * 'secure' - Whether the cookie is a secure cookie. | |
754 * | |
755 * 'expiry' - When the cookie expires, seconds since midnight, 1/1/1970 UTC. | |
756 * | |
757 * Potential Errors: NoSuchWindow. | |
758 */ | |
759 Future<List<Map>> getCookies() => _get('cookie'); | |
760 | |
761 /** | |
762 * Set a cookie. If the cookie path is not specified, it should be set | |
763 * to "/". Likewise, if the domain is omitted, it should default to the | |
764 * current page's domain. See [getCookies] for the structure of a cookie | |
765 * Map. | |
766 */ | |
767 Future setCookie(Map cookie) => | |
768 _post('cookie', params: { 'cookie': cookie }); | |
769 | |
770 /** | |
771 * Delete all cookies visible to the current page. | |
772 * | |
773 * Potential Errors: InvalidCookieDomain (the cookie's domain is not | |
774 * visible from the current page), NoSuchWindow, UnableToSetCookie (if | |
775 * attempting to set a cookie on a page that does not support cookies, | |
776 * e.g. pages with mime-type text/plain). | |
777 */ | |
778 Future deleteCookies() => _delete('cookie'); | |
779 | |
780 /** | |
781 * Delete the cookie with the given [name]. This command should be a no-op | |
782 * if there is no such cookie visible to the current page. | |
783 * | |
784 * Potential Errors: NoSuchWindow. | |
785 */ | |
786 Future deleteCookie(String name) => _delete('cookie/$name'); | |
787 | |
788 /** | |
789 * Get the current page source. | |
790 * | |
791 * Potential Errors: NoSuchWindow. | |
792 */ | |
793 Future<String> getPageSource() => _get('source'); | |
794 | |
795 /** | |
796 * Get the current page title. | |
797 * | |
798 * Potential Errors: NoSuchWindow. | |
799 */ | |
800 Future<String> getPageTitle() => _get('title'); | |
801 | |
802 /** | |
803 * Search for an element on the page, starting from the document root. The | |
804 * first matching located element will be returned as a WebElement JSON | |
805 * object (a [Map] with an 'ELEMENT' key whose value should be used to | |
806 * identify the element in further requests). The [strategy] should be | |
807 * one of: | |
808 * | |
809 * 'class name' - Returns an element whose class name contains the search | |
810 * value; compound class names are not permitted. | |
811 * | |
812 * 'css selector' - Returns an element matching a CSS selector. | |
813 * | |
814 * 'id' - Returns an element whose ID attribute matches the search value. | |
815 * | |
816 * 'name' - Returns an element whose NAME attribute matches the search value. | |
817 * | |
818 * 'link text' - Returns an anchor element whose visible text matches the | |
819 * search value. | |
820 * | |
821 * 'partial link text' - Returns an anchor element whose visible text | |
822 * partially matches the search value. | |
823 * | |
824 * 'tag name' - Returns an element whose tag name matches the search value. | |
825 * | |
826 * 'xpath' - Returns an element matching an XPath expression. | |
827 * | |
828 * Potential Errors: NoSuchWindow, NoSuchElement, XPathLookupError (if | |
829 * using XPath and the input expression is invalid). | |
830 */ | |
831 Future<String> findElement(String strategy, String searchValue) => | |
832 _post('element', params: { 'using': strategy, 'value' : searchValue }); | |
833 | |
834 /** | |
835 * Search for multiple elements on the page, starting from the document root. | |
836 * The located elements will be returned as WebElement JSON objects. See | |
837 * [findElement] for the locator strategies that each server supports. | |
838 * Elements are be returned in the order located in the DOM. | |
839 * | |
840 * Potential Errors: NoSuchWindow, XPathLookupError. | |
841 */ | |
842 Future<List<String>> findElements(String strategy, String searchValue) => | |
843 _post('elements', params: { 'using': strategy, 'value' : searchValue }); | |
844 | |
845 /** | |
846 * Get the element on the page that currently has focus. The element will | |
847 * be returned as a WebElement JSON object. | |
848 * | |
849 * Potential Errors: NoSuchWindow. | |
850 */ | |
851 Future<String> getElementWithFocus() => _post('element/active'); | |
852 | |
853 /** | |
854 * Search for an element on the page, starting from element with id [id]. | |
855 * The located element will be returned as WebElement JSON objects. See | |
856 * [findElement] for the locator strategies that each server supports. | |
857 * | |
858 * Potential Errors: NoSuchWindow, XPathLookupError. | |
859 */ | |
860 Future<String> | |
861 findElementFromId(String id, String strategy, String searchValue) { | |
862 _post('element/$id/element', | |
863 params: { 'using': strategy, 'value' : searchValue }); | |
864 } | |
865 | |
866 /** | |
867 * Search for multiple elements on the page, starting from the element with | |
868 * id [id].The located elements will be returned as WebElement JSON objects. | |
869 * See [findElement] for the locator strategies that each server supports. | |
870 * Elements are be returned in the order located in the DOM. | |
871 * | |
872 * Potential Errors: NoSuchWindow, XPathLookupError. | |
873 */ | |
874 Future<List<String>> | |
875 findElementsFromId(String id, String strategy, String searchValue) => | |
876 _post('element/$id/elements', | |
877 params: { 'using': strategy, 'value' : searchValue }); | |
878 | |
879 /** | |
880 * Click on an element specified by [id]. | |
881 * | |
882 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible | |
883 * (if the referenced element is not visible on the page, either hidden | |
884 * by CSS, or has 0-width or 0-height). | |
885 */ | |
886 Future clickElement(String id) => _post('element/$id/click'); | |
887 | |
888 /** | |
889 * Submit a FORM element. The submit command may also be applied to any | |
890 * element that is a descendant of a FORM element. | |
891 * | |
892 * Potential Errors: NoSuchWindow, StaleElementReference. | |
893 */ | |
894 Future submit(String id) => _post('element/$id/submit'); | |
895 | |
896 /** Returns the visible text for the element. | |
897 * | |
898 * Potential Errors: NoSuchWindow, StaleElementReference. | |
899 */ | |
900 Future<String> getElementText(String id) => _get('element/$id/text'); | |
901 | |
902 /** | |
903 * Send a sequence of key strokes to an element. | |
904 * | |
905 * Any UTF-8 character may be specified, however, if the server does not | |
906 * support native key events, it will simulate key strokes for a standard | |
907 * US keyboard layout. The Unicode Private Use Area code points, | |
908 * 0xE000-0xF8FF, are used to represent pressable, non-text keys: | |
909 * | |
910 * NULL - U+E000 | |
911 * | |
912 * Cancel - U+E001 | |
913 * | |
914 * Help - U+E002 | |
915 * | |
916 * Backspace - U+E003 | |
917 * | |
918 * Tab - U+E004 | |
919 * | |
920 * Clear - U+E005 | |
921 * | |
922 * Return - U+E006 | |
923 * | |
924 * Enter - U+E007 | |
925 * | |
926 * Shift - U+E008 | |
927 * | |
928 * Control - U+E009 | |
929 * | |
930 * Alt - U+E00A | |
931 * | |
932 * Pause - U+E00B | |
933 * | |
934 * Escape - U+E00C | |
935 * | |
936 * Space - U+E00D | |
937 * | |
938 * Pageup - U+E00E | |
939 * | |
940 * Pagedown - U+E00F | |
941 * | |
942 * End - U+E010 | |
943 * | |
944 * Home - U+E011 | |
945 * | |
946 * Left arrow - U+E012 | |
947 * | |
948 * Up arrow - U+E013 | |
949 * | |
950 * Right arrow - U+E014 | |
951 * | |
952 * Down arrow - U+E015 | |
953 * | |
954 * Insert - U+E016 | |
955 * | |
956 * Delete - U+E017 | |
957 * | |
958 * Semicolon - U+E018 | |
959 * | |
960 * Equals - U+E019 | |
961 * | |
962 * Numpad 0..9 - U+E01A..U+E023 | |
963 * | |
964 * Multiply - U+E024 | |
965 * | |
966 * Add - U+E025 | |
967 * | |
968 * Separator - U+E026 | |
969 * | |
970 * Subtract - U+E027 | |
971 * | |
972 * Decimal - U+E028 | |
973 * | |
974 * Divide - U+E029 | |
975 * | |
976 * F1..F12 - U+E031..U+E03C | |
977 * | |
978 * Command/Meta U+E03D | |
979 * | |
980 * The server processes the key sequence as follows: | |
981 * | |
982 * - Each key that appears on the keyboard without requiring modifiers is | |
983 * sent as a keydown followed by a key up. | |
984 * | |
985 * - If the server does not support native events and must simulate key | |
986 * strokes with JavaScript, it will generate keydown, keypress, and keyup | |
987 * events, in that order. The keypress event is only fired when the | |
988 * corresponding key is for a printable character. | |
989 * | |
990 * - If a key requires a modifier key (e.g. "!" on a standard US keyboard), | |
991 * the sequence is: modifier down, key down, key up, modifier up, where | |
992 * key is the ideal unmodified key value (using the previous example, | |
993 * a "1"). | |
994 * | |
995 * - Modifier keys (Ctrl, Shift, Alt, and Command/Meta) are assumed to be | |
996 * "sticky"; each modifier is held down (e.g. only a keydown event) until | |
997 * either the modifier is encountered again in the sequence, or the NULL | |
998 * (U+E000) key is encountered. | |
999 * | |
1000 * - Each key sequence is terminated with an implicit NULL key. | |
1001 * Subsequently, all depressed modifier keys are released (with | |
1002 * corresponding keyup events) at the end of the sequence. | |
1003 * | |
1004 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible. | |
1005 */ | |
1006 Future sendKeyStrokesToElement(String id, List<String> keys) => | |
1007 _post('element/$id/value', params: { 'value': keys }); | |
1008 | |
1009 /** | |
1010 * Send a sequence of key strokes to the active element. This command is | |
1011 * similar to [sendKeyStrokesToElement] command in every aspect except the | |
1012 * implicit termination: The modifiers are not released at the end of the | |
1013 * call. Rather, the state of the modifier keys is kept between calls, | |
1014 * so mouse interactions can be performed while modifier keys are depressed. | |
1015 * | |
1016 * Potential Errors: NoSuchWindow. | |
1017 */ | |
1018 Future sendKeyStrokes(List<String> keys) => | |
1019 _post('keys', params: { 'value': keys }); | |
1020 | |
1021 /** | |
1022 * Query for an element's tag name, as a lower-case string. | |
1023 * | |
1024 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1025 */ | |
1026 Future<String> getElementTagName(String id) => _get('element/$id/name'); | |
1027 | |
1028 /** | |
1029 * Clear a TEXTAREA or text INPUT element's value. | |
1030 * | |
1031 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible, | |
1032 * InvalidElementState. | |
1033 */ | |
1034 Future clearValue(String id) => _post('/element/$id/clear'); | |
1035 | |
1036 /** | |
1037 * Determine if an OPTION element, or an INPUT element of type checkbox | |
1038 * or radiobutton is currently selected. | |
1039 * | |
1040 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1041 */ | |
1042 Future<bool> isSelected(String id) => _get('element/$id/selected'); | |
1043 | |
1044 /** | |
1045 * Determine if an element is currently enabled. | |
1046 * | |
1047 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1048 */ | |
1049 Future<bool> isEnabled(String id) => _get('element/$id/enabled'); | |
1050 | |
1051 /** | |
1052 * Get the value of an element's attribute, or null if it has no such | |
1053 * attribute. | |
1054 * | |
1055 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1056 */ | |
1057 Future<String> getAttribute(String id, String attribute) => | |
1058 _get('element/$id/attribute/$attribute'); | |
1059 | |
1060 /** | |
1061 * Test if two element IDs refer to the same DOM element. | |
1062 * | |
1063 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1064 */ | |
1065 Future<bool> areSameElement(String id, String other) => | |
1066 _get('element/$id/equals/$other'); | |
1067 | |
1068 /** | |
1069 * Determine if an element is currently displayed. | |
1070 * | |
1071 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1072 */ | |
1073 Future<bool> isDiplayed(String id) => _get('element/$id/displayed'); | |
1074 | |
1075 /** | |
1076 * Determine an element's location on the page. The point (0, 0) refers to | |
1077 * the upper-left corner of the page. The element's coordinates are returned | |
1078 * as a [Map] object with x and y properties. | |
1079 * | |
1080 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1081 */ | |
1082 Future<Map> getElementLocation(String id) => _get('element/$id/location'); | |
1083 | |
1084 /** | |
1085 * Determine an element's size in pixels. The size will be returned as a | |
1086 * [Map] object with width and height properties. | |
1087 * | |
1088 * Potential Errors: NoSuchWindow, StalElementReference. | |
1089 */ | |
1090 Future<Map> getElementSize(String id) => _get('element/$id/size'); | |
1091 | |
1092 /** | |
1093 * Query the value of an element's computed CSS property. The CSS property | |
1094 * to query should be specified using the CSS property name, not the | |
1095 * JavaScript property name (e.g. background-color instead of | |
1096 * backgroundColor). | |
1097 * | |
1098 * Potential Errors: NoSuchWindow, StaleElementReference. | |
1099 */ | |
1100 Future<String> getElementCssProperty(String id, String property) => | |
1101 _get('element/$id/css/$property'); | |
1102 | |
1103 /** | |
1104 * Get the current browser orientation ('LANDSCAPE' or 'PORTRAIT'). | |
1105 * | |
1106 * Potential Errors: NoSuchWindow. | |
1107 */ | |
1108 Future<String> getBrowserOrientation() => _get('orientation'); | |
1109 | |
1110 /** | |
1111 * Gets the text of the currently displayed JavaScript alert(), confirm(), | |
1112 * or prompt() dialog. | |
1113 * | |
1114 * Potential Errors: NoAlertPresent. | |
1115 */ | |
1116 Future<String> getAlertText() => _get('alert_text'); | |
1117 | |
1118 /** | |
1119 * Sends keystrokes to a JavaScript prompt() dialog. | |
1120 * | |
1121 * Potential Errors: NoAlertPresent. | |
1122 */ | |
1123 Future sendKeyStrokesToPrompt(String text) => | |
1124 _post('alert_text', params: { 'text': text }); | |
1125 | |
1126 /** | |
1127 * Accepts the currently displayed alert dialog. Usually, this is equivalent | |
1128 * to clicking on the 'OK' button in the dialog. | |
1129 * | |
1130 * Potential Errors: NoAlertPresent. | |
1131 */ | |
1132 Future acceptAlert() => _post('accept_alert'); | |
1133 | |
1134 /** | |
1135 * Dismisses the currently displayed alert dialog. For confirm() and prompt() | |
1136 * dialogs, this is equivalent to clicking the 'Cancel' button. For alert() | |
1137 * dialogs, this is equivalent to clicking the 'OK' button. | |
1138 * | |
1139 * Potential Errors: NoAlertPresent. | |
1140 */ | |
1141 Future dismissAlert() => _post('dismiss_alert'); | |
1142 | |
1143 /** | |
1144 * Move the mouse by an offset of the specificed element. If no element is | |
1145 * specified, the move is relative to the current mouse cursor. If an | |
1146 * element is provided but no offset, the mouse will be moved to the center | |
1147 * of the element. If the element is not visible, it will be scrolled | |
1148 * into view. | |
1149 */ | |
1150 Future moveTo(String id, int x, int y) => | |
1151 _post('moveto', params: { 'element': id, 'xoffset': x, 'yoffset' : y}); | |
1152 | |
1153 /** | |
1154 * Click a mouse button (at the coordinates set by the last [moveTo] command). | |
1155 * Note that calling this command after calling [buttonDown] and before | |
1156 * calling [buttonUp] (or any out-of-order interactions sequence) will yield | |
1157 * undefined behaviour). | |
1158 * | |
1159 * [button] should be 0 for left, 1 for middle, or 2 for right. | |
1160 */ | |
1161 Future clickMouse([button = 0]) => | |
1162 _post('click', params: { 'button' : button }); | |
1163 | |
1164 /** | |
1165 * Click and hold the left mouse button (at the coordinates set by the last | |
1166 * [moveTo] command). Note that the next mouse-related command that should | |
1167 * follow is [buttonDown]. Any other mouse command (such as [click] or | |
1168 * another call to [buttonDown]) will yield undefined behaviour. | |
1169 * | |
1170 * [button] should be 0 for left, 1 for middle, or 2 for right. | |
1171 */ | |
1172 Future buttonDown([button = 0]) => | |
1173 _post('click', params: { 'button' : button }); | |
1174 | |
1175 /** | |
1176 * Releases the mouse button previously held (where the mouse is currently | |
1177 * at). Must be called once for every [buttonDown] command issued. See the | |
1178 * note in [click] and [buttonDown] about implications of out-of-order | |
1179 * commands. | |
1180 * | |
1181 * [button] should be 0 for left, 1 for middle, or 2 for right. | |
1182 */ | |
1183 Future buttonUp([button = 0]) => | |
1184 _post('click', params: { 'button' : button }); | |
1185 | |
1186 /** Double-clicks at the current mouse coordinates (set by [moveTo]). */ | |
1187 Future doubleClick() => _post('doubleclick'); | |
1188 | |
1189 /** Single tap on the touch enabled device on the element with id [id]. */ | |
1190 Future touchClick(String id) => | |
1191 _post('touch/click', params: { 'element': id }); | |
1192 | |
1193 /** Finger down on the screen. */ | |
1194 Future touchDown(int x, int y) => | |
1195 _post('touch/down', params: { 'x': x, 'y': y }); | |
1196 | |
1197 /** Finger up on the screen. */ | |
1198 Future touchUp(int x, int y) => | |
1199 _post('touch/up', params: { 'x': x, 'y': y }); | |
1200 | |
1201 /** Finger move on the screen. */ | |
1202 Future touchMove(int x, int y) => | |
1203 _post('touch/move', params: { 'x': x, 'y': y }); | |
1204 | |
1205 /** | |
1206 * Scroll on the touch screen using finger based motion events. If [id] is | |
1207 * specified, scrolling will start at a particular screen location. | |
1208 */ | |
1209 Future touchScroll(int xOffset, int yOffset, [String id = null]) { | |
1210 if (id == null) { | |
1211 return _post('touch/scroll', | |
1212 params: { 'xoffset': xOffset, 'yoffset': yOffset }); | |
1213 } else { | |
1214 return _post('touch/scroll', | |
1215 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset }); | |
1216 } | |
1217 } | |
1218 | |
1219 /** Double tap on the touch screen using finger motion events. */ | |
1220 Future touchDoubleClick(String id) => | |
1221 _post('touch/doubleclick', params: { 'element': id }); | |
1222 | |
1223 /** Long press on the touch screen using finger motion events. */ | |
1224 Future touchLongClick(String id) => | |
1225 _post('touch/longclick', params: { 'element': id }); | |
1226 | |
1227 /** | |
1228 * Flick on the touch screen using finger based motion events, starting | |
1229 * at a particular screen location. [speed] is in pixels-per-second. | |
1230 */ | |
1231 Future touchFlickFrom(String id, int xOffset, int yOffset, int speed) => | |
1232 _post('touch/flick', | |
1233 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset, | |
1234 'speed': speed }); | |
1235 | |
1236 /** | |
1237 * Flick on the touch screen using finger based motion events. Use this | |
1238 * instead of [touchFlickFrom] if you don'tr care where the flick starts. | |
1239 */ | |
1240 Future touchFlick(int xSpeed, int ySpeed) => | |
1241 _post('touch/flick', params: { 'xSpeed': xSpeed, 'ySpeed': ySpeed }); | |
1242 | |
1243 /** | |
1244 * Get the current geo location. Returns a [Map] with latitude, | |
1245 * longitude and altitude properties. | |
1246 */ | |
1247 Future<Map> getGeolocation() => _get('location'); | |
1248 | |
1249 /** Set the current geo location. */ | |
1250 Future setLocation(double latitude, double longitude, double altitude) => | |
1251 _post('location', params: | |
1252 { 'latitude': latitude, | |
1253 'longitude': longitude, | |
1254 'altitude': altitude }); | |
1255 | |
1256 /** | |
1257 * Get all keys of the local storage. Completes with [null] if there | |
1258 * are no keys or the keys could not be retrieved. | |
1259 * | |
1260 * Potential Errors: NoSuchWindow. | |
1261 */ | |
1262 Future<List<String>> getLocalStorageKeys() => _get('local_storage'); | |
1263 | |
1264 /** | |
1265 * Set the local storage item for the given key. | |
1266 * | |
1267 * Potential Errors: NoSuchWindow. | |
1268 */ | |
1269 Future setLocalStorageItem(String key, String value) => | |
1270 _post('local_storage', params: { 'key': key, 'value': value }); | |
1271 | |
1272 /** | |
1273 * Clear the local storage. | |
1274 * | |
1275 * Potential Errors: NoSuchWindow. | |
1276 */ | |
1277 Future clearLocalStorage() => _delete('local_storage'); | |
1278 | |
1279 /** | |
1280 * Get the local storage item for the given key. | |
1281 * | |
1282 * Potential Errors: NoSuchWindow. | |
1283 */ | |
1284 Future<String> getLocalStorageValue(String key) => | |
1285 _get('local_storage/key/$key'); | |
1286 | |
1287 /** | |
1288 * Delete the local storage item for the given key. | |
1289 * | |
1290 * Potential Errors: NoSuchWindow. | |
1291 */ | |
1292 Future deleteLocalStorageValue(String key) => | |
1293 _delete('local_storage/key/$key'); | |
1294 | |
1295 /** | |
1296 * Get the number of items in the local storage. | |
1297 * | |
1298 * Potential Errors: NoSuchWindow. | |
1299 */ | |
1300 Future<int> getLocalStorageCount() => _get('local_storage/size'); | |
1301 | |
1302 /** | |
1303 * Get all keys of the session storage. | |
1304 * | |
1305 * Potential Errors: NoSuchWindow. | |
1306 */ | |
1307 Future<List<String>> getSessionStorageKeys() => _get('session_storage'); | |
1308 | |
1309 /** | |
1310 * Set the sessionstorage item for the given key. | |
1311 * | |
1312 * Potential Errors: NoSuchWindow. | |
1313 */ | |
1314 Future setSessionStorageItem(String key, String value) => | |
1315 _post('session_storage', params: { 'key': key, 'value': value }); | |
1316 | |
1317 /** | |
1318 * Clear the session storage. | |
1319 * | |
1320 * Potential Errors: NoSuchWindow. | |
1321 */ | |
1322 Future clearSessionStorage() => _delete('session_storage'); | |
1323 | |
1324 /** | |
1325 * Get the session storage item for the given key. | |
1326 * | |
1327 * Potential Errors: NoSuchWindow. | |
1328 */ | |
1329 Future<String> getSessionStorageValue(String key) => | |
1330 _get('session_storage/key/$key'); | |
1331 | |
1332 /** | |
1333 * Delete the session storage item for the given key. | |
1334 * | |
1335 * Potential Errors: NoSuchWindow. | |
1336 */ | |
1337 Future deleteSessionStorageValue(String key) => | |
1338 _delete('session_storage/key/$key'); | |
1339 | |
1340 /** | |
1341 * Get the number of items in the session storage. | |
1342 * | |
1343 * Potential Errors: NoSuchWindow. | |
1344 */ | |
1345 Future<String> getSessionStorageCount() => _get('session_storage/size'); | |
1346 | |
1347 /** Get available log types ('client', 'driver', 'browser', 'server'). */ | |
1348 Future<List<String>> getLogTypes() => _get('log/types'); | |
1349 | |
1350 /** | |
1351 * Get the log for a given log type. Log buffer is reset after each request. | |
1352 * Each log entry is a [Map] with these fields: | |
1353 * | |
1354 * 'timestamp' (int) - The timestamp of the entry. | |
1355 * 'level' (String) - The log level of the entry, for example, "INFO". | |
1356 * 'message' (String) - The log message. | |
1357 */ | |
1358 Future<List<Map>> getLogs(String type) => | |
1359 _post('log', params: { 'type': type }); | |
1360 } | |
OLD | NEW |