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
--- 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();