Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend_v1 / drive_file_sync_service.cc
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