diff --git a/testing/marionette/wait.js b/testing/marionette/wait.js index 65e2980342a4105e7f7c4994dd6c06235ef15402..5103ce59b2968202aac1e9b8723763ac803fe125 100644 --- a/testing/marionette/wait.js +++ b/testing/marionette/wait.js @@ -6,9 +6,12 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; -Cu.import("chrome://marionette/content/error.js"); +const { + error, + TimeoutError, +} = Cu.import("chrome://marionette/content/error.js", {}); -this.EXPORTED_SYMBOLS = ["wait"]; +this.EXPORTED_SYMBOLS = ["wait", "TimedPromise"]; /** * Poll-waiting utilities. @@ -17,6 +20,8 @@ this.EXPORTED_SYMBOLS = ["wait"]; */ this.wait = {}; +const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer; + /** * @callback Condition * @@ -104,9 +109,8 @@ wait.until = function(func, timeout = 2000, interval = 10) { // before invoking |evalFn| evalFn(); - timer.init(evalFn, interval, Ci.nsITimer.TYPE_REPEATING_SLACK); + timer.init(evalFn, interval, TYPE_REPEATING_SLACK); - // cancel timer and propagate result }).then(res => { timer.cancel(); return res; @@ -115,3 +119,59 @@ wait.until = function(func, timeout = 2000, interval = 10) { throw err; }); }; + +/** + * The <code>TimedPromise</code> object represents the timed, eventual + * completion (or failure) of an asynchronous operation, and its + * resulting value. + * + * In contrast to a regular {@link Promise}, it times out after + * <var>timeout</var>. + * + * @param {Condition} func + * Function to run, which will have its <code>reject</code> + * callback invoked after the <var>timeout</var> duration is reached. + * It is given two callbacks: <code>resolve(value)</code> and + * <code>reject(error)</code>. + * @param {timeout=} [timeout=1500] timeout + * <var>condition</var>'s <code>reject</code> callback will be called + * after this timeout. + * @param {Error=} [throws=TimeoutError] throws + * When the <var>timeout</var> is hit, this error class will be + * thrown. If it is null, no error is thrown and the promise is + * instead resolved on timeout. + * + * @return {Promise.<*>} + * Timed promise. + */ +function TimedPromise(fn, {timeout = 1500, throws = TimeoutError} = {}) { + const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + return new Promise((resolve, reject) => { + // Reject only if |throws| is given. Otherwise it is assumed that + // the user is OK with the promise timing out. + let bail = res => { + if (throws !== null) { + let err = new throws(); + reject(err); + } else { + resolve(); + } + }; + + timer.initWithCallback({notify: bail}, timeout, TYPE_ONE_SHOT); + + try { + fn(resolve, reject); + } catch (e) { + reject(e); + } + + }).then(res => { + timer.cancel(); + return res; + }, err => { + timer.cancel(); + throw err; + }); +}