Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / remote_to_local_syncer.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/remote_to_local_syncer.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/format_macros.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/task_runner_util.h"
14 #include "chrome/browser/drive/drive_api_util.h"
15 #include "chrome/browser/drive/drive_service_interface.h"
16 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
17 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
18 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
19 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
20 #include "chrome/browser/sync_file_system/logger.h"
21 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
22 #include "extensions/common/extension.h"
23 #include "google_apis/drive/drive_api_parser.h"
24 #include "google_apis/drive/gdata_wapi_parser.h"
25 #include "webkit/common/fileapi/file_system_util.h"
26
27 namespace sync_file_system {
28 namespace drive_backend {
29
30 namespace {
31
32 bool BuildFileSystemURL(
33     MetadataDatabase* metadata_database,
34     const FileTracker& tracker,
35     fileapi::FileSystemURL* url) {
36   base::FilePath path;
37   if (!metadata_database->BuildPathForTracker(
38           tracker.tracker_id(), &path))
39     return false;
40
41   GURL origin =
42       extensions::Extension::GetBaseURLFromExtensionId(tracker.app_id());
43   *url = sync_file_system::CreateSyncableFileSystemURL(origin, path);
44
45   return true;
46 }
47
48 bool HasFolderAsParent(const FileDetails& details,
49                        const std::string& folder_id) {
50   for (int i = 0; i < details.parent_folder_ids_size(); ++i) {
51     if (details.parent_folder_ids(i) == folder_id)
52       return true;
53   }
54   return false;
55 }
56
57 bool HasDisabledAppRoot(MetadataDatabase* database,
58                         const FileTracker& tracker) {
59   DCHECK(tracker.active());
60   FileTracker app_root_tracker;
61   if (database->FindAppRootTracker(tracker.app_id(), &app_root_tracker)) {
62     DCHECK(app_root_tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
63            app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT);
64     return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
65   }
66   return false;
67 }
68
69 scoped_ptr<FileMetadata> GetFileMetadata(MetadataDatabase* database,
70                                          const std::string& file_id) {
71   scoped_ptr<FileMetadata> metadata(new FileMetadata);
72   if (!database->FindFileByFileID(file_id, metadata.get()))
73     metadata.reset();
74   return metadata.Pass();
75 }
76
77 }  // namespace
78
79 RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext* sync_context)
80     : sync_context_(sync_context),
81       sync_action_(SYNC_ACTION_NONE),
82       prepared_(false),
83       sync_root_deletion_(false),
84       weak_ptr_factory_(this) {
85 }
86
87 RemoteToLocalSyncer::~RemoteToLocalSyncer() {
88 }
89
90 void RemoteToLocalSyncer::RunExclusive(const SyncStatusCallback& callback) {
91   if (!drive_service() || !metadata_database() || !remote_change_processor()) {
92     util::Log(logging::LOG_VERBOSE, FROM_HERE,
93               "[Remote -> Local] Context not ready.");
94     NOTREACHED();
95     callback.Run(SYNC_STATUS_FAILED);
96     return;
97   }
98
99   SyncStatusCallback wrapped_callback = base::Bind(
100       &RemoteToLocalSyncer::SyncCompleted, weak_ptr_factory_.GetWeakPtr(),
101       base::Bind(&RemoteToLocalSyncer::FinalizeSync,
102                  weak_ptr_factory_.GetWeakPtr(),
103                  callback));
104
105   dirty_tracker_ = make_scoped_ptr(new FileTracker);
106   if (metadata_database()->GetNormalPriorityDirtyTracker(
107           dirty_tracker_.get())) {
108     util::Log(logging::LOG_VERBOSE, FROM_HERE,
109               "[Remote -> Local] Start: tracker_id=%" PRId64,
110               dirty_tracker_->tracker_id());
111     ResolveRemoteChange(wrapped_callback);
112     return;
113   }
114
115   util::Log(logging::LOG_VERBOSE, FROM_HERE,
116             "[Remote -> Local] Nothing to do.");
117   sync_context_->GetWorkerTaskRunner()->PostTask(
118       FROM_HERE,
119       base::Bind(callback, SYNC_STATUS_NO_CHANGE_TO_SYNC));
120 }
121
122 void RemoteToLocalSyncer::ResolveRemoteChange(
123     const SyncStatusCallback& callback) {
124   DCHECK(dirty_tracker_);
125   remote_metadata_ = GetFileMetadata(
126       metadata_database(), dirty_tracker_->file_id());
127
128   if (!remote_metadata_ || !remote_metadata_->has_details()) {
129     if (remote_metadata_ && !remote_metadata_->has_details()) {
130       LOG(ERROR) << "Missing details of a remote file: "
131                  << remote_metadata_->file_id();
132       NOTREACHED();
133     }
134     util::Log(logging::LOG_VERBOSE, FROM_HERE,
135               "[Remote -> Local]: Missing remote metadata case.");
136     HandleMissingRemoteMetadata(callback);
137     return;
138   }
139
140   DCHECK(remote_metadata_);
141   DCHECK(remote_metadata_->has_details());
142   const FileDetails& remote_details = remote_metadata_->details();
143
144   if (!dirty_tracker_->active() ||
145       HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
146     // Handle inactive tracker in SyncCompleted.
147     util::Log(logging::LOG_VERBOSE, FROM_HERE,
148               "[Remote -> Local]: Inactive tracker case.");
149     callback.Run(SYNC_STATUS_OK);
150     return;
151   }
152
153   DCHECK(dirty_tracker_->active());
154   DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
155
156   if (!dirty_tracker_->has_synced_details()) {
157     LOG(ERROR) << "Missing synced_details of an active tracker: "
158                << dirty_tracker_->tracker_id();
159     NOTREACHED();
160     callback.Run(SYNC_STATUS_FAILED);
161     return;
162   }
163
164   DCHECK(dirty_tracker_->has_synced_details());
165   const FileDetails& synced_details = dirty_tracker_->synced_details();
166
167   if (dirty_tracker_->tracker_id() ==
168       metadata_database()->GetSyncRootTrackerID()) {
169     if (remote_details.missing() ||
170         synced_details.title() != remote_details.title() ||
171         remote_details.parent_folder_ids_size()) {
172       util::Log(logging::LOG_VERBOSE, FROM_HERE,
173                 "[Remote -> Local]: Sync-root deletion.");
174       HandleSyncRootDeletion(callback);
175       return;
176     }
177     util::Log(logging::LOG_VERBOSE, FROM_HERE,
178               "[Remote -> Local]: Trivial sync-root change.");
179     callback.Run(SYNC_STATUS_OK);
180     return;
181   }
182
183   DCHECK_NE(dirty_tracker_->tracker_id(),
184             metadata_database()->GetSyncRootTrackerID());
185
186   if (remote_details.missing()) {
187     if (!synced_details.missing()) {
188       util::Log(logging::LOG_VERBOSE, FROM_HERE,
189                 "[Remote -> Local]: Remote file deletion.");
190       HandleDeletion(callback);
191       return;
192     }
193
194     DCHECK(synced_details.missing());
195     LOG(ERROR) << "Found a stray missing tracker: "
196                << dirty_tracker_->file_id();
197     NOTREACHED();
198     callback.Run(SYNC_STATUS_OK);
199     return;
200   }
201
202   // Most of remote_details field is valid from here.
203   DCHECK(!remote_details.missing());
204
205   if (synced_details.file_kind() != remote_details.file_kind()) {
206     LOG(ERROR) << "Found type mismatch between remote and local file: "
207                << dirty_tracker_->file_id()
208                << " type: (local) " << synced_details.file_kind()
209                << " vs (remote) " << remote_details.file_kind();
210     NOTREACHED();
211     callback.Run(SYNC_STATUS_FAILED);
212     return;
213   }
214   DCHECK_EQ(synced_details.file_kind(), remote_details.file_kind());
215
216   if (synced_details.file_kind() == FILE_KIND_UNSUPPORTED) {
217     LOG(ERROR) << "Found an unsupported active file: "
218                << remote_metadata_->file_id();
219     NOTREACHED();
220     callback.Run(SYNC_STATUS_FAILED);
221     return;
222   }
223   DCHECK(remote_details.file_kind() == FILE_KIND_FILE ||
224          remote_details.file_kind() == FILE_KIND_FOLDER);
225
226   if (synced_details.title() != remote_details.title()) {
227     // Handle rename as deletion + addition.
228     util::Log(logging::LOG_VERBOSE, FROM_HERE,
229               "[Remote -> Local]: Detected file rename.");
230     Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
231                        weak_ptr_factory_.GetWeakPtr(), callback));
232     return;
233   }
234   DCHECK_EQ(synced_details.title(), remote_details.title());
235
236   FileTracker parent_tracker;
237   if (!metadata_database()->FindTrackerByTrackerID(
238           dirty_tracker_->parent_tracker_id(), &parent_tracker)) {
239     LOG(ERROR) << "Missing parent tracker for a non sync-root tracker: "
240                << dirty_tracker_->file_id();
241     NOTREACHED();
242     callback.Run(SYNC_STATUS_FAILED);
243     return;
244   }
245
246   if (!HasFolderAsParent(remote_details, parent_tracker.file_id())) {
247     // Handle reorganize as deletion + addition.
248     util::Log(logging::LOG_VERBOSE, FROM_HERE,
249               "[Remote -> Local]: Detected file reorganize.");
250     Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
251                        weak_ptr_factory_.GetWeakPtr(), callback));
252     return;
253   }
254
255   if (synced_details.file_kind() == FILE_KIND_FILE) {
256     if (synced_details.md5() != remote_details.md5()) {
257       util::Log(logging::LOG_VERBOSE, FROM_HERE,
258                 "[Remote -> Local]: Detected file content update.");
259       HandleContentUpdate(callback);
260       return;
261     }
262   } else {
263     DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
264     if (synced_details.missing()) {
265       util::Log(logging::LOG_VERBOSE, FROM_HERE,
266                 "[Remote -> Local]: Detected folder update.");
267       HandleFolderUpdate(callback);
268       return;
269     }
270     if (dirty_tracker_->needs_folder_listing()) {
271       util::Log(logging::LOG_VERBOSE, FROM_HERE,
272                 "[Remote -> Local]: Needs listing folder.");
273       ListFolderContent(callback);
274       return;
275     }
276     callback.Run(SYNC_STATUS_OK);
277     return;
278   }
279
280   util::Log(logging::LOG_VERBOSE, FROM_HERE,
281             "[Remote -> Local]: Trivial file change.");
282   callback.Run(SYNC_STATUS_OK);
283 }
284
285 void RemoteToLocalSyncer::HandleMissingRemoteMetadata(
286     const SyncStatusCallback& callback) {
287   DCHECK(dirty_tracker_);
288
289   drive_service()->GetFileResource(
290       dirty_tracker_->file_id(),
291       base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata,
292                  weak_ptr_factory_.GetWeakPtr(),
293                  callback));
294 }
295
296 void RemoteToLocalSyncer::DidGetRemoteMetadata(
297     const SyncStatusCallback& callback,
298     google_apis::GDataErrorCode error,
299     scoped_ptr<google_apis::FileResource> entry) {
300   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
301
302   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
303   if (status != SYNC_STATUS_OK &&
304       error != google_apis::HTTP_NOT_FOUND) {
305     callback.Run(status);
306     return;
307   }
308
309   if (error == google_apis::HTTP_NOT_FOUND) {
310     metadata_database()->UpdateByDeletedRemoteFile(
311         dirty_tracker_->file_id(), callback);
312     return;
313   }
314
315   if (!entry) {
316     NOTREACHED();
317     callback.Run(SYNC_STATUS_FAILED);
318     return;
319   }
320
321   metadata_database()->UpdateByFileResource(
322       *entry,
323       base::Bind(&RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata,
324                  weak_ptr_factory_.GetWeakPtr(), callback));
325 }
326
327 void RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata(
328     const SyncStatusCallback& callback,
329     SyncStatusCode status) {
330   if (status != SYNC_STATUS_OK) {
331     callback.Run(status);
332     return;
333   }
334
335   callback.Run(SYNC_STATUS_RETRY);  // Do not update |dirty_tracker_|.
336 }
337
338 void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
339     const SyncStatusCallback& callback,
340     SyncStatusCode status) {
341   if (status != SYNC_STATUS_OK) {
342     callback.Run(status);
343     return;
344   }
345
346   DCHECK(url_.is_valid());
347   DCHECK(local_metadata_);
348   DCHECK(local_changes_);
349
350   // Check if the local file exists.
351   if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
352       (!local_changes_->empty() && local_changes_->back().IsDelete())) {
353     sync_action_ = SYNC_ACTION_ADDED;
354     // Missing local file case.
355     // Download the file and add it to local as a new file.
356     DownloadFile(callback);
357     return;
358   }
359
360   DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
361   if (local_changes_->empty()) {
362     if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) {
363       sync_action_ = SYNC_ACTION_UPDATED;
364       // Download the file and overwrite the existing local file.
365       DownloadFile(callback);
366       return;
367     }
368
369     DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type);
370
371     // Got a remote regular file modification for existing local folder.
372     // Our policy prioritize folders in this case.
373     // Lower the priority of the tracker to prevent repeated remote sync to the
374     // same tracker, and let local-to-remote sync phase process this change.
375     metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
376     remote_change_processor()->RecordFakeLocalChange(
377         url_,
378         FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
379                    local_metadata_->file_type),
380         callback);
381     return;
382   }
383
384   DCHECK(local_changes_->back().IsAddOrUpdate());
385   // Conflict case.
386   // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
387
388   // Lower the priority of the tracker to prevent repeated remote sync to the
389   // same tracker.
390   metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
391   callback.Run(SYNC_STATUS_RETRY);
392 }
393
394 void RemoteToLocalSyncer::HandleFolderUpdate(
395     const SyncStatusCallback& callback) {
396   DCHECK(dirty_tracker_);
397   DCHECK(dirty_tracker_->active());
398   DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
399
400   DCHECK(remote_metadata_);
401   DCHECK(remote_metadata_->has_details());
402   DCHECK(!remote_metadata_->details().missing());
403   DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind());
404
405   Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate,
406                      weak_ptr_factory_.GetWeakPtr(), callback));
407 }
408
409 void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
410     const SyncStatusCallback& callback,
411     SyncStatusCode status) {
412   if (status != SYNC_STATUS_OK) {
413     callback.Run(status);
414     return;
415   }
416
417   DCHECK(url_.is_valid());
418   DCHECK(local_metadata_);
419   DCHECK(local_changes_);
420
421   // Check if the local file exists.
422   if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
423       (!local_changes_->empty() && local_changes_->back().IsDelete())) {
424     sync_action_ = SYNC_ACTION_ADDED;
425     // No local file exists at the path.
426     CreateFolder(callback);
427     return;
428   }
429
430   if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) {
431     // There already exists a folder, nothing left to do.
432     if (dirty_tracker_->needs_folder_listing() &&
433         !dirty_tracker_->synced_details().missing()) {
434       ListFolderContent(callback);
435     } else {
436       callback.Run(SYNC_STATUS_OK);
437     }
438     return;
439   }
440
441   DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type);
442   sync_action_ = SYNC_ACTION_ADDED;
443   // Got a remote folder for existing local file.
444   // Our policy prioritize folders in this case.
445   CreateFolder(callback);
446 }
447
448 void RemoteToLocalSyncer::HandleSyncRootDeletion(
449     const SyncStatusCallback& callback) {
450   sync_root_deletion_ = true;
451   callback.Run(SYNC_STATUS_OK);
452 }
453
454 void RemoteToLocalSyncer::HandleDeletion(
455     const SyncStatusCallback& callback) {
456   DCHECK(dirty_tracker_);
457   DCHECK(dirty_tracker_->active());
458   DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
459   DCHECK(dirty_tracker_->has_synced_details());
460   DCHECK(!dirty_tracker_->synced_details().missing());
461
462   DCHECK(remote_metadata_);
463   DCHECK(remote_metadata_->has_details());
464   DCHECK(remote_metadata_->details().missing());
465
466   Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
467                      weak_ptr_factory_.GetWeakPtr(), callback));
468 }
469
470 void RemoteToLocalSyncer::DidPrepareForDeletion(
471     const SyncStatusCallback& callback,
472     SyncStatusCode status) {
473   if (status != SYNC_STATUS_OK) {
474     callback.Run(status);
475     return;
476   }
477
478   DCHECK(url_.is_valid());
479   DCHECK(local_metadata_);
480   DCHECK(local_changes_);
481
482   // Check if the local file exists.
483   if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
484       (!local_changes_->empty() && local_changes_->back().IsDelete())) {
485     // No local file exists at the path.
486     callback.Run(SYNC_STATUS_OK);
487     return;
488   }
489
490   DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
491   if (local_changes_->empty()) {
492     sync_action_ = SYNC_ACTION_DELETED;
493     DeleteLocalFile(callback);
494     return;
495   }
496
497   DCHECK(local_changes_->back().IsAddOrUpdate());
498   // File is remotely deleted and locally updated.
499   // Ignore the remote deletion and handle it as if applied successfully.
500   callback.Run(SYNC_STATUS_OK);
501 }
502
503 void RemoteToLocalSyncer::HandleContentUpdate(
504     const SyncStatusCallback& callback) {
505   DCHECK(dirty_tracker_);
506   DCHECK(dirty_tracker_->active());
507   DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
508   DCHECK(dirty_tracker_->has_synced_details());
509   DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind());
510
511   DCHECK(remote_metadata_);
512   DCHECK(remote_metadata_->has_details());
513   DCHECK(!remote_metadata_->details().missing());
514
515   DCHECK_NE(dirty_tracker_->synced_details().md5(),
516             remote_metadata_->details().md5());
517
518   Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile,
519                      weak_ptr_factory_.GetWeakPtr(), callback));
520 }
521
522 void RemoteToLocalSyncer::ListFolderContent(
523     const SyncStatusCallback& callback) {
524   DCHECK(dirty_tracker_);
525   DCHECK(dirty_tracker_->active());
526   DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
527   DCHECK(dirty_tracker_->has_synced_details());
528   DCHECK(!dirty_tracker_->synced_details().missing());
529   DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind());
530   DCHECK(dirty_tracker_->needs_folder_listing());
531
532   DCHECK(remote_metadata_);
533   DCHECK(remote_metadata_->has_details());
534   DCHECK(!remote_metadata_->details().missing());
535
536   // TODO(tzik): Replace this call with ChildList version.
537   drive_service()->GetFileListInDirectory(
538       dirty_tracker_->file_id(),
539       base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
540                  weak_ptr_factory_.GetWeakPtr(),
541                  callback,
542                  base::Passed(make_scoped_ptr(new FileIDList))));
543 }
544
545 void RemoteToLocalSyncer::DidListFolderContent(
546     const SyncStatusCallback& callback,
547     scoped_ptr<FileIDList> children,
548     google_apis::GDataErrorCode error,
549     scoped_ptr<google_apis::FileList> file_list) {
550   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
551   if (status != SYNC_STATUS_OK) {
552     callback.Run(status);
553     return;
554   }
555
556   if (!file_list) {
557     NOTREACHED();
558     callback.Run(SYNC_STATUS_FAILED);
559     return;
560   }
561
562   children->reserve(children->size() + file_list->items().size());
563   for (ScopedVector<google_apis::FileResource>::const_iterator itr =
564            file_list->items().begin();
565        itr != file_list->items().end();
566        ++itr) {
567     children->push_back((*itr)->file_id());
568   }
569
570   if (!file_list->next_link().is_empty()) {
571     drive_service()->GetRemainingFileList(
572         file_list->next_link(),
573         base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
574                    weak_ptr_factory_.GetWeakPtr(),
575                    callback, base::Passed(&children)));
576     return;
577   }
578
579   metadata_database()->PopulateFolderByChildList(
580       dirty_tracker_->file_id(), *children, callback);
581 }
582
583 void RemoteToLocalSyncer::SyncCompleted(const SyncStatusCallback& callback,
584                                         SyncStatusCode status) {
585   util::Log(logging::LOG_VERBOSE, FROM_HERE,
586             "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64
587             " status=%s",
588             SyncActionToString(sync_action_), dirty_tracker_->tracker_id(),
589             SyncStatusCodeToString(status));
590
591   if (sync_root_deletion_) {
592     callback.Run(SYNC_STATUS_OK);
593     return;
594   }
595
596   if (status == SYNC_STATUS_RETRY) {
597     callback.Run(SYNC_STATUS_OK);
598     return;
599   }
600
601   if (status != SYNC_STATUS_OK) {
602     callback.Run(status);
603     return;
604   }
605
606   DCHECK(dirty_tracker_);
607   DCHECK(remote_metadata_);
608   DCHECK(remote_metadata_->has_details());
609
610   FileDetails updated_details = remote_metadata_->details();
611   if (!dirty_tracker_->active() ||
612       HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
613     // Operations for an inactive tracker don't update file content.
614     if (dirty_tracker_->has_synced_details())
615       updated_details.set_md5(dirty_tracker_->synced_details().md5());
616     if (!dirty_tracker_->active()) {
617       // Keep missing true, as the change hasn't been synced to local.
618       updated_details.clear_md5();
619       updated_details.set_missing(true);
620     }
621   }
622   metadata_database()->UpdateTracker(dirty_tracker_->tracker_id(),
623                                      updated_details,
624                                      callback);
625 }
626
627 void RemoteToLocalSyncer::FinalizeSync(const SyncStatusCallback& callback,
628                                        SyncStatusCode status) {
629   if (prepared_) {
630     remote_change_processor()->FinalizeRemoteSync(
631         url_, false /* clear_local_change */, base::Bind(callback, status));
632     return;
633   }
634
635   callback.Run(status);
636 }
637
638 void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) {
639   bool should_success = BuildFileSystemURL(
640       metadata_database(), *dirty_tracker_, &url_);
641   DCHECK(should_success);
642   DCHECK(url_.is_valid());
643   remote_change_processor()->PrepareForProcessRemoteChange(
644       url_,
645       base::Bind(&RemoteToLocalSyncer::DidPrepare,
646                  weak_ptr_factory_.GetWeakPtr(),
647                  callback));
648 }
649
650 void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback,
651                                      SyncStatusCode status,
652                                      const SyncFileMetadata& local_metadata,
653                                      const FileChangeList& local_changes) {
654   if (status != SYNC_STATUS_OK) {
655     callback.Run(status);
656     return;
657   }
658   prepared_ = true;
659
660   local_metadata_.reset(new SyncFileMetadata(local_metadata));
661   local_changes_.reset(new FileChangeList(local_changes));
662
663   callback.Run(status);
664 }
665
666 void RemoteToLocalSyncer::DeleteLocalFile(const SyncStatusCallback& callback) {
667   remote_change_processor()->ApplyRemoteChange(
668       FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN),
669       base::FilePath(),
670       url_,
671       callback);
672 }
673
674 void RemoteToLocalSyncer::DownloadFile(const SyncStatusCallback& callback) {
675   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
676
677   base::Callback<void(webkit_blob::ScopedFile)> did_create_callback =
678       base::Bind(&RemoteToLocalSyncer::DidCreateTemporaryFileForDownload,
679                  weak_ptr_factory_.GetWeakPtr(), callback);
680
681   sync_context_->GetFileTaskRunner()->PostTask(
682       FROM_HERE,
683       CreateComposedFunction(
684           base::Bind(&CreateTemporaryFile,
685                      make_scoped_refptr(sync_context_->GetFileTaskRunner())),
686           RelayCallbackToTaskRunner(
687               sync_context_->GetWorkerTaskRunner(), FROM_HERE,
688               did_create_callback)));
689 }
690
691 void RemoteToLocalSyncer::DidCreateTemporaryFileForDownload(
692     const SyncStatusCallback& callback,
693     webkit_blob::ScopedFile file) {
694   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
695   base::FilePath path = file.path();
696   drive_service()->DownloadFile(
697       path, remote_metadata_->file_id(),
698       base::Bind(&RemoteToLocalSyncer::DidDownloadFile,
699                  weak_ptr_factory_.GetWeakPtr(),
700                  callback, base::Passed(&file)),
701       google_apis::GetContentCallback(),
702       google_apis::ProgressCallback());
703 }
704
705 void RemoteToLocalSyncer::DidDownloadFile(const SyncStatusCallback& callback,
706                                           webkit_blob::ScopedFile file,
707                                           google_apis::GDataErrorCode error,
708                                           const base::FilePath&) {
709   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
710
711   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
712   if (status != SYNC_STATUS_OK) {
713     callback.Run(status);
714     return;
715   }
716
717   base::FilePath path = file.path();
718   base::Callback<void(const std::string&)> did_calculate_callback =
719       base::Bind(&RemoteToLocalSyncer::DidCalculateMD5ForDownload,
720                  weak_ptr_factory_.GetWeakPtr(),
721                  callback, base::Passed(&file));
722
723   sync_context_->GetFileTaskRunner()->PostTask(
724       FROM_HERE,
725       CreateComposedFunction(
726           base::Bind(&drive::util::GetMd5Digest, path),
727           RelayCallbackToTaskRunner(
728               sync_context_->GetWorkerTaskRunner(), FROM_HERE,
729               did_calculate_callback)));
730 }
731
732 void RemoteToLocalSyncer::DidCalculateMD5ForDownload(
733     const SyncStatusCallback& callback,
734     webkit_blob::ScopedFile file,
735     const std::string& md5) {
736   if (md5.empty()) {
737     callback.Run(SYNC_FILE_ERROR_NOT_FOUND);
738     return;
739   }
740
741   if (md5 != remote_metadata_->details().md5()) {
742     // File has been modified since last metadata retrieval.
743
744     // Lower the priority of the tracker to prevent repeated remote sync to the
745     // same tracker.
746     metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
747     callback.Run(SYNC_STATUS_RETRY);
748     return;
749   }
750
751   base::FilePath path = file.path();
752   remote_change_processor()->ApplyRemoteChange(
753       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE),
754       path, url_,
755       base::Bind(&RemoteToLocalSyncer::DidApplyDownload,
756                  weak_ptr_factory_.GetWeakPtr(),
757                  callback, base::Passed(&file)));
758 }
759
760 void RemoteToLocalSyncer::DidApplyDownload(const SyncStatusCallback& callback,
761                                            webkit_blob::ScopedFile,
762                                            SyncStatusCode status) {
763   if (status != SYNC_STATUS_OK)
764     metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
765   callback.Run(status);
766 }
767
768 void RemoteToLocalSyncer::CreateFolder(const SyncStatusCallback& callback) {
769   remote_change_processor()->ApplyRemoteChange(
770       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
771                  SYNC_FILE_TYPE_DIRECTORY),
772       base::FilePath(), url_,
773       callback);
774 }
775
776 drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() {
777   return sync_context_->GetDriveService();
778 }
779
780 MetadataDatabase* RemoteToLocalSyncer::metadata_database() {
781   return sync_context_->GetMetadataDatabase();
782 }
783
784 RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() {
785   DCHECK(sync_context_->GetRemoteChangeProcessor());
786   return sync_context_->GetRemoteChangeProcessor();
787 }
788
789 }  // namespace drive_backend
790 }  // namespace sync_file_system