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/local_sync_delegate.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"
15 namespace sync_file_system
{
16 namespace drive_backend
{
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
),
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) {}
34 LocalSyncDelegate::~LocalSyncDelegate() {}
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
;
42 metadata_store()->ReadEntry(url_
, &drive_metadata_
) == SYNC_STATUS_OK
;
44 if (!has_drive_metadata_
)
45 drive_metadata_
.set_md5_checksum(std::string());
47 sync_service_
->EnsureOriginRootDirectory(
49 base::Bind(&LocalSyncDelegate::DidGetOriginRoot
,
50 weak_factory_
.GetWeakPtr(),
54 void LocalSyncDelegate::DidGetOriginRoot(
55 const SyncStatusCallback
& callback
,
56 SyncStatusCode status
,
57 const std::string
& origin_resource_id
) {
58 if (status
!= SYNC_STATUS_OK
) {
63 origin_resource_id_
= origin_resource_id
;
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
);
70 SyncFileType remote_file_type
=
71 has_remote_change_
? remote_change_
.change
.file_type() :
73 DriveFileSyncService::DriveMetadataResourceTypeToSyncFileType(
74 drive_metadata_
.type())
75 : SYNC_FILE_TYPE_UNKNOWN
;
77 DCHECK_EQ(SYNC_OPERATION_NONE
, operation_
);
78 operation_
= LocalSyncOperationResolver::Resolve(
80 has_remote_change_
? &remote_change_
.change
: NULL
,
81 has_drive_metadata_
? &drive_metadata_
: NULL
);
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_
));
90 case SYNC_OPERATION_ADD_FILE
:
91 UploadNewFile(callback
);
93 case SYNC_OPERATION_ADD_DIRECTORY
:
94 CreateDirectory(callback
);
96 case SYNC_OPERATION_UPDATE_FILE
:
97 UploadExistingFile(callback
);
99 case SYNC_OPERATION_DELETE
:
102 case SYNC_OPERATION_NONE
:
103 callback
.Run(SYNC_STATUS_OK
);
105 case SYNC_OPERATION_CONFLICT
:
106 HandleConflict(callback
);
108 case SYNC_OPERATION_RESOLVE_TO_LOCAL
:
109 ResolveToLocal(callback
);
111 case SYNC_OPERATION_RESOLVE_TO_REMOTE
:
112 ResolveToRemote(callback
, remote_file_type
);
114 case SYNC_OPERATION_DELETE_METADATA
:
115 DeleteMetadata(base::Bind(
116 &LocalSyncDelegate::DidApplyLocalChange
,
117 weak_factory_
.GetWeakPtr(), callback
, google_apis::HTTP_SUCCESS
));
119 case SYNC_OPERATION_FAIL
: {
120 callback
.Run(SYNC_STATUS_FAILED
);
125 callback
.Run(SYNC_STATUS_FAILED
);
128 void LocalSyncDelegate::UploadNewFile(const SyncStatusCallback
& callback
) {
129 api_util()->UploadNewFile(
132 DriveFileSyncService::PathToTitle(url_
.path()),
133 base::Bind(&LocalSyncDelegate::DidUploadNewFile
,
134 weak_factory_
.GetWeakPtr(), callback
));
137 void LocalSyncDelegate::DidUploadNewFile(
138 const SyncStatusCallback
& callback
,
139 google_apis::GDataErrorCode error
,
140 const std::string
& resource_id
,
141 const std::string
& md5
) {
143 case google_apis::HTTP_CREATED
:
145 resource_id
, md5
, DriveMetadata::RESOURCE_TYPE_FILE
,
146 base::Bind(&LocalSyncDelegate::DidApplyLocalChange
,
147 weak_factory_
.GetWeakPtr(), callback
, error
));
148 sync_service_
->NotifyObserversFileStatusChanged(
150 SYNC_FILE_STATUS_SYNCED
,
152 SYNC_DIRECTION_LOCAL_TO_REMOTE
);
154 case google_apis::HTTP_CONFLICT
:
155 HandleCreationConflict(resource_id
, DriveMetadata::RESOURCE_TYPE_FILE
,
159 callback
.Run(GDataErrorCodeToSyncStatusCodeWrapper(error
));
163 void LocalSyncDelegate::CreateDirectory(const SyncStatusCallback
& callback
) {
164 DCHECK(IsSyncFSDirectoryOperationEnabled());
165 api_util()->CreateDirectory(
167 DriveFileSyncService::PathToTitle(url_
.path()),
168 base::Bind(&LocalSyncDelegate::DidCreateDirectory
,
169 weak_factory_
.GetWeakPtr(), callback
));
172 void LocalSyncDelegate::DidCreateDirectory(
173 const SyncStatusCallback
& callback
,
174 google_apis::GDataErrorCode error
,
175 const std::string
& resource_id
) {
177 case google_apis::HTTP_SUCCESS
:
178 case google_apis::HTTP_CREATED
: {
180 resource_id
, std::string(), DriveMetadata::RESOURCE_TYPE_FOLDER
,
181 base::Bind(&LocalSyncDelegate::DidApplyLocalChange
,
182 weak_factory_
.GetWeakPtr(), callback
, error
));
183 sync_service_
->NotifyObserversFileStatusChanged(
185 SYNC_FILE_STATUS_SYNCED
,
187 SYNC_DIRECTION_LOCAL_TO_REMOTE
);
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).
197 callback
.Run(GDataErrorCodeToSyncStatusCodeWrapper(error
));
201 void LocalSyncDelegate::UploadExistingFile(const SyncStatusCallback
& callback
) {
202 DCHECK(has_drive_metadata_
);
203 if (drive_metadata_
.resource_id().empty()) {
204 UploadNewFile(callback
);
208 api_util()->UploadExistingFile(
209 drive_metadata_
.resource_id(),
210 drive_metadata_
.md5_checksum(),
212 base::Bind(&LocalSyncDelegate::DidUploadExistingFile
,
213 weak_factory_
.GetWeakPtr(), callback
));
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_
);
223 case google_apis::HTTP_SUCCESS
:
225 resource_id
, md5
, DriveMetadata::RESOURCE_TYPE_FILE
,
226 base::Bind(&LocalSyncDelegate::DidApplyLocalChange
,
227 weak_factory_
.GetWeakPtr(), callback
, error
));
228 sync_service_
->NotifyObserversFileStatusChanged(
230 SYNC_FILE_STATUS_SYNCED
,
232 SYNC_DIRECTION_LOCAL_TO_REMOTE
);
234 case google_apis::HTTP_CONFLICT
:
235 HandleConflict(callback
);
237 case google_apis::HTTP_NOT_MODIFIED
:
238 DidApplyLocalChange(callback
,
239 google_apis::HTTP_SUCCESS
, SYNC_STATUS_OK
);
241 case google_apis::HTTP_NOT_FOUND
:
242 UploadNewFile(callback
);
245 const SyncStatusCode status
=
246 GDataErrorCodeToSyncStatusCodeWrapper(error
);
247 DCHECK_NE(SYNC_STATUS_OK
, status
);
248 callback
.Run(status
);
254 void LocalSyncDelegate::Delete(const SyncStatusCallback
& callback
) {
255 if (!has_drive_metadata_
) {
256 callback
.Run(SYNC_STATUS_OK
);
260 if (drive_metadata_
.resource_id().empty()) {
261 DidDelete(callback
, google_apis::HTTP_NOT_FOUND
);
265 api_util()->DeleteFile(
266 drive_metadata_
.resource_id(),
267 drive_metadata_
.md5_checksum(),
268 base::Bind(&LocalSyncDelegate::DidDelete
,
269 weak_factory_
.GetWeakPtr(), callback
));
272 void LocalSyncDelegate::DidDelete(
273 const SyncStatusCallback
& callback
,
274 google_apis::GDataErrorCode error
) {
275 DCHECK(has_drive_metadata_
);
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(
285 SYNC_FILE_STATUS_SYNCED
,
287 SYNC_DIRECTION_LOCAL_TO_REMOTE
);
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(
298 SYNC_FILE_STATUS_SYNCED
,
300 SYNC_DIRECTION_LOCAL_TO_REMOTE
);
303 const SyncStatusCode status
=
304 GDataErrorCodeToSyncStatusCodeWrapper(error
);
305 DCHECK_NE(SYNC_STATUS_OK
, status
);
306 callback
.Run(status
);
312 void LocalSyncDelegate::DidDeleteMetadataForDeletionConflict(
313 const SyncStatusCallback
& callback
,
314 SyncStatusCode status
) {
315 callback
.Run(SYNC_STATUS_OK
);
318 void LocalSyncDelegate::ResolveToLocal(const SyncStatusCallback
& callback
) {
319 if (drive_metadata_
.resource_id().empty()) {
320 DidDeleteFileToResolveToLocal(callback
, google_apis::HTTP_NOT_FOUND
);
324 api_util()->DeleteFile(
325 drive_metadata_
.resource_id(),
326 drive_metadata_
.md5_checksum(),
328 &LocalSyncDelegate::DidDeleteFileToResolveToLocal
,
329 weak_factory_
.GetWeakPtr(), callback
));
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
));
341 DCHECK_NE(SYNC_FILE_TYPE_UNKNOWN
, local_metadata_
.file_type
);
342 if (local_metadata_
.file_type
== SYNC_FILE_TYPE_FILE
) {
343 UploadNewFile(callback
);
347 DCHECK(IsSyncFSDirectoryOperationEnabled());
348 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY
, local_metadata_
.file_type
);
349 CreateDirectory(callback
);
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());
358 SetMetadataToBeFetched(
359 DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
361 base::Bind(&LocalSyncDelegate::DidResolveToRemote
,
362 weak_factory_
.GetWeakPtr(), callback
));
363 // The synced notification will be dispatched when the remote file is
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
);
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
);
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
;
395 if (status
== SYNC_STATUS_OK
) {
396 remote_change_handler()->RemoveChangeForURL(url_
);
397 status
= GDataErrorCodeToSyncStatusCodeWrapper(error
);
399 callback
.Run(status
);
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
);
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
);
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
);
437 void LocalSyncDelegate::DeleteMetadata(const SyncStatusCallback
& callback
) {
438 metadata_store()->DeleteEntry(url_
, callback
);
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
);
461 void LocalSyncDelegate::HandleConflict(const SyncStatusCallback
& callback
) {
462 DCHECK(!drive_metadata_
.resource_id().empty());
463 api_util()->GetResourceEntry(
464 drive_metadata_
.resource_id(),
466 &LocalSyncDelegate::DidGetEntryForConflictResolution
,
467 weak_factory_
.GetWeakPtr(), callback
));
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
;
477 if (error
!= google_apis::HTTP_SUCCESS
||
478 entry
->updated_time().is_null()) {
479 resolution
= CONFLICT_RESOLUTION_LOCAL_WIN
;
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
;
489 remote_file_type
= SYNC_FILE_TYPE_UNKNOWN
;
491 resolution
= conflict_resolution_resolver()->Resolve(
492 local_file_type
, local_modification_time
,
493 remote_file_type
, remote_modification_time
);
496 switch (resolution
) {
497 case CONFLICT_RESOLUTION_MARK_CONFLICT
:
498 HandleManualResolutionCase(callback
);
500 case CONFLICT_RESOLUTION_LOCAL_WIN
:
501 HandleLocalWinCase(callback
);
503 case CONFLICT_RESOLUTION_REMOTE_WIN
:
504 HandleRemoteWinCase(callback
, remote_file_type
);
506 case CONFLICT_RESOLUTION_UNKNOWN
:
510 callback
.Run(SYNC_STATUS_FAILED
);
513 void LocalSyncDelegate::HandleManualResolutionCase(
514 const SyncStatusCallback
& callback
) {
515 if (drive_metadata_
.conflicted()) {
516 callback
.Run(SYNC_STATUS_HAS_CONFLICT
);
520 has_drive_metadata_
= true;
521 sync_service_
->MarkConflict(
522 url_
, &drive_metadata_
,
523 base::Bind(&LocalSyncDelegate::DidMarkConflict
,
524 weak_factory_
.GetWeakPtr(), callback
));
527 void LocalSyncDelegate::DidMarkConflict(
528 const SyncStatusCallback
& callback
,
529 SyncStatusCode status
) {
530 DidApplyLocalChange(callback
, google_apis::HTTP_CONFLICT
, status
);
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());
539 DCHECK(!drive_metadata_
.resource_id().empty());
540 if (!has_drive_metadata_
) {
541 StartOver(callback
, SYNC_STATUS_OK
);
545 ResetMetadataForStartOver(base::Bind(&LocalSyncDelegate::StartOver
,
546 weak_factory_
.GetWeakPtr(), callback
));
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
);
558 void LocalSyncDelegate::StartOver(const SyncStatusCallback
& callback
,
559 SyncStatusCode status
) {
560 if (status
!= SYNC_STATUS_OK
) {
561 callback
.Run(status
);
565 remote_change_handler()->RemoveChangeForURL(url_
);
567 // Return the control back to the sync service once.
568 callback
.Run(SYNC_STATUS_RETRY
);
572 LocalSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
573 google_apis::GDataErrorCode error
) {
574 return sync_service_
->GDataErrorCodeToSyncStatusCodeWrapper(error
);
577 DriveMetadataStore
* LocalSyncDelegate::metadata_store() {
578 return sync_service_
->metadata_store_
.get();
581 APIUtilInterface
* LocalSyncDelegate::api_util() {
582 return sync_service_
->api_util_
.get();
585 RemoteChangeHandler
* LocalSyncDelegate::remote_change_handler() {
586 return &sync_service_
->remote_change_handler_
;
589 ConflictResolutionResolver
* LocalSyncDelegate::conflict_resolution_resolver() {
590 return &sync_service_
->conflict_resolution_resolver_
;
593 } // namespace drive_backend
594 } // namespace sync_file_system