selenium-webdriver 2.43.5 → 2.44.0

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 (219) hide show
  1. package/CHANGES.md +20 -0
  2. package/README.md +8 -13
  3. package/builder.js +4 -3
  4. package/docs/class_webdriver_Alert.html +6 -6
  5. package/docs/class_webdriver_AlertPromise.html +6 -6
  6. package/docs/class_webdriver_UnhandledAlertError.html +4 -4
  7. package/docs/class_webdriver_WebDriver.html +39 -37
  8. package/docs/class_webdriver_WebDriver_Logs.html +3 -3
  9. package/docs/class_webdriver_WebDriver_Navigation.html +5 -5
  10. package/docs/class_webdriver_WebDriver_Options.html +9 -9
  11. package/docs/class_webdriver_WebDriver_TargetLocator.html +6 -6
  12. package/docs/class_webdriver_WebDriver_Timeouts.html +4 -4
  13. package/docs/class_webdriver_WebDriver_Window.html +6 -6
  14. package/docs/class_webdriver_WebElement.html +23 -23
  15. package/docs/class_webdriver_WebElementPromise.html +22 -22
  16. package/docs/class_webdriver_promise_CancellationError.html +3 -0
  17. package/docs/class_webdriver_promise_ControlFlow.html +32 -32
  18. package/docs/class_webdriver_promise_Deferred.html +7 -8
  19. package/docs/class_webdriver_promise_Frame_.html +14 -30
  20. package/docs/class_webdriver_promise_Promise.html +2 -2
  21. package/docs/class_webdriver_promise_Task_.html +12 -12
  22. package/docs/class_webdriver_until_Condition.html +3 -0
  23. package/docs/enum_webdriver_Key.html +1 -1
  24. package/docs/enum_webdriver_promise_ControlFlow_EventType.html +1 -1
  25. package/docs/enum_webdriver_promise_Deferred_State_.html +1 -1
  26. package/docs/index.html +8 -13
  27. package/docs/interface_webdriver_promise_Thenable.html +8 -10
  28. package/docs/license.html +1 -1
  29. package/docs/module_selenium-webdriver.html +2 -2
  30. package/docs/module_selenium-webdriver__base.html +1 -1
  31. package/docs/module_selenium-webdriver__base_class_Context.html +1 -1
  32. package/docs/module_selenium-webdriver_builder.html +1 -1
  33. package/docs/module_selenium-webdriver_builder_class_Builder.html +1 -1
  34. package/docs/module_selenium-webdriver_chrome.html +1 -1
  35. package/docs/module_selenium-webdriver_chrome_class_Driver.html +2 -2
  36. package/docs/module_selenium-webdriver_chrome_class_Options.html +2 -2
  37. package/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html +1 -1
  38. package/docs/module_selenium-webdriver_error.html +1 -1
  39. package/docs/module_selenium-webdriver_executors.html +1 -1
  40. package/docs/module_selenium-webdriver_firefox.html +1 -1
  41. package/docs/module_selenium-webdriver_firefox_binary.html +1 -1
  42. package/docs/module_selenium-webdriver_firefox_binary_class_Binary.html +1 -1
  43. package/docs/module_selenium-webdriver_firefox_class_Driver.html +2 -2
  44. package/docs/module_selenium-webdriver_firefox_class_Options.html +1 -1
  45. package/docs/module_selenium-webdriver_firefox_extension.html +1 -1
  46. package/docs/module_selenium-webdriver_firefox_profile.html +1 -1
  47. package/docs/module_selenium-webdriver_firefox_profile_class_Profile.html +1 -1
  48. package/docs/module_selenium-webdriver_http.html +1 -1
  49. package/docs/module_selenium-webdriver_http_class_HttpClient.html +1 -1
  50. package/docs/module_selenium-webdriver_http_util.html +1 -1
  51. package/docs/module_selenium-webdriver_io.html +1 -1
  52. package/docs/module_selenium-webdriver_io_exec.html +1 -1
  53. package/docs/module_selenium-webdriver_net.html +1 -1
  54. package/docs/module_selenium-webdriver_net_portprober.html +1 -1
  55. package/docs/module_selenium-webdriver_phantomjs.html +1 -1
  56. package/docs/module_selenium-webdriver_phantomjs_class_Driver.html +2 -2
  57. package/docs/module_selenium-webdriver_proxy.html +1 -1
  58. package/docs/module_selenium-webdriver_remote.html +1 -1
  59. package/docs/module_selenium-webdriver_remote_class_DriverService.html +2 -2
  60. package/docs/module_selenium-webdriver_remote_class_SeleniumServer.html +2 -2
  61. package/docs/module_selenium-webdriver_testing.html +16 -29
  62. package/docs/module_selenium-webdriver_testing_assert.html +1 -1
  63. package/docs/namespace_webdriver_promise.html +18 -20
  64. package/docs/namespace_webdriver_stacktrace.html +15 -15
  65. package/docs/namespace_webdriver_until.html +32 -0
  66. package/docs/source/_base.js.src.html +1 -1
  67. package/docs/source/builder.js.src.html +1 -1
  68. package/docs/source/chrome.js.src.html +1 -1
  69. package/docs/source/error.js.src.html +1 -1
  70. package/docs/source/executors.js.src.html +1 -1
  71. package/docs/source/firefox/binary.js.src.html +1 -1
  72. package/docs/source/firefox/extension.js.src.html +1 -1
  73. package/docs/source/firefox/index.js.src.html +1 -1
  74. package/docs/source/firefox/profile.js.src.html +1 -1
  75. package/docs/source/http/index.js.src.html +1 -1
  76. package/docs/source/http/util.js.src.html +1 -1
  77. package/docs/source/index.js.src.html +1 -1
  78. package/docs/source/io/exec.js.src.html +1 -1
  79. package/docs/source/io/index.js.src.html +1 -1
  80. package/docs/source/lib/atoms/error.js.src.html +1 -1
  81. package/docs/source/lib/atoms/json.js.src.html +1 -1
  82. package/docs/source/lib/atoms/response.js.src.html +1 -1
  83. package/docs/source/lib/atoms/userAgent.js.src.html +1 -1
  84. package/docs/source/lib/goog/array/array.js.src.html +1 -1
  85. package/docs/source/lib/goog/asserts/asserts.js.src.html +1 -1
  86. package/docs/source/lib/goog/async/nexttick.js.src.html +1 -1
  87. package/docs/source/lib/goog/async/run.js.src.html +1 -1
  88. package/docs/source/lib/goog/base.js.src.html +1 -1
  89. package/docs/source/lib/goog/debug/debug.js.src.html +1 -1
  90. package/docs/source/lib/goog/debug/entrypointregistry.js.src.html +1 -1
  91. package/docs/source/lib/goog/debug/error.js.src.html +1 -1
  92. package/docs/source/lib/goog/debug/logbuffer.js.src.html +1 -1
  93. package/docs/source/lib/goog/debug/logger.js.src.html +1 -1
  94. package/docs/source/lib/goog/debug/logrecord.js.src.html +1 -1
  95. package/docs/source/lib/goog/deps.js.src.html +1 -1
  96. package/docs/source/lib/goog/disposable/disposable.js.src.html +1 -1
  97. package/docs/source/lib/goog/disposable/idisposable.js.src.html +1 -1
  98. package/docs/source/lib/goog/dom/browserfeature.js.src.html +1 -1
  99. package/docs/source/lib/goog/dom/dom.js.src.html +1 -1
  100. package/docs/source/lib/goog/dom/nodetype.js.src.html +1 -1
  101. package/docs/source/lib/goog/dom/tagname.js.src.html +1 -1
  102. package/docs/source/lib/goog/dom/vendor.js.src.html +1 -1
  103. package/docs/source/lib/goog/events/browserevent.js.src.html +1 -1
  104. package/docs/source/lib/goog/events/browserfeature.js.src.html +1 -1
  105. package/docs/source/lib/goog/events/event.js.src.html +1 -1
  106. package/docs/source/lib/goog/events/eventid.js.src.html +1 -1
  107. package/docs/source/lib/goog/events/events.js.src.html +1 -1
  108. package/docs/source/lib/goog/events/eventtarget.js.src.html +1 -1
  109. package/docs/source/lib/goog/events/eventtype.js.src.html +1 -1
  110. package/docs/source/lib/goog/events/keycodes.js.src.html +1 -1
  111. package/docs/source/lib/goog/events/listenable.js.src.html +1 -1
  112. package/docs/source/lib/goog/events/listener.js.src.html +1 -1
  113. package/docs/source/lib/goog/events/listenermap.js.src.html +1 -1
  114. package/docs/source/lib/goog/functions/functions.js.src.html +1 -1
  115. package/docs/source/lib/goog/iter/iter.js.src.html +1 -1
  116. package/docs/source/lib/goog/json/json.js.src.html +1 -1
  117. package/docs/source/lib/goog/labs/testing/assertthat.js.src.html +1 -1
  118. package/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html +1 -1
  119. package/docs/source/lib/goog/labs/testing/matcher.js.src.html +1 -1
  120. package/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html +1 -1
  121. package/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html +1 -1
  122. package/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html +1 -1
  123. package/docs/source/lib/goog/labs/useragent/browser.js.src.html +1 -1
  124. package/docs/source/lib/goog/labs/useragent/engine.js.src.html +1 -1
  125. package/docs/source/lib/goog/labs/useragent/util.js.src.html +1 -1
  126. package/docs/source/lib/goog/math/box.js.src.html +1 -1
  127. package/docs/source/lib/goog/math/coordinate.js.src.html +1 -1
  128. package/docs/source/lib/goog/math/math.js.src.html +1 -1
  129. package/docs/source/lib/goog/math/rect.js.src.html +1 -1
  130. package/docs/source/lib/goog/math/size.js.src.html +1 -1
  131. package/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html +1 -1
  132. package/docs/source/lib/goog/net/xhrlike.js.src.html +1 -1
  133. package/docs/source/lib/goog/net/xmlhttp.js.src.html +1 -1
  134. package/docs/source/lib/goog/net/xmlhttpfactory.js.src.html +1 -1
  135. package/docs/source/lib/goog/object/object.js.src.html +1 -1
  136. package/docs/source/lib/goog/reflect/reflect.js.src.html +1 -1
  137. package/docs/source/lib/goog/string/string.js.src.html +1 -1
  138. package/docs/source/lib/goog/structs/collection.js.src.html +1 -1
  139. package/docs/source/lib/goog/structs/map.js.src.html +1 -1
  140. package/docs/source/lib/goog/structs/set.js.src.html +1 -1
  141. package/docs/source/lib/goog/structs/structs.js.src.html +1 -1
  142. package/docs/source/lib/goog/style/style.js.src.html +1 -1
  143. package/docs/source/lib/goog/testing/asserts.js.src.html +1 -1
  144. package/docs/source/lib/goog/testing/asynctestcase.js.src.html +1 -1
  145. package/docs/source/lib/goog/testing/events/events.js.src.html +1 -1
  146. package/docs/source/lib/goog/testing/functionmock.js.src.html +1 -1
  147. package/docs/source/lib/goog/testing/jsunit.js.src.html +1 -1
  148. package/docs/source/lib/goog/testing/loosemock.js.src.html +1 -1
  149. package/docs/source/lib/goog/testing/mock.js.src.html +1 -1
  150. package/docs/source/lib/goog/testing/mockclock.js.src.html +1 -1
  151. package/docs/source/lib/goog/testing/mockcontrol.js.src.html +1 -1
  152. package/docs/source/lib/goog/testing/mockinterface.js.src.html +1 -1
  153. package/docs/source/lib/goog/testing/mockmatchers.js.src.html +1 -1
  154. package/docs/source/lib/goog/testing/objectpropertystring.js.src.html +1 -1
  155. package/docs/source/lib/goog/testing/propertyreplacer.js.src.html +1 -1
  156. package/docs/source/lib/goog/testing/recordfunction.js.src.html +1 -1
  157. package/docs/source/lib/goog/testing/stacktrace.js.src.html +1 -1
  158. package/docs/source/lib/goog/testing/strictmock.js.src.html +1 -1
  159. package/docs/source/lib/goog/testing/testcase.js.src.html +1 -1
  160. package/docs/source/lib/goog/testing/testrunner.js.src.html +1 -1
  161. package/docs/source/lib/goog/testing/watchers.js.src.html +1 -1
  162. package/docs/source/lib/goog/uri/uri.js.src.html +1 -1
  163. package/docs/source/lib/goog/uri/utils.js.src.html +1 -1
  164. package/docs/source/lib/goog/useragent/product.js.src.html +1 -1
  165. package/docs/source/lib/goog/useragent/product_isversion.js.src.html +1 -1
  166. package/docs/source/lib/goog/useragent/useragent.js.src.html +1 -1
  167. package/docs/source/lib/webdriver/abstractbuilder.js.src.html +1 -1
  168. package/docs/source/lib/webdriver/actionsequence.js.src.html +1 -1
  169. package/docs/source/lib/webdriver/builder.js.src.html +1 -1
  170. package/docs/source/lib/webdriver/button.js.src.html +1 -1
  171. package/docs/source/lib/webdriver/capabilities.js.src.html +1 -1
  172. package/docs/source/lib/webdriver/command.js.src.html +1 -1
  173. package/docs/source/lib/webdriver/events.js.src.html +1 -1
  174. package/docs/source/lib/webdriver/firefoxdomexecutor.js.src.html +1 -1
  175. package/docs/source/lib/webdriver/http/corsclient.js.src.html +1 -1
  176. package/docs/source/lib/webdriver/http/http.js.src.html +1 -1
  177. package/docs/source/lib/webdriver/http/xhrclient.js.src.html +1 -1
  178. package/docs/source/lib/webdriver/key.js.src.html +1 -1
  179. package/docs/source/lib/webdriver/locators.js.src.html +1 -1
  180. package/docs/source/lib/webdriver/logging.js.src.html +1 -1
  181. package/docs/source/lib/webdriver/process.js.src.html +1 -1
  182. package/docs/source/lib/webdriver/promise.js.src.html +1 -1
  183. package/docs/source/lib/webdriver/session.js.src.html +1 -1
  184. package/docs/source/lib/webdriver/stacktrace.js.src.html +1 -1
  185. package/docs/source/lib/webdriver/testing/asserts.js.src.html +1 -1
  186. package/docs/source/lib/webdriver/testing/testcase.js.src.html +1 -1
  187. package/docs/source/lib/webdriver/until.js.src.html +1 -0
  188. package/docs/source/lib/webdriver/webdriver.js.src.html +1 -1
  189. package/docs/source/net/index.js.src.html +1 -1
  190. package/docs/source/net/portprober.js.src.html +1 -1
  191. package/docs/source/phantomjs.js.src.html +1 -1
  192. package/docs/source/proxy.js.src.html +1 -1
  193. package/docs/source/remote/index.js.src.html +1 -1
  194. package/docs/source/testing/assert.js.src.html +1 -1
  195. package/docs/source/testing/index.js.src.html +1 -1
  196. package/docs/types.js +1 -1
  197. package/example/google_search.js +9 -17
  198. package/example/google_search_generator.js +6 -7
  199. package/example/google_search_test.js +10 -18
  200. package/example/parallel_flows.js +4 -7
  201. package/index.js +6 -0
  202. package/lib/firefox/webdriver.xpi +0 -0
  203. package/lib/goog/deps.js +7 -5
  204. package/lib/test/index.js +0 -8
  205. package/lib/webdriver/promise.js +263 -251
  206. package/lib/webdriver/stacktrace.js +15 -1
  207. package/lib/webdriver/test/promise_flow_test.js +82 -76
  208. package/lib/webdriver/test/promise_test.js +22 -62
  209. package/lib/webdriver/test/stacktrace_test.js +18 -0
  210. package/lib/webdriver/test/until_test.js +388 -0
  211. package/lib/webdriver/test/webdriver_test.js +41 -0
  212. package/lib/webdriver/until.js +408 -0
  213. package/lib/webdriver/webdriver.js +91 -29
  214. package/package.json +2 -2
  215. package/test/element_finding_test.js +6 -6
  216. package/test/http/util_test.js +4 -5
  217. package/test/page_loading_test.js +7 -7
  218. package/test/stale_element_test.js +2 -10
  219. package/testing/index.js +9 -22
@@ -61,6 +61,24 @@ goog.require('webdriver.stacktrace.Snapshot');
61
61
 
62
62
 
63
63
 
64
+ /**
65
+ * Error used when the computation of a promise is cancelled.
66
+ *
67
+ * @param {string=} opt_msg The cancellation message.
68
+ * @constructor
69
+ * @extends {goog.debug.Error}
70
+ * @final
71
+ */
72
+ webdriver.promise.CancellationError = function(opt_msg) {
73
+ goog.debug.Error.call(this, opt_msg);
74
+
75
+ /** @override */
76
+ this.name = 'CancellationError';
77
+ };
78
+ goog.inherits(webdriver.promise.CancellationError, goog.debug.Error);
79
+
80
+
81
+
64
82
  /**
65
83
  * Thenable is a promise-like object with a {@code then} method which may be
66
84
  * used to schedule callbacks on a promised value.
@@ -75,9 +93,7 @@ webdriver.promise.Thenable = function() {};
75
93
  * Cancels the computation of this promise's value, rejecting the promise in the
76
94
  * process. This method is a no-op if the promise has alreayd been resolved.
77
95
  *
78
- * @param {*=} opt_reason The reason this promise is being cancelled. If not an
79
- * {@code Error}, one will be created using the value's string
80
- * representation.
96
+ * @param {string=} opt_reason The reason this promise is being cancelled.
81
97
  */
82
98
  webdriver.promise.Thenable.prototype.cancel = function(opt_reason) {};
83
99
 
@@ -297,8 +313,6 @@ webdriver.promise.Promise.prototype.thenFinally = function(callback) {
297
313
  * the Deferred's canceller function (if provided). The canceller may return a
298
314
  * truth-y value to override the reason provided for rejection.
299
315
  *
300
- * @param {Function=} opt_canceller Function to call when cancelling the
301
- * computation of this instance's value.
302
316
  * @param {webdriver.promise.ControlFlow=} opt_flow The control flow
303
317
  * this instance was created under. This should only be provided during
304
318
  * unit tests.
@@ -306,7 +320,7 @@ webdriver.promise.Promise.prototype.thenFinally = function(callback) {
306
320
  * @extends {webdriver.promise.Promise.<T>}
307
321
  * @template T
308
322
  */
309
- webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
323
+ webdriver.promise.Deferred = function(opt_flow) {
310
324
  /* NOTE: This class's implementation diverges from the prototypical style
311
325
  * used in the rest of the atoms library. This was done intentionally to
312
326
  * protect the internal Deferred state from consumers, as outlined by
@@ -316,6 +330,12 @@ webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
316
330
 
317
331
  var flow = opt_flow || webdriver.promise.controlFlow();
318
332
 
333
+ /**
334
+ * The deferred this instance is chained from, if any.
335
+ * @private {webdriver.promise.Deferred.<?>}
336
+ */
337
+ this.parent_ = null;
338
+
319
339
  /**
320
340
  * The listeners registered with this Deferred. Each element in the list will
321
341
  * be a 3-tuple of the callback function, errback function, and the
@@ -422,7 +442,8 @@ webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
422
442
  notify(listeners.shift());
423
443
  }
424
444
 
425
- if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) {
445
+ if (!handled && state == webdriver.promise.Deferred.State_.REJECTED &&
446
+ !(value instanceof webdriver.promise.CancellationError)) {
426
447
  flow.pendingRejections_ += 1;
427
448
  pendingRejectionKey = flow.timer.setTimeout(function() {
428
449
  pendingRejectionKey = null;
@@ -457,6 +478,8 @@ webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
457
478
  */
458
479
  var promise = new webdriver.promise.Promise();
459
480
 
481
+ var self = this;
482
+
460
483
  /**
461
484
  * Registers a callback on this Deferred.
462
485
  *
@@ -482,7 +505,9 @@ webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
482
505
  pendingRejectionKey = null;
483
506
  }
484
507
 
485
- var deferred = new webdriver.promise.Deferred(cancel, flow);
508
+ var deferred = new webdriver.promise.Deferred(flow);
509
+ deferred.parent_ = self;
510
+
486
511
  var listener = {
487
512
  callback: opt_callback,
488
513
  errback: opt_errback,
@@ -500,8 +525,6 @@ webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
500
525
  return deferred.promise;
501
526
  }
502
527
 
503
- var self = this;
504
-
505
528
  /**
506
529
  * Resolves this promise with the given value. If the value is itself a
507
530
  * promise and not a reference to this deferred, this instance will wait for
@@ -525,18 +548,17 @@ webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
525
548
  /**
526
549
  * Attempts to cancel the computation of this instance's value. This attempt
527
550
  * will silently fail if this instance has already resolved.
528
- * @param {*=} opt_reason The reason for cancelling this promise.
529
- */
551
+ * @param {string=} opt_reason The reason for cancelling this promise. */
530
552
  function cancel(opt_reason) {
531
553
  if (!isPending()) {
532
554
  return;
533
555
  }
534
556
 
535
- if (opt_canceller) {
536
- opt_reason = opt_canceller(opt_reason) || opt_reason;
557
+ if (self.parent_) {
558
+ self.parent_.cancel(opt_reason);
559
+ } else {
560
+ reject(new webdriver.promise.CancellationError(opt_reason));
537
561
  }
538
-
539
- reject(opt_reason);
540
562
  }
541
563
 
542
564
  this.promise = promise;
@@ -630,24 +652,22 @@ webdriver.promise.isPromise = function(value) {
630
652
  */
631
653
  webdriver.promise.delayed = function(ms) {
632
654
  var timer = webdriver.promise.controlFlow().timer;
633
- var key;
634
- var deferred = new webdriver.promise.Deferred(function() {
655
+ var deferred = new webdriver.promise.Deferred();
656
+ var key = timer.setTimeout(deferred.fulfill, ms);
657
+ return deferred.thenCatch(function(e) {
635
658
  timer.clearTimeout(key);
659
+ throw e;
636
660
  });
637
- key = timer.setTimeout(deferred.fulfill, ms);
638
- return deferred.promise;
639
661
  };
640
662
 
641
663
 
642
664
  /**
643
665
  * Creates a new deferred object.
644
- * @param {Function=} opt_canceller Function to call when cancelling the
645
- * computation of this instance's value.
646
666
  * @return {!webdriver.promise.Deferred.<T>} The new deferred object.
647
667
  * @template T
648
668
  */
649
- webdriver.promise.defer = function(opt_canceller) {
650
- return new webdriver.promise.Deferred(opt_canceller);
669
+ webdriver.promise.defer = function() {
670
+ return new webdriver.promise.Deferred();
651
671
  };
652
672
 
653
673
 
@@ -694,9 +714,7 @@ webdriver.promise.rejected = function(opt_reason) {
694
714
  * result of the provided function's callback.
695
715
  */
696
716
  webdriver.promise.checkedNodeCall = function(fn, var_args) {
697
- var deferred = new webdriver.promise.Deferred(function() {
698
- throw Error('This Deferred may not be cancelled');
699
- });
717
+ var deferred = new webdriver.promise.Deferred();
700
718
  try {
701
719
  var args = goog.array.slice(arguments, 1);
702
720
  args.push(function(error, value) {
@@ -1044,6 +1062,75 @@ webdriver.promise.ControlFlow = function(opt_timer) {
1044
1062
  * @private {!Array.<!webdriver.promise.Task_>}
1045
1063
  */
1046
1064
  this.history_ = [];
1065
+
1066
+ /**
1067
+ * Tracks the active execution frame for this instance. Lazily initialized
1068
+ * when the first task is scheduled.
1069
+ * @private {webdriver.promise.Frame_}
1070
+ */
1071
+ this.activeFrame_ = null;
1072
+
1073
+ /**
1074
+ * A reference to the frame in which new tasks should be scheduled. If
1075
+ * {@code null}, tasks will be scheduled within the active frame. When forcing
1076
+ * a function to run in the context of a new frame, this pointer is used to
1077
+ * ensure tasks are scheduled within the newly created frame, even though it
1078
+ * won't be active yet.
1079
+ * @private {webdriver.promise.Frame_}
1080
+ * @see {#runInNewFrame_}
1081
+ */
1082
+ this.schedulingFrame_ = null;
1083
+
1084
+ /**
1085
+ * Timeout ID set when the flow is about to shutdown without any errors
1086
+ * being detected. Upon shutting down, the flow will emit an
1087
+ * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events
1088
+ * always follow a brief timeout in order to catch latent errors from the last
1089
+ * completed task. If this task had a callback registered, but no errback, and
1090
+ * the task fails, the unhandled failure would not be reported by the promise
1091
+ * system until the next turn of the event loop:
1092
+ *
1093
+ * // Schedule 1 task that fails.
1094
+ * var result = webriver.promise.controlFlow().schedule('example',
1095
+ * function() { return webdriver.promise.rejected('failed'); });
1096
+ * // Set a callback on the result. This delays reporting the unhandled
1097
+ * // failure for 1 turn of the event loop.
1098
+ * result.then(goog.nullFunction);
1099
+ *
1100
+ * @private {?number}
1101
+ */
1102
+ this.shutdownId_ = null;
1103
+
1104
+ /**
1105
+ * Interval ID for this instance's event loop.
1106
+ * @private {?number}
1107
+ */
1108
+ this.eventLoopId_ = null;
1109
+
1110
+ /**
1111
+ * The number of "pending" promise rejections.
1112
+ *
1113
+ * <p>Each time a promise is rejected and is not handled by a listener, it
1114
+ * will schedule a 0-based timeout to check if it is still unrejected in the
1115
+ * next turn of the JS-event loop. This allows listeners to attach to, and
1116
+ * handle, the rejected promise at any point in same turn of the event loop
1117
+ * that the promise was rejected.
1118
+ *
1119
+ * <p>When this flow's own event loop triggers, it will not run if there
1120
+ * are any outstanding promise rejections. This allows unhandled promises to
1121
+ * be reported before a new task is started, ensuring the error is reported
1122
+ * to the current task queue.
1123
+ *
1124
+ * @private {number}
1125
+ */
1126
+ this.pendingRejections_ = 0;
1127
+
1128
+ /**
1129
+ * The number of aborted frames since the last time a task was executed or a
1130
+ * frame completed successfully.
1131
+ * @private {number}
1132
+ */
1133
+ this.numAbortedFrames_ = 0;
1047
1134
  };
1048
1135
  goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter);
1049
1136
 
@@ -1118,81 +1205,6 @@ webdriver.promise.ControlFlow.EventType = {
1118
1205
  webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10;
1119
1206
 
1120
1207
 
1121
- /**
1122
- * Tracks the active execution frame for this instance. Lazily initialized
1123
- * when the first task is scheduled.
1124
- * @private {webdriver.promise.Frame_}
1125
- */
1126
- webdriver.promise.ControlFlow.prototype.activeFrame_ = null;
1127
-
1128
-
1129
- /**
1130
- * A reference to the frame in which new tasks should be scheduled. If
1131
- * {@code null}, tasks will be scheduled within the active frame. When forcing
1132
- * a function to run in the context of a new frame, this pointer is used to
1133
- * ensure tasks are scheduled within the newly created frame, even though it
1134
- * won't be active yet.
1135
- * @private {webdriver.promise.Frame_}
1136
- * @see {#runInNewFrame_}
1137
- */
1138
- webdriver.promise.ControlFlow.prototype.schedulingFrame_ = null;
1139
-
1140
-
1141
- /**
1142
- * Timeout ID set when the flow is about to shutdown without any errors
1143
- * being detected. Upon shutting down, the flow will emit an
1144
- * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events
1145
- * always follow a brief timeout in order to catch latent errors from the last
1146
- * completed task. If this task had a callback registered, but no errback, and
1147
- * the task fails, the unhandled failure would not be reported by the promise
1148
- * system until the next turn of the event loop:
1149
- *
1150
- * // Schedule 1 task that fails.
1151
- * var result = webriver.promise.controlFlow().schedule('example',
1152
- * function() { return webdriver.promise.rejected('failed'); });
1153
- * // Set a callback on the result. This delays reporting the unhandled
1154
- * // failure for 1 turn of the event loop.
1155
- * result.then(goog.nullFunction);
1156
- *
1157
- * @private {?number}
1158
- */
1159
- webdriver.promise.ControlFlow.prototype.shutdownId_ = null;
1160
-
1161
-
1162
- /**
1163
- * Interval ID for this instance's event loop.
1164
- * @private {?number}
1165
- */
1166
- webdriver.promise.ControlFlow.prototype.eventLoopId_ = null;
1167
-
1168
-
1169
- /**
1170
- * The number of "pending" promise rejections.
1171
- *
1172
- * <p>Each time a promise is rejected and is not handled by a listener, it will
1173
- * schedule a 0-based timeout to check if it is still unrejected in the next
1174
- * turn of the JS-event loop. This allows listeners to attach to, and handle,
1175
- * the rejected promise at any point in same turn of the event loop that the
1176
- * promise was rejected.
1177
- *
1178
- * <p>When this flow's own event loop triggers, it will not run if there
1179
- * are any outstanding promise rejections. This allows unhandled promises to
1180
- * be reported before a new task is started, ensuring the error is reported to
1181
- * the current task queue.
1182
- *
1183
- * @private {number}
1184
- */
1185
- webdriver.promise.ControlFlow.prototype.pendingRejections_ = 0;
1186
-
1187
-
1188
- /**
1189
- * The number of aborted frames since the last time a task was executed or a
1190
- * frame completed successfully.
1191
- * @private {number}
1192
- */
1193
- webdriver.promise.ControlFlow.prototype.numAbortedFrames_ = 0;
1194
-
1195
-
1196
1208
  /**
1197
1209
  * Resets this instance, clearing its queue and removing all event listeners.
1198
1210
  */
@@ -1376,14 +1388,15 @@ webdriver.promise.ControlFlow.prototype.timeout = function(
1376
1388
  * <p>If the condition function throws, or returns a rejected promise, the
1377
1389
  * wait task will fail.
1378
1390
  *
1379
- * @param {!Function} condition The condition function to poll.
1391
+ * @param {function(): T} condition The condition function to poll.
1380
1392
  * @param {number} timeout How long to wait, in milliseconds, for the condition
1381
1393
  * to hold before timing out.
1382
1394
  * @param {string=} opt_message An optional error message to include if the
1383
1395
  * wait times out; defaults to the empty string.
1384
- * @return {!webdriver.promise.Promise} A promise that will be resolved when the
1385
- * condition has been satisified. The promise shall be rejected if the wait
1386
- * times out waiting for the condition.
1396
+ * @return {!webdriver.promise.Promise.<T>} A promise that will be fulfilled
1397
+ * when the condition has been satisified. The promise shall be rejected if
1398
+ * the wait times out waiting for the condition.
1399
+ * @template T
1387
1400
  */
1388
1401
  webdriver.promise.ControlFlow.prototype.wait = function(
1389
1402
  condition, timeout, opt_message) {
@@ -1524,10 +1537,11 @@ webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() {
1524
1537
  * @private
1525
1538
  */
1526
1539
  webdriver.promise.ControlFlow.prototype.getNextTask_ = function() {
1527
- var firstChild = this.activeFrame_.getFirstChild();
1540
+ var frame = this.activeFrame_;
1541
+ var firstChild = frame.getFirstChild();
1528
1542
  if (!firstChild) {
1529
- if (!this.activeFrame_.isWaiting) {
1530
- this.resolveFrame_(this.activeFrame_);
1543
+ if (!frame.isWaiting) {
1544
+ this.resolveFrame_(frame);
1531
1545
  }
1532
1546
  return null;
1533
1547
  }
@@ -1537,7 +1551,7 @@ webdriver.promise.ControlFlow.prototype.getNextTask_ = function() {
1537
1551
  return this.getNextTask_();
1538
1552
  }
1539
1553
 
1540
- firstChild.getParent().removeChild(firstChild);
1554
+ frame.removeChild(firstChild);
1541
1555
  return firstChild;
1542
1556
  };
1543
1557
 
@@ -1558,7 +1572,7 @@ webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) {
1558
1572
  frame.getParent().removeChild(frame);
1559
1573
  }
1560
1574
  this.trimHistory_();
1561
- frame.fulfill();
1575
+ frame.close();
1562
1576
 
1563
1577
  if (!this.activeFrame_) {
1564
1578
  this.commenceShutdown_();
@@ -1596,7 +1610,7 @@ webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) {
1596
1610
 
1597
1611
  var frame = this.activeFrame_;
1598
1612
  this.activeFrame_ = parent;
1599
- frame.reject(error);
1613
+ frame.abort(error);
1600
1614
  };
1601
1615
 
1602
1616
 
@@ -1642,7 +1656,7 @@ webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(
1642
1656
  webdriver.promise.popFlow_();
1643
1657
  this.schedulingFrame_ = null;
1644
1658
  }
1645
- newFrame.lockFrame();
1659
+ newFrame.isLocked_ = true;
1646
1660
 
1647
1661
  // If there was nothing scheduled in the new frame we can discard the
1648
1662
  // frame and return immediately.
@@ -1652,24 +1666,25 @@ webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(
1652
1666
  return;
1653
1667
  }
1654
1668
 
1655
- newFrame.then(function() {
1669
+ newFrame.onComplete = function() {
1656
1670
  webdriver.promise.asap(result, callback, errback);
1657
- }, function(e) {
1671
+ };
1672
+
1673
+ newFrame.onAbort = function(e) {
1658
1674
  if (webdriver.promise.Thenable.isImplementation(result) &&
1659
1675
  result.isPending()) {
1660
1676
  result.cancel(e);
1661
1677
  e = result;
1662
1678
  }
1663
1679
  errback(e);
1664
- });
1680
+ };
1665
1681
  } catch (ex) {
1666
- removeNewFrame(new webdriver.promise.CanceledTaskError_(ex));
1682
+ removeNewFrame(ex);
1667
1683
  errback(ex);
1668
1684
  }
1669
1685
 
1670
1686
  /**
1671
- * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the
1672
- * error that triggered the removal of this frame.
1687
+ * @param {*=} opt_err If provided, the reason that the frame was removed.
1673
1688
  */
1674
1689
  function removeNewFrame(opt_err) {
1675
1690
  var parent = newFrame.getParent();
@@ -1678,7 +1693,8 @@ webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(
1678
1693
  }
1679
1694
 
1680
1695
  if (opt_err) {
1681
- newFrame.cancelRemainingTasks(opt_err);
1696
+ newFrame.cancelRemainingTasks(
1697
+ 'Tasks cancelled due to uncaught error: ' + opt_err);
1682
1698
  }
1683
1699
  self.activeFrame_ = oldFrame;
1684
1700
  }
@@ -1753,44 +1769,98 @@ webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) {
1753
1769
 
1754
1770
 
1755
1771
  /**
1756
- * A single node in an {@link webdriver.promise.ControlFlow}'s task tree.
1772
+ * An execution frame within a {@link webdriver.promise.ControlFlow}. Each
1773
+ * frame represents the execution context for either a
1774
+ * {@link webdriver.promise.Task_} or a callback on a
1775
+ * {@link webdriver.promise.Deferred}.
1776
+ *
1777
+ * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the
1778
+ * items queued within it are given priority over child N+1.
1779
+ *
1757
1780
  * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs
1758
1781
  * to.
1759
1782
  * @constructor
1760
- * @extends {webdriver.promise.Deferred}
1761
1783
  * @private
1784
+ * @final
1785
+ * @struct
1762
1786
  */
1763
- webdriver.promise.Node_ = function(flow) {
1764
- webdriver.promise.Deferred.call(this, null, flow);
1765
- };
1766
- goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred);
1787
+ webdriver.promise.Frame_ = function(flow) {
1788
+ /** @private {!webdriver.promise.ControlFlow} */
1789
+ this.flow_ = flow;
1767
1790
 
1791
+ /** @private {webdriver.promise.Frame_} */
1792
+ this.parent_ = null;
1768
1793
 
1769
- /**
1770
- * This node's parent.
1771
- * @private {webdriver.promise.Node_}
1772
- */
1773
- webdriver.promise.Node_.prototype.parent_ = null;
1794
+ /**
1795
+ * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>}
1796
+ */
1797
+ this.children_ = [];
1798
+
1799
+
1800
+ /** @private {(webdriver.promise.Frame_|webdriver.promise.Task_)} */
1801
+ this.lastInsertedChild_ = null;
1802
+
1803
+ /**
1804
+ * The task currently being executed within this frame.
1805
+ * @private {webdriver.promise.Task_}
1806
+ */
1807
+ this.pendingTask_ = null;
1808
+
1809
+ /**
1810
+ * Whether this frame is currently locked. A locked frame represents an
1811
+ * executed function that has scheduled all of its tasks.
1812
+ *
1813
+ * <p>Once a frame becomes locked, any new frames which are added as children
1814
+ * represent interrupts (such as a {@link webdriver.promise.Promise}
1815
+ * callback) whose tasks must be given priority over those already scheduled
1816
+ * within this frame. For example:
1817
+ * <code><pre>
1818
+ * var flow = webdriver.promise.controlFlow();
1819
+ * flow.execute('start here', goog.nullFunction).then(function() {
1820
+ * flow.execute('this should execute 2nd', goog.nullFunction);
1821
+ * });
1822
+ * flow.execute('this should execute last', goog.nullFunction);
1823
+ * </pre></code>
1824
+ *
1825
+ * @private {boolean}
1826
+ */
1827
+ this.isLocked_ = false;
1828
+
1829
+ /** @type {boolean} */
1830
+ this.isWaiting = false;
1831
+
1832
+ /**
1833
+ * The function to notify if this frame executes without error.
1834
+ * @type {?function()}
1835
+ */
1836
+ this.onComplete = null;
1837
+
1838
+ /**
1839
+ * The function to notify if this frame is aborted with an error.
1840
+ * @type {?function(*)}
1841
+ */
1842
+ this.onAbort = null;
1843
+ };
1774
1844
 
1775
1845
 
1776
- /** @return {webdriver.promise.Node_} This node's parent. */
1777
- webdriver.promise.Node_.prototype.getParent = function() {
1846
+ /** @return {webdriver.promise.Frame_} This frame's parent, if any. */
1847
+ webdriver.promise.Frame_.prototype.getParent = function() {
1778
1848
  return this.parent_;
1779
1849
  };
1780
1850
 
1781
1851
 
1782
1852
  /**
1783
- * @param {webdriver.promise.Node_} parent This node's new parent.
1853
+ * @param {webdriver.promise.Frame_} parent This frame's new parent.
1784
1854
  */
1785
- webdriver.promise.Node_.prototype.setParent = function(parent) {
1855
+ webdriver.promise.Frame_.prototype.setParent = function(parent) {
1786
1856
  this.parent_ = parent;
1787
1857
  };
1788
1858
 
1789
1859
 
1790
1860
  /**
1791
- * @return {!webdriver.promise.Node_} The root of this node's tree.
1861
+ * @return {!webdriver.promise.Frame_} The root of this frame's tree.
1792
1862
  */
1793
- webdriver.promise.Node_.prototype.getRoot = function() {
1863
+ webdriver.promise.Frame_.prototype.getRoot = function() {
1794
1864
  var root = this;
1795
1865
  while (root.parent_) {
1796
1866
  root = root.parent_;
@@ -1799,88 +1869,51 @@ webdriver.promise.Node_.prototype.getRoot = function() {
1799
1869
  };
1800
1870
 
1801
1871
 
1802
-
1803
1872
  /**
1804
- * An execution frame within a {@link webdriver.promise.ControlFlow}. Each
1805
- * frame represents the execution context for either a
1806
- * {@link webdriver.promise.Task_} or a callback on a
1807
- * {@link webdriver.promise.Deferred}.
1873
+ * Aborts the execution of this frame, cancelling all outstanding tasks
1874
+ * scheduled within this frame.
1808
1875
  *
1809
- * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the
1810
- * items queued within it are given priority over child N+1.
1811
- *
1812
- * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs
1813
- * to.
1814
- * @constructor
1815
- * @extends {webdriver.promise.Node_}
1816
- * @private
1817
- */
1818
- webdriver.promise.Frame_ = function(flow) {
1819
- webdriver.promise.Node_.call(this, flow);
1820
-
1821
- var reject = goog.bind(this.reject, this);
1822
- var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this);
1823
-
1824
- /** @override */
1825
- this.reject = function(e) {
1826
- cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e));
1827
- reject(e);
1828
- };
1829
-
1830
- /**
1831
- * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>}
1832
- */
1833
- this.children_ = [];
1876
+ * @param {*} error The error that triggered this abortion.
1877
+ */
1878
+ webdriver.promise.Frame_.prototype.abort = function(error) {
1879
+ this.cancelRemainingTasks(
1880
+ 'Task discarded due to a previous task failure: ' + error);
1881
+
1882
+ var frame = this;
1883
+ frame.flow_.pendingRejections_ += 1;
1884
+ this.flow_.timer.setTimeout(function() {
1885
+ frame.flow_.pendingRejections_ -= 1;
1886
+ if (frame.onAbort) {
1887
+ frame.notify_(frame.onAbort, error);
1888
+ } else {
1889
+ frame.flow_.abortFrame_(error);
1890
+ }
1891
+ }, 0);
1834
1892
  };
1835
- goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_);
1836
1893
 
1837
1894
 
1838
1895
  /**
1839
- * The task currently being executed within this frame.
1840
- * @private {webdriver.promise.Task_}
1896
+ * Signals that this frame has successfully finished executing.
1841
1897
  */
1842
- webdriver.promise.Frame_.prototype.pendingTask_ = null;
1843
-
1844
-
1845
- /**
1846
- * Whether this frame is active. A frame is considered active once one of its
1847
- * descendants has been removed for execution.
1848
- *
1849
- * Adding a sub-frame as a child to an active frame is an indication that
1850
- * a callback to a {@link webdriver.promise.Deferred} is being invoked and any
1851
- * tasks scheduled within it should have priority over previously scheduled
1852
- * tasks:
1853
- * <code><pre>
1854
- * var flow = webdriver.promise.controlFlow();
1855
- * flow.execute('start here', goog.nullFunction).then(function() {
1856
- * flow.execute('this should execute 2nd', goog.nullFunction);
1857
- * });
1858
- * flow.execute('this should execute last', goog.nullFunction);
1859
- * </pre></code>
1860
- *
1861
- * @private {boolean}
1862
- */
1863
- webdriver.promise.Frame_.prototype.isActive_ = false;
1864
-
1865
-
1866
- /**
1867
- * Whether this frame is currently locked. A locked frame represents a callback
1868
- * or task function which has run to completion and scheduled all of its tasks.
1869
- *
1870
- * <p>Once a frame becomes {@link #isActive_ active}, any new frames which are
1871
- * added represent callbacks on a {@link webdriver.promise.Deferred}, whose
1872
- * tasks must be given priority over previously scheduled tasks.
1873
- *
1874
- * @private {boolean}
1875
- */
1876
- webdriver.promise.Frame_.prototype.isLocked_ = false;
1898
+ webdriver.promise.Frame_.prototype.close = function() {
1899
+ var frame = this;
1900
+ this.flow_.timer.setTimeout(function() {
1901
+ frame.notify_(frame.onComplete);
1902
+ }, 0);
1903
+ };
1877
1904
 
1878
1905
 
1879
1906
  /**
1880
- * A reference to the last node inserted in this frame.
1881
- * @private {webdriver.promise.Node_}
1907
+ * @param {?(function(*)|function())} fn The function to notify.
1908
+ * @param {*=} opt_error Value to pass to the notified function, if any.
1909
+ * @private
1882
1910
  */
1883
- webdriver.promise.Frame_.prototype.lastInsertedChild_ = null;
1911
+ webdriver.promise.Frame_.prototype.notify_ = function(fn, opt_error) {
1912
+ this.onAbort = this.onComplete = null;
1913
+ if (fn) {
1914
+ fn(opt_error);
1915
+ }
1916
+ };
1884
1917
 
1885
1918
 
1886
1919
  /**
@@ -1901,23 +1934,22 @@ webdriver.promise.Frame_.prototype.lastInsertedChild_ = null;
1901
1934
  * });
1902
1935
  * });
1903
1936
  * // flow failed: Error: boom
1904
- * // task failed! CanceledTaskError: Task discarded due to a previous
1937
+ * // task failed! CancelledTaskError: Task discarded due to a previous
1905
1938
  * // task failure: Error: boom
1906
1939
  *
1907
- * @param {!webdriver.promise.CanceledTaskError_} error The cancellation
1908
- * error.
1940
+ * @param {string} reason The cancellation reason.
1909
1941
  */
1910
- webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(error) {
1942
+ webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(reason) {
1911
1943
  goog.array.forEach(this.children_, function(child) {
1912
1944
  if (child instanceof webdriver.promise.Frame_) {
1913
- child.cancelRemainingTasks(error);
1945
+ child.cancelRemainingTasks(reason);
1914
1946
  } else {
1915
1947
  // None of the previously registered listeners should be notified that
1916
1948
  // the task is being canceled, however, we need at least one errback
1917
1949
  // to prevent the cancellation from bubbling up.
1918
1950
  child.removeAll();
1919
1951
  child.thenCatch(goog.nullFunction);
1920
- child.cancel(error);
1952
+ child.cancel(reason);
1921
1953
  }
1922
1954
  });
1923
1955
  };
@@ -1941,12 +1973,6 @@ webdriver.promise.Frame_.prototype.setPendingTask = function(task) {
1941
1973
  };
1942
1974
 
1943
1975
 
1944
- /** Locks this frame. */
1945
- webdriver.promise.Frame_.prototype.lockFrame = function() {
1946
- this.isLocked_ = true;
1947
- };
1948
-
1949
-
1950
1976
  /**
1951
1977
  * Adds a new node to this frame.
1952
1978
  * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node
@@ -1960,9 +1986,11 @@ webdriver.promise.Frame_.prototype.addChild = function(node) {
1960
1986
  return;
1961
1987
  }
1962
1988
 
1963
- node.setParent(this);
1989
+ if (node instanceof webdriver.promise.Frame_) {
1990
+ node.setParent(this);
1991
+ }
1964
1992
 
1965
- if (this.isActive_ && node instanceof webdriver.promise.Frame_) {
1993
+ if (this.isLocked_ && node instanceof webdriver.promise.Frame_) {
1966
1994
  var index = 0;
1967
1995
  if (this.lastInsertedChild_ instanceof
1968
1996
  webdriver.promise.Frame_) {
@@ -1983,7 +2011,7 @@ webdriver.promise.Frame_.prototype.addChild = function(node) {
1983
2011
  * fist child.
1984
2012
  */
1985
2013
  webdriver.promise.Frame_.prototype.getFirstChild = function() {
1986
- this.isActive_ = true;
2014
+ this.isLocked_ = true;
1987
2015
  this.lastInsertedChild_ = null;
1988
2016
  return this.children_[0];
1989
2017
  };
@@ -1996,7 +2024,9 @@ webdriver.promise.Frame_.prototype.getFirstChild = function() {
1996
2024
  */
1997
2025
  webdriver.promise.Frame_.prototype.removeChild = function(child) {
1998
2026
  var index = goog.array.indexOf(this.children_, child);
1999
- child.setParent(null);
2027
+ if (child instanceof webdriver.promise.Frame_) {
2028
+ child.setParent(null);
2029
+ }
2000
2030
  goog.array.removeAt(this.children_, index);
2001
2031
  if (this.lastInsertedChild_ === child) {
2002
2032
  this.lastInsertedChild_ = null;
@@ -2018,22 +2048,23 @@ webdriver.promise.Frame_.prototype.toString = function() {
2018
2048
  *
2019
2049
  * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs
2020
2050
  * to.
2021
- * @param {!Function} fn The function to call when the task executes. If it
2022
- * returns a {@code webdriver.promise.Promise}, the flow will wait
2023
- * for it to be resolved before starting the next task.
2051
+ * @param {function(): (T|!webdriver.promise.Promise.<T>)} fn The function to
2052
+ * call when the task executes. If it returns a
2053
+ * {@link webdriver.promise.Promise}, the flow will wait for it to be
2054
+ * resolved before starting the next task.
2024
2055
  * @param {string} description A description of the task for debugging.
2025
2056
  * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack
2026
2057
  * when this task was scheduled.
2027
2058
  * @constructor
2028
- * @extends {webdriver.promise.Node_}
2059
+ * @extends {webdriver.promise.Deferred.<T>}
2060
+ * @template T
2029
2061
  * @private
2030
2062
  */
2031
2063
  webdriver.promise.Task_ = function(flow, fn, description, snapshot) {
2032
- webdriver.promise.Node_.call(this, flow);
2064
+ webdriver.promise.Deferred.call(this, flow);
2033
2065
 
2034
2066
  /**
2035
- * Executes this task.
2036
- * @type {!Function}
2067
+ * @type {function(): (T|!webdriver.promise.Promise.<T>)}
2037
2068
  */
2038
2069
  this.execute = fn;
2039
2070
 
@@ -2043,7 +2074,7 @@ webdriver.promise.Task_ = function(flow, fn, description, snapshot) {
2043
2074
  /** @private {!webdriver.stacktrace.Snapshot} */
2044
2075
  this.snapshot_ = snapshot;
2045
2076
  };
2046
- goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_);
2077
+ goog.inherits(webdriver.promise.Task_, webdriver.promise.Deferred);
2047
2078
 
2048
2079
 
2049
2080
  /** @return {string} This task's description. */
@@ -2067,25 +2098,6 @@ webdriver.promise.Task_.prototype.toString = function() {
2067
2098
 
2068
2099
 
2069
2100
 
2070
- /**
2071
- * Special error used to signal when a task is canceled because a previous
2072
- * task in the same frame failed.
2073
- * @param {*} err The error that caused the task cancellation.
2074
- * @constructor
2075
- * @extends {goog.debug.Error}
2076
- * @private
2077
- */
2078
- webdriver.promise.CanceledTaskError_ = function(err) {
2079
- goog.base(this, 'Task discarded due to a previous task failure: ' + err);
2080
- };
2081
- goog.inherits(webdriver.promise.CanceledTaskError_, goog.debug.Error);
2082
-
2083
-
2084
- /** @override */
2085
- webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError';
2086
-
2087
-
2088
-
2089
2101
  /**
2090
2102
  * The default flow to use if no others are active.
2091
2103
  * @private {!webdriver.promise.ControlFlow}