Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: chrome/browser/resources/file_manager/js/file_copy_manager.js

Issue 11309014: File manager: support for zipping selected files. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Still missing unit test for ZipFileCreator. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 /** 5 /**
6 * @constructor 6 * @constructor
7 * @param {DirectoryEntry} root Root directory entry. 7 * @param {DirectoryEntry} root Root directory entry.
8 */ 8 */
9 function FileCopyManager(root) { 9 function FileCopyManager(root) {
10 this.copyTasks_ = []; 10 this.copyTasks_ = [];
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 this.pendingDirectories = []; 54 this.pendingDirectories = [];
55 this.pendingFiles = []; 55 this.pendingFiles = [];
56 this.pendingBytes = 0; 56 this.pendingBytes = 0;
57 57
58 this.completedDirectories = []; 58 this.completedDirectories = [];
59 this.completedFiles = []; 59 this.completedFiles = [];
60 this.completedBytes = 0; 60 this.completedBytes = 0;
61 61
62 this.deleteAfterCopy = false; 62 this.deleteAfterCopy = false;
63 this.move = false; 63 this.move = false;
64 this.zip = false;
64 this.sourceOnGData = false; 65 this.sourceOnGData = false;
65 this.targetOnGData = false; 66 this.targetOnGData = false;
66 67
67 // If directory already exists, we try to make a copy named 'dir (X)', 68 // If directory already exists, we try to make a copy named 'dir (X)',
68 // where X is a number. When we do this, all subsequent copies from 69 // where X is a number. When we do this, all subsequent copies from
69 // inside the subtree should be mapped to the new directory name. 70 // inside the subtree should be mapped to the new directory name.
70 // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should 71 // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should
71 // become 'dir (1)\file.txt'. 72 // become 'dir (1)\file.txt'.
72 this.renamedDirectories_ = []; 73 this.renamedDirectories_ = [];
73 }; 74 };
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 pendingBytes: 0, 224 pendingBytes: 0,
224 225
225 completedItems: 0, // Files + Directories 226 completedItems: 0, // Files + Directories
226 completedFiles: 0, 227 completedFiles: 0,
227 completedDirectories: 0, 228 completedDirectories: 0,
228 completedBytes: 0, 229 completedBytes: 0,
229 230
230 percentage: NaN, 231 percentage: NaN,
231 pendingCopies: 0, 232 pendingCopies: 0,
232 pendingMoves: 0, 233 pendingMoves: 0,
234 pendingZips: 0,
233 filename: '' // In case pendingItems == 1 235 filename: '' // In case pendingItems == 1
234 }; 236 };
235 237
236 var pendingFile = null; 238 var pendingFile = null;
237 239
238 for (var i = 0; i < this.copyTasks_.length; i++) { 240 for (var i = 0; i < this.copyTasks_.length; i++) {
239 var task = this.copyTasks_[i]; 241 var task = this.copyTasks_[i];
240 var pendingFiles = task.pendingFiles.length; 242 var pendingFiles = task.pendingFiles.length;
241 var pendingDirectories = task.pendingDirectories.length; 243 var pendingDirectories = task.pendingDirectories.length;
242 rv.pendingFiles += pendingFiles; 244 rv.pendingFiles += pendingFiles;
243 rv.pendingDirectories += pendingDirectories; 245 rv.pendingDirectories += pendingDirectories;
244 rv.pendingBytes += task.pendingBytes; 246 rv.pendingBytes += task.pendingBytes;
245 247
246 rv.completedFiles += task.completedFiles.length; 248 rv.completedFiles += task.completedFiles.length;
247 rv.completedDirectories += task.completedDirectories.length; 249 rv.completedDirectories += task.completedDirectories.length;
248 rv.completedBytes += task.completedBytes; 250 rv.completedBytes += task.completedBytes;
249 251
250 if (task.move || task.deleteAfterCopy) { 252 if (task.zip) {
253 rv.pendingZips += pendingFiles + pendingDirectories;
254 } else if (task.move || task.deleteAfterCopy) {
251 rv.pendingMoves += pendingFiles + pendingDirectories; 255 rv.pendingMoves += pendingFiles + pendingDirectories;
252 } else { 256 } else {
253 rv.pendingCopies += pendingFiles + pendingDirectories; 257 rv.pendingCopies += pendingFiles + pendingDirectories;
254 } 258 }
255 259
256 if (task.pendingFiles.length === 1) 260 if (task.pendingFiles.length === 1)
257 pendingFile = task.pendingFiles[0]; 261 pendingFile = task.pendingFiles[0];
258 262
259 if (task.pendingDirectories.length === 1) 263 if (task.pendingDirectories.length === 1)
260 pendingFile = task.pendingDirectories[0]; 264 pendingFile = task.pendingDirectories[0];
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after
628 632
629 self.sendProgressEvent_('PROGRESS'); 633 self.sendProgressEvent_('PROGRESS');
630 634
631 // We yield a few ms between copies to give the browser a chance to service 635 // We yield a few ms between copies to give the browser a chance to service
632 // events (like perhaps the user clicking to cancel the copy, for example). 636 // events (like perhaps the user clicking to cancel the copy, for example).
633 setTimeout(function() { 637 setTimeout(function() {
634 self.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); 638 self.serviceNextTaskEntry_(task, onEntryServiced, errorCallback);
635 }, 10); 639 }, 10);
636 } 640 }
637 641
638 this.serviceNextTaskEntry_(task, onEntryServiced, errorCallback); 642 if (!task.zip)
643 this.serviceNextTaskEntry_(task, onEntryServiced, errorCallback);
644 else
645 this.serviceZipTask_(task, onTaskComplete, errorCallback);
639 }; 646 };
640 647
641 /** 648 /**
642 * Service the next entry in a given task. 649 * Service the next entry in a given task.
643 * TODO(olege): Refactor this method into a separate class. 650 * TODO(olege): Refactor this method into a separate class.
644 * 651 *
645 * @private 652 * @private
646 * @param {FileManager.Task} task A task. 653 * @param {FileManager.Task} task A task.
647 * @param {Function} successCallback On success. 654 * @param {Function} successCallback On success.
648 * @param {Function} errorCallback On error. 655 * @param {Function} errorCallback On error.
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
958 // Check to see if the target exists. This kicks off the rest of the copy 965 // Check to see if the target exists. This kicks off the rest of the copy
959 // if the target is not found, or raises an error if it does. 966 // if the target is not found, or raises an error if it does.
960 util.resolvePath(targetDirEntry, targetRelativePath, onTargetExists, 967 util.resolvePath(targetDirEntry, targetRelativePath, onTargetExists,
961 onTargetNotResolved); 968 onTargetNotResolved);
962 } 969 }
963 970
964 tryNextCopy(); 971 tryNextCopy();
965 }; 972 };
966 973
967 /** 974 /**
975 * Service a zip file creation task.
976 *
977 * @private
978 * @param {FileManager.Task} task A task.
979 * @param {Function} completeCallback On complete.
980 * @param {Function} errorCallback On error.
981 */
982 FileCopyManager.prototype.serviceZipTask_ = function(task, completeCallback,
983 errorCallback) {
984 var self = this;
985 var dirURL = task.sourceDirEntry.toURL();
986 var selectionURLs = [];
987 for (var i = 0; i < task.pendingDirectories.length; i++)
988 selectionURLs.push(task.pendingDirectories[i].toURL());
989 for (var i = 0; i < task.pendingFiles.length; i++)
990 selectionURLs.push(task.pendingFiles[i].toURL());
991
992 var destName = 'Archive';
993 if (task.originalEntries.length == 1) {
994 var entryPath = task.originalEntries[0].fullPath;
995 var i = entryPath.lastIndexOf('/');
996 var basename = (i < 0) ? entryPath : entryPath.substr(i + 1);
997 i = basename.lastIndexOf('.');
998 destName = ((i < 0) ? basename : basename.substr(0, i));
999 }
1000
1001 var copyNumber = 0;
1002 var firstExistingEntry = null;
1003 var destPath = destName + '.zip';
1004
1005 function onError(reason, data) {
1006 self.log_('serviceZipTask error: ' + reason + ':', data);
1007 errorCallback(new FileCopyManager.Error(reason, data));
1008 }
1009
1010 function onTargetExists(existingEntry) {
1011 if (copyNumber < 10) {
1012 if (!firstExistingEntry)
1013 firstExistingEntry = existingEntry;
1014 copyNumber++;
1015 tryZipSelection();
1016 } else {
1017 onError('TARGET_EXISTS', firstExistingEntry);
1018 }
1019 }
1020
1021 function onTargetNotResolved() {
1022 function onZipSelectionComplete() {
tbarzic 2012/11/10 03:40:56 what if zip failed? we should probably show an err
hshi1 2012/11/12 20:12:12 Done.
1023 self.sendProgressEvent_('SUCCESS');
1024 completeCallback(task);
1025 }
1026
1027 self.sendProgressEvent_('PROGRESS');
1028 chrome.fileBrowserPrivate.zipSelection(dirURL, selectionURLs, destPath,
1029 onZipSelectionComplete);
1030 }
1031
1032 function tryZipSelection() {
1033 if (copyNumber > 0)
1034 destPath = destName + ' (' + copyNumber + ').zip';
1035
1036 // Check if the target exists. This kicks off the rest of the zip file
1037 // creation if the target is not found, or raises an error if it does.
1038 util.resolvePath(task.targetDirEntry, destPath, onTargetExists,
1039 onTargetNotResolved);
1040 }
1041
1042 tryZipSelection();
1043 };
1044
1045 /**
968 * Copy the contents of sourceEntry into targetEntry. 1046 * Copy the contents of sourceEntry into targetEntry.
969 * 1047 *
970 * @private 1048 * @private
971 * @param {Entry} sourceEntry entry that will be copied. 1049 * @param {Entry} sourceEntry entry that will be copied.
972 * @param {Entry} targetEntry entry to which sourceEntry will be copied. 1050 * @param {Entry} targetEntry entry to which sourceEntry will be copied.
973 * @param {function(Entry, number)} progressCallback function that will be 1051 * @param {function(Entry, number)} progressCallback function that will be
974 * called when a part of the source entry is copied. It takes |targetEntry| 1052 * called when a part of the source entry is copied. It takes |targetEntry|
975 * and size of the last copied chunk as parameters. 1053 * and size of the last copied chunk as parameters.
976 * @param {function(Entry, number)} successCallback function that will be called 1054 * @param {function(Entry, number)} successCallback function that will be called
977 * the copy operation finishes. It takes |targetEntry| and size of the last 1055 * the copy operation finishes. It takes |targetEntry| and size of the last
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
1044 id: id, 1122 id: id,
1045 timeout: setTimeout(this.forceDeleteTask.bind(this, id), 1123 timeout: setTimeout(this.forceDeleteTask.bind(this, id),
1046 FileCopyManager.DELETE_TIMEOUT) 1124 FileCopyManager.DELETE_TIMEOUT)
1047 }; 1125 };
1048 this.deleteTasks_.push(task); 1126 this.deleteTasks_.push(task);
1049 callback(id); 1127 callback(id);
1050 this.sendDeleteEvent_(task, 'SCHEDULED'); 1128 this.sendDeleteEvent_(task, 'SCHEDULED');
1051 }; 1129 };
1052 1130
1053 /** 1131 /**
1132 * Creates a zip file for the selection of files.
1133 * @param {Entry} dirEntry the directory containing the selection.
1134 * @param {boolean} isOnGData If directory is on GDrive.
1135 * @param {Array.<Entry>} selectionEntries the selected entries.
1136 */
1137 FileCopyManager.prototype.zipSelection = function(dirEntry, isOnGData,
1138 selectionEntries) {
1139 var self = this;
1140 var zipTask = new FileCopyManager.Task(dirEntry, dirEntry);
1141 zipTask.zip = true;
1142 zipTask.sourceOnGData = isOnGData;
1143 zipTask.targetOnGData = isOnGData;
1144 zipTask.setEntries(selectionEntries, function() {
1145 // TODO: per-entry zip progress update with accurate byte count.
1146 // For now just set pendingBytes to zero so that the progress bar is full.
1147 zipTask.pendingBytes = 0;
1148 self.copyTasks_.push(zipTask);
1149 if (self.copyTasks_.length == 1) {
1150 // Assume self.cancelRequested_ == false.
1151 // This moved us from 0 to 1 active tasks, let the servicing begin!
1152 self.serviceAllTasks_();
1153 } else {
1154 // Force to update the progress of butter bar when there are new tasks
1155 // coming while servicing current task.
1156 self.sendProgressEvent_('PROGRESS');
1157 }
1158 });
1159 };
1160
1161 /**
1054 * Force deletion before timeout runs out. 1162 * Force deletion before timeout runs out.
1055 * @param {number} id The delete task id (as returned by deleteEntries). 1163 * @param {number} id The delete task id (as returned by deleteEntries).
1056 */ 1164 */
1057 FileCopyManager.prototype.forceDeleteTask = function(id) { 1165 FileCopyManager.prototype.forceDeleteTask = function(id) {
1058 var task = this.findDeleteTaskAndCancelTimeout_(id); 1166 var task = this.findDeleteTaskAndCancelTimeout_(id);
1059 if (task) this.serviceDeleteTask_(task); 1167 if (task) this.serviceDeleteTask_(task);
1060 }; 1168 };
1061 1169
1062 /** 1170 /**
1063 * Cancels the scheduled deletion. 1171 * Cancels the scheduled deletion.
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1120 */ 1228 */
1121 FileCopyManager.prototype.sendDeleteEvent_ = function(task, reason) { 1229 FileCopyManager.prototype.sendDeleteEvent_ = function(task, reason) {
1122 this.sendEvent_('delete', { 1230 this.sendEvent_('delete', {
1123 reason: reason, 1231 reason: reason,
1124 id: task.id, 1232 id: task.id,
1125 urls: task.entries.map(function(e) { 1233 urls: task.entries.map(function(e) {
1126 return util.makeFilesystemUrl(e.fullPath); 1234 return util.makeFilesystemUrl(e.fullPath);
1127 }) 1235 })
1128 }); 1236 });
1129 }; 1237 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698