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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/drive/file_system/copy_operation.h" 6 7 #include <string> 8 9 #include "base/task_runner_util.h" 10 #include "chrome/browser/chromeos/drive/drive.pb.h" 11 #include "chrome/browser/chromeos/drive/file_cache.h" 12 #include "chrome/browser/chromeos/drive/file_change.h" 13 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h" 14 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h" 15 #include "chrome/browser/chromeos/drive/file_system_util.h" 16 #include "chrome/browser/chromeos/drive/job_scheduler.h" 17 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h" 18 #include "chrome/browser/chromeos/drive/resource_metadata.h" 19 #include "chrome/browser/drive/drive_api_util.h" 20 #include "content/public/browser/browser_thread.h" 21 #include "google_apis/drive/drive_api_parser.h" 22 23 using content::BrowserThread; 24 25 namespace drive { 26 namespace file_system { 27 28 struct CopyOperation::CopyParams { 29 base::FilePath src_file_path; 30 base::FilePath dest_file_path; 31 bool preserve_last_modified; 32 FileOperationCallback callback; 33 ResourceEntry src_entry; 34 ResourceEntry parent_entry; 35 }; 36 37 // Enum for categorizing where a gdoc represented by a JSON file exists. 38 enum JsonGdocLocationType { 39 NOT_IN_METADATA, 40 IS_ORPHAN, 41 HAS_PARENT, 42 }; 43 44 struct CopyOperation::TransferJsonGdocParams { 45 TransferJsonGdocParams(const FileOperationCallback& callback, 46 const std::string& resource_id, 47 const ResourceEntry& parent_entry, 48 const std::string& new_title) 49 : callback(callback), 50 resource_id(resource_id), 51 parent_resource_id(parent_entry.resource_id()), 52 parent_local_id(parent_entry.local_id()), 53 new_title(new_title), 54 location_type(NOT_IN_METADATA) { 55 } 56 // Parameters supplied or calculated from operation arguments. 57 const FileOperationCallback callback; 58 const std::string resource_id; 59 const std::string parent_resource_id; 60 const std::string parent_local_id; 61 const std::string new_title; 62 63 // Values computed during operation. 64 JsonGdocLocationType location_type; // types where the gdoc file is located. 65 std::string local_id; // the local_id of the file (if exists in metadata.) 66 base::FilePath changed_path; 67 }; 68 69 namespace { 70 71 FileError TryToCopyLocally(internal::ResourceMetadata* metadata, 72 internal::FileCache* cache, 73 CopyOperation::CopyParams* params, 74 std::vector<std::string>* updated_local_ids, 75 bool* directory_changed, 76 bool* should_copy_on_server) { 77 FileError error = metadata->GetResourceEntryByPath(params->src_file_path, 78 ¶ms->src_entry); 79 if (error != FILE_ERROR_OK) 80 return error; 81 82 error = metadata->GetResourceEntryByPath(params->dest_file_path.DirName(), 83 ¶ms->parent_entry); 84 if (error != FILE_ERROR_OK) 85 return error; 86 87 if (!params->parent_entry.file_info().is_directory()) 88 return FILE_ERROR_NOT_A_DIRECTORY; 89 90 // Drive File System doesn't support recursive copy. 91 if (params->src_entry.file_info().is_directory()) 92 return FILE_ERROR_NOT_A_FILE; 93 94 // Check destination. 95 ResourceEntry dest_entry; 96 error = metadata->GetResourceEntryByPath(params->dest_file_path, &dest_entry); 97 switch (error) { 98 case FILE_ERROR_OK: 99 // File API spec says it is an error to try to "copy a file to a path 100 // occupied by a directory". 101 if (dest_entry.file_info().is_directory()) 102 return FILE_ERROR_INVALID_OPERATION; 103 104 // Move the existing entry to the trash. 105 dest_entry.set_parent_local_id(util::kDriveTrashDirLocalId); 106 error = metadata->RefreshEntry(dest_entry); 107 if (error != FILE_ERROR_OK) 108 return error; 109 updated_local_ids->push_back(dest_entry.local_id()); 110 *directory_changed = true; 111 break; 112 case FILE_ERROR_NOT_FOUND: 113 break; 114 default: 115 return error; 116 } 117 118 // If the cache file is not present and the entry exists on the server, 119 // server side copy should be used. 120 if (!params->src_entry.file_specific_info().cache_state().is_present() && 121 !params->src_entry.resource_id().empty()) { 122 *should_copy_on_server = true; 123 return FILE_ERROR_OK; 124 } 125 126 // Copy locally. 127 ResourceEntry entry; 128 const int64 now = base::Time::Now().ToInternalValue(); 129 entry.set_title(params->dest_file_path.BaseName().AsUTF8Unsafe()); 130 entry.set_parent_local_id(params->parent_entry.local_id()); 131 entry.mutable_file_specific_info()->set_content_mime_type( 132 params->src_entry.file_specific_info().content_mime_type()); 133 entry.set_metadata_edit_state(ResourceEntry::DIRTY); 134 entry.set_modification_date(base::Time::Now().ToInternalValue()); 135 entry.mutable_file_info()->set_last_modified( 136 params->preserve_last_modified ? 137 params->src_entry.file_info().last_modified() : now); 138 entry.mutable_file_info()->set_last_accessed(now); 139 140 std::string local_id; 141 error = metadata->AddEntry(entry, &local_id); 142 if (error != FILE_ERROR_OK) 143 return error; 144 updated_local_ids->push_back(local_id); 145 *directory_changed = true; 146 147 if (!params->src_entry.file_specific_info().cache_state().is_present()) { 148 DCHECK(params->src_entry.resource_id().empty()); 149 // Locally created empty file may have no cache file. 150 return FILE_ERROR_OK; 151 } 152 153 base::FilePath cache_file_path; 154 error = cache->GetFile(params->src_entry.local_id(), &cache_file_path); 155 if (error != FILE_ERROR_OK) 156 return error; 157 158 return cache->Store(local_id, std::string(), cache_file_path, 159 internal::FileCache::FILE_OPERATION_COPY); 160 } 161 162 // Stores the entry returned from the server and returns its path. 163 FileError UpdateLocalStateForServerSideOperation( 164 internal::ResourceMetadata* metadata, 165 scoped_ptr<google_apis::FileResource> file_resource, 166 ResourceEntry* entry, 167 base::FilePath* file_path) { 168 DCHECK(file_resource); 169 170 std::string parent_resource_id; 171 if (!ConvertFileResourceToResourceEntry( 172 *file_resource, entry, &parent_resource_id) || 173 parent_resource_id.empty()) 174 return FILE_ERROR_NOT_A_FILE; 175 176 std::string parent_local_id; 177 FileError error = metadata->GetIdByResourceId(parent_resource_id, 178 &parent_local_id); 179 if (error != FILE_ERROR_OK) 180 return error; 181 entry->set_parent_local_id(parent_local_id); 182 183 std::string local_id; 184 error = metadata->AddEntry(*entry, &local_id); 185 // Depending on timing, the metadata may have inserted via change list 186 // already. So, FILE_ERROR_EXISTS is not an error. 187 if (error == FILE_ERROR_EXISTS) 188 error = metadata->GetIdByResourceId(entry->resource_id(), &local_id); 189 190 if (error != FILE_ERROR_OK) 191 return error; 192 193 return metadata->GetFilePath(local_id, file_path); 194 } 195 196 // Stores the file at |local_file_path| to the cache as a content of entry at 197 // |remote_dest_path|, and marks it dirty. 198 FileError UpdateLocalStateForScheduleTransfer( 199 internal::ResourceMetadata* metadata, 200 internal::FileCache* cache, 201 const base::FilePath& local_src_path, 202 const base::FilePath& remote_dest_path, 203 ResourceEntry* entry, 204 std::string* local_id) { 205 FileError error = metadata->GetIdByPath(remote_dest_path, local_id); 206 if (error != FILE_ERROR_OK) 207 return error; 208 209 error = metadata->GetResourceEntryById(*local_id, entry); 210 if (error != FILE_ERROR_OK) 211 return error; 212 213 return cache->Store(*local_id, std::string(), local_src_path, 214 internal::FileCache::FILE_OPERATION_COPY); 215 } 216 217 // Gets the file size of the |local_path|, and the ResourceEntry for the parent 218 // of |remote_path| to prepare the necessary information for transfer. 219 FileError PrepareTransferFileFromLocalToRemote( 220 internal::ResourceMetadata* metadata, 221 const base::FilePath& local_src_path, 222 const base::FilePath& remote_dest_path, 223 std::string* gdoc_resource_id, 224 ResourceEntry* parent_entry) { 225 FileError error = metadata->GetResourceEntryByPath( 226 remote_dest_path.DirName(), parent_entry); 227 if (error != FILE_ERROR_OK) 228 return error; 229 230 // The destination's parent must be a directory. 231 if (!parent_entry->file_info().is_directory()) 232 return FILE_ERROR_NOT_A_DIRECTORY; 233 234 // Try to parse GDoc File and extract the resource id, if necessary. 235 // Failing isn't problem. It'd be handled as a regular file, then. 236 if (util::HasHostedDocumentExtension(local_src_path)) 237 *gdoc_resource_id = util::ReadResourceIdFromGDocFile(local_src_path); 238 return FILE_ERROR_OK; 239 } 240 241 // Performs local work before server-side work for transferring JSON-represented 242 // gdoc files. 243 FileError LocalWorkForTransferJsonGdocFile( 244 internal::ResourceMetadata* metadata, 245 CopyOperation::TransferJsonGdocParams* params) { 246 std::string local_id; 247 FileError error = metadata->GetIdByResourceId(params->resource_id, &local_id); 248 if (error != FILE_ERROR_OK) { 249 params->location_type = NOT_IN_METADATA; 250 return error == FILE_ERROR_NOT_FOUND ? FILE_ERROR_OK : error; 251 } 252 253 ResourceEntry entry; 254 error = metadata->GetResourceEntryById(local_id, &entry); 255 if (error != FILE_ERROR_OK) 256 return error; 257 params->local_id = entry.local_id(); 258 259 if (entry.parent_local_id() == util::kDriveOtherDirLocalId) { 260 params->location_type = IS_ORPHAN; 261 entry.set_title(params->new_title); 262 entry.set_parent_local_id(params->parent_local_id); 263 entry.set_metadata_edit_state(ResourceEntry::DIRTY); 264 entry.set_modification_date(base::Time::Now().ToInternalValue()); 265 error = metadata->RefreshEntry(entry); 266 if (error != FILE_ERROR_OK) 267 return error; 268 return metadata->GetFilePath(local_id, ¶ms->changed_path); 269 } 270 271 params->location_type = HAS_PARENT; 272 return FILE_ERROR_OK; 273 } 274 275 } // namespace 276 277 CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner, 278 OperationDelegate* delegate, 279 JobScheduler* scheduler, 280 internal::ResourceMetadata* metadata, 281 internal::FileCache* cache) 282 : blocking_task_runner_(blocking_task_runner), 283 delegate_(delegate), 284 scheduler_(scheduler), 285 metadata_(metadata), 286 cache_(cache), 287 create_file_operation_(new CreateFileOperation(blocking_task_runner, 288 delegate, 289 metadata)), 290 weak_ptr_factory_(this) { 291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 292 } 293 294 CopyOperation::~CopyOperation() { 295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 296 } 297 298 void CopyOperation::Copy(const base::FilePath& src_file_path, 299 const base::FilePath& dest_file_path, 300 bool preserve_last_modified, 301 const FileOperationCallback& callback) { 302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 303 DCHECK(!callback.is_null()); 304 305 CopyParams* params = new CopyParams; 306 params->src_file_path = src_file_path; 307 params->dest_file_path = dest_file_path; 308 params->preserve_last_modified = preserve_last_modified; 309 params->callback = callback; 310 311 std::vector<std::string>* updated_local_ids = new std::vector<std::string>; 312 bool* directory_changed = new bool(false); 313 bool* should_copy_on_server = new bool(false); 314 base::PostTaskAndReplyWithResult( 315 blocking_task_runner_.get(), 316 FROM_HERE, 317 base::Bind(&TryToCopyLocally, metadata_, cache_, params, 318 updated_local_ids, directory_changed, should_copy_on_server), 319 base::Bind(&CopyOperation::CopyAfterTryToCopyLocally, 320 weak_ptr_factory_.GetWeakPtr(), base::Owned(params), 321 base::Owned(updated_local_ids), base::Owned(directory_changed), 322 base::Owned(should_copy_on_server))); 323 } 324 325 void CopyOperation::CopyAfterTryToCopyLocally( 326 const CopyParams* params, 327 const std::vector<std::string>* updated_local_ids, 328 const bool* directory_changed, 329 const bool* should_copy_on_server, 330 FileError error) { 331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 332 DCHECK(!params->callback.is_null()); 333 334 for (size_t i = 0; i < updated_local_ids->size(); ++i) 335 delegate_->OnEntryUpdatedByOperation((*updated_local_ids)[i]); 336 337 if (*directory_changed) { 338 FileChange changed_file; 339 DCHECK(!params->src_entry.file_info().is_directory()); 340 changed_file.Update(params->dest_file_path, 341 FileChange::FILE_TYPE_FILE, 342 FileChange::ADD_OR_UPDATE); 343 delegate_->OnFileChangedByOperation(changed_file); 344 } 345 346 if (error != FILE_ERROR_OK || !*should_copy_on_server) { 347 params->callback.Run(error); 348 return; 349 } 350 351 if (params->parent_entry.resource_id().empty()) { 352 // Parent entry may be being synced. 353 const bool waiting = delegate_->WaitForSyncComplete( 354 params->parent_entry.local_id(), 355 base::Bind(&CopyOperation::CopyAfterParentSync, 356 weak_ptr_factory_.GetWeakPtr(), *params)); 357 if (!waiting) 358 params->callback.Run(FILE_ERROR_NOT_FOUND); 359 } else { 360 CopyAfterGetParentResourceId(*params, ¶ms->parent_entry, FILE_ERROR_OK); 361 } 362 } 363 364 void CopyOperation::CopyAfterParentSync(const CopyParams& params, 365 FileError error) { 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 367 DCHECK(!params.callback.is_null()); 368 369 if (error != FILE_ERROR_OK) { 370 params.callback.Run(error); 371 return; 372 } 373 374 ResourceEntry* parent = new ResourceEntry; 375 base::PostTaskAndReplyWithResult( 376 blocking_task_runner_.get(), 377 FROM_HERE, 378 base::Bind(&internal::ResourceMetadata::GetResourceEntryById, 379 base::Unretained(metadata_), 380 params.parent_entry.local_id(), 381 parent), 382 base::Bind(&CopyOperation::CopyAfterGetParentResourceId, 383 weak_ptr_factory_.GetWeakPtr(), 384 params, 385 base::Owned(parent))); 386 } 387 388 void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params, 389 const ResourceEntry* parent, 390 FileError error) { 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 392 DCHECK(!params.callback.is_null()); 393 394 if (error != FILE_ERROR_OK) { 395 params.callback.Run(error); 396 return; 397 } 398 399 base::FilePath new_title = params.dest_file_path.BaseName(); 400 if (params.src_entry.file_specific_info().is_hosted_document()) { 401 // Drop the document extension, which should not be in the title. 402 // TODO(yoshiki): Remove this code with crbug.com/223304. 403 new_title = new_title.RemoveExtension(); 404 } 405 406 base::Time last_modified = 407 params.preserve_last_modified ? 408 base::Time::FromInternalValue( 409 params.src_entry.file_info().last_modified()) : base::Time(); 410 411 CopyResourceOnServer( 412 params.src_entry.resource_id(), parent->resource_id(), 413 new_title.AsUTF8Unsafe(), last_modified, params.callback); 414 } 415 416 void CopyOperation::TransferFileFromLocalToRemote( 417 const base::FilePath& local_src_path, 418 const base::FilePath& remote_dest_path, 419 const FileOperationCallback& callback) { 420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 421 DCHECK(!callback.is_null()); 422 423 std::string* gdoc_resource_id = new std::string; 424 ResourceEntry* parent_entry = new ResourceEntry; 425 base::PostTaskAndReplyWithResult( 426 blocking_task_runner_.get(), 427 FROM_HERE, 428 base::Bind( 429 &PrepareTransferFileFromLocalToRemote, 430 metadata_, local_src_path, remote_dest_path, 431 gdoc_resource_id, parent_entry), 432 base::Bind( 433 &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare, 434 weak_ptr_factory_.GetWeakPtr(), 435 local_src_path, remote_dest_path, callback, 436 base::Owned(gdoc_resource_id), base::Owned(parent_entry))); 437 } 438 439 void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare( 440 const base::FilePath& local_src_path, 441 const base::FilePath& remote_dest_path, 442 const FileOperationCallback& callback, 443 std::string* gdoc_resource_id, 444 ResourceEntry* parent_entry, 445 FileError error) { 446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 447 DCHECK(!callback.is_null()); 448 449 if (error != FILE_ERROR_OK) { 450 callback.Run(error); 451 return; 452 } 453 454 // For regular files, schedule the transfer. 455 if (gdoc_resource_id->empty()) { 456 ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback); 457 return; 458 } 459 460 // GDoc file may contain a resource ID in the old format. 461 const std::string canonicalized_resource_id = 462 util::CanonicalizeResourceId(*gdoc_resource_id); 463 464 // Drop the document extension, which should not be in the title. 465 // TODO(yoshiki): Remove this code with crbug.com/223304. 466 const std::string new_title = 467 remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe(); 468 469 // This is uploading a JSON file representing a hosted document. 470 TransferJsonGdocParams* params = new TransferJsonGdocParams( 471 callback, canonicalized_resource_id, *parent_entry, new_title); 472 base::PostTaskAndReplyWithResult( 473 blocking_task_runner_.get(), 474 FROM_HERE, 475 base::Bind(&LocalWorkForTransferJsonGdocFile, metadata_, params), 476 base::Bind(&CopyOperation::TransferJsonGdocFileAfterLocalWork, 477 weak_ptr_factory_.GetWeakPtr(), base::Owned(params))); 478 } 479 480 void CopyOperation::TransferJsonGdocFileAfterLocalWork( 481 TransferJsonGdocParams* params, 482 FileError error) { 483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 484 485 if (error != FILE_ERROR_OK) { 486 params->callback.Run(error); 487 return; 488 } 489 490 switch (params->location_type) { 491 // When |resource_id| is found in the local metadata and it has a specific 492 // parent folder, we assume the user's intention is to copy the document and 493 // thus perform the server-side copy operation. 494 case HAS_PARENT: 495 CopyResourceOnServer(params->resource_id, 496 params->parent_resource_id, 497 params->new_title, 498 base::Time(), 499 params->callback); 500 break; 501 // When |resource_id| has no parent, we just set the new destination folder 502 // as the parent, for sharing the document between the original source. 503 // This reparenting is already done in LocalWorkForTransferJsonGdocFile(). 504 case IS_ORPHAN: { 505 DCHECK(!params->changed_path.empty()); 506 delegate_->OnEntryUpdatedByOperation(params->local_id); 507 508 FileChange changed_file; 509 changed_file.Update( 510 params->changed_path, 511 FileChange::FILE_TYPE_FILE, // This must be a hosted document. 512 FileChange::ADD_OR_UPDATE); 513 delegate_->OnFileChangedByOperation(changed_file); 514 params->callback.Run(error); 515 break; 516 } 517 // When the |resource_id| is not in the local metadata, assume it to be a 518 // document just now shared on the server but not synced locally. 519 // Same as the IS_ORPHAN case, we want to deal the case by setting parent, 520 // but this time we need to resort to server side operation. 521 case NOT_IN_METADATA: 522 scheduler_->UpdateResource( 523 params->resource_id, 524 params->parent_resource_id, 525 params->new_title, 526 base::Time(), 527 base::Time(), 528 ClientContext(USER_INITIATED), 529 base::Bind(&CopyOperation::UpdateAfterServerSideOperation, 530 weak_ptr_factory_.GetWeakPtr(), 531 params->callback)); 532 break; 533 } 534 } 535 536 void CopyOperation::CopyResourceOnServer( 537 const std::string& resource_id, 538 const std::string& parent_resource_id, 539 const std::string& new_title, 540 const base::Time& last_modified, 541 const FileOperationCallback& callback) { 542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 543 DCHECK(!callback.is_null()); 544 545 scheduler_->CopyResource( 546 resource_id, parent_resource_id, new_title, last_modified, 547 base::Bind(&CopyOperation::UpdateAfterServerSideOperation, 548 weak_ptr_factory_.GetWeakPtr(), 549 callback)); 550 } 551 552 void CopyOperation::UpdateAfterServerSideOperation( 553 const FileOperationCallback& callback, 554 google_apis::GDataErrorCode status, 555 scoped_ptr<google_apis::FileResource> entry) { 556 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 557 DCHECK(!callback.is_null()); 558 559 FileError error = GDataToFileError(status); 560 if (error != FILE_ERROR_OK) { 561 callback.Run(error); 562 return; 563 } 564 565 ResourceEntry* resource_entry = new ResourceEntry; 566 567 // The copy on the server side is completed successfully. Update the local 568 // metadata. 569 base::FilePath* file_path = new base::FilePath; 570 base::PostTaskAndReplyWithResult( 571 blocking_task_runner_.get(), 572 FROM_HERE, 573 base::Bind(&UpdateLocalStateForServerSideOperation, 574 metadata_, 575 base::Passed(&entry), 576 resource_entry, 577 file_path), 578 base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate, 579 weak_ptr_factory_.GetWeakPtr(), 580 callback, 581 base::Owned(file_path), 582 base::Owned(resource_entry))); 583 } 584 585 void CopyOperation::UpdateAfterLocalStateUpdate( 586 const FileOperationCallback& callback, 587 base::FilePath* file_path, 588 const ResourceEntry* entry, 589 FileError error) { 590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 591 DCHECK(!callback.is_null()); 592 593 if (error == FILE_ERROR_OK) { 594 FileChange changed_file; 595 changed_file.Update(*file_path, *entry, FileChange::ADD_OR_UPDATE); 596 delegate_->OnFileChangedByOperation(changed_file); 597 } 598 callback.Run(error); 599 } 600 601 void CopyOperation::ScheduleTransferRegularFile( 602 const base::FilePath& local_src_path, 603 const base::FilePath& remote_dest_path, 604 const FileOperationCallback& callback) { 605 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 606 DCHECK(!callback.is_null()); 607 608 create_file_operation_->CreateFile( 609 remote_dest_path, 610 false, // Not exclusive (OK even if a file already exists). 611 std::string(), // no specific mime type; CreateFile should guess it. 612 base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate, 613 weak_ptr_factory_.GetWeakPtr(), 614 local_src_path, remote_dest_path, callback)); 615 } 616 617 void CopyOperation::ScheduleTransferRegularFileAfterCreate( 618 const base::FilePath& local_src_path, 619 const base::FilePath& remote_dest_path, 620 const FileOperationCallback& callback, 621 FileError error) { 622 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 623 DCHECK(!callback.is_null()); 624 625 if (error != FILE_ERROR_OK) { 626 callback.Run(error); 627 return; 628 } 629 630 std::string* local_id = new std::string; 631 ResourceEntry* entry = new ResourceEntry; 632 base::PostTaskAndReplyWithResult( 633 blocking_task_runner_.get(), 634 FROM_HERE, 635 base::Bind(&UpdateLocalStateForScheduleTransfer, 636 metadata_, 637 cache_, 638 local_src_path, 639 remote_dest_path, 640 entry, 641 local_id), 642 base::Bind( 643 &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState, 644 weak_ptr_factory_.GetWeakPtr(), 645 callback, 646 remote_dest_path, 647 base::Owned(entry), 648 base::Owned(local_id))); 649 } 650 651 void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState( 652 const FileOperationCallback& callback, 653 const base::FilePath& remote_dest_path, 654 const ResourceEntry* entry, 655 std::string* local_id, 656 FileError error) { 657 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 658 DCHECK(!callback.is_null()); 659 660 if (error == FILE_ERROR_OK) { 661 FileChange changed_file; 662 changed_file.Update(remote_dest_path, *entry, FileChange::ADD_OR_UPDATE); 663 delegate_->OnFileChangedByOperation(changed_file); 664 delegate_->OnEntryUpdatedByOperation(*local_id); 665 } 666 callback.Run(error); 667 } 668 669 } // namespace file_system 670 } // namespace drive 671