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