@@ -53,6 +53,7 @@ goog.module('webdriver.promise');
53
53
goog . module . declareLegacyNamespace ( ) ;
54
54
55
55
var Arrays = goog . require ( 'goog.array' ) ;
56
+ var asserts = goog . require ( 'goog.asserts' ) ;
56
57
var asyncRun = goog . require ( 'goog.async.run' ) ;
57
58
var throwException = goog . require ( 'goog.async.throwException' ) ;
58
59
var DebugError = goog . require ( 'goog.debug.Error' ) ;
@@ -515,7 +516,7 @@ promise.Promise.prototype.scheduleNotifications_ = function() {
515
516
}
516
517
517
518
if ( this . callbacks_ && this . callbacks_ . length ) {
518
- activeFrame = this . flow_ . getSchedulingFrame_ ( ) ;
519
+ activeFrame = this . flow_ . getRunningFrame_ ( ) ;
519
520
var self = this ;
520
521
this . callbacks_ . forEach ( function ( callback ) {
521
522
if ( ! callback . frame_ . getParent ( ) ) {
@@ -1238,6 +1239,17 @@ promise.ControlFlow = function() {
1238
1239
*/
1239
1240
this . activeFrame_ = null ;
1240
1241
1242
+ /**
1243
+ * A reference to the frame which is currently top of the stack in
1244
+ * {@link #runInFrame_}. The {@link #activeFrame_} will always be an ancestor
1245
+ * of the {@link #runningFrame_}, but the two will often not be the same. The
1246
+ * active frame represents which frame is currently executing a task while the
1247
+ * running frame represents either the task itself or a promise callback which
1248
+ * has fired asynchronously.
1249
+ * @private {Frame}
1250
+ */
1251
+ this . runningFrame_ = null ;
1252
+
1241
1253
/**
1242
1254
* A reference to the frame in which new tasks should be scheduled. If
1243
1255
* {@code null}, tasks will be scheduled within the active frame. When forcing
@@ -1368,6 +1380,7 @@ promise.ControlFlow.prototype.reset = function() {
1368
1380
promise . ControlFlow . prototype . getSchedule = function ( opt_includeStackTraces ) {
1369
1381
var ret = 'ControlFlow::' + goog . getUid ( this ) ;
1370
1382
var activeFrame = this . activeFrame_ ;
1383
+ var runningFrame = this . runningFrame_ ;
1371
1384
if ( ! activeFrame ) {
1372
1385
return ret ;
1373
1386
}
@@ -1388,6 +1401,9 @@ promise.ControlFlow.prototype.getSchedule = function(opt_includeStackTraces) {
1388
1401
if ( node === activeFrame ) {
1389
1402
ret = '(active) ' + ret ;
1390
1403
}
1404
+ if ( node === runningFrame ) {
1405
+ ret = '(running) ' + ret ;
1406
+ }
1391
1407
if ( node instanceof Frame ) {
1392
1408
if ( node . getPendingTask ( ) ) {
1393
1409
ret += '\n' + toStringHelper (
@@ -1445,6 +1461,15 @@ promise.ControlFlow.prototype.getSchedulingFrame_ = function() {
1445
1461
} ;
1446
1462
1447
1463
1464
+ /**
1465
+ * @return {!Frame } The frame that is current executing.
1466
+ * @private
1467
+ */
1468
+ promise . ControlFlow . prototype . getRunningFrame_ = function ( ) {
1469
+ return this . runningFrame_ || this . getActiveFrame_ ( ) ;
1470
+ } ;
1471
+
1472
+
1448
1473
/**
1449
1474
* Schedules a task for execution. If there is nothing currently in the
1450
1475
* queue, the task will be executed in the next turn of the event loop. If
@@ -1790,22 +1815,27 @@ promise.ControlFlow.prototype.abortFrame_ = function(error, opt_frame) {
1790
1815
/**
1791
1816
* Executes a function within a specific frame. If the function does not
1792
1817
* schedule any new tasks, the frame will be discarded and the function's result
1793
- * returned immediately. Otherwise, a promise will be returned. This promise
1794
- * will be resolved with the function's result once all of the tasks scheduled
1795
- * within the function have been completed. If the function's frame is aborted,
1796
- * the returned promise will be rejected .
1818
+ * returned passed to the given callback immediately. Otherwise, the callback
1819
+ * will be invoked once all of the tasks scheduled within the function have been
1820
+ * completed. If the frame is aborted, the `errback` will be invoked with the
1821
+ * offending error .
1797
1822
*
1798
1823
* @param {!Frame } newFrame The frame to use.
1799
1824
* @param {!Function } fn The function to execute.
1800
1825
* @param {function(T) } callback The function to call with a successful result.
1801
1826
* @param {function(*) } errback The function to call if there is an error.
1802
1827
* @param {boolean= } opt_isTask Whether the function is a task and the frame
1803
1828
* should be immediately activated to capture subtasks and errors.
1829
+ * @throws {Error } If this function is invoked while another call to this
1830
+ * function is present on the stack.
1804
1831
* @template T
1805
1832
* @private
1806
1833
*/
1807
1834
promise . ControlFlow . prototype . runInFrame_ = function (
1808
1835
newFrame , fn , callback , errback , opt_isTask ) {
1836
+ asserts . assert (
1837
+ ! this . runningFrame_ , 'unexpected recursive call to runInFrame' ) ;
1838
+
1809
1839
var self = this ,
1810
1840
oldFrame = this . activeFrame_ ;
1811
1841
@@ -1821,12 +1851,19 @@ promise.ControlFlow.prototype.runInFrame_ = function(
1821
1851
}
1822
1852
1823
1853
try {
1854
+ this . runningFrame_ = newFrame ;
1824
1855
this . schedulingFrame_ = newFrame ;
1825
1856
activeFlows . push ( this ) ;
1826
1857
var result = fn ( ) ;
1827
1858
} finally {
1828
1859
activeFlows . pop ( ) ;
1829
1860
this . schedulingFrame_ = null ;
1861
+
1862
+ // `newFrame` should only be considered the running frame when it is
1863
+ // actually executing. After it finishes, set top of stack to its parent
1864
+ // so any failures/interrupts that occur while processing newFrame's
1865
+ // result are handled there.
1866
+ this . runningFrame_ = newFrame . parent_ ;
1830
1867
}
1831
1868
newFrame . isLocked_ = true ;
1832
1869
@@ -1878,6 +1915,9 @@ promise.ControlFlow.prototype.runInFrame_ = function(
1878
1915
} catch ( ex ) {
1879
1916
removeNewFrame ( ex ) ;
1880
1917
errback ( ex ) ;
1918
+ } finally {
1919
+ // No longer running anything, clear the reference.
1920
+ this . runningFrame_ = null ;
1881
1921
}
1882
1922
1883
1923
function isCloseable ( frame ) {
0 commit comments