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. | 8 * Utilities for FileCopyManager. |
9 */ | 9 */ |
10 var fileOperationUtil = {}; | 10 var fileOperationUtil = {}; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 * Sets last modified date to the entry. | 49 * Sets last modified date to the entry. |
50 * @param {Entry} entry The entry to which the last modified is set. | 50 * @param {Entry} entry The entry to which the last modified is set. |
51 * @param {Date} modificationTime The last modified time. | 51 * @param {Date} modificationTime The last modified time. |
52 */ | 52 */ |
53 fileOperationUtil.setLastModified = function(entry, modificationTime) { | 53 fileOperationUtil.setLastModified = function(entry, modificationTime) { |
54 chrome.fileBrowserPrivate.setLastModified( | 54 chrome.fileBrowserPrivate.setLastModified( |
55 entry.toURL(), '' + Math.round(modificationTime.getTime() / 1000)); | 55 entry.toURL(), '' + Math.round(modificationTime.getTime() / 1000)); |
56 }; | 56 }; |
57 | 57 |
58 /** | 58 /** |
| 59 * Copies a file a) from Drive to local, b) from local to Drive, or c) from |
| 60 * Drive to Drive. |
| 61 * Currently, we need to take care about following two things for Drive: |
| 62 * |
| 63 * 1) Copying hosted document. |
| 64 * In theory, it is impossible to actual copy a hosted document to other |
| 65 * file system. Thus, instead, Drive file system backend creates a JSON file |
| 66 * referring to the hosted document. Also, when it is uploaded by copyTo, |
| 67 * the hosted document is copied on the server. Note that, this doesn't work |
| 68 * when a user creates a file by FileWriter (as copyFileEntry_ does). |
| 69 * |
| 70 * 2) File transfer between local and Drive server. |
| 71 * There are two directions of file transfer; from local to Drive and from |
| 72 * Drive to local. |
| 73 * The file transfer from local to Drive is done as a part of file system |
| 74 * background sync (kicked after the copy operation is done). So we don't need |
| 75 * to take care about it here. To copy the file from Drive to local (or Drive |
| 76 * to Drive with GData WAPI), we need to download the file content (if it is |
| 77 * not locally cached). During the downloading, we can listen the periodical |
| 78 * updating and cancel the downloding via private API. |
| 79 * |
| 80 * This function supports progress updating and cancelling partially. |
| 81 * Unfortunately, FileEntry.copyTo doesn't support progress updating nor |
| 82 * cancelling, so we support them only during file downloading. |
| 83 * |
| 84 * Note: we're planning to move copyTo logic into c++ side. crbug.com/261492 |
| 85 * |
| 86 * @param {FileEntry} source The entry of the file to be copied. |
| 87 * @param {DirectoryEntry} parent The entry of the destination directory. |
| 88 * @param {string} newName The name of the copied file. |
| 89 * @param {function(FileEntry, number)} progressCallback Callback periodically |
| 90 * invoked during file transfer with the source and the number of |
| 91 * transferred bytes from the last call. |
| 92 * @param {function(FileEntry)} successCallback Callback invoked when the |
| 93 * file copy is successfully done with the entry of the copied file. |
| 94 * @param {function(FileError)} errorCallback Callback invoked when an error |
| 95 * is found. |
| 96 * @return {function()} Callback to cancel the current file copy operation. |
| 97 * When the cancel is done, errorCallback will be called. The returned |
| 98 * callback must not be called more than once. |
| 99 */ |
| 100 fileOperationUtil.copyFileOnDrive = function( |
| 101 source, parent, newName, progressCallback, successCallback, errorCallback) { |
| 102 // Set to true when cancel is requested. |
| 103 var cancelRequested = false; |
| 104 var cancelCallback = null; |
| 105 |
| 106 var onCopyToCompleted = null; |
| 107 |
| 108 // Progress callback. |
| 109 // Because the uploading the file from local cache to Drive server will be |
| 110 // done as a part of background Drive file system sync, so for this copy |
| 111 // operation, what we need to take care about is only file downloading. |
| 112 var numTransferredBytes = 0; |
| 113 if (PathUtil.isDriveBasedPath(source.fullPath)) { |
| 114 var sourceUrl = source.toURL(); |
| 115 var sourcePath = util.extractFilePath(sourceUrl); |
| 116 var onFileTransfersUpdated = function(statusList) { |
| 117 for (var i = 0; i < statusList.length; i++) { |
| 118 var status = statusList[i]; |
| 119 |
| 120 // Comparing urls is unreliable, since they may use different |
| 121 // url encoding schemes (eg. rfc2396 vs. rfc3986). |
| 122 var filePath = util.extractFilePath(status.fileUrl); |
| 123 if (filePath == sourcePath) { |
| 124 var processed = status.processed; |
| 125 if (processed > numTransferredBytes) { |
| 126 progressCallback(source, processed - numTransferredBytes); |
| 127 numTransferredBytes = processed; |
| 128 } |
| 129 return; |
| 130 } |
| 131 } |
| 132 }; |
| 133 |
| 134 // Subscribe to listen file transfer updating notifications. |
| 135 chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener( |
| 136 onFileTransfersUpdated); |
| 137 |
| 138 // Currently, we do NOT upload the file during the copy operation. |
| 139 // It will be done as a part of file system sync after copy operation. |
| 140 // So, we can cancel only file downloading. |
| 141 cancelCallback = function() { |
| 142 chrome.fileBrowserPrivate.cancelFileTransfers( |
| 143 [sourceUrl], function() {}); |
| 144 }; |
| 145 |
| 146 // We need to clean up on copyTo completion regardless if it is |
| 147 // successfully done or not. |
| 148 onCopyToCompleted = function() { |
| 149 cancelCallback = null; |
| 150 chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener( |
| 151 onFileTransfersUpdated); |
| 152 }; |
| 153 } |
| 154 |
| 155 source.copyTo( |
| 156 parent, newName, |
| 157 function(entry) { |
| 158 if (onCopyToCompleted) |
| 159 onCopyToCompleted(); |
| 160 |
| 161 if (cancelRequested) { |
| 162 errorCallback(util.createFileError(FileError.ABORT_ERR)); |
| 163 return; |
| 164 } |
| 165 |
| 166 entry.getMetadata(function(metadata) { |
| 167 if (metadata.size > numTransferredBytes) |
| 168 progressCallback(source, metadata.size - numTransferredBytes); |
| 169 successCallback(entry); |
| 170 }, errorCallback); |
| 171 }, |
| 172 function(error) { |
| 173 if (onCopyToCompleted) |
| 174 onCopyToCompleted(); |
| 175 |
| 176 errorCallback(error); |
| 177 }); |
| 178 |
| 179 return function() { |
| 180 cancelRequested = true; |
| 181 if (cancelCallback) { |
| 182 cancelCallback(); |
| 183 cancelCallback = null; |
| 184 } |
| 185 }; |
| 186 }; |
| 187 |
| 188 /** |
59 * Thin wrapper of chrome.fileBrowserPrivate.zipSelection to adapt its | 189 * Thin wrapper of chrome.fileBrowserPrivate.zipSelection to adapt its |
60 * interface similar to copyTo(). | 190 * interface similar to copyTo(). |
61 * | 191 * |
62 * @param {Array.<Entry>} sources The array of entries to be archived. | 192 * @param {Array.<Entry>} sources The array of entries to be archived. |
63 * @param {DirectoryEntry} parent The entry of the destination directory. | 193 * @param {DirectoryEntry} parent The entry of the destination directory. |
64 * @param {string} newName The name of the archive to be created. | 194 * @param {string} newName The name of the archive to be created. |
65 * @param {function(FileEntry)} successCallback Callback invoked when the | 195 * @param {function(FileEntry)} successCallback Callback invoked when the |
66 * operation is successfully done with the entry of the created archive. | 196 * operation is successfully done with the entry of the created archive. |
67 * @param {function(FileError)} errorCallback Callback invoked when an error | 197 * @param {function(FileError)} errorCallback Callback invoked when an error |
68 * is found. | 198 * is found. |
(...skipping 875 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
944 onCopyComplete(entry, size); | 1074 onCopyComplete(entry, size); |
945 }, | 1075 }, |
946 function(error) { | 1076 function(error) { |
947 self.cancelCallback_ = null; | 1077 self.cancelCallback_ = null; |
948 onFilesystemError(error); | 1078 onFilesystemError(error); |
949 }); | 1079 }); |
950 }, | 1080 }, |
951 util.flog('Error getting file: ' + targetRelativePath, | 1081 util.flog('Error getting file: ' + targetRelativePath, |
952 onFilesystemError)); | 1082 onFilesystemError)); |
953 return; | 1083 return; |
| 1084 } else { |
| 1085 // Sending a file from a) Drive to Drive, b) Drive to local or c) local |
| 1086 // to Drive. |
| 1087 targetDirEntry.getDirectory( |
| 1088 PathUtil.dirname(targetRelativePath), {create: false}, |
| 1089 function(dirEntry) { |
| 1090 self.cancelCallback_ = fileOperationUtil.copyFileOnDrive( |
| 1091 sourceEntry, dirEntry, PathUtil.basename(targetRelativePath), |
| 1092 onCopyProgress, |
| 1093 function(entry) { |
| 1094 self.cancelCallback_ = null; |
| 1095 onCopyComplete(entry, 0); |
| 1096 }, |
| 1097 function(error) { |
| 1098 self.cancelCallback_ = null; |
| 1099 onFilesystemError(error); |
| 1100 }); |
| 1101 }, |
| 1102 onFilesystemError); |
954 } | 1103 } |
955 | |
956 // TODO(hidehiko): Move following code to fileOperationUtil. | |
957 | |
958 // Sending a file from a) Drive to Drive, b) Drive to local or c) local to | |
959 // Drive. | |
960 var sourceFileUrl = sourceEntry.toURL(); | |
961 var sourceFilePath = util.extractFilePath(sourceFileUrl); | |
962 | |
963 // Progress callback. | |
964 // Because the uploading the file from local cache to Drive server will be | |
965 // done as a part of background Drive file system sync, so for this copy | |
966 // operation, what we need to take care about is only file downloading. | |
967 var numTransferredBytes = 0; | |
968 var onFileTransfersUpdated = null; | |
969 if (isSourceOnDrive) { | |
970 onFileTransfersUpdated = function(statusList) { | |
971 for (var i = 0; i < statusList.length; i++) { | |
972 var status = statusList[i]; | |
973 | |
974 // Comparing urls is unreliable, since they may use different | |
975 // url encoding schemes (eg. rfc2396 vs. rfc3986). | |
976 var filePath = util.extractFilePath(status.fileUrl); | |
977 if (filePath == sourceFilePath) { | |
978 var processed = status.processed; | |
979 if (processed > numTransferredBytes) { | |
980 onCopyProgress(sourceEntry, processed - numTransferredBytes); | |
981 numTransferredBytes = processed; | |
982 } | |
983 return; | |
984 } | |
985 } | |
986 }; | |
987 | |
988 // Currently, we do NOT upload the file during the copy operation. | |
989 // It will be done as a part of file system sync after copy operation. | |
990 // So, we can cancel only file downloading. | |
991 self.cancelCallback_ = function() { | |
992 self.cancelCallback_ = null; | |
993 chrome.fileBrowserPrivate.cancelFileTransfers( | |
994 [sourceFileUrl], function() {}); | |
995 }; | |
996 } | |
997 | |
998 // If this is the copy operation from Drive file system, | |
999 // we use copyTo method. | |
1000 targetDirEntry.getDirectory( | |
1001 PathUtil.dirname(targetRelativePath), {create: false}, | |
1002 function(dirEntry) { | |
1003 if (onFileTransfersUpdated) | |
1004 chrome.fileBrowserPrivate.onFileTransfersUpdated | |
1005 .addListener(onFileTransfersUpdated); | |
1006 | |
1007 sourceEntry.copyTo( | |
1008 dirEntry, PathUtil.basename(targetRelativePath), | |
1009 function(entry) { | |
1010 self.cancelCallback_ = null; | |
1011 if (onFileTransfersUpdated) | |
1012 chrome.fileBrowserPrivate.onFileTransfersUpdated | |
1013 .removeListener(onFileTransfersUpdated); | |
1014 | |
1015 entry.getMetadata(function(metadata) { | |
1016 if (metadata.size > numTransferredBytes) | |
1017 onCopyProgress( | |
1018 sourceEntry, metadata.size - numTransferredBytes); | |
1019 onCopyComplete(entry, 0); | |
1020 }); | |
1021 }, | |
1022 function(error) { | |
1023 self.cancelCallback_ = null; | |
1024 chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener( | |
1025 onFileTransfersUpdated); | |
1026 onFilesystemError(error); | |
1027 }); | |
1028 }, | |
1029 onFilesystemError); | |
1030 }; | 1104 }; |
1031 | 1105 |
1032 fileOperationUtil.deduplicatePath( | 1106 fileOperationUtil.deduplicatePath( |
1033 targetDirEntry, originalPath, onDeduplicated, errorCallback); | 1107 targetDirEntry, originalPath, onDeduplicated, errorCallback); |
1034 }; | 1108 }; |
1035 | 1109 |
1036 /** | 1110 /** |
1037 * Copies the contents of sourceEntry into targetEntry. | 1111 * Copies the contents of sourceEntry into targetEntry. |
1038 * TODO(hidehiko): Move this method into fileOperationUtil. | 1112 * TODO(hidehiko): Move this method into fileOperationUtil. |
1039 * | 1113 * |
(...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1416 // Assume self.cancelRequested_ == false. | 1490 // Assume self.cancelRequested_ == false. |
1417 // This moved us from 0 to 1 active tasks, let the servicing begin! | 1491 // This moved us from 0 to 1 active tasks, let the servicing begin! |
1418 self.serviceAllTasks_(); | 1492 self.serviceAllTasks_(); |
1419 } else { | 1493 } else { |
1420 // Force to update the progress of butter bar when there are new tasks | 1494 // Force to update the progress of butter bar when there are new tasks |
1421 // coming while servicing current task. | 1495 // coming while servicing current task. |
1422 self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); | 1496 self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus()); |
1423 } | 1497 } |
1424 }); | 1498 }); |
1425 }; | 1499 }; |
OLD | NEW |