copy_operation.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 23using content::BrowserThread; 24 25namespace drive { 26namespace file_system { 27 28struct 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. 38enum JsonGdocLocationType { 39 NOT_IN_METADATA, 40 IS_ORPHAN, 41 HAS_PARENT, 42}; 43 44struct 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 69namespace { 70 71FileError 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. 163FileError 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. 198FileError 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. 219FileError 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. 243FileError 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 277CopyOperation::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 294CopyOperation::~CopyOperation() { 295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 296} 297 298void 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 325void 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 364void 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_, 377 FROM_HERE, 378 base::Bind(&internal::ResourceMetadata::GetResourceEntryById, 379 base::Unretained(metadata_), 380 params.parent_entry.local_id(), parent), 381 base::Bind(&CopyOperation::CopyAfterGetParentResourceId, 382 weak_ptr_factory_.GetWeakPtr(), params, base::Owned(parent))); 383} 384 385void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params, 386 const ResourceEntry* parent, 387 FileError error) { 388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 389 DCHECK(!params.callback.is_null()); 390 391 if (error != FILE_ERROR_OK) { 392 params.callback.Run(error); 393 return; 394 } 395 396 base::FilePath new_title = params.dest_file_path.BaseName(); 397 if (params.src_entry.file_specific_info().is_hosted_document()) { 398 // Drop the document extension, which should not be in the title. 399 // TODO(yoshiki): Remove this code with crbug.com/223304. 400 new_title = new_title.RemoveExtension(); 401 } 402 403 base::Time last_modified = 404 params.preserve_last_modified ? 405 base::Time::FromInternalValue( 406 params.src_entry.file_info().last_modified()) : base::Time(); 407 408 CopyResourceOnServer( 409 params.src_entry.resource_id(), parent->resource_id(), 410 new_title.AsUTF8Unsafe(), last_modified, params.callback); 411} 412 413void CopyOperation::TransferFileFromLocalToRemote( 414 const base::FilePath& local_src_path, 415 const base::FilePath& remote_dest_path, 416 const FileOperationCallback& callback) { 417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 418 DCHECK(!callback.is_null()); 419 420 std::string* gdoc_resource_id = new std::string; 421 ResourceEntry* parent_entry = new ResourceEntry; 422 base::PostTaskAndReplyWithResult( 423 blocking_task_runner_.get(), 424 FROM_HERE, 425 base::Bind( 426 &PrepareTransferFileFromLocalToRemote, 427 metadata_, local_src_path, remote_dest_path, 428 gdoc_resource_id, parent_entry), 429 base::Bind( 430 &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare, 431 weak_ptr_factory_.GetWeakPtr(), 432 local_src_path, remote_dest_path, callback, 433 base::Owned(gdoc_resource_id), base::Owned(parent_entry))); 434} 435 436void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare( 437 const base::FilePath& local_src_path, 438 const base::FilePath& remote_dest_path, 439 const FileOperationCallback& callback, 440 std::string* gdoc_resource_id, 441 ResourceEntry* parent_entry, 442 FileError error) { 443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 444 DCHECK(!callback.is_null()); 445 446 if (error != FILE_ERROR_OK) { 447 callback.Run(error); 448 return; 449 } 450 451 // For regular files, schedule the transfer. 452 if (gdoc_resource_id->empty()) { 453 ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback); 454 return; 455 } 456 457 // GDoc file may contain a resource ID in the old format. 458 const std::string canonicalized_resource_id = 459 util::CanonicalizeResourceId(*gdoc_resource_id); 460 461 // Drop the document extension, which should not be in the title. 462 // TODO(yoshiki): Remove this code with crbug.com/223304. 463 const std::string new_title = 464 remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe(); 465 466 // This is uploading a JSON file representing a hosted document. 467 TransferJsonGdocParams* params = new TransferJsonGdocParams( 468 callback, canonicalized_resource_id, *parent_entry, new_title); 469 base::PostTaskAndReplyWithResult( 470 blocking_task_runner_.get(), 471 FROM_HERE, 472 base::Bind(&LocalWorkForTransferJsonGdocFile, metadata_, params), 473 base::Bind(&CopyOperation::TransferJsonGdocFileAfterLocalWork, 474 weak_ptr_factory_.GetWeakPtr(), base::Owned(params))); 475} 476 477void CopyOperation::TransferJsonGdocFileAfterLocalWork( 478 TransferJsonGdocParams* params, 479 FileError error) { 480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 481 482 if (error != FILE_ERROR_OK) { 483 params->callback.Run(error); 484 return; 485 } 486 487 switch (params->location_type) { 488 // When |resource_id| is found in the local metadata and it has a specific 489 // parent folder, we assume the user's intention is to copy the document and 490 // thus perform the server-side copy operation. 491 case HAS_PARENT: 492 CopyResourceOnServer(params->resource_id, 493 params->parent_resource_id, 494 params->new_title, 495 base::Time(), 496 params->callback); 497 break; 498 // When |resource_id| has no parent, we just set the new destination folder 499 // as the parent, for sharing the document between the original source. 500 // This reparenting is already done in LocalWorkForTransferJsonGdocFile(). 501 case IS_ORPHAN: { 502 DCHECK(!params->changed_path.empty()); 503 delegate_->OnEntryUpdatedByOperation(params->local_id); 504 505 FileChange changed_file; 506 changed_file.Update( 507 params->changed_path, 508 FileChange::FILE_TYPE_FILE, // This must be a hosted document. 509 FileChange::ADD_OR_UPDATE); 510 delegate_->OnFileChangedByOperation(changed_file); 511 params->callback.Run(error); 512 break; 513 } 514 // When the |resource_id| is not in the local metadata, assume it to be a 515 // document just now shared on the server but not synced locally. 516 // Same as the IS_ORPHAN case, we want to deal the case by setting parent, 517 // but this time we need to resort to server side operation. 518 case NOT_IN_METADATA: 519 scheduler_->UpdateResource( 520 params->resource_id, 521 params->parent_resource_id, 522 params->new_title, 523 base::Time(), 524 base::Time(), 525 ClientContext(USER_INITIATED), 526 base::Bind(&CopyOperation::UpdateAfterServerSideOperation, 527 weak_ptr_factory_.GetWeakPtr(), 528 params->callback)); 529 break; 530 } 531} 532 533void CopyOperation::CopyResourceOnServer( 534 const std::string& resource_id, 535 const std::string& parent_resource_id, 536 const std::string& new_title, 537 const base::Time& last_modified, 538 const FileOperationCallback& callback) { 539 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 540 DCHECK(!callback.is_null()); 541 542 scheduler_->CopyResource( 543 resource_id, parent_resource_id, new_title, last_modified, 544 base::Bind(&CopyOperation::UpdateAfterServerSideOperation, 545 weak_ptr_factory_.GetWeakPtr(), 546 callback)); 547} 548 549void CopyOperation::UpdateAfterServerSideOperation( 550 const FileOperationCallback& callback, 551 google_apis::GDataErrorCode status, 552 scoped_ptr<google_apis::FileResource> entry) { 553 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 554 DCHECK(!callback.is_null()); 555 556 FileError error = GDataToFileError(status); 557 if (error != FILE_ERROR_OK) { 558 callback.Run(error); 559 return; 560 } 561 562 ResourceEntry* resource_entry = new ResourceEntry; 563 564 // The copy on the server side is completed successfully. Update the local 565 // metadata. 566 base::FilePath* file_path = new base::FilePath; 567 base::PostTaskAndReplyWithResult( 568 blocking_task_runner_.get(), 569 FROM_HERE, 570 base::Bind(&UpdateLocalStateForServerSideOperation, 571 metadata_, 572 base::Passed(&entry), 573 resource_entry, 574 file_path), 575 base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate, 576 weak_ptr_factory_.GetWeakPtr(), 577 callback, 578 base::Owned(file_path), 579 base::Owned(resource_entry))); 580} 581 582void CopyOperation::UpdateAfterLocalStateUpdate( 583 const FileOperationCallback& callback, 584 base::FilePath* file_path, 585 const ResourceEntry* entry, 586 FileError error) { 587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 588 DCHECK(!callback.is_null()); 589 590 if (error == FILE_ERROR_OK) { 591 FileChange changed_file; 592 changed_file.Update(*file_path, *entry, FileChange::ADD_OR_UPDATE); 593 delegate_->OnFileChangedByOperation(changed_file); 594 } 595 callback.Run(error); 596} 597 598void CopyOperation::ScheduleTransferRegularFile( 599 const base::FilePath& local_src_path, 600 const base::FilePath& remote_dest_path, 601 const FileOperationCallback& callback) { 602 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 603 DCHECK(!callback.is_null()); 604 605 create_file_operation_->CreateFile( 606 remote_dest_path, 607 false, // Not exclusive (OK even if a file already exists). 608 std::string(), // no specific mime type; CreateFile should guess it. 609 base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate, 610 weak_ptr_factory_.GetWeakPtr(), 611 local_src_path, remote_dest_path, callback)); 612} 613 614void CopyOperation::ScheduleTransferRegularFileAfterCreate( 615 const base::FilePath& local_src_path, 616 const base::FilePath& remote_dest_path, 617 const FileOperationCallback& callback, 618 FileError error) { 619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 620 DCHECK(!callback.is_null()); 621 622 if (error != FILE_ERROR_OK) { 623 callback.Run(error); 624 return; 625 } 626 627 std::string* local_id = new std::string; 628 ResourceEntry* entry = new ResourceEntry; 629 base::PostTaskAndReplyWithResult( 630 blocking_task_runner_.get(), 631 FROM_HERE, 632 base::Bind(&UpdateLocalStateForScheduleTransfer, 633 metadata_, 634 cache_, 635 local_src_path, 636 remote_dest_path, 637 entry, 638 local_id), 639 base::Bind( 640 &CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState, 641 weak_ptr_factory_.GetWeakPtr(), 642 callback, 643 remote_dest_path, 644 base::Owned(entry), 645 base::Owned(local_id))); 646} 647 648void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState( 649 const FileOperationCallback& callback, 650 const base::FilePath& remote_dest_path, 651 const ResourceEntry* entry, 652 std::string* local_id, 653 FileError error) { 654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 655 DCHECK(!callback.is_null()); 656 657 if (error == FILE_ERROR_OK) { 658 FileChange changed_file; 659 changed_file.Update(remote_dest_path, *entry, FileChange::ADD_OR_UPDATE); 660 delegate_->OnFileChangedByOperation(changed_file); 661 delegate_->OnEntryUpdatedByOperation(*local_id); 662 } 663 callback.Run(error); 664} 665 666} // namespace file_system 667} // namespace drive 668