From e611d3c3756b35a0c9afe596d5e1a1dc2429c2de Mon Sep 17 00:00:00 2001
From: Andreas Tolfsen <ato@sny.no>
Date: Tue, 1 Aug 2017 18:28:13 +0100
Subject: [PATCH] Bug 1381876 - Introduce TimedPromise. r=automatedtester

This introduces a specialisation of the well-known Promise that can
time out after a set limit, causing the promises' "reject" callback to
be invoked.

The TimedPromise object represents the timed, eventual completion
(or failure) of an asynchronous operation, and its resulting value.
In contrast to a regular Promise, it times out after a set timeframe.

MozReview-Commit-ID: Rb3POsPYeT

--HG--
extra : rebase_source : cdff792dfa43af3cff1b20b3e81edb1a826e561e
---
 testing/marionette/wait.js | 68 +++++++++++++++++++++++++++++++++++---
 1 file changed, 64 insertions(+), 4 deletions(-)

diff --git a/testing/marionette/wait.js b/testing/marionette/wait.js
index 65e2980342a41..5103ce59b2968 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;
+  });
+}
-- 
GitLab