Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: webkit/browser/fileapi/copy_or_move_operation_delegate.cc

Issue 539143002: Migrate webkit/browser/ to storage/browser/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/copy_or_move_operation_delegate.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "net/base/io_buffer.h"
10 #include "net/base/net_errors.h"
11 #include "webkit/browser/blob/file_stream_reader.h"
12 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
13 #include "webkit/browser/fileapi/file_stream_writer.h"
14 #include "webkit/browser/fileapi/file_system_context.h"
15 #include "webkit/browser/fileapi/file_system_operation_runner.h"
16 #include "webkit/browser/fileapi/file_system_url.h"
17 #include "webkit/browser/fileapi/recursive_operation_delegate.h"
18 #include "webkit/common/blob/shareable_file_reference.h"
19 #include "webkit/common/fileapi/file_system_util.h"
20
21 namespace storage {
22
23 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
24
25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
26 public:
27 virtual ~CopyOrMoveImpl() {}
28 virtual void Run(
29 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
30 virtual void Cancel() = 0;
31
32 protected:
33 CopyOrMoveImpl() {}
34
35 private:
36 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
37 };
38
39 namespace {
40
41 // Copies a file on a (same) file system. Just delegate the operation to
42 // |operation_runner|.
43 class CopyOrMoveOnSameFileSystemImpl
44 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
45 public:
46 CopyOrMoveOnSameFileSystemImpl(
47 FileSystemOperationRunner* operation_runner,
48 CopyOrMoveOperationDelegate::OperationType operation_type,
49 const FileSystemURL& src_url,
50 const FileSystemURL& dest_url,
51 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
52 const FileSystemOperation::CopyFileProgressCallback&
53 file_progress_callback)
54 : operation_runner_(operation_runner),
55 operation_type_(operation_type),
56 src_url_(src_url),
57 dest_url_(dest_url),
58 option_(option),
59 file_progress_callback_(file_progress_callback) {
60 }
61
62 virtual void Run(
63 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
64 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
65 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
66 } else {
67 operation_runner_->CopyFileLocal(
68 src_url_, dest_url_, option_, file_progress_callback_, callback);
69 }
70 }
71
72 virtual void Cancel() OVERRIDE {
73 // We can do nothing for the copy/move operation on a local file system.
74 // Assuming the operation is quickly done, it should be ok to just wait
75 // for the completion.
76 }
77
78 private:
79 FileSystemOperationRunner* operation_runner_;
80 CopyOrMoveOperationDelegate::OperationType operation_type_;
81 FileSystemURL src_url_;
82 FileSystemURL dest_url_;
83 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
84 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
85 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
86 };
87
88 // Specifically for cross file system copy/move operation, this class creates
89 // a snapshot file, validates it if necessary, runs copying process,
90 // validates the created file, and removes source file for move (noop for
91 // copy).
92 class SnapshotCopyOrMoveImpl
93 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
94 public:
95 SnapshotCopyOrMoveImpl(
96 FileSystemOperationRunner* operation_runner,
97 CopyOrMoveOperationDelegate::OperationType operation_type,
98 const FileSystemURL& src_url,
99 const FileSystemURL& dest_url,
100 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
101 CopyOrMoveFileValidatorFactory* validator_factory,
102 const FileSystemOperation::CopyFileProgressCallback&
103 file_progress_callback)
104 : operation_runner_(operation_runner),
105 operation_type_(operation_type),
106 src_url_(src_url),
107 dest_url_(dest_url),
108 option_(option),
109 validator_factory_(validator_factory),
110 file_progress_callback_(file_progress_callback),
111 cancel_requested_(false),
112 weak_factory_(this) {
113 }
114
115 virtual void Run(
116 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
117 file_progress_callback_.Run(0);
118 operation_runner_->CreateSnapshotFile(
119 src_url_,
120 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
121 weak_factory_.GetWeakPtr(), callback));
122 }
123
124 virtual void Cancel() OVERRIDE {
125 cancel_requested_ = true;
126 }
127
128 private:
129 void RunAfterCreateSnapshot(
130 const CopyOrMoveOperationDelegate::StatusCallback& callback,
131 base::File::Error error,
132 const base::File::Info& file_info,
133 const base::FilePath& platform_path,
134 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
135 if (cancel_requested_)
136 error = base::File::FILE_ERROR_ABORT;
137
138 if (error != base::File::FILE_OK) {
139 callback.Run(error);
140 return;
141 }
142
143 // For now we assume CreateSnapshotFile always return a valid local file
144 // path.
145 DCHECK(!platform_path.empty());
146
147 if (!validator_factory_) {
148 // No validation is needed.
149 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
150 base::File::FILE_OK);
151 return;
152 }
153
154 // Run pre write validation.
155 PreWriteValidation(
156 platform_path,
157 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
158 weak_factory_.GetWeakPtr(),
159 platform_path, file_info, file_ref, callback));
160 }
161
162 void RunAfterPreWriteValidation(
163 const base::FilePath& platform_path,
164 const base::File::Info& file_info,
165 const scoped_refptr<storage::ShareableFileReference>& file_ref,
166 const CopyOrMoveOperationDelegate::StatusCallback& callback,
167 base::File::Error error) {
168 if (cancel_requested_)
169 error = base::File::FILE_ERROR_ABORT;
170
171 if (error != base::File::FILE_OK) {
172 callback.Run(error);
173 return;
174 }
175
176 // |file_ref| is unused but necessary to keep the file alive until
177 // CopyInForeignFile() is completed.
178 operation_runner_->CopyInForeignFile(
179 platform_path, dest_url_,
180 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
181 weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
182 }
183
184 void RunAfterCopyInForeignFile(
185 const base::File::Info& file_info,
186 const scoped_refptr<storage::ShareableFileReference>& file_ref,
187 const CopyOrMoveOperationDelegate::StatusCallback& callback,
188 base::File::Error error) {
189 if (cancel_requested_)
190 error = base::File::FILE_ERROR_ABORT;
191
192 if (error != base::File::FILE_OK) {
193 callback.Run(error);
194 return;
195 }
196
197 file_progress_callback_.Run(file_info.size);
198
199 if (option_ == FileSystemOperation::OPTION_NONE) {
200 RunAfterTouchFile(callback, base::File::FILE_OK);
201 return;
202 }
203
204 operation_runner_->TouchFile(
205 dest_url_, base::Time::Now() /* last_access */,
206 file_info.last_modified,
207 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile,
208 weak_factory_.GetWeakPtr(), callback));
209 }
210
211 void RunAfterTouchFile(
212 const CopyOrMoveOperationDelegate::StatusCallback& callback,
213 base::File::Error error) {
214 // Even if TouchFile is failed, just ignore it.
215
216 if (cancel_requested_) {
217 callback.Run(base::File::FILE_ERROR_ABORT);
218 return;
219 }
220
221 // |validator_| is NULL when the destination filesystem does not do
222 // validation.
223 if (!validator_) {
224 // No validation is needed.
225 RunAfterPostWriteValidation(callback, base::File::FILE_OK);
226 return;
227 }
228
229 PostWriteValidation(
230 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
231 weak_factory_.GetWeakPtr(), callback));
232 }
233
234 void RunAfterPostWriteValidation(
235 const CopyOrMoveOperationDelegate::StatusCallback& callback,
236 base::File::Error error) {
237 if (cancel_requested_) {
238 callback.Run(base::File::FILE_ERROR_ABORT);
239 return;
240 }
241
242 if (error != base::File::FILE_OK) {
243 // Failed to validate. Remove the destination file.
244 operation_runner_->Remove(
245 dest_url_, true /* recursive */,
246 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
247 weak_factory_.GetWeakPtr(), error, callback));
248 return;
249 }
250
251 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
252 callback.Run(base::File::FILE_OK);
253 return;
254 }
255
256 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
257
258 // Remove the source for finalizing move operation.
259 operation_runner_->Remove(
260 src_url_, true /* recursive */,
261 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
262 weak_factory_.GetWeakPtr(), callback));
263 }
264
265 void RunAfterRemoveSourceForMove(
266 const CopyOrMoveOperationDelegate::StatusCallback& callback,
267 base::File::Error error) {
268 if (cancel_requested_)
269 error = base::File::FILE_ERROR_ABORT;
270
271 if (error == base::File::FILE_ERROR_NOT_FOUND)
272 error = base::File::FILE_OK;
273 callback.Run(error);
274 }
275
276 void DidRemoveDestForError(
277 base::File::Error prior_error,
278 const CopyOrMoveOperationDelegate::StatusCallback& callback,
279 base::File::Error error) {
280 if (error != base::File::FILE_OK) {
281 VLOG(1) << "Error removing destination file after validation error: "
282 << error;
283 }
284 callback.Run(prior_error);
285 }
286
287 // Runs pre-write validation.
288 void PreWriteValidation(
289 const base::FilePath& platform_path,
290 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
291 DCHECK(validator_factory_);
292 validator_.reset(
293 validator_factory_->CreateCopyOrMoveFileValidator(
294 src_url_, platform_path));
295 validator_->StartPreWriteValidation(callback);
296 }
297
298 // Runs post-write validation.
299 void PostWriteValidation(
300 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
301 operation_runner_->CreateSnapshotFile(
302 dest_url_,
303 base::Bind(
304 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
305 weak_factory_.GetWeakPtr(), callback));
306 }
307
308 void PostWriteValidationAfterCreateSnapshotFile(
309 const CopyOrMoveOperationDelegate::StatusCallback& callback,
310 base::File::Error error,
311 const base::File::Info& file_info,
312 const base::FilePath& platform_path,
313 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
314 if (cancel_requested_)
315 error = base::File::FILE_ERROR_ABORT;
316
317 if (error != base::File::FILE_OK) {
318 callback.Run(error);
319 return;
320 }
321
322 DCHECK(validator_);
323 // Note: file_ref passed here to keep the file alive until after
324 // the StartPostWriteValidation operation finishes.
325 validator_->StartPostWriteValidation(
326 platform_path,
327 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
328 weak_factory_.GetWeakPtr(), file_ref, callback));
329 }
330
331 // |file_ref| is unused; it is passed here to make sure the reference is
332 // alive until after post-write validation is complete.
333 void DidPostWriteValidation(
334 const scoped_refptr<storage::ShareableFileReference>& file_ref,
335 const CopyOrMoveOperationDelegate::StatusCallback& callback,
336 base::File::Error error) {
337 callback.Run(error);
338 }
339
340 FileSystemOperationRunner* operation_runner_;
341 CopyOrMoveOperationDelegate::OperationType operation_type_;
342 FileSystemURL src_url_;
343 FileSystemURL dest_url_;
344
345 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
346 CopyOrMoveFileValidatorFactory* validator_factory_;
347 scoped_ptr<CopyOrMoveFileValidator> validator_;
348 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
349 bool cancel_requested_;
350 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
351 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
352 };
353
354 // The size of buffer for StreamCopyHelper.
355 const int kReadBufferSize = 32768;
356
357 // To avoid too many progress callbacks, it should be called less
358 // frequently than 50ms.
359 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
360
361 // Specifically for cross file system copy/move operation, this class uses
362 // stream reader and writer for copying. Validator is not supported, so if
363 // necessary SnapshotCopyOrMoveImpl should be used.
364 class StreamCopyOrMoveImpl
365 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
366 public:
367 StreamCopyOrMoveImpl(
368 FileSystemOperationRunner* operation_runner,
369 CopyOrMoveOperationDelegate::OperationType operation_type,
370 const FileSystemURL& src_url,
371 const FileSystemURL& dest_url,
372 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
373 scoped_ptr<storage::FileStreamReader> reader,
374 scoped_ptr<FileStreamWriter> writer,
375 const FileSystemOperation::CopyFileProgressCallback&
376 file_progress_callback)
377 : operation_runner_(operation_runner),
378 operation_type_(operation_type),
379 src_url_(src_url),
380 dest_url_(dest_url),
381 option_(option),
382 reader_(reader.Pass()),
383 writer_(writer.Pass()),
384 file_progress_callback_(file_progress_callback),
385 cancel_requested_(false),
386 weak_factory_(this) {}
387
388 virtual void Run(
389 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
390 // Reader can be created even if the entry does not exist or the entry is
391 // a directory. To check errors before destination file creation,
392 // check metadata first.
393 operation_runner_->GetMetadata(
394 src_url_,
395 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
396 weak_factory_.GetWeakPtr(), callback));
397 }
398
399 virtual void Cancel() OVERRIDE {
400 cancel_requested_ = true;
401 if (copy_helper_)
402 copy_helper_->Cancel();
403 }
404
405 private:
406 void RunAfterGetMetadataForSource(
407 const CopyOrMoveOperationDelegate::StatusCallback& callback,
408 base::File::Error error,
409 const base::File::Info& file_info) {
410 if (cancel_requested_)
411 error = base::File::FILE_ERROR_ABORT;
412
413 if (error != base::File::FILE_OK) {
414 callback.Run(error);
415 return;
416 }
417
418 if (file_info.is_directory) {
419 // If not a directory, failed with appropriate error code.
420 callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
421 return;
422 }
423
424 // To use FileStreamWriter, we need to ensure the destination file exists.
425 operation_runner_->CreateFile(
426 dest_url_,
427 true /* exclusive */,
428 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
429 weak_factory_.GetWeakPtr(),
430 callback,
431 file_info.last_modified));
432 }
433
434 void RunAfterCreateFileForDestination(
435 const CopyOrMoveOperationDelegate::StatusCallback& callback,
436 const base::Time& last_modified,
437 base::File::Error error) {
438 if (cancel_requested_)
439 error = base::File::FILE_ERROR_ABORT;
440
441 if (error != base::File::FILE_OK &&
442 error != base::File::FILE_ERROR_EXISTS) {
443 callback.Run(error);
444 return;
445 }
446
447 if (error == base::File::FILE_ERROR_EXISTS) {
448 operation_runner_->Truncate(
449 dest_url_,
450 0 /* length */,
451 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
452 weak_factory_.GetWeakPtr(),
453 callback,
454 last_modified));
455 return;
456 }
457 RunAfterTruncateForDestination(
458 callback, last_modified, base::File::FILE_OK);
459 }
460
461 void RunAfterTruncateForDestination(
462 const CopyOrMoveOperationDelegate::StatusCallback& callback,
463 const base::Time& last_modified,
464 base::File::Error error) {
465 if (cancel_requested_)
466 error = base::File::FILE_ERROR_ABORT;
467
468 if (error != base::File::FILE_OK) {
469 callback.Run(error);
470 return;
471 }
472
473 const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
474 storage::COPY_SYNC_OPTION_SYNC;
475
476 DCHECK(!copy_helper_);
477 copy_helper_.reset(
478 new CopyOrMoveOperationDelegate::StreamCopyHelper(
479 reader_.Pass(), writer_.Pass(),
480 need_flush,
481 kReadBufferSize,
482 file_progress_callback_,
483 base::TimeDelta::FromMilliseconds(
484 kMinProgressCallbackInvocationSpanInMilliseconds)));
485 copy_helper_->Run(
486 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
487 weak_factory_.GetWeakPtr(), callback, last_modified));
488 }
489
490 void RunAfterStreamCopy(
491 const CopyOrMoveOperationDelegate::StatusCallback& callback,
492 const base::Time& last_modified,
493 base::File::Error error) {
494 if (cancel_requested_)
495 error = base::File::FILE_ERROR_ABORT;
496
497 if (error != base::File::FILE_OK) {
498 callback.Run(error);
499 return;
500 }
501
502 if (option_ == FileSystemOperation::OPTION_NONE) {
503 RunAfterTouchFile(callback, base::File::FILE_OK);
504 return;
505 }
506
507 operation_runner_->TouchFile(
508 dest_url_, base::Time::Now() /* last_access */, last_modified,
509 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
510 weak_factory_.GetWeakPtr(), callback));
511 }
512
513 void RunAfterTouchFile(
514 const CopyOrMoveOperationDelegate::StatusCallback& callback,
515 base::File::Error error) {
516 // Even if TouchFile is failed, just ignore it.
517 if (cancel_requested_) {
518 callback.Run(base::File::FILE_ERROR_ABORT);
519 return;
520 }
521
522 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
523 callback.Run(base::File::FILE_OK);
524 return;
525 }
526
527 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
528
529 // Remove the source for finalizing move operation.
530 operation_runner_->Remove(
531 src_url_, false /* recursive */,
532 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
533 weak_factory_.GetWeakPtr(), callback));
534 }
535
536 void RunAfterRemoveForMove(
537 const CopyOrMoveOperationDelegate::StatusCallback& callback,
538 base::File::Error error) {
539 if (cancel_requested_)
540 error = base::File::FILE_ERROR_ABORT;
541 if (error == base::File::FILE_ERROR_NOT_FOUND)
542 error = base::File::FILE_OK;
543 callback.Run(error);
544 }
545
546 FileSystemOperationRunner* operation_runner_;
547 CopyOrMoveOperationDelegate::OperationType operation_type_;
548 FileSystemURL src_url_;
549 FileSystemURL dest_url_;
550 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
551 scoped_ptr<storage::FileStreamReader> reader_;
552 scoped_ptr<FileStreamWriter> writer_;
553 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
554 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
555 bool cancel_requested_;
556 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
557 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
558 };
559
560 } // namespace
561
562 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
563 scoped_ptr<storage::FileStreamReader> reader,
564 scoped_ptr<FileStreamWriter> writer,
565 bool need_flush,
566 int buffer_size,
567 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
568 const base::TimeDelta& min_progress_callback_invocation_span)
569 : reader_(reader.Pass()),
570 writer_(writer.Pass()),
571 need_flush_(need_flush),
572 file_progress_callback_(file_progress_callback),
573 io_buffer_(new net::IOBufferWithSize(buffer_size)),
574 num_copied_bytes_(0),
575 previous_flush_offset_(0),
576 min_progress_callback_invocation_span_(
577 min_progress_callback_invocation_span),
578 cancel_requested_(false),
579 weak_factory_(this) {
580 }
581
582 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
583 }
584
585 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
586 const StatusCallback& callback) {
587 file_progress_callback_.Run(0);
588 last_progress_callback_invocation_time_ = base::Time::Now();
589 Read(callback);
590 }
591
592 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
593 cancel_requested_ = true;
594 }
595
596 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
597 const StatusCallback& callback) {
598 int result = reader_->Read(
599 io_buffer_.get(), io_buffer_->size(),
600 base::Bind(&StreamCopyHelper::DidRead,
601 weak_factory_.GetWeakPtr(), callback));
602 if (result != net::ERR_IO_PENDING)
603 DidRead(callback, result);
604 }
605
606 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
607 const StatusCallback& callback, int result) {
608 if (cancel_requested_) {
609 callback.Run(base::File::FILE_ERROR_ABORT);
610 return;
611 }
612
613 if (result < 0) {
614 callback.Run(NetErrorToFileError(result));
615 return;
616 }
617
618 if (result == 0) {
619 // Here is the EOF.
620 if (need_flush_)
621 Flush(callback, true /* is_eof */);
622 else
623 callback.Run(base::File::FILE_OK);
624 return;
625 }
626
627 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
628 }
629
630 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
631 const StatusCallback& callback,
632 scoped_refptr<net::DrainableIOBuffer> buffer) {
633 DCHECK_GT(buffer->BytesRemaining(), 0);
634
635 int result = writer_->Write(
636 buffer.get(), buffer->BytesRemaining(),
637 base::Bind(&StreamCopyHelper::DidWrite,
638 weak_factory_.GetWeakPtr(), callback, buffer));
639 if (result != net::ERR_IO_PENDING)
640 DidWrite(callback, buffer, result);
641 }
642
643 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
644 const StatusCallback& callback,
645 scoped_refptr<net::DrainableIOBuffer> buffer,
646 int result) {
647 if (cancel_requested_) {
648 callback.Run(base::File::FILE_ERROR_ABORT);
649 return;
650 }
651
652 if (result < 0) {
653 callback.Run(NetErrorToFileError(result));
654 return;
655 }
656
657 buffer->DidConsume(result);
658 num_copied_bytes_ += result;
659
660 // Check the elapsed time since last |file_progress_callback_| invocation.
661 base::Time now = base::Time::Now();
662 if (now - last_progress_callback_invocation_time_ >=
663 min_progress_callback_invocation_span_) {
664 file_progress_callback_.Run(num_copied_bytes_);
665 last_progress_callback_invocation_time_ = now;
666 }
667
668 if (buffer->BytesRemaining() > 0) {
669 Write(callback, buffer);
670 return;
671 }
672
673 if (need_flush_ &&
674 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
675 Flush(callback, false /* not is_eof */);
676 } else {
677 Read(callback);
678 }
679 }
680
681 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
682 const StatusCallback& callback, bool is_eof) {
683 int result = writer_->Flush(
684 base::Bind(&StreamCopyHelper::DidFlush,
685 weak_factory_.GetWeakPtr(), callback, is_eof));
686 if (result != net::ERR_IO_PENDING)
687 DidFlush(callback, is_eof, result);
688 }
689
690 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
691 const StatusCallback& callback, bool is_eof, int result) {
692 if (cancel_requested_) {
693 callback.Run(base::File::FILE_ERROR_ABORT);
694 return;
695 }
696
697 previous_flush_offset_ = num_copied_bytes_;
698 if (is_eof)
699 callback.Run(NetErrorToFileError(result));
700 else
701 Read(callback);
702 }
703
704 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
705 FileSystemContext* file_system_context,
706 const FileSystemURL& src_root,
707 const FileSystemURL& dest_root,
708 OperationType operation_type,
709 CopyOrMoveOption option,
710 const CopyProgressCallback& progress_callback,
711 const StatusCallback& callback)
712 : RecursiveOperationDelegate(file_system_context),
713 src_root_(src_root),
714 dest_root_(dest_root),
715 operation_type_(operation_type),
716 option_(option),
717 progress_callback_(progress_callback),
718 callback_(callback),
719 weak_factory_(this) {
720 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
721 }
722
723 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
724 STLDeleteElements(&running_copy_set_);
725 }
726
727 void CopyOrMoveOperationDelegate::Run() {
728 // Not supported; this should never be called.
729 NOTREACHED();
730 }
731
732 void CopyOrMoveOperationDelegate::RunRecursively() {
733 // Perform light-weight checks first.
734
735 // It is an error to try to copy/move an entry into its child.
736 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
737 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
738 return;
739 }
740
741 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
742 // In JS API this should return error, but we return success because Pepper
743 // wants to return success and we have a code path that returns error in
744 // Blink for JS (http://crbug.com/329517).
745 callback_.Run(base::File::FILE_OK);
746 return;
747 }
748
749 // Start to process the source directory recursively.
750 // TODO(kinuko): This could be too expensive for same_file_system_==true
751 // and operation==MOVE case, probably we can just rename the root directory.
752 // http://crbug.com/172187
753 StartRecursiveOperation(src_root_, callback_);
754 }
755
756 void CopyOrMoveOperationDelegate::ProcessFile(
757 const FileSystemURL& src_url,
758 const StatusCallback& callback) {
759 if (!progress_callback_.is_null()) {
760 progress_callback_.Run(
761 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
762 }
763
764 FileSystemURL dest_url = CreateDestURL(src_url);
765 CopyOrMoveImpl* impl = NULL;
766 if (same_file_system_ &&
767 (file_system_context()
768 ->GetFileSystemBackend(src_url.type())
769 ->HasInplaceCopyImplementation(src_url.type()) ||
770 operation_type_ == OperationType::OPERATION_MOVE)) {
771 impl = new CopyOrMoveOnSameFileSystemImpl(
772 operation_runner(), operation_type_, src_url, dest_url, option_,
773 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
774 weak_factory_.GetWeakPtr(), src_url));
775 } else {
776 // Cross filesystem case.
777 base::File::Error error = base::File::FILE_ERROR_FAILED;
778 CopyOrMoveFileValidatorFactory* validator_factory =
779 file_system_context()->GetCopyOrMoveFileValidatorFactory(
780 dest_root_.type(), &error);
781 if (error != base::File::FILE_OK) {
782 callback.Run(error);
783 return;
784 }
785
786 if (!validator_factory) {
787 scoped_ptr<storage::FileStreamReader> reader =
788 file_system_context()->CreateFileStreamReader(
789 src_url, 0, base::Time());
790 scoped_ptr<FileStreamWriter> writer =
791 file_system_context()->CreateFileStreamWriter(dest_url, 0);
792 if (reader && writer) {
793 impl = new StreamCopyOrMoveImpl(
794 operation_runner(), operation_type_, src_url, dest_url, option_,
795 reader.Pass(), writer.Pass(),
796 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
797 weak_factory_.GetWeakPtr(), src_url));
798 }
799 }
800
801 if (!impl) {
802 impl = new SnapshotCopyOrMoveImpl(
803 operation_runner(), operation_type_, src_url, dest_url, option_,
804 validator_factory,
805 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
806 weak_factory_.GetWeakPtr(), src_url));
807 }
808 }
809
810 // Register the running task.
811 running_copy_set_.insert(impl);
812 impl->Run(base::Bind(
813 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
814 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
815 }
816
817 void CopyOrMoveOperationDelegate::ProcessDirectory(
818 const FileSystemURL& src_url,
819 const StatusCallback& callback) {
820 if (src_url == src_root_) {
821 // The src_root_ looks to be a directory.
822 // Try removing the dest_root_ to see if it exists and/or it is an
823 // empty directory.
824 // We do not invoke |progress_callback_| for source root, because it is
825 // already called in ProcessFile().
826 operation_runner()->RemoveDirectory(
827 dest_root_,
828 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
829 weak_factory_.GetWeakPtr(), callback));
830 return;
831 }
832
833 if (!progress_callback_.is_null()) {
834 progress_callback_.Run(
835 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
836 }
837
838 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
839 }
840
841 void CopyOrMoveOperationDelegate::PostProcessDirectory(
842 const FileSystemURL& src_url,
843 const StatusCallback& callback) {
844 if (option_ == FileSystemOperation::OPTION_NONE) {
845 PostProcessDirectoryAfterTouchFile(
846 src_url, callback, base::File::FILE_OK);
847 return;
848 }
849
850 operation_runner()->GetMetadata(
851 src_url,
852 base::Bind(
853 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
854 weak_factory_.GetWeakPtr(), src_url, callback));
855 }
856
857 void CopyOrMoveOperationDelegate::OnCancel() {
858 // Request to cancel all running Copy/Move file.
859 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
860 iter != running_copy_set_.end(); ++iter)
861 (*iter)->Cancel();
862 }
863
864 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
865 const FileSystemURL& src_url,
866 const FileSystemURL& dest_url,
867 const StatusCallback& callback,
868 CopyOrMoveImpl* impl,
869 base::File::Error error) {
870 running_copy_set_.erase(impl);
871 delete impl;
872
873 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
874 progress_callback_.Run(
875 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
876 }
877
878 callback.Run(error);
879 }
880
881 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
882 const StatusCallback& callback,
883 base::File::Error error) {
884 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
885 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
886 return;
887 }
888 if (error != base::File::FILE_OK &&
889 error != base::File::FILE_ERROR_NOT_FOUND) {
890 callback_.Run(error);
891 return;
892 }
893
894 ProcessDirectoryInternal(src_root_, dest_root_, callback);
895 }
896
897 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
898 const FileSystemURL& src_url,
899 const FileSystemURL& dest_url,
900 const StatusCallback& callback) {
901 // If operation_type == Move we may need to record directories and
902 // restore directory timestamps in the end, though it may have
903 // negative performance impact.
904 // See http://crbug.com/171284 for more details.
905 operation_runner()->CreateDirectory(
906 dest_url, false /* exclusive */, false /* recursive */,
907 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
908 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
909 }
910
911 void CopyOrMoveOperationDelegate::DidCreateDirectory(
912 const FileSystemURL& src_url,
913 const FileSystemURL& dest_url,
914 const StatusCallback& callback,
915 base::File::Error error) {
916 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
917 progress_callback_.Run(
918 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
919 }
920
921 callback.Run(error);
922 }
923
924 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
925 const FileSystemURL& src_url,
926 const StatusCallback& callback,
927 base::File::Error error,
928 const base::File::Info& file_info) {
929 if (error != base::File::FILE_OK) {
930 // Ignore the error, and run post process which should run after TouchFile.
931 PostProcessDirectoryAfterTouchFile(
932 src_url, callback, base::File::FILE_OK);
933 return;
934 }
935
936 operation_runner()->TouchFile(
937 CreateDestURL(src_url), base::Time::Now() /* last access */,
938 file_info.last_modified,
939 base::Bind(
940 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
941 weak_factory_.GetWeakPtr(), src_url, callback));
942 }
943
944 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
945 const FileSystemURL& src_url,
946 const StatusCallback& callback,
947 base::File::Error error) {
948 // Even if the TouchFile is failed, just ignore it.
949
950 if (operation_type_ == OPERATION_COPY) {
951 callback.Run(base::File::FILE_OK);
952 return;
953 }
954
955 DCHECK_EQ(OPERATION_MOVE, operation_type_);
956
957 // All files and subdirectories in the directory should be moved here,
958 // so remove the source directory for finalizing move operation.
959 operation_runner()->Remove(
960 src_url, false /* recursive */,
961 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
962 weak_factory_.GetWeakPtr(), callback));
963 }
964
965 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
966 const StatusCallback& callback,
967 base::File::Error error) {
968 if (error == base::File::FILE_ERROR_NOT_FOUND)
969 error = base::File::FILE_OK;
970 callback.Run(error);
971 }
972
973 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
974 const FileSystemURL& src_url, int64 size) {
975 if (!progress_callback_.is_null()) {
976 progress_callback_.Run(
977 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
978 }
979 }
980
981 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
982 const FileSystemURL& src_url) const {
983 DCHECK_EQ(src_root_.type(), src_url.type());
984 DCHECK_EQ(src_root_.origin(), src_url.origin());
985
986 base::FilePath relative = dest_root_.virtual_path();
987 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
988 &relative);
989 return file_system_context()->CreateCrackedFileSystemURL(
990 dest_root_.origin(),
991 dest_root_.mount_type(),
992 relative);
993 }
994
995 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698