Home | History | Annotate | Download | only in drive_backend
      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/drive_file_sync_service.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 #include <utility>
     10 
     11 #include "base/bind.h"
     12 #include "base/file_util.h"
     13 #include "base/location.h"
     14 #include "base/memory/weak_ptr.h"
     15 #include "base/message_loop/message_loop_proxy.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/values.h"
     18 #include "chrome/browser/drive/drive_api_util.h"
     19 #include "chrome/browser/drive/drive_notification_manager.h"
     20 #include "chrome/browser/drive/drive_notification_manager_factory.h"
     21 #include "chrome/browser/extensions/extension_service.h"
     22 #include "chrome/browser/extensions/extension_system.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/browser/sync_file_system/drive_backend/api_util.h"
     25 #include "chrome/browser/sync_file_system/drive_backend/drive_file_sync_util.h"
     26 #include "chrome/browser/sync_file_system/drive_backend/drive_metadata_store.h"
     27 #include "chrome/browser/sync_file_system/drive_backend/local_sync_delegate.h"
     28 #include "chrome/browser/sync_file_system/drive_backend/remote_sync_delegate.h"
     29 #include "chrome/browser/sync_file_system/file_status_observer.h"
     30 #include "chrome/browser/sync_file_system/logger.h"
     31 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
     32 #include "chrome/browser/sync_file_system/sync_file_system.pb.h"
     33 #include "chrome/browser/sync_file_system/sync_file_type.h"
     34 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     35 #include "chrome/common/extensions/extension.h"
     36 #include "content/public/browser/browser_thread.h"
     37 #include "extensions/common/constants.h"
     38 #include "webkit/browser/fileapi/file_system_url.h"
     39 #include "webkit/common/blob/scoped_file.h"
     40 #include "webkit/common/fileapi/file_system_util.h"
     41 
     42 using fileapi::FileSystemURL;
     43 
     44 namespace sync_file_system {
     45 
     46 typedef RemoteFileSyncService::OriginStatusMap OriginStatusMap;
     47 
     48 namespace {
     49 
     50 const base::FilePath::CharType kTempDirName[] = FILE_PATH_LITERAL("tmp");
     51 const base::FilePath::CharType kSyncFileSystemDir[] =
     52     FILE_PATH_LITERAL("Sync FileSystem");
     53 const base::FilePath::CharType kSyncFileSystemDirDev[] =
     54     FILE_PATH_LITERAL("Sync FileSystem Dev");
     55 
     56 const base::FilePath::CharType* GetSyncFileSystemDir() {
     57   return IsSyncFSDirectoryOperationEnabled()
     58       ? kSyncFileSystemDirDev : kSyncFileSystemDir;
     59 }
     60 
     61 void EmptyStatusCallback(SyncStatusCode status) {}
     62 
     63 }  // namespace
     64 
     65 ConflictResolutionPolicy DriveFileSyncService::kDefaultPolicy =
     66     CONFLICT_RESOLUTION_POLICY_LAST_WRITE_WIN;
     67 
     68 // DriveFileSyncService ------------------------------------------------------
     69 
     70 DriveFileSyncService::~DriveFileSyncService() {
     71   if (api_util_)
     72     api_util_->RemoveObserver(this);
     73 
     74   drive::DriveNotificationManager* drive_notification_manager =
     75       drive::DriveNotificationManagerFactory::GetForProfile(profile_);
     76   if (drive_notification_manager)
     77     drive_notification_manager->RemoveObserver(this);
     78 }
     79 
     80 scoped_ptr<DriveFileSyncService> DriveFileSyncService::Create(
     81     Profile* profile) {
     82   scoped_ptr<DriveFileSyncService> service(new DriveFileSyncService(profile));
     83   scoped_ptr<SyncTaskManager> task_manager(
     84       new SyncTaskManager(service->AsWeakPtr()));
     85   SyncStatusCallback callback = base::Bind(
     86       &SyncTaskManager::Initialize, task_manager->AsWeakPtr());
     87   service->Initialize(task_manager.Pass(), callback);
     88   return service.Pass();
     89 }
     90 
     91 scoped_ptr<DriveFileSyncService> DriveFileSyncService::CreateForTesting(
     92     Profile* profile,
     93     const base::FilePath& base_dir,
     94     scoped_ptr<drive_backend::APIUtilInterface> api_util,
     95     scoped_ptr<DriveMetadataStore> metadata_store) {
     96   scoped_ptr<DriveFileSyncService> service(new DriveFileSyncService(profile));
     97   scoped_ptr<SyncTaskManager> task_manager(
     98       new SyncTaskManager(service->AsWeakPtr()));
     99   SyncStatusCallback callback = base::Bind(
    100       &SyncTaskManager::Initialize, task_manager->AsWeakPtr());
    101   service->InitializeForTesting(task_manager.Pass(),
    102                                 base_dir,
    103                                 api_util.Pass(),
    104                                 metadata_store.Pass(),
    105                                 callback);
    106   return service.Pass();
    107 }
    108 
    109 scoped_ptr<drive_backend::APIUtilInterface>
    110 DriveFileSyncService::DestroyAndPassAPIUtilForTesting(
    111     scoped_ptr<DriveFileSyncService> sync_service) {
    112   return sync_service->api_util_.Pass();
    113 }
    114 
    115 void DriveFileSyncService::AddServiceObserver(Observer* observer) {
    116   service_observers_.AddObserver(observer);
    117 }
    118 
    119 void DriveFileSyncService::AddFileStatusObserver(
    120     FileStatusObserver* observer) {
    121   file_status_observers_.AddObserver(observer);
    122 }
    123 
    124 void DriveFileSyncService::RegisterOriginForTrackingChanges(
    125     const GURL& origin,
    126     const SyncStatusCallback& callback) {
    127   task_manager_->ScheduleTask(
    128       base::Bind(&DriveFileSyncService::DoRegisterOriginForTrackingChanges,
    129                  AsWeakPtr(), origin),
    130       callback);
    131 }
    132 
    133 void DriveFileSyncService::UnregisterOriginForTrackingChanges(
    134     const GURL& origin,
    135     const SyncStatusCallback& callback) {
    136   task_manager_->ScheduleTask(
    137       base::Bind(&DriveFileSyncService::DoUnregisterOriginForTrackingChanges,
    138                  AsWeakPtr(), origin),
    139       callback);
    140 }
    141 
    142 void DriveFileSyncService::EnableOriginForTrackingChanges(
    143     const GURL& origin,
    144     const SyncStatusCallback& callback) {
    145   task_manager_->ScheduleTask(
    146       base::Bind(&DriveFileSyncService::DoEnableOriginForTrackingChanges,
    147                  AsWeakPtr(), origin),
    148       callback);
    149 }
    150 
    151 void DriveFileSyncService::DisableOriginForTrackingChanges(
    152     const GURL& origin,
    153     const SyncStatusCallback& callback) {
    154   task_manager_->ScheduleTask(
    155       base::Bind(&DriveFileSyncService::DoDisableOriginForTrackingChanges,
    156                  AsWeakPtr(), origin),
    157       callback);
    158 }
    159 
    160 void DriveFileSyncService::UninstallOrigin(
    161     const GURL& origin,
    162     const SyncStatusCallback& callback) {
    163   task_manager_->ScheduleTask(
    164       base::Bind(&DriveFileSyncService::DoUninstallOrigin, AsWeakPtr(), origin),
    165       callback);
    166 }
    167 
    168 void DriveFileSyncService::ProcessRemoteChange(
    169     const SyncFileCallback& callback) {
    170   task_manager_->ScheduleTask(
    171       base::Bind(&DriveFileSyncService::DoProcessRemoteChange, AsWeakPtr(),
    172                  callback),
    173       base::Bind(&EmptyStatusCallback));
    174 }
    175 
    176 void DriveFileSyncService::SetRemoteChangeProcessor(
    177     RemoteChangeProcessor* processor) {
    178   remote_change_processor_ = processor;
    179 }
    180 
    181 LocalChangeProcessor* DriveFileSyncService::GetLocalChangeProcessor() {
    182   return this;
    183 }
    184 
    185 bool DriveFileSyncService::IsConflicting(const FileSystemURL& url) {
    186   DriveMetadata metadata;
    187   const SyncStatusCode status = metadata_store_->ReadEntry(url, &metadata);
    188   if (status != SYNC_STATUS_OK) {
    189     DCHECK_EQ(SYNC_DATABASE_ERROR_NOT_FOUND, status);
    190     return false;
    191   }
    192   return metadata.conflicted();
    193 }
    194 
    195 RemoteServiceState DriveFileSyncService::GetCurrentState() const {
    196   if (!sync_enabled_)
    197     return REMOTE_SERVICE_DISABLED;
    198   return state_;
    199 }
    200 
    201 void DriveFileSyncService::GetOriginStatusMap(OriginStatusMap* status_map) {
    202   DCHECK(status_map);
    203 
    204   // Add batch sync origins held by DriveFileSyncService.
    205   typedef std::map<GURL, std::string>::const_iterator iterator;
    206   for (iterator itr = pending_batch_sync_origins_.begin();
    207        itr != pending_batch_sync_origins_.end();
    208        ++itr)
    209     (*status_map)[itr->first] = "Pending";
    210 
    211   // Add incremental and disabled origins held by DriveMetadataStore.
    212   for (iterator itr = metadata_store_->incremental_sync_origins().begin();
    213        itr != metadata_store_->incremental_sync_origins().end();
    214        ++itr)
    215     (*status_map)[itr->first] = "Enabled";
    216 
    217   for (iterator itr = metadata_store_->disabled_origins().begin();
    218        itr != metadata_store_->disabled_origins().end();
    219        ++itr)
    220     (*status_map)[itr->first] = "Disabled";
    221 }
    222 
    223 scoped_ptr<base::ListValue> DriveFileSyncService::DumpFiles(
    224     const GURL& origin) {
    225   return metadata_store_->DumpFiles(origin);
    226 }
    227 
    228 void DriveFileSyncService::SetSyncEnabled(bool enabled) {
    229   if (sync_enabled_ == enabled)
    230     return;
    231 
    232   RemoteServiceState old_state = GetCurrentState();
    233   sync_enabled_ = enabled;
    234   if (old_state == GetCurrentState())
    235     return;
    236 
    237   const char* status_message = enabled ? "Sync is enabled" : "Sync is disabled";
    238   FOR_EACH_OBSERVER(
    239       Observer, service_observers_,
    240       OnRemoteServiceStateUpdated(GetCurrentState(), status_message));
    241 }
    242 
    243 SyncStatusCode DriveFileSyncService::SetConflictResolutionPolicy(
    244     ConflictResolutionPolicy policy) {
    245   conflict_resolution_resolver_.set_policy(policy);
    246   return SYNC_STATUS_OK;
    247 }
    248 
    249 ConflictResolutionPolicy
    250 DriveFileSyncService::GetConflictResolutionPolicy() const {
    251   return conflict_resolution_resolver_.policy();
    252 }
    253 
    254 void DriveFileSyncService::ApplyLocalChange(
    255     const FileChange& local_file_change,
    256     const base::FilePath& local_file_path,
    257     const SyncFileMetadata& local_file_metadata,
    258     const FileSystemURL& url,
    259     const SyncStatusCallback& callback) {
    260   task_manager_->ScheduleTask(
    261       base::Bind(&DriveFileSyncService::DoApplyLocalChange, AsWeakPtr(),
    262                  local_file_change,
    263                  local_file_path,
    264                  local_file_metadata,
    265                  url),
    266       callback);
    267 }
    268 
    269 void DriveFileSyncService::OnAuthenticated() {
    270   if (state_ == REMOTE_SERVICE_OK)
    271     return;
    272   util::Log(logging::LOG_INFO, FROM_HERE, "OnAuthenticated");
    273 
    274   UpdateServiceState(REMOTE_SERVICE_OK, "Authenticated");
    275 
    276   may_have_unfetched_changes_ = true;
    277   MaybeStartFetchChanges();
    278 }
    279 
    280 void DriveFileSyncService::OnNetworkConnected() {
    281   if (state_ == REMOTE_SERVICE_OK)
    282     return;
    283   util::Log(logging::LOG_INFO, FROM_HERE, "OnNetworkConnected");
    284 
    285   UpdateServiceState(REMOTE_SERVICE_OK, "Network connected");
    286 
    287   may_have_unfetched_changes_ = true;
    288   MaybeStartFetchChanges();
    289 }
    290 
    291 DriveFileSyncService::DriveFileSyncService(Profile* profile)
    292     : profile_(profile),
    293       state_(REMOTE_SERVICE_OK),
    294       sync_enabled_(true),
    295       largest_fetched_changestamp_(0),
    296       may_have_unfetched_changes_(false),
    297       remote_change_processor_(NULL),
    298       last_gdata_error_(google_apis::HTTP_SUCCESS),
    299       conflict_resolution_resolver_(kDefaultPolicy) {
    300 }
    301 
    302 void DriveFileSyncService::Initialize(
    303     scoped_ptr<SyncTaskManager> task_manager,
    304     const SyncStatusCallback& callback) {
    305   DCHECK(profile_);
    306   DCHECK(!metadata_store_);
    307   DCHECK(!task_manager_);
    308 
    309   task_manager_ = task_manager.Pass();
    310 
    311   temporary_file_dir_ =
    312       profile_->GetPath().Append(GetSyncFileSystemDir()).Append(kTempDirName);
    313 
    314   api_util_.reset(new drive_backend::APIUtil(profile_, temporary_file_dir_));
    315   api_util_->AddObserver(this);
    316 
    317   metadata_store_.reset(new DriveMetadataStore(
    318       profile_->GetPath().Append(GetSyncFileSystemDir()),
    319       content::BrowserThread::GetMessageLoopProxyForThread(
    320           content::BrowserThread::FILE).get()));
    321 
    322   metadata_store_->Initialize(
    323       base::Bind(&DriveFileSyncService::DidInitializeMetadataStore,
    324                  AsWeakPtr(), callback));
    325 }
    326 
    327 void DriveFileSyncService::InitializeForTesting(
    328     scoped_ptr<SyncTaskManager> task_manager,
    329     const base::FilePath& base_dir,
    330     scoped_ptr<drive_backend::APIUtilInterface> api_util,
    331     scoped_ptr<DriveMetadataStore> metadata_store,
    332     const SyncStatusCallback& callback) {
    333   DCHECK(!metadata_store_);
    334   DCHECK(!task_manager_);
    335 
    336   task_manager_ = task_manager.Pass();
    337   temporary_file_dir_ = base_dir.Append(kTempDirName);
    338 
    339   api_util_ = api_util.Pass();
    340   metadata_store_ = metadata_store.Pass();
    341 
    342   base::MessageLoopProxy::current()->PostTask(
    343       FROM_HERE,
    344       base::Bind(&DriveFileSyncService::DidInitializeMetadataStore,
    345                  AsWeakPtr(), callback, SYNC_STATUS_OK, false));
    346 }
    347 
    348 void DriveFileSyncService::DidInitializeMetadataStore(
    349     const SyncStatusCallback& callback,
    350     SyncStatusCode status,
    351     bool created) {
    352   if (status != SYNC_STATUS_OK) {
    353     callback.Run(status);
    354     return;
    355   }
    356 
    357   DCHECK(pending_batch_sync_origins_.empty());
    358 
    359   UpdateRegisteredOrigins();
    360 
    361   largest_fetched_changestamp_ = metadata_store_->GetLargestChangeStamp();
    362 
    363   DriveMetadataStore::URLAndDriveMetadataList to_be_fetched_files;
    364   status = metadata_store_->GetToBeFetchedFiles(&to_be_fetched_files);
    365   DCHECK_EQ(SYNC_STATUS_OK, status);
    366   typedef DriveMetadataStore::URLAndDriveMetadataList::const_iterator iterator;
    367   for (iterator itr = to_be_fetched_files.begin();
    368        itr != to_be_fetched_files.end(); ++itr) {
    369     const FileSystemURL& url = itr->first;
    370     const DriveMetadata& metadata = itr->second;
    371     const std::string& resource_id = metadata.resource_id();
    372 
    373     SyncFileType file_type = SYNC_FILE_TYPE_FILE;
    374     if (metadata.type() == DriveMetadata::RESOURCE_TYPE_FOLDER)
    375       file_type = SYNC_FILE_TYPE_DIRECTORY;
    376     if (!metadata_store_->IsIncrementalSyncOrigin(url.origin())) {
    377       metadata_store_->DeleteEntry(url, base::Bind(&EmptyStatusCallback));
    378       continue;
    379     }
    380     AppendFetchChange(url.origin(), url.path(), resource_id, file_type);
    381   }
    382 
    383   if (!sync_root_resource_id().empty())
    384     api_util_->EnsureSyncRootIsNotInMyDrive(sync_root_resource_id());
    385 
    386   callback.Run(status);
    387   may_have_unfetched_changes_ = true;
    388 
    389   drive::DriveNotificationManager* drive_notification_manager =
    390       drive::DriveNotificationManagerFactory::GetForProfile(profile_);
    391   if (drive_notification_manager)
    392     drive_notification_manager->AddObserver(this);
    393 }
    394 
    395 void DriveFileSyncService::UpdateServiceStateFromLastOperationStatus(
    396     SyncStatusCode sync_status,
    397     google_apis::GDataErrorCode gdata_error) {
    398   switch (sync_status) {
    399     case SYNC_STATUS_OK:
    400       // If the last Drive-related operation was successful we can
    401       // change the service state to OK.
    402       if (GDataErrorCodeToSyncStatusCode(gdata_error) == SYNC_STATUS_OK)
    403         UpdateServiceState(REMOTE_SERVICE_OK, std::string());
    404       break;
    405 
    406     // Authentication error.
    407     case SYNC_STATUS_AUTHENTICATION_FAILED:
    408       UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
    409                          "Authentication required");
    410       break;
    411 
    412     // OAuth token error.
    413     case SYNC_STATUS_ACCESS_FORBIDDEN:
    414       UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
    415                          "Access forbidden");
    416       break;
    417 
    418     // Errors which could make the service temporarily unavailable.
    419     case SYNC_STATUS_RETRY:
    420     case SYNC_STATUS_NETWORK_ERROR:
    421     case SYNC_STATUS_ABORT:
    422     case SYNC_STATUS_FAILED:
    423       UpdateServiceState(REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
    424                          "Network or temporary service error.");
    425       break;
    426 
    427     // Errors which would require manual user intervention to resolve.
    428     case SYNC_DATABASE_ERROR_CORRUPTION:
    429     case SYNC_DATABASE_ERROR_IO_ERROR:
    430     case SYNC_DATABASE_ERROR_FAILED:
    431       UpdateServiceState(REMOTE_SERVICE_DISABLED,
    432                          "Unrecoverable database error");
    433       break;
    434 
    435     default:
    436       // Other errors don't affect service state
    437       break;
    438   }
    439 }
    440 
    441 void DriveFileSyncService::UpdateServiceState(RemoteServiceState state,
    442                                               const std::string& description) {
    443   RemoteServiceState old_state = GetCurrentState();
    444   state_ = state;
    445 
    446   // Notify remote sync service state if the state has been changed.
    447   if (old_state != GetCurrentState()) {
    448     util::Log(logging::LOG_INFO, FROM_HERE,
    449               "Service state changed: %d->%d: %s",
    450               old_state, GetCurrentState(), description.c_str());
    451     FOR_EACH_OBSERVER(
    452         Observer, service_observers_,
    453         OnRemoteServiceStateUpdated(GetCurrentState(), description));
    454   }
    455 }
    456 
    457 void DriveFileSyncService::DoRegisterOriginForTrackingChanges(
    458     const GURL& origin,
    459     const SyncStatusCallback& callback) {
    460   DCHECK(origin.SchemeIs(extensions::kExtensionScheme));
    461 
    462   DCHECK(!metadata_store_->IsOriginDisabled(origin));
    463   if (!metadata_store_->GetResourceIdForOrigin(origin).empty()) {
    464     callback.Run(SYNC_STATUS_OK);
    465     return;
    466   }
    467 
    468   EnsureOriginRootDirectory(
    469       origin, base::Bind(&DriveFileSyncService::DidGetDriveDirectoryForOrigin,
    470                          AsWeakPtr(), origin, callback));
    471 }
    472 
    473 void DriveFileSyncService::DoUnregisterOriginForTrackingChanges(
    474     const GURL& origin,
    475     const SyncStatusCallback& callback) {
    476   remote_change_handler_.RemoveChangesForOrigin(origin);
    477   pending_batch_sync_origins_.erase(origin);
    478   metadata_store_->RemoveOrigin(origin, callback);
    479 }
    480 
    481 void DriveFileSyncService::DoEnableOriginForTrackingChanges(
    482     const GURL& origin,
    483     const SyncStatusCallback& callback) {
    484   // If origin cannot be found in disabled list, then it's not a SyncFS app
    485   // and should be ignored.
    486   if (!metadata_store_->IsOriginDisabled(origin)) {
    487     callback.Run(SYNC_STATUS_OK);
    488     return;
    489   }
    490 
    491   pending_batch_sync_origins_.insert(
    492       *metadata_store_->disabled_origins().find(origin));
    493   metadata_store_->EnableOrigin(origin, callback);
    494 }
    495 
    496 void DriveFileSyncService::DoDisableOriginForTrackingChanges(
    497     const GURL& origin,
    498     const SyncStatusCallback& callback) {
    499   pending_batch_sync_origins_.erase(origin);
    500   if (!metadata_store_->IsIncrementalSyncOrigin(origin)) {
    501     callback.Run(SYNC_STATUS_OK);
    502     return;
    503   }
    504 
    505   remote_change_handler_.RemoveChangesForOrigin(origin);
    506   metadata_store_->DisableOrigin(origin, callback);
    507 }
    508 
    509 void DriveFileSyncService::DoUninstallOrigin(
    510     const GURL& origin,
    511     const SyncStatusCallback& callback) {
    512   // Because origin management is now split between DriveFileSyncService and
    513   // DriveMetadataStore, resource_id must be checked for in two places.
    514   std::string resource_id = metadata_store_->GetResourceIdForOrigin(origin);
    515   if (resource_id.empty()) {
    516     std::map<GURL, std::string>::const_iterator iterator =
    517         pending_batch_sync_origins_.find(origin);
    518     if (iterator != pending_batch_sync_origins_.end())
    519       resource_id = iterator->second;
    520   }
    521 
    522   // An empty resource_id indicates either one of following two cases:
    523   // 1) origin is not in metadata_store_ because the extension was never
    524   //    run or it's not managed by this service, and thus no
    525   //    origin directory on the remote drive was created.
    526   // 2) origin or sync root folder is deleted on Drive.
    527   if (resource_id.empty()) {
    528     callback.Run(SYNC_STATUS_UNKNOWN_ORIGIN);
    529     return;
    530   }
    531 
    532   // Convert origin's directory GURL to ResourceID and delete it. Expected MD5
    533   // is empty to force delete (i.e. skip conflict resolution).
    534   api_util_->DeleteFile(resource_id,
    535                         std::string(),
    536                         base::Bind(&DriveFileSyncService::DidUninstallOrigin,
    537                                    AsWeakPtr(),
    538                                    origin,
    539                                    callback));
    540 }
    541 
    542 void DriveFileSyncService::DoProcessRemoteChange(
    543     const SyncFileCallback& sync_callback,
    544     const SyncStatusCallback& completion_callback) {
    545   DCHECK(remote_change_processor_);
    546 
    547   SyncStatusCallback callback = base::Bind(
    548       &DriveFileSyncService::DidProcessRemoteChange, AsWeakPtr(),
    549       sync_callback, completion_callback);
    550 
    551   if (GetCurrentState() == REMOTE_SERVICE_DISABLED) {
    552     callback.Run(SYNC_STATUS_SYNC_DISABLED);
    553     return;
    554   }
    555 
    556   if (!remote_change_handler_.HasChanges()) {
    557     callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC);
    558     return;
    559   }
    560 
    561   RemoteChangeHandler::RemoteChange remote_change;
    562   bool has_remote_change =
    563       remote_change_handler_.GetChange(&remote_change);
    564   DCHECK(has_remote_change);
    565 
    566   DCHECK(!running_remote_sync_task_);
    567   running_remote_sync_task_.reset(new drive_backend::RemoteSyncDelegate(
    568       this, remote_change));
    569   running_remote_sync_task_->Run(callback);
    570 }
    571 
    572 void DriveFileSyncService::DoApplyLocalChange(
    573     const FileChange& local_file_change,
    574     const base::FilePath& local_file_path,
    575     const SyncFileMetadata& local_file_metadata,
    576     const FileSystemURL& url,
    577     const SyncStatusCallback& callback) {
    578   if (GetCurrentState() == REMOTE_SERVICE_DISABLED) {
    579     callback.Run(SYNC_STATUS_SYNC_DISABLED);
    580     return;
    581   }
    582 
    583   if (!metadata_store_->IsIncrementalSyncOrigin(url.origin())) {
    584     // We may get called by LocalFileSyncService to sync local changes
    585     // for the origins that are disabled.
    586     DVLOG(1) << "Got request for stray origin: " << url.origin().spec();
    587     callback.Run(SYNC_STATUS_UNKNOWN_ORIGIN);
    588     return;
    589   }
    590 
    591   DCHECK(!running_local_sync_task_);
    592   running_local_sync_task_.reset(new drive_backend::LocalSyncDelegate(
    593       this, local_file_change, local_file_path, local_file_metadata, url));
    594   running_local_sync_task_->Run(base::Bind(
    595       &DriveFileSyncService::DidApplyLocalChange, AsWeakPtr(), callback));
    596 }
    597 
    598 void DriveFileSyncService::UpdateRegisteredOrigins() {
    599   ExtensionService* extension_service =
    600       extensions::ExtensionSystem::Get(profile_)->extension_service();
    601   DCHECK(pending_batch_sync_origins_.empty());
    602   if (!extension_service)
    603     return;
    604 
    605   std::vector<GURL> origins;
    606   metadata_store_->GetAllOrigins(&origins);
    607 
    608   // Update the status of every origin using status from ExtensionService.
    609   for (std::vector<GURL>::const_iterator itr = origins.begin();
    610        itr != origins.end(); ++itr) {
    611     std::string extension_id = itr->host();
    612     GURL origin =
    613         extensions::Extension::GetBaseURLFromExtensionId(extension_id);
    614 
    615     if (!extension_service->GetInstalledExtension(extension_id)) {
    616       // Extension has been uninstalled.
    617       UninstallOrigin(origin, base::Bind(&EmptyStatusCallback));
    618     } else if (metadata_store_->IsIncrementalSyncOrigin(origin) &&
    619                !extension_service->IsExtensionEnabled(extension_id)) {
    620       // Incremental Extension has been disabled.
    621       metadata_store_->DisableOrigin(origin, base::Bind(&EmptyStatusCallback));
    622     } else if (metadata_store_->IsOriginDisabled(origin) &&
    623                extension_service->IsExtensionEnabled(extension_id)) {
    624       // Extension has been re-enabled.
    625       pending_batch_sync_origins_.insert(
    626           *metadata_store_->disabled_origins().find(origin));
    627       metadata_store_->EnableOrigin(origin, base::Bind(&EmptyStatusCallback));
    628     }
    629   }
    630 }
    631 
    632 void DriveFileSyncService::StartBatchSync(
    633     const SyncStatusCallback& callback) {
    634   DCHECK(GetCurrentState() == REMOTE_SERVICE_OK || may_have_unfetched_changes_);
    635   DCHECK(!pending_batch_sync_origins_.empty());
    636 
    637   GURL origin = pending_batch_sync_origins_.begin()->first;
    638   std::string resource_id = pending_batch_sync_origins_.begin()->second;
    639   DCHECK(!resource_id.empty());
    640   pending_batch_sync_origins_.erase(pending_batch_sync_origins_.begin());
    641 
    642   DCHECK(!metadata_store_->IsOriginDisabled(origin));
    643 
    644   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    645             "Start batch sync for: %s", origin.spec().c_str());
    646 
    647   api_util_->GetLargestChangeStamp(
    648       base::Bind(&DriveFileSyncService::DidGetLargestChangeStampForBatchSync,
    649                  AsWeakPtr(),
    650                  callback,
    651                  origin,
    652                  resource_id));
    653 
    654   may_have_unfetched_changes_ = false;
    655 }
    656 
    657 void DriveFileSyncService::DidGetDriveDirectoryForOrigin(
    658     const GURL& origin,
    659     const SyncStatusCallback& callback,
    660     SyncStatusCode status,
    661     const std::string& resource_id) {
    662   if (status == SYNC_FILE_ERROR_NOT_FOUND &&
    663       !sync_root_resource_id().empty()) {
    664     // Retry after (re-)creating the sync root directory.
    665     metadata_store_->SetSyncRootDirectory(std::string());
    666     EnsureOriginRootDirectory(
    667         origin, base::Bind(
    668             &DriveFileSyncService::DidGetDriveDirectoryForOrigin,
    669             AsWeakPtr(), origin, callback));
    670     return;
    671   }
    672 
    673   if (status != SYNC_STATUS_OK) {
    674     callback.Run(status);
    675     return;
    676   }
    677 
    678   if (!metadata_store_->IsKnownOrigin(origin))
    679     pending_batch_sync_origins_.insert(std::make_pair(origin, resource_id));
    680 
    681   callback.Run(SYNC_STATUS_OK);
    682 }
    683 
    684 void DriveFileSyncService::DidUninstallOrigin(
    685     const GURL& origin,
    686     const SyncStatusCallback& callback,
    687     google_apis::GDataErrorCode error) {
    688   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
    689   if (status != SYNC_STATUS_OK && status != SYNC_FILE_ERROR_NOT_FOUND) {
    690     callback.Run(status);
    691     return;
    692   }
    693 
    694   // Origin directory has been removed so it's now safe to remove the origin
    695   // from the metadata store.
    696   DoUnregisterOriginForTrackingChanges(origin, callback);
    697 }
    698 
    699 void DriveFileSyncService::DidGetLargestChangeStampForBatchSync(
    700     const SyncStatusCallback& callback,
    701     const GURL& origin,
    702     const std::string& resource_id,
    703     google_apis::GDataErrorCode error,
    704     int64 largest_changestamp) {
    705   if (error != google_apis::HTTP_SUCCESS) {
    706     pending_batch_sync_origins_.insert(std::make_pair(origin, resource_id));
    707     callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
    708     return;
    709   }
    710 
    711   if (metadata_store_->incremental_sync_origins().empty()) {
    712     largest_fetched_changestamp_ = largest_changestamp;
    713     metadata_store_->SetLargestChangeStamp(
    714         largest_changestamp,
    715         base::Bind(&EmptyStatusCallback));
    716   }
    717 
    718   api_util_->ListFiles(
    719       resource_id,
    720       base::Bind(&DriveFileSyncService::DidGetDirectoryContentForBatchSync,
    721                  AsWeakPtr(),
    722                  callback,
    723                  origin,
    724                  resource_id,
    725                  largest_changestamp));
    726 }
    727 
    728 void DriveFileSyncService::DidGetDirectoryContentForBatchSync(
    729     const SyncStatusCallback& callback,
    730     const GURL& origin,
    731     const std::string& resource_id,
    732     int64 largest_changestamp,
    733     google_apis::GDataErrorCode error,
    734     scoped_ptr<google_apis::ResourceList> feed) {
    735   if (error != google_apis::HTTP_SUCCESS) {
    736     pending_batch_sync_origins_.insert(std::make_pair(origin, resource_id));
    737     callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
    738     return;
    739   }
    740 
    741   typedef ScopedVector<google_apis::ResourceEntry>::const_iterator iterator;
    742   for (iterator itr = feed->entries().begin();
    743        itr != feed->entries().end(); ++itr) {
    744     const google_apis::ResourceEntry& entry = **itr;
    745     if (entry.deleted())
    746       continue;
    747 
    748     SyncFileType file_type = SYNC_FILE_TYPE_UNKNOWN;
    749     if (entry.is_file())
    750       file_type = SYNC_FILE_TYPE_FILE;
    751     else if (entry.is_folder() && IsSyncFSDirectoryOperationEnabled())
    752       file_type = SYNC_FILE_TYPE_DIRECTORY;
    753     else
    754       continue;
    755 
    756     // Save to be fetched file to DB for restore in case of crash.
    757     DriveMetadata metadata;
    758     metadata.set_resource_id(entry.resource_id());
    759     metadata.set_md5_checksum(std::string());
    760     metadata.set_conflicted(false);
    761     metadata.set_to_be_fetched(true);
    762 
    763     base::FilePath path = TitleToPath(entry.title());
    764     fileapi::FileSystemURL url(CreateSyncableFileSystemURL(
    765         origin, path));
    766     // TODO(calvinlo): Write metadata and origin data as single batch command
    767     // so it's not possible for the DB to contain a DriveMetadata with an
    768     // unknown origin.
    769     metadata_store_->UpdateEntry(url, metadata,
    770                                  base::Bind(&EmptyStatusCallback));
    771 
    772     AppendFetchChange(origin, path, entry.resource_id(), file_type);
    773   }
    774 
    775   GURL next_feed_url;
    776   if (feed->GetNextFeedURL(&next_feed_url)) {
    777     api_util_->ContinueListing(
    778         next_feed_url,
    779         base::Bind(&DriveFileSyncService::DidGetDirectoryContentForBatchSync,
    780                    AsWeakPtr(),
    781                    callback,
    782                    origin,
    783                    resource_id,
    784                    largest_changestamp));
    785     return;
    786   }
    787 
    788   metadata_store_->AddIncrementalSyncOrigin(origin, resource_id);
    789   may_have_unfetched_changes_ = true;
    790   callback.Run(SYNC_STATUS_OK);
    791 }
    792 
    793 void DriveFileSyncService::DidProcessRemoteChange(
    794     const SyncFileCallback& sync_callback,
    795     const SyncStatusCallback& completion_callback,
    796     SyncStatusCode status) {
    797   fileapi::FileSystemURL url;
    798   if (running_remote_sync_task_)
    799     url = running_remote_sync_task_->url();
    800   running_remote_sync_task_.reset();
    801 
    802   completion_callback.Run(status);
    803   sync_callback.Run(status, url);
    804 }
    805 
    806 void DriveFileSyncService::DidApplyLocalChange(
    807     const SyncStatusCallback& callback,
    808     SyncStatusCode status) {
    809   running_local_sync_task_.reset();
    810   callback.Run(status);
    811 }
    812 
    813 bool DriveFileSyncService::AppendRemoteChange(
    814     const GURL& origin,
    815     const google_apis::ResourceEntry& entry,
    816     int64 changestamp) {
    817   base::FilePath path = TitleToPath(entry.title());
    818 
    819   if (!entry.is_folder() && !entry.is_file() && !entry.deleted())
    820     return false;
    821 
    822   if (entry.is_folder() && !IsSyncFSDirectoryOperationEnabled())
    823     return false;
    824 
    825   SyncFileType file_type = entry.is_file() ?
    826       SYNC_FILE_TYPE_FILE : SYNC_FILE_TYPE_DIRECTORY;
    827 
    828   return AppendRemoteChangeInternal(
    829       origin, path, entry.deleted(),
    830       entry.resource_id(), changestamp,
    831       entry.deleted() ? std::string() : entry.file_md5(),
    832       entry.updated_time(), file_type);
    833 }
    834 
    835 bool DriveFileSyncService::AppendFetchChange(
    836     const GURL& origin,
    837     const base::FilePath& path,
    838     const std::string& resource_id,
    839     SyncFileType type) {
    840   return AppendRemoteChangeInternal(
    841       origin, path,
    842       false,  // is_deleted
    843       resource_id,
    844       0,  // changestamp
    845       std::string(),  // remote_file_md5
    846       base::Time(),  // updated_time
    847       type);
    848 }
    849 
    850 bool DriveFileSyncService::AppendRemoteChangeInternal(
    851     const GURL& origin,
    852     const base::FilePath& path,
    853     bool is_deleted,
    854     const std::string& remote_resource_id,
    855     int64 changestamp,
    856     const std::string& remote_file_md5,
    857     const base::Time& updated_time,
    858     SyncFileType file_type) {
    859   fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
    860   DCHECK(url.is_valid());
    861 
    862   // Note that we create a normalized path from url.path() rather than
    863   // path here (as FileSystemURL does extra normalization).
    864   base::FilePath::StringType normalized_path =
    865     fileapi::VirtualPath::GetNormalizedFilePath(url.path());
    866 
    867   std::string local_resource_id;
    868   std::string local_file_md5;
    869 
    870   DriveMetadata metadata;
    871   bool has_db_entry =
    872       (metadata_store_->ReadEntry(url, &metadata) == SYNC_STATUS_OK);
    873   if (has_db_entry) {
    874     local_resource_id = metadata.resource_id();
    875     if (!metadata.to_be_fetched())
    876       local_file_md5 = metadata.md5_checksum();
    877   }
    878 
    879   RemoteChangeHandler::RemoteChange pending_change;
    880   if (remote_change_handler_.GetChangeForURL(url, &pending_change)) {
    881     if (pending_change.changestamp >= changestamp)
    882       return false;
    883 
    884     if (pending_change.change.IsDelete()) {
    885       local_resource_id.clear();
    886       local_file_md5.clear();
    887     } else {
    888       local_resource_id = pending_change.resource_id;
    889       local_file_md5 = pending_change.md5_checksum;
    890     }
    891   }
    892 
    893   // Drop the change if remote update change does not change file content.
    894   if (!remote_file_md5.empty() &&
    895       !local_file_md5.empty() &&
    896       remote_file_md5 == local_file_md5)
    897     return false;
    898 
    899   // Drop any change if the change has unknown resource id.
    900   if (!remote_resource_id.empty() &&
    901       !local_resource_id.empty() &&
    902       remote_resource_id != local_resource_id)
    903     return false;
    904 
    905   if (is_deleted) {
    906     // Drop any change if the change is for deletion and local resource id is
    907     // empty.
    908     if (local_resource_id.empty())
    909       return false;
    910 
    911     // Determine a file type of the deleted change by local metadata.
    912     if (!remote_resource_id.empty() &&
    913         !local_resource_id.empty() &&
    914         remote_resource_id == local_resource_id) {
    915       DCHECK(IsSyncFSDirectoryOperationEnabled() ||
    916              DriveMetadata::RESOURCE_TYPE_FILE == metadata.type());
    917       file_type = metadata.type() == DriveMetadata::RESOURCE_TYPE_FILE ?
    918           SYNC_FILE_TYPE_FILE : SYNC_FILE_TYPE_DIRECTORY;
    919     }
    920 
    921     if (has_db_entry) {
    922       metadata.set_resource_id(std::string());
    923       metadata_store_->UpdateEntry(url, metadata,
    924                                    base::Bind(&EmptyStatusCallback));
    925     }
    926   }
    927 
    928   FileChange file_change(is_deleted ? FileChange::FILE_CHANGE_DELETE
    929                                     : FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    930                          file_type);
    931 
    932   RemoteChangeHandler::RemoteChange remote_change(
    933       changestamp, remote_resource_id, remote_file_md5,
    934       updated_time, url, file_change);
    935   remote_change_handler_.AppendChange(remote_change);
    936 
    937   DVLOG(3) << "Append remote change: " << path.value()
    938            << " (" << normalized_path << ")"
    939            << "@" << changestamp << " "
    940            << file_change.DebugString();
    941 
    942   return true;
    943 }
    944 
    945 void DriveFileSyncService::RemoveRemoteChange(
    946     const FileSystemURL& url) {
    947   remote_change_handler_.RemoveChangeForURL(url);
    948 }
    949 
    950 void DriveFileSyncService::MarkConflict(
    951     const fileapi::FileSystemURL& url,
    952     DriveMetadata* drive_metadata,
    953     const SyncStatusCallback& callback) {
    954   DCHECK(drive_metadata);
    955   DCHECK(!drive_metadata->resource_id().empty());
    956   drive_metadata->set_conflicted(true);
    957   drive_metadata->set_to_be_fetched(false);
    958   metadata_store_->UpdateEntry(
    959       url, *drive_metadata, base::Bind(
    960           &DriveFileSyncService::NotifyConflict,
    961           AsWeakPtr(), url, callback));
    962 }
    963 
    964 void DriveFileSyncService::NotifyConflict(
    965     const fileapi::FileSystemURL& url,
    966     const SyncStatusCallback& callback,
    967     SyncStatusCode status) {
    968   if (status != SYNC_STATUS_OK) {
    969     callback.Run(status);
    970     return;
    971   }
    972   NotifyObserversFileStatusChanged(url,
    973                                    SYNC_FILE_STATUS_CONFLICTING,
    974                                    SYNC_ACTION_NONE,
    975                                    SYNC_DIRECTION_NONE);
    976   callback.Run(status);
    977 }
    978 
    979 SyncStatusCode DriveFileSyncService::GDataErrorCodeToSyncStatusCodeWrapper(
    980     google_apis::GDataErrorCode error) {
    981   last_gdata_error_ = error;
    982   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
    983   if (status != SYNC_STATUS_OK && !api_util_->IsAuthenticated())
    984     return SYNC_STATUS_AUTHENTICATION_FAILED;
    985   return status;
    986 }
    987 
    988 void DriveFileSyncService::MaybeStartFetchChanges() {
    989   if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
    990     return;
    991 
    992   // If we have pending_batch_sync_origins, try starting the batch sync.
    993   if (!pending_batch_sync_origins_.empty()) {
    994     if (GetCurrentState() == REMOTE_SERVICE_OK || may_have_unfetched_changes_) {
    995       task_manager_->ScheduleTaskIfIdle(
    996           base::Bind(&DriveFileSyncService::StartBatchSync, AsWeakPtr()));
    997     }
    998     return;
    999   }
   1000 
   1001   if (may_have_unfetched_changes_ &&
   1002       !metadata_store_->incremental_sync_origins().empty()) {
   1003     task_manager_->ScheduleTaskIfIdle(
   1004         base::Bind(&DriveFileSyncService::FetchChangesForIncrementalSync,
   1005                    AsWeakPtr()));
   1006   }
   1007 }
   1008 
   1009 void DriveFileSyncService::OnNotificationReceived() {
   1010   VLOG(2) << "Notification received to check for Google Drive updates";
   1011 
   1012   // Likely indicating the network is enabled again.
   1013   UpdateServiceState(REMOTE_SERVICE_OK, "Got push notification for Drive.");
   1014 
   1015   // TODO(calvinlo): Try to eliminate may_have_unfetched_changes_ variable.
   1016   may_have_unfetched_changes_ = true;
   1017   MaybeStartFetchChanges();
   1018 }
   1019 
   1020 void DriveFileSyncService::OnPushNotificationEnabled(bool enabled) {
   1021   VLOG(2) << "XMPP Push notification is " << (enabled ? "enabled" : "disabled");
   1022 }
   1023 
   1024 void DriveFileSyncService::MaybeScheduleNextTask() {
   1025   if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
   1026     return;
   1027 
   1028   // Notify observer of the update of |pending_changes_|.
   1029   FOR_EACH_OBSERVER(Observer, service_observers_,
   1030                     OnRemoteChangeQueueUpdated(
   1031                         remote_change_handler_.ChangesSize()));
   1032 
   1033   MaybeStartFetchChanges();
   1034 }
   1035 
   1036 void DriveFileSyncService::NotifyLastOperationStatus(
   1037     SyncStatusCode sync_status) {
   1038   UpdateServiceStateFromLastOperationStatus(sync_status, last_gdata_error_);
   1039 }
   1040 
   1041 // static
   1042 std::string DriveFileSyncService::PathToTitle(const base::FilePath& path) {
   1043   if (!IsSyncFSDirectoryOperationEnabled())
   1044     return path.AsUTF8Unsafe();
   1045 
   1046   return fileapi::FilePathToString(
   1047       base::FilePath(fileapi::VirtualPath::GetNormalizedFilePath(path)));
   1048 }
   1049 
   1050 // static
   1051 base::FilePath DriveFileSyncService::TitleToPath(const std::string& title) {
   1052   if (!IsSyncFSDirectoryOperationEnabled())
   1053     return base::FilePath::FromUTF8Unsafe(title);
   1054 
   1055   return fileapi::StringToFilePath(title).NormalizePathSeparators();
   1056 }
   1057 
   1058 // static
   1059 DriveMetadata::ResourceType
   1060 DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
   1061     SyncFileType file_type) {
   1062   DCHECK_NE(SYNC_FILE_TYPE_UNKNOWN, file_type);
   1063   switch (file_type) {
   1064     case SYNC_FILE_TYPE_UNKNOWN:
   1065       return DriveMetadata_ResourceType_RESOURCE_TYPE_FILE;
   1066     case SYNC_FILE_TYPE_FILE:
   1067       return DriveMetadata_ResourceType_RESOURCE_TYPE_FILE;
   1068     case SYNC_FILE_TYPE_DIRECTORY:
   1069       return DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER;
   1070   }
   1071   NOTREACHED();
   1072   return DriveMetadata_ResourceType_RESOURCE_TYPE_FILE;
   1073 }
   1074 
   1075 // static
   1076 SyncFileType DriveFileSyncService::DriveMetadataResourceTypeToSyncFileType(
   1077     DriveMetadata::ResourceType resource_type) {
   1078   switch (resource_type) {
   1079     case DriveMetadata_ResourceType_RESOURCE_TYPE_FILE:
   1080       return SYNC_FILE_TYPE_FILE;
   1081     case DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER:
   1082       return SYNC_FILE_TYPE_DIRECTORY;
   1083   }
   1084   NOTREACHED();
   1085   return SYNC_FILE_TYPE_UNKNOWN;
   1086 }
   1087 
   1088 void DriveFileSyncService::FetchChangesForIncrementalSync(
   1089     const SyncStatusCallback& callback) {
   1090   DCHECK(may_have_unfetched_changes_);
   1091   DCHECK(pending_batch_sync_origins_.empty());
   1092   DCHECK(!metadata_store_->incremental_sync_origins().empty());
   1093 
   1094   DVLOG(1) << "FetchChangesForIncrementalSync (start_changestamp:"
   1095            << (largest_fetched_changestamp_ + 1) << ")";
   1096 
   1097   api_util_->ListChanges(
   1098       largest_fetched_changestamp_ + 1,
   1099       base::Bind(&DriveFileSyncService::DidFetchChangesForIncrementalSync,
   1100                  AsWeakPtr(),
   1101                  callback,
   1102                  false));
   1103 
   1104   may_have_unfetched_changes_ = false;
   1105 }
   1106 
   1107 void DriveFileSyncService::DidFetchChangesForIncrementalSync(
   1108     const SyncStatusCallback& callback,
   1109     bool has_new_changes,
   1110     google_apis::GDataErrorCode error,
   1111     scoped_ptr<google_apis::ResourceList> changes) {
   1112   if (error != google_apis::HTTP_SUCCESS) {
   1113     callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
   1114     return;
   1115   }
   1116 
   1117   bool reset_sync_root = false;
   1118   std::set<GURL> reset_origins;
   1119 
   1120   typedef ScopedVector<google_apis::ResourceEntry>::const_iterator iterator;
   1121   for (iterator itr = changes->entries().begin();
   1122        itr != changes->entries().end(); ++itr) {
   1123     const google_apis::ResourceEntry& entry = **itr;
   1124 
   1125     if (entry.deleted()) {
   1126       // Check if the sync root or origin root folder is deleted.
   1127       // (We reset resource_id after the for loop so that we can handle
   1128       // recursive delete for the origin (at least in this feed)
   1129       // while GetOriginForEntry for the origin still works.)
   1130       if (entry.resource_id() == sync_root_resource_id()) {
   1131         reset_sync_root = true;
   1132         continue;
   1133       }
   1134       GURL origin;
   1135       if (metadata_store_->GetOriginByOriginRootDirectoryId(
   1136               entry.resource_id(), &origin)) {
   1137         reset_origins.insert(origin);
   1138         continue;
   1139       }
   1140     }
   1141 
   1142     GURL origin;
   1143     if (!GetOriginForEntry(entry, &origin))
   1144       continue;
   1145 
   1146     DVLOG(3) << " * change:" << entry.title()
   1147              << (entry.deleted() ? " (deleted)" : " ")
   1148              << "[" << origin.spec() << "]";
   1149     has_new_changes = AppendRemoteChange(
   1150         origin, entry, entry.changestamp()) || has_new_changes;
   1151   }
   1152 
   1153   if (reset_sync_root) {
   1154     util::Log(logging::LOG_WARNING,
   1155               FROM_HERE,
   1156               "Detected unexpected SyncRoot deletion.");
   1157     metadata_store_->SetSyncRootDirectory(std::string());
   1158   }
   1159   for (std::set<GURL>::iterator itr = reset_origins.begin();
   1160        itr != reset_origins.end(); ++itr) {
   1161     util::Log(logging::LOG_WARNING,
   1162               FROM_HERE,
   1163               "Detected unexpected OriginRoot deletion: %s",
   1164               itr->spec().c_str());
   1165     pending_batch_sync_origins_.erase(*itr);
   1166     metadata_store_->SetOriginRootDirectory(*itr, std::string());
   1167   }
   1168 
   1169   GURL next_feed;
   1170   if (changes->GetNextFeedURL(&next_feed))
   1171     may_have_unfetched_changes_ = true;
   1172 
   1173   if (!changes->entries().empty())
   1174     largest_fetched_changestamp_ = changes->entries().back()->changestamp();
   1175 
   1176   callback.Run(SYNC_STATUS_OK);
   1177 }
   1178 
   1179 bool DriveFileSyncService::GetOriginForEntry(
   1180     const google_apis::ResourceEntry& entry,
   1181     GURL* origin_out) {
   1182   typedef ScopedVector<google_apis::Link>::const_iterator iterator;
   1183   for (iterator itr = entry.links().begin();
   1184        itr != entry.links().end(); ++itr) {
   1185     if ((*itr)->type() != google_apis::Link::LINK_PARENT)
   1186       continue;
   1187 
   1188     std::string resource_id(
   1189         drive::util::ExtractResourceIdFromUrl((*itr)->href()));
   1190     if (resource_id.empty())
   1191       continue;
   1192 
   1193     GURL origin;
   1194     metadata_store_->GetOriginByOriginRootDirectoryId(resource_id, &origin);
   1195     if (!origin.is_valid() || !metadata_store_->IsIncrementalSyncOrigin(origin))
   1196       continue;
   1197 
   1198     *origin_out = origin;
   1199     return true;
   1200   }
   1201   return false;
   1202 }
   1203 
   1204 void DriveFileSyncService::NotifyObserversFileStatusChanged(
   1205     const FileSystemURL& url,
   1206     SyncFileStatus sync_status,
   1207     SyncAction action_taken,
   1208     SyncDirection direction) {
   1209   if (sync_status != SYNC_FILE_STATUS_SYNCED) {
   1210     DCHECK_EQ(SYNC_ACTION_NONE, action_taken);
   1211     DCHECK_EQ(SYNC_DIRECTION_NONE, direction);
   1212   }
   1213 
   1214   FOR_EACH_OBSERVER(
   1215       FileStatusObserver, file_status_observers_,
   1216       OnFileStatusChanged(url, sync_status, action_taken, direction));
   1217 }
   1218 
   1219 void DriveFileSyncService::EnsureSyncRootDirectory(
   1220     const ResourceIdCallback& callback) {
   1221   if (!sync_root_resource_id().empty()) {
   1222     callback.Run(SYNC_STATUS_OK, sync_root_resource_id());
   1223     return;
   1224   }
   1225 
   1226   api_util_->GetDriveDirectoryForSyncRoot(base::Bind(
   1227       &DriveFileSyncService::DidEnsureSyncRoot, AsWeakPtr(), callback));
   1228 }
   1229 
   1230 void DriveFileSyncService::DidEnsureSyncRoot(
   1231     const ResourceIdCallback& callback,
   1232     google_apis::GDataErrorCode error,
   1233     const std::string& sync_root_resource_id) {
   1234   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
   1235   if (status == SYNC_STATUS_OK)
   1236     metadata_store_->SetSyncRootDirectory(sync_root_resource_id);
   1237   callback.Run(status, sync_root_resource_id);
   1238 }
   1239 
   1240 void DriveFileSyncService::EnsureOriginRootDirectory(
   1241     const GURL& origin,
   1242     const ResourceIdCallback& callback) {
   1243   std::string resource_id = metadata_store_->GetResourceIdForOrigin(origin);
   1244   if (!resource_id.empty()) {
   1245     callback.Run(SYNC_STATUS_OK, resource_id);
   1246     return;
   1247   }
   1248 
   1249   EnsureSyncRootDirectory(base::Bind(
   1250       &DriveFileSyncService::DidEnsureSyncRootForOriginRoot,
   1251       AsWeakPtr(), origin, callback));
   1252 }
   1253 
   1254 void DriveFileSyncService::DidEnsureSyncRootForOriginRoot(
   1255     const GURL& origin,
   1256     const ResourceIdCallback& callback,
   1257     SyncStatusCode status,
   1258     const std::string& sync_root_resource_id) {
   1259   if (status != SYNC_STATUS_OK) {
   1260     callback.Run(status, std::string());
   1261     return;
   1262   }
   1263 
   1264   api_util_->GetDriveDirectoryForOrigin(
   1265       sync_root_resource_id,
   1266       origin,
   1267       base::Bind(&DriveFileSyncService::DidEnsureOriginRoot,
   1268                  AsWeakPtr(),
   1269                  origin,
   1270                  callback));
   1271 }
   1272 
   1273 void DriveFileSyncService::DidEnsureOriginRoot(
   1274     const GURL& origin,
   1275     const ResourceIdCallback& callback,
   1276     google_apis::GDataErrorCode error,
   1277     const std::string& resource_id) {
   1278   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
   1279   if (status == SYNC_STATUS_OK &&
   1280       metadata_store_->IsKnownOrigin(origin)) {
   1281     metadata_store_->SetOriginRootDirectory(origin, resource_id);
   1282   }
   1283   callback.Run(status, resource_id);
   1284 }
   1285 
   1286 std::string DriveFileSyncService::sync_root_resource_id() {
   1287   return metadata_store_->sync_root_directory();
   1288 }
   1289 
   1290 }  // namespace sync_file_system
   1291