Bug 1371895 - Support legacy Task.jsm generators in DeferredTask.jsm. r=florian draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Mon, 12 Jun 2017 12:18:55 +0100
changeset 592527 f5a9b38a34e382b666a7e072faa19d0f8d0c86ad
parent 591677 1742b1bdadd13a02df95ca690bea9cc42ff40c91
child 632854 6e45fa46bf5344d8d1203c1cd440944f46ef0055
push id63425
push userpaolo.mozmail@amadzone.org
push dateMon, 12 Jun 2017 11:19:25 +0000
reviewersflorian
bugs1371895
milestone55.0a1
Bug 1371895 - Support legacy Task.jsm generators in DeferredTask.jsm. r=florian This ensures legacy add-on compatibility by loading Task.jsm lazily when required. Once Task.jsm is removed, this code can be changed back to support only async functions. MozReview-Commit-ID: 15nY8yArNlZ
toolkit/modules/DeferredTask.jsm
toolkit/modules/tests/xpcshell/test_DeferredTask.js
--- a/toolkit/modules/DeferredTask.jsm
+++ b/toolkit/modules/DeferredTask.jsm
@@ -85,16 +85,18 @@ this.EXPORTED_SYMBOLS = [
 // Globals
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
 
 const Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer",
                                      "initWithCallback");
 
 // DeferredTask
 
 /**
  * Sets up a task whose execution can be triggered after a delay.
@@ -267,30 +269,46 @@ this.DeferredTask.prototype = {
     // means that the assignment of "this._runningPromise" must complete before
     // the task gets a chance to start.
     this._timer = null;
     this._armed = false;
     this._runningPromise = runningDeferred.promise;
 
     runningDeferred.resolve((async () => {
       // Execute the provided function asynchronously.
-      await (this._taskFn() || Promise.resolve()).then(null, Cu.reportError);
+      await this._runTask();
 
       // Now that the task has finished, we check the state of the object to
       // determine if we should restart the task again.
       if (this._armed) {
         if (!this._finalized) {
           this._startTimer();
         } else {
           // Execute the task again immediately, for the last time.  The isArmed
           // property should return false while the task is running, and should
           // remain false after the last execution terminates.
           this._armed = false;
-          await (this._taskFn() || Promise.resolve()).then(null, Cu.reportError);
+          await this._runTask();
         }
       }
 
       // Indicate that the execution of the task has finished.  This happens
       // synchronously with the previous state changes in the function.
       this._runningPromise = null;
     })().then(null, Cu.reportError));
   },
+
+  /**
+   * Executes the associated task and catches exceptions.
+   */
+  async _runTask() {
+    try {
+      let result = this._taskFn();
+      if (Object.prototype.toString.call(result) == "[object Generator]") {
+        await Task.spawn(result); // eslint-disable-line mozilla/no-task
+      } else {
+        await result;
+      }
+    } catch (ex) {
+      Cu.reportError(ex);
+    }
+  },
 };
--- a/toolkit/modules/tests/xpcshell/test_DeferredTask.js
+++ b/toolkit/modules/tests/xpcshell/test_DeferredTask.js
@@ -181,16 +181,43 @@ add_test(function test_arm_async() {
   do_timeout(13 * T, function() {
     do_check_false(deferredTask.isRunning);
     do_check_true(finishedExecutionAgain);
     run_next_test();
   });
 });
 
 /**
+ * Checks that "arm" accepts a Task.jsm generator function.
+ */
+add_test(function test_arm_async_generator() {
+  let deferredTask = new DeferredTask(function*() {
+    yield Promise.resolve();
+    run_next_test();
+  }, 50);
+
+  deferredTask.arm();
+});
+
+/**
+ * Checks that "arm" accepts a Task.jsm legacy generator function.
+ */
+add_test(function test_arm_async_legacy_generator() {
+  // ESLint cannot parse legacy generator functions, so we need an eval block.
+  /* eslint-disable no-eval */
+  let deferredTask = new DeferredTask(eval(`function() {
+    yield Promise.resolve();
+    run_next_test();
+  }`), 50);
+  /* eslint-enable no-eval */
+
+  deferredTask.arm();
+});
+
+/**
  * Checks that an armed task can be disarmed.
  */
 add_test(function test_disarm() {
   // Create a task that will run later.
   let deferredTask = new DeferredTask(function() {
     do_throw("This task should not run.");
   }, 2 * T);
   deferredTask.arm();