| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 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 | |
| 5 #include "webkit/browser/fileapi/cross_operation_delegate.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/files/file_path.h" | |
| 9 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" | |
| 10 #include "webkit/browser/fileapi/file_system_context.h" | |
| 11 #include "webkit/browser/fileapi/file_system_operation_context.h" | |
| 12 #include "webkit/browser/fileapi/file_system_url.h" | |
| 13 #include "webkit/browser/fileapi/local_file_system_operation.h" | |
| 14 #include "webkit/common/blob/shareable_file_reference.h" | |
| 15 #include "webkit/common/fileapi/file_system_util.h" | |
| 16 | |
| 17 namespace fileapi { | |
| 18 | |
| 19 CrossOperationDelegate::CrossOperationDelegate( | |
| 20 FileSystemContext* file_system_context, | |
| 21 scoped_ptr<LocalFileSystemOperation> src_root_operation, | |
| 22 LocalFileSystemOperation* dest_root_operation, | |
| 23 const FileSystemURL& src_root, | |
| 24 const FileSystemURL& dest_root, | |
| 25 OperationType operation_type, | |
| 26 const StatusCallback& callback) | |
| 27 : RecursiveOperationDelegate(file_system_context, dest_root_operation), | |
| 28 src_root_(src_root), | |
| 29 dest_root_(dest_root), | |
| 30 operation_type_(operation_type), | |
| 31 callback_(callback), | |
| 32 src_root_operation_(src_root_operation.Pass()) { | |
| 33 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_); | |
| 34 } | |
| 35 | |
| 36 CrossOperationDelegate::~CrossOperationDelegate() { | |
| 37 } | |
| 38 | |
| 39 void CrossOperationDelegate::Run() { | |
| 40 // Not supported; this should never be called. | |
| 41 NOTREACHED(); | |
| 42 } | |
| 43 | |
| 44 void CrossOperationDelegate::RunRecursively() { | |
| 45 // Perform light-weight checks first. | |
| 46 | |
| 47 // It is an error to try to copy/move an entry into its child. | |
| 48 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) { | |
| 49 callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); | |
| 50 return; | |
| 51 } | |
| 52 | |
| 53 // It is an error to copy/move an entry into the same path. | |
| 54 if (same_file_system_ && src_root_.path() == dest_root_.path()) { | |
| 55 callback_.Run(base::PLATFORM_FILE_ERROR_EXISTS); | |
| 56 return; | |
| 57 } | |
| 58 | |
| 59 // First try to copy/move it as a file. | |
| 60 CopyOrMoveFile(URLPair(src_root_, dest_root_), | |
| 61 base::Bind(&CrossOperationDelegate::DidTryCopyOrMoveFile, | |
| 62 AsWeakPtr())); | |
| 63 } | |
| 64 | |
| 65 void CrossOperationDelegate::ProcessFile(const FileSystemURL& src_url, | |
| 66 const StatusCallback& callback) { | |
| 67 CopyOrMoveFile(URLPair(src_url, CreateDestURL(src_url)), callback); | |
| 68 } | |
| 69 | |
| 70 void CrossOperationDelegate::ProcessDirectory(const FileSystemURL& src_url, | |
| 71 const StatusCallback& callback) { | |
| 72 FileSystemURL dest_url = CreateDestURL(src_url); | |
| 73 | |
| 74 // If operation_type == Move we may need to record directories and | |
| 75 // restore directory timestamps in the end, though it may have | |
| 76 // negative performance impact. | |
| 77 // See http://crbug.com/171284 for more details. | |
| 78 NewDestOperation()->CreateDirectory( | |
| 79 dest_url, false /* exclusive */, false /* recursive */, callback); | |
| 80 } | |
| 81 | |
| 82 void CrossOperationDelegate::DidTryCopyOrMoveFile( | |
| 83 base::PlatformFileError error) { | |
| 84 if (error == base::PLATFORM_FILE_OK || | |
| 85 error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) { | |
| 86 callback_.Run(error); | |
| 87 return; | |
| 88 } | |
| 89 | |
| 90 // The src_root_ looks to be a directory. | |
| 91 // Try removing the dest_root_ to see if it exists and/or it is an | |
| 92 // empty directory. | |
| 93 NewDestOperation()->RemoveDirectory( | |
| 94 dest_root_, base::Bind(&CrossOperationDelegate::DidTryRemoveDestRoot, | |
| 95 AsWeakPtr())); | |
| 96 } | |
| 97 | |
| 98 void CrossOperationDelegate::DidTryRemoveDestRoot( | |
| 99 base::PlatformFileError error) { | |
| 100 if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) { | |
| 101 callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); | |
| 102 return; | |
| 103 } | |
| 104 if (error != base::PLATFORM_FILE_OK && | |
| 105 error != base::PLATFORM_FILE_ERROR_NOT_FOUND) { | |
| 106 callback_.Run(error); | |
| 107 return; | |
| 108 } | |
| 109 | |
| 110 // Start to process the source directory recursively. | |
| 111 // TODO(kinuko): This could be too expensive for same_file_system_==true | |
| 112 // and operation==MOVE case, probably we can just rename the root directory. | |
| 113 // http://crbug.com/172187 | |
| 114 StartRecursiveOperation( | |
| 115 src_root_, base::Bind(&CrossOperationDelegate::DidFinishCopy, | |
| 116 AsWeakPtr(), src_root_, callback_)); | |
| 117 } | |
| 118 | |
| 119 void CrossOperationDelegate::CopyOrMoveFile(const URLPair& url_pair, | |
| 120 const StatusCallback& callback) { | |
| 121 // Same filesystem case. | |
| 122 if (same_file_system_) { | |
| 123 if (operation_type_ == OPERATION_MOVE) { | |
| 124 NewSourceOperation()->MoveFileLocal(url_pair.src, url_pair.dest, | |
| 125 callback); | |
| 126 } else { | |
| 127 NewSourceOperation()->CopyFileLocal(url_pair.src, url_pair.dest, | |
| 128 callback); | |
| 129 } | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 // Cross filesystem case. | |
| 134 // Perform CreateSnapshotFile, CopyInForeignFile and then calls | |
| 135 // copy_callback which removes the source file if operation_type == MOVE. | |
| 136 StatusCallback copy_callback = | |
| 137 base::Bind(&CrossOperationDelegate::DidFinishCopy, AsWeakPtr(), | |
| 138 url_pair.src, callback); | |
| 139 NewSourceOperation()->CreateSnapshotFile( | |
| 140 url_pair.src, | |
| 141 base::Bind(&CrossOperationDelegate::DidCreateSnapshot, AsWeakPtr(), | |
| 142 url_pair, copy_callback)); | |
| 143 } | |
| 144 | |
| 145 void CrossOperationDelegate::DidCreateSnapshot( | |
| 146 const URLPair& url_pair, | |
| 147 const StatusCallback& callback, | |
| 148 base::PlatformFileError error, | |
| 149 const base::PlatformFileInfo& file_info, | |
| 150 const base::FilePath& platform_path, | |
| 151 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) { | |
| 152 if (error != base::PLATFORM_FILE_OK) { | |
| 153 callback.Run(error); | |
| 154 return; | |
| 155 } | |
| 156 current_file_ref_ = file_ref; | |
| 157 | |
| 158 // For now we assume CreateSnapshotFile always return a valid local file path. | |
| 159 // TODO(kinuko): Otherwise create a FileStreamReader to perform a copy/move. | |
| 160 DCHECK(!platform_path.empty()); | |
| 161 | |
| 162 CopyOrMoveFileValidatorFactory* factory = | |
| 163 file_system_context()->GetCopyOrMoveFileValidatorFactory( | |
| 164 dest_root_.type(), &error); | |
| 165 if (error != base::PLATFORM_FILE_OK) { | |
| 166 callback.Run(error); | |
| 167 return; | |
| 168 } | |
| 169 if (!factory) { | |
| 170 DidValidateFile(url_pair.dest, callback, file_info, platform_path, error); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 validator_.reset( | |
| 175 factory->CreateCopyOrMoveFileValidator(url_pair.src, platform_path)); | |
| 176 validator_->StartValidation( | |
| 177 base::Bind(&CrossOperationDelegate::DidValidateFile, AsWeakPtr(), | |
| 178 url_pair.dest, callback, file_info, platform_path)); | |
| 179 } | |
| 180 | |
| 181 void CrossOperationDelegate::DidValidateFile( | |
| 182 const FileSystemURL& dest, | |
| 183 const StatusCallback& callback, | |
| 184 const base::PlatformFileInfo& file_info, | |
| 185 const base::FilePath& platform_path, | |
| 186 base::PlatformFileError error) { | |
| 187 if (error != base::PLATFORM_FILE_OK) { | |
| 188 callback.Run(error); | |
| 189 return; | |
| 190 } | |
| 191 | |
| 192 NewDestOperation()->CopyInForeignFile(platform_path, dest, callback); | |
| 193 } | |
| 194 | |
| 195 void CrossOperationDelegate::DidFinishCopy( | |
| 196 const FileSystemURL& src, | |
| 197 const StatusCallback& callback, | |
| 198 base::PlatformFileError error) { | |
| 199 if (error != base::PLATFORM_FILE_OK || | |
| 200 operation_type_ == OPERATION_COPY) { | |
| 201 callback.Run(error); | |
| 202 return; | |
| 203 } | |
| 204 | |
| 205 DCHECK_EQ(OPERATION_MOVE, operation_type_); | |
| 206 | |
| 207 // Remove the source for finalizing move operation. | |
| 208 NewSourceOperation()->Remove( | |
| 209 src, true /* recursive */, | |
| 210 base::Bind(&CrossOperationDelegate::DidRemoveSourceForMove, | |
| 211 AsWeakPtr(), callback)); | |
| 212 } | |
| 213 | |
| 214 void CrossOperationDelegate::DidRemoveSourceForMove( | |
| 215 const StatusCallback& callback, | |
| 216 base::PlatformFileError error) { | |
| 217 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) | |
| 218 error = base::PLATFORM_FILE_OK; | |
| 219 callback.Run(error); | |
| 220 } | |
| 221 | |
| 222 FileSystemURL CrossOperationDelegate::CreateDestURL( | |
| 223 const FileSystemURL& src_url) const { | |
| 224 DCHECK_EQ(src_root_.type(), src_url.type()); | |
| 225 DCHECK_EQ(src_root_.origin(), src_url.origin()); | |
| 226 | |
| 227 base::FilePath relative = dest_root_.virtual_path(); | |
| 228 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(), | |
| 229 &relative); | |
| 230 return file_system_context()->CreateCrackedFileSystemURL( | |
| 231 dest_root_.origin(), | |
| 232 dest_root_.mount_type(), | |
| 233 relative); | |
| 234 } | |
| 235 | |
| 236 LocalFileSystemOperation* CrossOperationDelegate::NewDestOperation() { | |
| 237 return NewNestedOperation(); | |
| 238 } | |
| 239 | |
| 240 LocalFileSystemOperation* CrossOperationDelegate::NewSourceOperation() { | |
| 241 if (same_file_system_) | |
| 242 return NewDestOperation(); | |
| 243 return src_root_operation_->CreateNestedOperation(); | |
| 244 } | |
| 245 | |
| 246 } // namespace fileapi | |
| OLD | NEW |