| Index: ui/file_manager/file_manager/background/js/file_operation_manager.js
|
| diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js
|
| index 28d4312a570095f0e3bbf3eaec89998450ec53bc..4c3baa209a3f6973f3ae6f9bba2869346f8fddc9 100644
|
| --- a/ui/file_manager/file_manager/background/js/file_operation_manager.js
|
| +++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js
|
| @@ -17,9 +17,18 @@ function FileOperationManager(volumeManager) {
|
| this.volumeManager_ = volumeManager;
|
|
|
| /**
|
| + * List of pending copy tasks. The manager can execute tasks in arbitary
|
| + * order.
|
| * @private {!Array<!fileOperationUtil.Task>}
|
| */
|
| - this.copyTasks_ = [];
|
| + this.pendingCopyTasks_ = [];
|
| +
|
| + /**
|
| + * Map of volume id and running copy task. The key is a volume id and the
|
| + * value is a copy task.
|
| + * @private {!Object<string,!fileOperationUtil.Task>}
|
| + */
|
| + this.runningCopyTasks_ = {};
|
|
|
| /**
|
| * @private {!Array<!fileOperationUtil.Task>}
|
| @@ -39,6 +48,14 @@ function FileOperationManager(volumeManager) {
|
| }
|
|
|
| /**
|
| + * Returns pending copy tasks for testing.
|
| + * @return {!Array<!fileOperationUtil.Task>} Pending copy tasks.
|
| + */
|
| +FileOperationManager.prototype.getPendingCopyTasksForTesting = function() {
|
| + return this.pendingCopyTasks_;
|
| +};
|
| +
|
| +/**
|
| * Adds an event listener for the tasks.
|
| * @param {string} type The name of the event.
|
| * @param {EventListenerType} handler The handler for the event. This is called
|
| @@ -64,17 +81,9 @@ FileOperationManager.prototype.removeEventListener = function(type, handler) {
|
| * @return {boolean} True, if there are any tasks.
|
| */
|
| FileOperationManager.prototype.hasQueuedTasks = function() {
|
| - return this.copyTasks_.length > 0 || this.deleteTasks_.length > 0;
|
| -};
|
| -
|
| -/**
|
| - * Completely clear out the copy queue, either because we encountered an error
|
| - * or completed successfully.
|
| - *
|
| - * @private
|
| - */
|
| -FileOperationManager.prototype.resetQueue_ = function() {
|
| - this.copyTasks_ = [];
|
| + return Object.keys(this.runningCopyTasks_).length > 0 ||
|
| + this.pendingCopyTasks_.length > 0 ||
|
| + this.deleteTasks_.length > 0;
|
| };
|
|
|
| /**
|
| @@ -83,20 +92,26 @@ FileOperationManager.prototype.resetQueue_ = function() {
|
| */
|
| FileOperationManager.prototype.requestTaskCancel = function(taskId) {
|
| var task = null;
|
| - for (var i = 0; i < this.copyTasks_.length; i++) {
|
| - task = this.copyTasks_[i];
|
| +
|
| + // If the task is not on progress, remove it immediately.
|
| + for (var i = 0; i < this.pendingCopyTasks_.length; i++) {
|
| + task = this.pendingCopyTasks_[i];
|
| if (task.taskId !== taskId)
|
| continue;
|
| task.requestCancel();
|
| - // If the task is not on progress, remove it immediately.
|
| - if (i !== 0) {
|
| - this.eventRouter_.sendProgressEvent(
|
| - fileOperationUtil.EventRouter.EventType.CANCELED,
|
| - task.getStatus(),
|
| - task.taskId);
|
| - this.copyTasks_.splice(i, 1);
|
| - }
|
| + this.eventRouter_.sendProgressEvent(
|
| + fileOperationUtil.EventRouter.EventType.CANCELED,
|
| + task.getStatus(),
|
| + task.taskId);
|
| + this.pendingCopyTasks_.splice(i, 1);
|
| }
|
| +
|
| + for (var volumeId in this.runningCopyTasks_) {
|
| + task = this.runningCopyTasks_[volumeId];
|
| + if (task.taskId === taskId)
|
| + task.requestCancel();
|
| + }
|
| +
|
| for (var i = 0; i < this.deleteTasks_.length; i++) {
|
| task = this.deleteTasks_[i];
|
| if (task.taskId !== taskId)
|
| @@ -216,43 +231,84 @@ FileOperationManager.prototype.queueCopy_ = function(
|
| fileOperationUtil.EventRouter.EventType.BEGIN,
|
| task.getStatus(),
|
| task.taskId);
|
| +
|
| task.initialize(function() {
|
| - this.copyTasks_.push(task);
|
| - if (this.copyTasks_.length === 1)
|
| - this.serviceAllTasks_();
|
| + this.pendingCopyTasks_.push(task);
|
| + this.serviceAllTasks_();
|
| }.bind(this));
|
| };
|
|
|
| /**
|
| * Service all pending tasks, as well as any that might appear during the
|
| - * copy.
|
| + * copy. We allow to run tasks in parallel when destinations are different
|
| + * volumes.
|
| *
|
| * @private
|
| */
|
| FileOperationManager.prototype.serviceAllTasks_ = function() {
|
| - if (!this.copyTasks_.length) {
|
| + if (this.pendingCopyTasks_.length === 0 &&
|
| + Object.keys(this.runningCopyTasks_).length === 0) {
|
| // All tasks have been serviced, clean up and exit.
|
| chrome.power.releaseKeepAwake();
|
| - this.resetQueue_();
|
| return;
|
| }
|
|
|
| // Prevent the system from sleeping while copy is in progress.
|
| chrome.power.requestKeepAwake('system');
|
|
|
| - var onTaskProgress = function() {
|
| + // Find next task which can run at now.
|
| + var nextTask = null;
|
| + var nextTaskVolumeId = null;
|
| + for (var i = 0; i < this.pendingCopyTasks_.length; i++) {
|
| + var task = this.pendingCopyTasks_[i];
|
| +
|
| + // Fails a copy task of which it fails to get volume info. The destination
|
| + // volume might be already unmounted.
|
| + var volumeInfo = this.volumeManager_.getVolumeInfo(task.targetDirEntry);
|
| + if (volumeInfo === null) {
|
| + this.eventRouter_.sendProgressEvent(
|
| + fileOperationUtil.EventRouter.EventType.ERROR,
|
| + task.getStatus(),
|
| + task.taskId,
|
| + new fileOperationUtil.Error(
|
| + util.FileOperationErrorType.FILESYSTEM_ERROR,
|
| + util.createDOMError(util.FileError.NOT_FOUND_ERR)));
|
| +
|
| + this.pendingCopyTasks_.splice(i, 1);
|
| + i--;
|
| +
|
| + continue;
|
| + }
|
| +
|
| + // When no task is running for the volume, run the task.
|
| + if (!this.runningCopyTasks_[volumeInfo.volumeId]) {
|
| + nextTask = this.pendingCopyTasks_.splice(i, 1)[0];
|
| + nextTaskVolumeId = volumeInfo.volumeId;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // There is no task which can run at now.
|
| + if (nextTask === null)
|
| + return;
|
| +
|
| + var onTaskProgress = function(task) {
|
| this.eventRouter_.sendProgressEvent(
|
| fileOperationUtil.EventRouter.EventType.PROGRESS,
|
| - this.copyTasks_[0].getStatus(),
|
| - this.copyTasks_[0].taskId);
|
| - }.bind(this);
|
| + task.getStatus(),
|
| + task.taskId);
|
| + }.bind(this, nextTask);
|
|
|
| var onEntryChanged = function(kind, entry) {
|
| this.eventRouter_.sendEntryChangedEvent(kind, entry);
|
| }.bind(this);
|
|
|
| - var onTaskError = function(err) {
|
| - var task = this.copyTasks_.shift();
|
| + // Since getVolumeInfo of targetDirEntry might not be available when this
|
| + // callback is called, bind volume id here.
|
| + var onTaskError = function(volumeId, err) {
|
| + var task = this.runningCopyTasks_[volumeId];
|
| + delete this.runningCopyTasks_[volumeId];
|
| +
|
| var reason = err.data.name === util.FileError.ABORT_ERR ?
|
| fileOperationUtil.EventRouter.EventType.CANCELED :
|
| fileOperationUtil.EventRouter.EventType.ERROR;
|
| @@ -261,19 +317,22 @@ FileOperationManager.prototype.serviceAllTasks_ = function() {
|
| task.taskId,
|
| err);
|
| this.serviceAllTasks_();
|
| - }.bind(this);
|
| + }.bind(this, nextTaskVolumeId);
|
| +
|
| + var onTaskSuccess = function(volumeId) {
|
| + var task = this.runningCopyTasks_[volumeId];
|
| + delete this.runningCopyTasks_[volumeId];
|
|
|
| - var onTaskSuccess = function() {
|
| - // The task at the front of the queue is completed. Pop it from the queue.
|
| - var task = this.copyTasks_.shift();
|
| this.eventRouter_.sendProgressEvent(
|
| fileOperationUtil.EventRouter.EventType.SUCCESS,
|
| task.getStatus(),
|
| task.taskId);
|
| this.serviceAllTasks_();
|
| - }.bind(this);
|
| + }.bind(this, nextTaskVolumeId);
|
| +
|
| + // Add to running tasks and run it.
|
| + this.runningCopyTasks_[nextTaskVolumeId] = nextTask;
|
|
|
| - var nextTask = this.copyTasks_[0];
|
| this.eventRouter_.sendProgressEvent(
|
| fileOperationUtil.EventRouter.EventType.PROGRESS,
|
| nextTask.getStatus(),
|
| @@ -414,9 +473,8 @@ FileOperationManager.prototype.zipSelection = function(
|
| zipTask.getStatus(),
|
| zipTask.taskId);
|
| zipTask.initialize(function() {
|
| - this.copyTasks_.push(zipTask);
|
| - if (this.copyTasks_.length == 1)
|
| - this.serviceAllTasks_();
|
| + this.pendingCopyTasks_.push(zipTask);
|
| + this.serviceAllTasks_();
|
| }.bind(this));
|
| };
|
|
|
|
|