Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / local_to_remote_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/local_to_remote_syncer.h"
6
7 #include <string>
8 #include <vector>
9
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/task_runner_util.h"
16 #include "chrome/browser/drive/drive_api_util.h"
17 #include "chrome/browser/drive/drive_service_interface.h"
18 #include "chrome/browser/drive/drive_uploader.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/logger.h"
25 #include "google_apis/drive/drive_api_parser.h"
26 #include "webkit/common/fileapi/file_system_util.h"
27
28 namespace sync_file_system {
29 namespace drive_backend {
30
31 namespace {
32
33 scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
34                                         int64 tracker_id) {
35   scoped_ptr<FileTracker> tracker(new FileTracker);
36   if (metadata_database->FindTrackerByTrackerID(tracker_id, tracker.get()))
37     return tracker.Pass();
38   return scoped_ptr<FileTracker>();
39 }
40
41 void ReturnRetryOnSuccess(const SyncStatusCallback& callback,
42                           SyncStatusCode status) {
43   if (status == SYNC_STATUS_OK)
44     status = SYNC_STATUS_RETRY;
45   callback.Run(status);
46 }
47
48 bool IsLocalFileMissing(const SyncFileMetadata& local_metadata,
49                         const FileChange& local_change) {
50   return local_metadata.file_type == SYNC_FILE_TYPE_UNKNOWN ||
51          local_change.IsDelete();
52 }
53
54 }  // namespace
55
56 LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
57                                          const SyncFileMetadata& local_metadata,
58                                          const FileChange& local_change,
59                                          const base::FilePath& local_path,
60                                          const fileapi::FileSystemURL& url)
61     : sync_context_(sync_context),
62       local_change_(local_change),
63       local_is_missing_(IsLocalFileMissing(local_metadata, local_change)),
64       local_path_(local_path),
65       url_(url),
66       sync_action_(SYNC_ACTION_NONE),
67       needs_remote_change_listing_(false),
68       weak_ptr_factory_(this) {
69   DCHECK(local_is_missing_ ||
70          local_change.file_type() == local_metadata.file_type)
71       << local_change.DebugString() << " metadata:" << local_metadata.file_type;
72 }
73
74 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
75 }
76
77 void LocalToRemoteSyncer::RunExclusive(const SyncStatusCallback& callback) {
78   if (!IsContextReady()) {
79     util::Log(logging::LOG_VERBOSE, FROM_HERE,
80               "[Local -> Remote] Context not ready.");
81     NOTREACHED();
82     callback.Run(SYNC_STATUS_FAILED);
83     return;
84   }
85
86   SyncStatusCallback wrapped_callback = base::Bind(
87       &LocalToRemoteSyncer::SyncCompleted, weak_ptr_factory_.GetWeakPtr(),
88       callback);
89
90   util::Log(logging::LOG_VERBOSE, FROM_HERE,
91             "[Local -> Remote] Start: %s on %s@%s %s",
92             local_change_.DebugString().c_str(),
93             url_.path().AsUTF8Unsafe().c_str(),
94             url_.origin().host().c_str(),
95             local_is_missing_ ? "(missing)" : "");
96
97   if (local_is_missing_ && !local_change_.IsDelete()) {
98     // Stray file, we can just return.
99     util::Log(logging::LOG_VERBOSE, FROM_HERE,
100               "[Local -> Remote]: Missing file for non-delete change");
101     callback.Run(SYNC_STATUS_OK);
102     return;
103   }
104
105   std::string app_id = url_.origin().host();
106   base::FilePath path = url_.path();
107
108   scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
109   base::FilePath active_ancestor_path;
110   if (!metadata_database()->FindNearestActiveAncestor(
111           app_id, path,
112           active_ancestor_tracker.get(), &active_ancestor_path)) {
113     // The app is disabled or not registered.
114     util::Log(logging::LOG_VERBOSE, FROM_HERE,
115               "[Local -> Remote]: App is disabled or not registered");
116     callback.Run(SYNC_STATUS_UNKNOWN_ORIGIN);
117     return;
118   }
119   DCHECK(active_ancestor_tracker->active());
120   DCHECK(active_ancestor_tracker->has_synced_details());
121   const FileDetails& active_ancestor_details =
122       active_ancestor_tracker->synced_details();
123
124   // TODO(tzik): Consider handling
125   // active_ancestor_tracker->synced_details().missing() case.
126
127   DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
128          active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
129
130   base::FilePath missing_entries;
131   if (active_ancestor_path.empty()) {
132     missing_entries = path;
133   } else if (active_ancestor_path != path) {
134     bool should_success = active_ancestor_path.AppendRelativePath(
135         path, &missing_entries);
136     if (!should_success) {
137       NOTREACHED() << "[Local -> Remote]: Detected invalid ancestor: "
138                    << active_ancestor_path.value();
139       callback.Run(SYNC_STATUS_FAILED);
140       return;
141     }
142   }
143
144   std::vector<base::FilePath::StringType> missing_components;
145   fileapi::VirtualPath::GetComponents(missing_entries, &missing_components);
146
147   if (!missing_components.empty()) {
148     if (local_is_missing_) {
149       util::Log(logging::LOG_VERBOSE, FROM_HERE,
150                 "[Local -> Remote]: Both local and remote are marked missing");
151       // !IsDelete() but SYNC_FILE_TYPE_UNKNOWN could happen when a file is
152       // deleted by recursive deletion (which is not recorded by tracker)
153       // but there're remaining changes for the same file in the tracker.
154
155       // Local file is deleted and remote file is missing, already deleted or
156       // not yet synced.  There is nothing to do for the file.
157       callback.Run(SYNC_STATUS_OK);
158       return;
159     }
160   }
161
162   if (missing_components.size() > 1) {
163     // The original target doesn't have remote file and parent.
164     // Try creating the parent first.
165     if (active_ancestor_details.file_kind() == FILE_KIND_FOLDER) {
166       remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
167       target_path_ = active_ancestor_path.Append(missing_components[0]);
168       util::Log(logging::LOG_VERBOSE, FROM_HERE,
169                 "[Local -> Remote]: Detected missing parent folder.");
170       CreateRemoteFolder(wrapped_callback);
171       return;
172     }
173
174     DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE);
175     remote_parent_folder_tracker_ =
176         FindTrackerByID(metadata_database(),
177                         active_ancestor_tracker->parent_tracker_id());
178     remote_file_tracker_ = active_ancestor_tracker.Pass();
179     target_path_ = active_ancestor_path;
180     util::Log(logging::LOG_VERBOSE, FROM_HERE,
181               "[Local -> Remote]: Detected non-folder file in its path.");
182     DeleteRemoteFile(base::Bind(&LocalToRemoteSyncer::DidDeleteForCreateFolder,
183                                 weak_ptr_factory_.GetWeakPtr(),
184                                 wrapped_callback));
185
186     return;
187   }
188
189   if (missing_components.empty()) {
190     // The original target has remote active file/folder.
191     remote_parent_folder_tracker_ =
192         FindTrackerByID(metadata_database(),
193                         active_ancestor_tracker->parent_tracker_id());
194     remote_file_tracker_ = active_ancestor_tracker.Pass();
195     target_path_ = url_.path();
196     DCHECK(target_path_ == active_ancestor_path);
197
198     if (remote_file_tracker_->dirty()) {
199       util::Log(logging::LOG_VERBOSE, FROM_HERE,
200                 "[Local -> Remote]: Detected conflicting dirty tracker:%"
201                 PRId64, remote_file_tracker_->tracker_id());
202       // Both local and remote file has pending modification.
203       HandleConflict(wrapped_callback);
204       return;
205     }
206
207     // Non-conflicting file/folder update case.
208     HandleExistingRemoteFile(wrapped_callback);
209     return;
210   }
211
212   DCHECK(local_change_.IsAddOrUpdate());
213   DCHECK_EQ(1u, missing_components.size());
214   // The original target has remote parent folder and doesn't have remote active
215   // file.
216   // Upload the file as a new file or create a folder.
217   remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
218   target_path_ = url_.path();
219   DCHECK(target_path_ == active_ancestor_path.Append(missing_components[0]));
220   if (local_change_.file_type() == SYNC_FILE_TYPE_FILE) {
221     util::Log(logging::LOG_VERBOSE, FROM_HERE,
222               "[Local -> Remote]: Detected a new file.");
223     UploadNewFile(wrapped_callback);
224     return;
225   }
226   util::Log(logging::LOG_VERBOSE, FROM_HERE,
227             "[Local -> Remote]: Detected a new folder.");
228   CreateRemoteFolder(wrapped_callback);
229 }
230
231 void LocalToRemoteSyncer::SyncCompleted(const SyncStatusCallback& callback,
232                                         SyncStatusCode status) {
233   if (status == SYNC_STATUS_OK && target_path_ != url_.path())
234     status = SYNC_STATUS_RETRY;
235
236   if (needs_remote_change_listing_)
237     status = SYNC_STATUS_FILE_BUSY;
238
239   util::Log(logging::LOG_VERBOSE, FROM_HERE,
240             "[Local -> Remote]: Finished: action=%s, status=%s for %s@%s",
241             SyncActionToString(sync_action_),
242             SyncStatusCodeToString(status),
243             target_path_.AsUTF8Unsafe().c_str(),
244             url_.origin().host().c_str());
245
246   callback.Run(status);
247 }
248
249 void LocalToRemoteSyncer::HandleConflict(const SyncStatusCallback& callback) {
250   DCHECK(remote_file_tracker_);
251   DCHECK(remote_file_tracker_->has_synced_details());
252   DCHECK(remote_file_tracker_->active());
253   DCHECK(remote_file_tracker_->dirty());
254
255   if (local_is_missing_) {
256     callback.Run(SYNC_STATUS_OK);
257     return;
258   }
259
260   if (local_change_.IsFile()) {
261     UploadNewFile(callback);
262     return;
263   }
264
265   DCHECK(local_change_.IsDirectory());
266   // Check if we can reuse the remote folder.
267   FileMetadata remote_file_metadata;
268   bool should_success = metadata_database()->FindFileByFileID(
269       remote_file_tracker_->file_id(), &remote_file_metadata);
270   if (!should_success) {
271     NOTREACHED();
272     CreateRemoteFolder(callback);
273     return;
274   }
275
276   const FileDetails& remote_details = remote_file_metadata.details();
277   base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
278   if (!remote_details.missing() &&
279       remote_details.file_kind() == FILE_KIND_FOLDER &&
280       remote_details.title() == title.AsUTF8Unsafe() &&
281       HasFileAsParent(remote_details,
282                       remote_parent_folder_tracker_->file_id())) {
283     metadata_database()->UpdateTracker(
284         remote_file_tracker_->tracker_id(), remote_details, callback);
285     return;
286   }
287
288   // Create new remote folder.
289   CreateRemoteFolder(callback);
290 }
291
292 void LocalToRemoteSyncer::HandleExistingRemoteFile(
293     const SyncStatusCallback& callback) {
294   DCHECK(remote_file_tracker_);
295   DCHECK(!remote_file_tracker_->dirty());
296   DCHECK(remote_file_tracker_->active());
297   DCHECK(remote_file_tracker_->has_synced_details());
298
299   if (local_is_missing_) {
300     // Local file deletion for existing remote file.
301     DeleteRemoteFile(callback);
302     return;
303   }
304
305   DCHECK(local_change_.IsAddOrUpdate());
306   DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
307
308   const FileDetails& synced_details = remote_file_tracker_->synced_details();
309   DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
310          synced_details.file_kind() == FILE_KIND_FOLDER);
311   if (local_change_.IsFile()) {
312     if (synced_details.file_kind() == FILE_KIND_FILE) {
313       // Non-conflicting local file update to existing remote regular file.
314       UploadExistingFile(callback);
315       return;
316     }
317
318     DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
319     // Non-conflicting local file update to existing remote *folder*.
320     // Assuming this case as local folder deletion + local file creation, delete
321     // the remote folder and upload the file.
322     DeleteRemoteFile(base::Bind(&LocalToRemoteSyncer::DidDeleteForUploadNewFile,
323                                 weak_ptr_factory_.GetWeakPtr(),
324                                 callback));
325     return;
326   }
327
328   DCHECK(local_change_.IsDirectory());
329   if (synced_details.file_kind() == FILE_KIND_FILE) {
330     // Non-conflicting local folder creation to existing remote *file*.
331     // Assuming this case as local file deletion + local folder creation, delete
332     // the remote file and create a remote folder.
333     DeleteRemoteFile(base::Bind(&LocalToRemoteSyncer::DidDeleteForCreateFolder,
334                                 weak_ptr_factory_.GetWeakPtr(), callback));
335     return;
336   }
337
338   // Non-conflicting local folder creation to existing remote folder.
339   DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
340   callback.Run(SYNC_STATUS_OK);
341 }
342
343 void LocalToRemoteSyncer::DeleteRemoteFile(
344     const SyncStatusCallback& callback) {
345   DCHECK(remote_file_tracker_);
346   DCHECK(remote_file_tracker_->has_synced_details());
347
348   sync_action_ = SYNC_ACTION_DELETED;
349   drive_service()->DeleteResource(
350       remote_file_tracker_->file_id(),
351       remote_file_tracker_->synced_details().etag(),
352       base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
353                  weak_ptr_factory_.GetWeakPtr(),
354                  callback));
355 }
356
357 void LocalToRemoteSyncer::DidDeleteRemoteFile(
358     const SyncStatusCallback& callback,
359     google_apis::GDataErrorCode error) {
360   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
361   if (status != SYNC_STATUS_OK &&
362       error != google_apis::HTTP_NOT_FOUND &&
363       error != google_apis::HTTP_PRECONDITION &&
364       error != google_apis::HTTP_CONFLICT) {
365     callback.Run(status);
366     return;
367   }
368
369   // Handle NOT_FOUND case as SUCCESS case.
370   // For PRECONDITION / CONFLICT case, the remote file is modified since the
371   // last sync completed.  As our policy for deletion-modification conflict
372   // resolution, ignore the local deletion.
373   if (error == google_apis::HTTP_NOT_FOUND) {
374     metadata_database()->UpdateByDeletedRemoteFile(
375         remote_file_tracker_->file_id(), callback);
376     return;
377   }
378   callback.Run(SYNC_STATUS_OK);
379 }
380
381 void LocalToRemoteSyncer::UploadExistingFile(
382     const SyncStatusCallback& callback)  {
383   DCHECK(remote_file_tracker_);
384   DCHECK(remote_file_tracker_->has_synced_details());
385
386   base::PostTaskAndReplyWithResult(
387       sync_context_->GetFileTaskRunner(), FROM_HERE,
388       base::Bind(&drive::util::GetMd5Digest, local_path_),
389       base::Bind(&LocalToRemoteSyncer::DidGetMD5ForUpload,
390                  weak_ptr_factory_.GetWeakPtr(),
391                  callback));
392 }
393
394 void LocalToRemoteSyncer::DidGetMD5ForUpload(
395     const SyncStatusCallback& callback,
396     const std::string& local_file_md5) {
397   if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
398     // Local file is not changed.
399     callback.Run(SYNC_STATUS_OK);
400     return;
401   }
402
403   sync_action_ = SYNC_ACTION_UPDATED;
404
405   drive::DriveUploader::UploadExistingFileOptions options;
406   options.etag = remote_file_tracker_->synced_details().etag();
407   drive_uploader()->UploadExistingFile(
408       remote_file_tracker_->file_id(),
409       local_path_,
410       "application/octet_stream",
411       options,
412       base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
413                  weak_ptr_factory_.GetWeakPtr(),
414                  callback),
415       google_apis::ProgressCallback());
416 }
417
418 void LocalToRemoteSyncer::DidUploadExistingFile(
419     const SyncStatusCallback& callback,
420     google_apis::GDataErrorCode error,
421     const GURL&,
422     scoped_ptr<google_apis::ResourceEntry> entry) {
423   if (error == google_apis::HTTP_PRECONDITION ||
424       error == google_apis::HTTP_CONFLICT ||
425       error == google_apis::HTTP_NOT_FOUND) {
426     // The remote file has unfetched remote change.  Fetch latest metadata and
427     // update database with it.
428     // TODO(tzik): Consider adding local side low-priority dirtiness handling to
429     // handle this as ListChangesTask.
430
431     needs_remote_change_listing_ = true;
432     UpdateRemoteMetadata(remote_file_tracker_->file_id(),
433                          base::Bind(&ReturnRetryOnSuccess, callback));
434     return;
435   }
436
437   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
438   if (status != SYNC_STATUS_OK) {
439     callback.Run(status);
440     return;
441   }
442
443   if (!entry) {
444     NOTREACHED();
445     callback.Run(SYNC_STATUS_FAILED);
446     return;
447   }
448
449   DCHECK(entry);
450   metadata_database()->UpdateByFileResource(
451       *drive::util::ConvertResourceEntryToFileResource(*entry),
452       base::Bind(&LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile,
453                  weak_ptr_factory_.GetWeakPtr(),
454                  callback));
455 }
456
457 void LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile(
458     const SyncStatusCallback& callback,
459     SyncStatusCode status) {
460   if (status != SYNC_STATUS_OK) {
461     callback.Run(status);
462     return;
463   }
464
465   FileMetadata file;
466   bool should_success = metadata_database()->FindFileByFileID(
467       remote_file_tracker_->file_id(), &file);
468   if (!should_success) {
469     NOTREACHED();
470     callback.Run(SYNC_STATUS_FAILED);
471     return;
472   }
473
474   const FileDetails& details = file.details();
475   base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
476   if (!details.missing() &&
477       details.file_kind() == FILE_KIND_FILE &&
478       details.title() == title.AsUTF8Unsafe() &&
479       HasFileAsParent(details,
480                       remote_parent_folder_tracker_->file_id())) {
481     metadata_database()->UpdateTracker(
482         remote_file_tracker_->tracker_id(),
483         file.details(),
484         callback);
485     return;
486   }
487
488   callback.Run(SYNC_STATUS_RETRY);
489 }
490
491 void LocalToRemoteSyncer::UpdateRemoteMetadata(
492     const std::string& file_id,
493     const SyncStatusCallback& callback) {
494   DCHECK(remote_file_tracker_);
495   drive_service()->GetResourceEntry(
496       file_id,
497       base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
498                  weak_ptr_factory_.GetWeakPtr(),
499                  file_id, callback));
500 }
501
502 void LocalToRemoteSyncer::DidGetRemoteMetadata(
503     const std::string& file_id,
504     const SyncStatusCallback& callback,
505     google_apis::GDataErrorCode error,
506     scoped_ptr<google_apis::ResourceEntry> entry) {
507   if (error == google_apis::HTTP_NOT_FOUND) {
508     metadata_database()->UpdateByDeletedRemoteFile(file_id, callback);
509     return;
510   }
511
512   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
513   if (status != SYNC_STATUS_OK) {
514     callback.Run(status);
515     return;
516   }
517
518   if (!entry) {
519     NOTREACHED();
520     callback.Run(SYNC_STATUS_FAILED);
521     return;
522   }
523
524   metadata_database()->UpdateByFileResource(
525       *drive::util::ConvertResourceEntryToFileResource(*entry), callback);
526 }
527
528 void LocalToRemoteSyncer::DidDeleteForUploadNewFile(
529     const SyncStatusCallback& callback,
530     SyncStatusCode status) {
531   if (status == SYNC_STATUS_HAS_CONFLICT) {
532     UpdateRemoteMetadata(remote_file_tracker_->file_id(),
533                          base::Bind(&ReturnRetryOnSuccess, callback));
534     return;
535   }
536
537   if (status != SYNC_STATUS_OK) {
538     callback.Run(status);
539     return;
540   }
541
542   UploadNewFile(callback);
543 }
544
545 void LocalToRemoteSyncer::DidDeleteForCreateFolder(
546     const SyncStatusCallback& callback,
547     SyncStatusCode status) {
548   if (status == SYNC_STATUS_HAS_CONFLICT) {
549     UpdateRemoteMetadata(remote_file_tracker_->file_id(),
550                          base::Bind(&ReturnRetryOnSuccess, callback));
551     return;
552   }
553
554   if (status != SYNC_STATUS_OK) {
555     callback.Run(status);
556     return;
557   }
558
559   CreateRemoteFolder(callback);
560 }
561
562 void LocalToRemoteSyncer::UploadNewFile(const SyncStatusCallback& callback) {
563   DCHECK(remote_parent_folder_tracker_);
564
565   sync_action_ = SYNC_ACTION_ADDED;
566   base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
567   drive_uploader()->UploadNewFile(
568       remote_parent_folder_tracker_->file_id(),
569       local_path_,
570       title.AsUTF8Unsafe(),
571       GetMimeTypeFromTitle(title),
572       drive::DriveUploader::UploadNewFileOptions(),
573       base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
574                  weak_ptr_factory_.GetWeakPtr(),
575                  callback),
576       google_apis::ProgressCallback());
577 }
578
579 void LocalToRemoteSyncer::DidUploadNewFile(
580     const SyncStatusCallback& callback,
581     google_apis::GDataErrorCode error,
582     const GURL& upload_location,
583     scoped_ptr<google_apis::ResourceEntry> entry) {
584   if (error == google_apis::HTTP_NOT_FOUND)
585     needs_remote_change_listing_ = true;
586
587   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
588   if (status != SYNC_STATUS_OK) {
589     callback.Run(status);
590     return;
591   }
592
593   if (!entry) {
594     NOTREACHED();
595     callback.Run(SYNC_STATUS_FAILED);
596     return;
597   }
598
599   metadata_database()->ReplaceActiveTrackerWithNewResource(
600       remote_parent_folder_tracker_->tracker_id(),
601       *drive::util::ConvertResourceEntryToFileResource(*entry),
602       callback);
603 }
604
605 void LocalToRemoteSyncer::CreateRemoteFolder(
606     const SyncStatusCallback& callback) {
607   DCHECK(remote_parent_folder_tracker_);
608
609   base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
610   sync_action_ = SYNC_ACTION_ADDED;
611
612   DCHECK(!folder_creator_);
613   folder_creator_.reset(new FolderCreator(
614       drive_service(), metadata_database(),
615       remote_parent_folder_tracker_->file_id(),
616       title.AsUTF8Unsafe()));
617   folder_creator_->Run(base::Bind(
618       &LocalToRemoteSyncer::DidCreateRemoteFolder,
619       weak_ptr_factory_.GetWeakPtr(),
620       callback));
621 }
622
623 void LocalToRemoteSyncer::DidCreateRemoteFolder(
624     const SyncStatusCallback& callback,
625     const std::string& file_id,
626     SyncStatusCode status) {
627   if (status == SYNC_FILE_ERROR_NOT_FOUND)
628     needs_remote_change_listing_ = true;
629
630   scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
631   if (status != SYNC_STATUS_OK) {
632     callback.Run(status);
633     return;
634   }
635
636   MetadataDatabase::ActivationStatus activation_status =
637       metadata_database()->TryActivateTracker(
638           remote_parent_folder_tracker_->tracker_id(),
639           file_id, callback);
640   switch (activation_status) {
641     case MetadataDatabase::ACTIVATION_PENDING:
642       // |callback| will be invoked by MetadataDatabase later in this case.
643       return;
644     case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
645       // The activation failed due to another tracker that has another parent.
646       // Detach the folder from the current parent to avoid using this folder as
647       // active folder.
648       drive_service()->RemoveResourceFromDirectory(
649           remote_parent_folder_tracker_->file_id(), file_id,
650           base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
651                      weak_ptr_factory_.GetWeakPtr(),
652                      callback));
653       return;
654   }
655
656   NOTREACHED();
657   callback.Run(SYNC_STATUS_FAILED);
658   return;
659 }
660
661 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
662     const SyncStatusCallback& callback,
663     google_apis::GDataErrorCode error) {
664   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
665   if (status != SYNC_STATUS_OK) {
666     callback.Run(status);
667     return;
668   }
669
670   callback.Run(SYNC_STATUS_RETRY);
671 }
672
673 bool LocalToRemoteSyncer::IsContextReady() {
674   return sync_context_->GetDriveService() &&
675       sync_context_->GetDriveUploader() &&
676       sync_context_->GetMetadataDatabase();
677 }
678
679 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
680   set_used_network(true);
681   return sync_context_->GetDriveService();
682 }
683
684 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
685   set_used_network(true);
686   return sync_context_->GetDriveUploader();
687 }
688
689 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
690   return sync_context_->GetMetadataDatabase();
691 }
692
693 }  // namespace drive_backend
694 }  // namespace sync_file_system