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