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