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

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

Issue 12413012: Bring webdriver up to date with SDK changes, and a bit of refactoring. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 9 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/unittest/lib/vm_config.dart ('k') | pkg/webdriver/test/webdriver_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library webdriver; 5 library webdriver;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:io'; 8 import 'dart:io';
9 import 'dart:json' as json; 9 import 'dart:json' as json;
10 import 'dart:uri'; 10 import 'dart:uri';
(...skipping 16 matching lines...) Expand all
27 * 27 *
28 * There are a number of commands that use ids to access page elements. 28 * There are a number of commands that use ids to access page elements.
29 * These ids are not the HTML ids; they are opaque ids internal to 29 * These ids are not the HTML ids; they are opaque ids internal to
30 * WebDriver. To get the id for an element you would first need to do 30 * WebDriver. To get the id for an element you would first need to do
31 * a search, get the results, and extract the WebDriver id from the returned 31 * a search, get the results, and extract the WebDriver id from the returned
32 * [Map] using the 'ELEMENT' key. For example: 32 * [Map] using the 'ELEMENT' key. For example:
33 * 33 *
34 * String id; 34 * String id;
35 * WebDriverSession session; 35 * WebDriverSession session;
36 * Future f = web_driver.newSession('chrome'); 36 * Future f = web_driver.newSession('chrome');
37 * f.chain((_session) { 37 * f.then((_session) {
38 * session = _session; 38 * session = _session;
39 * return session.setUrl('http://my.web.site.com'); 39 * return session.setUrl('http://my.web.site.com');
40 * }).chain((_) { 40 * }).then((_) {
41 * return session.findElement('id', 'username'); 41 * return session.findElement('id', 'username');
42 * }).chain((element) { 42 * }).then((element) {
43 * id = element['ELEMENT']; 43 * id = element['ELEMENT'];
44 * return session.sendKeyStrokesToElement(id, 44 * return session.sendKeyStrokesToElement(id,
45 * [ 'j', 'o', 'e', ' ', 'u', 's', 'e', 'r' ]); 45 * [ 'j', 'o', 'e', ' ', 'u', 's', 'e', 'r' ]);
46 * }).chain((_) { 46 * }).then((_) {
47 * return session.submit(id); 47 * return session.submit(id);
48 * }).chain((_) { 48 * }).then((_) {
49 * return session.close(); 49 * return session.close();
50 * }).then((_) { 50 * }).then((_) {
51 * session = null; 51 * session = null;
52 * }); 52 * });
53 */ 53 */
54 54
55 void writeStringToFile(String fileName, String contents) { 55 void writeStringToFile(String fileName, String contents) {
56 new File(fileName).writeAsStringSync(contents); 56 new File(fileName).writeAsStringSync(contents);
57 } 57 }
58 58
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 } 208 }
209 } 209 }
210 210
211 WebDriverBase([ 211 WebDriverBase([
212 this._host = 'localhost', 212 this._host = 'localhost',
213 this._port = 4444, 213 this._port = 4444,
214 this._path = '/wd/hub']) { 214 this._path = '/wd/hub']) {
215 _url = 'http://$_host:$_port$_path'; 215 _url = 'http://$_host:$_port$_path';
216 } 216 }
217 217
218 void _failRequest(Completer completer, error, StackTrace stackTrace) {
219 if (completer != null) {
220 completer.completeError(new WebDriverError(-1, error), stackTrace);
221 }
222 }
223
218 /** 224 /**
219 * Execute a request to the WebDriver server. [http_method] should be 225 * Execute a request to the WebDriver server. [http_method] should be
220 * one of 'GET', 'POST', or 'DELETE'. [command] is the text to append 226 * 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 227 * 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. 228 * 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. 229 * If a number or string, "/params" is appended to the URL.
224 */ 230 */
225 void _serverRequest(String http_method, String command, Completer completer, 231 void _serverRequest(String http_method, String command, Completer completer,
226 [List successCodes, Map params, Function customHandler]) { 232 {List successCodes, params, Function customHandler}) {
227 var status = 0; 233 var status = 0;
228 var results = null; 234 var results = null;
229 var message = null; 235 var message = null;
230 if (successCodes == null) { 236 if (successCodes == null) {
231 successCodes = [ 200, 204 ]; 237 successCodes = [ 200, 204 ];
232 } 238 }
233 try { 239 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; 240 var path = command;
242 if (params != null && (params is num || params is String)) { 241 if (params != null) {
243 path = '$path/$params'; 242 if (params is num || params is String) {
243 path = '$path/$params';
244 params = null;
245 } else if (http_method != 'POST') {
246 throw new Exception(
247 'The http method called for ${command} is ${http_method} but it has '
Siggi Cherem (dart-lang) 2013/03/14 16:41:24 80 col
248 'to be POST if you want to pass the JSON params '
249 '${json.stringify(params)}');
250 }
244 } 251 }
245 252
246 var client = new HttpClient(); 253 var client = new HttpClient();
247 var connection = client.open(http_method, _host, _port, path); 254 client.open(http_method, _host, _port, path).then((req) {
248 255 req.followRedirects = false;
249 connection.onRequest = (r) { 256 req.headers.add(HttpHeaders.ACCEPT, "application/json");
250 r.headers.add(HttpHeaders.ACCEPT, "application/json"); 257 req.headers.add(
251 r.headers.add(
252 HttpHeaders.CONTENT_TYPE, 'application/json;charset=UTF-8'); 258 HttpHeaders.CONTENT_TYPE, 'application/json;charset=UTF-8');
253 OutputStream s = r.outputStream; 259 if (params != null) {
254 if (params != null && params is Map) { 260 var body = json.stringify(params);
255 s.writeString(json.stringify(params)); 261 req.write(body);
256 } 262 }
257 s.close(); 263 req.close().then((rsp) {
258 }; 264 List<int> body = new List<int>();
259 connection.onError = (e) { 265 rsp.listen(body.addAll, onDone: () {
260 if (completer != null) { 266 var value = null;
261 completer.completeError(new WebDriverError(-1, e)); 267 results = new String.fromCharCodes(body);
268 // For some reason we get a bunch of NULs on the end
269 // of the text and the json.parse blows up on these, so
270 // strip them.
Siggi Cherem (dart-lang) 2013/03/14 16:41:24 would results.trim() work here?
271 // These NULs can be seen in the TCP packet, so it is not
272 // an issue with character encoding; it seems to be a bug
273 // in WebDriver stack.
274 for (var i = results.length; --i >= 0;) {
Siggi Cherem (dart-lang) 2013/03/14 16:41:24 style nit for (var i = results.length - 1; i >= 0;
275 var code = results.codeUnitAt(i);
276 if (code != 0) {
277 results = results.substring(0, i + 1);
278 break;
279 }
280 }
281 if (successCodes.indexOf(rsp.statusCode) < 0) {
Siggi Cherem (dart-lang) 2013/03/14 16:41:24 now that we have it, let's switch to use List.cont
282 _failRequest(completer,
283 'Unexpected response ${rsp.statusCode}; $results', null);
284 completer = null;
285 return;
286 }
287 if (status == 0 && results.length > 0) {
288 // 4xx responses send plain text; others send JSON.
289 if (rsp.statusCode < 400) {
290 results = json.parse(results);
291 status = results['status'];
292 }
293 if (results is Map && (results as Map).containsKey('value')) {
294 value = results['value'];
295 }
296 if (value is Map && value.containsKey('message')) {
297 message = value['message'];
298 }
299 }
300 if (status == 0) {
301 if (customHandler != null) {
302 customHandler(rsp, value);
303 } else if (completer != null) {
304 completer.complete(value);
305 }
306 }
307 }, onError: (e) {
308 _failRequest(completer, e.error, e.stackTrace);
309 completer = null;
310 });
311 })
312 .catchError((e) {
313 _failRequest(completer, e.error, e.stackTrace);
262 completer = null; 314 completer = null;
263 } 315 });
264 }; 316 })
265 connection.followRedirects = false; 317 .catchError((e) {
266 connection.onResponse = (r) { 318 _failRequest(completer, e.error, e.stackTrace);
267 StringInputStream s = new StringInputStream(r.inputStream); 319 completer = null;
268 StringBuffer sbuf = new StringBuffer(); 320 });
269 s.onData = () {
270 var data = s.read();
271 if (data != null) {
272 sbuf.write(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.parse blows up on these, so
280 // strip them.
281 // These NULs can be seen in the TCP packet, so it is not
282 // an issue with character encoding; it seems to be a bug
283 // in WebDriver stack.
284 for (var i = results.length; --i >= 0;) {
285 var code = results.codeUnitAt(i);
286 if (code != 0) {
287 results = results.substring(0, i + 1);
288 break;
289 }
290 }
291 if (successCodes.indexOf(r.statusCode) < 0) {
292 throw 'Unexpected response ${r.statusCode}';
293 }
294 if (status == 0 && results.length > 0) {
295 // 4xx responses send plain text; others send JSON.
296 if (r.statusCode < 400) {
297 results = json.parse(results);
298 status = results['status'];
299 }
300 if (results is Map && (results as Map).containsKey('value')) {
301 value = results['value'];
302 }
303 if (value is Map && value.containsKey('message')) {
304 message = value['message'];
305 }
306 }
307 if (status == 0) {
308 if (customHandler != null) {
309 customHandler(r, value);
310 } else if (completer != null) {
311 completer.complete(value);
312 }
313 }
314 };
315 };
316 } catch (e, s) { 321 } catch (e, s) {
317 completer.completeError( 322 _failRequest(completer, e, s);
318 new WebDriverError(-1, e), s);
319 completer = null; 323 completer = null;
320 } 324 }
321 } 325 }
322 326
323 Future _simpleCommand(method, extraPath, [successCodes, params]) { 327 Future _get(String extraPath,
324 var completer = new Completer(); 328 [Completer completer, Function customHandler]) {
325 _serverRequest(method, '${_path}/$extraPath', completer, 329 if (completer == null) completer = new Completer();
326 successCodes, params: params); 330 _serverRequest('GET', '${_path}/$extraPath', completer,
331 customHandler: customHandler);
327 return completer.future; 332 return completer.future;
328 } 333 }
329 334
330 Future _get(extraPath, [successCodes]) => 335 Future _post(String extraPath, [params]) {
331 _simpleCommand('GET', extraPath, successCodes); 336 var completer = new Completer();
337 _serverRequest('POST', '${_path}/$extraPath', completer,
338 params: params);
339 return completer.future;
340 }
332 341
333 Future _post(extraPath, [successCodes, params]) => 342 Future _delete(String extraPath) {
334 _simpleCommand('POST', extraPath, successCodes, params); 343 var completer = new Completer();
335 344 _serverRequest('DELETE', '${_path}/$extraPath', completer);
336 Future _delete(extraPath, [successCodes]) => 345 return completer.future;
337 _simpleCommand('DELETE', extraPath, successCodes); 346 }
338 } 347 }
339 348
340 class WebDriver extends WebDriverBase { 349 class WebDriver extends WebDriverBase {
341 350
342 WebDriver(host, port, path) : super(host, port, path); 351 WebDriver(host, port, path) : super(host, port, path);
343 352
344 /** 353 /**
345 * Create a new session. The server will attempt to create a session that 354 * Create a new session. The server will attempt to create a session that
346 * most closely matches the desired and required capabilities. Required 355 * most closely matches the desired and required capabilities. Required
347 * capabilities have higher priority than desired capabilities and must be 356 * capabilities have higher priority than desired capabilities and must be
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
426 */ 435 */
427 Future<WebDriverSession> newSession([ 436 Future<WebDriverSession> newSession([
428 browser = 'chrome', Map additional_capabilities]) { 437 browser = 'chrome', Map additional_capabilities]) {
429 var completer = new Completer(); 438 var completer = new Completer();
430 if (additional_capabilities == null) { 439 if (additional_capabilities == null) {
431 additional_capabilities = {}; 440 additional_capabilities = {};
432 } 441 }
433 442
434 additional_capabilities['browserName'] = browser; 443 additional_capabilities['browserName'] = browser;
435 444
436 _serverRequest('POST', '${_path}/session', null, [ 302 ], 445 _serverRequest('POST', '${_path}/session', completer,
446 successCodes: [ 302 ],
437 customHandler: (r, v) { 447 customHandler: (r, v) {
438 var url = r.headers.value(HttpHeaders.LOCATION); 448 var url = r.headers.value(HttpHeaders.LOCATION);
439 var session = new WebDriverSession.fromUrl(url); 449 var session = new WebDriverSession.fromUrl(url);
440 completer.complete(session); 450 completer.complete(session);
441 }, params: { 'desiredCapabilities': additional_capabilities }); 451 },
452 params: { 'desiredCapabilities': additional_capabilities });
442 return completer.future; 453 return completer.future;
443 } 454 }
444 455
445 /** Get the set of currently active sessions. */ 456 /** Get the set of currently active sessions. */
446 Future<List<WebDriverSession>> getSessions() { 457 Future<List<WebDriverSession>> getSessions() {
447 var completer = new Completer(); 458 var completer = new Completer();
448 _get('sessions', (result) { 459 return _get('sessions', completer, (r, v) {
449 var _sessions = []; 460 var _sessions = [];
450 for (var session in result) { 461 for (var session in v) {
451 _sessions.add(new WebDriverSession.fromUrl( 462 var url = 'http://${this._host}:${this._port}${this._path}/'
452 '${this._path}/session/${session["id"]}')); 463 'session/${session["id"]}';
464 _sessions.add(new WebDriverSession.fromUrl(url));
453 } 465 }
454 completer.complete(_sessions); 466 completer.complete(_sessions);
455 }); 467 });
456 return completer.future;
457 } 468 }
458 469
459 /** Query the server's current status. */ 470 /** Query the server's current status. */
460 Future<Map> getStatus() => _get('status'); 471 Future<Map> getStatus() => _get('status');
461 } 472 }
462 473
463 class WebDriverWindow extends WebDriverBase { 474 class WebDriverWindow extends WebDriverBase {
464 WebDriverWindow.fromUrl(url) : super.fromUrl(url); 475 WebDriverWindow.fromUrl(url) : super.fromUrl(url);
465 476
466 /** Get the window size. */ 477 /** Get the window size. */
467 Future<Map> getSize() => _get('size'); 478 Future<Map> getSize() => _get('size');
468 479
469 /** 480 /**
470 * Set the window size. 481 * Set the window size. Note that this is flaky and often
482 * has no effect.
471 * 483 *
472 * Potential Errors: 484 * Potential Errors:
473 * NoSuchWindow - If the specified window cannot be found. 485 * NoSuchWindow - If the specified window cannot be found.
474 */ 486 */
475 Future<String> setSize(int width, int height) => 487 Future<String> setSize(int width, int height) =>
476 _post('size', params: { 'width': width, 'height': height }); 488 _post('size', { 'width': width, 'height': height });
477 489
478 /** Get the window position. */ 490 /** Get the window position. */
479 Future<Map> getPosition() => _get('position'); 491 Future<Map> getPosition() => _get('position');
480 492
481 /** 493 /**
482 * Set the window position. 494 * Set the window position. Note that this is flaky and often
495 * has no effect.
483 * 496 *
484 * Potential Errors: NoSuchWindow. 497 * Potential Errors: NoSuchWindow.
485 */ 498 */
486 Future setPosition(int x, int y) => 499 Future setPosition(int x, int y) =>
487 _post('position', params: { 'x': x, 'y': y }); 500 _post('position', { 'x': x, 'y': y });
488 501
489 /** Maximize the specified window if not already maximized. */ 502 /** Maximize the specified window if not already maximized. */
490 Future maximize() => _post('maximize'); 503 Future maximize() => _post('maximize');
491 } 504 }
492 505
493 class WebDriverSession extends WebDriverBase { 506 class WebDriverSession extends WebDriverBase {
494 WebDriverSession.fromUrl(url) : super.fromUrl(url); 507 WebDriverSession.fromUrl(url) : super.fromUrl(url);
495 508
496 /** Close the session. */ 509 /** Close the session. */
497 Future close() => _delete(''); 510 Future close() => _delete('');
498 511
499 /** Get the session capabilities. See [newSession] for details. */ 512 /** Get the session capabilities. See [newSession] for details. */
500 Future<Map> getCapabilities() => _get(''); 513 Future<Map> getCapabilities() => _get('');
501 514
502 /** 515 /**
503 * Configure the amount of time in milliseconds that a script can execute 516 * Configure the amount of time in milliseconds that a script can execute
504 * for before it is aborted and a Timeout error is returned to the client. 517 * for before it is aborted and a Timeout error is returned to the client.
505 */ 518 */
506 Future setScriptTimeout(t) => 519 Future setScriptTimeout(t) =>
507 _post('timeouts', params: { 'type': 'script', 'ms': t }); 520 _post('timeouts', { 'type': 'script', 'ms': t });
508 521
509 /*Future<String> setImplicitWaitTimeout(t) => 522 /*Future<String> setImplicitWaitTimeout(t) =>
510 simplePost('timeouts', { 'type': 'implicit', 'ms': t });*/ 523 simplePost('timeouts', { 'type': 'implicit', 'ms': t });*/
511 524
512 /** 525 /**
513 * Configure the amount of time in milliseconds that a page can load for 526 * Configure the amount of time in milliseconds that a page can load for
514 * before it is aborted and a Timeout error is returned to the client. 527 * before it is aborted and a Timeout error is returned to the client.
515 */ 528 */
516 Future setPageLoadTimeout(t) => 529 Future setPageLoadTimeout(t) =>
517 _post('timeouts', params: { 'type': 'page load', 'ms': t }); 530 _post('timeouts', { 'type': 'page load', 'ms': t });
518 531
519 /** 532 /**
520 * Set the amount of time, in milliseconds, that asynchronous scripts 533 * Set the amount of time, in milliseconds, that asynchronous scripts
521 * executed by /session/:sessionId/execute_async are permitted to run 534 * executed by /session/:sessionId/execute_async are permitted to run
522 * before they are aborted and a Timeout error is returned to the client. 535 * before they are aborted and a Timeout error is returned to the client.
523 */ 536 */
524 Future setAsyncScriptTimeout(t) => 537 Future setAsyncScriptTimeout(t) =>
525 _post('timeouts/async_script', params: { 'ms': t }); 538 _post('timeouts/async_script', { 'ms': t });
526 539
527 /** 540 /**
528 * Set the amount of time the driver should wait when searching for elements. 541 * Set the amount of time the driver should wait when searching for elements.
529 * When searching for a single element, the driver should poll the page until 542 * When searching for a single element, the driver should poll the page until
530 * an element is found or the timeout expires, whichever occurs first. When 543 * an element is found or the timeout expires, whichever occurs first. When
531 * searching for multiple elements, the driver should poll the page until at 544 * searching for multiple elements, the driver should poll the page until at
532 * least one element is found or the timeout expires, at which point it should 545 * least one element is found or the timeout expires, at which point it should
533 * return an empty list. 546 * return an empty list.
534 * 547 *
535 * If this command is never sent, the driver should default to an implicit 548 * If this command is never sent, the driver should default to an implicit
536 * wait of 0ms. 549 * wait of 0ms.
537 */ 550 */
538 Future setImplicitWaitTimeout(t) => 551 Future setImplicitWaitTimeout(t) =>
539 _post('timeouts/implicit_wait', params: { 'ms': t }); 552 _post('timeouts/implicit_wait', { 'ms': t });
540 553
541 /** 554 /**
542 * Retrieve the current window handle. 555 * Retrieve the current window handle.
543 * 556 *
544 * Potential Errors: NoSuchWindow. 557 * Potential Errors: NoSuchWindow.
545 */ 558 */
546 Future<String> getWindowHandle() => _get('window_handle'); 559 Future<String> getWindowHandle() => _get('window_handle');
547 560
548 /** 561 /**
549 * Retrieve a [WebDriverWindow] for the specified window. We don't 562 * Retrieve a [WebDriverWindow] for the specified window. We don't
(...skipping 13 matching lines...) Expand all
563 * 576 *
564 * Potential Errors: NoSuchWindow. 577 * Potential Errors: NoSuchWindow.
565 */ 578 */
566 Future<String> getUrl() => _get('url'); 579 Future<String> getUrl() => _get('url');
567 580
568 /** 581 /**
569 * Navigate to a new URL. 582 * Navigate to a new URL.
570 * 583 *
571 * Potential Errors: NoSuchWindow. 584 * Potential Errors: NoSuchWindow.
572 */ 585 */
573 Future setUrl(String url) => _post('url', params: { 'url': url }); 586 Future setUrl(String url) => _post('url', { 'url': url });
574 587
575 /** 588 /**
576 * Navigate forwards in the browser history, if possible. 589 * Navigate forwards in the browser history, if possible.
577 * 590 *
578 * Potential Errors: NoSuchWindow. 591 * Potential Errors: NoSuchWindow.
579 */ 592 */
580 Future navigateForward() => _post('forward'); 593 Future navigateForward() => _post('forward');
581 594
582 /** 595 /**
583 * Navigate backwards in the browser history, if possible. 596 * Navigate backwards in the browser history, if possible.
(...skipping 21 matching lines...) Expand all
605 * and the values may be accessed via the arguments object in the order 618 * and the values may be accessed via the arguments object in the order
606 * specified. 619 * specified.
607 * 620 *
608 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects 621 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects
609 * that define a WebElement reference will be converted to the corresponding 622 * that define a WebElement reference will be converted to the corresponding
610 * DOM element. Likewise, any WebElements in the script result will be 623 * DOM element. Likewise, any WebElements in the script result will be
611 * returned to the client as WebElement JSON objects. 624 * returned to the client as WebElement JSON objects.
612 * 625 *
613 * Potential Errors: NoSuchWindow, StaleElementReference, JavaScriptError. 626 * Potential Errors: NoSuchWindow, StaleElementReference, JavaScriptError.
614 */ 627 */
615 Future execute(String script, [List args]) => 628 Future execute(String script, [List args]) {
616 _post('execute', params: { 'script': script, 'args': args }); 629 if (args == null) args = [];
630 return _post('execute', { 'script': script, 'args': args });
631 }
617 632
618 /** 633 /**
619 * Inject a snippet of JavaScript into the page for execution in the context 634 * Inject a snippet of JavaScript into the page for execution in the context
620 * of the currently selected frame. The executed script is assumed to be 635 * of the currently selected frame. The executed script is assumed to be
621 * asynchronous and must signal that it is done by invoking the provided 636 * asynchronous and must signal that it is done by invoking the provided
622 * callback, which is always provided as the final argument to the function. 637 * callback, which is always provided as the final argument to the function.
623 * The value to this callback will be returned to the client. 638 * The value to this callback will be returned to the client.
624 * 639 *
625 * Asynchronous script commands may not span page loads. If an unload event 640 * Asynchronous script commands may not span page loads. If an unload event
626 * is fired while waiting for a script result, an error should be returned 641 * is fired while waiting for a script result, an error should be returned
627 * to the client. 642 * to the client.
628 * 643 *
629 * The script argument defines the script to execute in the form of a function 644 * The script argument defines the script to execute in the form of a function
630 * body. The function will be invoked with the provided args array and the 645 * body. The function will be invoked with the provided args array and the
631 * values may be accessed via the arguments object in the order specified. 646 * values may be accessed via the arguments object in the order specified.
632 * The final argument will always be a callback function that must be invoked 647 * The final argument will always be a callback function that must be invoked
633 * to signal that the script has finished. 648 * to signal that the script has finished.
634 * 649 *
635 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects 650 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects
636 * that define a WebElement reference will be converted to the corresponding 651 * that define a WebElement reference will be converted to the corresponding
637 * DOM element. Likewise, any WebElements in the script result will be 652 * DOM element. Likewise, any WebElements in the script result will be
638 * returned to the client as WebElement JSON objects. 653 * returned to the client as WebElement JSON objects.
639 * 654 *
640 * Potential Errors: NoSuchWindow, StaleElementReference, Timeout (controlled 655 * Potential Errors: NoSuchWindow, StaleElementReference, Timeout (controlled
641 * by the [setAsyncScriptTimeout] command), JavaScriptError (if the script 656 * by the [setAsyncScriptTimeout] command), JavaScriptError (if the script
642 * callback is not invoked before the timout expires). 657 * callback is not invoked before the timout expires).
643 */ 658 */
644 Future executeAsync(String script, [List args]) => 659 Future executeAsync(String script, [List args]) {
645 _post('execute_async', params: { 'script': script, 'args': args }); 660 if (args == null) args = [];
661 return _post('execute_async', { 'script': script, 'args': args });
662 }
646 663
647 /** 664 /**
648 * Take a screenshot of the current page (PNG). 665 * Take a screenshot of the current page (PNG).
649 * 666 *
650 * Potential Errors: NoSuchWindow. 667 * Potential Errors: NoSuchWindow.
651 */ 668 */
652 Future<List<int>> getScreenshot([fname]) { 669 Future<List<int>> getScreenshot([fname]) {
653 var completer = new Completer(); 670 var completer = new Completer();
654 var result = _serverRequest('GET', '$_path/screenshot', completer, 671 return _get('screenshot', completer, (r, v) {
655 customHandler: (r, v) {
656 var image = Base64Decoder.decode(v); 672 var image = Base64Decoder.decode(v);
657 if (fname != null) { 673 if (fname != null) {
658 writeBytesToFile(fname, image); 674 writeBytesToFile(fname, image);
659 } 675 }
660 completer.complete(image); 676 completer.complete(image);
661 }); 677 });
662 return completer.future;
663 } 678 }
664 679
665 /** 680 /**
666 * List all available IME (Input Method Editor) engines on the machine. 681 * List all available IME (Input Method Editor) engines on the machine.
667 * To use an engine, it has to be present in this list. 682 * To use an engine, it has to be present in this list.
668 * 683 *
669 * Potential Errors: ImeNotAvailableException. 684 * Potential Errors: ImeNotAvailableException.
670 */ 685 */
671 Future<List<String>> getAvailableImeEngines() => 686 Future<List<String>> getAvailableImeEngines() =>
672 _get('ime/available_engines'); 687 _get('ime/available_engines');
(...skipping 25 matching lines...) Expand all
698 * Make an engine that is available (appears on the list returned by 713 * Make an engine that is available (appears on the list returned by
699 * getAvailableEngines) active. After this call, the engine will be added 714 * getAvailableEngines) active. After this call, the engine will be added
700 * to the list of engines loaded in the IME daemon and the input sent using 715 * to the list of engines loaded in the IME daemon and the input sent using
701 * sendKeys will be converted by the active engine. Note that this is a 716 * sendKeys will be converted by the active engine. Note that this is a
702 * platform-independent method of activating IME (the platform-specific way 717 * platform-independent method of activating IME (the platform-specific way
703 * being using keyboard shortcuts). 718 * being using keyboard shortcuts).
704 * 719 *
705 * Potential Errors: ImeActivationFailedException, ImeNotAvailableException. 720 * Potential Errors: ImeActivationFailedException, ImeNotAvailableException.
706 */ 721 */
707 Future activateIme(String engine) => 722 Future activateIme(String engine) =>
708 _post('ime/activate', params: { 'engine': engine }); 723 _post('ime/activate', { 'engine': engine });
709 724
710 /** 725 /**
711 * Change focus to another frame on the page. If the frame id is null, 726 * Change focus to another frame on the page. If the frame id is null,
712 * the server should switch to the page's default content. 727 * the server should switch to the page's default content.
713 * [id] is the Identifier for the frame to change focus to, and can be 728 * [id] is the Identifier for the frame to change focus to, and can be
714 * a string, number, null, or JSON Object. 729 * a string, number, null, or JSON Object.
715 * 730 *
716 * Potential Errors: NoSuchWindow, NoSuchFrame. 731 * Potential Errors: NoSuchWindow, NoSuchFrame.
717 */ 732 */
718 Future setFrameFocus(id) => _post('frame', params: { 'id': id }); 733 Future setFrameFocus(id) => _post('frame', { 'id': id });
719 734
720 /** 735 /**
721 * Change focus to another window. The window to change focus to may be 736 * Change focus to another window. The window to change focus to may be
722 * specified by [name], which is its server assigned window handle, or 737 * specified by [name], which is its server assigned window handle, or
723 * the value of its name attribute. 738 * the value of its name attribute.
724 * 739 *
725 * Potential Errors: NoSuchWindow. 740 * Potential Errors: NoSuchWindow.
726 */ 741 */
727 Future setWindowFocus(name) => 742 Future setWindowFocus(name) => _post('window', { 'name': name });
728 _post('window', params: { 'name': name });
729 743
730 /** 744 /**
731 * Close the current window. 745 * Close the current window.
732 * 746 *
733 * Potential Errors: NoSuchWindow. 747 * Potential Errors: NoSuchWindow.
734 */ 748 */
735 Future closeWindow() => _delete('window'); 749 Future closeWindow() => _delete('window');
736 750
737 /** 751 /**
738 * Retrieve all cookies visible to the current page. 752 * Retrieve all cookies visible to the current page.
(...skipping 17 matching lines...) Expand all
756 * Potential Errors: NoSuchWindow. 770 * Potential Errors: NoSuchWindow.
757 */ 771 */
758 Future<List<Map>> getCookies() => _get('cookie'); 772 Future<List<Map>> getCookies() => _get('cookie');
759 773
760 /** 774 /**
761 * Set a cookie. If the cookie path is not specified, it should be set 775 * Set a cookie. If the cookie path is not specified, it should be set
762 * to "/". Likewise, if the domain is omitted, it should default to the 776 * to "/". Likewise, if the domain is omitted, it should default to the
763 * current page's domain. See [getCookies] for the structure of a cookie 777 * current page's domain. See [getCookies] for the structure of a cookie
764 * Map. 778 * Map.
765 */ 779 */
766 Future setCookie(Map cookie) => 780 Future setCookie(Map cookie) => _post('cookie', { 'cookie': cookie });
767 _post('cookie', params: { 'cookie': cookie });
768 781
769 /** 782 /**
770 * Delete all cookies visible to the current page. 783 * Delete all cookies visible to the current page.
771 * 784 *
772 * Potential Errors: InvalidCookieDomain (the cookie's domain is not 785 * Potential Errors: InvalidCookieDomain (the cookie's domain is not
773 * visible from the current page), NoSuchWindow, UnableToSetCookie (if 786 * visible from the current page), NoSuchWindow, UnableToSetCookie (if
774 * attempting to set a cookie on a page that does not support cookies, 787 * attempting to set a cookie on a page that does not support cookies,
775 * e.g. pages with mime-type text/plain). 788 * e.g. pages with mime-type text/plain).
776 */ 789 */
777 Future deleteCookies() => _delete('cookie'); 790 Future deleteCookies() => _delete('cookie');
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
821 * partially matches the search value. 834 * partially matches the search value.
822 * 835 *
823 * 'tag name' - Returns an element whose tag name matches the search value. 836 * 'tag name' - Returns an element whose tag name matches the search value.
824 * 837 *
825 * 'xpath' - Returns an element matching an XPath expression. 838 * 'xpath' - Returns an element matching an XPath expression.
826 * 839 *
827 * Potential Errors: NoSuchWindow, NoSuchElement, XPathLookupError (if 840 * Potential Errors: NoSuchWindow, NoSuchElement, XPathLookupError (if
828 * using XPath and the input expression is invalid). 841 * using XPath and the input expression is invalid).
829 */ 842 */
830 Future<String> findElement(String strategy, String searchValue) => 843 Future<String> findElement(String strategy, String searchValue) =>
831 _post('element', params: { 'using': strategy, 'value' : searchValue }); 844 _post('element', { 'using': strategy, 'value' : searchValue });
832 845
833 /** 846 /**
834 * Search for multiple elements on the page, starting from the document root. 847 * Search for multiple elements on the page, starting from the document root.
835 * The located elements will be returned as WebElement JSON objects. See 848 * The located elements will be returned as WebElement JSON objects. See
836 * [findElement] for the locator strategies that each server supports. 849 * [findElement] for the locator strategies that each server supports.
837 * Elements are be returned in the order located in the DOM. 850 * Elements are be returned in the order located in the DOM.
838 * 851 *
839 * Potential Errors: NoSuchWindow, XPathLookupError. 852 * Potential Errors: NoSuchWindow, XPathLookupError.
840 */ 853 */
841 Future<List<String>> findElements(String strategy, String searchValue) => 854 Future<List<String>> findElements(String strategy, String searchValue) =>
842 _post('elements', params: { 'using': strategy, 'value' : searchValue }); 855 _post('elements', { 'using': strategy, 'value' : searchValue });
843 856
844 /** 857 /**
845 * Get the element on the page that currently has focus. The element will 858 * Get the element on the page that currently has focus. The element will
846 * be returned as a WebElement JSON object. 859 * be returned as a WebElement JSON object.
847 * 860 *
848 * Potential Errors: NoSuchWindow. 861 * Potential Errors: NoSuchWindow.
849 */ 862 */
850 Future<String> getElementWithFocus() => _post('element/active'); 863 Future<String> getElementWithFocus() => _post('element/active');
851 864
852 /** 865 /**
853 * Search for an element on the page, starting from element with id [id]. 866 * Search for an element on the page, starting from element with id [id].
854 * The located element will be returned as WebElement JSON objects. See 867 * The located element will be returned as WebElement JSON objects. See
855 * [findElement] for the locator strategies that each server supports. 868 * [findElement] for the locator strategies that each server supports.
856 * 869 *
857 * Potential Errors: NoSuchWindow, XPathLookupError. 870 * Potential Errors: NoSuchWindow, XPathLookupError.
858 */ 871 */
859 Future<String> 872 Future<String>
860 findElementFromId(String id, String strategy, String searchValue) { 873 findElementFromId(String id, String strategy, String searchValue) {
861 _post('element/$id/element', 874 _post('element/$id/element', { 'using': strategy, 'value' : searchValue });
862 params: { 'using': strategy, 'value' : searchValue });
863 } 875 }
864 876
865 /** 877 /**
866 * Search for multiple elements on the page, starting from the element with 878 * Search for multiple elements on the page, starting from the element with
867 * id [id].The located elements will be returned as WebElement JSON objects. 879 * id [id].The located elements will be returned as WebElement JSON objects.
868 * See [findElement] for the locator strategies that each server supports. 880 * See [findElement] for the locator strategies that each server supports.
869 * Elements are be returned in the order located in the DOM. 881 * Elements are be returned in the order located in the DOM.
870 * 882 *
871 * Potential Errors: NoSuchWindow, XPathLookupError. 883 * Potential Errors: NoSuchWindow, XPathLookupError.
872 */ 884 */
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
996 * either the modifier is encountered again in the sequence, or the NULL 1008 * either the modifier is encountered again in the sequence, or the NULL
997 * (U+E000) key is encountered. 1009 * (U+E000) key is encountered.
998 * 1010 *
999 * - Each key sequence is terminated with an implicit NULL key. 1011 * - Each key sequence is terminated with an implicit NULL key.
1000 * Subsequently, all depressed modifier keys are released (with 1012 * Subsequently, all depressed modifier keys are released (with
1001 * corresponding keyup events) at the end of the sequence. 1013 * corresponding keyup events) at the end of the sequence.
1002 * 1014 *
1003 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible. 1015 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible.
1004 */ 1016 */
1005 Future sendKeyStrokesToElement(String id, List<String> keys) => 1017 Future sendKeyStrokesToElement(String id, List<String> keys) =>
1006 _post('element/$id/value', params: { 'value': keys }); 1018 _post('element/$id/value', { 'value': keys });
1007 1019
1008 /** 1020 /**
1009 * Send a sequence of key strokes to the active element. This command is 1021 * Send a sequence of key strokes to the active element. This command is
1010 * similar to [sendKeyStrokesToElement] command in every aspect except the 1022 * similar to [sendKeyStrokesToElement] command in every aspect except the
1011 * implicit termination: The modifiers are not released at the end of the 1023 * implicit termination: The modifiers are not released at the end of the
1012 * call. Rather, the state of the modifier keys is kept between calls, 1024 * call. Rather, the state of the modifier keys is kept between calls,
1013 * so mouse interactions can be performed while modifier keys are depressed. 1025 * so mouse interactions can be performed while modifier keys are depressed.
1014 * 1026 *
1015 * Potential Errors: NoSuchWindow. 1027 * Potential Errors: NoSuchWindow.
1016 */ 1028 */
1017 Future sendKeyStrokes(List<String> keys) => 1029 Future sendKeyStrokes(List<String> keys) => _post('keys', { 'value': keys });
1018 _post('keys', params: { 'value': keys });
1019 1030
1020 /** 1031 /**
1021 * Query for an element's tag name, as a lower-case string. 1032 * Query for an element's tag name, as a lower-case string.
1022 * 1033 *
1023 * Potential Errors: NoSuchWindow, StaleElementReference. 1034 * Potential Errors: NoSuchWindow, StaleElementReference.
1024 */ 1035 */
1025 Future<String> getElementTagName(String id) => _get('element/$id/name'); 1036 Future<String> getElementTagName(String id) => _get('element/$id/name');
1026 1037
1027 /** 1038 /**
1028 * Clear a TEXTAREA or text INPUT element's value. 1039 * Clear a TEXTAREA or text INPUT element's value.
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1113 * Potential Errors: NoAlertPresent. 1124 * Potential Errors: NoAlertPresent.
1114 */ 1125 */
1115 Future<String> getAlertText() => _get('alert_text'); 1126 Future<String> getAlertText() => _get('alert_text');
1116 1127
1117 /** 1128 /**
1118 * Sends keystrokes to a JavaScript prompt() dialog. 1129 * Sends keystrokes to a JavaScript prompt() dialog.
1119 * 1130 *
1120 * Potential Errors: NoAlertPresent. 1131 * Potential Errors: NoAlertPresent.
1121 */ 1132 */
1122 Future sendKeyStrokesToPrompt(String text) => 1133 Future sendKeyStrokesToPrompt(String text) =>
1123 _post('alert_text', params: { 'text': text }); 1134 _post('alert_text', { 'text': text });
1124 1135
1125 /** 1136 /**
1126 * Accepts the currently displayed alert dialog. Usually, this is equivalent 1137 * Accepts the currently displayed alert dialog. Usually, this is equivalent
1127 * to clicking on the 'OK' button in the dialog. 1138 * to clicking on the 'OK' button in the dialog.
1128 * 1139 *
1129 * Potential Errors: NoAlertPresent. 1140 * Potential Errors: NoAlertPresent.
1130 */ 1141 */
1131 Future acceptAlert() => _post('accept_alert'); 1142 Future acceptAlert() => _post('accept_alert');
1132 1143
1133 /** 1144 /**
1134 * Dismisses the currently displayed alert dialog. For confirm() and prompt() 1145 * Dismisses the currently displayed alert dialog. For confirm() and prompt()
1135 * dialogs, this is equivalent to clicking the 'Cancel' button. For alert() 1146 * dialogs, this is equivalent to clicking the 'Cancel' button. For alert()
1136 * dialogs, this is equivalent to clicking the 'OK' button. 1147 * dialogs, this is equivalent to clicking the 'OK' button.
1137 * 1148 *
1138 * Potential Errors: NoAlertPresent. 1149 * Potential Errors: NoAlertPresent.
1139 */ 1150 */
1140 Future dismissAlert() => _post('dismiss_alert'); 1151 Future dismissAlert() => _post('dismiss_alert');
1141 1152
1142 /** 1153 /**
1143 * Move the mouse by an offset of the specificed element. If no element is 1154 * Move the mouse by an offset of the specificed element. If no element is
1144 * specified, the move is relative to the current mouse cursor. If an 1155 * specified, the move is relative to the current mouse cursor. If an
1145 * element is provided but no offset, the mouse will be moved to the center 1156 * element is provided but no offset, the mouse will be moved to the center
1146 * of the element. If the element is not visible, it will be scrolled 1157 * of the element. If the element is not visible, it will be scrolled
1147 * into view. 1158 * into view.
1148 */ 1159 */
1149 Future moveTo(String id, int x, int y) => 1160 Future moveTo(String id, int x, int y) =>
1150 _post('moveto', params: { 'element': id, 'xoffset': x, 'yoffset' : y}); 1161 _post('moveto', { 'element': id, 'xoffset': x, 'yoffset' : y});
1151 1162
1152 /** 1163 /**
1153 * Click a mouse button (at the coordinates set by the last [moveTo] command). 1164 * Click a mouse button (at the coordinates set by the last [moveTo] command).
1154 * Note that calling this command after calling [buttonDown] and before 1165 * Note that calling this command after calling [buttonDown] and before
1155 * calling [buttonUp] (or any out-of-order interactions sequence) will yield 1166 * calling [buttonUp] (or any out-of-order interactions sequence) will yield
1156 * undefined behaviour). 1167 * undefined behaviour).
1157 * 1168 *
1158 * [button] should be 0 for left, 1 for middle, or 2 for right. 1169 * [button] should be 0 for left, 1 for middle, or 2 for right.
1159 */ 1170 */
1160 Future clickMouse([button = 0]) => 1171 Future clickMouse([button = 0]) => _post('click', { 'button' : button });
1161 _post('click', params: { 'button' : button });
1162 1172
1163 /** 1173 /**
1164 * Click and hold the left mouse button (at the coordinates set by the last 1174 * Click and hold the left mouse button (at the coordinates set by the last
1165 * [moveTo] command). Note that the next mouse-related command that should 1175 * [moveTo] command). Note that the next mouse-related command that should
1166 * follow is [buttonDown]. Any other mouse command (such as [click] or 1176 * follow is [buttonDown]. Any other mouse command (such as [click] or
1167 * another call to [buttonDown]) will yield undefined behaviour. 1177 * another call to [buttonDown]) will yield undefined behaviour.
1168 * 1178 *
1169 * [button] should be 0 for left, 1 for middle, or 2 for right. 1179 * [button] should be 0 for left, 1 for middle, or 2 for right.
1170 */ 1180 */
1171 Future buttonDown([button = 0]) => 1181 Future buttonDown([button = 0]) => _post('click', { 'button' : button });
1172 _post('click', params: { 'button' : button });
1173 1182
1174 /** 1183 /**
1175 * Releases the mouse button previously held (where the mouse is currently 1184 * Releases the mouse button previously held (where the mouse is currently
1176 * at). Must be called once for every [buttonDown] command issued. See the 1185 * at). Must be called once for every [buttonDown] command issued. See the
1177 * note in [click] and [buttonDown] about implications of out-of-order 1186 * note in [click] and [buttonDown] about implications of out-of-order
1178 * commands. 1187 * commands.
1179 * 1188 *
1180 * [button] should be 0 for left, 1 for middle, or 2 for right. 1189 * [button] should be 0 for left, 1 for middle, or 2 for right.
1181 */ 1190 */
1182 Future buttonUp([button = 0]) => 1191 Future buttonUp([button = 0]) => _post('click', { 'button' : button });
1183 _post('click', params: { 'button' : button });
1184 1192
1185 /** Double-clicks at the current mouse coordinates (set by [moveTo]). */ 1193 /** Double-clicks at the current mouse coordinates (set by [moveTo]). */
1186 Future doubleClick() => _post('doubleclick'); 1194 Future doubleClick() => _post('doubleclick');
1187 1195
1188 /** Single tap on the touch enabled device on the element with id [id]. */ 1196 /** Single tap on the touch enabled device on the element with id [id]. */
1189 Future touchClick(String id) => 1197 Future touchClick(String id) => _post('touch/click', { 'element': id });
1190 _post('touch/click', params: { 'element': id });
1191 1198
1192 /** Finger down on the screen. */ 1199 /** Finger down on the screen. */
1193 Future touchDown(int x, int y) => 1200 Future touchDown(int x, int y) => _post('touch/down', { 'x': x, 'y': y });
1194 _post('touch/down', params: { 'x': x, 'y': y });
1195 1201
1196 /** Finger up on the screen. */ 1202 /** Finger up on the screen. */
1197 Future touchUp(int x, int y) => 1203 Future touchUp(int x, int y) => _post('touch/up', { 'x': x, 'y': y });
1198 _post('touch/up', params: { 'x': x, 'y': y });
1199 1204
1200 /** Finger move on the screen. */ 1205 /** Finger move on the screen. */
1201 Future touchMove(int x, int y) => 1206 Future touchMove(int x, int y) => _post('touch/move', { 'x': x, 'y': y });
1202 _post('touch/move', params: { 'x': x, 'y': y });
1203 1207
1204 /** 1208 /**
1205 * Scroll on the touch screen using finger based motion events. If [id] is 1209 * Scroll on the touch screen using finger based motion events. If [id] is
1206 * specified, scrolling will start at a particular screen location. 1210 * specified, scrolling will start at a particular screen location.
1207 */ 1211 */
1208 Future touchScroll(int xOffset, int yOffset, [String id = null]) { 1212 Future touchScroll(int xOffset, int yOffset, [String id = null]) {
1209 if (id == null) { 1213 if (id == null) {
1210 return _post('touch/scroll', 1214 return _post('touch/scroll', { 'xoffset': xOffset, 'yoffset': yOffset });
1211 params: { 'xoffset': xOffset, 'yoffset': yOffset });
1212 } else { 1215 } else {
1213 return _post('touch/scroll', 1216 return _post('touch/scroll',
1214 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset }); 1217 { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset });
1215 } 1218 }
1216 } 1219 }
1217 1220
1218 /** Double tap on the touch screen using finger motion events. */ 1221 /** Double tap on the touch screen using finger motion events. */
1219 Future touchDoubleClick(String id) => 1222 Future touchDoubleClick(String id) =>
1220 _post('touch/doubleclick', params: { 'element': id }); 1223 _post('touch/doubleclick', { 'element': id });
1221 1224
1222 /** Long press on the touch screen using finger motion events. */ 1225 /** Long press on the touch screen using finger motion events. */
1223 Future touchLongClick(String id) => 1226 Future touchLongClick(String id) =>
1224 _post('touch/longclick', params: { 'element': id }); 1227 _post('touch/longclick', { 'element': id });
1225 1228
1226 /** 1229 /**
1227 * Flick on the touch screen using finger based motion events, starting 1230 * Flick on the touch screen using finger based motion events, starting
1228 * at a particular screen location. [speed] is in pixels-per-second. 1231 * at a particular screen location. [speed] is in pixels-per-second.
1229 */ 1232 */
1230 Future touchFlickFrom(String id, int xOffset, int yOffset, int speed) => 1233 Future touchFlickFrom(String id, int xOffset, int yOffset, int speed) =>
1231 _post('touch/flick', 1234 _post('touch/flick',
1232 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset, 1235 { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset,
1233 'speed': speed }); 1236 'speed': speed });
1234 1237
1235 /** 1238 /**
1236 * Flick on the touch screen using finger based motion events. Use this 1239 * Flick on the touch screen using finger based motion events. Use this
1237 * instead of [touchFlickFrom] if you don'tr care where the flick starts. 1240 * instead of [touchFlickFrom] if you don'tr care where the flick starts.
1238 */ 1241 */
1239 Future touchFlick(int xSpeed, int ySpeed) => 1242 Future touchFlick(int xSpeed, int ySpeed) =>
1240 _post('touch/flick', params: { 'xSpeed': xSpeed, 'ySpeed': ySpeed }); 1243 _post('touch/flick', { 'xSpeed': xSpeed, 'ySpeed': ySpeed });
1241 1244
1242 /** 1245 /**
1243 * Get the current geo location. Returns a [Map] with latitude, 1246 * Get the current geo location. Returns a [Map] with latitude,
1244 * longitude and altitude properties. 1247 * longitude and altitude properties.
1245 */ 1248 */
1246 Future<Map> getGeolocation() => _get('location'); 1249 Future<Map> getGeolocation() => _get('location');
1247 1250
1248 /** Set the current geo location. */ 1251 /** Set the current geo location. */
1249 Future setLocation(double latitude, double longitude, double altitude) => 1252 Future setLocation(double latitude, double longitude, double altitude) =>
1250 _post('location', params: 1253 _post('location',
1251 { 'latitude': latitude, 1254 { 'latitude': latitude,
1252 'longitude': longitude, 1255 'longitude': longitude,
1253 'altitude': altitude }); 1256 'altitude': altitude });
1254 1257
1255 /** 1258 /**
1259 * Only a few drivers actually support the JSON storage commands.
1260 * Currently it looks like this is the Android and iPhone drivers only.
1261 * For the rest, we can achieve a similar effect with Javascript
1262 * execution. The flag below is used to control whether to do this.
1263 */
1264 bool useJavascriptForStorageAPIs = true;
1265
1266 /**
1256 * Get all keys of the local storage. Completes with [null] if there 1267 * Get all keys of the local storage. Completes with [null] if there
1257 * are no keys or the keys could not be retrieved. 1268 * are no keys or the keys could not be retrieved.
1258 * 1269 *
1259 * Potential Errors: NoSuchWindow. 1270 * Potential Errors: NoSuchWindow.
1260 */ 1271 */
1261 Future<List<String>> getLocalStorageKeys() => _get('local_storage'); 1272 Future<List<String>> getLocalStorageKeys() {
1273 if (useJavascriptForStorageAPIs) {
1274 return execute(
1275 'var rtn = [];'
1276 'for (var i = 0; i < window.localStorage.length; i++)'
1277 ' rtn.push(window.localStorage.key(i));'
1278 'return rtn;');
1279 } else {
1280 return _get('local_storage');
1281 }
1282 }
1262 1283
1263 /** 1284 /**
1264 * Set the local storage item for the given key. 1285 * Set the local storage item for the given key.
1265 * 1286 *
1266 * Potential Errors: NoSuchWindow. 1287 * Potential Errors: NoSuchWindow.
1267 */ 1288 */
1268 Future setLocalStorageItem(String key, String value) => 1289 Future setLocalStorageItem(String key, String value) {
1269 _post('local_storage', params: { 'key': key, 'value': value }); 1290 if (useJavascriptForStorageAPIs) {
1291 return execute('window.localStorage.setItem(arguments[0], arguments[1]);',
1292 [ key, value ]);
Siggi Cherem (dart-lang) 2013/03/14 16:41:24 style nit: remove extra spaces before/after [] [k
1293 } else {
1294 return _post('local_storage', { 'key': key, 'value': value });
1295 }
1296 }
1270 1297
1271 /** 1298 /**
1272 * Clear the local storage. 1299 * Clear the local storage.
1273 * 1300 *
1274 * Potential Errors: NoSuchWindow. 1301 * Potential Errors: NoSuchWindow.
1275 */ 1302 */
1276 Future clearLocalStorage() => _delete('local_storage'); 1303 Future clearLocalStorage() {
1304 if (useJavascriptForStorageAPIs) {
1305 return execute('return window.localStorage.clear();');
1306 } else {
1307 return _delete('local_storage');
1308 }
1309 }
1277 1310
1278 /** 1311 /**
1279 * Get the local storage item for the given key. 1312 * Get the local storage item for the given key.
1280 * 1313 *
1281 * Potential Errors: NoSuchWindow. 1314 * Potential Errors: NoSuchWindow.
1282 */ 1315 */
1283 Future<String> getLocalStorageValue(String key) => 1316 Future<String> getLocalStorageValue(String key) {
1284 _get('local_storage/key/$key'); 1317 if (useJavascriptForStorageAPIs) {
1318 return execute('return window.localStorage.getItem(arguments[0]);',
1319 [ key ]);
Siggi Cherem (dart-lang) 2013/03/14 16:41:24 nit: spaces (here and below)
1320 } else {
1321 return _get('local_storage/key/$key');
1322 }
1323 }
1285 1324
1286 /** 1325 /**
1287 * Delete the local storage item for the given key. 1326 * Delete the local storage item for the given key.
1288 * 1327 *
1289 * Potential Errors: NoSuchWindow. 1328 * Potential Errors: NoSuchWindow.
1290 */ 1329 */
1291 Future deleteLocalStorageValue(String key) => 1330 Future deleteLocalStorageValue(String key) {
1292 _delete('local_storage/key/$key'); 1331 if (useJavascriptForStorageAPIs) {
1332 return execute('return window.localStorage.removeItem(arguments[0]);',
1333 [ key ]);
1334 } else {
1335 return _delete('local_storage/key/$key');
1336 }
1337 }
1293 1338
1294 /** 1339 /**
1295 * Get the number of items in the local storage. 1340 * Get the number of items in the local storage.
1296 * 1341 *
1297 * Potential Errors: NoSuchWindow. 1342 * Potential Errors: NoSuchWindow.
1298 */ 1343 */
1299 Future<int> getLocalStorageCount() => _get('local_storage/size'); 1344 Future<int> getLocalStorageCount() {
1345 if (useJavascriptForStorageAPIs) {
1346 return execute('return window.localStorage.length;');
1347 } else {
1348 return _get('local_storage/size');
1349 }
1350 }
1300 1351
1301 /** 1352 /**
1302 * Get all keys of the session storage. 1353 * Get all keys of the session storage.
1303 * 1354 *
1304 * Potential Errors: NoSuchWindow. 1355 * Potential Errors: NoSuchWindow.
1305 */ 1356 */
1306 Future<List<String>> getSessionStorageKeys() => _get('session_storage'); 1357 Future<List<String>> getSessionStorageKeys() {
1358 if (useJavascriptForStorageAPIs) {
1359 return execute(
1360 'var rtn = [];'
1361 'for (var i = 0; i < window.sessionStorage.length; i++)'
1362 ' rtn.push(window.sessionStorage.key(i));'
1363 'return rtn;');
1364 } else {
1365 return _get('session_storage');
1366 }
1367 }
1307 1368
1308 /** 1369 /**
1309 * Set the sessionstorage item for the given key. 1370 * Set the sessionstorage item for the given key.
1310 * 1371 *
1311 * Potential Errors: NoSuchWindow. 1372 * Potential Errors: NoSuchWindow.
1312 */ 1373 */
1313 Future setSessionStorageItem(String key, String value) => 1374 Future setSessionStorageItem(String key, String value) {
1314 _post('session_storage', params: { 'key': key, 'value': value }); 1375 if (useJavascriptForStorageAPIs) {
1376 return execute(
1377 'window.sessionStorage.setItem(arguments[0], arguments[1]);',
1378 [ key, value ]);
1379 } else {
1380 return _post('session_storage', { 'key': key, 'value': value });
1381 }
1382 }
1315 1383
1316 /** 1384 /**
1317 * Clear the session storage. 1385 * Clear the session storage.
1318 * 1386 *
1319 * Potential Errors: NoSuchWindow. 1387 * Potential Errors: NoSuchWindow.
1320 */ 1388 */
1321 Future clearSessionStorage() => _delete('session_storage'); 1389 Future clearSessionStorage() {
1390 if (useJavascriptForStorageAPIs) {
1391 return execute('window.sessionStorage.clear();');
1392 } else {
1393 return _delete('session_storage');
1394 }
1395 }
1322 1396
1323 /** 1397 /**
1324 * Get the session storage item for the given key. 1398 * Get the session storage item for the given key.
1325 * 1399 *
1326 * Potential Errors: NoSuchWindow. 1400 * Potential Errors: NoSuchWindow.
1327 */ 1401 */
1328 Future<String> getSessionStorageValue(String key) => 1402 Future<String> getSessionStorageValue(String key) {
1329 _get('session_storage/key/$key'); 1403 if (useJavascriptForStorageAPIs) {
1404 return execute('return window.sessionStorage.getItem(arguments[0]);',
1405 [ key ]);
1406 } else {
1407 return _get('session_storage/key/$key');
1408 }
1409 }
1330 1410
1331 /** 1411 /**
1332 * Delete the session storage item for the given key. 1412 * Delete the session storage item for the given key.
1333 * 1413 *
1334 * Potential Errors: NoSuchWindow. 1414 * Potential Errors: NoSuchWindow.
1335 */ 1415 */
1336 Future deleteSessionStorageValue(String key) => 1416 Future deleteSessionStorageValue(String key) {
1337 _delete('session_storage/key/$key'); 1417 if (useJavascriptForStorageAPIs) {
1418 return execute('return window.sessionStorage.removeItem(arguments[0]);',
1419 [ key ]);
1420 } else {
1421 return _delete('session_storage/key/$key');
1422 }
1423 }
1338 1424
1339 /** 1425 /**
1340 * Get the number of items in the session storage. 1426 * Get the number of items in the session storage.
1341 * 1427 *
1342 * Potential Errors: NoSuchWindow. 1428 * Potential Errors: NoSuchWindow.
1343 */ 1429 */
1344 Future<String> getSessionStorageCount() => _get('session_storage/size'); 1430 Future<String> getSessionStorageCount() {
1431 if (useJavascriptForStorageAPIs) {
1432 return execute('return window.sessionStorage.length;');
1433 } else {
1434 return _get('session_storage/size');
1435 }
1436 }
1345 1437
1346 /** Get available log types ('client', 'driver', 'browser', 'server'). */ 1438 /**
1439 * Get available log types ('client', 'driver', 'browser', 'server').
1440 * This works with Firefox but Chrome returns a 500 response due to a
1441 * bad cast.
1442 */
1347 Future<List<String>> getLogTypes() => _get('log/types'); 1443 Future<List<String>> getLogTypes() => _get('log/types');
1348 1444
1349 /** 1445 /**
1350 * Get the log for a given log type. Log buffer is reset after each request. 1446 * Get the log for a given log type. Log buffer is reset after each request.
1351 * Each log entry is a [Map] with these fields: 1447 * Each log entry is a [Map] with these fields:
1352 * 1448 *
1353 * 'timestamp' (int) - The timestamp of the entry. 1449 * 'timestamp' (int) - The timestamp of the entry.
1354 * 'level' (String) - The log level of the entry, for example, "INFO". 1450 * 'level' (String) - The log level of the entry, for example, "INFO".
1355 * 'message' (String) - The log message. 1451 * 'message' (String) - The log message.
1452 *
1453 * This works with Firefox but Chrome returns a 500 response due to a
1454 * bad cast.
1356 */ 1455 */
1357 Future<List<Map>> getLogs(String type) => 1456 Future<List<Map>> getLogs(String type) => _post('log', { 'type': type });
1358 _post('log', params: { 'type': type });
1359 } 1457 }
OLDNEW
« no previous file with comments | « pkg/unittest/lib/vm_config.dart ('k') | pkg/webdriver/test/webdriver_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698