@@ -156,11 +156,11 @@ const COMMAND_MAP = new Map([
156
156
[ cmd . Name . ELEMENT_EQUALS , get ( '/session/:sessionId/element/:id/equals/:other' ) ] ,
157
157
[ cmd . Name . TAKE_ELEMENT_SCREENSHOT , get ( '/session/:sessionId/element/:id/screenshot' ) ] ,
158
158
[ cmd . Name . SWITCH_TO_WINDOW , post ( '/session/:sessionId/window' ) ] ,
159
- [ cmd . Name . MAXIMIZE_WINDOW , post ( '/session/:sessionId/window/:windowHandle /maximize' ) ] ,
160
- [ cmd . Name . GET_WINDOW_POSITION , get ( '/session/:sessionId/window/:windowHandle /position' ) ] ,
161
- [ cmd . Name . SET_WINDOW_POSITION , post ( '/session/:sessionId/window/:windowHandle /position' ) ] ,
162
- [ cmd . Name . GET_WINDOW_SIZE , get ( '/session/:sessionId/window/:windowHandle /size' ) ] ,
163
- [ cmd . Name . SET_WINDOW_SIZE , post ( '/session/:sessionId/window/:windowHandle /size' ) ] ,
159
+ [ cmd . Name . MAXIMIZE_WINDOW , post ( '/session/:sessionId/window/current /maximize' ) ] ,
160
+ [ cmd . Name . GET_WINDOW_POSITION , get ( '/session/:sessionId/window/current /position' ) ] ,
161
+ [ cmd . Name . SET_WINDOW_POSITION , post ( '/session/:sessionId/window/current /position' ) ] ,
162
+ [ cmd . Name . GET_WINDOW_SIZE , get ( '/session/:sessionId/window/current /size' ) ] ,
163
+ [ cmd . Name . SET_WINDOW_SIZE , post ( '/session/:sessionId/window/current /size' ) ] ,
164
164
[ cmd . Name . SWITCH_TO_FRAME , post ( '/session/:sessionId/frame' ) ] ,
165
165
[ cmd . Name . GET_PAGE_SOURCE , get ( '/session/:sessionId/source' ) ] ,
166
166
[ cmd . Name . GET_TITLE , get ( '/session/:sessionId/title' ) ] ,
@@ -196,6 +196,14 @@ const COMMAND_MAP = new Map([
196
196
] ) ;
197
197
198
198
199
+ /** @const {!Map<string, {method: string, path: string}> } */
200
+ const W3C_COMMAND_MAP = new Map ( [
201
+ [ cmd . Name . GET_WINDOW_SIZE , get ( '/session/:sessionId/window/size' ) ] ,
202
+ [ cmd . Name . SET_WINDOW_SIZE , post ( '/session/:sessionId/window/size' ) ] ,
203
+ [ cmd . Name . MAXIMIZE_WINDOW , post ( '/session/:sessionId/window/maximize' ) ] ,
204
+ ] ) ;
205
+
206
+
199
207
/**
200
208
* A basic HTTP client used to send messages to a remote end.
201
209
*/
@@ -377,6 +385,15 @@ function sendRequest(options, onOk, onError, opt_data, opt_proxy) {
377
385
378
386
/**
379
387
* A command executor that communicates with the server using HTTP + JSON.
388
+ *
389
+ * By default, each instance of this class will use the legacy wire protocol
390
+ * from [Selenium project][json]. The executor will automatically switch to the
391
+ * [W3C wire protocol][w3c] if the remote end returns a compliant response to
392
+ * a new session command.
393
+ *
394
+ * [json]: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
395
+ * [w3c]: https://w3c.github.io/webdriver/webdriver-spec.html
396
+ *
380
397
* @implements {cmd.Executor}
381
398
*/
382
399
class Executor {
@@ -388,6 +405,15 @@ class Executor {
388
405
/** @private {!HttpClient} */
389
406
this . client_ = client ;
390
407
408
+ /**
409
+ * Whether this executor should use the W3C wire protocol. The executor
410
+ * will automatically switch if the remote end sends a compliant response
411
+ * to a new session command, however, this property may be directly set to
412
+ * `true` to force the executor into W3C mode.
413
+ * @type {boolean }
414
+ */
415
+ this . w3c = false ;
416
+
391
417
/** @private {Map<string, {method: string, path: string}> } */
392
418
this . customCommands_ = null ;
393
419
@@ -419,6 +445,7 @@ class Executor {
419
445
execute ( command ) {
420
446
let resource =
421
447
( this . customCommands_ && this . customCommands_ . get ( command . getName ( ) ) )
448
+ || ( this . w3c && W3C_COMMAND_MAP . get ( command . getName ( ) ) )
422
449
|| COMMAND_MAP . get ( command . getName ( ) ) ;
423
450
if ( ! resource ) {
424
451
throw new error . UnknownCommandError (
@@ -431,19 +458,26 @@ class Executor {
431
458
432
459
let log = this . log_ ;
433
460
log . finer ( ( ) => '>>>\n' + request ) ;
434
- return this . client_ . send ( request ) . then ( function ( response ) {
461
+ return this . client_ . send ( request ) . then ( response => {
435
462
log . finer ( ( ) => '<<<\n' + response ) ;
436
463
437
- let value = parseHttpResponse ( /** @type {!HttpResponse } */ ( response ) ) ;
438
- let isResponseObj = ( value && typeof value === 'object' ) ;
464
+ let parsed =
465
+ parseHttpResponse ( /** @type {!HttpResponse } */ ( response ) , this . w3c ) ;
466
+
439
467
if ( command . getName ( ) === cmd . Name . NEW_SESSION ) {
440
- if ( ! isResponseObj ) {
468
+ if ( ! parsed || ! parsed [ 'sessionId' ] ) {
441
469
throw new error . WebDriverError (
442
470
'Unable to parse new session response: ' + response . body ) ;
443
471
}
444
- return new Session ( value [ 'sessionId' ] , value [ 'value' ] ) ;
472
+
473
+ // The remote end is a W3C compliant server if there is no `status`
474
+ // field in the response.
475
+ this . w3c = this . w3c || ! ( 'status' in parsed ) ;
476
+
477
+ return new Session ( parsed [ 'sessionId' ] , parsed [ 'value' ] ) ;
445
478
}
446
- return isResponseObj ? value [ 'value' ] : value ;
479
+
480
+ return parsed ? ( parsed [ 'value' ] || null ) : parsed ;
447
481
} ) ;
448
482
}
449
483
}
@@ -466,29 +500,46 @@ function tryParse(str) {
466
500
* Callback used to parse {@link HttpResponse} objects from a
467
501
* {@link HttpClient}.
468
502
* @param {!HttpResponse } httpResponse The HTTP response to parse.
469
- * @return {!Object } The parsed response.
503
+ * @param {boolean } w3c Whether the response should be processed using the
504
+ * W3C wire protocol.
505
+ * @return {{value: ?} } The parsed response.
506
+ * @throws {WebDriverError } If the HTTP response is an error.
470
507
*/
471
- function parseHttpResponse ( httpResponse ) {
508
+ function parseHttpResponse ( httpResponse , w3c ) {
472
509
let parsed = tryParse ( httpResponse . body ) ;
473
510
if ( parsed !== undefined ) {
474
- error . checkLegacyResponse ( parsed ) ;
475
- return parsed ;
511
+ if ( w3c ) {
512
+ if ( httpResponse . status > 399 ) {
513
+ error . throwDecodedError ( parsed ) ;
514
+ }
515
+
516
+ if ( httpResponse . status < 200 ) {
517
+ // This should never happen, but throw the raw response so
518
+ // users report it.
519
+ throw error . WebDriverError (
520
+ `Unexpected HTTP response:\n${ httpResponse } ` ) ;
521
+ }
522
+ } else {
523
+ error . checkLegacyResponse ( parsed ) ;
524
+ }
525
+
526
+ if ( ! parsed || typeof parsed !== 'object' ) {
527
+ parsed = { value : parsed } ;
528
+ }
529
+ return parsed
476
530
}
477
531
478
532
let value = httpResponse . body . replace ( / \r \n / g, '\n' ) ;
479
- if ( ! value ) {
480
- return null ;
481
- }
482
533
483
- // 404 represents an unknown command; anything else is a generic unknown
534
+ // 404 represents an unknown command; anything else > 399 is a generic unknown
484
535
// error.
485
536
if ( httpResponse . status == 404 ) {
486
537
throw new error . UnsupportedOperationError ( value ) ;
487
538
} else if ( httpResponse . status >= 400 ) {
488
539
throw new error . WebDriverError ( value ) ;
489
540
}
490
541
491
- return value ;
542
+ return value ? { value : value } : null ;
492
543
}
493
544
494
545
0 commit comments