Home | History | Annotate | Download | only in drive_backend_v1
      1 // Copyright 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 "chrome/browser/sync_file_system/drive_backend_v1/local_sync_delegate.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "chrome/browser/sync_file_system/conflict_resolution_resolver.h"
     10 #include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h"
     11 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h"
     12 #include "chrome/browser/sync_file_system/logger.h"
     13 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     14 
     15 namespace sync_file_system {
     16 namespace drive_backend {
     17 
     18 LocalSyncDelegate::LocalSyncDelegate(
     19     DriveFileSyncService* sync_service,
     20     const FileChange& local_change,
     21     const base::FilePath& local_path,
     22     const SyncFileMetadata& local_metadata,
     23     const fileapi::FileSystemURL& url)
     24     : sync_service_(sync_service),
     25       operation_(SYNC_OPERATION_NONE),
     26       url_(url),
     27       local_change_(local_change),
     28       local_path_(local_path),
     29       local_metadata_(local_metadata),
     30       has_drive_metadata_(false),
     31       has_remote_change_(false),
     32       weak_factory_(this) {}
     33 
     34 LocalSyncDelegate::~LocalSyncDelegate() {}
     35 
     36 void LocalSyncDelegate::Run(const SyncStatusCallback& callback) {
     37   // TODO(nhiroki): support directory operations (http://crbug.com/161442).
     38   DCHECK(IsSyncFSDirectoryOperationEnabled() || !local_change_.IsDirectory());
     39   operation_ = SYNC_OPERATION_NONE;
     40 
     41   has_drive_metadata_ =
     42       metadata_store()->ReadEntry(url_, &drive_metadata_) == SYNC_STATUS_OK;
     43 
     44   if (!has_drive_metadata_)
     45     drive_metadata_.set_md5_checksum(std::string());
     46 
     47   sync_service_->EnsureOriginRootDirectory(
     48       url_.origin(),
     49       base::Bind(&LocalSyncDelegate::DidGetOriginRoot,
     50                  weak_factory_.GetWeakPtr(),
     51                  callback));
     52 }
     53 
     54 void LocalSyncDelegate::DidGetOriginRoot(
     55     const SyncStatusCallback& callback,
     56     SyncStatusCode status,
     57     const std::string& origin_resource_id) {
     58   if (status != SYNC_STATUS_OK) {
     59     callback.Run(status);
     60     return;
     61   }
     62 
     63   origin_resource_id_ = origin_resource_id;
     64 
     65   has_remote_change_ =
     66       remote_change_handler()->GetChangeForURL(url_, &remote_change_);
     67   if (has_remote_change_ && drive_metadata_.resource_id().empty())
     68     drive_metadata_.set_resource_id(remote_change_.resource_id);
     69 
     70   SyncFileType remote_file_type =
     71       has_remote_change_ ? remote_change_.change.file_type() :
     72       has_drive_metadata_ ?
     73           DriveFileSyncService::DriveMetadataResourceTypeToSyncFileType(
     74               drive_metadata_.type())
     75       : SYNC_FILE_TYPE_UNKNOWN;
     76 
     77   DCHECK_EQ(SYNC_OPERATION_NONE, operation_);
     78   operation_ = LocalSyncOperationResolver::Resolve(
     79       local_change_,
     80       has_remote_change_ ? &remote_change_.change : NULL,
     81       has_drive_metadata_ ? &drive_metadata_ : NULL);
     82 
     83   util::Log(logging::LOG_VERBOSE, FROM_HERE,
     84             "ApplyLocalChange for %s local_change:%s ===> %s",
     85             url_.DebugString().c_str(),
     86             local_change_.DebugString().c_str(),
     87             SyncOperationTypeToString(operation_));
     88 
     89   switch (operation_) {
     90     case SYNC_OPERATION_ADD_FILE:
     91       UploadNewFile(callback);
     92       return;
     93     case SYNC_OPERATION_ADD_DIRECTORY:
     94       CreateDirectory(callback);
     95       return;
     96     case SYNC_OPERATION_UPDATE_FILE:
     97       UploadExistingFile(callback);
     98       return;
     99     case SYNC_OPERATION_DELETE:
    100       Delete(callback);
    101       return;
    102     case SYNC_OPERATION_NONE:
    103       callback.Run(SYNC_STATUS_OK);
    104       return;
    105     case SYNC_OPERATION_CONFLICT:
    106       HandleConflict(callback);
    107       return;
    108     case SYNC_OPERATION_RESOLVE_TO_LOCAL:
    109       ResolveToLocal(callback);
    110       return;
    111     case SYNC_OPERATION_RESOLVE_TO_REMOTE:
    112       ResolveToRemote(callback, remote_file_type);
    113       return;
    114     case SYNC_OPERATION_DELETE_METADATA:
    115       DeleteMetadata(base::Bind(
    116           &LocalSyncDelegate::DidApplyLocalChange,
    117           weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS));
    118       return;
    119     case SYNC_OPERATION_FAIL: {
    120       callback.Run(SYNC_STATUS_FAILED);
    121       return;
    122     }
    123   }
    124   NOTREACHED();
    125   callback.Run(SYNC_STATUS_FAILED);
    126 }
    127 
    128 void LocalSyncDelegate::UploadNewFile(const SyncStatusCallback& callback) {
    129   api_util()->UploadNewFile(
    130       origin_resource_id_,
    131       local_path_,
    132       DriveFileSyncService::PathToTitle(url_.path()),
    133       base::Bind(&LocalSyncDelegate::DidUploadNewFile,
    134                  weak_factory_.GetWeakPtr(), callback));
    135 }
    136 
    137 void LocalSyncDelegate::DidUploadNewFile(
    138     const SyncStatusCallback& callback,
    139     google_apis::GDataErrorCode error,
    140     const std::string& resource_id,
    141     const std::string& md5) {
    142   switch (error) {
    143     case google_apis::HTTP_CREATED:
    144       UpdateMetadata(
    145           resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE,
    146           base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
    147                      weak_factory_.GetWeakPtr(), callback, error));
    148       sync_service_->NotifyObserversFileStatusChanged(
    149           url_,
    150           SYNC_FILE_STATUS_SYNCED,
    151           SYNC_ACTION_ADDED,
    152           SYNC_DIRECTION_LOCAL_TO_REMOTE);
    153       return;
    154     case google_apis::HTTP_CONFLICT:
    155       HandleCreationConflict(resource_id, DriveMetadata::RESOURCE_TYPE_FILE,
    156                              callback);
    157       return;
    158     default:
    159       callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
    160   }
    161 }
    162 
    163 void LocalSyncDelegate::CreateDirectory(const SyncStatusCallback& callback) {
    164   DCHECK(IsSyncFSDirectoryOperationEnabled());
    165   api_util()->CreateDirectory(
    166       origin_resource_id_,
    167       DriveFileSyncService::PathToTitle(url_.path()),
    168       base::Bind(&LocalSyncDelegate::DidCreateDirectory,
    169                  weak_factory_.GetWeakPtr(), callback));
    170 }
    171 
    172 void LocalSyncDelegate::DidCreateDirectory(
    173     const SyncStatusCallback& callback,
    174     google_apis::GDataErrorCode error,
    175     const std::string& resource_id) {
    176   switch (error) {
    177     case google_apis::HTTP_SUCCESS:
    178     case google_apis::HTTP_CREATED: {
    179       UpdateMetadata(
    180           resource_id, std::string(), DriveMetadata::RESOURCE_TYPE_FOLDER,
    181           base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
    182                      weak_factory_.GetWeakPtr(), callback, error));
    183       sync_service_->NotifyObserversFileStatusChanged(
    184           url_,
    185           SYNC_FILE_STATUS_SYNCED,
    186           SYNC_ACTION_ADDED,
    187           SYNC_DIRECTION_LOCAL_TO_REMOTE);
    188       return;
    189     }
    190 
    191     case google_apis::HTTP_CONFLICT:
    192       // There were conflicts and a file was left.
    193       // TODO(kinuko): Handle the latter case (http://crbug.com/237090).
    194       // Fall-through
    195 
    196     default:
    197       callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
    198   }
    199 }
    200 
    201 void LocalSyncDelegate::UploadExistingFile(const SyncStatusCallback& callback) {
    202   DCHECK(has_drive_metadata_);
    203   if (drive_metadata_.resource_id().empty()) {
    204     UploadNewFile(callback);
    205     return;
    206   }
    207 
    208   api_util()->UploadExistingFile(
    209       drive_metadata_.resource_id(),
    210       drive_metadata_.md5_checksum(),
    211       local_path_,
    212       base::Bind(&LocalSyncDelegate::DidUploadExistingFile,
    213                  weak_factory_.GetWeakPtr(), callback));
    214 }
    215 
    216 void LocalSyncDelegate::DidUploadExistingFile(
    217     const SyncStatusCallback& callback,
    218     google_apis::GDataErrorCode error,
    219     const std::string& resource_id,
    220     const std::string& md5) {
    221   DCHECK(has_drive_metadata_);
    222   switch (error) {
    223     case google_apis::HTTP_SUCCESS:
    224       UpdateMetadata(
    225           resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE,
    226           base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
    227                      weak_factory_.GetWeakPtr(), callback, error));
    228       sync_service_->NotifyObserversFileStatusChanged(
    229           url_,
    230           SYNC_FILE_STATUS_SYNCED,
    231           SYNC_ACTION_UPDATED,
    232           SYNC_DIRECTION_LOCAL_TO_REMOTE);
    233       return;
    234     case google_apis::HTTP_CONFLICT:
    235       HandleConflict(callback);
    236       return;
    237     case google_apis::HTTP_NOT_MODIFIED:
    238       DidApplyLocalChange(callback,
    239                           google_apis::HTTP_SUCCESS, SYNC_STATUS_OK);
    240       return;
    241     case google_apis::HTTP_NOT_FOUND:
    242       UploadNewFile(callback);
    243       return;
    244     default: {
    245       const SyncStatusCode status =
    246           GDataErrorCodeToSyncStatusCodeWrapper(error);
    247       DCHECK_NE(SYNC_STATUS_OK, status);
    248       callback.Run(status);
    249       return;
    250     }
    251   }
    252 }
    253 
    254 void LocalSyncDelegate::Delete(const SyncStatusCallback& callback) {
    255   if (!has_drive_metadata_) {
    256     callback.Run(SYNC_STATUS_OK);
    257     return;
    258   }
    259 
    260   if (drive_metadata_.resource_id().empty()) {
    261     DidDelete(callback, google_apis::HTTP_NOT_FOUND);
    262     return;
    263   }
    264 
    265   api_util()->DeleteFile(
    266       drive_metadata_.resource_id(),
    267       drive_metadata_.md5_checksum(),
    268       base::Bind(&LocalSyncDelegate::DidDelete,
    269                  weak_factory_.GetWeakPtr(), callback));
    270 }
    271 
    272 void LocalSyncDelegate::DidDelete(
    273     const SyncStatusCallback& callback,
    274     google_apis::GDataErrorCode error) {
    275   DCHECK(has_drive_metadata_);
    276 
    277   switch (error) {
    278     case google_apis::HTTP_SUCCESS:
    279     case google_apis::HTTP_NOT_FOUND:
    280       DeleteMetadata(base::Bind(
    281           &LocalSyncDelegate::DidApplyLocalChange,
    282           weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS));
    283       sync_service_->NotifyObserversFileStatusChanged(
    284           url_,
    285           SYNC_FILE_STATUS_SYNCED,
    286           SYNC_ACTION_DELETED,
    287           SYNC_DIRECTION_LOCAL_TO_REMOTE);
    288       return;
    289     case google_apis::HTTP_PRECONDITION:
    290     case google_apis::HTTP_CONFLICT:
    291       // Delete |drive_metadata| on the conflict case.
    292       // Conflicted remote change should be applied as a future remote change.
    293       DeleteMetadata(base::Bind(
    294           &LocalSyncDelegate::DidDeleteMetadataForDeletionConflict,
    295           weak_factory_.GetWeakPtr(), callback));
    296       sync_service_->NotifyObserversFileStatusChanged(
    297           url_,
    298           SYNC_FILE_STATUS_SYNCED,
    299           SYNC_ACTION_DELETED,
    300           SYNC_DIRECTION_LOCAL_TO_REMOTE);
    301       return;
    302     default: {
    303       const SyncStatusCode status =
    304           GDataErrorCodeToSyncStatusCodeWrapper(error);
    305       DCHECK_NE(SYNC_STATUS_OK, status);
    306       callback.Run(status);
    307       return;
    308     }
    309   }
    310 }
    311 
    312 void LocalSyncDelegate::DidDeleteMetadataForDeletionConflict(
    313     const SyncStatusCallback& callback,
    314     SyncStatusCode status) {
    315   callback.Run(SYNC_STATUS_OK);
    316 }
    317 
    318 void LocalSyncDelegate::ResolveToLocal(const SyncStatusCallback& callback) {
    319   if (drive_metadata_.resource_id().empty()) {
    320     DidDeleteFileToResolveToLocal(callback, google_apis::HTTP_NOT_FOUND);
    321     return;
    322   }
    323 
    324   api_util()->DeleteFile(
    325       drive_metadata_.resource_id(),
    326       drive_metadata_.md5_checksum(),
    327       base::Bind(
    328           &LocalSyncDelegate::DidDeleteFileToResolveToLocal,
    329           weak_factory_.GetWeakPtr(), callback));
    330 }
    331 
    332 void LocalSyncDelegate::DidDeleteFileToResolveToLocal(
    333     const SyncStatusCallback& callback,
    334     google_apis::GDataErrorCode error) {
    335   if (error != google_apis::HTTP_SUCCESS &&
    336       error != google_apis::HTTP_NOT_FOUND) {
    337     callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
    338     return;
    339   }
    340 
    341   DCHECK_NE(SYNC_FILE_TYPE_UNKNOWN, local_metadata_.file_type);
    342   if (local_metadata_.file_type == SYNC_FILE_TYPE_FILE) {
    343     UploadNewFile(callback);
    344     return;
    345   }
    346 
    347   DCHECK(IsSyncFSDirectoryOperationEnabled());
    348   DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_.file_type);
    349   CreateDirectory(callback);
    350 }
    351 
    352 void LocalSyncDelegate::ResolveToRemote(
    353     const SyncStatusCallback& callback,
    354     SyncFileType remote_file_type) {
    355   // Mark the file as to-be-fetched.
    356   DCHECK(!drive_metadata_.resource_id().empty());
    357 
    358   SetMetadataToBeFetched(
    359       DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
    360           remote_file_type),
    361       base::Bind(&LocalSyncDelegate::DidResolveToRemote,
    362                  weak_factory_.GetWeakPtr(), callback));
    363   // The synced notification will be dispatched when the remote file is
    364   // downloaded.
    365 }
    366 
    367 void LocalSyncDelegate::DidResolveToRemote(
    368     const SyncStatusCallback& callback,
    369     SyncStatusCode status) {
    370   DCHECK(has_drive_metadata_);
    371   if (status != SYNC_STATUS_OK) {
    372     callback.Run(status);
    373     return;
    374   }
    375 
    376   SyncFileType file_type = SYNC_FILE_TYPE_FILE;
    377   if (drive_metadata_.type() == DriveMetadata::RESOURCE_TYPE_FOLDER)
    378     file_type = SYNC_FILE_TYPE_DIRECTORY;
    379   sync_service_->AppendFetchChange(
    380       url_.origin(), url_.path(), drive_metadata_.resource_id(), file_type);
    381   callback.Run(status);
    382 }
    383 
    384 void LocalSyncDelegate::DidApplyLocalChange(
    385     const SyncStatusCallback& callback,
    386     const google_apis::GDataErrorCode error,
    387     SyncStatusCode status) {
    388   if ((operation_ == SYNC_OPERATION_DELETE ||
    389        operation_ == SYNC_OPERATION_DELETE_METADATA) &&
    390       (status == SYNC_FILE_ERROR_NOT_FOUND ||
    391        status == SYNC_DATABASE_ERROR_NOT_FOUND)) {
    392     status = SYNC_STATUS_OK;
    393   }
    394 
    395   if (status == SYNC_STATUS_OK) {
    396     remote_change_handler()->RemoveChangeForURL(url_);
    397     status = GDataErrorCodeToSyncStatusCodeWrapper(error);
    398   }
    399   callback.Run(status);
    400 }
    401 
    402 void LocalSyncDelegate::UpdateMetadata(
    403     const std::string& resource_id,
    404     const std::string& md5,
    405     DriveMetadata::ResourceType type,
    406     const SyncStatusCallback& callback) {
    407   has_drive_metadata_ = true;
    408   drive_metadata_.set_resource_id(resource_id);
    409   drive_metadata_.set_md5_checksum(md5);
    410   drive_metadata_.set_conflicted(false);
    411   drive_metadata_.set_to_be_fetched(false);
    412   drive_metadata_.set_type(type);
    413   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
    414 }
    415 
    416 void LocalSyncDelegate::ResetMetadataForStartOver(
    417     const SyncStatusCallback& callback) {
    418   has_drive_metadata_ = true;
    419   DCHECK(!drive_metadata_.resource_id().empty());
    420   drive_metadata_.set_md5_checksum(std::string());
    421   drive_metadata_.set_conflicted(false);
    422   drive_metadata_.set_to_be_fetched(false);
    423   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
    424 }
    425 
    426 void LocalSyncDelegate::SetMetadataToBeFetched(
    427     DriveMetadata::ResourceType type,
    428     const SyncStatusCallback& callback) {
    429   has_drive_metadata_ = true;
    430   drive_metadata_.set_md5_checksum(std::string());
    431   drive_metadata_.set_conflicted(false);
    432   drive_metadata_.set_to_be_fetched(true);
    433   drive_metadata_.set_type(type);
    434   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
    435 }
    436 
    437 void LocalSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
    438   metadata_store()->DeleteEntry(url_, callback);
    439 }
    440 
    441 void LocalSyncDelegate::HandleCreationConflict(
    442     const std::string& resource_id,
    443     DriveMetadata::ResourceType type,
    444     const SyncStatusCallback& callback) {
    445   // File-file conflict is found.
    446   // Populates a fake drive_metadata and set has_drive_metadata = true.
    447   // In HandleConflictLocalSync:
    448   // - If conflict_resolution is manual, we'll change conflicted to true
    449   //   and save the metadata.
    450   // - Otherwise we'll save the metadata with empty md5 and will start
    451   //   over local sync as UploadExistingFile.
    452   drive_metadata_.set_resource_id(resource_id);
    453   drive_metadata_.set_md5_checksum(std::string());
    454   drive_metadata_.set_conflicted(false);
    455   drive_metadata_.set_to_be_fetched(false);
    456   drive_metadata_.set_type(type);
    457   has_drive_metadata_ = true;
    458   HandleConflict(callback);
    459 }
    460 
    461 void LocalSyncDelegate::HandleConflict(const SyncStatusCallback& callback) {
    462   DCHECK(!drive_metadata_.resource_id().empty());
    463   api_util()->GetResourceEntry(
    464       drive_metadata_.resource_id(),
    465       base::Bind(
    466           &LocalSyncDelegate::DidGetEntryForConflictResolution,
    467           weak_factory_.GetWeakPtr(), callback));
    468 }
    469 
    470 void LocalSyncDelegate::DidGetEntryForConflictResolution(
    471     const SyncStatusCallback& callback,
    472     google_apis::GDataErrorCode error,
    473     scoped_ptr<google_apis::ResourceEntry> entry) {
    474   SyncFileType remote_file_type = SYNC_FILE_TYPE_UNKNOWN;
    475   ConflictResolution resolution = CONFLICT_RESOLUTION_UNKNOWN;
    476 
    477   if (error != google_apis::HTTP_SUCCESS ||
    478       entry->updated_time().is_null()) {
    479     resolution = CONFLICT_RESOLUTION_LOCAL_WIN;
    480   } else {
    481     SyncFileType local_file_type = local_metadata_.file_type;
    482     base::Time local_modification_time = local_metadata_.last_modified;
    483     base::Time remote_modification_time = entry->updated_time();
    484     if (entry->is_file())
    485       remote_file_type = SYNC_FILE_TYPE_FILE;
    486     else if (entry->is_folder())
    487       remote_file_type = SYNC_FILE_TYPE_DIRECTORY;
    488     else
    489       remote_file_type = SYNC_FILE_TYPE_UNKNOWN;
    490 
    491     resolution = conflict_resolution_resolver()->Resolve(
    492         local_file_type, local_modification_time,
    493         remote_file_type, remote_modification_time);
    494   }
    495 
    496   switch (resolution) {
    497     case CONFLICT_RESOLUTION_MARK_CONFLICT:
    498       HandleManualResolutionCase(callback);
    499       return;
    500     case CONFLICT_RESOLUTION_LOCAL_WIN:
    501       HandleLocalWinCase(callback);
    502       return;
    503     case CONFLICT_RESOLUTION_REMOTE_WIN:
    504       HandleRemoteWinCase(callback, remote_file_type);
    505       return;
    506     case CONFLICT_RESOLUTION_UNKNOWN:
    507       NOTREACHED();
    508   }
    509   NOTREACHED();
    510   callback.Run(SYNC_STATUS_FAILED);
    511 }
    512 
    513 void LocalSyncDelegate::HandleManualResolutionCase(
    514     const SyncStatusCallback& callback) {
    515   if (drive_metadata_.conflicted()) {
    516     callback.Run(SYNC_STATUS_HAS_CONFLICT);
    517     return;
    518   }
    519 
    520   has_drive_metadata_ = true;
    521   sync_service_->MarkConflict(
    522       url_, &drive_metadata_,
    523       base::Bind(&LocalSyncDelegate::DidMarkConflict,
    524                  weak_factory_.GetWeakPtr(), callback));
    525 }
    526 
    527 void LocalSyncDelegate::DidMarkConflict(
    528     const SyncStatusCallback& callback,
    529     SyncStatusCode status) {
    530   DidApplyLocalChange(callback, google_apis::HTTP_CONFLICT, status);
    531 }
    532 
    533 void LocalSyncDelegate::HandleLocalWinCase(
    534     const SyncStatusCallback& callback) {
    535   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    536             "Resolving conflict for local sync: %s: LOCAL WIN",
    537             url_.DebugString().c_str());
    538 
    539   DCHECK(!drive_metadata_.resource_id().empty());
    540   if (!has_drive_metadata_) {
    541     StartOver(callback, SYNC_STATUS_OK);
    542     return;
    543   }
    544 
    545   ResetMetadataForStartOver(base::Bind(&LocalSyncDelegate::StartOver,
    546                                        weak_factory_.GetWeakPtr(), callback));
    547 }
    548 
    549 void LocalSyncDelegate::HandleRemoteWinCase(
    550     const SyncStatusCallback& callback,
    551     SyncFileType remote_file_type) {
    552   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    553             "Resolving conflict for local sync: %s: REMOTE WIN",
    554             url_.DebugString().c_str());
    555   ResolveToRemote(callback, remote_file_type);
    556 }
    557 
    558 void LocalSyncDelegate::StartOver(const SyncStatusCallback& callback,
    559                                   SyncStatusCode status) {
    560   if (status != SYNC_STATUS_OK) {
    561     callback.Run(status);
    562     return;
    563   }
    564 
    565   remote_change_handler()->RemoveChangeForURL(url_);
    566 
    567   // Return the control back to the sync service once.
    568   callback.Run(SYNC_STATUS_RETRY);
    569 }
    570 
    571 SyncStatusCode
    572 LocalSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
    573     google_apis::GDataErrorCode error) {
    574   return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
    575 }
    576 
    577 DriveMetadataStore* LocalSyncDelegate::metadata_store() {
    578   return sync_service_->metadata_store_.get();
    579 }
    580 
    581 APIUtilInterface* LocalSyncDelegate::api_util() {
    582   return sync_service_->api_util_.get();
    583 }
    584 
    585 RemoteChangeHandler* LocalSyncDelegate::remote_change_handler() {
    586   return &sync_service_->remote_change_handler_;
    587 }
    588 
    589 ConflictResolutionResolver* LocalSyncDelegate::conflict_resolution_resolver() {
    590   return &sync_service_->conflict_resolution_resolver_;
    591 }
    592 
    593 }  // namespace drive_backend
    594 }  // namespace sync_file_system
    595