Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / remote_to_local_syncer.cc
blob9a571f2b990fa9d48d7e54dbd5b8c13ffeec5307
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.
5 #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
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/drive_backend_util.h"
17 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
18 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
19 #include "chrome/browser/sync_file_system/logger.h"
20 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
21 #include "extensions/common/extension.h"
22 #include "google_apis/drive/drive_api_parser.h"
23 #include "google_apis/drive/gdata_wapi_parser.h"
24 #include "webkit/common/fileapi/file_system_util.h"
26 namespace sync_file_system {
27 namespace drive_backend {
29 namespace {
31 bool BuildFileSystemURL(
32 MetadataDatabase* metadata_database,
33 const FileTracker& tracker,
34 fileapi::FileSystemURL* url) {
35 base::FilePath path;
36 if (!metadata_database->BuildPathForTracker(
37 tracker.tracker_id(), &path))
38 return false;
40 GURL origin =
41 extensions::Extension::GetBaseURLFromExtensionId(tracker.app_id());
42 *url = sync_file_system::CreateSyncableFileSystemURL(origin, path);
44 return true;
47 bool HasFolderAsParent(const FileDetails& details,
48 const std::string& folder_id) {
49 for (int i = 0; i < details.parent_folder_ids_size(); ++i) {
50 if (details.parent_folder_ids(i) == folder_id)
51 return true;
53 return false;
56 bool HasDisabledAppRoot(MetadataDatabase* database,
57 const FileTracker& tracker) {
58 DCHECK(tracker.active());
59 FileTracker app_root_tracker;
60 if (database->FindAppRootTracker(tracker.app_id(), &app_root_tracker)) {
61 DCHECK(app_root_tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
62 app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT);
63 return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
65 return false;
68 scoped_ptr<FileMetadata> GetFileMetadata(MetadataDatabase* database,
69 const std::string& file_id) {
70 scoped_ptr<FileMetadata> metadata(new FileMetadata);
71 if (!database->FindFileByFileID(file_id, metadata.get()))
72 metadata.reset();
73 return metadata.Pass();
76 } // namespace
78 RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext* sync_context)
79 : sync_context_(sync_context),
80 sync_action_(SYNC_ACTION_NONE),
81 prepared_(false),
82 sync_root_deletion_(false),
83 weak_ptr_factory_(this) {
86 RemoteToLocalSyncer::~RemoteToLocalSyncer() {
89 void RemoteToLocalSyncer::Run(const SyncStatusCallback& callback) {
90 if (!drive_service() || !metadata_database() || !remote_change_processor()) {
91 util::Log(logging::LOG_VERBOSE, FROM_HERE,
92 "[Remote -> Local] Context not ready.");
93 NOTREACHED();
94 callback.Run(SYNC_STATUS_FAILED);
95 return;
98 SyncStatusCallback wrapped_callback = base::Bind(
99 &RemoteToLocalSyncer::SyncCompleted, weak_ptr_factory_.GetWeakPtr(),
100 base::Bind(&RemoteToLocalSyncer::FinalizeSync,
101 weak_ptr_factory_.GetWeakPtr(),
102 callback));
104 dirty_tracker_ = make_scoped_ptr(new FileTracker);
105 if (metadata_database()->GetNormalPriorityDirtyTracker(
106 dirty_tracker_.get())) {
107 util::Log(logging::LOG_VERBOSE, FROM_HERE,
108 "[Remote -> Local] Start: tracker_id=%" PRId64,
109 dirty_tracker_->tracker_id());
110 ResolveRemoteChange(wrapped_callback);
111 return;
114 util::Log(logging::LOG_VERBOSE, FROM_HERE,
115 "[Remote -> Local] Nothing to do.");
116 base::MessageLoopProxy::current()->PostTask(
117 FROM_HERE,
118 base::Bind(callback, SYNC_STATUS_NO_CHANGE_TO_SYNC));
121 void RemoteToLocalSyncer::ResolveRemoteChange(
122 const SyncStatusCallback& callback) {
123 DCHECK(dirty_tracker_);
124 remote_metadata_ = GetFileMetadata(
125 metadata_database(), dirty_tracker_->file_id());
127 if (!remote_metadata_ || !remote_metadata_->has_details()) {
128 if (remote_metadata_ && !remote_metadata_->has_details()) {
129 LOG(ERROR) << "Missing details of a remote file: "
130 << remote_metadata_->file_id();
131 NOTREACHED();
133 util::Log(logging::LOG_VERBOSE, FROM_HERE,
134 "[Remote -> Local]: Missing remote file case.");
135 HandleMissingRemoteMetadata(callback);
136 return;
139 DCHECK(remote_metadata_);
140 DCHECK(remote_metadata_->has_details());
141 const FileDetails& remote_details = remote_metadata_->details();
143 if (!dirty_tracker_->active() ||
144 HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
145 // Handle inactive tracker in SyncCompleted.
146 util::Log(logging::LOG_VERBOSE, FROM_HERE,
147 "[Remote -> Local]: Inactive tracker case.");
148 callback.Run(SYNC_STATUS_OK);
149 return;
152 DCHECK(dirty_tracker_->active());
153 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
155 if (!dirty_tracker_->has_synced_details()) {
156 LOG(ERROR) << "Missing synced_details of an active tracker: "
157 << dirty_tracker_->tracker_id();
158 NOTREACHED();
159 callback.Run(SYNC_STATUS_FAILED);
160 return;
163 DCHECK(dirty_tracker_->has_synced_details());
164 const FileDetails& synced_details = dirty_tracker_->synced_details();
166 if (dirty_tracker_->tracker_id() ==
167 metadata_database()->GetSyncRootTrackerID()) {
168 if (remote_details.missing() ||
169 synced_details.title() != remote_details.title() ||
170 remote_details.parent_folder_ids_size()) {
171 util::Log(logging::LOG_VERBOSE, FROM_HERE,
172 "[Remote -> Local]: Sync-root deletion.");
173 HandleSyncRootDeletion(callback);
174 return;
176 util::Log(logging::LOG_VERBOSE, FROM_HERE,
177 "[Remote -> Local]: Trivial sync-root change.");
178 callback.Run(SYNC_STATUS_OK);
179 return;
182 DCHECK_NE(dirty_tracker_->tracker_id(),
183 metadata_database()->GetSyncRootTrackerID());
185 if (remote_details.missing()) {
186 if (!synced_details.missing()) {
187 util::Log(logging::LOG_VERBOSE, FROM_HERE,
188 "[Remote -> Local]: Remote file deletion.");
189 HandleDeletion(callback);
190 return;
193 DCHECK(synced_details.missing());
194 LOG(ERROR) << "Found a stray missing tracker: "
195 << dirty_tracker_->file_id();
196 NOTREACHED();
197 callback.Run(SYNC_STATUS_OK);
198 return;
201 // Most of remote_details field is valid from here.
202 DCHECK(!remote_details.missing());
204 if (synced_details.file_kind() != remote_details.file_kind()) {
205 LOG(ERROR) << "Found type mismatch between remote and local file: "
206 << dirty_tracker_->file_id()
207 << " type: (local) " << synced_details.file_kind()
208 << " vs (remote) " << remote_details.file_kind();
209 NOTREACHED();
210 callback.Run(SYNC_STATUS_FAILED);
211 return;
213 DCHECK_EQ(synced_details.file_kind(), remote_details.file_kind());
215 if (synced_details.file_kind() == FILE_KIND_UNSUPPORTED) {
216 LOG(ERROR) << "Found an unsupported active file: "
217 << remote_metadata_->file_id();
218 NOTREACHED();
219 callback.Run(SYNC_STATUS_FAILED);
220 return;
222 DCHECK(remote_details.file_kind() == FILE_KIND_FILE ||
223 remote_details.file_kind() == FILE_KIND_FOLDER);
225 if (synced_details.title() != remote_details.title()) {
226 // Handle rename as deletion + addition.
227 util::Log(logging::LOG_VERBOSE, FROM_HERE,
228 "[Remote -> Local]: Detected file rename.");
229 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
230 weak_ptr_factory_.GetWeakPtr(), callback));
231 return;
233 DCHECK_EQ(synced_details.title(), remote_details.title());
235 FileTracker parent_tracker;
236 if (!metadata_database()->FindTrackerByTrackerID(
237 dirty_tracker_->parent_tracker_id(), &parent_tracker)) {
238 LOG(ERROR) << "Missing parent tracker for a non sync-root tracker: "
239 << dirty_tracker_->file_id();
240 NOTREACHED();
241 callback.Run(SYNC_STATUS_FAILED);
242 return;
245 if (!HasFolderAsParent(remote_details, parent_tracker.file_id())) {
246 // Handle reorganize as deletion + addition.
247 util::Log(logging::LOG_VERBOSE, FROM_HERE,
248 "[Remote -> Local]: Detected file reorganize.");
249 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
250 weak_ptr_factory_.GetWeakPtr(), callback));
251 return;
254 if (synced_details.file_kind() == FILE_KIND_FILE) {
255 if (synced_details.md5() != remote_details.md5()) {
256 util::Log(logging::LOG_VERBOSE, FROM_HERE,
257 "[Remote -> Local]: Detected file content update.");
258 HandleContentUpdate(callback);
259 return;
261 } else {
262 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
263 if (synced_details.missing()) {
264 util::Log(logging::LOG_VERBOSE, FROM_HERE,
265 "[Remote -> Local]: Detected folder update.");
266 HandleFolderUpdate(callback);
267 return;
269 if (dirty_tracker_->needs_folder_listing()) {
270 util::Log(logging::LOG_VERBOSE, FROM_HERE,
271 "[Remote -> Local]: Needs listing folder.");
272 ListFolderContent(callback);
273 return;
275 callback.Run(SYNC_STATUS_OK);
276 return;
279 util::Log(logging::LOG_VERBOSE, FROM_HERE,
280 "[Remote -> Local]: Trivial file change.");
281 callback.Run(SYNC_STATUS_OK);
284 void RemoteToLocalSyncer::HandleMissingRemoteMetadata(
285 const SyncStatusCallback& callback) {
286 DCHECK(dirty_tracker_);
288 drive_service()->GetResourceEntry(
289 dirty_tracker_->file_id(),
290 base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata,
291 weak_ptr_factory_.GetWeakPtr(),
292 callback));
295 void RemoteToLocalSyncer::DidGetRemoteMetadata(
296 const SyncStatusCallback& callback,
297 google_apis::GDataErrorCode error,
298 scoped_ptr<google_apis::ResourceEntry> entry) {
299 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
300 if (status != SYNC_STATUS_OK &&
301 error != google_apis::HTTP_NOT_FOUND) {
302 callback.Run(status);
303 return;
306 if (error == google_apis::HTTP_NOT_FOUND) {
307 metadata_database()->UpdateByDeletedRemoteFile(
308 dirty_tracker_->file_id(), callback);
309 return;
312 if (!entry) {
313 NOTREACHED();
314 callback.Run(SYNC_STATUS_FAILED);
315 return;
318 metadata_database()->UpdateByFileResource(
319 *drive::util::ConvertResourceEntryToFileResource(*entry),
320 base::Bind(&RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata,
321 weak_ptr_factory_.GetWeakPtr(), callback));
324 void RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata(
325 const SyncStatusCallback& callback,
326 SyncStatusCode status) {
327 if (status != SYNC_STATUS_OK) {
328 callback.Run(status);
329 return;
332 callback.Run(SYNC_STATUS_RETRY); // Do not update |dirty_tracker_|.
335 void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
336 const SyncStatusCallback& callback,
337 SyncStatusCode status) {
338 if (status != SYNC_STATUS_OK) {
339 callback.Run(status);
340 return;
343 DCHECK(url_.is_valid());
344 DCHECK(local_metadata_);
345 DCHECK(local_changes_);
347 // Check if the local file exists.
348 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
349 (!local_changes_->empty() && local_changes_->back().IsDelete())) {
350 sync_action_ = SYNC_ACTION_ADDED;
351 // Missing local file case.
352 // Download the file and add it to local as a new file.
353 DownloadFile(callback);
354 return;
357 DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
358 if (local_changes_->empty()) {
359 if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) {
360 sync_action_ = SYNC_ACTION_UPDATED;
361 // Download the file and overwrite the existing local file.
362 DownloadFile(callback);
363 return;
366 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type);
368 // Got a remote regular file modification for existing local folder.
369 // Our policy prioritize folders in this case.
370 // Lower the priority of the tracker to prevent repeated remote sync to the
371 // same tracker, and let local-to-remote sync phase process this change.
372 metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
373 remote_change_processor()->RecordFakeLocalChange(
374 url_,
375 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
376 local_metadata_->file_type),
377 callback);
378 return;
381 DCHECK(local_changes_->back().IsAddOrUpdate());
382 // Conflict case.
383 // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
385 // Lower the priority of the tracker to prevent repeated remote sync to the
386 // same tracker.
387 metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
388 callback.Run(SYNC_STATUS_RETRY);
391 void RemoteToLocalSyncer::HandleFolderUpdate(
392 const SyncStatusCallback& callback) {
393 DCHECK(dirty_tracker_);
394 DCHECK(dirty_tracker_->active());
395 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
397 DCHECK(remote_metadata_);
398 DCHECK(remote_metadata_->has_details());
399 DCHECK(!remote_metadata_->details().missing());
400 DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind());
402 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate,
403 weak_ptr_factory_.GetWeakPtr(), callback));
406 void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
407 const SyncStatusCallback& callback,
408 SyncStatusCode status) {
409 if (status != SYNC_STATUS_OK) {
410 callback.Run(status);
411 return;
414 DCHECK(url_.is_valid());
415 DCHECK(local_metadata_);
416 DCHECK(local_changes_);
418 // Check if the local file exists.
419 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
420 (!local_changes_->empty() && local_changes_->back().IsDelete())) {
421 sync_action_ = SYNC_ACTION_ADDED;
422 // No local file exists at the path.
423 CreateFolder(callback);
424 return;
427 if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) {
428 // There already exists a folder, nothing left to do.
429 if (dirty_tracker_->needs_folder_listing() &&
430 !dirty_tracker_->synced_details().missing()) {
431 ListFolderContent(callback);
432 } else {
433 callback.Run(SYNC_STATUS_OK);
435 return;
438 DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type);
439 sync_action_ = SYNC_ACTION_ADDED;
440 // Got a remote folder for existing local file.
441 // Our policy prioritize folders in this case.
442 CreateFolder(callback);
445 void RemoteToLocalSyncer::HandleSyncRootDeletion(
446 const SyncStatusCallback& callback) {
447 sync_root_deletion_ = true;
448 callback.Run(SYNC_STATUS_OK);
451 void RemoteToLocalSyncer::HandleDeletion(
452 const SyncStatusCallback& callback) {
453 DCHECK(dirty_tracker_);
454 DCHECK(dirty_tracker_->active());
455 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
456 DCHECK(dirty_tracker_->has_synced_details());
457 DCHECK(!dirty_tracker_->synced_details().missing());
459 DCHECK(remote_metadata_);
460 DCHECK(remote_metadata_->has_details());
461 DCHECK(remote_metadata_->details().missing());
463 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
464 weak_ptr_factory_.GetWeakPtr(), callback));
467 void RemoteToLocalSyncer::DidPrepareForDeletion(
468 const SyncStatusCallback& callback,
469 SyncStatusCode status) {
470 if (status != SYNC_STATUS_OK) {
471 callback.Run(status);
472 return;
475 DCHECK(url_.is_valid());
476 DCHECK(local_metadata_);
477 DCHECK(local_changes_);
479 // Check if the local file exists.
480 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
481 (!local_changes_->empty() && local_changes_->back().IsDelete())) {
482 // No local file exists at the path.
483 callback.Run(SYNC_STATUS_OK);
484 return;
487 DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
488 if (local_changes_->empty()) {
489 sync_action_ = SYNC_ACTION_DELETED;
490 DeleteLocalFile(callback);
491 return;
494 DCHECK(local_changes_->back().IsAddOrUpdate());
495 // File is remotely deleted and locally updated.
496 // Ignore the remote deletion and handle it as if applied successfully.
497 callback.Run(SYNC_STATUS_OK);
500 void RemoteToLocalSyncer::HandleContentUpdate(
501 const SyncStatusCallback& callback) {
502 DCHECK(dirty_tracker_);
503 DCHECK(dirty_tracker_->active());
504 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
505 DCHECK(dirty_tracker_->has_synced_details());
506 DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind());
508 DCHECK(remote_metadata_);
509 DCHECK(remote_metadata_->has_details());
510 DCHECK(!remote_metadata_->details().missing());
512 DCHECK_NE(dirty_tracker_->synced_details().md5(),
513 remote_metadata_->details().md5());
515 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile,
516 weak_ptr_factory_.GetWeakPtr(), callback));
519 void RemoteToLocalSyncer::ListFolderContent(
520 const SyncStatusCallback& callback) {
521 DCHECK(dirty_tracker_);
522 DCHECK(dirty_tracker_->active());
523 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
524 DCHECK(dirty_tracker_->has_synced_details());
525 DCHECK(!dirty_tracker_->synced_details().missing());
526 DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind());
527 DCHECK(dirty_tracker_->needs_folder_listing());
529 DCHECK(remote_metadata_);
530 DCHECK(remote_metadata_->has_details());
531 DCHECK(!remote_metadata_->details().missing());
533 // TODO(tzik): Replace this call with ChildList version.
534 drive_service()->GetResourceListInDirectory(
535 dirty_tracker_->file_id(),
536 base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
537 weak_ptr_factory_.GetWeakPtr(),
538 callback,
539 base::Passed(make_scoped_ptr(new FileIDList))));
542 void RemoteToLocalSyncer::DidListFolderContent(
543 const SyncStatusCallback& callback,
544 scoped_ptr<FileIDList> children,
545 google_apis::GDataErrorCode error,
546 scoped_ptr<google_apis::ResourceList> resource_list) {
547 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
548 if (status != SYNC_STATUS_OK) {
549 callback.Run(status);
550 return;
553 if (!resource_list) {
554 NOTREACHED();
555 callback.Run(SYNC_STATUS_FAILED);
556 return;
559 children->reserve(children->size() + resource_list->entries().size());
560 for (ScopedVector<google_apis::ResourceEntry>::const_iterator itr =
561 resource_list->entries().begin();
562 itr != resource_list->entries().end();
563 ++itr) {
564 children->push_back((*itr)->resource_id());
567 GURL next_feed;
568 if (resource_list->GetNextFeedURL(&next_feed)) {
569 drive_service()->GetRemainingFileList(
570 next_feed,
571 base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
572 weak_ptr_factory_.GetWeakPtr(),
573 callback, base::Passed(&children)));
574 return;
577 metadata_database()->PopulateFolderByChildList(
578 dirty_tracker_->file_id(), *children, callback);
581 void RemoteToLocalSyncer::SyncCompleted(const SyncStatusCallback& callback,
582 SyncStatusCode status) {
583 util::Log(logging::LOG_VERBOSE, FROM_HERE,
584 "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64
585 " status=%s",
586 SyncActionToString(sync_action_), dirty_tracker_->tracker_id(),
587 SyncStatusCodeToString(status));
589 if (sync_root_deletion_) {
590 callback.Run(SYNC_STATUS_OK);
591 return;
594 if (status == SYNC_STATUS_RETRY) {
595 callback.Run(SYNC_STATUS_OK);
596 return;
599 if (status != SYNC_STATUS_OK) {
600 callback.Run(status);
601 return;
604 DCHECK(dirty_tracker_);
605 DCHECK(remote_metadata_);
606 DCHECK(remote_metadata_->has_details());
608 FileDetails updated_details = remote_metadata_->details();
609 if (!dirty_tracker_->active() ||
610 HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
611 // Operations for an inactive tracker don't update file content.
612 if (dirty_tracker_->has_synced_details())
613 updated_details.set_md5(dirty_tracker_->synced_details().md5());
614 if (!dirty_tracker_->active()) {
615 // Keep missing true, as the change hasn't been synced to local.
616 updated_details.clear_md5();
617 updated_details.set_missing(true);
620 metadata_database()->UpdateTracker(dirty_tracker_->tracker_id(),
621 updated_details,
622 callback);
625 void RemoteToLocalSyncer::FinalizeSync(const SyncStatusCallback& callback,
626 SyncStatusCode status) {
627 if (prepared_) {
628 remote_change_processor()->FinalizeRemoteSync(
629 url_, false /* clear_local_change */, base::Bind(callback, status));
630 return;
633 callback.Run(status);
636 void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) {
637 bool should_success = BuildFileSystemURL(
638 metadata_database(), *dirty_tracker_, &url_);
639 DCHECK(should_success);
640 DCHECK(url_.is_valid());
641 remote_change_processor()->PrepareForProcessRemoteChange(
642 url_,
643 base::Bind(&RemoteToLocalSyncer::DidPrepare,
644 weak_ptr_factory_.GetWeakPtr(),
645 callback));
648 void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback,
649 SyncStatusCode status,
650 const SyncFileMetadata& local_metadata,
651 const FileChangeList& local_changes) {
652 if (status != SYNC_STATUS_OK) {
653 callback.Run(status);
654 return;
656 prepared_ = true;
658 local_metadata_.reset(new SyncFileMetadata(local_metadata));
659 local_changes_.reset(new FileChangeList(local_changes));
661 callback.Run(status);
664 void RemoteToLocalSyncer::DeleteLocalFile(const SyncStatusCallback& callback) {
665 remote_change_processor()->ApplyRemoteChange(
666 FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN),
667 base::FilePath(),
668 url_,
669 callback);
672 void RemoteToLocalSyncer::DownloadFile(const SyncStatusCallback& callback) {
673 base::PostTaskAndReplyWithResult(
674 sync_context_->GetBlockingTaskRunner(), FROM_HERE,
675 base::Bind(&sync_file_system::drive_backend::CreateTemporaryFile,
676 make_scoped_refptr(sync_context_->GetBlockingTaskRunner())),
677 base::Bind(&RemoteToLocalSyncer::DidCreateTemporaryFileForDownload,
678 weak_ptr_factory_.GetWeakPtr(), callback));
681 void RemoteToLocalSyncer::DidCreateTemporaryFileForDownload(
682 const SyncStatusCallback& callback,
683 webkit_blob::ScopedFile file) {
684 base::FilePath path = file.path();
685 drive_service()->DownloadFile(
686 path, remote_metadata_->file_id(),
687 base::Bind(&RemoteToLocalSyncer::DidDownloadFile,
688 weak_ptr_factory_.GetWeakPtr(),
689 callback, base::Passed(&file)),
690 google_apis::GetContentCallback(),
691 google_apis::ProgressCallback());
694 void RemoteToLocalSyncer::DidDownloadFile(const SyncStatusCallback& callback,
695 webkit_blob::ScopedFile file,
696 google_apis::GDataErrorCode error,
697 const base::FilePath&) {
698 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
699 if (status != SYNC_STATUS_OK) {
700 callback.Run(status);
701 return;
704 base::FilePath path = file.path();
705 base::PostTaskAndReplyWithResult(
706 sync_context_->GetBlockingTaskRunner(), FROM_HERE,
707 base::Bind(&drive::util::GetMd5Digest, path),
708 base::Bind(&RemoteToLocalSyncer::DidCalculateMD5ForDownload,
709 weak_ptr_factory_.GetWeakPtr(),
710 callback, base::Passed(&file)));
713 void RemoteToLocalSyncer::DidCalculateMD5ForDownload(
714 const SyncStatusCallback& callback,
715 webkit_blob::ScopedFile file,
716 const std::string& md5) {
717 if (md5.empty()) {
718 callback.Run(SYNC_FILE_ERROR_NOT_FOUND);
719 return;
722 if (md5 != remote_metadata_->details().md5()) {
723 // File has been modified since last metadata retrieval.
725 // Lower the priority of the tracker to prevent repeated remote sync to the
726 // same tracker.
727 metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
728 callback.Run(SYNC_STATUS_RETRY);
729 return;
732 base::FilePath path = file.path();
733 remote_change_processor()->ApplyRemoteChange(
734 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE),
735 path, url_,
736 base::Bind(&RemoteToLocalSyncer::DidApplyDownload,
737 weak_ptr_factory_.GetWeakPtr(),
738 callback, base::Passed(&file)));
741 void RemoteToLocalSyncer::DidApplyDownload(const SyncStatusCallback& callback,
742 webkit_blob::ScopedFile,
743 SyncStatusCode status) {
744 if (status != SYNC_STATUS_OK)
745 metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
746 callback.Run(status);
749 void RemoteToLocalSyncer::CreateFolder(const SyncStatusCallback& callback) {
750 remote_change_processor()->ApplyRemoteChange(
751 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
752 SYNC_FILE_TYPE_DIRECTORY),
753 base::FilePath(), url_,
754 callback);
757 drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() {
758 return sync_context_->GetDriveService();
761 MetadataDatabase* RemoteToLocalSyncer::metadata_database() {
762 return sync_context_->GetMetadataDatabase();
765 RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() {
766 DCHECK(sync_context_->GetRemoteChangeProcessor());
767 return sync_context_->GetRemoteChangeProcessor();
770 } // namespace drive_backend
771 } // namespace sync_file_system