Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 5 | 'use strict'; |
| 6 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 7 | /** |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 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 | /** |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 59 | * Copies a file from source to the parent directory with newName. |
| 60 | * See also copyFileByStream_ and copyFileOnDrive_ for the implementation |
| 61 | * details. |
| 62 | * |
| 63 | * @param {FileEntry} source The file entry to be copied. |
| 64 | * @param {DirectoryEntry} parent The entry of the destination directory. |
| 65 | * @param {string} newName The name of copied file. |
| 66 | * @param {function(FileEntry, number)} progressCallback Callback invoked |
| 67 | * periodically during the file writing. It takes source and the number of |
| 68 | * copied bytes since the last invocation. This is also called just before |
| 69 | * starting the operation (with '0' bytes) and just after the finishing the |
| 70 | * operation (with the total copied size). |
| 71 | * @param {function(FileEntry)} successCallback Callback invoked when the copy |
| 72 | * is successfully done with the entry of the created file. |
| 73 | * @param {function(FileError)} errorCallback Callback invoked when an error |
| 74 | * is found. |
| 75 | * @return {function()} Callback to cancle the current file copy operation. |
| 76 | * When the cancel is done, errorCallback will be called. The returned |
| 77 | * callback must not be called more than once. |
| 78 | */ |
| 79 | fileOperationUtil.copyFile = function( |
| 80 | source, parent, newName, progressCallback, successCallback, errorCallback) { |
| 81 | if (!PathUtil.isDriveBasedPath(source.fullPath) && |
| 82 | !PathUtil.isDriveBasedPath(parent.fullPath)) { |
| 83 | // Copying a file between non-Drive file systems. |
| 84 | return fileOperationUtil.copyFileByStream_( |
| 85 | source, parent, newName, progressCallback, successCallback, |
| 86 | errorCallback); |
| 87 | } else { |
| 88 | // Copying related to the Drive file system. |
| 89 | return fileOperationUtil.copyFileOnDrive_( |
| 90 | source, parent, newName, progressCallback, successCallback, |
| 91 | errorCallback); |
| 92 | } |
| 93 | }; |
| 94 | |
| 95 | /** |
| 96 | * Copies a file by using File and FileWriter objects. |
| 97 | * |
| 98 | * This is a js-implementation of FileEntry.copyTo(). Unfortunately, copyTo |
| 99 | * doesn't support periodical progress updating nor cancelling. To support |
| 100 | * these operations, this method implements copyTo by streaming way in |
| 101 | * JavaScript. |
| 102 | * |
| 103 | * Note that this is designed for file copying on local file system. We have |
| 104 | * some special cases about copying on Drive file system. See also |
| 105 | * copyFileOnDrive_() for more details. |
| 106 | * |
| 107 | * @param {FileEntry} source The file entry to be copied. |
| 108 | * @param {DirectoryEntry} parent The entry of the destination directory. |
| 109 | * @param {string} newName The name of copied file. |
| 110 | * @param {function(FileEntry, number)} progressCallback Callback invoked |
| 111 | * periodically during the file writing. It takes source and the number of |
| 112 | * copied bytes since the last invocation. This is also called just before |
| 113 | * starting the operation (with '0' bytes) and just after the finishing the |
| 114 | * operation (with the total copied size). |
| 115 | * @param {function(FileEntry)} successCallback Callback invoked when the copy |
| 116 | * is successfully done with the entry of the created file. |
| 117 | * @param {function(FileError)} errorCallback Callback invoked when an error |
| 118 | * is found. |
| 119 | * @return {function()} Callback to cancel the current file copy operation. |
| 120 | * When the cancel is done, errorCallback will be called. The returned |
| 121 | * callback must not be called more than once. |
| 122 | * @private |
| 123 | */ |
| 124 | fileOperationUtil.copyFileByStream_ = function( |
| 125 | source, parent, newName, progressCallback, successCallback, errorCallback) { |
| 126 | // Set to true when cancel is requested. |
| 127 | var cancelRequested = false; |
| 128 | |
| 129 | source.file(function(file) { |
| 130 | if (cancelRequested) { |
| 131 | errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | parent.getFile(newName, {create: true, exclusive: true}, function(target) { |
| 136 | if (cancelRequested) { |
| 137 | errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | target.createWriter(function(writer) { |
| 142 | if (cancelRequested) { |
| 143 | errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 144 | return; |
| 145 | } |
| 146 | |
| 147 | writer.onerror = writer.onabort = function(progress) { |
| 148 | errorCallback(cancelRequested ? |
| 149 | util.createFileError(FileError.ABORT_ERR) : |
| 150 | writer.error); |
| 151 | }; |
| 152 | |
| 153 | var reportedProgress = 0; |
| 154 | writer.onprogress = function(progress) { |
| 155 | if (cancelRequested) { |
| 156 | // If the copy was cancelled, we should abort the operation. |
| 157 | // The errorCallback will be called by writer.onabort after the |
| 158 | // termination. |
| 159 | writer.abort(); |
| 160 | return; |
| 161 | } |
| 162 | |
| 163 | // |progress.loaded| will contain total amount of data copied by now. |
| 164 | // |progressCallback| expects data amount delta from the last progress |
| 165 | // update. |
| 166 | progressCallback(target, progress.loaded - reportedProgress); |
| 167 | reportedProgress = progress.loaded; |
| 168 | }; |
| 169 | |
| 170 | writer.onwrite = function() { |
| 171 | if (cancelRequested) { |
| 172 | errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 173 | return; |
| 174 | } |
| 175 | |
| 176 | source.getMetadata(function(metadata) { |
| 177 | if (cancelRequested) { |
| 178 | errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | fileOperationUtil.setLastModified( |
| 183 | target, metadata.modificationTime); |
| 184 | successCallback(target); |
| 185 | }, errorCallback); |
| 186 | }; |
| 187 | |
| 188 | writer.write(file); |
| 189 | }, errorCallback); |
| 190 | }, errorCallback); |
| 191 | }, errorCallback); |
| 192 | |
| 193 | return function() { cancelRequested = true; }; |
| 194 | }; |
| 195 | |
| 196 | /** |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 197 | * Copies a file a) from Drive to local, b) from local to Drive, or c) from |
| 198 | * Drive to Drive. |
| 199 | * Currently, we need to take care about following two things for Drive: |
| 200 | * |
| 201 | * 1) Copying hosted document. |
| 202 | * In theory, it is impossible to actual copy a hosted document to other |
| 203 | * file system. Thus, instead, Drive file system backend creates a JSON file |
| 204 | * referring to the hosted document. Also, when it is uploaded by copyTo, |
| 205 | * the hosted document is copied on the server. Note that, this doesn't work |
| 206 | * when a user creates a file by FileWriter (as copyFileEntry_ does). |
| 207 | * |
| 208 | * 2) File transfer between local and Drive server. |
| 209 | * There are two directions of file transfer; from local to Drive and from |
| 210 | * Drive to local. |
| 211 | * The file transfer from local to Drive is done as a part of file system |
| 212 | * background sync (kicked after the copy operation is done). So we don't need |
| 213 | * to take care about it here. To copy the file from Drive to local (or Drive |
| 214 | * to Drive with GData WAPI), we need to download the file content (if it is |
| 215 | * not locally cached). During the downloading, we can listen the periodical |
| 216 | * updating and cancel the downloding via private API. |
| 217 | * |
| 218 | * This function supports progress updating and cancelling partially. |
| 219 | * Unfortunately, FileEntry.copyTo doesn't support progress updating nor |
| 220 | * cancelling, so we support them only during file downloading. |
| 221 | * |
| 222 | * Note: we're planning to move copyTo logic into c++ side. crbug.com/261492 |
| 223 | * |
| 224 | * @param {FileEntry} source The entry of the file to be copied. |
| 225 | * @param {DirectoryEntry} parent The entry of the destination directory. |
| 226 | * @param {string} newName The name of the copied file. |
| 227 | * @param {function(FileEntry, number)} progressCallback Callback periodically |
| 228 | * invoked during file transfer with the source and the number of |
| 229 | * transferred bytes from the last call. |
| 230 | * @param {function(FileEntry)} successCallback Callback invoked when the |
| 231 | * file copy is successfully done with the entry of the copied file. |
| 232 | * @param {function(FileError)} errorCallback Callback invoked when an error |
| 233 | * is found. |
| 234 | * @return {function()} Callback to cancel the current file copy operation. |
| 235 | * When the cancel is done, errorCallback will be called. The returned |
| 236 | * callback must not be called more than once. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 237 | * @private |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 238 | */ |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 239 | fileOperationUtil.copyFileOnDrive_ = function( |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 240 | source, parent, newName, progressCallback, successCallback, errorCallback) { |
| 241 | // Set to true when cancel is requested. |
| 242 | var cancelRequested = false; |
| 243 | var cancelCallback = null; |
| 244 | |
| 245 | var onCopyToCompleted = null; |
| 246 | |
| 247 | // Progress callback. |
| 248 | // Because the uploading the file from local cache to Drive server will be |
| 249 | // done as a part of background Drive file system sync, so for this copy |
| 250 | // operation, what we need to take care about is only file downloading. |
| 251 | var numTransferredBytes = 0; |
| 252 | if (PathUtil.isDriveBasedPath(source.fullPath)) { |
| 253 | var sourceUrl = source.toURL(); |
| 254 | var sourcePath = util.extractFilePath(sourceUrl); |
| 255 | var onFileTransfersUpdated = function(statusList) { |
| 256 | for (var i = 0; i < statusList.length; i++) { |
| 257 | var status = statusList[i]; |
| 258 | |
| 259 | // Comparing urls is unreliable, since they may use different |
| 260 | // url encoding schemes (eg. rfc2396 vs. rfc3986). |
| 261 | var filePath = util.extractFilePath(status.fileUrl); |
| 262 | if (filePath == sourcePath) { |
| 263 | var processed = status.processed; |
| 264 | if (processed > numTransferredBytes) { |
| 265 | progressCallback(source, processed - numTransferredBytes); |
| 266 | numTransferredBytes = processed; |
| 267 | } |
| 268 | return; |
| 269 | } |
| 270 | } |
| 271 | }; |
| 272 | |
| 273 | // Subscribe to listen file transfer updating notifications. |
| 274 | chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener( |
| 275 | onFileTransfersUpdated); |
| 276 | |
| 277 | // Currently, we do NOT upload the file during the copy operation. |
| 278 | // It will be done as a part of file system sync after copy operation. |
| 279 | // So, we can cancel only file downloading. |
| 280 | cancelCallback = function() { |
| 281 | chrome.fileBrowserPrivate.cancelFileTransfers( |
| 282 | [sourceUrl], function() {}); |
| 283 | }; |
| 284 | |
| 285 | // We need to clean up on copyTo completion regardless if it is |
| 286 | // successfully done or not. |
| 287 | onCopyToCompleted = function() { |
| 288 | cancelCallback = null; |
| 289 | chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener( |
| 290 | onFileTransfersUpdated); |
| 291 | }; |
| 292 | } |
| 293 | |
| 294 | source.copyTo( |
| 295 | parent, newName, |
| 296 | function(entry) { |
| 297 | if (onCopyToCompleted) |
| 298 | onCopyToCompleted(); |
| 299 | |
| 300 | if (cancelRequested) { |
| 301 | errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 302 | return; |
| 303 | } |
| 304 | |
| 305 | entry.getMetadata(function(metadata) { |
| 306 | if (metadata.size > numTransferredBytes) |
| 307 | progressCallback(source, metadata.size - numTransferredBytes); |
| 308 | successCallback(entry); |
| 309 | }, errorCallback); |
| 310 | }, |
| 311 | function(error) { |
| 312 | if (onCopyToCompleted) |
| 313 | onCopyToCompleted(); |
| 314 | |
| 315 | errorCallback(error); |
| 316 | }); |
| 317 | |
| 318 | return function() { |
| 319 | cancelRequested = true; |
| 320 | if (cancelCallback) { |
| 321 | cancelCallback(); |
| 322 | cancelCallback = null; |
| 323 | } |
| 324 | }; |
| 325 | }; |
| 326 | |
| 327 | /** |
| 328 | * Thin wrapper of chrome.fileBrowserPrivate.zipSelection to adapt its |
| 329 | * interface similar to copyTo(). |
| 330 | * |
| 331 | * @param {Array.<Entry>} sources The array of entries to be archived. |
| 332 | * @param {DirectoryEntry} parent The entry of the destination directory. |
| 333 | * @param {string} newName The name of the archive to be created. |
| 334 | * @param {function(FileEntry)} successCallback Callback invoked when the |
| 335 | * operation is successfully done with the entry of the created archive. |
| 336 | * @param {function(FileError)} errorCallback Callback invoked when an error |
| 337 | * is found. |
| 338 | */ |
| 339 | fileOperationUtil.zipSelection = function( |
| 340 | sources, parent, newName, successCallback, errorCallback) { |
| 341 | chrome.fileBrowserPrivate.zipSelection( |
| 342 | parent.toURL(), |
| 343 | sources.map(function(e) { return e.toURL(); }), |
| 344 | newName, function(success) { |
| 345 | if (!success) { |
| 346 | // Failed to create a zip archive. |
| 347 | errorCallback( |
| 348 | util.createFileError(FileError.INVALID_MODIFICATION_ERR)); |
| 349 | return; |
| 350 | } |
| 351 | |
| 352 | // Returns the created entry via callback. |
| 353 | parent.getFile( |
| 354 | newName, {create: false}, successCallback, errorCallback); |
| 355 | }); |
| 356 | }; |
| 357 | |
| 358 | /** |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 359 | * @constructor |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 360 | */ |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 361 | function FileCopyManager() { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 362 | this.copyTasks_ = []; |
| 363 | this.deleteTasks_ = []; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 364 | this.cancelObservers_ = []; |
| 365 | this.cancelRequested_ = false; |
| 366 | this.cancelCallback_ = null; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 367 | this.unloadTimeout_ = null; |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 368 | |
| 369 | this.eventRouter_ = new FileCopyManager.EventRouter(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 370 | } |
| 371 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 372 | /** |
| 373 | * Get FileCopyManager instance. In case is hasn't been initialized, a new |
| 374 | * instance is created. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 375 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 376 | * @return {FileCopyManager} A FileCopyManager instance. |
| 377 | */ |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 378 | FileCopyManager.getInstance = function() { |
| 379 | if (!FileCopyManager.instance_) |
| 380 | FileCopyManager.instance_ = new FileCopyManager(); |
| 381 | |
| 382 | return FileCopyManager.instance_; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 383 | }; |
| 384 | |
| 385 | /** |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 386 | * Manages cr.Event dispatching. |
| 387 | * Currently this can send three types of events: "copy-progress", |
| 388 | * "copy-operation-completed" and "delete". |
| 389 | * |
| 390 | * TODO(hidehiko): Reorganize the event dispatching mechanism. |
| 391 | * @constructor |
| 392 | * @extends {cr.EventTarget} |
| 393 | */ |
| 394 | FileCopyManager.EventRouter = function() { |
| 395 | }; |
| 396 | |
| 397 | /** |
| 398 | * Extends cr.EventTarget. |
| 399 | */ |
| 400 | FileCopyManager.EventRouter.prototype.__proto__ = cr.EventTarget.prototype; |
| 401 | |
| 402 | /** |
| 403 | * Dispatches a simple "copy-progress" event with reason and current |
| 404 | * FileCopyManager status. If it is an ERROR event, error should be set. |
| 405 | * |
| 406 | * @param {string} reason Event type. One of "BEGIN", "PROGRESS", "SUCCESS", |
| 407 | * "ERROR" or "CANCELLED". TODO(hidehiko): Use enum. |
| 408 | * @param {Object} status Current FileCopyManager's status. See also |
| 409 | * FileCopyManager.getStatus(). |
| 410 | * @param {FileCopyManager.Error=} opt_error The info for the error. This |
| 411 | * should be set iff the reason is "ERROR". |
| 412 | */ |
| 413 | FileCopyManager.EventRouter.prototype.sendProgressEvent = function( |
| 414 | reason, status, opt_error) { |
| 415 | var event = new cr.Event('copy-progress'); |
| 416 | event.reason = reason; |
| 417 | event.status = status; |
| 418 | if (opt_error) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 419 | event.error = opt_error; |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 420 | this.dispatchEvent(event); |
| 421 | }; |
| 422 | |
| 423 | /** |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 424 | * Dispatches an event to notify that an entry is changed (created or deleted). |
| 425 | * @param {util.EntryChangedType} type The enum to represent if the entry |
| 426 | * is created or deleted. |
| 427 | * @param {Entry} entry The changed entry. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 428 | */ |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 429 | FileCopyManager.EventRouter.prototype.sendEntryChangedEvent = function( |
| 430 | type, entry) { |
| 431 | var event = new cr.Event('entry-changed'); |
| 432 | event.type = type; |
| 433 | event.entry = entry; |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 434 | this.dispatchEvent(event); |
| 435 | }; |
| 436 | |
| 437 | /** |
| 438 | * Dispatches an event to notify entries are changed for delete task. |
| 439 | * |
| 440 | * @param {string} reason Event type. One of "BEGIN", "PROGRESS", "SUCCESS", |
| 441 | * or "ERROR". TODO(hidehiko): Use enum. |
| 442 | * @param {Array.<string>} urls An array of URLs which are affected by delete |
| 443 | * operation. |
| 444 | */ |
| 445 | FileCopyManager.EventRouter.prototype.sendDeleteEvent = function( |
| 446 | reason, urls) { |
| 447 | var event = new cr.Event('delete'); |
| 448 | event.reason = reason; |
| 449 | event.urls = urls; |
| 450 | this.dispatchEvent(event); |
| 451 | }; |
| 452 | |
| 453 | /** |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 454 | * A record of a queued copy operation. |
| 455 | * |
| 456 | * Multiple copy operations may be queued at any given time. Additional |
| 457 | * Tasks may be added while the queue is being serviced. Though a |
| 458 | * cancel operation cancels everything in the queue. |
| 459 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 460 | * @param {DirectoryEntry} targetDirEntry Target directory. |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 461 | * @param {DirectoryEntry=} opt_zipBaseDirEntry Base directory dealt as a root |
| 462 | * in ZIP archive. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 463 | * @constructor |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 464 | */ |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 465 | FileCopyManager.Task = function(targetDirEntry, opt_zipBaseDirEntry) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 466 | this.targetDirEntry = targetDirEntry; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 467 | this.zipBaseDirEntry = opt_zipBaseDirEntry; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 468 | this.originalEntries = null; |
| 469 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 470 | // TODO(hidehiko): When we support recursive copy, we should be able to |
| 471 | // rely on originalEntries. Then remove this. |
| 472 | this.entries = []; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 473 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 474 | /** |
| 475 | * The index of entries being processed. The entries should be processed |
| 476 | * from 0, so this is also the number of completed entries. |
| 477 | * @type {number} |
| 478 | */ |
| 479 | this.entryIndex = 0; |
| 480 | this.totalBytes = 0; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 481 | this.completedBytes = 0; |
| 482 | |
| 483 | this.deleteAfterCopy = false; |
| 484 | this.move = false; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 485 | this.zip = false; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 486 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 487 | // TODO(hidehiko): After we support recursive copy, we don't need this. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 488 | // If directory already exists, we try to make a copy named 'dir (X)', |
| 489 | // where X is a number. When we do this, all subsequent copies from |
| 490 | // inside the subtree should be mapped to the new directory name. |
| 491 | // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should |
| 492 | // become 'dir (1)\file.txt'. |
| 493 | this.renamedDirectories_ = []; |
| 494 | }; |
| 495 | |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 496 | /** |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 497 | * @param {Array.<Entry>} entries Entries. |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 498 | * @param {function()} callback When entries resolved. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 499 | */ |
| 500 | FileCopyManager.Task.prototype.setEntries = function(entries, callback) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 501 | this.originalEntries = entries; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 502 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 503 | // When moving directories, FileEntry.moveTo() is used if both source |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 504 | // and target are on Drive. There is no need to recurse into directories. |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 505 | util.recurseAndResolveEntries(entries, !this.move, function(result) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 506 | if (this.move) { |
| 507 | // This may be moving from search results, where it fails if we move |
| 508 | // parent entries earlier than child entries. We should process the |
| 509 | // deepest entry first. Since move of each entry is done by a single |
| 510 | // moveTo() call, we don't need to care about the recursive traversal |
| 511 | // order. |
| 512 | this.entries = result.dirEntries.concat(result.fileEntries).sort( |
| 513 | function(entry1, entry2) { |
| 514 | return entry2.fullPath.length - entry1.fullPath.length; |
| 515 | }); |
| 516 | } else { |
| 517 | // Copying tasks are recursively processed. So, directories must be |
| 518 | // processed earlier than their child files. Since |
| 519 | // util.recurseAndResolveEntries is already listing entries in the |
| 520 | // recursive traversal order, we just keep the ordering. |
| 521 | this.entries = result.dirEntries.concat(result.fileEntries); |
| 522 | } |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 523 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 524 | this.totalBytes = result.fileBytes; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 525 | callback(); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 526 | }.bind(this)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 527 | }; |
| 528 | |
| 529 | /** |
| 530 | * Updates copy progress status for the entry. |
| 531 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 532 | * @param {number} size Number of bytes that has been copied since last update. |
| 533 | */ |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 534 | FileCopyManager.Task.prototype.updateFileCopyProgress = function(size) { |
| 535 | this.completedBytes += size; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 536 | }; |
| 537 | |
| 538 | /** |
| 539 | * @param {string} fromName Old name. |
| 540 | * @param {string} toName New name. |
| 541 | */ |
| 542 | FileCopyManager.Task.prototype.registerRename = function(fromName, toName) { |
| 543 | this.renamedDirectories_.push({from: fromName + '/', to: toName + '/'}); |
| 544 | }; |
| 545 | |
| 546 | /** |
| 547 | * @param {string} path A path. |
| 548 | * @return {string} Path after renames. |
| 549 | */ |
| 550 | FileCopyManager.Task.prototype.applyRenames = function(path) { |
| 551 | // Directories are processed in pre-order, so we will store only the first |
| 552 | // renaming point: |
| 553 | // x -> x (1) -- new directory created. |
| 554 | // x\y -> x (1)\y -- no more renames inside the new directory, so |
| 555 | // this one will not be stored. |
| 556 | // x\y\a.txt -- only one rename will be applied. |
| 557 | for (var index = 0; index < this.renamedDirectories_.length; ++index) { |
| 558 | var rename = this.renamedDirectories_[index]; |
| 559 | if (path.indexOf(rename.from) == 0) { |
| 560 | path = rename.to + path.substr(rename.from.length); |
| 561 | } |
| 562 | } |
| 563 | return path; |
| 564 | }; |
| 565 | |
| 566 | /** |
| 567 | * Error class used to report problems with a copy operation. |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 568 | * If the code is UNEXPECTED_SOURCE_FILE, data should be a path of the file. |
| 569 | * If the code is TARGET_EXISTS, data should be the existing Entry. |
| 570 | * If the code is FILESYSTEM_ERROR, data should be the FileError. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 571 | * |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 572 | * @param {util.FileOperationErrorType} code Error type. |
| 573 | * @param {string|Entry|FileError} data Additional data. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 574 | * @constructor |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 575 | */ |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 576 | FileCopyManager.Error = function(code, data) { |
| 577 | this.code = code; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 578 | this.data = data; |
| 579 | }; |
| 580 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 581 | // FileCopyManager methods. |
| 582 | |
| 583 | /** |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 584 | * Initializes the filesystem if it is not done yet. |
| 585 | * @param {function()} callback Completion callback. |
| 586 | */ |
| 587 | FileCopyManager.prototype.initialize = function(callback) { |
| 588 | // Already initialized. |
| 589 | if (this.root_) { |
| 590 | callback(); |
| 591 | return; |
| 592 | } |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 593 | chrome.fileBrowserPrivate.requestFileSystem(function(filesystem) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 594 | this.root_ = filesystem.root; |
| 595 | callback(); |
| 596 | }.bind(this)); |
| 597 | }; |
| 598 | |
| 599 | /** |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 600 | * Called before a new method is run in the manager. Prepares the manager's |
| 601 | * state for running a new method. |
| 602 | */ |
| 603 | FileCopyManager.prototype.willRunNewMethod = function() { |
| 604 | // Cancel any pending close actions so the file copy manager doesn't go away. |
| 605 | if (this.unloadTimeout_) |
| 606 | clearTimeout(this.unloadTimeout_); |
| 607 | this.unloadTimeout_ = null; |
| 608 | }; |
| 609 | |
| 610 | /** |
| 611 | * @return {Object} Status object. |
| 612 | */ |
| 613 | FileCopyManager.prototype.getStatus = function() { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 614 | // TODO(hidehiko): Reorganize the structure when delete queue is merged |
| 615 | // into copy task queue. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 616 | var rv = { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 617 | totalItems: 0, |
| 618 | completedItems: 0, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 619 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 620 | totalBytes: 0, |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 621 | completedBytes: 0, |
| 622 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 623 | pendingCopies: 0, |
| 624 | pendingMoves: 0, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 625 | pendingZips: 0, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 626 | |
| 627 | // In case the number of the incompleted entry is exactly one. |
| 628 | filename: '', |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 629 | }; |
| 630 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 631 | var pendingEntry = null; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 632 | for (var i = 0; i < this.copyTasks_.length; i++) { |
| 633 | var task = this.copyTasks_[i]; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 634 | rv.totalItems += task.entries.length; |
| 635 | rv.completedItems += task.entryIndex; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 636 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 637 | rv.totalBytes += task.totalBytes; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 638 | rv.completedBytes += task.completedBytes; |
| 639 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 640 | var numPendingEntries = task.entries.length - task.entryIndex; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 641 | if (task.zip) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 642 | rv.pendingZips += numPendingEntries; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 643 | } else if (task.move || task.deleteAfterCopy) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 644 | rv.pendingMoves += numPendingEntries; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 645 | } else { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 646 | rv.pendingCopies += numPendingEntries; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 647 | } |
| 648 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 649 | if (numPendingEntries == 1) |
| 650 | pendingEntry = task.entries[task.entries.length - 1]; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 651 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 652 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 653 | if (rv.totalItems - rv.completedItems == 1 && pendingEntry) |
| 654 | rv.filename = pendingEntry.name; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 655 | |
| 656 | return rv; |
| 657 | }; |
| 658 | |
| 659 | /** |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 660 | * Adds an event listener for the tasks. |
| 661 | * @param {string} type The name of the event. |
| 662 | * @param {function(cr.Event)} handler The handler for the event. |
| 663 | * This is called when the event is dispatched. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 664 | */ |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 665 | FileCopyManager.prototype.addEventListener = function(type, handler) { |
| 666 | this.eventRouter_.addEventListener(type, handler); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 667 | }; |
| 668 | |
| 669 | /** |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 670 | * Removes an event listener for the tasks. |
| 671 | * @param {string} type The name of the event. |
| 672 | * @param {function(cr.Event)} handler The handler to be removed. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 673 | */ |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 674 | FileCopyManager.prototype.removeEventListener = function(type, handler) { |
| 675 | this.eventRouter_.removeEventListener(type, handler); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 676 | }; |
| 677 | |
| 678 | /** |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 679 | * Says if there are any tasks in the queue. |
| 680 | * @return {boolean} True, if there are any tasks. |
| 681 | */ |
| 682 | FileCopyManager.prototype.hasQueuedTasks = function() { |
| 683 | return this.copyTasks_.length > 0 || this.deleteTasks_.length > 0; |
| 684 | }; |
| 685 | |
| 686 | /** |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 687 | * Unloads the host page in 5 secs of idleing. Need to be called |
| 688 | * each time this.copyTasks_.length or this.deleteTasks_.length |
| 689 | * changed. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 690 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 691 | * @private |
| 692 | */ |
| 693 | FileCopyManager.prototype.maybeScheduleCloseBackgroundPage_ = function() { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 694 | if (!this.hasQueuedTasks()) { |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 695 | if (this.unloadTimeout_ === null) |
| 696 | this.unloadTimeout_ = setTimeout(maybeCloseBackgroundPage, 5000); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 697 | } else if (this.unloadTimeout_) { |
| 698 | clearTimeout(this.unloadTimeout_); |
| 699 | this.unloadTimeout_ = null; |
| 700 | } |
| 701 | }; |
| 702 | |
| 703 | /** |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 704 | * Completely clear out the copy queue, either because we encountered an error |
| 705 | * or completed successfully. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 706 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 707 | * @private |
| 708 | */ |
| 709 | FileCopyManager.prototype.resetQueue_ = function() { |
| 710 | for (var i = 0; i < this.cancelObservers_.length; i++) |
| 711 | this.cancelObservers_[i](); |
| 712 | |
| 713 | this.copyTasks_ = []; |
| 714 | this.cancelObservers_ = []; |
| 715 | this.maybeScheduleCloseBackgroundPage_(); |
| 716 | }; |
| 717 | |
| 718 | /** |
| 719 | * Request that the current copy queue be abandoned. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 720 | * |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 721 | * @param {function()=} opt_callback On cancel. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 722 | */ |
| 723 | FileCopyManager.prototype.requestCancel = function(opt_callback) { |
| 724 | this.cancelRequested_ = true; |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 725 | if (this.cancelCallback_) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 726 | this.cancelCallback_(); |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 727 | this.cancelCallback_ = null; |
| 728 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 729 | if (opt_callback) |
| 730 | this.cancelObservers_.push(opt_callback); |
| 731 | |
| 732 | // If there is any active task it will eventually call maybeCancel_. |
| 733 | // Otherwise call it right now. |
| 734 | if (this.copyTasks_.length == 0) |
| 735 | this.doCancel_(); |
| 736 | }; |
| 737 | |
| 738 | /** |
| 739 | * Perform the bookkeeping required to cancel. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 740 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 741 | * @private |
| 742 | */ |
| 743 | FileCopyManager.prototype.doCancel_ = function() { |
| 744 | this.resetQueue_(); |
| 745 | this.cancelRequested_ = false; |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 746 | this.eventRouter_.sendProgressEvent('CANCELLED', this.getStatus()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 747 | }; |
| 748 | |
| 749 | /** |
| 750 | * Used internally to check if a cancel has been requested, and handle |
| 751 | * it if so. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 752 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 753 | * @return {boolean} If canceled. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 754 | * @private |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 755 | */ |
| 756 | FileCopyManager.prototype.maybeCancel_ = function() { |
| 757 | if (!this.cancelRequested_) |
| 758 | return false; |
| 759 | |
| 760 | this.doCancel_(); |
| 761 | return true; |
| 762 | }; |
| 763 | |
| 764 | /** |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 765 | * Kick off pasting. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 766 | * |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 767 | * @param {Array.<string>} files Pathes of source files. |
| 768 | * @param {Array.<string>} directories Pathes of source directories. |
| 769 | * @param {boolean} isCut If the source items are removed from original |
| 770 | * location. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 771 | * @param {string} targetPath Target path. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 772 | */ |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 773 | FileCopyManager.prototype.paste = function( |
| 774 | files, directories, isCut, targetPath) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 775 | var self = this; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 776 | var entries = []; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 777 | var added = 0; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 778 | var total; |
| 779 | |
| 780 | var steps = { |
| 781 | start: function() { |
| 782 | // Filter entries. |
| 783 | var entryFilterFunc = function(entry) { |
| 784 | if (entry == '') |
| 785 | return false; |
| 786 | if (isCut && entry.replace(/\/[^\/]+$/, '') == targetPath) |
| 787 | // Moving to the same directory is a redundant operation. |
| 788 | return false; |
| 789 | return true; |
| 790 | }; |
| 791 | directories = directories ? directories.filter(entryFilterFunc) : []; |
| 792 | files = files ? files.filter(entryFilterFunc) : []; |
| 793 | |
| 794 | // Check the number of filtered entries. |
| 795 | total = directories.length + files.length; |
| 796 | if (total == 0) |
| 797 | return; |
| 798 | |
| 799 | // Retrieve entries. |
| 800 | util.getDirectories(self.root_, {create: false}, directories, |
| 801 | steps.onEntryFound, steps.onPathError); |
| 802 | util.getFiles(self.root_, {create: false}, files, |
| 803 | steps.onEntryFound, steps.onPathError); |
| 804 | }, |
| 805 | |
| 806 | onEntryFound: function(entry) { |
| 807 | // When getDirectories/getFiles finish, they call addEntry with null. |
| 808 | // We don't want to add null to our entries. |
| 809 | if (entry == null) |
| 810 | return; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 811 | entries.push(entry); |
| 812 | added++; |
| 813 | if (added == total) |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 814 | steps.onSourceEntriesFound(); |
| 815 | }, |
| 816 | |
| 817 | onSourceEntriesFound: function() { |
| 818 | self.root_.getDirectory(targetPath, {}, |
| 819 | steps.onTargetEntryFound, steps.onPathError); |
| 820 | }, |
| 821 | |
| 822 | onTargetEntryFound: function(targetEntry) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 823 | self.queueCopy_(targetEntry, entries, isCut); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 824 | }, |
| 825 | |
| 826 | onPathError: function(err) { |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 827 | self.eventRouter_.sendProgressEvent( |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 828 | 'ERROR', |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 829 | self.getStatus(), |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 830 | new FileCopyManager.Error( |
| 831 | util.FileOperationErrorType.FILESYSTEM_ERROR, err)); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 832 | } |
| 833 | }; |
| 834 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 835 | steps.start(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 836 | }; |
| 837 | |
| 838 | /** |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 839 | * Checks if the move operation is avaiable between the given two locations. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 840 | * |
| 841 | * @param {DirectoryEntry} sourceEntry An entry from the source. |
| 842 | * @param {DirectoryEntry} targetDirEntry Directory entry for the target. |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 843 | * @return {boolean} Whether we can move from the source to the target. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 844 | */ |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 845 | FileCopyManager.prototype.isMovable = function(sourceEntry, |
| 846 | targetDirEntry) { |
| 847 | return (PathUtil.isDriveBasedPath(sourceEntry.fullPath) && |
| 848 | PathUtil.isDriveBasedPath(targetDirEntry.fullPath)) || |
| 849 | (PathUtil.getRootPath(sourceEntry.fullPath) == |
| 850 | PathUtil.getRootPath(targetDirEntry.fullPath)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 851 | }; |
| 852 | |
| 853 | /** |
| 854 | * Initiate a file copy. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 855 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 856 | * @param {DirectoryEntry} targetDirEntry Target directory. |
| 857 | * @param {Array.<Entry>} entries Entries to copy. |
| 858 | * @param {boolean} deleteAfterCopy In case of move. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 859 | * @return {FileCopyManager.Task} Copy task. |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 860 | * @private |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 861 | */ |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 862 | FileCopyManager.prototype.queueCopy_ = function( |
| 863 | targetDirEntry, entries, deleteAfterCopy) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 864 | var self = this; |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 865 | // When copying files, null can be specified as source directory. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 866 | var copyTask = new FileCopyManager.Task(targetDirEntry); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 867 | if (deleteAfterCopy) { |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 868 | if (this.isMovable(entries[0], targetDirEntry)) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 869 | copyTask.move = true; |
| 870 | } else { |
| 871 | copyTask.deleteAfterCopy = true; |
| 872 | } |
| 873 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 874 | copyTask.setEntries(entries, function() { |
| 875 | self.copyTasks_.push(copyTask); |
| 876 | self.maybeScheduleCloseBackgroundPage_(); |
| 877 | if (self.copyTasks_.length == 1) { |
| 878 | // Assume self.cancelRequested_ == false. |
| 879 | // This moved us from 0 to 1 active tasks, let the servicing begin! |
| 880 | self.serviceAllTasks_(); |
| 881 | } else { |
| 882 | // Force to update the progress of butter bar when there are new tasks |
| 883 | // coming while servicing current task. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 884 | self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 885 | } |
| 886 | }); |
| 887 | |
| 888 | return copyTask; |
| 889 | }; |
| 890 | |
| 891 | /** |
| 892 | * Service all pending tasks, as well as any that might appear during the |
| 893 | * copy. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 894 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 895 | * @private |
| 896 | */ |
| 897 | FileCopyManager.prototype.serviceAllTasks_ = function() { |
| 898 | var self = this; |
| 899 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 900 | var onTaskProgress = function() { |
| 901 | self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); |
| 902 | }; |
| 903 | |
| 904 | var onEntryChanged = function(type, entry) { |
| 905 | self.eventRouter_.sendEntryChangedEvent(type, entry); |
| 906 | }; |
| 907 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 908 | var onTaskError = function(err) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 909 | if (self.maybeCancel_()) |
| 910 | return; |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 911 | self.eventRouter_.sendProgressEvent('ERROR', self.getStatus(), err); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 912 | self.resetQueue_(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 913 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 914 | |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 915 | var onTaskSuccess = function() { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 916 | if (self.maybeCancel_()) |
| 917 | return; |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 918 | |
| 919 | // The task at the front of the queue is completed. Pop it from the queue. |
| 920 | self.copyTasks_.shift(); |
| 921 | self.maybeScheduleCloseBackgroundPage_(); |
| 922 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 923 | if (!self.copyTasks_.length) { |
| 924 | // All tasks have been serviced, clean up and exit. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 925 | self.eventRouter_.sendProgressEvent('SUCCESS', self.getStatus()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 926 | self.resetQueue_(); |
| 927 | return; |
| 928 | } |
| 929 | |
| 930 | // We want to dispatch a PROGRESS event when there are more tasks to serve |
| 931 | // right after one task finished in the queue. We treat all tasks as one |
| 932 | // big task logically, so there is only one BEGIN/SUCCESS event pair for |
| 933 | // these continuous tasks. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 934 | self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 935 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 936 | self.serviceTask_(self.copyTasks_[0], onEntryChanged, onTaskProgress, |
| 937 | onTaskSuccess, onTaskError); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 938 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 939 | |
| 940 | // If the queue size is 1 after pushing our task, it was empty before, |
| 941 | // so we need to kick off queue processing and dispatch BEGIN event. |
| 942 | |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 943 | this.eventRouter_.sendProgressEvent('BEGIN', this.getStatus()); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 944 | this.serviceTask_(this.copyTasks_[0], onEntryChanged, onTaskProgress, |
| 945 | onTaskSuccess, onTaskError); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 946 | }; |
| 947 | |
| 948 | /** |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 949 | * Runs a given task. |
| 950 | * Note that the responsibility of this method is just dispatching to the |
| 951 | * appropriate serviceXxxTask_() method. |
| 952 | * TODO(hidehiko): Remove this method by introducing FileCopyManager.Task.run() |
| 953 | * (crbug.com/246976). |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 954 | * |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 955 | * @param {FileCopyManager.Task} task A task to be run. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 956 | * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback |
| 957 | * invoked when an entry is changed. |
| 958 | * @param {function()} progressCallback Callback invoked periodically during |
| 959 | * the operation. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 960 | * @param {function()} successCallback Callback run on success. |
| 961 | * @param {function(FileCopyManager.Error)} errorCallback Callback run on error. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 962 | * @private |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 963 | */ |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 964 | FileCopyManager.prototype.serviceTask_ = function( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 965 | task, entryChangedCallback, progressCallback, |
| 966 | successCallback, errorCallback) { |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 967 | if (task.zip) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 968 | this.serviceZipTask_(task, entryChangedCallback, progressCallback, |
| 969 | successCallback, errorCallback); |
Ben Murdoch | 58e6fbe | 2013-07-26 10:20:38 +0100 | [diff] [blame] | 970 | else if (task.move) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 971 | this.serviceMoveTask_(task, entryChangedCallback, progressCallback, |
| 972 | successCallback, errorCallback); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 973 | else |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 974 | this.serviceCopyTask_(task, entryChangedCallback, progressCallback, |
| 975 | successCallback, errorCallback); |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 976 | }; |
| 977 | |
| 978 | /** |
| 979 | * Service all entries in the copy (and move) task. |
| 980 | * Note: this method contains also the operation of "Move" due to historical |
| 981 | * reason. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 982 | * |
| 983 | * @param {FileCopyManager.Task} task A copy task to be run. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 984 | * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback |
| 985 | * invoked when an entry is changed. |
| 986 | * @param {function()} progressCallback Callback invoked periodically during |
| 987 | * the copying. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 988 | * @param {function()} successCallback On success. |
| 989 | * @param {function(FileCopyManager.Error)} errorCallback On error. |
| 990 | * @private |
| 991 | */ |
| 992 | FileCopyManager.prototype.serviceCopyTask_ = function( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 993 | task, entryChangedCallback, progressCallback, successCallback, |
| 994 | errorCallback) { |
| 995 | // TODO(hidehiko): We should be able to share the code to iterate on entries |
| 996 | // with serviceMoveTask_(). |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 997 | if (task.entries.length == 0) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 998 | successCallback(); |
| 999 | return; |
| 1000 | } |
| 1001 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1002 | var self = this; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1003 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1004 | var deleteOriginals = function() { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1005 | var count = task.originalEntries.length; |
| 1006 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1007 | var onEntryDeleted = function(entry) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1008 | entryChangedCallback(util.EntryChangedType.DELETED, entry); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1009 | count--; |
| 1010 | if (!count) |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1011 | successCallback(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1012 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1013 | |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 1014 | var onFilesystemError = function(err) { |
| 1015 | errorCallback(new FileCopyManager.Error( |
| 1016 | util.FileOperationErrorType.FILESYSTEM_ERROR, err)); |
| 1017 | }; |
| 1018 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1019 | for (var i = 0; i < task.originalEntries.length; i++) { |
| 1020 | var entry = task.originalEntries[i]; |
| 1021 | util.removeFileOrDirectory( |
| 1022 | entry, onEntryDeleted.bind(self, entry), onFilesystemError); |
| 1023 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1024 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1025 | |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1026 | var onEntryServiced = function() { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1027 | task.entryIndex++; |
| 1028 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1029 | // We should not dispatch a PROGRESS event when there is no pending items |
| 1030 | // in the task. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1031 | if (task.entryIndex >= task.entries.length) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1032 | if (task.deleteAfterCopy) { |
| 1033 | deleteOriginals(); |
| 1034 | } else { |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1035 | successCallback(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1036 | } |
| 1037 | return; |
| 1038 | } |
| 1039 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1040 | progressCallback(); |
| 1041 | self.processCopyEntry_( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1042 | task, task.entries[task.entryIndex], entryChangedCallback, |
| 1043 | progressCallback, onEntryServiced, errorCallback); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1044 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1045 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1046 | this.processCopyEntry_( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1047 | task, task.entries[task.entryIndex], entryChangedCallback, |
| 1048 | progressCallback, onEntryServiced, errorCallback); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1049 | }; |
| 1050 | |
| 1051 | /** |
Ben Murdoch | 58e6fbe | 2013-07-26 10:20:38 +0100 | [diff] [blame] | 1052 | * Copies the next entry in a given task. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1053 | * TODO(olege): Refactor this method into a separate class. |
| 1054 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1055 | * @param {FileManager.Task} task A task. |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1056 | * @param {Entry} sourceEntry An entry to be copied. |
| 1057 | * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback |
| 1058 | * invoked when an entry is changed. |
| 1059 | * @param {function()} progressCallback Callback invoked periodically during |
| 1060 | * the copying. |
Ben Murdoch | a3f7b4e | 2013-07-24 10:36:34 +0100 | [diff] [blame] | 1061 | * @param {function()} successCallback On success. |
| 1062 | * @param {function(FileCopyManager.Error)} errorCallback On error. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1063 | * @private |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1064 | */ |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1065 | FileCopyManager.prototype.processCopyEntry_ = function( |
| 1066 | task, sourceEntry, entryChangedCallback, progressCallback, successCallback, |
| 1067 | errorCallback) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1068 | if (this.maybeCancel_()) |
| 1069 | return; |
| 1070 | |
| 1071 | var self = this; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1072 | |
| 1073 | // |sourceEntry.originalSourcePath| is set in util.recurseAndResolveEntries. |
| 1074 | var sourcePath = sourceEntry.originalSourcePath; |
| 1075 | if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) { |
| 1076 | // We found an entry in the list that is not relative to the base source |
| 1077 | // path, something is wrong. |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 1078 | errorCallback(new FileCopyManager.Error( |
| 1079 | util.FileOperationErrorType.UNEXPECTED_SOURCE_FILE, |
| 1080 | sourceEntry.fullPath)); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1081 | return; |
| 1082 | } |
| 1083 | |
| 1084 | var targetDirEntry = task.targetDirEntry; |
| 1085 | var originalPath = sourceEntry.fullPath.substr(sourcePath.length + 1); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1086 | originalPath = task.applyRenames(originalPath); |
| 1087 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 1088 | var onDeduplicated = function(targetRelativePath) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1089 | var onCopyComplete = function(entry) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1090 | entryChangedCallback(util.EntryChangedType.CREATED, entry); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1091 | successCallback(); |
| 1092 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1093 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1094 | var onFilesystemError = function(err) { |
| 1095 | errorCallback(new FileCopyManager.Error( |
| 1096 | util.FileOperationErrorType.FILESYSTEM_ERROR, err)); |
| 1097 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1098 | |
| 1099 | if (sourceEntry.isDirectory) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1100 | // Copying the directory means just creating a new directory. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1101 | targetDirEntry.getDirectory( |
| 1102 | targetRelativePath, |
| 1103 | {create: true, exclusive: true}, |
| 1104 | function(targetEntry) { |
| 1105 | if (targetRelativePath != originalPath) { |
| 1106 | task.registerRename(originalPath, targetRelativePath); |
| 1107 | } |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1108 | onCopyComplete(targetEntry); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1109 | }, |
| 1110 | util.flog('Error getting dir: ' + targetRelativePath, |
| 1111 | onFilesystemError)); |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1112 | } else { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1113 | // Copy a file. |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1114 | targetDirEntry.getDirectory( |
| 1115 | PathUtil.dirname(targetRelativePath), {create: false}, |
| 1116 | function(dirEntry) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1117 | self.cancelCallback_ = fileOperationUtil.copyFile( |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1118 | sourceEntry, dirEntry, PathUtil.basename(targetRelativePath), |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1119 | function(entry, size) { |
| 1120 | task.updateFileCopyProgress(size); |
| 1121 | progressCallback(); |
| 1122 | }, |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1123 | function(entry) { |
| 1124 | self.cancelCallback_ = null; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1125 | onCopyComplete(entry); |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1126 | }, |
| 1127 | function(error) { |
| 1128 | self.cancelCallback_ = null; |
| 1129 | onFilesystemError(error); |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 1130 | }); |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1131 | }, |
| 1132 | onFilesystemError); |
| 1133 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1134 | }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1135 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1136 | fileOperationUtil.deduplicatePath( |
Ben Murdoch | 558790d | 2013-07-30 15:19:42 +0100 | [diff] [blame] | 1137 | targetDirEntry, originalPath, onDeduplicated, errorCallback); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1138 | }; |
| 1139 | |
| 1140 | /** |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1141 | * Moves all entries in the task. |
| 1142 | * |
| 1143 | * @param {FileCopyManager.Task} task A move task to be run. |
| 1144 | * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback |
| 1145 | * invoked when an entry is changed. |
| 1146 | * @param {function()} progressCallback Callback invoked periodically during |
| 1147 | * the moving. |
| 1148 | * @param {function()} successCallback On success. |
| 1149 | * @param {function(FileCopyManager.Error)} errorCallback On error. |
| 1150 | * @private |
| 1151 | */ |
| 1152 | FileCopyManager.prototype.serviceMoveTask_ = function( |
| 1153 | task, entryChangedCallback, progressCallback, successCallback, |
| 1154 | errorCallback) { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1155 | if (task.entries.length == 0) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1156 | successCallback(); |
| 1157 | return; |
| 1158 | } |
| 1159 | |
| 1160 | this.processMoveEntry_( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1161 | task, task.entries[task.entryIndex], entryChangedCallback, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1162 | (function onCompleted() { |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1163 | task.entryIndex++; |
| 1164 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1165 | // We should not dispatch a PROGRESS event when there is no pending |
| 1166 | // items in the task. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1167 | if (task.entryIndex >= task.entries.length) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1168 | successCallback(); |
| 1169 | return; |
| 1170 | } |
| 1171 | |
| 1172 | // Move the next entry. |
| 1173 | progressCallback(); |
| 1174 | this.processMoveEntry_( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1175 | task, task.entries[task.entryIndex], entryChangedCallback, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1176 | onCompleted.bind(this), errorCallback); |
| 1177 | }).bind(this), |
| 1178 | errorCallback); |
| 1179 | }; |
| 1180 | |
| 1181 | /** |
| 1182 | * Moves the next entry in a given task. |
| 1183 | * |
| 1184 | * Implementation note: This method can be simplified more. For example, in |
| 1185 | * Task.setEntries(), the flag to recurse is set to false for move task, |
| 1186 | * so that all the entries' originalSourcePath should be |
| 1187 | * dirname(sourceEntry.fullPath). |
| 1188 | * Thus, targetRelativePath should contain exact one component. Also we can |
| 1189 | * skip applyRenames, because the destination directory always should be |
| 1190 | * task.targetDirEntry. |
| 1191 | * The unnecessary complexity is due to historical reason. |
| 1192 | * TODO(hidehiko): Refactor this method. |
| 1193 | * |
| 1194 | * @param {FileManager.Task} task A move task. |
| 1195 | * @param {Entry} sourceEntry An entry to be moved. |
| 1196 | * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback |
| 1197 | * invoked when an entry is changed. |
| 1198 | * @param {function()} successCallback On success. |
| 1199 | * @param {function(FileCopyManager.Error)} errorCallback On error. |
| 1200 | * @private |
| 1201 | */ |
| 1202 | FileCopyManager.prototype.processMoveEntry_ = function( |
| 1203 | task, sourceEntry, entryChangedCallback, successCallback, errorCallback) { |
| 1204 | if (this.maybeCancel_()) |
| 1205 | return; |
| 1206 | |
| 1207 | // |sourceEntry.originalSourcePath| is set in util.recurseAndResolveEntries. |
| 1208 | var sourcePath = sourceEntry.originalSourcePath; |
| 1209 | if (sourceEntry.fullPath.substr(0, sourcePath.length) != sourcePath) { |
| 1210 | // We found an entry in the list that is not relative to the base source |
| 1211 | // path, something is wrong. |
| 1212 | errorCallback(new FileCopyManager.Error( |
| 1213 | util.FileOperationErrorType.UNEXPECTED_SOURCE_FILE, |
| 1214 | sourceEntry.fullPath)); |
| 1215 | return; |
| 1216 | } |
| 1217 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1218 | fileOperationUtil.deduplicatePath( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1219 | task.targetDirEntry, |
| 1220 | task.applyRenames(sourceEntry.fullPath.substr(sourcePath.length + 1)), |
| 1221 | function(targetRelativePath) { |
| 1222 | var onFilesystemError = function(err) { |
| 1223 | errorCallback(new FileCopyManager.Error( |
| 1224 | util.FileOperationErrorType.FILESYSTEM_ERROR, |
| 1225 | err)); |
| 1226 | }; |
| 1227 | |
| 1228 | task.targetDirEntry.getDirectory( |
| 1229 | PathUtil.dirname(targetRelativePath), {create: false}, |
| 1230 | function(dirEntry) { |
| 1231 | sourceEntry.moveTo( |
| 1232 | dirEntry, PathUtil.basename(targetRelativePath), |
| 1233 | function(targetEntry) { |
| 1234 | entryChangedCallback( |
| 1235 | util.EntryChangedType.CREATED, targetEntry); |
| 1236 | entryChangedCallback( |
| 1237 | util.EntryChangedType.DELETED, sourceEntry); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1238 | successCallback(); |
| 1239 | }, |
| 1240 | onFilesystemError); |
| 1241 | }, |
| 1242 | onFilesystemError); |
| 1243 | }, |
| 1244 | errorCallback); |
| 1245 | }; |
| 1246 | |
| 1247 | /** |
| 1248 | * Service a zip file creation task. |
| 1249 | * |
| 1250 | * @param {FileCopyManager.Task} task A zip task to be run. |
| 1251 | * @param {function(util.EntryChangedType, Entry)} entryChangedCallback Callback |
| 1252 | * invoked when an entry is changed. |
| 1253 | * @param {function()} progressCallback Callback invoked periodically during |
| 1254 | * the moving. |
| 1255 | * @param {function()} successCallback On complete. |
| 1256 | * @param {function(FileCopyManager.Error)} errorCallback On error. |
| 1257 | * @private |
| 1258 | */ |
| 1259 | FileCopyManager.prototype.serviceZipTask_ = function( |
| 1260 | task, entryChangedCallback, progressCallback, successCallback, |
| 1261 | errorCallback) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1262 | // TODO(hidehiko): we should localize the name. |
| 1263 | var destName = 'Archive'; |
| 1264 | if (task.originalEntries.length == 1) { |
| 1265 | var entryPath = task.originalEntries[0].fullPath; |
| 1266 | var i = entryPath.lastIndexOf('/'); |
| 1267 | var basename = (i < 0) ? entryPath : entryPath.substr(i + 1); |
| 1268 | i = basename.lastIndexOf('.'); |
| 1269 | destName = ((i < 0) ? basename : basename.substr(0, i)); |
| 1270 | } |
| 1271 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1272 | fileOperationUtil.deduplicatePath( |
| 1273 | task.targetDirEntry, destName + '.zip', |
| 1274 | function(destPath) { |
| 1275 | progressCallback(); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1276 | |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1277 | fileOperationUtil.zipSelection( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 1278 | task.entries, |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1279 | task.zipBaseDirEntry, |
| 1280 | destPath, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1281 | function(entry) { |
| 1282 | entryChangedCallback(util.EntryChangedType.CREATE, entry); |
| 1283 | successCallback(); |
| 1284 | }, |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1285 | function(error) { |
| 1286 | errorCallback(new FileCopyManager.Error( |
| 1287 | util.FileOperationErrorType.FILESYSTEM_ERROR, error)); |
| 1288 | }); |
| 1289 | }, |
| 1290 | errorCallback); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1291 | }; |
| 1292 | |
| 1293 | /** |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1294 | * Timeout before files are really deleted (to allow undo). |
| 1295 | */ |
| 1296 | FileCopyManager.DELETE_TIMEOUT = 30 * 1000; |
| 1297 | |
| 1298 | /** |
| 1299 | * Schedules the files deletion. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1300 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1301 | * @param {Array.<Entry>} entries The entries. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1302 | */ |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1303 | FileCopyManager.prototype.deleteEntries = function(entries) { |
| 1304 | var task = { entries: entries }; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1305 | this.deleteTasks_.push(task); |
| 1306 | this.maybeScheduleCloseBackgroundPage_(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1307 | if (this.deleteTasks_.length == 1) |
| 1308 | this.serviceAllDeleteTasks_(); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1309 | }; |
| 1310 | |
| 1311 | /** |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1312 | * Service all pending delete tasks, as well as any that might appear during the |
| 1313 | * deletion. |
| 1314 | * |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1315 | * @private |
| 1316 | */ |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1317 | FileCopyManager.prototype.serviceAllDeleteTasks_ = function() { |
| 1318 | var self = this; |
| 1319 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1320 | var onTaskSuccess = function() { |
| 1321 | var task = self.deleteTasks_[0]; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1322 | self.deleteTasks_.shift(); |
| 1323 | if (!self.deleteTasks_.length) { |
| 1324 | // All tasks have been serviced, clean up and exit. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 1325 | self.eventRouter_.sendDeleteEvent( |
| 1326 | 'SUCCESS', |
| 1327 | task.entries.map(function(e) { |
| 1328 | return util.makeFilesystemUrl(e.fullPath); |
| 1329 | })); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1330 | self.maybeScheduleCloseBackgroundPage_(); |
| 1331 | return; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1332 | } |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1333 | |
| 1334 | // We want to dispatch a PROGRESS event when there are more tasks to serve |
| 1335 | // right after one task finished in the queue. We treat all tasks as one |
| 1336 | // big task logically, so there is only one BEGIN/SUCCESS event pair for |
| 1337 | // these continuous tasks. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 1338 | self.eventRouter_.sendDeleteEvent( |
| 1339 | 'PROGRESS', |
| 1340 | task.entries.map(function(e) { |
| 1341 | return util.makeFilesystemUrl(e.fullPath); |
| 1342 | })); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 1343 | self.serviceDeleteTask_(self.deleteTasks_[0], onTaskSuccess, onTaskFailure); |
| 1344 | }; |
| 1345 | |
| 1346 | var onTaskFailure = function(task) { |
| 1347 | self.deleteTasks_ = []; |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 1348 | self.eventRouter_.sendDeleteEvent( |
| 1349 | 'ERROR', |
| 1350 | task.entries.map(function(e) { |
| 1351 | return util.makeFilesystemUrl(e.fullPath); |
| 1352 | })); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 1353 | self.maybeScheduleCloseBackgroundPage_(); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1354 | }; |
| 1355 | |
| 1356 | // If the queue size is 1 after pushing our task, it was empty before, |
| 1357 | // so we need to kick off queue processing and dispatch BEGIN event. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 1358 | this.eventRouter_.sendDeleteEvent( |
| 1359 | 'BEGIN', |
| 1360 | this.deleteTasks_[0].entries.map(function(e) { |
| 1361 | return util.makeFilesystemUrl(e.fullPath); |
| 1362 | })); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 1363 | this.serviceDeleteTask_(this.deleteTasks_[0], onTaskSuccess, onTaskFailure); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1364 | }; |
| 1365 | |
| 1366 | /** |
| 1367 | * Performs the deletion. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1368 | * |
| 1369 | * @param {Object} task The delete task (see deleteEntries function). |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1370 | * @param {function()} successCallback Callback run on success. |
| 1371 | * @param {function(FileCopyManager.Error)} errorCallback Callback run on error. |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1372 | * @private |
| 1373 | */ |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1374 | FileCopyManager.prototype.serviceDeleteTask_ = function( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1375 | task, successCallback, errorCallback) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1376 | var downcount = task.entries.length; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1377 | if (downcount == 0) { |
| 1378 | successCallback(); |
| 1379 | return; |
| 1380 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1381 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1382 | var filesystemError = null; |
| 1383 | var onComplete = function() { |
| 1384 | if (--downcount > 0) |
| 1385 | return; |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1386 | |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1387 | // All remove operations are processed. Run callback. |
| 1388 | if (filesystemError) { |
| 1389 | errorCallback(new FileCopyManager.Error( |
| 1390 | util.FileOperationErrorType.FILESYSTEM_ERROR, filesystemError)); |
| 1391 | } else { |
| 1392 | successCallback(); |
| 1393 | } |
| 1394 | }; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1395 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1396 | for (var i = 0; i < task.entries.length; i++) { |
| 1397 | var entry = task.entries[i]; |
| 1398 | util.removeFileOrDirectory( |
| 1399 | entry, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1400 | function(currentEntry) { |
| 1401 | this.eventRouter_.sendEntryChangedEvent( |
| 1402 | util.EntryChangedType.DELETED, currentEntry); |
| 1403 | onComplete(); |
| 1404 | }.bind(this, entry), |
| 1405 | function(error) { |
| 1406 | if (!filesystemError) |
| 1407 | filesystemError = error; |
| 1408 | onComplete(); |
| 1409 | }); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1410 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1411 | }; |
| 1412 | |
| 1413 | /** |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1414 | * Creates a zip file for the selection of files. |
| 1415 | * |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1416 | * @param {Entry} dirEntry The directory containing the selection. |
| 1417 | * @param {Array.<Entry>} selectionEntries The selected entries. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1418 | */ |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1419 | FileCopyManager.prototype.zipSelection = function(dirEntry, selectionEntries) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1420 | var self = this; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1421 | var zipTask = new FileCopyManager.Task(dirEntry, dirEntry); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1422 | zipTask.zip = true; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1423 | zipTask.setEntries(selectionEntries, function() { |
| 1424 | // TODO: per-entry zip progress update with accurate byte count. |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 1425 | // For now just set completedBytes to same value as totalBytes so that the |
| 1426 | // progress bar is full. |
Ben Murdoch | 3240926 | 2013-08-07 11:04:47 +0100 | [diff] [blame] | 1427 | zipTask.completedBytes = zipTask.totalBytes; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1428 | self.copyTasks_.push(zipTask); |
| 1429 | if (self.copyTasks_.length == 1) { |
| 1430 | // Assume self.cancelRequested_ == false. |
| 1431 | // This moved us from 0 to 1 active tasks, let the servicing begin! |
| 1432 | self.serviceAllTasks_(); |
| 1433 | } else { |
| 1434 | // Force to update the progress of butter bar when there are new tasks |
| 1435 | // coming while servicing current task. |
Ben Murdoch | d386803 | 2013-07-31 10:55:33 +0100 | [diff] [blame] | 1436 | self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1437 | } |
| 1438 | }); |
| 1439 | }; |