OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 function FileCopyManager() { | 5 function FileCopyManager() { |
6 this.copyTasks_ = []; | 6 this.copyTasks_ = []; |
7 this.cancelObservers_ = []; | 7 this.cancelObservers_ = []; |
8 this.cancelRequested_ = false; | 8 this.cancelRequested_ = false; |
9 } | 9 } |
10 | 10 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
49 self.pendingDirectories = result.dirEntries; | 49 self.pendingDirectories = result.dirEntries; |
50 self.pendingFiles = result.fileEntries; | 50 self.pendingFiles = result.fileEntries; |
51 self.pendingBytes = result.fileBytes; | 51 self.pendingBytes = result.fileBytes; |
52 callback(); | 52 callback(); |
53 } | 53 } |
54 | 54 |
55 this.originalEntries = entries; | 55 this.originalEntries = entries; |
56 util.recurseAndResolveEntries(entries, onEntriesRecursed); | 56 util.recurseAndResolveEntries(entries, onEntriesRecursed); |
57 } | 57 } |
58 | 58 |
59 FileCopyManager.Task.prototype.takeNextEntry = function() { | 59 FileCopyManager.Task.prototype.getNextEntry = function() { |
60 // We should keep the file in pending list and remove it after complete. | |
61 // Otherwise, if we try to get status in the middle of copying. The returned | |
62 // status is wrong (miss count the pasting item in totalItems). | |
60 if (this.pendingDirectories.length) | 63 if (this.pendingDirectories.length) |
61 return this.pendingDirectories.shift(); | 64 return this.pendingDirectories[0]; |
62 | 65 |
63 if (this.pendingFiles.length) | 66 if (this.pendingFiles.length) |
64 return this.pendingFiles.shift(); | 67 return this.pendingFiles[0]; |
65 | 68 |
66 return null; | 69 return null; |
67 }; | 70 }; |
68 | 71 |
69 FileCopyManager.Task.prototype.markEntryComplete = function(entry, size) { | 72 FileCopyManager.Task.prototype.markEntryComplete = function(entry, size) { |
70 if (entry.isDirectory) { | 73 if (entry.isDirectory) { |
71 this.completedDirectories.push(entry); | 74 this.completedDirectories.push(entry); |
75 this.pendingDirectories.shift(); | |
72 } else { | 76 } else { |
73 this.completedFiles.push(entry); | 77 this.completedFiles.push(entry); |
74 this.completedBytes += size; | 78 this.completedBytes += size; |
79 this.pendingFiles.shift(); | |
75 } | 80 } |
76 }; | 81 }; |
77 | 82 |
78 FileCopyManager.Task.prototype.registerRename = function(fromName, toName) { | 83 FileCopyManager.Task.prototype.registerRename = function(fromName, toName) { |
79 this.renamedDirectories_.push({from: fromName + '/', to: toName + '/'}); | 84 this.renamedDirectories_.push({from: fromName + '/', to: toName + '/'}); |
80 }; | 85 }; |
81 | 86 |
82 FileCopyManager.Task.prototype.applyRenames = function(path) { | 87 FileCopyManager.Task.prototype.applyRenames = function(path) { |
83 // Directories are processed in pre-order, so we will store only the first | 88 // Directories are processed in pre-order, so we will store only the first |
84 // renaming point: | 89 // renaming point: |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
122 completedFiles: 0, | 127 completedFiles: 0, |
123 completedDirectories: 0, | 128 completedDirectories: 0, |
124 completedBytes: 0 | 129 completedBytes: 0 |
125 }; | 130 }; |
126 | 131 |
127 for (var i = 0; i < this.copyTasks_.length; i++) { | 132 for (var i = 0; i < this.copyTasks_.length; i++) { |
128 var task = this.copyTasks_[i]; | 133 var task = this.copyTasks_[i]; |
129 rv.pendingFiles += task.pendingFiles.length; | 134 rv.pendingFiles += task.pendingFiles.length; |
130 rv.pendingDirectories += task.pendingDirectories.length; | 135 rv.pendingDirectories += task.pendingDirectories.length; |
131 rv.pendingBytes += task.pendingBytes; | 136 rv.pendingBytes += task.pendingBytes; |
132 rv.pendingItems += rv.pendingFiles + rv.pendingDirectories; | |
133 | 137 |
134 rv.completedFiles += task.completedFiles.length; | 138 rv.completedFiles += task.completedFiles.length; |
135 rv.completedDirectories += task.completedDirectories.length; | 139 rv.completedDirectories += task.completedDirectories.length; |
136 rv.completedBytes += task.completedBytes; | 140 rv.completedBytes += task.completedBytes; |
137 rv.completedItems += rv.completedFiles + rv.completedDirectories; | |
138 | |
139 } | 141 } |
142 rv.pendingItems = rv.pendingFiles + rv.pendingDirectories; | |
143 rv.completedItems = rv.completedFiles + rv.completedDirectories; | |
140 | 144 |
141 rv.totalFiles = rv.pendingFiles + rv.completedFiles; | 145 rv.totalFiles = rv.pendingFiles + rv.completedFiles; |
142 rv.totalDirectories = rv.pendingDirectories + rv.completedDirectories; | 146 rv.totalDirectories = rv.pendingDirectories + rv.completedDirectories; |
143 rv.totalItems = rv.pendingItems + rv.completedItems; | 147 rv.totalItems = rv.pendingItems + rv.completedItems; |
144 rv.totalBytes = rv.pendingBytes + rv.completedBytes; | 148 rv.totalBytes = rv.pendingBytes + rv.completedBytes; |
145 | 149 |
146 return rv; | 150 return rv; |
147 }; | 151 }; |
148 | 152 |
153 FileCopyManager.prototype.getProgress = function() { | |
Rick Byers
2012/03/12 14:20:08
Since this is a public function you should have a
bshe
2012/03/12 16:41:19
Done.
| |
154 var status = this.getStatus(); | |
155 var result = { | |
Rick Byers
2012/03/12 14:20:08
No need to create a variable and initializing to 0
bshe
2012/03/12 16:41:19
Done.
| |
156 percentage: 0, | |
157 pendingItems: 0 | |
158 }; | |
159 | |
160 // TODO(bshe): Need to figure out a way to get completed bytes in real | |
161 // time. We currently use completedItems and totalItems to estimate the | |
162 // progress. There are completeBytes and totalBytes ready to use. | |
163 // However, the completedBytes is not in real time. It only updates | |
164 // itself after each item finished. So if there is a large item to | |
165 // copy, the progress bar will stop moving until it finishes and jump | |
166 // a large portion of the bar. | |
167 // There is case that when user copy a large file, we want to show an | |
168 // 100% animated progress bar. So we use completedItems + 1 here. | |
169 result.percentage = (status.completedItems + 1) / status.totalItems; | |
170 | |
171 result.pendingItems = status.pendingItems; | |
172 return result; | |
173 }; | |
174 | |
149 /** | 175 /** |
150 * Completely clear out the copy queue, either because we encountered an error | 176 * Completely clear out the copy queue, either because we encountered an error |
151 * or completed successfully. | 177 * or completed successfully. |
152 */ | 178 */ |
153 FileCopyManager.prototype.resetQueue_ = function() { | 179 FileCopyManager.prototype.resetQueue_ = function() { |
154 for (var i = 0; i < this.cancelObservers_.length; i++) | 180 for (var i = 0; i < this.cancelObservers_.length; i++) |
155 this.cancelObservers_[i](); | 181 this.cancelObservers_[i](); |
156 | 182 |
157 this.copyTasks_ = []; | 183 this.copyTasks_ = []; |
158 this.cancelObservers_ = []; | 184 this.cancelObservers_ = []; |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
264 entries, | 290 entries, |
265 deleteAfterCopy) { | 291 deleteAfterCopy) { |
266 var self = this; | 292 var self = this; |
267 var copyTask = new FileCopyManager.Task(sourceDirEntry, targetDirEntry); | 293 var copyTask = new FileCopyManager.Task(sourceDirEntry, targetDirEntry); |
268 copyTask.deleteAfterCopy = deleteAfterCopy; | 294 copyTask.deleteAfterCopy = deleteAfterCopy; |
269 copyTask.setEntries(entries, function() { | 295 copyTask.setEntries(entries, function() { |
270 self.copyTasks_.push(copyTask); | 296 self.copyTasks_.push(copyTask); |
271 if (self.copyTasks_.length == 1) { | 297 if (self.copyTasks_.length == 1) { |
272 // This moved us from 0 to 1 active tasks, let the servicing begin! | 298 // This moved us from 0 to 1 active tasks, let the servicing begin! |
273 self.serviceAllTasks_(); | 299 self.serviceAllTasks_(); |
300 } else { | |
301 // Force to update the progress of butter bar when there are new tasks | |
302 // coming while servicing current task. | |
303 var event = new cr.Event('copy-progress'); | |
304 event.reason = 'PROGRESS'; | |
305 self.dispatchEvent(event); | |
274 } | 306 } |
275 }); | 307 }); |
276 | 308 |
277 return copyTask; | 309 return copyTask; |
278 }; | 310 }; |
279 | 311 |
280 /** | 312 /** |
281 * Service all pending tasks, as well as any that might appear during the | 313 * Service all pending tasks, as well as any that might appear during the |
282 * copy. | 314 * copy. |
283 */ | 315 */ |
284 FileCopyManager.prototype.serviceAllTasks_ = function() { | 316 FileCopyManager.prototype.serviceAllTasks_ = function() { |
285 var self = this; | 317 var self = this; |
286 | 318 |
287 function onTaskError(err) { | 319 function onTaskError(err) { |
288 var event = new cr.Event('copy-progress'); | 320 var event = new cr.Event('copy-progress'); |
289 event.reason = 'ERROR'; | 321 event.reason = 'ERROR'; |
290 event.error = err; | 322 event.error = err; |
291 self.dispatchEvent(event); | 323 self.dispatchEvent(event); |
292 self.resetQueue_(); | 324 self.resetQueue_(); |
293 } | 325 } |
294 | 326 |
295 function onTaskSuccess(task) { | 327 function onTaskSuccess(task) { |
296 if (task == null) { | 328 if (!self.copyTasks_.length) { |
297 // All tasks have been serviced, clean up and exit. | 329 // All tasks have been serviced, clean up and exit. |
298 var event = new cr.Event('copy-progress'); | 330 var event = new cr.Event('copy-progress'); |
299 event.reason = 'SUCCESS'; | 331 event.reason = 'SUCCESS'; |
300 self.dispatchEvent(event); | 332 self.dispatchEvent(event); |
301 self.resetQueue_(); | 333 self.resetQueue_(); |
302 return; | 334 return; |
303 } | 335 } |
304 | 336 |
337 // We want to dispatch a PROGRESS event when there are more tasks to serve | |
338 // right after one task finished in the queue. We treat all tasks as one | |
339 // big task logically, so there is only one BEGIN/SUCCESS event pair for | |
340 // these continuous tasks. | |
341 var event = new cr.Event('copy-progress'); | |
342 event.reason = 'PROGRESS'; | |
343 self.dispatchEvent(event); | |
Rick Byers
2012/03/12 14:20:08
Probably worth encapsulating these 3 lines in a si
bshe
2012/03/12 16:41:19
Done.
| |
344 | |
305 self.serviceNextTask_(onTaskSuccess, onTaskError); | 345 self.serviceNextTask_(onTaskSuccess, onTaskError); |
306 } | 346 } |
307 | 347 |
308 // If the queue size is 1 after pushing our task, it was empty before, | 348 // If the queue size is 1 after pushing our task, it was empty before, |
309 // so we need to kick off queue processing. | 349 // so we need to kick off queue processing and dispatch BEGIN event. |
350 | |
351 var event = new cr.Event('copy-progress'); | |
352 event.reason = 'BEGIN'; | |
353 this.dispatchEvent(event); | |
310 this.serviceNextTask_(onTaskSuccess, onTaskError); | 354 this.serviceNextTask_(onTaskSuccess, onTaskError); |
311 }; | 355 }; |
312 | 356 |
313 /** | 357 /** |
314 * Service all entries in the next copy task. | 358 * Service all entries in the next copy task. |
315 */ | 359 */ |
316 FileCopyManager.prototype.serviceNextTask_ = function( | 360 FileCopyManager.prototype.serviceNextTask_ = function( |
317 successCallback, errorCallback) { | 361 successCallback, errorCallback) { |
318 if (this.maybeCancel_()) | 362 if (this.maybeCancel_()) |
319 return; | 363 return; |
320 | 364 |
321 if (!this.copyTasks_.length) { | |
322 successCallback(null); | |
323 return; | |
324 } | |
325 | |
326 var self = this; | 365 var self = this; |
327 var task = this.copyTasks_[0]; | 366 var task = this.copyTasks_[0]; |
328 | 367 |
329 function onFilesystemError(err) { | 368 function onFilesystemError(err) { |
330 errorCallback(new FileCopyManager.Error('FILESYSTEM_ERROR', err)); | 369 errorCallback(new FileCopyManager.Error('FILESYSTEM_ERROR', err)); |
331 } | 370 } |
332 | 371 |
333 function onTaskComplete() { | 372 function onTaskComplete() { |
334 self.copyTasks_.shift(); | 373 self.copyTasks_.shift(); |
335 successCallback(task); | 374 successCallback(task); |
336 } | 375 } |
337 | 376 |
338 function deleteOriginals() { | 377 function deleteOriginals() { |
339 var count = task.originalEntries.length; | 378 var count = task.originalEntries.length; |
340 | 379 |
341 function onEntryDeleted() { | 380 function onEntryDeleted() { |
342 count--; | 381 count--; |
343 if (!count) | 382 if (!count) |
344 onTaskComplete(); | 383 onTaskComplete(); |
345 } | 384 } |
346 | 385 |
347 for (var i = 0; i < task.originalEntries.length; i++) { | 386 for (var i = 0; i < task.originalEntries.length; i++) { |
348 util.removeFileOrDirectory( | 387 util.removeFileOrDirectory( |
349 task.originalEntries[i], onEntryDeleted, onFilesystemError); | 388 task.originalEntries[i], onEntryDeleted, onFilesystemError); |
350 } | 389 } |
351 } | 390 } |
352 | 391 |
353 function onEntryServiced(targetEntry, size) { | 392 function onEntryServiced(targetEntry, size) { |
354 if (!targetEntry) { | 393 // We should not dispatch a PROGRESS event when there is no pending items |
394 // in the task. | |
395 if (task.pendingDirectories.length + task.pendingFiles.length == 0) { | |
355 // All done with the entries in this task. | 396 // All done with the entries in this task. |
356 if (task.deleteAfterCopy) { | 397 if (task.deleteAfterCopy) { |
357 deleteOriginals() | 398 deleteOriginals() |
358 } else { | 399 } else { |
359 onTaskComplete(); | 400 onTaskComplete(); |
360 } | 401 } |
361 return; | 402 return; |
362 } | 403 } |
363 | 404 |
364 var event = new cr.Event('copy-progress'); | 405 var event = new cr.Event('copy-progress'); |
365 event.reason = 'PROGRESS'; | 406 event.reason = 'PROGRESS'; |
366 self.dispatchEvent(event); | 407 self.dispatchEvent(event); |
367 | 408 |
368 // We yield a few ms between copies to give the browser a chance to service | 409 // We yield a few ms between copies to give the browser a chance to service |
369 // events (like perhaps the user clicking to cancel the copy, for example). | 410 // events (like perhaps the user clicking to cancel the copy, for example). |
370 setTimeout(function() { | 411 setTimeout(function() { |
371 self.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); | 412 self.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); |
372 }, 10); | 413 }, 10); |
373 } | 414 } |
374 | 415 |
375 var event = new cr.Event('copy-progress'); | |
376 event.reason = 'BEGIN'; | |
377 this.dispatchEvent(event); | |
378 this.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); | 416 this.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); |
379 } | 417 } |
380 | 418 |
381 /** | 419 /** |
382 * Service the next entry in a given task. | 420 * Service the next entry in a given task. |
383 */ | 421 */ |
384 FileCopyManager.prototype.serviceNextTaskEntry_ = function( | 422 FileCopyManager.prototype.serviceNextTaskEntry_ = function( |
385 task, successCallback, errorCallback) { | 423 task, successCallback, errorCallback) { |
386 if (this.maybeCancel_()) | 424 if (this.maybeCancel_()) |
387 return; | 425 return; |
388 | 426 |
389 var self = this; | 427 var self = this; |
390 var sourceEntry = task.takeNextEntry(); | 428 var sourceEntry = task.getNextEntry(); |
391 | 429 |
392 if (!sourceEntry) { | 430 if (!sourceEntry) { |
393 // All entries in this task have been copied. | 431 // All entries in this task have been copied. |
394 successCallback(null); | 432 successCallback(null); |
395 return; | 433 return; |
396 } | 434 } |
397 | 435 |
398 var sourcePath = task.sourceDirEntry.fullPath; | 436 var sourcePath = task.sourceDirEntry.fullPath; |
399 if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) { | 437 if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) { |
400 // We found an entry in the list that is not relative to the base source | 438 // We found an entry in the list that is not relative to the base source |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
475 onFilesystemError)); | 513 onFilesystemError)); |
476 } else { | 514 } else { |
477 targetDirEntry.getFile( | 515 targetDirEntry.getFile( |
478 targetRelativePath, | 516 targetRelativePath, |
479 {create: true, exclusive: true}, | 517 {create: true, exclusive: true}, |
480 function(targetEntry) { | 518 function(targetEntry) { |
481 self.copyEntry_(sourceEntry, targetEntry, | 519 self.copyEntry_(sourceEntry, targetEntry, |
482 onCopyComplete, onError); | 520 onCopyComplete, onError); |
483 }, | 521 }, |
484 util.flog('Error getting file: ' + targetRelativePath, | 522 util.flog('Error getting file: ' + targetRelativePath, |
485 onFilesystemError)); | 523 onFilesystemError)); |
Rick Byers
2012/03/12 14:20:08
Remove added extra space
bshe
2012/03/12 16:41:19
Done.
| |
486 } | 524 } |
487 } | 525 } |
488 | 526 |
489 function tryNextCopy() { | 527 function tryNextCopy() { |
490 targetRelativePath = targetRelativePrefix; | 528 targetRelativePath = targetRelativePrefix; |
491 if (copyNumber > 0) { | 529 if (copyNumber > 0) { |
492 targetRelativePath += ' (' + copyNumber + ')'; | 530 targetRelativePath += ' (' + copyNumber + ')'; |
493 } | 531 } |
494 targetRelativePath += targetExt; | 532 targetRelativePath += targetExt; |
495 | 533 |
(...skipping 23 matching lines...) Expand all Loading... | |
519 successCallback(targetEntry, file.size) | 557 successCallback(targetEntry, file.size) |
520 }; | 558 }; |
521 writer.write(file); | 559 writer.write(file); |
522 } | 560 } |
523 | 561 |
524 targetEntry.createWriter(onWriterCreated, errorCallback); | 562 targetEntry.createWriter(onWriterCreated, errorCallback); |
525 } | 563 } |
526 | 564 |
527 sourceEntry.file(onSourceFileFound, errorCallback); | 565 sourceEntry.file(onSourceFileFound, errorCallback); |
528 }; | 566 }; |
OLD | NEW |