Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend_v1 / remote_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/remote_sync_delegate.h"
6
7 #include "base/file_util.h"
8 #include "chrome/browser/sync_file_system/drive_backend_v1/remote_sync_operation_resolver.h"
9 #include "chrome/browser/sync_file_system/logger.h"
10 #include "chrome/browser/sync_file_system/remote_change_processor.h"
11 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
12
13 using fileapi::FileSystemURL;
14
15 namespace {
16
17 void EmptyStatusCallback(sync_file_system::SyncStatusCode status) {}
18
19 }  // namespace
20
21 namespace sync_file_system {
22 namespace drive_backend {
23
24 RemoteSyncDelegate::RemoteSyncDelegate(
25     DriveFileSyncService* sync_service,
26     const RemoteChange& remote_change)
27     : sync_service_(sync_service),
28       remote_change_(remote_change),
29       sync_action_(SYNC_ACTION_NONE),
30       metadata_updated_(false),
31       clear_local_changes_(true) {
32 }
33
34 RemoteSyncDelegate::~RemoteSyncDelegate() {}
35
36 void RemoteSyncDelegate::Run(const SyncStatusCallback& callback) {
37   util::Log(logging::LOG_VERBOSE, FROM_HERE,
38             "ProcessRemoteChange for %s change:%s",
39             url().DebugString().c_str(),
40             remote_file_change().DebugString().c_str());
41
42   remote_change_processor()->PrepareForProcessRemoteChange(
43       url(),
44       base::Bind(&RemoteSyncDelegate::DidPrepareForProcessRemoteChange,
45                  AsWeakPtr(), callback));
46 }
47
48 void RemoteSyncDelegate::DidPrepareForProcessRemoteChange(
49     const SyncStatusCallback& callback,
50     SyncStatusCode status,
51     const SyncFileMetadata& metadata,
52     const FileChangeList& local_changes) {
53   if (status != SYNC_STATUS_OK) {
54     AbortSync(callback, status);
55     return;
56   }
57
58   local_metadata_ = metadata;
59   status = metadata_store()->ReadEntry(url(), &drive_metadata_);
60   DCHECK(status == SYNC_STATUS_OK || status == SYNC_DATABASE_ERROR_NOT_FOUND);
61
62   bool missing_db_entry = (status != SYNC_STATUS_OK);
63   if (missing_db_entry) {
64     drive_metadata_.set_resource_id(remote_change_.resource_id);
65     drive_metadata_.set_md5_checksum(std::string());
66     drive_metadata_.set_conflicted(false);
67     drive_metadata_.set_to_be_fetched(false);
68   }
69   bool missing_local_file = (metadata.file_type == SYNC_FILE_TYPE_UNKNOWN);
70
71   if (drive_metadata_.resource_id().empty()) {
72     // This (missing_db_entry is false but resource_id is empty) could
73     // happen when the remote file gets deleted (this clears resource_id
74     // in drive_metadata) but then a file is added with the same name.
75     drive_metadata_.set_resource_id(remote_change_.resource_id);
76   }
77
78   SyncOperationType operation =
79       RemoteSyncOperationResolver::Resolve(remote_file_change(),
80                                            local_changes,
81                                            local_metadata_.file_type,
82                                            drive_metadata_.conflicted());
83
84   util::Log(logging::LOG_VERBOSE, FROM_HERE,
85             "ProcessRemoteChange for %s %s%sremote_change: %s ==> %s",
86             url().DebugString().c_str(),
87             drive_metadata_.conflicted() ? " (conflicted)" : " ",
88             missing_local_file ? " (missing local file)" : " ",
89             remote_file_change().DebugString().c_str(),
90             SyncOperationTypeToString(operation));
91   DCHECK_NE(SYNC_OPERATION_FAIL, operation);
92
93   switch (operation) {
94     case SYNC_OPERATION_ADD_FILE:
95     case SYNC_OPERATION_ADD_DIRECTORY:
96       sync_action_ = SYNC_ACTION_ADDED;
97       break;
98     case SYNC_OPERATION_UPDATE_FILE:
99       sync_action_ = SYNC_ACTION_UPDATED;
100       break;
101     case SYNC_OPERATION_DELETE:
102       sync_action_ = SYNC_ACTION_DELETED;
103       break;
104     case SYNC_OPERATION_NONE:
105     case SYNC_OPERATION_DELETE_METADATA:
106       sync_action_ = SYNC_ACTION_NONE;
107       break;
108     default:
109       break;
110   }
111
112   switch (operation) {
113     case SYNC_OPERATION_ADD_FILE:
114     case SYNC_OPERATION_UPDATE_FILE:
115       DownloadFile(callback);
116       return;
117     case SYNC_OPERATION_ADD_DIRECTORY:
118     case SYNC_OPERATION_DELETE:
119       ApplyRemoteChange(callback);
120       return;
121     case SYNC_OPERATION_NONE:
122       CompleteSync(callback, SYNC_STATUS_OK);
123       return;
124     case SYNC_OPERATION_CONFLICT:
125       HandleConflict(callback, remote_file_change().file_type());
126       return;
127     case SYNC_OPERATION_RESOLVE_TO_LOCAL:
128       ResolveToLocal(callback);
129       return;
130     case SYNC_OPERATION_RESOLVE_TO_REMOTE:
131       ResolveToRemote(callback);
132       return;
133     case SYNC_OPERATION_DELETE_METADATA:
134       if (missing_db_entry)
135         CompleteSync(callback, SYNC_STATUS_OK);
136       else
137         DeleteMetadata(callback);
138       return;
139     case SYNC_OPERATION_FAIL:
140       AbortSync(callback, SYNC_STATUS_FAILED);
141       return;
142   }
143   NOTREACHED();
144   AbortSync(callback, SYNC_STATUS_FAILED);
145 }
146
147 void RemoteSyncDelegate::ApplyRemoteChange(const SyncStatusCallback& callback) {
148   remote_change_processor()->ApplyRemoteChange(
149       remote_file_change(), temporary_file_.path(), url(),
150       base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange, AsWeakPtr(),
151                  callback));
152 }
153
154 void RemoteSyncDelegate::DidApplyRemoteChange(
155     const SyncStatusCallback& callback,
156     SyncStatusCode status) {
157   if (status != SYNC_STATUS_OK) {
158     AbortSync(callback, status);
159     return;
160   }
161
162   if (remote_file_change().IsDelete()) {
163     DeleteMetadata(callback);
164     return;
165   }
166
167   drive_metadata_.set_resource_id(remote_change_.resource_id);
168   drive_metadata_.set_conflicted(false);
169   if (remote_file_change().IsFile()) {
170     drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FILE);
171   } else {
172     NOTREACHED();
173     drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FOLDER);
174   }
175
176   metadata_store()->UpdateEntry(
177       url(), drive_metadata_,
178       base::Bind(&RemoteSyncDelegate::CompleteSync,
179                  AsWeakPtr(), callback));
180 }
181
182 void RemoteSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
183   metadata_store()->DeleteEntry(
184       url(),
185       base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
186 }
187
188 void RemoteSyncDelegate::DownloadFile(const SyncStatusCallback& callback) {
189   // We should not use the md5 in metadata for FETCH type to avoid the download
190   // finishes due to NOT_MODIFIED.
191   std::string md5_checksum;
192   if (!drive_metadata_.to_be_fetched())
193     md5_checksum = drive_metadata_.md5_checksum();
194
195   api_util()->DownloadFile(
196       remote_change_.resource_id,
197       md5_checksum,
198       base::Bind(&RemoteSyncDelegate::DidDownloadFile,
199                  AsWeakPtr(),
200                  callback));
201 }
202
203 void RemoteSyncDelegate::DidDownloadFile(
204     const SyncStatusCallback& callback,
205     google_apis::GDataErrorCode error,
206     const std::string& md5_checksum,
207     int64 file_size,
208     const base::Time& updated_time,
209     webkit_blob::ScopedFile downloaded_file) {
210   if (error == google_apis::HTTP_NOT_MODIFIED) {
211     sync_action_ = SYNC_ACTION_NONE;
212     DidApplyRemoteChange(callback, SYNC_STATUS_OK);
213     return;
214   }
215
216   // File may be deleted. If this was for new file it's ok, if this was
217   // for existing file we'll process the delete change later.
218   if (error == google_apis::HTTP_NOT_FOUND) {
219     sync_action_ = SYNC_ACTION_NONE;
220     DidApplyRemoteChange(callback, SYNC_STATUS_OK);
221     return;
222   }
223
224   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
225   if (status != SYNC_STATUS_OK) {
226     AbortSync(callback, status);
227     return;
228   }
229
230   temporary_file_ = downloaded_file.Pass();
231   drive_metadata_.set_md5_checksum(md5_checksum);
232   remote_change_processor()->ApplyRemoteChange(
233       remote_file_change(), temporary_file_.path(), url(),
234       base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange,
235                  AsWeakPtr(), callback));
236 }
237
238 void RemoteSyncDelegate::HandleConflict(
239     const SyncStatusCallback& callback,
240     SyncFileType remote_file_type) {
241   ConflictResolution resolution = conflict_resolution_resolver()->Resolve(
242       local_metadata_.file_type,
243       local_metadata_.last_modified,
244       remote_file_type,
245       remote_change_.updated_time);
246
247   switch (resolution) {
248     case CONFLICT_RESOLUTION_LOCAL_WIN:
249       HandleLocalWin(callback);
250       return;
251     case CONFLICT_RESOLUTION_REMOTE_WIN:
252       HandleRemoteWin(callback, remote_file_type);
253       return;
254     case CONFLICT_RESOLUTION_MARK_CONFLICT:
255       HandleManualResolutionCase(callback);
256       return;
257     case CONFLICT_RESOLUTION_UNKNOWN:
258       // Get remote file time and call this method again.
259       api_util()->GetResourceEntry(
260           remote_change_.resource_id,
261           base::Bind(
262               &RemoteSyncDelegate::DidGetEntryForConflictResolution,
263               AsWeakPtr(), callback));
264       return;
265   }
266   NOTREACHED();
267   AbortSync(callback, SYNC_STATUS_FAILED);
268 }
269
270 void RemoteSyncDelegate::HandleLocalWin(
271     const SyncStatusCallback& callback) {
272   util::Log(logging::LOG_VERBOSE, FROM_HERE,
273             "Resolving conflict for remote sync: %s: LOCAL WIN",
274             url().DebugString().c_str());
275   ResolveToLocal(callback);
276 }
277
278 void RemoteSyncDelegate::HandleRemoteWin(
279     const SyncStatusCallback& callback,
280     SyncFileType remote_file_type) {
281   // Make sure we reset the conflict flag and start over the remote sync
282   // with empty local changes.
283   util::Log(logging::LOG_VERBOSE, FROM_HERE,
284             "Resolving conflict for remote sync: %s: REMOTE WIN",
285             url().DebugString().c_str());
286
287   drive_metadata_.set_conflicted(false);
288   drive_metadata_.set_to_be_fetched(false);
289   drive_metadata_.set_type(
290       DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
291           remote_file_type));
292   metadata_store()->UpdateEntry(
293       url(), drive_metadata_,
294       base::Bind(&RemoteSyncDelegate::StartOver, AsWeakPtr(), callback));
295 }
296
297 void RemoteSyncDelegate::HandleManualResolutionCase(
298     const SyncStatusCallback& callback) {
299   sync_action_ = SYNC_ACTION_NONE;
300   sync_service_->MarkConflict(
301       url(), &drive_metadata_,
302       base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
303 }
304
305 void RemoteSyncDelegate::DidGetEntryForConflictResolution(
306     const SyncStatusCallback& callback,
307     google_apis::GDataErrorCode error,
308     scoped_ptr<google_apis::ResourceEntry> entry) {
309   SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error);
310   if (status != SYNC_STATUS_OK || entry->updated_time().is_null()) {
311     HandleLocalWin(callback);
312     return;
313   }
314
315   SyncFileType file_type = SYNC_FILE_TYPE_UNKNOWN;
316   if (entry->is_file())
317     file_type = SYNC_FILE_TYPE_FILE;
318   if (entry->is_folder())
319     file_type = SYNC_FILE_TYPE_DIRECTORY;
320
321   remote_change_.updated_time = entry->updated_time();
322   HandleConflict(callback, file_type);
323 }
324
325 void RemoteSyncDelegate::ResolveToLocal(
326     const SyncStatusCallback& callback) {
327   sync_action_ = SYNC_ACTION_NONE;
328   clear_local_changes_ = false;
329
330   // Re-add a fake local change to resolve it later in next LocalSync.
331   remote_change_processor()->RecordFakeLocalChange(
332       url(),
333       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
334                  local_metadata_.file_type),
335       base::Bind(&RemoteSyncDelegate::DidResolveToLocal,
336                  AsWeakPtr(), callback));
337 }
338
339 void RemoteSyncDelegate::DidResolveToLocal(
340     const SyncStatusCallback& callback,
341     SyncStatusCode status) {
342   if (status != SYNC_STATUS_OK) {
343     DCHECK_NE(SYNC_STATUS_HAS_CONFLICT, status);
344     AbortSync(callback, status);
345     return;
346   }
347
348   if (remote_file_change().IsDelete()) {
349     metadata_store()->DeleteEntry(
350         url(),
351         base::Bind(&RemoteSyncDelegate::CompleteSync,
352                    AsWeakPtr(), callback));
353   } else {
354     DCHECK(!remote_change_.resource_id.empty());
355     drive_metadata_.set_resource_id(remote_change_.resource_id);
356     drive_metadata_.set_conflicted(false);
357     drive_metadata_.set_to_be_fetched(false);
358     drive_metadata_.set_md5_checksum(std::string());
359     metadata_store()->UpdateEntry(
360         url(), drive_metadata_,
361         base::Bind(&RemoteSyncDelegate::CompleteSync,
362                    AsWeakPtr(), callback));
363   }
364 }
365
366 void RemoteSyncDelegate::ResolveToRemote(
367     const SyncStatusCallback& callback) {
368   drive_metadata_.set_conflicted(false);
369   drive_metadata_.set_to_be_fetched(true);
370   metadata_store()->UpdateEntry(
371       url(), drive_metadata_,
372       base::Bind(&RemoteSyncDelegate::DidResolveToRemote,
373                  AsWeakPtr(), callback));
374 }
375
376 void RemoteSyncDelegate::DidResolveToRemote(
377     const SyncStatusCallback& callback,
378     SyncStatusCode status) {
379   if (status != SYNC_STATUS_OK) {
380     AbortSync(callback, status);
381     return;
382   }
383
384   sync_action_ = SYNC_ACTION_ADDED;
385   if (remote_file_change().file_type() == SYNC_FILE_TYPE_FILE) {
386     DownloadFile(callback);
387     return;
388   }
389
390   // ApplyRemoteChange should replace any existing local file or
391   // directory with remote_change_.
392   ApplyRemoteChange(callback);
393 }
394
395 void RemoteSyncDelegate::StartOver(
396     const SyncStatusCallback& callback,
397     SyncStatusCode status) {
398   DidPrepareForProcessRemoteChange(
399       callback, status, local_metadata_, FileChangeList());
400 }
401
402 void RemoteSyncDelegate::CompleteSync(
403     const SyncStatusCallback& callback,
404     SyncStatusCode status) {
405   if (status != SYNC_STATUS_OK) {
406     AbortSync(callback, status);
407     return;
408   }
409
410   sync_service_->RemoveRemoteChange(url());
411
412   if (drive_metadata_.to_be_fetched()) {
413     // Clear |to_be_fetched| flag since we completed fetching the remote change
414     // and applying it to the local file.
415     DCHECK(!drive_metadata_.conflicted());
416     drive_metadata_.set_conflicted(false);
417     drive_metadata_.set_to_be_fetched(false);
418     metadata_store()->UpdateEntry(url(), drive_metadata_,
419                                   base::Bind(&EmptyStatusCallback));
420   }
421
422   if (remote_change_.changestamp > 0) {
423     DCHECK(metadata_store()->IsIncrementalSyncOrigin(url().origin()));
424     metadata_store()->SetLargestChangeStamp(
425         remote_change_.changestamp,
426         base::Bind(&RemoteSyncDelegate::DidFinish, AsWeakPtr(), callback));
427     return;
428   }
429
430   if (drive_metadata_.conflicted())
431     status = SYNC_STATUS_HAS_CONFLICT;
432
433   DidFinish(callback, status);
434 }
435
436 void RemoteSyncDelegate::AbortSync(
437     const SyncStatusCallback& callback,
438     SyncStatusCode status) {
439   clear_local_changes_ = false;
440   DidFinish(callback, status);
441 }
442
443 void RemoteSyncDelegate::DidFinish(
444     const SyncStatusCallback& callback,
445     SyncStatusCode status) {
446   remote_change_processor()->FinalizeRemoteSync(
447       url(), clear_local_changes_,
448       base::Bind(&RemoteSyncDelegate::DispatchCallbackAfterDidFinish,
449                  AsWeakPtr(), callback, status));
450 }
451
452 void RemoteSyncDelegate::DispatchCallbackAfterDidFinish(
453     const SyncStatusCallback& callback,
454     SyncStatusCode status) {
455   if (status == SYNC_STATUS_OK && sync_action_ != SYNC_ACTION_NONE) {
456     sync_service_->NotifyObserversFileStatusChanged(
457         url(),
458         SYNC_FILE_STATUS_SYNCED,
459         sync_action_,
460         SYNC_DIRECTION_REMOTE_TO_LOCAL);
461   }
462
463   callback.Run(status);
464 }
465
466 SyncStatusCode RemoteSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
467     google_apis::GDataErrorCode error) {
468   return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
469 }
470
471 DriveMetadataStore* RemoteSyncDelegate::metadata_store() {
472   return sync_service_->metadata_store_.get();
473 }
474
475 APIUtilInterface* RemoteSyncDelegate::api_util() {
476   return sync_service_->api_util_.get();
477 }
478
479 RemoteChangeHandler* RemoteSyncDelegate::remote_change_handler() {
480   return &sync_service_->remote_change_handler_;
481 }
482
483 RemoteChangeProcessor* RemoteSyncDelegate::remote_change_processor() {
484   return sync_service_->remote_change_processor_;
485 }
486
487 ConflictResolutionResolver* RemoteSyncDelegate::conflict_resolution_resolver() {
488   return &sync_service_->conflict_resolution_resolver_;
489 }
490
491 }  // namespace drive_backend
492 }  // namespace sync_file_system