Index: ui/file_manager/file_manager/background/js/task_queue.js |
diff --git a/ui/file_manager/file_manager/background/js/task_queue.js b/ui/file_manager/file_manager/background/js/task_queue.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a65698eadd2e9e53cc6766fafa3682a2c9f03a61 |
--- /dev/null |
+++ b/ui/file_manager/file_manager/background/js/task_queue.js |
@@ -0,0 +1,216 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// Namespace |
+var importer = importer || {}; |
+ |
+/** |
+ * A queue of tasks. Tasks (subclasses of TaskQueue.Task) can be pushed onto |
+ * the queue. The queue becomes active whenever it is not empty, and it will |
+ * begin executing tasks one at a time. The tasks are executed in a separate |
+ * asynchronous context. As each task runs, it can send update notifications |
+ * which are relayed back to clients via callbacks. When the queue runs of of |
+ * tasks, it goes back into an idle state. Clients can set callbacks which will |
+ * be triggered whenever the queue transitions between the active and idle |
+ * states. |
+ * |
+ * @constructor |
+ * @struct |
+ */ |
+importer.TaskQueue = function() { |
+ /** @private {!Array<!importer.TaskQueue.Task>} */ |
+ this.tasks_ = []; |
+ |
+ /** @private {!Array<!function(string, !importer.TaskQueue.Task)>} */ |
+ this.updateCallbacks_ = []; |
+ |
+ /** @private {?function()} */ |
+ this.activeCallback_ = null; |
+ |
+ /** @private {?function()} */ |
+ this.idleCallback_ = null; |
+ |
+ /** @private {boolean} */ |
+ this.active_ = false; |
+}; |
+ |
+/** |
+ * @enum {string} |
+ */ |
+importer.TaskQueue.UpdateType = { |
+ PROGRESS: 'PROGRESS', |
+ SUCCESS: 'SUCCESS', |
+ ERROR: 'ERROR' |
+}; |
+ |
+/** |
+ * @param {!importer.TaskQueue.Task} task |
+ */ |
+importer.TaskQueue.prototype.queueTask = function(task) { |
+ // The Tasks that are pushed onto the queue aren't required to be inherently |
+ // asynchronous. This code force task execution to occur asynchronously. |
+ Promise.resolve().then(function() { |
+ task.setOwner(this); |
+ this.tasks_.push(task); |
+ // If more than one task is queued, then the queue is already running. |
+ if (this.tasks_.length === 1) { |
+ this.runPending_(); |
+ } |
+ }.bind(this)); |
+}; |
+ |
+/** |
+ * Sets a callback to be triggered when a task updates. |
+ * @param {function(string, !importer.TaskQueue.Task)} callback |
+ */ |
+importer.TaskQueue.prototype.addUpdateCallback = function(callback) { |
+ this.updateCallbacks_.push(callback); |
+}; |
+ |
+/** |
+ * Sets a callback that is triggered each time the queue goes from an idle |
+ * (i.e. empty with no running tasks) to an active (i.e. having a running task) |
+ * state. |
+ * @param {function()} callback |
+ */ |
+importer.TaskQueue.prototype.setActiveCallback = function(callback) { |
+ this.activeCallback_ = callback; |
+}; |
+ |
+/** |
+ * Sets a callback that is triggered each time the queue goes from an active to |
+ * an idle state. Also see #onActive. |
+ * @param {function()} callback |
+ */ |
+importer.TaskQueue.prototype.setIdleCallback = function(callback) { |
+ this.idleCallback_ = callback; |
+}; |
+ |
+/** |
+ * Sends out notifications when a task updates. This is meant to be called by |
+ * the running tasks owned by this queue. |
+ * @param {!importer.TaskQueue.UpdateType} updateType |
+ * @param {!importer.TaskQueue.Task} task |
+ */ |
+importer.TaskQueue.prototype.taskUpdate = function(updateType, task) { |
+ this.updateCallbacks_.forEach(function(callback) { |
+ callback.call(null, updateType, task); |
+ }); |
+}; |
+ |
+/** |
+ * Sends out notifications when a task stops. Stopping could be due to |
+ * successful completion, cancellation, or errors. This is meant to be called |
+ * by the running tasks owned by this queue. |
+ * @param {!importer.TaskQueue.UpdateType} reason |
+ * @param {!importer.TaskQueue.Task} task |
+ */ |
+importer.TaskQueue.prototype.taskDone = function(reason, task) { |
+ // Assumption: the currently running task is at the head of the queue. |
+ assert(this.tasks_[0] === task); |
+ // Remove the completed task from the queue. |
+ this.tasks_.shift(); |
+ // Send updates to clients. |
+ this.taskUpdate(reason, task); |
+ // Run the next thing in the queue. |
+ this.runPending_(); |
+}; |
+ |
+/** |
+ * Wakes the task queue up and runs the next pending task, or makes the queue go |
+ * back to sleep if no tasks are pending. |
+ * @private |
+ */ |
+importer.TaskQueue.prototype.runPending_ = function() { |
+ if (this.tasks_.length === 0) { |
+ // All done - go back to idle. |
+ this.active_ = false; |
+ if (this.idleCallback_) |
+ this.idleCallback_(); |
+ return; |
+ } |
+ |
+ if (!this.active_) { |
+ // If the queue is currently idle, transition to active state. |
+ this.active_ = true; |
+ if (this.activeCallback_) |
+ this.activeCallback_(); |
+ } |
+ |
+ var nextTask = this.tasks_[0]; |
+ nextTask.run(); |
+}; |
+ |
+/** |
+ * Interface for any Task that is to run on the TaskQueue. |
+ * @interface |
+ */ |
+importer.TaskQueue.Task = function() {}; |
+ |
+/** |
+ * Sets the TaskQueue that will own this task. The TaskQueue must call this |
+ * prior to enqueuing a Task. |
+ * @param {!importer.TaskQueue} owner |
+ */ |
+importer.TaskQueue.Task.prototype.setOwner; |
+ |
+/** |
+ * Performs the actual work of the Task. Child classes should implement this. |
+ */ |
+importer.TaskQueue.Task.prototype.run; |
+ |
+/** |
+ * Base class for importer tasks. |
+ * @constructor |
+ * @implements {importer.TaskQueue.Task} |
+ * |
+ * @param {string} taskId |
+ */ |
+importer.TaskQueue.BaseTask = function(taskId) { |
+ /** @private {string} */ |
+ this.taskId_ = taskId; |
+ /** @private {importer.TaskQueue} */ |
+ this.owner_ = null; |
+}; |
+ |
+/** |
+ * Sets the TaskQueue that will own this task. The TaskQueue must call this |
+ * prior to enqueuing a Task. |
+ * @param {!importer.TaskQueue} owner |
+ */ |
+importer.TaskQueue.BaseTask.prototype.setOwner = function(owner) { |
+ this.owner_ = owner; |
+}; |
+ |
+/** @override */ |
+importer.TaskQueue.BaseTask.prototype.run = function() {}; |
+ |
+/** |
+ * Sends progress notifications. Task subclasses should call this to report |
+ * progress. |
+ * @protected |
+ */ |
+importer.TaskQueue.BaseTask.prototype.notifyProgress = function() { |
+ this.owner_.taskUpdate(importer.TaskQueue.UpdateType.PROGRESS, this); |
+}; |
+ |
+/** |
+ * Sends success notifications. Task subclasses should call this to indicate |
+ * when they successfully complete. Calling this results in the Task instance |
+ * being dequeued. |
+ * @protected |
+ */ |
+importer.TaskQueue.BaseTask.prototype.notifySuccess = function() { |
+ this.owner_.taskDone(importer.TaskQueue.UpdateType.SUCCESS, this); |
+}; |
+ |
+/** |
+ * Sends error notifications. Task subclasses should call this to indicate when |
+ * an error occurs. Tasks are assumed to stop execution once they call |
+ * notifyError (i.e. they will be dequeued). |
+ * @protected |
+ */ |
+importer.TaskQueue.BaseTask.prototype.notifyError = function() { |
+ this.owner_.taskDone(importer.TaskQueue.UpdateType.ERROR, this); |
+}; |