- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend_v1 / local_sync_delegate.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_v1/local_sync_delegate.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "chrome/browser/sync_file_system/conflict_resolution_resolver.h"
10 #include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h"
11 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h"
12 #include "chrome/browser/sync_file_system/logger.h"
13 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
14
15 namespace sync_file_system {
16 namespace drive_backend {
17
18 LocalSyncDelegate::LocalSyncDelegate(
19     DriveFileSyncService* sync_service,
20     const FileChange& local_change,
21     const base::FilePath& local_path,
22     const SyncFileMetadata& local_metadata,
23     const fileapi::FileSystemURL& url)
24     : sync_service_(sync_service),
25       operation_(SYNC_OPERATION_NONE),
26       url_(url),
27       local_change_(local_change),
28       local_path_(local_path),
29       local_metadata_(local_metadata),
30       has_drive_metadata_(false),
31       has_remote_change_(false),
32       weak_factory_(this) {}
33
34 LocalSyncDelegate::~LocalSyncDelegate() {}
35
36 void LocalSyncDelegate::Run(const SyncStatusCallback& callback) {
37   // TODO(nhiroki): support directory operations (http://crbug.com/161442).
38   DCHECK(IsSyncFSDirectoryOperationEnabled() || !local_change_.IsDirectory());
39   operation_ = SYNC_OPERATION_NONE;
40
41   has_drive_metadata_ =
42       metadata_store()->ReadEntry(url_, &drive_metadata_) == SYNC_STATUS_OK;
43
44   if (!has_drive_metadata_)
45     drive_metadata_.set_md5_checksum(std::string());
46
47   sync_service_->EnsureOriginRootDirectory(
48       url_.origin(),
49       base::Bind(&LocalSyncDelegate::DidGetOriginRoot,
50                  weak_factory_.GetWeakPtr(),
51                  callback));
52 }
53
54 void LocalSyncDelegate::DidGetOriginRoot(
55     const SyncStatusCallback& callback,
56     SyncStatusCode status,
57     const std::string& origin_resource_id) {
58   if (status != SYNC_STATUS_OK) {
59     callback.Run(status);
60     return;
61   }
62
63   origin_resource_id_ = origin_resource_id;
64
65   has_remote_change_ =
66       remote_change_handler()->GetChangeForURL(url_, &remote_change_);
67   if (has_remote_change_ && drive_metadata_.resource_id().empty())
68     drive_metadata_.set_resource_id(remote_change_.resource_id);
69
70   SyncFileType remote_file_type =
71       has_remote_change_ ? remote_change_.change.file_type() :
72       has_drive_metadata_ ?
73           DriveFileSyncService::DriveMetadataResourceTypeToSyncFileType(
74               drive_metadata_.type())
75       : SYNC_FILE_TYPE_UNKNOWN;
76
77   DCHECK_EQ(SYNC_OPERATION_NONE, operation_);
78   operation_ = LocalSyncOperationResolver::Resolve(
79       local_change_,
80       has_remote_change_ ? &remote_change_.change : NULL,
81       has_drive_metadata_ ? &drive_metadata_ : NULL);
82
83   util::Log(logging::LOG_VERBOSE, FROM_HERE,
84             "ApplyLocalChange for %s local_change:%s ===> %s",
85             url_.DebugString().c_str(),
86             local_change_.DebugString().c_str(),
87             SyncOperationTypeToString(operation_));
88
89   switch (operation_) {
90     case SYNC_OPERATION_ADD_FILE:
91       UploadNewFile(callback);
92       return;
93     case SYNC_OPERATION_ADD_DIRECTORY:
94       CreateDirectory(callback);
95       return;
96     case SYNC_OPERATION_UPDATE_FILE:
97       UploadExistingFile(callback);
98       return;
99     case SYNC_OPERATION_DELETE:
100       Delete(callback);
101       return;
102     case SYNC_OPERATION_NONE:
103       callback.Run(SYNC_STATUS_OK);
104       return;
105     case SYNC_OPERATION_CONFLICT:
106       HandleConflict(callback);
107       return;
108     case SYNC_OPERATION_RESOLVE_TO_LOCAL:
109       ResolveToLocal(callback);
110       return;
111     case SYNC_OPERATION_RESOLVE_TO_REMOTE:
112       ResolveToRemote(callback, remote_file_type);
113       return;
114     case SYNC_OPERATION_DELETE_METADATA:
115       DeleteMetadata(base::Bind(
116           &LocalSyncDelegate::DidApplyLocalChange,
117           weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS));
118       return;
119     case SYNC_OPERATION_FAIL: {
120       callback.Run(SYNC_STATUS_FAILED);
121       return;
122     }
123   }
124   NOTREACHED();
125   callback.Run(SYNC_STATUS_FAILED);
126 }
127
128 void LocalSyncDelegate::UploadNewFile(const SyncStatusCallback& callback) {
129   api_util()->UploadNewFile(
130       origin_resource_id_,
131       local_path_,
132       DriveFileSyncService::PathToTitle(url_.path()),
133       base::Bind(&LocalSyncDelegate::DidUploadNewFile,
134                  weak_factory_.GetWeakPtr(), callback));
135 }
136
137 void LocalSyncDelegate::DidUploadNewFile(
138     const SyncStatusCallback& callback,
139     google_apis::GDataErrorCode error,
140     const std::string& resource_id,
141     const std::string& md5) {
142   switch (error) {
143     case google_apis::HTTP_CREATED:
144       UpdateMetadata(
145           resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE,
146           base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
147                      weak_factory_.GetWeakPtr(), callback, error));
148       sync_service_->NotifyObserversFileStatusChanged(
149           url_,
150           SYNC_FILE_STATUS_SYNCED,
151           SYNC_ACTION_ADDED,
152           SYNC_DIRECTION_LOCAL_TO_REMOTE);
153       return;
154     case google_apis::HTTP_CONFLICT:
155       HandleCreationConflict(resource_id, DriveMetadata::RESOURCE_TYPE_FILE,
156                              callback);
157       return;
158     default:
159       callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
160   }
161 }
162
163 void LocalSyncDelegate::CreateDirectory(const SyncStatusCallback& callback) {
164   DCHECK(IsSyncFSDirectoryOperationEnabled());
165   api_util()->CreateDirectory(
166       origin_resource_id_,
167       DriveFileSyncService::PathToTitle(url_.path()),
168       base::Bind(&LocalSyncDelegate::DidCreateDirectory,
169                  weak_factory_.GetWeakPtr(), callback));
170 }
171
172 void LocalSyncDelegate::DidCreateDirectory(
173     const SyncStatusCallback& callback,
174     google_apis::GDataErrorCode error,
175     const std::string& resource_id) {
176   switch (error) {
177     case google_apis::HTTP_SUCCESS:
178     case google_apis::HTTP_CREATED: {
179       UpdateMetadata(
180           resource_id, std::string(), DriveMetadata::RESOURCE_TYPE_FOLDER,
181           base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
182                      weak_factory_.GetWeakPtr(), callback, error));
183       sync_service_->NotifyObserversFileStatusChanged(
184           url_,
185           SYNC_FILE_STATUS_SYNCED,
186           SYNC_ACTION_ADDED,
187           SYNC_DIRECTION_LOCAL_TO_REMOTE);
188       return;
189     }
190
191     case google_apis::HTTP_CONFLICT:
192       // There were conflicts and a file was left.
193       // TODO(kinuko): Handle the latter case (http://crbug.com/237090).
194       // Fall-through
195
196     default:
197       callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
198   }
199 }
200
201 void LocalSyncDelegate::UploadExistingFile(const SyncStatusCallback& callback) {
202   DCHECK(has_drive_metadata_);
203   if (drive_metadata_.resource_id().empty()) {
204     UploadNewFile(callback);
205     return;
206   }
207
208   api_util()->UploadExistingFile(
209       drive_metadata_.resource_id(),
210       drive_metadata_.md5_checksum(),
211       local_path_,
212       base::Bind(&LocalSyncDelegate::DidUploadExistingFile,
213                  weak_factory_.GetWeakPtr(), callback));
214 }
215
216 void LocalSyncDelegate::DidUploadExistingFile(
217     const SyncStatusCallback& callback,
218     google_apis::GDataErrorCode error,
219     const std::string& resource_id,
220     const std::string& md5) {
221   DCHECK(has_drive_metadata_);
222   switch (error) {
223     case google_apis::HTTP_SUCCESS:
224       UpdateMetadata(
225           resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE,
226           base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
227                      weak_factory_.GetWeakPtr(), callback, error));
228       sync_service_->NotifyObserversFileStatusChanged(
229           url_,
230           SYNC_FILE_STATUS_SYNCED,
231           SYNC_ACTION_UPDATED,
232           SYNC_DIRECTION_LOCAL_TO_REMOTE);
233       return;
234     case google_apis::HTTP_CONFLICT:
235       HandleConflict(callback);
236       return;
237     case google_apis::HTTP_NOT_MODIFIED:
238       DidApplyLocalChange(callback,
239                           google_apis::HTTP_SUCCESS, SYNC_STATUS_OK);
240       return;
241     case google_apis::HTTP_NOT_FOUND:
242       UploadNewFile(callback);
243       return;
244     default: {
245       const SyncStatusCode status =
246           GDataErrorCodeToSyncStatusCodeWrapper(error);
247       DCHECK_NE(SYNC_STATUS_OK, status);
248       callback.Run(status);
249       return;
250     }
251   }
252 }
253
254 void LocalSyncDelegate::Delete(const SyncStatusCallback& callback) {
255   if (!has_drive_metadata_) {
256     callback.Run(SYNC_STATUS_OK);
257     return;
258   }
259
260   if (drive_metadata_.resource_id().empty()) {
261     DidDelete(callback, google_apis::HTTP_NOT_FOUND);
262     return;
263   }
264
265   api_util()->DeleteFile(
266       drive_metadata_.resource_id(),
267       drive_metadata_.md5_checksum(),
268       base::Bind(&LocalSyncDelegate::DidDelete,
269                  weak_factory_.GetWeakPtr(), callback));
270 }
271
272 void LocalSyncDelegate::DidDelete(
273     const SyncStatusCallback& callback,
274     google_apis::GDataErrorCode error) {
275   DCHECK(has_drive_metadata_);
276
277   switch (error) {
278     case google_apis::HTTP_SUCCESS:
279     case google_apis::HTTP_NOT_FOUND:
280       DeleteMetadata(base::Bind(
281           &LocalSyncDelegate::DidApplyLocalChange,
282           weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS));
283       sync_service_->NotifyObserversFileStatusChanged(
284           url_,
285           SYNC_FILE_STATUS_SYNCED,
286           SYNC_ACTION_DELETED,
287           SYNC_DIRECTION_LOCAL_TO_REMOTE);
288       return;
289     case google_apis::HTTP_PRECONDITION:
290     case google_apis::HTTP_CONFLICT:
291       // Delete |drive_metadata| on the conflict case.
292       // Conflicted remote change should be applied as a future remote change.
293       DeleteMetadata(base::Bind(
294           &LocalSyncDelegate::DidDeleteMetadataForDeletionConflict,
295           weak_factory_.GetWeakPtr(), callback));
296       sync_service_->NotifyObserversFileStatusChanged(
297           url_,
298           SYNC_FILE_STATUS_SYNCED,
299           SYNC_ACTION_DELETED,
300           SYNC_DIRECTION_LOCAL_TO_REMOTE);
301       return;
302     default: {
303       const SyncStatusCode status =
304           GDataErrorCodeToSyncStatusCodeWrapper(error);
305       DCHECK_NE(SYNC_STATUS_OK, status);
306       callback.Run(status);
307       return;
308     }
309   }
310 }
311
312 void LocalSyncDelegate::DidDeleteMetadataForDeletionConflict(
313     const SyncStatusCallback& callback,
314     SyncStatusCode status) {
315   callback.Run(SYNC_STATUS_OK);
316 }
317
318 void LocalSyncDelegate::ResolveToLocal(const SyncStatusCallback& callback) {
319   if (drive_metadata_.resource_id().empty()) {
320     DidDeleteFileToResolveToLocal(callback, google_apis::HTTP_NOT_FOUND);
321     return;
322   }
323
324   api_util()->DeleteFile(
325       drive_metadata_.resource_id(),
326       drive_metadata_.md5_checksum(),
327       base::Bind(
328           &LocalSyncDelegate::DidDeleteFileToResolveToLocal,
329           weak_factory_.GetWeakPtr(), callback));
330 }
331
332 void LocalSyncDelegate::DidDeleteFileToResolveToLocal(
333     const SyncStatusCallback& callback,
334     google_apis::GDataErrorCode error) {
335   if (error != google_apis::HTTP_SUCCESS &&
336       error != google_apis::HTTP_NOT_FOUND) {
337     callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
338     return;
339   }
340
341   DCHECK_NE(SYNC_FILE_TYPE_UNKNOWN, local_metadata_.file_type);
342   if (local_metadata_.file_type == SYNC_FILE_TYPE_FILE) {
343     UploadNewFile(callback);
344     return;
345   }
346
347   DCHECK(IsSyncFSDirectoryOperationEnabled());
348   DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_.file_type);
349   CreateDirectory(callback);
350 }
351
352 void LocalSyncDelegate::ResolveToRemote(
353     const SyncStatusCallback& callback,
354     SyncFileType remote_file_type) {
355   // Mark the file as to-be-fetched.
356   DCHECK(!drive_metadata_.resource_id().empty());
357
358   SetMetadataToBeFetched(
359       DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
360           remote_file_type),
361       base::Bind(&LocalSyncDelegate::DidResolveToRemote,
362                  weak_factory_.GetWeakPtr(), callback));
363   // The synced notification will be dispatched when the remote file is
364   // downloaded.
365 }
366
367 void LocalSyncDelegate::DidResolveToRemote(
368     const SyncStatusCallback& callback,
369     SyncStatusCode status) {
370   DCHECK(has_drive_metadata_);
371   if (status != SYNC_STATUS_OK) {
372     callback.Run(status);
373     return;
374   }
375
376   SyncFileType file_type = SYNC_FILE_TYPE_FILE;
377   if (drive_metadata_.type() == DriveMetadata::RESOURCE_TYPE_FOLDER)
378     file_type = SYNC_FILE_TYPE_DIRECTORY;
379   sync_service_->AppendFetchChange(
380       url_.origin(), url_.path(), drive_metadata_.resource_id(), file_type);
381   callback.Run(status);
382 }
383
384 void LocalSyncDelegate::DidApplyLocalChange(
385     const SyncStatusCallback& callback,
386     const google_apis::GDataErrorCode error,
387     SyncStatusCode status) {
388   if ((operation_ == SYNC_OPERATION_DELETE ||
389        operation_ == SYNC_OPERATION_DELETE_METADATA) &&
390       (status == SYNC_FILE_ERROR_NOT_FOUND ||
391        status == SYNC_DATABASE_ERROR_NOT_FOUND)) {
392     status = SYNC_STATUS_OK;
393   }
394
395   if (status == SYNC_STATUS_OK) {
396     remote_change_handler()->RemoveChangeForURL(url_);
397     status = GDataErrorCodeToSyncStatusCodeWrapper(error);
398   }
399   callback.Run(status);
400 }
401
402 void LocalSyncDelegate::UpdateMetadata(
403     const std::string& resource_id,
404     const std::string& md5,
405     DriveMetadata::ResourceType type,
406     const SyncStatusCallback& callback) {
407   has_drive_metadata_ = true;
408   drive_metadata_.set_resource_id(resource_id);
409   drive_metadata_.set_md5_checksum(md5);
410   drive_metadata_.set_conflicted(false);
411   drive_metadata_.set_to_be_fetched(false);
412   drive_metadata_.set_type(type);
413   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
414 }
415
416 void LocalSyncDelegate::ResetMetadataForStartOver(
417     const SyncStatusCallback& callback) {
418   has_drive_metadata_ = true;
419   DCHECK(!drive_metadata_.resource_id().empty());
420   drive_metadata_.set_md5_checksum(std::string());
421   drive_metadata_.set_conflicted(false);
422   drive_metadata_.set_to_be_fetched(false);
423   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
424 }
425
426 void LocalSyncDelegate::SetMetadataToBeFetched(
427     DriveMetadata::ResourceType type,
428     const SyncStatusCallback& callback) {
429   has_drive_metadata_ = true;
430   drive_metadata_.set_md5_checksum(std::string());
431   drive_metadata_.set_conflicted(false);
432   drive_metadata_.set_to_be_fetched(true);
433   drive_metadata_.set_type(type);
434   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
435 }
436
437 void LocalSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
438   metadata_store()->DeleteEntry(url_, callback);
439 }
440
441 void LocalSyncDelegate::HandleCreationConflict(
442     const std::string& resource_id,
443     DriveMetadata::ResourceType type,
444     const SyncStatusCallback& callback) {
445   // File-file conflict is found.
446   // Populates a fake drive_metadata and set has_drive_metadata = true.
447   // In HandleConflictLocalSync:
448   // - If conflict_resolution is manual, we'll change conflicted to true
449   //   and save the metadata.
450   // - Otherwise we'll save the metadata with empty md5 and will start
451   //   over local sync as UploadExistingFile.
452   drive_metadata_.set_resource_id(resource_id);
453   drive_metadata_.set_md5_checksum(std::string());
454   drive_metadata_.set_conflicted(false);
455   drive_metadata_.set_to_be_fetched(false);
456   drive_metadata_.set_type(type);
457   has_drive_metadata_ = true;
458   HandleConflict(callback);
459 }
460
461 void LocalSyncDelegate::HandleConflict(const SyncStatusCallback& callback) {
462   DCHECK(!drive_metadata_.resource_id().empty());
463   api_util()->GetResourceEntry(
464       drive_metadata_.resource_id(),
465       base::Bind(
466           &LocalSyncDelegate::DidGetEntryForConflictResolution,
467           weak_factory_.GetWeakPtr(), callback));
468 }
469
470 void LocalSyncDelegate::DidGetEntryForConflictResolution(
471     const SyncStatusCallback& callback,
472     google_apis::GDataErrorCode error,
473     scoped_ptr<google_apis::ResourceEntry> entry) {
474   SyncFileType remote_file_type = SYNC_FILE_TYPE_UNKNOWN;
475   ConflictResolution resolution = CONFLICT_RESOLUTION_UNKNOWN;
476
477   if (error != google_apis::HTTP_SUCCESS ||
478       entry->updated_time().is_null()) {
479     resolution = CONFLICT_RESOLUTION_LOCAL_WIN;
480   } else {
481     SyncFileType local_file_type = local_metadata_.file_type;
482     base::Time local_modification_time = local_metadata_.last_modified;
483     base::Time remote_modification_time = entry->updated_time();
484     if (entry->is_file())
485       remote_file_type = SYNC_FILE_TYPE_FILE;
486     else if (entry->is_folder())
487       remote_file_type = SYNC_FILE_TYPE_DIRECTORY;
488     else
489       remote_file_type = SYNC_FILE_TYPE_UNKNOWN;
490
491     resolution = conflict_resolution_resolver()->Resolve(
492         local_file_type, local_modification_time,
493         remote_file_type, remote_modification_time);
494   }
495
496   switch (resolution) {
497     case CONFLICT_RESOLUTION_MARK_CONFLICT:
498       HandleManualResolutionCase(callback);
499       return;
500     case CONFLICT_RESOLUTION_LOCAL_WIN:
501       HandleLocalWinCase(callback);
502       return;
503     case CONFLICT_RESOLUTION_REMOTE_WIN:
504       HandleRemoteWinCase(callback, remote_file_type);
505       return;
506     case CONFLICT_RESOLUTION_UNKNOWN:
507       NOTREACHED();
508   }
509   NOTREACHED();
510   callback.Run(SYNC_STATUS_FAILED);
511 }
512
513 void LocalSyncDelegate::HandleManualResolutionCase(
514     const SyncStatusCallback& callback) {
515   if (drive_metadata_.conflicted()) {
516     callback.Run(SYNC_STATUS_HAS_CONFLICT);
517     return;
518   }
519
520   has_drive_metadata_ = true;
521   sync_service_->MarkConflict(
522       url_, &drive_metadata_,
523       base::Bind(&LocalSyncDelegate::DidMarkConflict,
524                  weak_factory_.GetWeakPtr(), callback));
525 }
526
527 void LocalSyncDelegate::DidMarkConflict(
528     const SyncStatusCallback& callback,
529     SyncStatusCode status) {
530   DidApplyLocalChange(callback, google_apis::HTTP_CONFLICT, status);
531 }
532
533 void LocalSyncDelegate::HandleLocalWinCase(
534     const SyncStatusCallback& callback) {
535   util::Log(logging::LOG_VERBOSE, FROM_HERE,
536             "Resolving conflict for local sync: %s: LOCAL WIN",
537             url_.DebugString().c_str());
538
539   DCHECK(!drive_metadata_.resource_id().empty());
540   if (!has_drive_metadata_) {
541     StartOver(callback, SYNC_STATUS_OK);
542     return;
543   }
544
545   ResetMetadataForStartOver(base::Bind(&LocalSyncDelegate::StartOver,
546                                        weak_factory_.GetWeakPtr(), callback));
547 }
548
549 void LocalSyncDelegate::HandleRemoteWinCase(
550     const SyncStatusCallback& callback,
551     SyncFileType remote_file_type) {
552   util::Log(logging::LOG_VERBOSE, FROM_HERE,
553             "Resolving conflict for local sync: %s: REMOTE WIN",
554             url_.DebugString().c_str());
555   ResolveToRemote(callback, remote_file_type);
556 }
557
558 void LocalSyncDelegate::StartOver(const SyncStatusCallback& callback,
559                                   SyncStatusCode status) {
560   if (status != SYNC_STATUS_OK) {
561     callback.Run(status);
562     return;
563   }
564
565   remote_change_handler()->RemoveChangeForURL(url_);
566
567   // Return the control back to the sync service once.
568   callback.Run(SYNC_STATUS_RETRY);
569 }
570
571 SyncStatusCode
572 LocalSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
573     google_apis::GDataErrorCode error) {
574   return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
575 }
576
577 DriveMetadataStore* LocalSyncDelegate::metadata_store() {
578   return sync_service_->metadata_store_.get();
579 }
580
581 APIUtilInterface* LocalSyncDelegate::api_util() {
582   return sync_service_->api_util_.get();
583 }
584
585 RemoteChangeHandler* LocalSyncDelegate::remote_change_handler() {
586   return &sync_service_->remote_change_handler_;
587 }
588
589 ConflictResolutionResolver* LocalSyncDelegate::conflict_resolution_resolver() {
590   return &sync_service_->conflict_resolution_resolver_;
591 }
592
593 }  // namespace drive_backend
594 }  // namespace sync_file_system