BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / local_to_remote_syncer.cc
blob7280f5e7986df296365e71f4af98b37b72078e55
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/local_to_remote_syncer.h"
7 #include <string>
8 #include <vector>
10 #include "base/callback.h"
11 #include "base/format_macros.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/task_runner_util.h"
17 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
18 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
19 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
20 #include "chrome/browser/sync_file_system/drive_backend/folder_creator.h"
21 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
22 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
23 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
24 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
25 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
26 #include "chrome/browser/sync_file_system/logger.h"
27 #include "components/drive/drive_api_util.h"
28 #include "components/drive/drive_uploader.h"
29 #include "components/drive/service/drive_service_interface.h"
30 #include "google_apis/drive/drive_api_parser.h"
31 #include "net/base/mime_util.h"
32 #include "storage/common/fileapi/file_system_util.h"
34 namespace sync_file_system {
35 namespace drive_backend {
37 namespace {
39 scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
40 int64 tracker_id) {
41 scoped_ptr<FileTracker> tracker(new FileTracker);
42 if (metadata_database->FindTrackerByTrackerID(tracker_id, tracker.get()))
43 return tracker.Pass();
44 return scoped_ptr<FileTracker>();
47 bool GetKnownChangeID(MetadataDatabase* metadata_database,
48 const std::string& file_id,
49 int64* change_id) {
50 FileMetadata remote_file_metadata;
51 if (!metadata_database->FindFileByFileID(file_id, &remote_file_metadata))
52 return false;
53 *change_id = remote_file_metadata.details().change_id();
54 return true;
57 bool IsLocalFileMissing(const SyncFileMetadata& local_metadata,
58 const FileChange& local_change) {
59 return local_metadata.file_type == SYNC_FILE_TYPE_UNKNOWN ||
60 local_change.IsDelete();
63 std::string GetMimeTypeFromTitle(const base::FilePath& title) {
64 base::FilePath::StringType extension = title.Extension();
65 std::string mime_type;
66 if (extension.empty() ||
67 !net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type))
68 return kMimeTypeOctetStream;
69 return mime_type;
72 } // namespace
74 LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
75 const SyncFileMetadata& local_metadata,
76 const FileChange& local_change,
77 const base::FilePath& local_path,
78 const storage::FileSystemURL& url)
79 : sync_context_(sync_context),
80 local_change_(local_change),
81 local_is_missing_(IsLocalFileMissing(local_metadata, local_change)),
82 local_path_(local_path),
83 url_(url),
84 file_type_(SYNC_FILE_TYPE_UNKNOWN),
85 sync_action_(SYNC_ACTION_NONE),
86 remote_file_change_id_(0),
87 retry_on_success_(false),
88 needs_remote_change_listing_(false),
89 weak_ptr_factory_(this) {
90 DCHECK(local_is_missing_ ||
91 local_change.file_type() == local_metadata.file_type)
92 << local_change.DebugString() << " metadata:" << local_metadata.file_type;
95 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
98 void LocalToRemoteSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
99 token->InitializeTaskLog("Local -> Remote");
101 if (!IsContextReady()) {
102 token->RecordLog("Context not ready.");
103 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
104 return;
107 token->RecordLog(base::StringPrintf(
108 "Start: %s on %s@%s %s",
109 local_change_.DebugString().c_str(),
110 url_.path().AsUTF8Unsafe().c_str(),
111 url_.origin().host().c_str(),
112 local_is_missing_ ? "(missing)" : ""));
114 if (local_is_missing_ && !local_change_.IsDelete()) {
115 // Stray file, we can just return.
116 token->RecordLog("Missing file for non-delete change.");
117 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
118 return;
121 std::string app_id = url_.origin().host();
122 base::FilePath path = url_.path();
124 scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
125 base::FilePath active_ancestor_path;
126 if (!metadata_database()->FindNearestActiveAncestor(
127 app_id, path,
128 active_ancestor_tracker.get(), &active_ancestor_path)) {
129 // The app is disabled or not registered.
130 token->RecordLog("App is disabled or not registered");
131 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_UNKNOWN_ORIGIN);
132 return;
134 DCHECK(active_ancestor_tracker->active());
135 DCHECK(active_ancestor_tracker->has_synced_details());
136 const FileDetails& active_ancestor_details =
137 active_ancestor_tracker->synced_details();
139 // TODO(tzik): Consider handling
140 // active_ancestor_tracker->synced_details().missing() case.
142 DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
143 active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
145 base::FilePath missing_entries;
146 if (active_ancestor_path.empty()) {
147 missing_entries = path;
148 } else if (active_ancestor_path != path) {
149 if (!active_ancestor_path.AppendRelativePath(path, &missing_entries)) {
150 NOTREACHED();
151 token->RecordLog(base::StringPrintf(
152 "Detected invalid ancestor: %s",
153 active_ancestor_path.value().c_str()));
154 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
155 return;
159 std::vector<base::FilePath::StringType> missing_components;
160 storage::VirtualPath::GetComponents(missing_entries, &missing_components);
162 if (!missing_components.empty()) {
163 if (local_is_missing_) {
164 token->RecordLog("Both local and remote are marked missing");
165 // !IsDelete() but SYNC_FILE_TYPE_UNKNOWN could happen when a file is
166 // deleted by recursive deletion (which is not recorded by tracker)
167 // but there're remaining changes for the same file in the tracker.
169 // Local file is deleted and remote file is missing, already deleted or
170 // not yet synced. There is nothing to do for the file.
171 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
172 return;
176 if (missing_components.size() > 1) {
177 // The original target doesn't have remote file and parent.
178 // Try creating the parent first.
179 if (active_ancestor_details.file_kind() == FILE_KIND_FOLDER) {
180 remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
181 target_path_ = active_ancestor_path.Append(missing_components[0]);
182 token->RecordLog("Detected missing parent folder.");
184 retry_on_success_ = true;
185 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
186 weak_ptr_factory_.GetWeakPtr()),
187 token.Pass());
188 return;
191 DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE);
192 remote_parent_folder_tracker_ =
193 FindTrackerByID(metadata_database(),
194 active_ancestor_tracker->parent_tracker_id());
195 remote_file_tracker_ = active_ancestor_tracker.Pass();
196 target_path_ = active_ancestor_path;
197 token->RecordLog("Detected non-folder file in its path.");
199 retry_on_success_ = true;
200 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
201 weak_ptr_factory_.GetWeakPtr()),
202 token.Pass());
203 return;
206 if (missing_components.empty()) {
207 // The original target has remote active file/folder.
208 remote_parent_folder_tracker_ =
209 FindTrackerByID(metadata_database(),
210 active_ancestor_tracker->parent_tracker_id());
211 remote_file_tracker_ = active_ancestor_tracker.Pass();
212 target_path_ = url_.path();
213 DCHECK(target_path_ == active_ancestor_path);
215 if (remote_file_tracker_->dirty()) {
216 token->RecordLog(base::StringPrintf(
217 "Detected conflicting dirty tracker:%" PRId64,
218 remote_file_tracker_->tracker_id()));
219 // Both local and remote file has pending modification.
220 HandleConflict(token.Pass());
221 return;
224 // Non-conflicting file/folder update case.
225 HandleExistingRemoteFile(token.Pass());
226 return;
229 DCHECK(local_change_.IsAddOrUpdate());
230 DCHECK_EQ(1u, missing_components.size());
231 // The original target has remote parent folder and doesn't have remote active
232 // file.
233 // Upload the file as a new file or create a folder.
234 remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
235 target_path_ = url_.path();
236 DCHECK(target_path_ == active_ancestor_path.Append(missing_components[0]));
237 if (local_change_.file_type() == SYNC_FILE_TYPE_FILE) {
238 token->RecordLog("Detected a new file.");
239 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
240 weak_ptr_factory_.GetWeakPtr()),
241 token.Pass());
242 return;
245 token->RecordLog("Detected a new folder.");
246 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
247 weak_ptr_factory_.GetWeakPtr()),
248 token.Pass());
251 void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation,
252 scoped_ptr<SyncTaskToken> token) {
253 scoped_ptr<TaskBlocker> blocker(new TaskBlocker);
254 blocker->app_id = url_.origin().host();
255 blocker->paths.push_back(target_path_);
257 if (remote_file_tracker_) {
258 if (!GetKnownChangeID(metadata_database(),
259 remote_file_tracker_->file_id(),
260 &remote_file_change_id_)) {
261 NOTREACHED();
262 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
263 return;
266 blocker->tracker_ids.push_back(remote_file_tracker_->tracker_id());
267 blocker->file_ids.push_back(remote_file_tracker_->file_id());
270 // Run current task as a background task with |blocker|.
271 // After the invocation of ContinueAsBackgroundTask
272 SyncTaskManager::UpdateTaskBlocker(
273 token.Pass(), blocker.Pass(),
274 base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask,
275 weak_ptr_factory_.GetWeakPtr(),
276 continuation));
279 void LocalToRemoteSyncer::ContinueAsBackgroundTask(
280 const Continuation& continuation,
281 scoped_ptr<SyncTaskToken> token) {
282 // The SyncTask runs as a background task beyond this point.
283 // Note that any task can run between MoveToBackground() and
284 // ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
285 // affect to the current LocalToRemoteSyncer task.
287 // - For RemoteToLocalSyncer, it doesn't actually run beyond
288 // PrepareForProcessRemoteChange() since it should be blocked in
289 // LocalFileSyncService.
290 // - For ListChangesTask, it may update FileMetatada together with |change_id|
291 // and may delete FileTracker. So, ensure |change_id| is not changed and
292 // check if FileTracker still exists.
293 // - For UninstallAppTask, it may also delete FileMetadata and FileTracker.
294 // Check if FileTracker still exists.
295 // - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
296 // LocalToRemoteSyncer.
297 if (remote_file_tracker_) {
298 int64 latest_change_id = 0;
299 if (!GetKnownChangeID(metadata_database(),
300 remote_file_tracker_->file_id(),
301 &latest_change_id) ||
302 latest_change_id > remote_file_change_id_) {
303 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
304 return;
307 if (!metadata_database()->FindTrackerByTrackerID(
308 remote_file_tracker_->tracker_id(), nullptr)) {
309 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
310 return;
314 continuation.Run(token.Pass());
317 void LocalToRemoteSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
318 SyncStatusCode status) {
319 if (status == SYNC_STATUS_OK && retry_on_success_)
320 status = SYNC_STATUS_RETRY;
322 if (needs_remote_change_listing_)
323 status = SYNC_STATUS_FILE_BUSY;
325 token->RecordLog(base::StringPrintf(
326 "Finished: action=%s, status=%s for %s@%s",
327 SyncActionToString(sync_action_),
328 SyncStatusCodeToString(status),
329 target_path_.AsUTF8Unsafe().c_str(),
330 url_.origin().host().c_str()));
332 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
335 void LocalToRemoteSyncer::HandleConflict(scoped_ptr<SyncTaskToken> token) {
336 DCHECK(remote_file_tracker_);
337 DCHECK(remote_file_tracker_->has_synced_details());
338 DCHECK(remote_file_tracker_->active());
339 DCHECK(remote_file_tracker_->dirty());
341 if (local_is_missing_) {
342 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
343 return;
346 if (local_change_.IsFile()) {
347 // Upload the conflicting file as a new file and let ConflictResolver
348 // resolve it.
349 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
350 weak_ptr_factory_.GetWeakPtr()),
351 token.Pass());
352 return;
355 DCHECK(local_change_.IsDirectory());
356 // Check if we can reuse the remote folder.
357 FileMetadata remote_file_metadata;
358 if (!metadata_database()->FindFileByFileID(
359 remote_file_tracker_->file_id(), &remote_file_metadata)) {
360 NOTREACHED();
361 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
362 weak_ptr_factory_.GetWeakPtr()),
363 token.Pass());
364 return;
367 const FileDetails& remote_details = remote_file_metadata.details();
368 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
369 if (!remote_details.missing() &&
370 remote_details.file_kind() == FILE_KIND_FOLDER &&
371 remote_details.title() == title.AsUTF8Unsafe() &&
372 HasFileAsParent(remote_details,
373 remote_parent_folder_tracker_->file_id())) {
374 MoveToBackground(
375 base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
376 weak_ptr_factory_.GetWeakPtr(),
377 remote_details),
378 token.Pass());
379 return;
382 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
383 weak_ptr_factory_.GetWeakPtr()),
384 token.Pass());
387 void LocalToRemoteSyncer::UpdateTrackerForReusedFolder(
388 const FileDetails& details,
389 scoped_ptr<SyncTaskToken> token) {
390 SyncStatusCode status = metadata_database()->UpdateTracker(
391 remote_file_tracker_->tracker_id(), details);
392 SyncCompleted(token.Pass(), status);
395 void LocalToRemoteSyncer::HandleExistingRemoteFile(
396 scoped_ptr<SyncTaskToken> token) {
397 DCHECK(remote_file_tracker_);
398 DCHECK(!remote_file_tracker_->dirty());
399 DCHECK(remote_file_tracker_->active());
400 DCHECK(remote_file_tracker_->has_synced_details());
402 if (local_is_missing_) {
403 // Local file deletion for existing remote file.
404 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
405 weak_ptr_factory_.GetWeakPtr()),
406 token.Pass());
407 return;
410 DCHECK(local_change_.IsAddOrUpdate());
411 DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
413 const FileDetails& synced_details = remote_file_tracker_->synced_details();
414 DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
415 synced_details.file_kind() == FILE_KIND_FOLDER);
416 if (local_change_.IsFile()) {
417 if (synced_details.file_kind() == FILE_KIND_FILE) {
418 // Non-conflicting local file update to existing remote regular file.
419 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile,
420 weak_ptr_factory_.GetWeakPtr()),
421 token.Pass());
422 return;
425 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
426 // Non-conflicting local file update to existing remote *folder*.
427 // Assuming this case as local folder deletion + local file creation, delete
428 // the remote folder and upload the file.
429 retry_on_success_ = true;
430 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
431 weak_ptr_factory_.GetWeakPtr()),
432 token.Pass());
433 return;
436 DCHECK(local_change_.IsDirectory());
437 if (synced_details.file_kind() == FILE_KIND_FILE) {
438 // Non-conflicting local folder creation to existing remote *file*.
439 // Assuming this case as local file deletion + local folder creation, delete
440 // the remote file and create a remote folder.
441 retry_on_success_ = true;
442 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
443 weak_ptr_factory_.GetWeakPtr()),
444 token.Pass());
445 return;
448 // Non-conflicting local folder creation to existing remote folder.
449 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
450 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
453 void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
454 DCHECK(remote_file_tracker_);
455 DCHECK(remote_file_tracker_->has_synced_details());
457 switch (remote_file_tracker_->synced_details().file_kind()) {
458 case FILE_KIND_UNSUPPORTED:
459 NOTREACHED();
460 file_type_ = SYNC_FILE_TYPE_UNKNOWN;
461 break;
462 case FILE_KIND_FILE:
463 file_type_ = SYNC_FILE_TYPE_FILE;
464 break;
465 case FILE_KIND_FOLDER:
466 file_type_ = SYNC_FILE_TYPE_DIRECTORY;
467 break;
469 sync_action_ = SYNC_ACTION_DELETED;
470 drive_service()->DeleteResource(
471 remote_file_tracker_->file_id(),
472 remote_file_tracker_->synced_details().etag(),
473 base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
474 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
477 void LocalToRemoteSyncer::DidDeleteRemoteFile(
478 scoped_ptr<SyncTaskToken> token,
479 google_apis::DriveApiErrorCode error) {
480 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
481 if (status != SYNC_STATUS_OK &&
482 error != google_apis::HTTP_NOT_FOUND &&
483 error != google_apis::HTTP_PRECONDITION &&
484 error != google_apis::HTTP_CONFLICT) {
485 SyncCompleted(token.Pass(), status);
486 return;
489 // Handle NOT_FOUND case as SUCCESS case.
490 // For PRECONDITION / CONFLICT case, the remote file is modified since the
491 // last sync completed. As our policy for deletion-modification conflict
492 // resolution, ignore the local deletion.
493 if (status == SYNC_STATUS_OK ||
494 error == google_apis::HTTP_NOT_FOUND) {
495 SyncStatusCode status = metadata_database()->UpdateByDeletedRemoteFile(
496 remote_file_tracker_->file_id());
497 SyncCompleted(token.Pass(), status);
498 return;
501 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
504 void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) {
505 DCHECK(remote_file_tracker_);
506 DCHECK(remote_file_tracker_->has_synced_details());
507 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
509 const std::string local_file_md5 = drive::util::GetMd5Digest(local_path_,
510 nullptr);
511 if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
512 // Local file is not changed.
513 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
514 return;
517 file_type_ = SYNC_FILE_TYPE_FILE;
518 sync_action_ = SYNC_ACTION_UPDATED;
520 drive::UploadExistingFileOptions options;
521 options.etag = remote_file_tracker_->synced_details().etag();
522 drive_uploader()->UploadExistingFile(
523 remote_file_tracker_->file_id(),
524 local_path_,
525 "application/octet_stream",
526 options,
527 base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
528 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
529 google_apis::ProgressCallback());
532 void LocalToRemoteSyncer::DidUploadExistingFile(
533 scoped_ptr<SyncTaskToken> token,
534 google_apis::DriveApiErrorCode error,
535 const GURL&,
536 scoped_ptr<google_apis::FileResource> entry) {
537 if (error == google_apis::HTTP_PRECONDITION ||
538 error == google_apis::HTTP_CONFLICT ||
539 error == google_apis::HTTP_NOT_FOUND) {
540 // The remote file has unfetched remote change. Fetch latest metadata and
541 // update database with it.
542 // TODO(tzik): Consider adding local side low-priority dirtiness handling to
543 // handle this as ListChangesTask.
545 needs_remote_change_listing_ = true;
546 UpdateRemoteMetadata(
547 remote_file_tracker_->file_id(),
548 token.Pass());
549 return;
552 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
553 if (status != SYNC_STATUS_OK) {
554 SyncCompleted(token.Pass(), status);
555 return;
558 if (!entry) {
559 NOTREACHED();
560 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
561 return;
564 DCHECK(entry);
565 status = metadata_database()->UpdateByFileResource(*entry);
566 if (status != SYNC_STATUS_OK) {
567 SyncCompleted(token.Pass(), status);
568 return;
571 FileMetadata file;
572 if (!metadata_database()->FindFileByFileID(
573 remote_file_tracker_->file_id(), &file)) {
574 NOTREACHED();
575 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
576 return;
579 const FileDetails& details = file.details();
580 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
581 if (!details.missing() &&
582 details.file_kind() == FILE_KIND_FILE &&
583 details.title() == title.AsUTF8Unsafe() &&
584 HasFileAsParent(details,
585 remote_parent_folder_tracker_->file_id())) {
586 SyncStatusCode status = metadata_database()->UpdateTracker(
587 remote_file_tracker_->tracker_id(), file.details());
588 SyncCompleted(token.Pass(), status);
589 return;
592 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
595 void LocalToRemoteSyncer::UpdateRemoteMetadata(
596 const std::string& file_id,
597 scoped_ptr<SyncTaskToken> token) {
598 DCHECK(remote_file_tracker_);
600 drive_service()->GetFileResource(
601 file_id,
602 base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
603 weak_ptr_factory_.GetWeakPtr(),
604 file_id, base::Passed(&token)));
607 void LocalToRemoteSyncer::DidGetRemoteMetadata(
608 const std::string& file_id,
609 scoped_ptr<SyncTaskToken> token,
610 google_apis::DriveApiErrorCode error,
611 scoped_ptr<google_apis::FileResource> entry) {
612 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
614 if (error == google_apis::HTTP_NOT_FOUND) {
615 retry_on_success_ = true;
616 SyncStatusCode status =
617 metadata_database()->UpdateByDeletedRemoteFile(file_id);
618 SyncCompleted(token.Pass(), status);
619 return;
622 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
623 if (status != SYNC_STATUS_OK) {
624 SyncCompleted(token.Pass(), status);
625 return;
628 if (!entry) {
629 NOTREACHED();
630 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
631 return;
634 retry_on_success_ = true;
635 status = metadata_database()->UpdateByFileResource(*entry);
636 SyncCompleted(token.Pass(), status);
639 void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
640 DCHECK(remote_parent_folder_tracker_);
642 file_type_ = SYNC_FILE_TYPE_FILE;
643 sync_action_ = SYNC_ACTION_ADDED;
644 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
645 drive_uploader()->UploadNewFile(
646 remote_parent_folder_tracker_->file_id(), local_path_,
647 title.AsUTF8Unsafe(), GetMimeTypeFromTitle(title),
648 drive::UploadNewFileOptions(),
649 base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
650 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
651 google_apis::ProgressCallback());
654 void LocalToRemoteSyncer::DidUploadNewFile(
655 scoped_ptr<SyncTaskToken> token,
656 google_apis::DriveApiErrorCode error,
657 const GURL& upload_location,
658 scoped_ptr<google_apis::FileResource> entry) {
659 if (error == google_apis::HTTP_NOT_FOUND)
660 needs_remote_change_listing_ = true;
662 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
663 if (status != SYNC_STATUS_OK) {
664 SyncCompleted(token.Pass(), status);
665 return;
668 if (!entry) {
669 NOTREACHED();
670 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
671 return;
674 status = metadata_database()->ReplaceActiveTrackerWithNewResource(
675 remote_parent_folder_tracker_->tracker_id(), *entry);
676 SyncCompleted(token.Pass(), status);
679 void LocalToRemoteSyncer::CreateRemoteFolder(scoped_ptr<SyncTaskToken> token) {
680 DCHECK(remote_parent_folder_tracker_);
682 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
683 file_type_ = SYNC_FILE_TYPE_DIRECTORY;
684 sync_action_ = SYNC_ACTION_ADDED;
686 DCHECK(!folder_creator_);
687 folder_creator_.reset(new FolderCreator(
688 drive_service(), metadata_database(),
689 remote_parent_folder_tracker_->file_id(),
690 title.AsUTF8Unsafe()));
691 folder_creator_->Run(base::Bind(
692 &LocalToRemoteSyncer::DidCreateRemoteFolder,
693 weak_ptr_factory_.GetWeakPtr(),
694 base::Passed(&token)));
697 void LocalToRemoteSyncer::DidCreateRemoteFolder(
698 scoped_ptr<SyncTaskToken> token,
699 const std::string& file_id,
700 SyncStatusCode status) {
701 if (status == SYNC_FILE_ERROR_NOT_FOUND)
702 needs_remote_change_listing_ = true;
704 scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
705 if (status != SYNC_STATUS_OK) {
706 SyncCompleted(token.Pass(), status);
707 return;
710 status = SYNC_STATUS_FAILED;
711 MetadataDatabase::ActivationStatus activation_status =
712 metadata_database()->TryActivateTracker(
713 remote_parent_folder_tracker_->tracker_id(),
714 file_id, &status);
715 switch (activation_status) {
716 case MetadataDatabase::ACTIVATION_PENDING:
717 SyncCompleted(token.Pass(), status);
718 return;
719 case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
720 // The activation failed due to another tracker that has another parent.
721 // Detach the folder from the current parent to avoid using this folder as
722 // active folder.
723 drive_service()->RemoveResourceFromDirectory(
724 remote_parent_folder_tracker_->file_id(), file_id,
725 base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
726 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
727 return;
730 NOTREACHED();
731 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
732 return;
735 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
736 scoped_ptr<SyncTaskToken> token,
737 google_apis::DriveApiErrorCode error) {
738 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
739 if (status != SYNC_STATUS_OK) {
740 SyncCompleted(token.Pass(), status);
741 return;
744 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
747 bool LocalToRemoteSyncer::IsContextReady() {
748 return sync_context_->GetDriveService() &&
749 sync_context_->GetDriveUploader() &&
750 sync_context_->GetMetadataDatabase();
753 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
754 set_used_network(true);
755 return sync_context_->GetDriveService();
758 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
759 set_used_network(true);
760 return sync_context_->GetDriveUploader();
763 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
764 return sync_context_->GetMetadataDatabase();
767 } // namespace drive_backend
768 } // namespace sync_file_system