selenium-webdriver 3.0.0-beta-1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGES.md +109 -0
  2. package/LICENSE +1 -1
  3. package/README.md +7 -6
  4. package/chrome.js +31 -128
  5. package/edge.js +13 -91
  6. package/example/chrome_android.js +18 -13
  7. package/example/chrome_mobile_emulation.js +19 -15
  8. package/example/google_search.js +11 -11
  9. package/example/google_search_generator.js +23 -18
  10. package/example/google_search_test.js +31 -17
  11. package/example/logging.js +9 -17
  12. package/example/parallel_flows.js +3 -0
  13. package/firefox/binary.js +17 -0
  14. package/firefox/index.js +198 -101
  15. package/firefox/profile.js +24 -3
  16. package/http/util.js +95 -59
  17. package/ie.js +6 -10
  18. package/index.js +636 -2
  19. package/lib/actions.js +18 -10
  20. package/lib/atoms/getAttribute.js +11 -0
  21. package/lib/atoms/isDisplayed.js +106 -0
  22. package/lib/firefox/amd64/libnoblur64.so +0 -0
  23. package/lib/firefox/i386/libnoblur.so +0 -0
  24. package/lib/firefox/webdriver.xpi +0 -0
  25. package/lib/http.js +159 -46
  26. package/lib/logging.js +7 -1
  27. package/lib/promise.js +602 -358
  28. package/lib/proxy.js +28 -0
  29. package/lib/session.js +1 -1
  30. package/lib/test/build.js +26 -28
  31. package/lib/test/data/formPage.html +1 -0
  32. package/lib/test/fileserver.js +12 -10
  33. package/lib/test/httpserver.js +4 -6
  34. package/lib/test/index.js +18 -11
  35. package/lib/test/promise.js +79 -0
  36. package/lib/webdriver.js +587 -479
  37. package/opera.js +12 -88
  38. package/package.json +13 -14
  39. package/phantomjs.js +7 -14
  40. package/remote/index.js +201 -30
  41. package/safari.js +62 -416
  42. package/test/actions_test.js +9 -11
  43. package/test/chrome/options_test.js +8 -8
  44. package/test/chrome/service_test.js +2 -2
  45. package/test/cookie_test.js +57 -57
  46. package/test/element_finding_test.js +201 -185
  47. package/test/execute_script_test.js +108 -111
  48. package/test/fingerprint_test.js +16 -11
  49. package/test/firefox/firefox_test.js +75 -49
  50. package/test/firefox/profile_test.js +1 -3
  51. package/test/http/util_test.js +21 -27
  52. package/test/io_test.js +2 -5
  53. package/test/lib/http_test.js +8 -6
  54. package/test/lib/promise_aplus_test.js +48 -44
  55. package/test/lib/promise_error_test.js +687 -611
  56. package/test/lib/promise_flow_test.js +1810 -1806
  57. package/test/lib/promise_generator_test.js +227 -225
  58. package/test/lib/promise_test.js +864 -835
  59. package/test/lib/until_test.js +1 -1
  60. package/test/lib/webdriver_test.js +520 -424
  61. package/test/logging_test.js +43 -45
  62. package/test/net/portprober_test.js +23 -24
  63. package/test/page_loading_test.js +84 -78
  64. package/test/phantomjs/execute_phantomjs_test.js +11 -25
  65. package/test/proxy_test.js +23 -23
  66. package/test/remote_test.js +22 -22
  67. package/test/safari_test.js +108 -0
  68. package/test/session_test.js +11 -10
  69. package/test/stale_element_test.js +18 -16
  70. package/test/tag_name_test.js +7 -5
  71. package/test/testing/assert_test.js +2 -3
  72. package/test/testing/index_test.js +163 -117
  73. package/test/upload_test.js +13 -13
  74. package/test/window_test.js +94 -70
  75. package/testing/index.js +176 -37
  76. package/builder.js +0 -553
  77. package/lib/safari/client.js +0 -56
package/lib/promise.js CHANGED
@@ -17,6 +17,42 @@
17
17
 
18
18
  /**
19
19
  * @fileoverview
20
+ *
21
+ * > ### IMPORTANT NOTICE
22
+ * >
23
+ * > The promise manager contained in this module is in the process of being
24
+ * > phased out in favor of native JavaScript promises. This will be a long
25
+ * > process and will not be completed until there have been two major LTS Node
26
+ * > releases (approx. Node v10.0) that support
27
+ * > [async functions](https://tc39.github.io/ecmascript-asyncawait/).
28
+ * >
29
+ * > At this time, the promise manager can be disabled by setting an environment
30
+ * > variable, `SELENIUM_PROMISE_MANAGER=0`. In the absence of async functions,
31
+ * > users may use generators with the
32
+ * > {@link ./promise.consume promise.consume()} function to write "synchronous"
33
+ * > style tests:
34
+ * >
35
+ * > ```js
36
+ * > const {Builder, By, promise, until} = require('selenium-webdriver');
37
+ * >
38
+ * > let result = promise.consume(function* doGoogleSearch() {
39
+ * > let driver = new Builder().forBrowser('firefox').build();
40
+ * > yield driver.get('http://www.google.com/ncr');
41
+ * > yield driver.findElement(By.name('q')).sendKeys('webdriver');
42
+ * > yield driver.findElement(By.name('btnG')).click();
43
+ * > yield driver.wait(until.titleIs('webdriver - Google Search'), 1000);
44
+ * > yield driver.quit();
45
+ * > });
46
+ * >
47
+ * > result.then(_ => console.log('SUCCESS!'),
48
+ * > e => console.error('FAILURE: ' + e));
49
+ * > ```
50
+ * >
51
+ * > The motiviation behind this change and full deprecation plan are documented
52
+ * > in [issue 2969](https://github.com/SeleniumHQ/selenium/issues/2969).
53
+ * >
54
+ * >
55
+ *
20
56
  * The promise module is centered around the {@linkplain ControlFlow}, a class
21
57
  * that coordinates the execution of asynchronous tasks. The ControlFlow allows
22
58
  * users to focus on the imperative commands for their script without worrying
@@ -592,6 +628,7 @@
592
628
 
593
629
  'use strict';
594
630
 
631
+ const error = require('./error');
595
632
  const events = require('./events');
596
633
  const logging = require('./logging');
597
634
 
@@ -643,19 +680,6 @@ function asyncRun(fn) {
643
680
  });
644
681
  }
645
682
 
646
-
647
- /**
648
- * Throws an error asynchronously so it is reported to the global error handler.
649
- *
650
- * @param {!Error} error The error to throw.
651
- */
652
- function asyncThrow(error) {
653
- setTimeout(function() {
654
- throw error;
655
- }, 0);
656
- }
657
-
658
-
659
683
  /**
660
684
  * @param {number} level What level of verbosity to log with.
661
685
  * @param {(string|function(this: T): string)} loggable The message to log.
@@ -811,32 +835,56 @@ class MultipleUnhandledRejectionError extends Error {
811
835
  * @const
812
836
  */
813
837
  const IMPLEMENTED_BY_SYMBOL = Symbol('promise.Thenable');
838
+ const CANCELLABLE_SYMBOL = Symbol('promise.CancellableThenable');
839
+
840
+
841
+ /**
842
+ * @param {function(new: ?)} ctor
843
+ * @param {!Object} symbol
844
+ */
845
+ function addMarkerSymbol(ctor, symbol) {
846
+ try {
847
+ ctor.prototype[symbol] = true;
848
+ } catch (ignored) {
849
+ // Property access denied?
850
+ }
851
+ }
852
+
853
+
854
+ /**
855
+ * @param {*} object
856
+ * @param {!Object} symbol
857
+ * @return {boolean}
858
+ */
859
+ function hasMarkerSymbol(object, symbol) {
860
+ if (!object) {
861
+ return false;
862
+ }
863
+ try {
864
+ return !!object[symbol];
865
+ } catch (e) {
866
+ return false; // Property access seems to be forbidden.
867
+ }
868
+ }
814
869
 
815
870
 
816
871
  /**
817
872
  * Thenable is a promise-like object with a {@code then} method which may be
818
873
  * used to schedule callbacks on a promised value.
819
874
  *
820
- * @interface
875
+ * @record
821
876
  * @extends {IThenable<T>}
822
877
  * @template T
823
878
  */
824
879
  class Thenable {
825
880
  /**
826
881
  * Adds a property to a class prototype to allow runtime checks of whether
827
- * instances of that class implement the Thenable interface. This function
828
- * will also ensure the prototype's {@code then} function is exported from
829
- * compiled code.
882
+ * instances of that class implement the Thenable interface.
830
883
  * @param {function(new: Thenable, ...?)} ctor The
831
884
  * constructor whose prototype to modify.
832
885
  */
833
886
  static addImplementation(ctor) {
834
- ctor.prototype['then'] = ctor.prototype.then;
835
- try {
836
- ctor.prototype[IMPLEMENTED_BY_SYMBOL] = true;
837
- } catch (ignored) {
838
- // Property access denied?
839
- }
887
+ addMarkerSymbol(ctor, IMPLEMENTED_BY_SYMBOL);
840
888
  }
841
889
 
842
890
  /**
@@ -847,29 +895,9 @@ class Thenable {
847
895
  * interface.
848
896
  */
849
897
  static isImplementation(object) {
850
- if (!object) {
851
- return false;
852
- }
853
- try {
854
- return !!object[IMPLEMENTED_BY_SYMBOL];
855
- } catch (e) {
856
- return false; // Property access seems to be forbidden.
857
- }
898
+ return hasMarkerSymbol(object, IMPLEMENTED_BY_SYMBOL);
858
899
  }
859
900
 
860
- /**
861
- * Cancels the computation of this promise's value, rejecting the promise in
862
- * the process. This method is a no-op if the promise has already been
863
- * resolved.
864
- *
865
- * @param {(string|Error)=} opt_reason The reason this promise is being
866
- * cancelled. This value will be wrapped in a {@link CancellationError}.
867
- */
868
- cancel(opt_reason) {}
869
-
870
- /** @return {boolean} Whether this promise's value is still being computed. */
871
- isPending() {}
872
-
873
901
  /**
874
902
  * Registers listeners for when this instance is resolved.
875
903
  *
@@ -879,8 +907,8 @@ class Thenable {
879
907
  * @param {?(function(*): (R|IThenable<R>))=} opt_errback
880
908
  * The function to call if this promise is rejected. The function should
881
909
  * expect a single argument: the rejection reason.
882
- * @return {!ManagedPromise<R>} A new promise which will be
883
- * resolved with the result of the invoked callback.
910
+ * @return {!Thenable<R>} A new promise which will be resolved with the result
911
+ * of the invoked callback.
884
912
  * @template R
885
913
  */
886
914
  then(opt_callback, opt_errback) {}
@@ -904,49 +932,53 @@ class Thenable {
904
932
  * @param {function(*): (R|IThenable<R>)} errback The
905
933
  * function to call if this promise is rejected. The function should
906
934
  * expect a single argument: the rejection reason.
907
- * @return {!ManagedPromise<R>} A new promise which will be
908
- * resolved with the result of the invoked callback.
935
+ * @return {!Thenable<R>} A new promise which will be resolved with the result
936
+ * of the invoked callback.
909
937
  * @template R
910
938
  */
911
939
  catch(errback) {}
940
+ }
912
941
 
942
+
943
+ /**
944
+ * Marker interface for objects that allow consumers to request the cancellation
945
+ * of a promies-based operation. A cancelled promise will be rejected with a
946
+ * {@link CancellationError}.
947
+ *
948
+ * This interface is considered package-private and should not be used outside
949
+ * of selenium-webdriver.
950
+ *
951
+ * @interface
952
+ * @extends {Thenable<T>}
953
+ * @template T
954
+ * @package
955
+ */
956
+ class CancellableThenable {
913
957
  /**
914
- * Registers a listener to invoke when this promise is resolved, regardless
915
- * of whether the promise's value was successfully computed. This function
916
- * is synonymous with the {@code finally} clause in a synchronous API:
917
- *
918
- * // Synchronous API:
919
- * try {
920
- * doSynchronousWork();
921
- * } finally {
922
- * cleanUp();
923
- * }
924
- *
925
- * // Asynchronous promise API:
926
- * doAsynchronousWork().finally(cleanUp);
927
- *
928
- * __Note:__ similar to the {@code finally} clause, if the registered
929
- * callback returns a rejected promise or throws an error, it will silently
930
- * replace the rejection error (if any) from this promise:
931
- *
932
- * try {
933
- * throw Error('one');
934
- * } finally {
935
- * throw Error('two'); // Hides Error: one
936
- * }
937
- *
938
- * promise.rejected(Error('one'))
939
- * .finally(function() {
940
- * throw Error('two'); // Hides Error: one
941
- * });
958
+ * @param {function(new: CancellableThenable, ...?)} ctor
959
+ */
960
+ static addImplementation(ctor) {
961
+ Thenable.addImplementation(ctor);
962
+ addMarkerSymbol(ctor, CANCELLABLE_SYMBOL);
963
+ }
964
+
965
+ /**
966
+ * @param {*} object
967
+ * @return {boolean}
968
+ */
969
+ static isImplementation(object) {
970
+ return hasMarkerSymbol(object, CANCELLABLE_SYMBOL);
971
+ }
972
+
973
+ /**
974
+ * Requests the cancellation of the computation of this promise's value,
975
+ * rejecting the promise in the process. This method is a no-op if the promise
976
+ * has already been resolved.
942
977
  *
943
- * @param {function(): (R|IThenable<R>)} callback The function to call when
944
- * this promise is resolved.
945
- * @return {!ManagedPromise<R>} A promise that will be fulfilled
946
- * with the callback result.
947
- * @template R
978
+ * @param {(string|Error)=} opt_reason The reason this promise is being
979
+ * cancelled. This value will be wrapped in a {@link CancellationError}.
948
980
  */
949
- finally(callback) {}
981
+ cancel(opt_reason) {}
950
982
  }
951
983
 
952
984
 
@@ -979,7 +1011,7 @@ const ON_CANCEL_HANDLER = new WeakMap;
979
1011
  * fulfilled or rejected state, at which point the promise is considered
980
1012
  * resolved.
981
1013
  *
982
- * @implements {Thenable<T>}
1014
+ * @implements {CancellableThenable<T>}
983
1015
  * @template T
984
1016
  * @see http://promises-aplus.github.io/promises-spec/
985
1017
  */
@@ -995,6 +1027,12 @@ class ManagedPromise {
995
1027
  * this instance was created under. Defaults to the currently active flow.
996
1028
  */
997
1029
  constructor(resolver, opt_flow) {
1030
+ if (!usePromiseManager()) {
1031
+ throw TypeError(
1032
+ 'Unable to create a managed promise instance: the promise manager has'
1033
+ + ' been disabled by the SELENIUM_PROMISE_MANAGER environment'
1034
+ + ' variable: ' + process.env['SELENIUM_PROMISE_MANAGER']);
1035
+ }
998
1036
  getUid(this);
999
1037
 
1000
1038
  /** @private {!ControlFlow} */
@@ -1036,6 +1074,30 @@ class ManagedPromise {
1036
1074
  }
1037
1075
  }
1038
1076
 
1077
+ /**
1078
+ * Creates a promise that is immediately resolved with the given value.
1079
+ *
1080
+ * @param {T=} opt_value The value to resolve.
1081
+ * @return {!ManagedPromise<T>} A promise resolved with the given value.
1082
+ * @template T
1083
+ */
1084
+ static resolve(opt_value) {
1085
+ if (opt_value instanceof ManagedPromise) {
1086
+ return opt_value;
1087
+ }
1088
+ return new ManagedPromise(resolve => resolve(opt_value));
1089
+ }
1090
+
1091
+ /**
1092
+ * Creates a promise that is immediately rejected with the given reason.
1093
+ *
1094
+ * @param {*=} opt_reason The rejection reason.
1095
+ * @return {!ManagedPromise<?>} A new rejected promise.
1096
+ */
1097
+ static reject(opt_reason) {
1098
+ return new ManagedPromise((_, reject) => reject(opt_reason));
1099
+ }
1100
+
1039
1101
  /** @override */
1040
1102
  toString() {
1041
1103
  return 'ManagedPromise::' + getUid(this) +
@@ -1188,7 +1250,7 @@ class ManagedPromise {
1188
1250
  }
1189
1251
 
1190
1252
  if (this.parent_ && canCancel(this.parent_)) {
1191
- this.parent_.cancel(opt_reason);
1253
+ /** @type {!CancellableThenable} */(this.parent_).cancel(opt_reason);
1192
1254
  } else {
1193
1255
  var reason = CancellationError.wrap(opt_reason);
1194
1256
  let onCancel = ON_CANCEL_HANDLER.get(this);
@@ -1206,18 +1268,13 @@ class ManagedPromise {
1206
1268
 
1207
1269
  function canCancel(promise) {
1208
1270
  if (!(promise instanceof ManagedPromise)) {
1209
- return Thenable.isImplementation(promise);
1271
+ return CancellableThenable.isImplementation(promise);
1210
1272
  }
1211
1273
  return promise.state_ === PromiseState.PENDING
1212
1274
  || promise.state_ === PromiseState.BLOCKED;
1213
1275
  }
1214
1276
  }
1215
1277
 
1216
- /** @override */
1217
- isPending() {
1218
- return this.state_ === PromiseState.PENDING;
1219
- }
1220
-
1221
1278
  /** @override */
1222
1279
  then(opt_callback, opt_errback) {
1223
1280
  return this.addCallback_(
@@ -1230,21 +1287,15 @@ class ManagedPromise {
1230
1287
  null, errback, 'catch', ManagedPromise.prototype.catch);
1231
1288
  }
1232
1289
 
1233
- /** @override */
1290
+ /**
1291
+ * @param {function(): (R|IThenable<R>)} callback
1292
+ * @return {!ManagedPromise<R>}
1293
+ * @template R
1294
+ * @see ./promise.finally()
1295
+ */
1234
1296
  finally(callback) {
1235
- var error;
1236
- var mustThrow = false;
1237
- return this.then(function() {
1238
- return callback();
1239
- }, function(err) {
1240
- error = err;
1241
- mustThrow = true;
1242
- return callback();
1243
- }).then(function() {
1244
- if (mustThrow) {
1245
- throw error;
1246
- }
1247
- });
1297
+ let result = thenFinally(this, callback);
1298
+ return /** @type {!ManagedPromise} */(result);
1248
1299
  }
1249
1300
 
1250
1301
  /**
@@ -1320,7 +1371,16 @@ class ManagedPromise {
1320
1371
  }
1321
1372
  }
1322
1373
  }
1323
- Thenable.addImplementation(ManagedPromise);
1374
+ CancellableThenable.addImplementation(ManagedPromise);
1375
+
1376
+
1377
+ /**
1378
+ * @param {!ManagedPromise} promise
1379
+ * @return {boolean}
1380
+ */
1381
+ function isPending(promise) {
1382
+ return promise.state_ === PromiseState.PENDING;
1383
+ }
1324
1384
 
1325
1385
 
1326
1386
  /**
@@ -1417,19 +1477,11 @@ function isPromise(value) {
1417
1477
  * Creates a promise that will be resolved at a set time in the future.
1418
1478
  * @param {number} ms The amount of time, in milliseconds, to wait before
1419
1479
  * resolving the promise.
1420
- * @return {!ManagedPromise} The promise.
1480
+ * @return {!Thenable} The promise.
1421
1481
  */
1422
1482
  function delayed(ms) {
1423
- var key;
1424
- return new ManagedPromise(function(fulfill) {
1425
- key = setTimeout(function() {
1426
- key = null;
1427
- fulfill();
1428
- }, ms);
1429
- }).catch(function(e) {
1430
- clearTimeout(key);
1431
- key = null;
1432
- throw e;
1483
+ return createPromise(resolve => {
1484
+ setTimeout(() => resolve(), ms);
1433
1485
  });
1434
1486
  }
1435
1487
 
@@ -1448,15 +1500,11 @@ function defer() {
1448
1500
  * Creates a promise that has been resolved with the given value.
1449
1501
  * @param {T=} opt_value The resolved value.
1450
1502
  * @return {!ManagedPromise<T>} The resolved promise.
1503
+ * @deprecated Use {@link ManagedPromise#resolve Promise.resolve(value)}.
1451
1504
  * @template T
1452
1505
  */
1453
1506
  function fulfilled(opt_value) {
1454
- if (opt_value instanceof ManagedPromise) {
1455
- return opt_value;
1456
- }
1457
- return new ManagedPromise(function(fulfill) {
1458
- fulfill(opt_value);
1459
- });
1507
+ return ManagedPromise.resolve(opt_value);
1460
1508
  }
1461
1509
 
1462
1510
 
@@ -1464,16 +1512,11 @@ function fulfilled(opt_value) {
1464
1512
  * Creates a promise that has been rejected with the given reason.
1465
1513
  * @param {*=} opt_reason The rejection reason; may be any value, but is
1466
1514
  * usually an Error or a string.
1467
- * @return {!ManagedPromise<T>} The rejected promise.
1468
- * @template T
1515
+ * @return {!ManagedPromise<?>} The rejected promise.
1516
+ * @deprecated Use {@link ManagedPromise#reject Promise.reject(reason)}.
1469
1517
  */
1470
1518
  function rejected(opt_reason) {
1471
- if (opt_reason instanceof ManagedPromise) {
1472
- return opt_reason;
1473
- }
1474
- return new ManagedPromise(function(_, reject) {
1475
- reject(opt_reason);
1476
- });
1519
+ return ManagedPromise.reject(opt_reason);
1477
1520
  }
1478
1521
 
1479
1522
 
@@ -1486,12 +1529,12 @@ function rejected(opt_reason) {
1486
1529
  * @param {!Function} fn The function to wrap.
1487
1530
  * @param {...?} var_args The arguments to apply to the function, excluding the
1488
1531
  * final callback.
1489
- * @return {!ManagedPromise} A promise that will be resolved with the
1532
+ * @return {!Thenable} A promise that will be resolved with the
1490
1533
  * result of the provided function's callback.
1491
1534
  */
1492
1535
  function checkedNodeCall(fn, var_args) {
1493
1536
  let args = Array.prototype.slice.call(arguments, 1);
1494
- return new ManagedPromise(function(fulfill, reject) {
1537
+ return createPromise(function(fulfill, reject) {
1495
1538
  try {
1496
1539
  args.push(function(error, value) {
1497
1540
  error ? reject(error) : fulfill(value);
@@ -1503,6 +1546,59 @@ function checkedNodeCall(fn, var_args) {
1503
1546
  });
1504
1547
  }
1505
1548
 
1549
+ /**
1550
+ * Registers a listener to invoke when a promise is resolved, regardless
1551
+ * of whether the promise's value was successfully computed. This function
1552
+ * is synonymous with the {@code finally} clause in a synchronous API:
1553
+ *
1554
+ * // Synchronous API:
1555
+ * try {
1556
+ * doSynchronousWork();
1557
+ * } finally {
1558
+ * cleanUp();
1559
+ * }
1560
+ *
1561
+ * // Asynchronous promise API:
1562
+ * doAsynchronousWork().finally(cleanUp);
1563
+ *
1564
+ * __Note:__ similar to the {@code finally} clause, if the registered
1565
+ * callback returns a rejected promise or throws an error, it will silently
1566
+ * replace the rejection error (if any) from this promise:
1567
+ *
1568
+ * try {
1569
+ * throw Error('one');
1570
+ * } finally {
1571
+ * throw Error('two'); // Hides Error: one
1572
+ * }
1573
+ *
1574
+ * let p = Promise.reject(Error('one'));
1575
+ * promise.finally(p, function() {
1576
+ * throw Error('two'); // Hides Error: one
1577
+ * });
1578
+ *
1579
+ * @param {!IThenable<?>} promise The promise to add the listener to.
1580
+ * @param {function(): (R|IThenable<R>)} callback The function to call when
1581
+ * the promise is resolved.
1582
+ * @return {!IThenable<R>} A promise that will be resolved with the callback
1583
+ * result.
1584
+ * @template R
1585
+ */
1586
+ function thenFinally(promise, callback) {
1587
+ let error;
1588
+ let mustThrow = false;
1589
+ return promise.then(function() {
1590
+ return callback();
1591
+ }, function(err) {
1592
+ error = err;
1593
+ mustThrow = true;
1594
+ return callback();
1595
+ }).then(function() {
1596
+ if (mustThrow) {
1597
+ throw error;
1598
+ }
1599
+ });
1600
+ }
1601
+
1506
1602
 
1507
1603
  /**
1508
1604
  * Registers an observer on a promised {@code value}, returning a new promise
@@ -1513,16 +1609,15 @@ function checkedNodeCall(fn, var_args) {
1513
1609
  * resolved successfully.
1514
1610
  * @param {Function=} opt_errback The function to call when the value is
1515
1611
  * rejected.
1516
- * @return {!ManagedPromise} A new promise.
1612
+ * @return {!Thenable} A new promise.
1517
1613
  */
1518
1614
  function when(value, opt_callback, opt_errback) {
1519
1615
  if (Thenable.isImplementation(value)) {
1520
1616
  return value.then(opt_callback, opt_errback);
1521
1617
  }
1522
1618
 
1523
- return new ManagedPromise(function(fulfill) {
1524
- fulfill(value);
1525
- }).then(opt_callback, opt_errback);
1619
+ return createPromise(resolve => resolve(value))
1620
+ .then(opt_callback, opt_errback);
1526
1621
  }
1527
1622
 
1528
1623
 
@@ -1554,14 +1649,14 @@ function asap(value, callback, opt_errback) {
1554
1649
  *
1555
1650
  * @param {!Array<(T|!ManagedPromise<T>)>} arr An array of
1556
1651
  * promises to wait on.
1557
- * @return {!ManagedPromise<!Array<T>>} A promise that is
1652
+ * @return {!Thenable<!Array<T>>} A promise that is
1558
1653
  * fulfilled with an array containing the fulfilled values of the
1559
1654
  * input array, or rejected with the same reason as the first
1560
1655
  * rejected value.
1561
1656
  * @template T
1562
1657
  */
1563
1658
  function all(arr) {
1564
- return new ManagedPromise(function(fulfill, reject) {
1659
+ return createPromise(function(fulfill, reject) {
1565
1660
  var n = arr.length;
1566
1661
  var values = [];
1567
1662
 
@@ -1615,12 +1710,12 @@ function all(arr) {
1615
1710
  * @template TYPE, SELF
1616
1711
  */
1617
1712
  function map(arr, fn, opt_self) {
1618
- return fulfilled(arr).then(function(v) {
1713
+ return createPromise(resolve => resolve(arr)).then(v => {
1619
1714
  if (!Array.isArray(v)) {
1620
1715
  throw TypeError('not an array');
1621
1716
  }
1622
1717
  var arr = /** @type {!Array} */(v);
1623
- return new ManagedPromise(function(fulfill, reject) {
1718
+ return createPromise(function(fulfill, reject) {
1624
1719
  var n = arr.length;
1625
1720
  var values = new Array(n);
1626
1721
  (function processNext(i) {
@@ -1673,12 +1768,12 @@ function map(arr, fn, opt_self) {
1673
1768
  * @template TYPE, SELF
1674
1769
  */
1675
1770
  function filter(arr, fn, opt_self) {
1676
- return fulfilled(arr).then(function(v) {
1771
+ return createPromise(resolve => resolve(arr)).then(v => {
1677
1772
  if (!Array.isArray(v)) {
1678
1773
  throw TypeError('not an array');
1679
1774
  }
1680
1775
  var arr = /** @type {!Array} */(v);
1681
- return new ManagedPromise(function(fulfill, reject) {
1776
+ return createPromise(function(fulfill, reject) {
1682
1777
  var n = arr.length;
1683
1778
  var values = [];
1684
1779
  var valuesLength = 0;
@@ -1726,7 +1821,7 @@ function filter(arr, fn, opt_self) {
1726
1821
  * promise.fullyResolved(value); // Stack overflow.
1727
1822
  *
1728
1823
  * @param {*} value The value to fully resolve.
1729
- * @return {!ManagedPromise} A promise for a fully resolved version
1824
+ * @return {!Thenable} A promise for a fully resolved version
1730
1825
  * of the input value.
1731
1826
  */
1732
1827
  function fullyResolved(value) {
@@ -1740,7 +1835,7 @@ function fullyResolved(value) {
1740
1835
  /**
1741
1836
  * @param {*} value The value to fully resolve. If a promise, assumed to
1742
1837
  * already be resolved.
1743
- * @return {!ManagedPromise} A promise for a fully resolved version
1838
+ * @return {!Thenable} A promise for a fully resolved version
1744
1839
  * of the input value.
1745
1840
  */
1746
1841
  function fullyResolveValue(value) {
@@ -1767,13 +1862,13 @@ function fullyResolveValue(value) {
1767
1862
  return fullyResolveKeys(/** @type {!Object} */ (value));
1768
1863
  }
1769
1864
 
1770
- return fulfilled(value);
1865
+ return createPromise(resolve => resolve(value));
1771
1866
  }
1772
1867
 
1773
1868
 
1774
1869
  /**
1775
1870
  * @param {!(Array|Object)} obj the object to resolve.
1776
- * @return {!ManagedPromise} A promise that will be resolved with the
1871
+ * @return {!Thenable} A promise that will be resolved with the
1777
1872
  * input object once all of its values have been fully resolved.
1778
1873
  */
1779
1874
  function fullyResolveKeys(obj) {
@@ -1785,8 +1880,9 @@ function fullyResolveKeys(obj) {
1785
1880
  }
1786
1881
  return n;
1787
1882
  })();
1883
+
1788
1884
  if (!numKeys) {
1789
- return fulfilled(obj);
1885
+ return createPromise(resolve => resolve(obj));
1790
1886
  }
1791
1887
 
1792
1888
  function forEachProperty(obj, fn) {
@@ -1800,7 +1896,7 @@ function fullyResolveKeys(obj) {
1800
1896
  }
1801
1897
 
1802
1898
  var numResolved = 0;
1803
- return new ManagedPromise(function(fulfill, reject) {
1899
+ return createPromise(function(fulfill, reject) {
1804
1900
  var forEachKey = isArray ? forEachElement: forEachProperty;
1805
1901
 
1806
1902
  forEachKey(obj, function(partialValue, key) {
@@ -1834,6 +1930,236 @@ function fullyResolveKeys(obj) {
1834
1930
  //////////////////////////////////////////////////////////////////////////////
1835
1931
 
1836
1932
 
1933
+ /**
1934
+ * Defines methods for coordinating the execution of asynchronous tasks.
1935
+ * @record
1936
+ */
1937
+ class Scheduler {
1938
+ /**
1939
+ * Schedules a task for execution. If the task function is a generator, the
1940
+ * task will be executed using {@link ./promise.consume consume()}.
1941
+ *
1942
+ * @param {function(): (T|IThenable<T>)} fn The function to call to start the
1943
+ * task.
1944
+ * @param {string=} opt_description A description of the task for debugging
1945
+ * purposes.
1946
+ * @return {!Thenable<T>} A promise that will be resolved with the task
1947
+ * result.
1948
+ * @template T
1949
+ */
1950
+ execute(fn, opt_description) {}
1951
+
1952
+ /**
1953
+ * Creates a new promise using the given resolver function.
1954
+ *
1955
+ * @param {function(
1956
+ * function((T|IThenable<T>|Thenable|null)=),
1957
+ * function(*=))} resolver
1958
+ * @return {!Thenable<T>}
1959
+ * @template T
1960
+ */
1961
+ promise(resolver) {}
1962
+
1963
+ /**
1964
+ * Schedules a `setTimeout` call.
1965
+ *
1966
+ * @param {number} ms The timeout delay, in milliseconds.
1967
+ * @param {string=} opt_description A description to accompany the timeout.
1968
+ * @return {!Thenable<void>} A promise that will be resolved when the timeout
1969
+ * fires.
1970
+ */
1971
+ timeout(ms, opt_description) {}
1972
+
1973
+ /**
1974
+ * Schedules a task to wait for a condition to hold.
1975
+ *
1976
+ * If the condition is defined as a function, it may return any value. Promies
1977
+ * will be resolved before testing if the condition holds (resolution time
1978
+ * counts towards the timeout). Once resolved, values are always evaluated as
1979
+ * booleans.
1980
+ *
1981
+ * If the condition function throws, or returns a rejected promise, the
1982
+ * wait task will fail.
1983
+ *
1984
+ * If the condition is defined as a promise, the scheduler will wait for it to
1985
+ * settle. If the timeout expires before the promise settles, the promise
1986
+ * returned by this function will be rejected.
1987
+ *
1988
+ * If this function is invoked with `timeout === 0`, or the timeout is
1989
+ * omitted, this scheduler will wait indefinitely for the condition to be
1990
+ * satisfied.
1991
+ *
1992
+ * @param {(!IThenable<T>|function())} condition The condition to poll,
1993
+ * or a promise to wait on.
1994
+ * @param {number=} opt_timeout How long to wait, in milliseconds, for the
1995
+ * condition to hold before timing out. If omitted, the flow will wait
1996
+ * indefinitely.
1997
+ * @param {string=} opt_message An optional error message to include if the
1998
+ * wait times out; defaults to the empty string.
1999
+ * @return {!Thenable<T>} A promise that will be fulfilled
2000
+ * when the condition has been satisified. The promise shall be rejected
2001
+ * if the wait times out waiting for the condition.
2002
+ * @throws {TypeError} If condition is not a function or promise or if timeout
2003
+ * is not a number >= 0.
2004
+ * @template T
2005
+ */
2006
+ wait(condition, opt_timeout, opt_message) {}
2007
+ }
2008
+
2009
+
2010
+ let USE_PROMISE_MANAGER;
2011
+ function usePromiseManager() {
2012
+ if (typeof USE_PROMISE_MANAGER !== 'undefined') {
2013
+ return !!USE_PROMISE_MANAGER;
2014
+ }
2015
+ return process.env['SELENIUM_PROMISE_MANAGER'] === undefined
2016
+ || !/^0|false$/i.test(process.env['SELENIUM_PROMISE_MANAGER']);
2017
+ }
2018
+
2019
+
2020
+ /**
2021
+ * @param {function(
2022
+ * function((T|IThenable<T>|Thenable|null)=),
2023
+ * function(*=))} resolver
2024
+ * @return {!Thenable<T>}
2025
+ * @template T
2026
+ */
2027
+ function createPromise(resolver) {
2028
+ let ctor = usePromiseManager() ? ManagedPromise : NativePromise;
2029
+ return new ctor(resolver);
2030
+ }
2031
+
2032
+
2033
+ /**
2034
+ * @param {!Scheduler} scheduler The scheduler to use.
2035
+ * @param {(!IThenable<T>|function())} condition The condition to poll,
2036
+ * or a promise to wait on.
2037
+ * @param {number=} opt_timeout How long to wait, in milliseconds, for the
2038
+ * condition to hold before timing out. If omitted, the flow will wait
2039
+ * indefinitely.
2040
+ * @param {string=} opt_message An optional error message to include if the
2041
+ * wait times out; defaults to the empty string.
2042
+ * @return {!Thenable<T>} A promise that will be fulfilled
2043
+ * when the condition has been satisified. The promise shall be rejected
2044
+ * if the wait times out waiting for the condition.
2045
+ * @throws {TypeError} If condition is not a function or promise or if timeout
2046
+ * is not a number >= 0.
2047
+ * @template T
2048
+ */
2049
+ function scheduleWait(scheduler, condition, opt_timeout, opt_message) {
2050
+ let timeout = opt_timeout || 0;
2051
+ if (typeof timeout !== 'number' || timeout < 0) {
2052
+ throw TypeError('timeout must be a number >= 0: ' + timeout);
2053
+ }
2054
+
2055
+ if (isPromise(condition)) {
2056
+ return scheduler.execute(function() {
2057
+ if (!timeout) {
2058
+ return condition;
2059
+ }
2060
+ return scheduler.promise(function(fulfill, reject) {
2061
+ let start = Date.now();
2062
+ let timer = setTimeout(function() {
2063
+ timer = null;
2064
+ reject(
2065
+ new error.TimeoutError(
2066
+ (opt_message ? opt_message + '\n' : '')
2067
+ + 'Timed out waiting for promise to resolve after '
2068
+ + (Date.now() - start) + 'ms'));
2069
+ }, timeout);
2070
+
2071
+ /** @type {Thenable} */(condition).then(
2072
+ function(value) {
2073
+ timer && clearTimeout(timer);
2074
+ fulfill(value);
2075
+ },
2076
+ function(error) {
2077
+ timer && clearTimeout(timer);
2078
+ reject(error);
2079
+ });
2080
+ });
2081
+ }, opt_message || '<anonymous wait: promise resolution>');
2082
+ }
2083
+
2084
+ if (typeof condition !== 'function') {
2085
+ throw TypeError('Invalid condition; must be a function or promise: ' +
2086
+ typeof condition);
2087
+ }
2088
+
2089
+ if (isGenerator(condition)) {
2090
+ let original = condition;
2091
+ condition = () => consume(original);
2092
+ }
2093
+
2094
+ return scheduler.execute(function() {
2095
+ var startTime = Date.now();
2096
+ return scheduler.promise(function(fulfill, reject) {
2097
+ pollCondition();
2098
+
2099
+ function pollCondition() {
2100
+ var conditionFn = /** @type {function()} */(condition);
2101
+ scheduler.execute(conditionFn).then(function(value) {
2102
+ var elapsed = Date.now() - startTime;
2103
+ if (!!value) {
2104
+ fulfill(value);
2105
+ } else if (timeout && elapsed >= timeout) {
2106
+ reject(
2107
+ new error.TimeoutError(
2108
+ (opt_message ? opt_message + '\n' : '')
2109
+ + `Wait timed out after ${elapsed}ms`));
2110
+ } else {
2111
+ // Do not use asyncRun here because we need a non-micro yield
2112
+ // here so the UI thread is given a chance when running in a
2113
+ // browser.
2114
+ setTimeout(pollCondition, 0);
2115
+ }
2116
+ }, reject);
2117
+ }
2118
+ });
2119
+ }, opt_message || '<anonymous wait>');
2120
+ }
2121
+
2122
+
2123
+ /**
2124
+ * A scheduler that executes all tasks immediately, with no coordination. This
2125
+ * class is an event emitter for API compatibility with the {@link ControlFlow},
2126
+ * however, it emits no events.
2127
+ *
2128
+ * @implements {Scheduler}
2129
+ */
2130
+ class SimpleScheduler extends events.EventEmitter {
2131
+ /** @override */
2132
+ execute(fn) {
2133
+ return this.promise((resolve, reject) => {
2134
+ try {
2135
+ if (isGenerator(fn)) {
2136
+ consume(fn).then(resolve, reject);
2137
+ } else {
2138
+ resolve(fn.call(undefined));
2139
+ }
2140
+ } catch (ex) {
2141
+ reject(ex);
2142
+ }
2143
+ });
2144
+ }
2145
+
2146
+ /** @override */
2147
+ promise(resolver) {
2148
+ return new NativePromise(resolver);
2149
+ }
2150
+
2151
+ /** @override */
2152
+ timeout(ms) {
2153
+ return this.promise(resolve => setTimeout(_ => resolve(), ms));
2154
+ }
2155
+
2156
+ /** @override */
2157
+ wait(condition, opt_timeout, opt_message) {
2158
+ return scheduleWait(this, condition, opt_timeout, opt_message);
2159
+ }
2160
+ }
2161
+ const SIMPLE_SCHEDULER = new SimpleScheduler;
2162
+
1837
2163
 
1838
2164
  /**
1839
2165
  * Handles the execution of scheduled tasks, each of which may be an
@@ -1860,13 +2186,20 @@ function fullyResolveKeys(obj) {
1860
2186
  * If there are no listeners registered with the flow, the error will be
1861
2187
  * rethrown to the global error handler.
1862
2188
  *
1863
- * Refer to the {@link ./promise} module documentation fora detailed
2189
+ * Refer to the {@link ./promise} module documentation for a detailed
1864
2190
  * explanation of how the ControlFlow coordinates task execution.
1865
2191
  *
2192
+ * @implements {Scheduler}
1866
2193
  * @final
1867
2194
  */
1868
2195
  class ControlFlow extends events.EventEmitter {
1869
2196
  constructor() {
2197
+ if (!usePromiseManager()) {
2198
+ throw TypeError(
2199
+ 'Cannot instantiate control flow when the promise manager has'
2200
+ + ' been disabled');
2201
+ }
2202
+
1870
2203
  super();
1871
2204
 
1872
2205
  /** @private {boolean} */
@@ -2036,21 +2369,7 @@ class ControlFlow extends events.EventEmitter {
2036
2369
  return this.activeQueue_;
2037
2370
  }
2038
2371
 
2039
- /**
2040
- * Schedules a task for execution. If there is nothing currently in the
2041
- * queue, the task will be executed in the next turn of the event loop. If
2042
- * the task function is a generator, the task will be executed using
2043
- * {@link ./promise.consume consume()}.
2044
- *
2045
- * @param {function(): (T|ManagedPromise<T>)} fn The function to
2046
- * call to start the task. If the function returns a
2047
- * {@link ManagedPromise}, this instance will wait for it to be
2048
- * resolved before starting the next task.
2049
- * @param {string=} opt_description A description of the task.
2050
- * @return {!ManagedPromise<T>} A promise that will be resolved
2051
- * with the result of the action.
2052
- * @template T
2053
- */
2372
+ /** @override */
2054
2373
  execute(fn, opt_description) {
2055
2374
  if (isGenerator(fn)) {
2056
2375
  let original = fn;
@@ -2072,126 +2391,21 @@ class ControlFlow extends events.EventEmitter {
2072
2391
  return task.promise;
2073
2392
  }
2074
2393
 
2075
- /**
2076
- * Inserts a {@code setTimeout} into the command queue. This is equivalent to
2077
- * a thread sleep in a synchronous programming language.
2078
- *
2079
- * @param {number} ms The timeout delay, in milliseconds.
2080
- * @param {string=} opt_description A description to accompany the timeout.
2081
- * @return {!ManagedPromise} A promise that will be resolved with
2082
- * the result of the action.
2083
- */
2394
+ /** @override */
2395
+ promise(resolver) {
2396
+ return new ManagedPromise(resolver, this);
2397
+ }
2398
+
2399
+ /** @override */
2084
2400
  timeout(ms, opt_description) {
2085
- return this.execute(function() {
2086
- return delayed(ms);
2401
+ return this.execute(() => {
2402
+ return this.promise(resolve => setTimeout(() => resolve(), ms));
2087
2403
  }, opt_description);
2088
2404
  }
2089
2405
 
2090
- /**
2091
- * Schedules a task that shall wait for a condition to hold. Each condition
2092
- * function may return any value, but it will always be evaluated as a
2093
- * boolean.
2094
- *
2095
- * Condition functions may schedule sub-tasks with this instance, however,
2096
- * their execution time will be factored into whether a wait has timed out.
2097
- *
2098
- * In the event a condition returns a ManagedPromise, the polling loop will wait for
2099
- * it to be resolved before evaluating whether the condition has been
2100
- * satisfied. The resolution time for a promise is factored into whether a
2101
- * wait has timed out.
2102
- *
2103
- * If the condition function throws, or returns a rejected promise, the
2104
- * wait task will fail.
2105
- *
2106
- * If the condition is defined as a promise, the flow will wait for it to
2107
- * settle. If the timeout expires before the promise settles, the promise
2108
- * returned by this function will be rejected.
2109
- *
2110
- * If this function is invoked with `timeout === 0`, or the timeout is
2111
- * omitted, the flow will wait indefinitely for the condition to be satisfied.
2112
- *
2113
- * @param {(!ManagedPromise<T>|function())} condition The condition to poll,
2114
- * or a promise to wait on.
2115
- * @param {number=} opt_timeout How long to wait, in milliseconds, for the
2116
- * condition to hold before timing out. If omitted, the flow will wait
2117
- * indefinitely.
2118
- * @param {string=} opt_message An optional error message to include if the
2119
- * wait times out; defaults to the empty string.
2120
- * @return {!ManagedPromise<T>} A promise that will be fulfilled
2121
- * when the condition has been satisified. The promise shall be rejected
2122
- * if the wait times out waiting for the condition.
2123
- * @throws {TypeError} If condition is not a function or promise or if timeout
2124
- * is not a number >= 0.
2125
- * @template T
2126
- */
2406
+ /** @override */
2127
2407
  wait(condition, opt_timeout, opt_message) {
2128
- var timeout = opt_timeout || 0;
2129
- if (typeof timeout !== 'number' || timeout < 0) {
2130
- throw TypeError('timeout must be a number >= 0: ' + timeout);
2131
- }
2132
-
2133
- if (isPromise(condition)) {
2134
- return this.execute(function() {
2135
- if (!timeout) {
2136
- return condition;
2137
- }
2138
- return new ManagedPromise(function(fulfill, reject) {
2139
- var start = Date.now();
2140
- var timer = setTimeout(function() {
2141
- timer = null;
2142
- reject(Error((opt_message ? opt_message + '\n' : '') +
2143
- 'Timed out waiting for promise to resolve after ' +
2144
- (Date.now() - start) + 'ms'));
2145
- }, timeout);
2146
-
2147
- /** @type {Thenable} */(condition).then(
2148
- function(value) {
2149
- timer && clearTimeout(timer);
2150
- fulfill(value);
2151
- },
2152
- function(error) {
2153
- timer && clearTimeout(timer);
2154
- reject(error);
2155
- });
2156
- });
2157
- }, opt_message || '<anonymous wait: promise resolution>');
2158
- }
2159
-
2160
- if (typeof condition !== 'function') {
2161
- throw TypeError('Invalid condition; must be a function or promise: ' +
2162
- typeof condition);
2163
- }
2164
-
2165
- if (isGenerator(condition)) {
2166
- let original = condition;
2167
- condition = () => consume(original);
2168
- }
2169
-
2170
- var self = this;
2171
- return this.execute(function() {
2172
- var startTime = Date.now();
2173
- return new ManagedPromise(function(fulfill, reject) {
2174
- pollCondition();
2175
-
2176
- function pollCondition() {
2177
- var conditionFn = /** @type {function()} */(condition);
2178
- self.execute(conditionFn).then(function(value) {
2179
- var elapsed = Date.now() - startTime;
2180
- if (!!value) {
2181
- fulfill(value);
2182
- } else if (timeout && elapsed >= timeout) {
2183
- reject(new Error((opt_message ? opt_message + '\n' : '') +
2184
- 'Wait timed out after ' + elapsed + 'ms'));
2185
- } else {
2186
- // Do not use asyncRun here because we need a non-micro yield
2187
- // here so the UI thread is given a chance when running in a
2188
- // browser.
2189
- setTimeout(pollCondition, 0);
2190
- }
2191
- }, reject);
2192
- }
2193
- });
2194
- }, opt_message || '<anonymous wait>');
2408
+ return scheduleWait(this, condition, opt_timeout, opt_message);
2195
2409
  }
2196
2410
 
2197
2411
  /**
@@ -2297,13 +2511,14 @@ class ControlFlow extends events.EventEmitter {
2297
2511
  this.cancelShutdown_();
2298
2512
  this.cancelHold_();
2299
2513
 
2300
- var listeners = this.listeners(
2301
- ControlFlow.EventType.UNCAUGHT_EXCEPTION);
2302
- if (!listeners.size) {
2303
- asyncThrow(/** @type {!Error} */(error));
2304
- } else {
2305
- this.reportUncaughtException_(error);
2306
- }
2514
+ setTimeout(() => {
2515
+ let listeners = this.listeners(ControlFlow.EventType.UNCAUGHT_EXCEPTION);
2516
+ if (!listeners.size) {
2517
+ throw error;
2518
+ } else {
2519
+ this.reportUncaughtException_(error);
2520
+ }
2521
+ }, 0);
2307
2522
  }
2308
2523
 
2309
2524
  /**
@@ -2521,6 +2736,9 @@ class TaskQueue extends events.EventEmitter {
2521
2736
  /** @private {({task: !Task, q: !TaskQueue}|null)} */
2522
2737
  this.pending_ = null;
2523
2738
 
2739
+ /** @private {TaskQueue} */
2740
+ this.subQ_ = null;
2741
+
2524
2742
  /** @private {TaskQueueState} */
2525
2743
  this.state_ = TaskQueueState.NEW;
2526
2744
 
@@ -2670,6 +2888,12 @@ class TaskQueue extends events.EventEmitter {
2670
2888
  this.tasks_ = [];
2671
2889
  }
2672
2890
 
2891
+ // Now that all of the remaining tasks have been silently cancelled (e.g. no
2892
+ // exisitng callbacks on those tasks will fire), clear the silence bit on
2893
+ // the cancellation error. This ensures additional callbacks registered in
2894
+ // the future will actually execute.
2895
+ cancellation.silent_ = false;
2896
+
2673
2897
  if (this.pending_) {
2674
2898
  vlog(2, () => this + '.abort(); cancelling pending task', this);
2675
2899
  this.pending_.task.promise.cancel(
@@ -2695,7 +2919,7 @@ class TaskQueue extends events.EventEmitter {
2695
2919
  var task;
2696
2920
  do {
2697
2921
  task = this.getNextTask_();
2698
- } while (task && !task.promise.isPending());
2922
+ } while (task && !isPending(task.promise));
2699
2923
 
2700
2924
  if (!task) {
2701
2925
  this.state_ = TaskQueueState.FINISHED;
@@ -2706,20 +2930,30 @@ class TaskQueue extends events.EventEmitter {
2706
2930
  return;
2707
2931
  }
2708
2932
 
2709
- var self = this;
2710
- var subQ = new TaskQueue(this.flow_);
2711
- subQ.once('end', () => self.onTaskComplete_(result))
2712
- .once('error', (e) => self.onTaskFailure_(result, e));
2713
- vlog(2, () => self + ' created ' + subQ + ' for ' + task);
2933
+ let result = undefined;
2934
+ this.subQ_ = new TaskQueue(this.flow_);
2935
+
2936
+ this.subQ_.once('end', () => { // On task completion.
2937
+ this.subQ_ = null;
2938
+ this.pending_ && this.pending_.task.fulfill(result);
2939
+ });
2940
+
2941
+ this.subQ_.once('error', e => { // On task failure.
2942
+ this.subQ_ = null;
2943
+ if (Thenable.isImplementation(result)) {
2944
+ result.cancel(CancellationError.wrap(e));
2945
+ }
2946
+ this.pending_ && this.pending_.task.reject(e);
2947
+ });
2948
+ vlog(2, () => `${this} created ${this.subQ_} for ${task}`);
2714
2949
 
2715
- var result = undefined;
2716
2950
  try {
2717
- this.pending_ = {task: task, q: subQ};
2951
+ this.pending_ = {task: task, q: this.subQ_};
2718
2952
  task.promise.queue_ = this;
2719
- result = subQ.execute_(task.execute);
2720
- subQ.start();
2953
+ result = this.subQ_.execute_(task.execute);
2954
+ this.subQ_.start();
2721
2955
  } catch (ex) {
2722
- subQ.abort_(ex);
2956
+ this.subQ_.abort_(ex);
2723
2957
  }
2724
2958
  }
2725
2959
 
@@ -2809,28 +3043,6 @@ class TaskQueue extends events.EventEmitter {
2809
3043
  }
2810
3044
  }
2811
3045
 
2812
- /**
2813
- * @param {*} value the value originally returned by the task function.
2814
- * @private
2815
- */
2816
- onTaskComplete_(value) {
2817
- if (this.pending_) {
2818
- this.pending_.task.fulfill(value);
2819
- }
2820
- }
2821
-
2822
- /**
2823
- * @param {*} taskFnResult the value originally returned by the task function.
2824
- * @param {*} error the error that caused the task function to terminate.
2825
- * @private
2826
- */
2827
- onTaskFailure_(taskFnResult, error) {
2828
- if (Thenable.isImplementation(taskFnResult)) {
2829
- taskFnResult.cancel(CancellationError.wrap(error));
2830
- }
2831
- this.pending_.task.reject(error);
2832
- }
2833
-
2834
3046
  /**
2835
3047
  * @return {(Task|undefined)} the next task scheduled within this queue,
2836
3048
  * if any.
@@ -2862,9 +3074,9 @@ class TaskQueue extends events.EventEmitter {
2862
3074
 
2863
3075
  /**
2864
3076
  * The default flow to use if no others are active.
2865
- * @type {!ControlFlow}
3077
+ * @type {ControlFlow}
2866
3078
  */
2867
- var defaultFlow = new ControlFlow();
3079
+ var defaultFlow;
2868
3080
 
2869
3081
 
2870
3082
  /**
@@ -2883,6 +3095,11 @@ var activeFlows = [];
2883
3095
  * @throws {Error} If the default flow is not currently active.
2884
3096
  */
2885
3097
  function setDefaultFlow(flow) {
3098
+ if (!usePromiseManager()) {
3099
+ throw Error(
3100
+ 'You may not change set the control flow when the promise'
3101
+ +' manager is disabled');
3102
+ }
2886
3103
  if (activeFlows.length) {
2887
3104
  throw Error('You may only change the default flow while it is active');
2888
3105
  }
@@ -2892,10 +3109,21 @@ function setDefaultFlow(flow) {
2892
3109
 
2893
3110
  /**
2894
3111
  * @return {!ControlFlow} The currently active control flow.
3112
+ * @suppress {checkTypes}
2895
3113
  */
2896
3114
  function controlFlow() {
2897
- return /** @type {!ControlFlow} */ (
2898
- activeFlows.length ? activeFlows[activeFlows.length - 1] : defaultFlow);
3115
+ if (!usePromiseManager()) {
3116
+ return SIMPLE_SCHEDULER;
3117
+ }
3118
+
3119
+ if (activeFlows.length) {
3120
+ return activeFlows[activeFlows.length - 1];
3121
+ }
3122
+
3123
+ if (!defaultFlow) {
3124
+ defaultFlow = new ControlFlow;
3125
+ }
3126
+ return defaultFlow;
2899
3127
  }
2900
3128
 
2901
3129
 
@@ -2905,8 +3133,7 @@ function controlFlow() {
2905
3133
  * a promise that resolves to the callback result.
2906
3134
  * @param {function(!ControlFlow)} callback The entry point
2907
3135
  * to the newly created flow.
2908
- * @return {!ManagedPromise} A promise that resolves to the callback
2909
- * result.
3136
+ * @return {!Thenable} A promise that resolves to the callback result.
2910
3137
  */
2911
3138
  function createFlow(callback) {
2912
3139
  var flow = new ControlFlow;
@@ -2960,53 +3187,51 @@ function isGenerator(fn) {
2960
3187
  * @param {Object=} opt_self The object to use as "this" when invoking the
2961
3188
  * initial generator.
2962
3189
  * @param {...*} var_args Any arguments to pass to the initial generator.
2963
- * @return {!ManagedPromise<?>} A promise that will resolve to the
3190
+ * @return {!Thenable<?>} A promise that will resolve to the
2964
3191
  * generator's final result.
2965
3192
  * @throws {TypeError} If the given function is not a generator.
2966
3193
  */
2967
- function consume(generatorFn, opt_self, var_args) {
3194
+ function consume(generatorFn, opt_self, ...var_args) {
2968
3195
  if (!isGenerator(generatorFn)) {
2969
3196
  throw new TypeError('Input is not a GeneratorFunction: ' +
2970
3197
  generatorFn.constructor.name);
2971
3198
  }
2972
3199
 
2973
- var deferred = defer();
2974
- var generator = generatorFn.apply(
2975
- opt_self, Array.prototype.slice.call(arguments, 2));
2976
- callNext();
2977
- return deferred.promise;
3200
+ let ret;
3201
+ return ret = createPromise((resolve, reject) => {
3202
+ let generator = generatorFn.apply(opt_self, var_args);
3203
+ callNext();
2978
3204
 
2979
- /** @param {*=} opt_value . */
2980
- function callNext(opt_value) {
2981
- pump(generator.next, opt_value);
2982
- }
2983
-
2984
- /** @param {*=} opt_error . */
2985
- function callThrow(opt_error) {
2986
- // Dictionary lookup required because Closure compiler's built-in
2987
- // externs does not include GeneratorFunction.prototype.throw.
2988
- pump(generator['throw'], opt_error);
2989
- }
2990
-
2991
- function pump(fn, opt_arg) {
2992
- if (!deferred.promise.isPending()) {
2993
- return; // Defererd was cancelled; silently abort.
3205
+ /** @param {*=} opt_value . */
3206
+ function callNext(opt_value) {
3207
+ pump(generator.next, opt_value);
2994
3208
  }
2995
3209
 
2996
- try {
2997
- var result = fn.call(generator, opt_arg);
2998
- } catch (ex) {
2999
- deferred.reject(ex);
3000
- return;
3210
+ /** @param {*=} opt_error . */
3211
+ function callThrow(opt_error) {
3212
+ pump(generator.throw, opt_error);
3001
3213
  }
3002
3214
 
3003
- if (result.done) {
3004
- deferred.fulfill(result.value);
3005
- return;
3006
- }
3215
+ function pump(fn, opt_arg) {
3216
+ if (ret instanceof ManagedPromise && !isPending(ret)) {
3217
+ return; // Defererd was cancelled; silently abort.
3218
+ }
3007
3219
 
3008
- asap(result.value, callNext, callThrow);
3009
- }
3220
+ try {
3221
+ var result = fn.call(generator, opt_arg);
3222
+ } catch (ex) {
3223
+ reject(ex);
3224
+ return;
3225
+ }
3226
+
3227
+ if (result.done) {
3228
+ resolve(result.value);
3229
+ return;
3230
+ }
3231
+
3232
+ asap(result.value, callNext, callThrow);
3233
+ }
3234
+ });
3010
3235
  }
3011
3236
 
3012
3237
 
@@ -3014,12 +3239,14 @@ function consume(generatorFn, opt_self, var_args) {
3014
3239
 
3015
3240
 
3016
3241
  module.exports = {
3242
+ CancellableThenable: CancellableThenable,
3017
3243
  CancellationError: CancellationError,
3018
3244
  ControlFlow: ControlFlow,
3019
3245
  Deferred: Deferred,
3020
3246
  MultipleUnhandledRejectionError: MultipleUnhandledRejectionError,
3021
3247
  Thenable: Thenable,
3022
3248
  Promise: ManagedPromise,
3249
+ Scheduler: Scheduler,
3023
3250
  all: all,
3024
3251
  asap: asap,
3025
3252
  captureStackTrace: captureStackTrace,
@@ -3030,6 +3257,7 @@ module.exports = {
3030
3257
  defer: defer,
3031
3258
  delayed: delayed,
3032
3259
  filter: filter,
3260
+ finally: thenFinally,
3033
3261
  fulfilled: fulfilled,
3034
3262
  fullyResolved: fullyResolved,
3035
3263
  isGenerator: isGenerator,
@@ -3039,6 +3267,22 @@ module.exports = {
3039
3267
  setDefaultFlow: setDefaultFlow,
3040
3268
  when: when,
3041
3269
 
3270
+ /**
3271
+ * Indicates whether the promise manager is currently enabled. When disabled,
3272
+ * attempting to use the {@link ControlFlow} or {@link ManagedPromise Promise}
3273
+ * classes will generate an error.
3274
+ *
3275
+ * The promise manager is currently enabled by default, but may be disabled
3276
+ * by setting the environment variable `SELENIUM_PROMISE_MANAGER=0` or by
3277
+ * setting this property to false. Setting this property will always take
3278
+ * precedence ove the use of the environment variable.
3279
+ *
3280
+ * @return {boolean} Whether the promise manager is enabled.
3281
+ * @see <https://github.com/SeleniumHQ/selenium/issues/2969>
3282
+ */
3283
+ get USE_PROMISE_MANAGER() { return usePromiseManager(); },
3284
+ set USE_PROMISE_MANAGER(/** boolean */value) { USE_PROMISE_MANAGER = value; },
3285
+
3042
3286
  get LONG_STACK_TRACES() { return LONG_STACK_TRACES; },
3043
3287
  set LONG_STACK_TRACES(v) { LONG_STACK_TRACES = v; },
3044
3288
  };