Index: chrome/browser/resources/file_manager/js/file_copy_manager.js |
diff --git a/chrome/browser/resources/file_manager/js/file_copy_manager.js b/chrome/browser/resources/file_manager/js/file_copy_manager.js |
index 8b017cef0cd13820d905239aae4ea8e725b92b9f..296226e2ed8da1199e0d46906868e598b3767b5f 100644 |
--- a/chrome/browser/resources/file_manager/js/file_copy_manager.js |
+++ b/chrome/browser/resources/file_manager/js/file_copy_manager.js |
@@ -60,22 +60,38 @@ FileCopyManager.Task.prototype.setEntries = function(entries, callback) { |
util.recurseAndResolveEntries(entries, recurse, onEntriesRecursed); |
} |
-FileCopyManager.Task.prototype.takeNextEntry = function() { |
- if (this.pendingDirectories.length) |
- return this.pendingDirectories.shift(); |
+FileCopyManager.Task.prototype.getNextEntry = function() { |
+ // We should keep the file in pending list and remove it after complete. |
+ // Otherwise, if we try to get status in the middle of copying. The returned |
+ // status is wrong (miss count the pasting item in totalItems). |
+ if (this.pendingDirectories.length) { |
+ this.pendingDirectories[0].inProgress = true; |
+ return this.pendingDirectories[0]; |
+ } |
- if (this.pendingFiles.length) |
- return this.pendingFiles.shift(); |
+ if (this.pendingFiles.length) { |
+ this.pendingFiles[0].inProgress = true; |
+ return this.pendingFiles[0]; |
+ } |
return null; |
}; |
FileCopyManager.Task.prototype.markEntryComplete = function(entry, size) { |
- if (entry.isDirectory) { |
+ // It is probably not safe to directly remove the first entry in pending list. |
+ // We need to check if the removed entry (srcEntry) corresponding to the added |
+ // entry (target entry). |
+ if (entry.isDirectory && this.pendingDirectories && |
+ this.pendingDirectories[0].inProgress) { |
this.completedDirectories.push(entry); |
- } else { |
+ this.pendingDirectories.shift(); |
+ } else if (this.pendingFiles && this.pendingFiles[0].inProgress) { |
this.completedFiles.push(entry); |
this.completedBytes += size; |
+ this.pendingFiles.shift(); |
+ } else { |
+ throw new Error('Try to remove a source entry which is not correspond to' + |
+ ' the finished target entry'); |
} |
}; |
@@ -133,14 +149,13 @@ FileCopyManager.prototype.getStatus = function() { |
rv.pendingFiles += task.pendingFiles.length; |
rv.pendingDirectories += task.pendingDirectories.length; |
rv.pendingBytes += task.pendingBytes; |
- rv.pendingItems += rv.pendingFiles + rv.pendingDirectories; |
rv.completedFiles += task.completedFiles.length; |
rv.completedDirectories += task.completedDirectories.length; |
rv.completedBytes += task.completedBytes; |
- rv.completedItems += rv.completedFiles + rv.completedDirectories; |
- |
} |
+ rv.pendingItems = rv.pendingFiles + rv.pendingDirectories; |
+ rv.completedItems = rv.completedFiles + rv.completedDirectories; |
rv.totalFiles = rv.pendingFiles + rv.completedFiles; |
rv.totalDirectories = rv.pendingDirectories + rv.completedDirectories; |
@@ -151,6 +166,40 @@ FileCopyManager.prototype.getStatus = function() { |
}; |
/** |
+ * Get the overall progress data of all queued copy tasks. |
+ * @return {Object} An object containing the following parameters: |
+ * percentage - The percentage (0-1) of finished items. |
+ * pendingItems - The number of pending/unfinished items. |
+ */ |
+FileCopyManager.prototype.getProgress = function() { |
+ var status = this.getStatus(); |
+ return { |
+ // TODO(bshe): Need to figure out a way to get completed bytes in real |
+ // time. We currently use completedItems and totalItems to estimate the |
+ // progress. There are completeBytes and totalBytes ready to use. |
+ // However, the completedBytes is not in real time. It only updates |
+ // itself after each item finished. So if there is a large item to |
+ // copy, the progress bar will stop moving until it finishes and jump |
+ // a large portion of the bar. |
+ // There is case that when user copy a large file, we want to show an |
+ // 100% animated progress bar. So we use completedItems + 1 here. |
+ percentage: (status.completedItems + 1) / status.totalItems, |
+ pendingItems: status.pendingItems |
+ }; |
+}; |
+ |
+/** |
+ * Dispatch a simple copy-progress event with reason and optional err data. |
+ */ |
+FileCopyManager.prototype.sendProgressEvent_ = function(reason, opt_err) { |
+ var event = new cr.Event('copy-progress'); |
+ event.reason = reason; |
+ if (opt_err) |
+ event.error = opt_err; |
+ this.dispatchEvent(event); |
+}; |
+ |
+/** |
* Completely clear out the copy queue, either because we encountered an error |
* or completed successfully. |
*/ |
@@ -176,9 +225,7 @@ FileCopyManager.prototype.requestCancel = function(opt_callback) { |
* Perform the bookeeping required to cancel. |
*/ |
FileCopyManager.prototype.doCancel_ = function() { |
- var event = new cr.Event('copy-progress'); |
- event.reason = 'CANCELLED'; |
- this.dispatchEvent(event); |
+ this.sendProgressEvent_('CANCELLED'); |
this.resetQueue_(); |
}; |
@@ -207,10 +254,8 @@ FileCopyManager.prototype.paste = function(clipboard, targetEntry, |
}; |
function onPathError(err) { |
- var event = new cr.Event('copy-progress'); |
- event.reason = 'ERROR'; |
- event.error = new FileCopyManager.Error('FILESYSTEM_ERROR', err); |
- self.dispatchEvent(event); |
+ self.sendProgressEvent_('ERROR', |
+ new FileCopyManager.Error('FILESYSTEM_ERROR', err)); |
} |
function onSourceEntryFound(dirEntry) { |
@@ -279,6 +324,10 @@ FileCopyManager.prototype.queueCopy = function(sourceDirEntry, |
if (self.copyTasks_.length == 1) { |
// This moved us from 0 to 1 active tasks, let the servicing begin! |
self.serviceAllTasks_(); |
+ } else { |
+ // Force to update the progress of butter bar when there are new tasks |
+ // coming while servicing current task. |
+ self.sendProgressEvent_('PROGRESS'); |
} |
}); |
@@ -293,28 +342,31 @@ FileCopyManager.prototype.serviceAllTasks_ = function() { |
var self = this; |
function onTaskError(err) { |
- var event = new cr.Event('copy-progress'); |
- event.reason = 'ERROR'; |
- event.error = err; |
- self.dispatchEvent(event); |
+ self.sendProgressEvent_('ERROR', err); |
self.resetQueue_(); |
} |
function onTaskSuccess(task) { |
- if (task == null) { |
+ if (!self.copyTasks_.length) { |
// All tasks have been serviced, clean up and exit. |
- var event = new cr.Event('copy-progress'); |
- event.reason = 'SUCCESS'; |
- self.dispatchEvent(event); |
+ self.sendProgressEvent_('SUCCESS'); |
self.resetQueue_(); |
return; |
} |
+ // We want to dispatch a PROGRESS event when there are more tasks to serve |
+ // right after one task finished in the queue. We treat all tasks as one |
+ // big task logically, so there is only one BEGIN/SUCCESS event pair for |
+ // these continuous tasks. |
+ self.sendProgressEvent_('PROGRESS'); |
+ |
self.serviceNextTask_(onTaskSuccess, onTaskError); |
} |
// If the queue size is 1 after pushing our task, it was empty before, |
- // so we need to kick off queue processing. |
+ // so we need to kick off queue processing and dispatch BEGIN event. |
+ |
+ this.sendProgressEvent_('BEGIN'); |
this.serviceNextTask_(onTaskSuccess, onTaskError); |
}; |
@@ -326,11 +378,6 @@ FileCopyManager.prototype.serviceNextTask_ = function( |
if (this.maybeCancel_()) |
return; |
- if (!this.copyTasks_.length) { |
- successCallback(null); |
- return; |
- } |
- |
var self = this; |
var task = this.copyTasks_[0]; |
@@ -359,7 +406,9 @@ FileCopyManager.prototype.serviceNextTask_ = function( |
} |
function onEntryServiced(targetEntry, size) { |
- if (!targetEntry) { |
+ // We should not dispatch a PROGRESS event when there is no pending items |
+ // in the task. |
+ if (task.pendingDirectories.length + task.pendingFiles.length == 0) { |
// All done with the entries in this task. |
// If files are moved within GData, FileEntry.moveTo() is used and |
// there is no need to delete the original files. |
@@ -371,9 +420,7 @@ FileCopyManager.prototype.serviceNextTask_ = function( |
return; |
} |
- var event = new cr.Event('copy-progress'); |
- event.reason = 'PROGRESS'; |
- self.dispatchEvent(event); |
+ self.sendProgressEvent_('PROGRESS'); |
// We yield a few ms between copies to give the browser a chance to service |
// events (like perhaps the user clicking to cancel the copy, for example). |
@@ -382,9 +429,6 @@ FileCopyManager.prototype.serviceNextTask_ = function( |
}, 10); |
} |
- var event = new cr.Event('copy-progress'); |
- event.reason = 'BEGIN'; |
- this.dispatchEvent(event); |
this.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); |
} |
@@ -397,7 +441,7 @@ FileCopyManager.prototype.serviceNextTaskEntry_ = function( |
return; |
var self = this; |
- var sourceEntry = task.takeNextEntry(); |
+ var sourceEntry = task.getNextEntry(); |
if (!sourceEntry) { |
// All entries in this task have been copied. |