OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h" | 5 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/files/file_path.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" |
9 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" | 12 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" |
| 13 #include "webkit/browser/fileapi/file_stream_writer.h" |
10 #include "webkit/browser/fileapi/file_system_context.h" | 14 #include "webkit/browser/fileapi/file_system_context.h" |
11 #include "webkit/browser/fileapi/file_system_operation_runner.h" | 15 #include "webkit/browser/fileapi/file_system_operation_runner.h" |
12 #include "webkit/browser/fileapi/file_system_url.h" | 16 #include "webkit/browser/fileapi/file_system_url.h" |
13 #include "webkit/browser/fileapi/recursive_operation_delegate.h" | 17 #include "webkit/browser/fileapi/recursive_operation_delegate.h" |
14 #include "webkit/common/blob/shareable_file_reference.h" | 18 #include "webkit/common/blob/shareable_file_reference.h" |
15 #include "webkit/common/fileapi/file_system_util.h" | 19 #include "webkit/common/fileapi/file_system_util.h" |
16 | 20 |
17 namespace fileapi { | 21 namespace fileapi { |
18 | 22 |
19 class CopyOrMoveOperationDelegate::CopyOrMoveImpl { | 23 class CopyOrMoveOperationDelegate::CopyOrMoveImpl { |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 // TODO(hidehiko): Implement the option's behavior. | 286 // TODO(hidehiko): Implement the option's behavior. |
283 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; | 287 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; |
284 CopyOrMoveFileValidatorFactory* validator_factory_; | 288 CopyOrMoveFileValidatorFactory* validator_factory_; |
285 scoped_ptr<CopyOrMoveFileValidator> validator_; | 289 scoped_ptr<CopyOrMoveFileValidator> validator_; |
286 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; | 290 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; |
287 | 291 |
288 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_; | 292 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_; |
289 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl); | 293 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl); |
290 }; | 294 }; |
291 | 295 |
| 296 // The size of buffer for StreamCopyHelper. |
| 297 const int kReadBufferSize = 32768; |
| 298 |
| 299 // To avoid too many progress callbacks, it should be called less |
| 300 // frequently than 50ms. |
| 301 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50; |
| 302 |
| 303 // Specifically for cross file system copy/move operation, this class uses |
| 304 // stream reader and writer for copying. Validator is not supported, so if |
| 305 // necessary SnapshotCopyOrMoveImpl should be used. |
| 306 class StreamCopyOrMoveImpl |
| 307 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { |
| 308 public: |
| 309 StreamCopyOrMoveImpl( |
| 310 FileSystemOperationRunner* operation_runner, |
| 311 CopyOrMoveOperationDelegate::OperationType operation_type, |
| 312 const FileSystemURL& src_url, |
| 313 const FileSystemURL& dest_url, |
| 314 scoped_ptr<webkit_blob::FileStreamReader> reader, |
| 315 scoped_ptr<FileStreamWriter> writer, |
| 316 const FileSystemOperation::CopyFileProgressCallback& |
| 317 file_progress_callback) |
| 318 : operation_runner_(operation_runner), |
| 319 operation_type_(operation_type), |
| 320 src_url_(src_url), |
| 321 dest_url_(dest_url), |
| 322 reader_(reader.Pass()), |
| 323 writer_(writer.Pass()), |
| 324 file_progress_callback_(file_progress_callback), |
| 325 weak_factory_(this) { |
| 326 } |
| 327 |
| 328 virtual void Run( |
| 329 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { |
| 330 // Reader can be created even if the entry does not exist or the entry is |
| 331 // a directory. To check errors before destination file creation, |
| 332 // check metadata first. |
| 333 operation_runner_->GetMetadata( |
| 334 src_url_, |
| 335 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource, |
| 336 weak_factory_.GetWeakPtr(), callback)); |
| 337 } |
| 338 |
| 339 private: |
| 340 void RunAfterGetMetadataForSource( |
| 341 const CopyOrMoveOperationDelegate::StatusCallback& callback, |
| 342 base::PlatformFileError error, |
| 343 const base::PlatformFileInfo& file_info) { |
| 344 if (error != base::PLATFORM_FILE_OK) { |
| 345 callback.Run(error); |
| 346 return; |
| 347 } |
| 348 if (file_info.is_directory) { |
| 349 // If not a directory, failed with appropriate error code. |
| 350 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE); |
| 351 return; |
| 352 } |
| 353 |
| 354 // To use FileStreamWriter, we need to ensure the destination file exists. |
| 355 operation_runner_->CreateFile( |
| 356 dest_url_, false /* exclusive */, |
| 357 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination, |
| 358 weak_factory_.GetWeakPtr(), callback)); |
| 359 } |
| 360 |
| 361 void RunAfterCreateFileForDestination( |
| 362 const CopyOrMoveOperationDelegate::StatusCallback& callback, |
| 363 base::PlatformFileError error) { |
| 364 if (error != base::PLATFORM_FILE_OK) { |
| 365 callback.Run(error); |
| 366 return; |
| 367 } |
| 368 |
| 369 DCHECK(!copy_helper_); |
| 370 copy_helper_.reset( |
| 371 new CopyOrMoveOperationDelegate::StreamCopyHelper( |
| 372 reader_.Pass(), writer_.Pass(), |
| 373 kReadBufferSize, |
| 374 file_progress_callback_, |
| 375 base::TimeDelta::FromMilliseconds( |
| 376 kMinProgressCallbackInvocationSpanInMilliseconds))); |
| 377 copy_helper_->Run( |
| 378 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy, |
| 379 weak_factory_.GetWeakPtr(), callback)); |
| 380 } |
| 381 |
| 382 void RunAfterStreamCopy( |
| 383 const CopyOrMoveOperationDelegate::StatusCallback& callback, |
| 384 base::PlatformFileError error) { |
| 385 if (error != base::PLATFORM_FILE_OK) { |
| 386 callback.Run(error); |
| 387 return; |
| 388 } |
| 389 |
| 390 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) { |
| 391 callback.Run(base::PLATFORM_FILE_OK); |
| 392 return; |
| 393 } |
| 394 |
| 395 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_); |
| 396 |
| 397 // Remove the source for finalizing move operation. |
| 398 operation_runner_->Remove( |
| 399 src_url_, false /* recursive */, |
| 400 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove, |
| 401 weak_factory_.GetWeakPtr(), callback)); |
| 402 } |
| 403 |
| 404 void RunAfterRemoveForMove( |
| 405 const CopyOrMoveOperationDelegate::StatusCallback& callback, |
| 406 base::PlatformFileError error) { |
| 407 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) |
| 408 error = base::PLATFORM_FILE_OK; |
| 409 callback.Run(error); |
| 410 } |
| 411 |
| 412 FileSystemOperationRunner* operation_runner_; |
| 413 CopyOrMoveOperationDelegate::OperationType operation_type_; |
| 414 FileSystemURL src_url_; |
| 415 FileSystemURL dest_url_; |
| 416 scoped_ptr<webkit_blob::FileStreamReader> reader_; |
| 417 scoped_ptr<FileStreamWriter> writer_; |
| 418 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; |
| 419 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_; |
| 420 |
| 421 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_; |
| 422 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl); |
| 423 }; |
| 424 |
292 } // namespace | 425 } // namespace |
293 | 426 |
| 427 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper( |
| 428 scoped_ptr<webkit_blob::FileStreamReader> reader, |
| 429 scoped_ptr<FileStreamWriter> writer, |
| 430 int buffer_size, |
| 431 const FileSystemOperation::CopyFileProgressCallback& |
| 432 file_progress_callback, |
| 433 const base::TimeDelta& min_progress_callback_invocation_span) |
| 434 : reader_(reader.Pass()), |
| 435 writer_(writer.Pass()), |
| 436 file_progress_callback_(file_progress_callback), |
| 437 io_buffer_(new net::IOBufferWithSize(buffer_size)), |
| 438 num_copied_bytes_(0), |
| 439 min_progress_callback_invocation_span_( |
| 440 min_progress_callback_invocation_span), |
| 441 weak_factory_(this) { |
| 442 } |
| 443 |
| 444 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() { |
| 445 } |
| 446 |
| 447 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run( |
| 448 const StatusCallback& callback) { |
| 449 file_progress_callback_.Run(0); |
| 450 last_progress_callback_invocation_time_ = base::Time::Now(); |
| 451 Read(callback); |
| 452 } |
| 453 |
| 454 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read( |
| 455 const StatusCallback& callback) { |
| 456 int result = reader_->Read( |
| 457 io_buffer_.get(), io_buffer_->size(), |
| 458 base::Bind(&StreamCopyHelper::DidRead, |
| 459 weak_factory_.GetWeakPtr(), callback)); |
| 460 if (result != net::ERR_IO_PENDING) |
| 461 DidRead(callback, result); |
| 462 } |
| 463 |
| 464 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead( |
| 465 const StatusCallback& callback, int result) { |
| 466 if (result < 0) { |
| 467 callback.Run(NetErrorToPlatformFileError(result)); |
| 468 return; |
| 469 } |
| 470 |
| 471 if (result == 0) { |
| 472 // Here is the EOF. |
| 473 callback.Run(base::PLATFORM_FILE_OK); |
| 474 return; |
| 475 } |
| 476 |
| 477 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result)); |
| 478 } |
| 479 |
| 480 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write( |
| 481 const StatusCallback& callback, |
| 482 scoped_refptr<net::DrainableIOBuffer> buffer) { |
| 483 DCHECK_GT(buffer->BytesRemaining(), 0); |
| 484 |
| 485 int result = writer_->Write( |
| 486 buffer.get(), buffer->BytesRemaining(), |
| 487 base::Bind(&StreamCopyHelper::DidWrite, |
| 488 weak_factory_.GetWeakPtr(), callback, buffer)); |
| 489 if (result != net::ERR_IO_PENDING) |
| 490 DidWrite(callback, buffer, result); |
| 491 } |
| 492 |
| 493 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite( |
| 494 const StatusCallback& callback, |
| 495 scoped_refptr<net::DrainableIOBuffer> buffer, |
| 496 int result) { |
| 497 if (result < 0) { |
| 498 callback.Run(NetErrorToPlatformFileError(result)); |
| 499 return; |
| 500 } |
| 501 |
| 502 buffer->DidConsume(result); |
| 503 num_copied_bytes_ += result; |
| 504 |
| 505 // Check the elapsed time since last |file_progress_callback_| invocation. |
| 506 base::Time now = base::Time::Now(); |
| 507 if (now - last_progress_callback_invocation_time_ >= |
| 508 min_progress_callback_invocation_span_) { |
| 509 file_progress_callback_.Run(num_copied_bytes_); |
| 510 last_progress_callback_invocation_time_ = now; |
| 511 } |
| 512 |
| 513 if (buffer->BytesRemaining() > 0) { |
| 514 Write(callback, buffer); |
| 515 return; |
| 516 } |
| 517 |
| 518 Read(callback); |
| 519 } |
294 | 520 |
295 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate( | 521 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate( |
296 FileSystemContext* file_system_context, | 522 FileSystemContext* file_system_context, |
297 const FileSystemURL& src_root, | 523 const FileSystemURL& src_root, |
298 const FileSystemURL& dest_root, | 524 const FileSystemURL& dest_root, |
299 OperationType operation_type, | 525 OperationType operation_type, |
300 CopyOrMoveOption option, | 526 CopyOrMoveOption option, |
301 const CopyProgressCallback& progress_callback, | 527 const CopyProgressCallback& progress_callback, |
302 const StatusCallback& callback) | 528 const StatusCallback& callback) |
303 : RecursiveOperationDelegate(file_system_context), | 529 : RecursiveOperationDelegate(file_system_context), |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 | 578 |
353 FileSystemURL dest_url = CreateDestURL(src_url); | 579 FileSystemURL dest_url = CreateDestURL(src_url); |
354 CopyOrMoveImpl* impl = NULL; | 580 CopyOrMoveImpl* impl = NULL; |
355 if (same_file_system_) { | 581 if (same_file_system_) { |
356 impl = new CopyOrMoveOnSameFileSystemImpl( | 582 impl = new CopyOrMoveOnSameFileSystemImpl( |
357 operation_runner(), operation_type_, src_url, dest_url, option_, | 583 operation_runner(), operation_type_, src_url, dest_url, option_, |
358 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, | 584 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, |
359 weak_factory_.GetWeakPtr(), src_url)); | 585 weak_factory_.GetWeakPtr(), src_url)); |
360 } else { | 586 } else { |
361 // Cross filesystem case. | 587 // Cross filesystem case. |
362 // TODO(hidehiko): Support stream based copy. crbug.com/279287. | |
363 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; | 588 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; |
364 CopyOrMoveFileValidatorFactory* validator_factory = | 589 CopyOrMoveFileValidatorFactory* validator_factory = |
365 file_system_context()->GetCopyOrMoveFileValidatorFactory( | 590 file_system_context()->GetCopyOrMoveFileValidatorFactory( |
366 dest_root_.type(), &error); | 591 dest_root_.type(), &error); |
367 if (error != base::PLATFORM_FILE_OK) { | 592 if (error != base::PLATFORM_FILE_OK) { |
368 callback.Run(error); | 593 callback.Run(error); |
369 return; | 594 return; |
370 } | 595 } |
371 | 596 |
372 impl = new SnapshotCopyOrMoveImpl( | 597 if (!validator_factory) { |
373 operation_runner(), operation_type_, src_url, dest_url, option_, | 598 scoped_ptr<webkit_blob::FileStreamReader> reader = |
374 validator_factory, | 599 file_system_context()->CreateFileStreamReader( |
375 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, | 600 src_url, 0, base::Time()); |
376 weak_factory_.GetWeakPtr(), src_url)); | 601 scoped_ptr<FileStreamWriter> writer = |
| 602 file_system_context()->CreateFileStreamWriter(dest_url, 0); |
| 603 if (reader && writer) { |
| 604 impl = new StreamCopyOrMoveImpl( |
| 605 operation_runner(), operation_type_, src_url, dest_url, |
| 606 reader.Pass(), writer.Pass(), |
| 607 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, |
| 608 weak_factory_.GetWeakPtr(), src_url)); |
| 609 } |
| 610 } |
| 611 |
| 612 if (!impl) { |
| 613 impl = new SnapshotCopyOrMoveImpl( |
| 614 operation_runner(), operation_type_, src_url, dest_url, option_, |
| 615 validator_factory, |
| 616 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, |
| 617 weak_factory_.GetWeakPtr(), src_url)); |
| 618 } |
377 } | 619 } |
378 | 620 |
379 // Register the running task. | 621 // Register the running task. |
380 running_copy_set_.insert(impl); | 622 running_copy_set_.insert(impl); |
381 impl->Run(base::Bind( | 623 impl->Run(base::Bind( |
382 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile, | 624 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile, |
383 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl)); | 625 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl)); |
384 } | 626 } |
385 | 627 |
386 void CopyOrMoveOperationDelegate::ProcessDirectory( | 628 void CopyOrMoveOperationDelegate::ProcessDirectory( |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
509 base::FilePath relative = dest_root_.virtual_path(); | 751 base::FilePath relative = dest_root_.virtual_path(); |
510 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(), | 752 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(), |
511 &relative); | 753 &relative); |
512 return file_system_context()->CreateCrackedFileSystemURL( | 754 return file_system_context()->CreateCrackedFileSystemURL( |
513 dest_root_.origin(), | 755 dest_root_.origin(), |
514 dest_root_.mount_type(), | 756 dest_root_.mount_type(), |
515 relative); | 757 relative); |
516 } | 758 } |
517 | 759 |
518 } // namespace fileapi | 760 } // namespace fileapi |
OLD | NEW |