Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Side by Side Diff: pkg/webdriver/lib/webdriver.dart

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

Powered by Google App Engine
This is Rietveld 408576698