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/fileapi/cross_operation_delegate.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/files/file_path.h" | |
9 #include "webkit/blob/shareable_file_reference.h" | |
10 #include "webkit/fileapi/copy_or_move_file_validator.h" | |
11 #include "webkit/fileapi/file_system_context.h" | |
12 #include "webkit/fileapi/file_system_operation_context.h" | |
13 #include "webkit/fileapi/file_system_url.h" | |
14 #include "webkit/fileapi/file_system_util.h" | |
15 #include "webkit/fileapi/local_file_system_operation.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_ = AreSameFileSystem(src_root_, 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 |