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_v1/remote_sync_delegate.h"
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"
13 using fileapi::FileSystemURL
;
17 void EmptyStatusCallback(sync_file_system::SyncStatusCode status
) {}
21 namespace sync_file_system
{
22 namespace drive_backend
{
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) {
34 RemoteSyncDelegate::~RemoteSyncDelegate() {}
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());
42 remote_change_processor()->PrepareForProcessRemoteChange(
44 base::Bind(&RemoteSyncDelegate::DidPrepareForProcessRemoteChange
,
45 AsWeakPtr(), callback
));
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
);
58 local_metadata_
= metadata
;
59 status
= metadata_store()->ReadEntry(url(), &drive_metadata_
);
60 DCHECK(status
== SYNC_STATUS_OK
|| status
== SYNC_DATABASE_ERROR_NOT_FOUND
);
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);
69 bool missing_local_file
= (metadata
.file_type
== SYNC_FILE_TYPE_UNKNOWN
);
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
);
78 SyncOperationType operation
=
79 RemoteSyncOperationResolver::Resolve(remote_file_change(),
81 local_metadata_
.file_type
,
82 drive_metadata_
.conflicted());
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
);
94 case SYNC_OPERATION_ADD_FILE
:
95 case SYNC_OPERATION_ADD_DIRECTORY
:
96 sync_action_
= SYNC_ACTION_ADDED
;
98 case SYNC_OPERATION_UPDATE_FILE
:
99 sync_action_
= SYNC_ACTION_UPDATED
;
101 case SYNC_OPERATION_DELETE
:
102 sync_action_
= SYNC_ACTION_DELETED
;
104 case SYNC_OPERATION_NONE
:
105 case SYNC_OPERATION_DELETE_METADATA
:
106 sync_action_
= SYNC_ACTION_NONE
;
113 case SYNC_OPERATION_ADD_FILE
:
114 case SYNC_OPERATION_UPDATE_FILE
:
115 DownloadFile(callback
);
117 case SYNC_OPERATION_ADD_DIRECTORY
:
118 case SYNC_OPERATION_DELETE
:
119 ApplyRemoteChange(callback
);
121 case SYNC_OPERATION_NONE
:
122 CompleteSync(callback
, SYNC_STATUS_OK
);
124 case SYNC_OPERATION_CONFLICT
:
125 HandleConflict(callback
, remote_file_change().file_type());
127 case SYNC_OPERATION_RESOLVE_TO_LOCAL
:
128 ResolveToLocal(callback
);
130 case SYNC_OPERATION_RESOLVE_TO_REMOTE
:
131 ResolveToRemote(callback
);
133 case SYNC_OPERATION_DELETE_METADATA
:
134 if (missing_db_entry
)
135 CompleteSync(callback
, SYNC_STATUS_OK
);
137 DeleteMetadata(callback
);
139 case SYNC_OPERATION_FAIL
:
140 AbortSync(callback
, SYNC_STATUS_FAILED
);
144 AbortSync(callback
, SYNC_STATUS_FAILED
);
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(),
154 void RemoteSyncDelegate::DidApplyRemoteChange(
155 const SyncStatusCallback
& callback
,
156 SyncStatusCode status
) {
157 if (status
!= SYNC_STATUS_OK
) {
158 AbortSync(callback
, status
);
162 if (remote_file_change().IsDelete()) {
163 DeleteMetadata(callback
);
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
);
172 DCHECK(IsSyncFSDirectoryOperationEnabled());
173 drive_metadata_
.set_type(DriveMetadata::RESOURCE_TYPE_FOLDER
);
176 metadata_store()->UpdateEntry(
177 url(), drive_metadata_
,
178 base::Bind(&RemoteSyncDelegate::CompleteSync
,
179 AsWeakPtr(), callback
));
182 void RemoteSyncDelegate::DeleteMetadata(const SyncStatusCallback
& callback
) {
183 metadata_store()->DeleteEntry(
185 base::Bind(&RemoteSyncDelegate::CompleteSync
, AsWeakPtr(), callback
));
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();
195 api_util()->DownloadFile(
196 remote_change_
.resource_id
,
198 base::Bind(&RemoteSyncDelegate::DidDownloadFile
,
203 void RemoteSyncDelegate::DidDownloadFile(
204 const SyncStatusCallback
& callback
,
205 google_apis::GDataErrorCode error
,
206 const std::string
& md5_checksum
,
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
);
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
);
224 SyncStatusCode status
= GDataErrorCodeToSyncStatusCodeWrapper(error
);
225 if (status
!= SYNC_STATUS_OK
) {
226 AbortSync(callback
, status
);
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
));
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
,
245 remote_change_
.updated_time
);
247 switch (resolution
) {
248 case CONFLICT_RESOLUTION_LOCAL_WIN
:
249 HandleLocalWin(callback
);
251 case CONFLICT_RESOLUTION_REMOTE_WIN
:
252 HandleRemoteWin(callback
, remote_file_type
);
254 case CONFLICT_RESOLUTION_MARK_CONFLICT
:
255 HandleManualResolutionCase(callback
);
257 case CONFLICT_RESOLUTION_UNKNOWN
:
258 // Get remote file time and call this method again.
259 api_util()->GetResourceEntry(
260 remote_change_
.resource_id
,
262 &RemoteSyncDelegate::DidGetEntryForConflictResolution
,
263 AsWeakPtr(), callback
));
267 AbortSync(callback
, SYNC_STATUS_FAILED
);
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
);
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());
287 drive_metadata_
.set_conflicted(false);
288 drive_metadata_
.set_to_be_fetched(false);
289 drive_metadata_
.set_type(
290 DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
292 metadata_store()->UpdateEntry(
293 url(), drive_metadata_
,
294 base::Bind(&RemoteSyncDelegate::StartOver
, AsWeakPtr(), callback
));
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
));
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
);
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
;
321 remote_change_
.updated_time
= entry
->updated_time();
322 HandleConflict(callback
, file_type
);
325 void RemoteSyncDelegate::ResolveToLocal(
326 const SyncStatusCallback
& callback
) {
327 sync_action_
= SYNC_ACTION_NONE
;
328 clear_local_changes_
= false;
330 // Re-add a fake local change to resolve it later in next LocalSync.
331 remote_change_processor()->RecordFakeLocalChange(
333 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
334 local_metadata_
.file_type
),
335 base::Bind(&RemoteSyncDelegate::DidResolveToLocal
,
336 AsWeakPtr(), callback
));
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
);
348 if (remote_file_change().IsDelete()) {
349 metadata_store()->DeleteEntry(
351 base::Bind(&RemoteSyncDelegate::CompleteSync
,
352 AsWeakPtr(), callback
));
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
));
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
));
376 void RemoteSyncDelegate::DidResolveToRemote(
377 const SyncStatusCallback
& callback
,
378 SyncStatusCode status
) {
379 if (status
!= SYNC_STATUS_OK
) {
380 AbortSync(callback
, status
);
384 sync_action_
= SYNC_ACTION_ADDED
;
385 if (remote_file_change().file_type() == SYNC_FILE_TYPE_FILE
) {
386 DownloadFile(callback
);
390 // ApplyRemoteChange should replace any existing local file or
391 // directory with remote_change_.
392 ApplyRemoteChange(callback
);
395 void RemoteSyncDelegate::StartOver(
396 const SyncStatusCallback
& callback
,
397 SyncStatusCode status
) {
398 DidPrepareForProcessRemoteChange(
399 callback
, status
, local_metadata_
, FileChangeList());
402 void RemoteSyncDelegate::CompleteSync(
403 const SyncStatusCallback
& callback
,
404 SyncStatusCode status
) {
405 if (status
!= SYNC_STATUS_OK
) {
406 AbortSync(callback
, status
);
410 sync_service_
->RemoveRemoteChange(url());
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
));
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
));
430 if (drive_metadata_
.conflicted())
431 status
= SYNC_STATUS_HAS_CONFLICT
;
433 DidFinish(callback
, status
);
436 void RemoteSyncDelegate::AbortSync(
437 const SyncStatusCallback
& callback
,
438 SyncStatusCode status
) {
439 clear_local_changes_
= false;
440 DidFinish(callback
, status
);
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
));
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(
458 SYNC_FILE_STATUS_SYNCED
,
460 SYNC_DIRECTION_REMOTE_TO_LOCAL
);
463 callback
.Run(status
);
466 SyncStatusCode
RemoteSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
467 google_apis::GDataErrorCode error
) {
468 return sync_service_
->GDataErrorCodeToSyncStatusCodeWrapper(error
);
471 DriveMetadataStore
* RemoteSyncDelegate::metadata_store() {
472 return sync_service_
->metadata_store_
.get();
475 APIUtilInterface
* RemoteSyncDelegate::api_util() {
476 return sync_service_
->api_util_
.get();
479 RemoteChangeHandler
* RemoteSyncDelegate::remote_change_handler() {
480 return &sync_service_
->remote_change_handler_
;
483 RemoteChangeProcessor
* RemoteSyncDelegate::remote_change_processor() {
484 return sync_service_
->remote_change_processor_
;
487 ConflictResolutionResolver
* RemoteSyncDelegate::conflict_resolution_resolver() {
488 return &sync_service_
->conflict_resolution_resolver_
;
491 } // namespace drive_backend
492 } // namespace sync_file_system