Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / remote_to_local_syncer.cc
blobb93728d7430a994d3a41f70e16185cb71818c7e2
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/files/file_util.h"
10 #include "base/format_macros.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/task_runner_util.h"
15 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.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/drive_backend/sync_task_manager.h"
20 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
21 #include "chrome/browser/sync_file_system/drive_backend/task_dependency_manager.h"
22 #include "chrome/browser/sync_file_system/logger.h"
23 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
24 #include "components/drive/drive_api_util.h"
25 #include "components/drive/service/drive_service_interface.h"
26 #include "extensions/common/extension.h"
27 #include "google_apis/drive/drive_api_parser.h"
28 #include "storage/common/fileapi/file_system_util.h"
30 namespace sync_file_system {
31 namespace drive_backend {
33 namespace {
35 bool BuildFileSystemURL(MetadataDatabase* metadata_database,
36 const FileTracker& tracker,
37 storage::FileSystemURL* url) {
38 base::FilePath path;
39 if (!metadata_database->BuildPathForTracker(
40 tracker.tracker_id(), &path))
41 return false;
43 GURL origin =
44 extensions::Extension::GetBaseURLFromExtensionId(tracker.app_id());
45 *url = sync_file_system::CreateSyncableFileSystemURL(origin, path);
47 return true;
50 bool HasFolderAsParent(const FileDetails& details,
51 const std::string& folder_id) {
52 for (int i = 0; i < details.parent_folder_ids_size(); ++i) {
53 if (details.parent_folder_ids(i) == folder_id)
54 return true;
56 return false;
59 bool HasDisabledAppRoot(MetadataDatabase* database,
60 const FileTracker& tracker) {
61 DCHECK(tracker.active());
62 FileTracker app_root_tracker;
63 if (database->FindAppRootTracker(tracker.app_id(), &app_root_tracker)) {
64 DCHECK(app_root_tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
65 app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT);
66 return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
68 return false;
71 scoped_ptr<FileMetadata> GetFileMetadata(MetadataDatabase* database,
72 const std::string& file_id) {
73 scoped_ptr<FileMetadata> metadata(new FileMetadata);
74 if (!database->FindFileByFileID(file_id, metadata.get()))
75 metadata.reset();
76 return metadata.Pass();
79 // Creates a temporary file in |dir_path|. This must be called on an
80 // IO-allowed task runner, and the runner must be given as |file_task_runner|.
81 storage::ScopedFile CreateTemporaryFile(
82 const scoped_refptr<base::TaskRunner>& file_task_runner) {
83 base::FilePath temp_file_path;
84 if (!base::CreateTemporaryFile(&temp_file_path))
85 return storage::ScopedFile();
87 return storage::ScopedFile(temp_file_path,
88 storage::ScopedFile::DELETE_ON_SCOPE_OUT,
89 file_task_runner);
92 } // namespace
94 RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext* sync_context)
95 : sync_context_(sync_context),
96 file_type_(SYNC_FILE_TYPE_UNKNOWN),
97 sync_action_(SYNC_ACTION_NONE),
98 prepared_(false),
99 sync_root_deletion_(false),
100 weak_ptr_factory_(this) {
103 RemoteToLocalSyncer::~RemoteToLocalSyncer() {
106 void RemoteToLocalSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
107 token->InitializeTaskLog("Remote -> Local");
109 if (!drive_service() || !metadata_database() || !remote_change_processor()) {
110 token->RecordLog("Context not ready.");
111 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
112 return;
115 dirty_tracker_ = make_scoped_ptr(new FileTracker);
116 if (metadata_database()->GetDirtyTracker(dirty_tracker_.get())) {
117 token->RecordLog(base::StringPrintf(
118 "Start: tracker_id=%" PRId64, dirty_tracker_->tracker_id()));
119 metadata_database()->DemoteTracker(dirty_tracker_->tracker_id());
120 ResolveRemoteChange(token.Pass());
121 return;
124 token->RecordLog("Nothing to do.");
125 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_NO_CHANGE_TO_SYNC);
128 void RemoteToLocalSyncer::ResolveRemoteChange(scoped_ptr<SyncTaskToken> token) {
129 DCHECK(dirty_tracker_);
130 remote_metadata_ = GetFileMetadata(
131 metadata_database(), dirty_tracker_->file_id());
133 if (!remote_metadata_ || !remote_metadata_->has_details()) {
134 if (remote_metadata_ && !remote_metadata_->has_details()) {
135 token->RecordLog(
136 "Missing details of a remote file: " + remote_metadata_->file_id());
137 NOTREACHED();
139 token->RecordLog("Missing remote metadata case.");
141 MoveToBackground(
142 token.Pass(),
143 base::Bind(&RemoteToLocalSyncer::HandleMissingRemoteMetadata,
144 weak_ptr_factory_.GetWeakPtr()));
145 return;
148 DCHECK(remote_metadata_);
149 DCHECK(remote_metadata_->has_details());
150 const FileDetails& remote_details = remote_metadata_->details();
152 if (!dirty_tracker_->active() ||
153 HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
154 // Handle inactive tracker in SyncCompleted.
155 token->RecordLog("Inactive tracker case.");
156 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
157 return;
160 DCHECK(dirty_tracker_->active());
161 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
163 if (!dirty_tracker_->has_synced_details()) {
164 token->RecordLog(base::StringPrintf(
165 "Missing synced_details of an active tracker: %" PRId64,
166 dirty_tracker_->tracker_id()));
167 NOTREACHED();
168 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
169 return;
172 DCHECK(dirty_tracker_->has_synced_details());
173 const FileDetails& synced_details = dirty_tracker_->synced_details();
175 if (dirty_tracker_->tracker_id() ==
176 metadata_database()->GetSyncRootTrackerID()) {
177 if (remote_details.missing() ||
178 synced_details.title() != remote_details.title() ||
179 remote_details.parent_folder_ids_size()) {
180 token->RecordLog("Sync-root deletion.");
181 sync_root_deletion_ = true;
182 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
183 return;
185 token->RecordLog("Trivial sync-root change.");
186 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
187 return;
190 DCHECK_NE(dirty_tracker_->tracker_id(),
191 metadata_database()->GetSyncRootTrackerID());
193 if (!BuildFileSystemURL(metadata_database(), *dirty_tracker_, &url_)) {
194 NOTREACHED();
195 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
196 return;
199 DCHECK(url_.is_valid());
201 if (remote_details.missing()) {
202 if (!synced_details.missing()) {
203 token->RecordLog("Remote file deletion.");
204 MoveToBackground(token.Pass(),
205 base::Bind(&RemoteToLocalSyncer::HandleDeletion,
206 weak_ptr_factory_.GetWeakPtr()));
207 return;
210 DCHECK(synced_details.missing());
211 token->RecordLog("Found a stray missing tracker: " +
212 dirty_tracker_->file_id());
213 NOTREACHED();
214 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
215 return;
218 // Most of remote_details field is valid from here.
219 DCHECK(!remote_details.missing());
221 if (synced_details.file_kind() != remote_details.file_kind()) {
222 token->RecordLog(base::StringPrintf(
223 "Found type mismatch between remote and local file: %s"
224 " type: (local) %d vs (remote) %d",
225 dirty_tracker_->file_id().c_str(),
226 synced_details.file_kind(),
227 remote_details.file_kind()));
228 NOTREACHED();
229 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
230 return;
232 DCHECK_EQ(synced_details.file_kind(), remote_details.file_kind());
234 if (synced_details.file_kind() == FILE_KIND_UNSUPPORTED) {
235 token->RecordLog("Found an unsupported active file: " +
236 remote_metadata_->file_id());
237 NOTREACHED();
238 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
239 return;
241 DCHECK(remote_details.file_kind() == FILE_KIND_FILE ||
242 remote_details.file_kind() == FILE_KIND_FOLDER);
244 if (synced_details.title() != remote_details.title()) {
245 // Handle rename as deletion + addition.
246 token->RecordLog("Detected file rename.");
248 MoveToBackground(token.Pass(),
249 base::Bind(&RemoteToLocalSyncer::HandleFileMove,
250 weak_ptr_factory_.GetWeakPtr()));
251 return;
253 DCHECK_EQ(synced_details.title(), remote_details.title());
255 FileTracker parent_tracker;
256 if (!metadata_database()->FindTrackerByTrackerID(
257 dirty_tracker_->parent_tracker_id(), &parent_tracker)) {
258 token->RecordLog("Missing parent tracker for a non sync-root tracker: "
259 + dirty_tracker_->file_id());
260 NOTREACHED();
261 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
262 return;
265 if (!HasFolderAsParent(remote_details, parent_tracker.file_id())) {
266 // Handle reorganize as deletion + addition.
267 token->RecordLog("Detected file reorganize.");
269 MoveToBackground(token.Pass(),
270 base::Bind(&RemoteToLocalSyncer::HandleFileMove,
271 weak_ptr_factory_.GetWeakPtr()));
272 return;
275 if (synced_details.file_kind() == FILE_KIND_FILE) {
276 if (synced_details.md5() != remote_details.md5()) {
277 token->RecordLog("Detected file content update.");
278 MoveToBackground(token.Pass(),
279 base::Bind(&RemoteToLocalSyncer::HandleContentUpdate,
280 weak_ptr_factory_.GetWeakPtr()));
281 return;
283 } else {
284 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
285 if (synced_details.missing()) {
286 token->RecordLog("Detected folder update.");
287 MoveToBackground(token.Pass(),
288 base::Bind(&RemoteToLocalSyncer::HandleFolderUpdate,
289 weak_ptr_factory_.GetWeakPtr()));
290 return;
292 if (dirty_tracker_->needs_folder_listing()) {
293 token->RecordLog("Needs listing folder.");
294 MoveToBackground(token.Pass(),
295 base::Bind(&RemoteToLocalSyncer::ListFolderContent,
296 weak_ptr_factory_.GetWeakPtr()));
297 return;
299 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
300 return;
303 token->RecordLog("Trivial file change.");
304 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
307 void RemoteToLocalSyncer::MoveToBackground(scoped_ptr<SyncTaskToken> token,
308 const Continuation& continuation) {
309 DCHECK(dirty_tracker_);
311 scoped_ptr<TaskBlocker> blocker(new TaskBlocker);
312 blocker->app_id = dirty_tracker_->app_id();
313 if (url_.is_valid())
314 blocker->paths.push_back(url_.path());
315 blocker->file_ids.push_back(dirty_tracker_->file_id());
316 blocker->tracker_ids.push_back(dirty_tracker_->tracker_id());
318 SyncTaskManager::UpdateTaskBlocker(
319 token.Pass(), blocker.Pass(),
320 base::Bind(&RemoteToLocalSyncer::ContinueAsBackgroundTask,
321 weak_ptr_factory_.GetWeakPtr(), continuation));
324 void RemoteToLocalSyncer::ContinueAsBackgroundTask(
325 const Continuation& continuation,
326 scoped_ptr<SyncTaskToken> token) {
327 DCHECK(dirty_tracker_);
329 // The SyncTask runs as a background task beyond this point.
330 // Not that any task can run between MoveToBackground() and
331 // ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
332 // affect to the current RemoteToLocalSyncer task.
334 // - For LocalToRemoteSyncer, it may update or delete any of FileTracker and
335 // FileMetadata. When it updates FileMetadata or FileDetails in FileTracker,
336 // it also updates |change_id|. So, ensure the target FileTracker and
337 // FileMetadata exist and their |change_id|s are not updated.
338 // - For ListChangesTask, it may update FileMetadata together with |change_id|
339 // and may delete FileTracker.
340 // - For UninstallAppTask, it may delete FileMetadata and FileTracker.
341 // Check if FileTracker still exists.
342 // - For other RemoteToLocalSyncer, it may delete the FileTracker of parent.
343 // Note that since RemoteToLocalSyncer demotes the target FileTracker first,
344 // any other RemoteToLocalSyncer does not run for current |dirty_tracker_|.
345 // - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
347 FileTracker latest_dirty_tracker;
348 if (!metadata_database()->FindTrackerByTrackerID(
349 dirty_tracker_->tracker_id(), &latest_dirty_tracker) ||
350 dirty_tracker_->active() != latest_dirty_tracker.active() ||
351 !latest_dirty_tracker.dirty()) {
352 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
353 return;
356 int64 current_change_id = kint64min;
357 int64 latest_change_id = kint64min;
358 if (dirty_tracker_->has_synced_details())
359 current_change_id = dirty_tracker_->synced_details().change_id();
360 if (latest_dirty_tracker.has_synced_details())
361 latest_change_id = latest_dirty_tracker.synced_details().change_id();
362 if (current_change_id != latest_change_id) {
363 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
364 return;
367 FileMetadata latest_file_metadata;
368 if (metadata_database()->FindFileByFileID(dirty_tracker_->file_id(),
369 &latest_file_metadata)) {
370 if (!remote_metadata_) {
371 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
372 return;
375 int64 change_id = remote_metadata_->details().change_id();
376 int64 latest_change_id = latest_file_metadata.details().change_id();
377 if (change_id != latest_change_id) {
378 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
379 return;
381 } else {
382 if (remote_metadata_) {
383 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
384 return;
387 continuation.Run(token.Pass());
390 void RemoteToLocalSyncer::HandleMissingRemoteMetadata(
391 scoped_ptr<SyncTaskToken> token) {
392 DCHECK(dirty_tracker_);
394 drive_service()->GetFileResource(
395 dirty_tracker_->file_id(),
396 base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata,
397 weak_ptr_factory_.GetWeakPtr(),
398 base::Passed(&token)));
401 void RemoteToLocalSyncer::DidGetRemoteMetadata(
402 scoped_ptr<SyncTaskToken> token,
403 google_apis::DriveApiErrorCode error,
404 scoped_ptr<google_apis::FileResource> entry) {
405 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
407 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
408 if (status != SYNC_STATUS_OK &&
409 error != google_apis::HTTP_NOT_FOUND) {
410 SyncCompleted(token.Pass(), status);
411 return;
414 if (error == google_apis::HTTP_NOT_FOUND) {
415 status = metadata_database()->UpdateByDeletedRemoteFile(
416 dirty_tracker_->file_id());
417 SyncCompleted(token.Pass(), status);
418 return;
421 if (!entry) {
422 NOTREACHED();
423 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
424 return;
427 status = metadata_database()->UpdateByFileResource(*entry);
428 if (status != SYNC_STATUS_OK) {
429 SyncCompleted(token.Pass(), status);
430 return;
433 metadata_database()->PromoteDemotedTracker(dirty_tracker_->tracker_id());
435 // Do not update |dirty_tracker_|.
436 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
439 void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
440 scoped_ptr<SyncTaskToken> token,
441 SyncStatusCode status) {
442 if (status != SYNC_STATUS_OK) {
443 SyncCompleted(token.Pass(), status);
444 return;
447 DCHECK(url_.is_valid());
448 DCHECK(local_metadata_);
449 DCHECK(local_changes_);
451 // Check if the local file exists.
452 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
453 (!local_changes_->empty() && local_changes_->back().IsDelete())) {
454 file_type_ = SYNC_FILE_TYPE_FILE;
455 sync_action_ = SYNC_ACTION_ADDED;
456 // Missing local file case.
457 // Download the file and add it to local as a new file.
458 DownloadFile(token.Pass());
459 return;
462 DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
463 if (local_changes_->empty()) {
464 if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) {
465 file_type_ = SYNC_FILE_TYPE_FILE;
466 sync_action_ = SYNC_ACTION_UPDATED;
467 // Download the file and overwrite the existing local file.
468 DownloadFile(token.Pass());
469 return;
472 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type);
474 // Got a remote regular file modification for existing local folder.
475 // Our policy prioritize folders in this case.
476 // Let local-to-remote sync phase process this change.
477 remote_change_processor()->RecordFakeLocalChange(
478 url_,
479 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
480 local_metadata_->file_type),
481 SyncCompletedCallback(token.Pass()));
482 return;
485 DCHECK(local_changes_->back().IsAddOrUpdate());
486 // Conflict case.
487 // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
488 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
491 void RemoteToLocalSyncer::HandleFolderUpdate(
492 scoped_ptr<SyncTaskToken> token) {
493 DCHECK(dirty_tracker_);
494 DCHECK(dirty_tracker_->active());
495 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
497 DCHECK(remote_metadata_);
498 DCHECK(remote_metadata_->has_details());
499 DCHECK(!remote_metadata_->details().missing());
500 DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind());
502 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate,
503 weak_ptr_factory_.GetWeakPtr(),
504 base::Passed(&token)));
507 void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
508 scoped_ptr<SyncTaskToken> token,
509 SyncStatusCode status) {
510 if (status != SYNC_STATUS_OK) {
511 SyncCompleted(token.Pass(), status);
512 return;
515 DCHECK(url_.is_valid());
516 DCHECK(local_metadata_);
517 DCHECK(local_changes_);
519 // Check if the local file exists.
520 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
521 (!local_changes_->empty() && local_changes_->back().IsDelete())) {
522 file_type_ = SYNC_FILE_TYPE_DIRECTORY;
523 sync_action_ = SYNC_ACTION_ADDED;
524 // No local file exists at the path.
525 CreateFolder(token.Pass());
526 return;
529 if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) {
530 // There already exists a folder, nothing left to do.
531 if (dirty_tracker_->needs_folder_listing() &&
532 !dirty_tracker_->synced_details().missing()) {
533 ListFolderContent(token.Pass());
534 } else {
535 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
537 return;
540 DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type);
541 file_type_ = SYNC_FILE_TYPE_DIRECTORY;
542 sync_action_ = SYNC_ACTION_ADDED;
543 // Got a remote folder for existing local file.
544 // Our policy prioritize folders in this case.
545 CreateFolder(token.Pass());
548 void RemoteToLocalSyncer::HandleDeletion(
549 scoped_ptr<SyncTaskToken> token) {
550 DCHECK(dirty_tracker_);
551 DCHECK(dirty_tracker_->active());
552 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
553 DCHECK(dirty_tracker_->has_synced_details());
554 DCHECK(!dirty_tracker_->synced_details().missing());
556 DCHECK(remote_metadata_);
557 DCHECK(remote_metadata_->has_details());
558 DCHECK(remote_metadata_->details().missing());
560 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
561 weak_ptr_factory_.GetWeakPtr(),
562 base::Passed(&token)));
565 void RemoteToLocalSyncer::HandleFileMove(scoped_ptr<SyncTaskToken> token) {
566 DCHECK(dirty_tracker_);
567 DCHECK(dirty_tracker_->active());
568 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
569 DCHECK(dirty_tracker_->has_synced_details());
571 DCHECK(remote_metadata_);
572 DCHECK(remote_metadata_->has_details());
573 DCHECK(!remote_metadata_->details().missing());
575 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
576 weak_ptr_factory_.GetWeakPtr(),
577 base::Passed(&token)));
580 void RemoteToLocalSyncer::DidPrepareForDeletion(
581 scoped_ptr<SyncTaskToken> token,
582 SyncStatusCode status) {
583 if (status != SYNC_STATUS_OK) {
584 SyncCompleted(token.Pass(), status);
585 return;
588 DCHECK(url_.is_valid());
589 DCHECK(local_metadata_);
590 DCHECK(local_changes_);
592 // Check if the local file exists.
593 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
594 (!local_changes_->empty() && local_changes_->back().IsDelete())) {
595 // No local file exists at the path.
596 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
597 return;
600 DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
601 if (local_changes_->empty()) {
602 file_type_ = local_metadata_->file_type;
603 sync_action_ = SYNC_ACTION_DELETED;
604 DeleteLocalFile(token.Pass());
605 return;
608 DCHECK(local_changes_->back().IsAddOrUpdate());
609 // File is remotely deleted and locally updated.
610 // Ignore the remote deletion and handle it as if applied successfully.
611 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
614 void RemoteToLocalSyncer::HandleContentUpdate(
615 scoped_ptr<SyncTaskToken> token) {
616 DCHECK(dirty_tracker_);
617 DCHECK(dirty_tracker_->active());
618 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
619 DCHECK(dirty_tracker_->has_synced_details());
620 DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind());
622 DCHECK(remote_metadata_);
623 DCHECK(remote_metadata_->has_details());
624 DCHECK(!remote_metadata_->details().missing());
626 DCHECK_NE(dirty_tracker_->synced_details().md5(),
627 remote_metadata_->details().md5());
629 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile,
630 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
633 void RemoteToLocalSyncer::ListFolderContent(
634 scoped_ptr<SyncTaskToken> token) {
635 DCHECK(dirty_tracker_);
636 DCHECK(dirty_tracker_->active());
637 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
638 DCHECK(dirty_tracker_->has_synced_details());
639 DCHECK(!dirty_tracker_->synced_details().missing());
640 DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind());
641 DCHECK(dirty_tracker_->needs_folder_listing());
643 DCHECK(remote_metadata_);
644 DCHECK(remote_metadata_->has_details());
645 DCHECK(!remote_metadata_->details().missing());
647 // TODO(tzik): Replace this call with ChildList version.
648 drive_service()->GetFileListInDirectory(
649 dirty_tracker_->file_id(),
650 base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
651 weak_ptr_factory_.GetWeakPtr(),
652 base::Passed(&token),
653 base::Passed(make_scoped_ptr(new FileIDList))));
656 void RemoteToLocalSyncer::DidListFolderContent(
657 scoped_ptr<SyncTaskToken> token,
658 scoped_ptr<FileIDList> children,
659 google_apis::DriveApiErrorCode error,
660 scoped_ptr<google_apis::FileList> file_list) {
661 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
662 if (status != SYNC_STATUS_OK) {
663 SyncCompleted(token.Pass(), status);
664 return;
667 if (!file_list) {
668 NOTREACHED();
669 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
670 return;
673 children->reserve(children->size() + file_list->items().size());
674 for (ScopedVector<google_apis::FileResource>::const_iterator itr =
675 file_list->items().begin();
676 itr != file_list->items().end();
677 ++itr) {
678 children->push_back((*itr)->file_id());
681 if (!file_list->next_link().is_empty()) {
682 drive_service()->GetRemainingFileList(
683 file_list->next_link(),
684 base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
685 weak_ptr_factory_.GetWeakPtr(),
686 base::Passed(&token), base::Passed(&children)));
687 return;
690 status = metadata_database()->PopulateFolderByChildList(
691 dirty_tracker_->file_id(), *children);
692 SyncCompleted(token.Pass(), status);
695 void RemoteToLocalSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
696 SyncStatusCode status) {
697 token->RecordLog(base::StringPrintf(
698 "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64 " status=%s",
699 SyncActionToString(sync_action_), dirty_tracker_->tracker_id(),
700 SyncStatusCodeToString(status)));
702 if (sync_root_deletion_) {
703 FinalizeSync(token.Pass(), SYNC_STATUS_OK);
704 return;
707 if (status == SYNC_STATUS_RETRY) {
708 FinalizeSync(token.Pass(), SYNC_STATUS_OK);
709 return;
712 if (status != SYNC_STATUS_OK) {
713 FinalizeSync(token.Pass(), status);
714 return;
717 DCHECK(dirty_tracker_);
718 DCHECK(remote_metadata_);
719 DCHECK(remote_metadata_->has_details());
721 FileDetails updated_details = remote_metadata_->details();
722 if (!dirty_tracker_->active() ||
723 HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
724 // Operations for an inactive tracker don't update file content.
725 if (dirty_tracker_->has_synced_details())
726 updated_details.set_md5(dirty_tracker_->synced_details().md5());
727 if (!dirty_tracker_->active()) {
728 // Keep missing true, as the change hasn't been synced to local.
729 updated_details.clear_md5();
730 updated_details.set_missing(true);
734 status = metadata_database()->UpdateTracker(
735 dirty_tracker_->tracker_id(), updated_details);
736 FinalizeSync(token.Pass(), status);
739 void RemoteToLocalSyncer::FinalizeSync(scoped_ptr<SyncTaskToken> token,
740 SyncStatusCode status) {
741 if (prepared_) {
742 remote_change_processor()->FinalizeRemoteSync(
743 url_, false /* clear_local_change */,
744 base::Bind(SyncTaskManager::NotifyTaskDone,
745 base::Passed(&token), status));
746 return;
749 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
752 void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) {
753 DCHECK(url_.is_valid());
754 remote_change_processor()->PrepareForProcessRemoteChange(
755 url_,
756 base::Bind(&RemoteToLocalSyncer::DidPrepare,
757 weak_ptr_factory_.GetWeakPtr(),
758 callback));
761 void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback,
762 SyncStatusCode status,
763 const SyncFileMetadata& local_metadata,
764 const FileChangeList& local_changes) {
765 if (status != SYNC_STATUS_OK) {
766 callback.Run(status);
767 return;
769 prepared_ = true;
771 local_metadata_.reset(new SyncFileMetadata(local_metadata));
772 local_changes_.reset(new FileChangeList(local_changes));
774 callback.Run(status);
777 void RemoteToLocalSyncer::DeleteLocalFile(scoped_ptr<SyncTaskToken> token) {
778 remote_change_processor()->ApplyRemoteChange(
779 FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN),
780 base::FilePath(),
781 url_,
782 SyncCompletedCallback(token.Pass()));
785 void RemoteToLocalSyncer::DownloadFile(scoped_ptr<SyncTaskToken> token) {
786 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
788 storage::ScopedFile file = CreateTemporaryFile(
789 make_scoped_refptr(sync_context_->GetWorkerTaskRunner()));
791 base::FilePath path = file.path();
792 drive_service()->DownloadFile(
793 path, remote_metadata_->file_id(),
794 base::Bind(&RemoteToLocalSyncer::DidDownloadFile,
795 weak_ptr_factory_.GetWeakPtr(),
796 base::Passed(&token), base::Passed(&file)),
797 google_apis::GetContentCallback(),
798 google_apis::ProgressCallback());
801 void RemoteToLocalSyncer::DidDownloadFile(scoped_ptr<SyncTaskToken> token,
802 storage::ScopedFile file,
803 google_apis::DriveApiErrorCode error,
804 const base::FilePath&) {
805 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
807 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
808 if (status != SYNC_STATUS_OK) {
809 SyncCompleted(token.Pass(), status);
810 return;
813 base::FilePath path = file.path();
814 const std::string md5 = drive::util::GetMd5Digest(path, nullptr);
815 if (md5.empty()) {
816 SyncCompleted(token.Pass(), SYNC_FILE_ERROR_NOT_FOUND);
817 return;
820 if (md5 != remote_metadata_->details().md5()) {
821 // File has been modified since last metadata retrieval.
822 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
823 return;
826 remote_change_processor()->ApplyRemoteChange(
827 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE),
828 path, url_,
829 base::Bind(&RemoteToLocalSyncer::DidApplyDownload,
830 weak_ptr_factory_.GetWeakPtr(),
831 base::Passed(&token), base::Passed(&file)));
834 void RemoteToLocalSyncer::DidApplyDownload(scoped_ptr<SyncTaskToken> token,
835 storage::ScopedFile,
836 SyncStatusCode status) {
837 SyncCompleted(token.Pass(), status);
840 void RemoteToLocalSyncer::CreateFolder(scoped_ptr<SyncTaskToken> token) {
841 remote_change_processor()->ApplyRemoteChange(
842 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
843 SYNC_FILE_TYPE_DIRECTORY),
844 base::FilePath(), url_,
845 SyncCompletedCallback(token.Pass()));
848 drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() {
849 return sync_context_->GetDriveService();
852 MetadataDatabase* RemoteToLocalSyncer::metadata_database() {
853 return sync_context_->GetMetadataDatabase();
856 RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() {
857 DCHECK(sync_context_->GetRemoteChangeProcessor());
858 return sync_context_->GetRemoteChangeProcessor();
861 SyncStatusCallback RemoteToLocalSyncer::SyncCompletedCallback(
862 scoped_ptr<SyncTaskToken> token) {
863 return base::Bind(&RemoteToLocalSyncer::SyncCompleted,
864 weak_ptr_factory_.GetWeakPtr(),
865 base::Passed(&token));
868 } // namespace drive_backend
869 } // namespace sync_file_system