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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
| 8 * Utilities for FileCopyManager. |
| 9 */ |
| 10 var fileOperationUtil = {}; |
| 11 |
| 12 /** |
| 13 * Simple wrapper for util.deduplicatePath. On error, this method translates |
| 14 * the FileError to FileCopyManager.Error object. |
| 15 * |
| 16 * @param {DirectoryEntry} dirEntry The target directory entry. |
| 17 * @param {string} relativePath The path to be deduplicated. |
| 18 * @param {function(string)} successCallback Callback run with the deduplicated |
| 19 * path on success. |
| 20 * @param {function(FileCopyManager.Error)} errorCallback Callback run on error. |
| 21 */ |
| 22 fileOperationUtil.deduplicatePath = function( |
| 23 dirEntry, relativePath, successCallback, errorCallback) { |
| 24 util.deduplicatePath( |
| 25 dirEntry, relativePath, successCallback, |
| 26 function(err) { |
| 27 var onFileSystemError = function(error) { |
| 28 errorCallback(new FileCopyManager.Error( |
| 29 util.FileOperationErrorType.FILESYSTEM_ERROR, error)); |
| 30 }; |
| 31 |
| 32 if (err.code == FileError.PATH_EXISTS_ERR) { |
| 33 // Failed to uniquify the file path. There should be an existing |
| 34 // entry, so return the error with it. |
| 35 util.resolvePath( |
| 36 dirEntry, relativePath, |
| 37 function(entry) { |
| 38 errorCallback(new FileCopyManager.Error( |
| 39 util.FileOperationErrorType.TARGET_EXISTS, entry)); |
| 40 }, |
| 41 onFileSystemError); |
| 42 return; |
| 43 } |
| 44 onFileSystemError(err); |
| 45 }); |
| 46 }; |
| 47 |
| 48 /** |
| 49 * Sets last modified date to the entry. |
| 50 * @param {Entry} entry The entry to which the last modified is set. |
| 51 * @param {Date} modificationTime The last modified time. |
| 52 */ |
| 53 fileOperationUtil.setLastModified = function(entry, modificationTime) { |
| 54 chrome.fileBrowserPrivate.setLastModified( |
| 55 entry.toURL(), '' + Math.round(modificationTime.getTime() / 1000)); |
| 56 }; |
| 57 |
| 58 /** |
| 59 * Thin wrapper of chrome.fileBrowserPrivate.zipSelection to adapt its |
| 60 * interface similar to copyTo(). |
| 61 * |
| 62 * @param {Array.<Entry>} sources The array of entries to be archived. |
| 63 * @param {DirectoryEntry} parent The entry of the destination directory. |
| 64 * @param {string} newName The name of the archive to be created. |
| 65 * @param {function(FileEntry)} successCallback Callback invoked when the |
| 66 * operation is successfully done with the entry of the created archive. |
| 67 * @param {function(FileError)} errorCallback Callback invoked when an error |
| 68 * is found. |
| 69 */ |
| 70 fileOperationUtil.zipSelection = function( |
| 71 sources, parent, newName, successCallback, errorCallback) { |
| 72 chrome.fileBrowserPrivate.zipSelection( |
| 73 parent.toURL(), |
| 74 sources.map(function(e) { return e.toURL(); }), |
| 75 newName, function(success) { |
| 76 if (!success) { |
| 77 // Failed to create a zip archive. |
| 78 errorCallback( |
| 79 util.createFileError(FileError.INVALID_MODIFICATION_ERR)); |
| 80 return; |
| 81 } |
| 82 |
| 83 // Returns the created entry via callback. |
| 84 parent.getFile( |
| 85 newName, {create: false}, successCallback, errorCallback); |
| 86 }); |
| 87 }; |
| 88 |
| 89 /** |
8 * @constructor | 90 * @constructor |
9 */ | 91 */ |
10 function FileCopyManager() { | 92 function FileCopyManager() { |
11 this.copyTasks_ = []; | 93 this.copyTasks_ = []; |
12 this.deleteTasks_ = []; | 94 this.deleteTasks_ = []; |
13 this.cancelObservers_ = []; | 95 this.cancelObservers_ = []; |
14 this.cancelRequested_ = false; | 96 this.cancelRequested_ = false; |
15 this.cancelCallback_ = null; | 97 this.cancelCallback_ = null; |
16 this.unloadTimeout_ = null; | 98 this.unloadTimeout_ = null; |
17 | 99 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 this.zip = false; | 211 this.zip = false; |
130 | 212 |
131 // If directory already exists, we try to make a copy named 'dir (X)', | 213 // If directory already exists, we try to make a copy named 'dir (X)', |
132 // where X is a number. When we do this, all subsequent copies from | 214 // where X is a number. When we do this, all subsequent copies from |
133 // inside the subtree should be mapped to the new directory name. | 215 // inside the subtree should be mapped to the new directory name. |
134 // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should | 216 // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should |
135 // become 'dir (1)\file.txt'. | 217 // become 'dir (1)\file.txt'. |
136 this.renamedDirectories_ = []; | 218 this.renamedDirectories_ = []; |
137 }; | 219 }; |
138 | 220 |
139 /** | |
140 * Simple wrapper for util.deduplicatePath. On error, this method translates | |
141 * the FileError to FileCopyManager.Error object. | |
142 * | |
143 * @param {DirectoryEntry} dirEntry The target directory entry. | |
144 * @param {string} relativePath The path to be deduplicated. | |
145 * @param {function(string)} successCallback Callback run with the deduplicated | |
146 * path on success. | |
147 * @param {function(FileCopyManager.Error)} errorCallback Callback run on error. | |
148 */ | |
149 FileCopyManager.Task.deduplicatePath = function( | |
150 dirEntry, relativePath, successCallback, errorCallback) { | |
151 util.deduplicatePath( | |
152 dirEntry, relativePath, successCallback, | |
153 function(err) { | |
154 var onFileSystemError = function(error) { | |
155 errorCallback(new FileCopyManager.Error( | |
156 util.FileOperationErrorType.FILESYSTEM_ERROR, error)); | |
157 }; | |
158 | |
159 if (err.code == FileError.PATH_EXISTS_ERR) { | |
160 // Failed to uniquify the file path. There should be an existing | |
161 // entry, so return the error with it. | |
162 util.resolvePath( | |
163 dirEntry, relativePath, | |
164 function(entry) { | |
165 errorCallback(new FileCopyManager.Error( | |
166 util.FileOperationErrorType.TARGET_EXISTS, entry)); | |
167 }, | |
168 onFileSystemError); | |
169 return; | |
170 } | |
171 onFileSystemError(err); | |
172 }); | |
173 }; | |
174 | 221 |
175 /** | 222 /** |
176 * @param {Array.<Entry>} entries Entries. | 223 * @param {Array.<Entry>} entries Entries. |
177 * @param {function()} callback When entries resolved. | 224 * @param {function()} callback When entries resolved. |
178 */ | 225 */ |
179 FileCopyManager.Task.prototype.setEntries = function(entries, callback) { | 226 FileCopyManager.Task.prototype.setEntries = function(entries, callback) { |
180 var self = this; | 227 var self = this; |
181 this.originalEntries = entries; | 228 this.originalEntries = entries; |
182 // When moving directories, FileEntry.moveTo() is used if both source | 229 // When moving directories, FileEntry.moveTo() is used if both source |
183 // and target are on Drive. There is no need to recurse into directories. | 230 // and target are on Drive. There is no need to recurse into directories. |
(...skipping 715 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
899 function(error) { | 946 function(error) { |
900 self.cancelCallback_ = null; | 947 self.cancelCallback_ = null; |
901 onFilesystemError(error); | 948 onFilesystemError(error); |
902 }); | 949 }); |
903 }, | 950 }, |
904 util.flog('Error getting file: ' + targetRelativePath, | 951 util.flog('Error getting file: ' + targetRelativePath, |
905 onFilesystemError)); | 952 onFilesystemError)); |
906 return; | 953 return; |
907 } | 954 } |
908 | 955 |
| 956 // TODO(hidehiko): Move following code to fileOperationUtil. |
| 957 |
909 // Sending a file from a) Drive to Drive, b) Drive to local or c) local to | 958 // Sending a file from a) Drive to Drive, b) Drive to local or c) local to |
910 // Drive. | 959 // Drive. |
911 var sourceFileUrl = sourceEntry.toURL(); | 960 var sourceFileUrl = sourceEntry.toURL(); |
912 var sourceFilePath = util.extractFilePath(sourceFileUrl); | 961 var sourceFilePath = util.extractFilePath(sourceFileUrl); |
913 | 962 |
914 // Progress callback. | 963 // Progress callback. |
915 // Because the uploading the file from local cache to Drive server will be | 964 // Because the uploading the file from local cache to Drive server will be |
916 // done as a part of background Drive file system sync, so for this copy | 965 // done as a part of background Drive file system sync, so for this copy |
917 // operation, what we need to take care about is only file downloading. | 966 // operation, what we need to take care about is only file downloading. |
918 var numTransferredBytes = 0; | 967 var numTransferredBytes = 0; |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
973 function(error) { | 1022 function(error) { |
974 self.cancelCallback_ = null; | 1023 self.cancelCallback_ = null; |
975 chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener( | 1024 chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener( |
976 onFileTransfersUpdated); | 1025 onFileTransfersUpdated); |
977 onFilesystemError(error); | 1026 onFilesystemError(error); |
978 }); | 1027 }); |
979 }, | 1028 }, |
980 onFilesystemError); | 1029 onFilesystemError); |
981 }; | 1030 }; |
982 | 1031 |
983 FileCopyManager.Task.deduplicatePath( | 1032 fileOperationUtil.deduplicatePath( |
984 targetDirEntry, originalPath, onDeduplicated, errorCallback); | 1033 targetDirEntry, originalPath, onDeduplicated, errorCallback); |
985 }; | 1034 }; |
986 | 1035 |
987 /** | 1036 /** |
988 * Copies the contents of sourceEntry into targetEntry. | 1037 * Copies the contents of sourceEntry into targetEntry. |
| 1038 * TODO(hidehiko): Move this method into fileOperationUtil. |
989 * | 1039 * |
990 * @param {FileEntry} sourceEntry The file entry that will be copied. | 1040 * @param {FileEntry} sourceEntry The file entry that will be copied. |
991 * @param {FileEntry} targetEntry The file entry to which sourceEntry will be | 1041 * @param {FileEntry} targetEntry The file entry to which sourceEntry will be |
992 * copied. | 1042 * copied. |
993 * @param {function(FileEntry, number)} progressCallback Function that will be | 1043 * @param {function(FileEntry, number)} progressCallback Function that will be |
994 * called when a part of the source entry is copied. It takes |targetEntry| | 1044 * called when a part of the source entry is copied. It takes |targetEntry| |
995 * and size of the last copied chunk as parameters. | 1045 * and size of the last copied chunk as parameters. |
996 * @param {function(FileEntry, number)} successCallback Function that will be | 1046 * @param {function(FileEntry, number)} successCallback Function that will be |
997 * called the copy operation finishes. It takes |targetEntry| and size of | 1047 * called the copy operation finishes. It takes |targetEntry| and size of |
998 * the last (not previously reported) copied chunk as parameters. | 1048 * the last (not previously reported) copied chunk as parameters. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1052 errorCallback(util.createFileError(FileError.ABORT_ERR)); | 1102 errorCallback(util.createFileError(FileError.ABORT_ERR)); |
1053 return; | 1103 return; |
1054 } | 1104 } |
1055 | 1105 |
1056 sourceEntry.getMetadata(function(metadata) { | 1106 sourceEntry.getMetadata(function(metadata) { |
1057 if (cancelRequested) { | 1107 if (cancelRequested) { |
1058 errorCallback(util.createFileError(FileError.ABORT_ERR)); | 1108 errorCallback(util.createFileError(FileError.ABORT_ERR)); |
1059 return; | 1109 return; |
1060 } | 1110 } |
1061 | 1111 |
1062 chrome.fileBrowserPrivate.setLastModified( | 1112 fileOperationUtil.setLastModified( |
1063 targetEntry.toURL(), | 1113 targetEntry, metadata.modificationTime); |
1064 '' + Math.round(metadata.modificationTime.getTime() / 1000)); | |
1065 successCallback(targetEntry, file.size - reportedProgress); | 1114 successCallback(targetEntry, file.size - reportedProgress); |
1066 }); | 1115 }); |
1067 }; | 1116 }; |
1068 | 1117 |
1069 writer.write(file); | 1118 writer.write(file); |
1070 }, errorCallback); | 1119 }, errorCallback); |
1071 }, errorCallback); | 1120 }, errorCallback); |
1072 | 1121 |
1073 return function() { | 1122 return function() { |
1074 cancelRequested = true; | 1123 cancelRequested = true; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1144 var sourcePath = sourceEntry.originalSourcePath; | 1193 var sourcePath = sourceEntry.originalSourcePath; |
1145 if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) { | 1194 if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) { |
1146 // We found an entry in the list that is not relative to the base source | 1195 // We found an entry in the list that is not relative to the base source |
1147 // path, something is wrong. | 1196 // path, something is wrong. |
1148 errorCallback(new FileCopyManager.Error( | 1197 errorCallback(new FileCopyManager.Error( |
1149 util.FileOperationErrorType.UNEXPECTED_SOURCE_FILE, | 1198 util.FileOperationErrorType.UNEXPECTED_SOURCE_FILE, |
1150 sourceEntry.fullPath)); | 1199 sourceEntry.fullPath)); |
1151 return; | 1200 return; |
1152 } | 1201 } |
1153 | 1202 |
1154 FileCopyManager.Task.deduplicatePath( | 1203 fileOperationUtil.deduplicatePath( |
1155 task.targetDirEntry, | 1204 task.targetDirEntry, |
1156 task.applyRenames(sourceEntry.fullPath.substr(sourcePath.length + 1)), | 1205 task.applyRenames(sourceEntry.fullPath.substr(sourcePath.length + 1)), |
1157 function(targetRelativePath) { | 1206 function(targetRelativePath) { |
1158 var onFilesystemError = function(err) { | 1207 var onFilesystemError = function(err) { |
1159 errorCallback(new FileCopyManager.Error( | 1208 errorCallback(new FileCopyManager.Error( |
1160 util.FileOperationErrorType.FILESYSTEM_ERROR, | 1209 util.FileOperationErrorType.FILESYSTEM_ERROR, |
1161 err)); | 1210 err)); |
1162 }; | 1211 }; |
1163 | 1212 |
1164 task.targetDirEntry.getDirectory( | 1213 task.targetDirEntry.getDirectory( |
(...skipping 24 matching lines...) Expand all Loading... |
1189 * invoked when an entry is changed. | 1238 * invoked when an entry is changed. |
1190 * @param {function()} progressCallback Callback invoked periodically during | 1239 * @param {function()} progressCallback Callback invoked periodically during |
1191 * the moving. | 1240 * the moving. |
1192 * @param {function()} successCallback On complete. | 1241 * @param {function()} successCallback On complete. |
1193 * @param {function(FileCopyManager.Error)} errorCallback On error. | 1242 * @param {function(FileCopyManager.Error)} errorCallback On error. |
1194 * @private | 1243 * @private |
1195 */ | 1244 */ |
1196 FileCopyManager.prototype.serviceZipTask_ = function( | 1245 FileCopyManager.prototype.serviceZipTask_ = function( |
1197 task, entryChangedCallback, progressCallback, successCallback, | 1246 task, entryChangedCallback, progressCallback, successCallback, |
1198 errorCallback) { | 1247 errorCallback) { |
1199 var dirURL = task.zipBaseDirEntry.toURL(); | |
1200 var selectionURLs = []; | |
1201 for (var i = 0; i < task.pendingDirectories.length; i++) | |
1202 selectionURLs.push(task.pendingDirectories[i].toURL()); | |
1203 for (var i = 0; i < task.pendingFiles.length; i++) | |
1204 selectionURLs.push(task.pendingFiles[i].toURL()); | |
1205 | |
1206 // TODO(hidehiko): we should localize the name. | 1248 // TODO(hidehiko): we should localize the name. |
1207 var destName = 'Archive'; | 1249 var destName = 'Archive'; |
1208 if (task.originalEntries.length == 1) { | 1250 if (task.originalEntries.length == 1) { |
1209 var entryPath = task.originalEntries[0].fullPath; | 1251 var entryPath = task.originalEntries[0].fullPath; |
1210 var i = entryPath.lastIndexOf('/'); | 1252 var i = entryPath.lastIndexOf('/'); |
1211 var basename = (i < 0) ? entryPath : entryPath.substr(i + 1); | 1253 var basename = (i < 0) ? entryPath : entryPath.substr(i + 1); |
1212 i = basename.lastIndexOf('.'); | 1254 i = basename.lastIndexOf('.'); |
1213 destName = ((i < 0) ? basename : basename.substr(0, i)); | 1255 destName = ((i < 0) ? basename : basename.substr(0, i)); |
1214 } | 1256 } |
1215 | 1257 |
1216 var onDeduplicated = function(destPath) { | 1258 fileOperationUtil.deduplicatePath( |
1217 var onZipSelectionComplete = function(success) { | 1259 task.targetDirEntry, destName + '.zip', |
1218 var onFilesystemError = function(err) { | 1260 function(destPath) { |
1219 errorCallback(new FileCopyManager.Error( | 1261 progressCallback(); |
1220 util.FileOperationErrorType.FILESYSTEM_ERROR, | |
1221 err)); | |
1222 }; | |
1223 | 1262 |
1224 if (success) { | 1263 fileOperationUtil.zipSelection( |
1225 task.targetDirEntry.getFile( | 1264 task.pendingDirectories.concat(task.pendingFiles), |
1226 destPath, {create: false}, | 1265 task.zipBaseDirEntry, |
| 1266 destPath, |
1227 function(entry) { | 1267 function(entry) { |
1228 entryChangedCallback(util.EntryChangedType.CREATE, entry); | 1268 entryChangedCallback(util.EntryChangedType.CREATE, entry); |
1229 successCallback(); | 1269 successCallback(); |
1230 }, | 1270 }, |
1231 onFilesystemError); | 1271 function(error) { |
1232 } else { | 1272 errorCallback(new FileCopyManager.Error( |
1233 onFilesystemError( | 1273 util.FileOperationErrorType.FILESYSTEM_ERROR, error)); |
1234 util.createFileError(FileError.INVALID_MODIFICATION_ERR)); | 1274 }); |
1235 } | 1275 }, |
1236 }; | 1276 errorCallback); |
1237 | |
1238 progressCallback(); | |
1239 chrome.fileBrowserPrivate.zipSelection(dirURL, selectionURLs, destPath, | |
1240 onZipSelectionComplete); | |
1241 }; | |
1242 | |
1243 FileCopyManager.Task.deduplicatePath( | |
1244 task.targetDirEntry, destName + '.zip', onDeduplicated, errorCallback); | |
1245 }; | 1277 }; |
1246 | 1278 |
1247 /** | 1279 /** |
1248 * Timeout before files are really deleted (to allow undo). | 1280 * Timeout before files are really deleted (to allow undo). |
1249 */ | 1281 */ |
1250 FileCopyManager.DELETE_TIMEOUT = 30 * 1000; | 1282 FileCopyManager.DELETE_TIMEOUT = 30 * 1000; |
1251 | 1283 |
1252 /** | 1284 /** |
1253 * Schedules the files deletion. | 1285 * Schedules the files deletion. |
1254 * | 1286 * |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1371 * @param {Array.<Entry>} selectionEntries The selected entries. | 1403 * @param {Array.<Entry>} selectionEntries The selected entries. |
1372 */ | 1404 */ |
1373 FileCopyManager.prototype.zipSelection = function(dirEntry, selectionEntries) { | 1405 FileCopyManager.prototype.zipSelection = function(dirEntry, selectionEntries) { |
1374 var self = this; | 1406 var self = this; |
1375 var zipTask = new FileCopyManager.Task(dirEntry, dirEntry); | 1407 var zipTask = new FileCopyManager.Task(dirEntry, dirEntry); |
1376 zipTask.zip = true; | 1408 zipTask.zip = true; |
1377 zipTask.setEntries(selectionEntries, function() { | 1409 zipTask.setEntries(selectionEntries, function() { |
1378 // TODO: per-entry zip progress update with accurate byte count. | 1410 // TODO: per-entry zip progress update with accurate byte count. |
1379 // For now just set completedBytes to same value as totalBytes so that the | 1411 // For now just set completedBytes to same value as totalBytes so that the |
1380 // progress bar is full. | 1412 // progress bar is full. |
1381 zipTask.completedBytes = zip.Task.totalBytes; | 1413 zipTask.completedBytes = zipTask.totalBytes; |
1382 self.copyTasks_.push(zipTask); | 1414 self.copyTasks_.push(zipTask); |
1383 if (self.copyTasks_.length == 1) { | 1415 if (self.copyTasks_.length == 1) { |
1384 // Assume self.cancelRequested_ == false. | 1416 // Assume self.cancelRequested_ == false. |
1385 // This moved us from 0 to 1 active tasks, let the servicing begin! | 1417 // This moved us from 0 to 1 active tasks, let the servicing begin! |
1386 self.serviceAllTasks_(); | 1418 self.serviceAllTasks_(); |
1387 } else { | 1419 } else { |
1388 // Force to update the progress of butter bar when there are new tasks | 1420 // Force to update the progress of butter bar when there are new tasks |
1389 // coming while servicing current task. | 1421 // coming while servicing current task. |
1390 self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); | 1422 self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); |
1391 } | 1423 } |
1392 }); | 1424 }); |
1393 }; | 1425 }; |
OLD | NEW |