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 DCHECK(!local_change_.IsDirectory());
38 operation_ = SYNC_OPERATION_NONE;
41 metadata_store()->ReadEntry(url_, &drive_metadata_) == SYNC_STATUS_OK;
43 if (!has_drive_metadata_)
44 drive_metadata_.set_md5_checksum(std::string());
46 sync_service_->EnsureOriginRootDirectory(
48 base::Bind(&LocalSyncDelegate::DidGetOriginRoot,
49 weak_factory_.GetWeakPtr(),
53 void LocalSyncDelegate::DidGetOriginRoot(
54 const SyncStatusCallback& callback,
55 SyncStatusCode status,
56 const std::string& origin_resource_id) {
57 if (status != SYNC_STATUS_OK) {
62 origin_resource_id_ = origin_resource_id;
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);
69 SyncFileType remote_file_type =
70 has_remote_change_ ? remote_change_.change.file_type() :
72 DriveFileSyncService::DriveMetadataResourceTypeToSyncFileType(
73 drive_metadata_.type())
74 : SYNC_FILE_TYPE_UNKNOWN;
76 DCHECK_EQ(SYNC_OPERATION_NONE, operation_);
77 operation_ = LocalSyncOperationResolver::Resolve(
79 has_remote_change_ ? &remote_change_.change : NULL,
80 has_drive_metadata_ ? &drive_metadata_ : NULL);
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_));
89 case SYNC_OPERATION_ADD_FILE:
90 UploadNewFile(callback);
92 case SYNC_OPERATION_ADD_DIRECTORY:
93 CreateDirectory(callback);
95 case SYNC_OPERATION_UPDATE_FILE:
96 UploadExistingFile(callback);
98 case SYNC_OPERATION_DELETE:
101 case SYNC_OPERATION_NONE:
102 callback.Run(SYNC_STATUS_OK);
104 case SYNC_OPERATION_CONFLICT:
105 HandleConflict(callback);
107 case SYNC_OPERATION_RESOLVE_TO_LOCAL:
108 ResolveToLocal(callback);
110 case SYNC_OPERATION_RESOLVE_TO_REMOTE:
111 ResolveToRemote(callback, remote_file_type);
113 case SYNC_OPERATION_DELETE_METADATA:
114 DeleteMetadata(base::Bind(
115 &LocalSyncDelegate::DidApplyLocalChange,
116 weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS));
118 case SYNC_OPERATION_FAIL: {
119 callback.Run(SYNC_STATUS_FAILED);
124 callback.Run(SYNC_STATUS_FAILED);
127 void LocalSyncDelegate::UploadNewFile(const SyncStatusCallback& callback) {
128 api_util()->UploadNewFile(
131 DriveFileSyncService::PathToTitle(url_.path()),
132 base::Bind(&LocalSyncDelegate::DidUploadNewFile,
133 weak_factory_.GetWeakPtr(), callback));
136 void LocalSyncDelegate::DidUploadNewFile(
137 const SyncStatusCallback& callback,
138 google_apis::GDataErrorCode error,
139 const std::string& resource_id,
140 const std::string& md5) {
142 case google_apis::HTTP_CREATED:
144 resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE,
145 base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
146 weak_factory_.GetWeakPtr(), callback, error));
147 sync_service_->NotifyObserversFileStatusChanged(
149 SYNC_FILE_STATUS_SYNCED,
151 SYNC_DIRECTION_LOCAL_TO_REMOTE);
153 case google_apis::HTTP_CONFLICT:
154 HandleCreationConflict(resource_id, DriveMetadata::RESOURCE_TYPE_FILE,
158 callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
162 void LocalSyncDelegate::CreateDirectory(const SyncStatusCallback& callback) {
164 api_util()->CreateDirectory(
166 DriveFileSyncService::PathToTitle(url_.path()),
167 base::Bind(&LocalSyncDelegate::DidCreateDirectory,
168 weak_factory_.GetWeakPtr(), callback));
171 void LocalSyncDelegate::DidCreateDirectory(
172 const SyncStatusCallback& callback,
173 google_apis::GDataErrorCode error,
174 const std::string& resource_id) {
176 case google_apis::HTTP_SUCCESS:
177 case google_apis::HTTP_CREATED: {
179 resource_id, std::string(), DriveMetadata::RESOURCE_TYPE_FOLDER,
180 base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
181 weak_factory_.GetWeakPtr(), callback, error));
182 sync_service_->NotifyObserversFileStatusChanged(
184 SYNC_FILE_STATUS_SYNCED,
186 SYNC_DIRECTION_LOCAL_TO_REMOTE);
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).
196 callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error));
200 void LocalSyncDelegate::UploadExistingFile(const SyncStatusCallback& callback) {
201 DCHECK(has_drive_metadata_);
202 if (drive_metadata_.resource_id().empty()) {
203 UploadNewFile(callback);
207 api_util()->UploadExistingFile(
208 drive_metadata_.resource_id(),
209 drive_metadata_.md5_checksum(),
211 base::Bind(&LocalSyncDelegate::DidUploadExistingFile,
212 weak_factory_.GetWeakPtr(), callback));
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_);
222 case google_apis::HTTP_SUCCESS:
224 resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE,
225 base::Bind(&LocalSyncDelegate::DidApplyLocalChange,
226 weak_factory_.GetWeakPtr(), callback, error));
227 sync_service_->NotifyObserversFileStatusChanged(
229 SYNC_FILE_STATUS_SYNCED,
231 SYNC_DIRECTION_LOCAL_TO_REMOTE);
233 case google_apis::HTTP_CONFLICT:
234 HandleConflict(callback);
236 case google_apis::HTTP_NOT_MODIFIED:
237 DidApplyLocalChange(callback,
238 google_apis::HTTP_SUCCESS, SYNC_STATUS_OK);
240 case google_apis::HTTP_NOT_FOUND:
241 UploadNewFile(callback);
244 const SyncStatusCode status =
245 GDataErrorCodeToSyncStatusCodeWrapper(error);
246 DCHECK_NE(SYNC_STATUS_OK, status);
247 callback.Run(status);
253 void LocalSyncDelegate::Delete(const SyncStatusCallback& callback) {
254 if (!has_drive_metadata_) {
255 callback.Run(SYNC_STATUS_OK);
259 if (drive_metadata_.resource_id().empty()) {
260 DidDelete(callback, google_apis::HTTP_NOT_FOUND);
264 api_util()->DeleteFile(
265 drive_metadata_.resource_id(),
266 drive_metadata_.md5_checksum(),
267 base::Bind(&LocalSyncDelegate::DidDelete,
268 weak_factory_.GetWeakPtr(), callback));
271 void LocalSyncDelegate::DidDelete(
272 const SyncStatusCallback& callback,
273 google_apis::GDataErrorCode error) {
274 DCHECK(has_drive_metadata_);
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(
284 SYNC_FILE_STATUS_SYNCED,
286 SYNC_DIRECTION_LOCAL_TO_REMOTE);
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(
297 SYNC_FILE_STATUS_SYNCED,
299 SYNC_DIRECTION_LOCAL_TO_REMOTE);
302 const SyncStatusCode status =
303 GDataErrorCodeToSyncStatusCodeWrapper(error);
304 DCHECK_NE(SYNC_STATUS_OK, status);
305 callback.Run(status);
311 void LocalSyncDelegate::DidDeleteMetadataForDeletionConflict(
312 const SyncStatusCallback& callback,
313 SyncStatusCode status) {
314 callback.Run(SYNC_STATUS_OK);
317 void LocalSyncDelegate::ResolveToLocal(const SyncStatusCallback& callback) {
318 if (drive_metadata_.resource_id().empty()) {
319 DidDeleteFileToResolveToLocal(callback, google_apis::HTTP_NOT_FOUND);
323 api_util()->DeleteFile(
324 drive_metadata_.resource_id(),
325 drive_metadata_.md5_checksum(),
327 &LocalSyncDelegate::DidDeleteFileToResolveToLocal,
328 weak_factory_.GetWeakPtr(), callback));
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));
340 DCHECK_NE(SYNC_FILE_TYPE_UNKNOWN, local_metadata_.file_type);
341 if (local_metadata_.file_type == SYNC_FILE_TYPE_FILE) {
342 UploadNewFile(callback);
347 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_.file_type);
348 CreateDirectory(callback);
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());
357 SetMetadataToBeFetched(
358 DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType(
360 base::Bind(&LocalSyncDelegate::DidResolveToRemote,
361 weak_factory_.GetWeakPtr(), callback));
362 // The synced notification will be dispatched when the remote file is
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);
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);
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;
394 if (status == SYNC_STATUS_OK) {
395 remote_change_handler()->RemoveChangeForURL(url_);
396 status = GDataErrorCodeToSyncStatusCodeWrapper(error);
398 callback.Run(status);
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);
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);
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);
436 void LocalSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
437 metadata_store()->DeleteEntry(url_, callback);
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);
460 void LocalSyncDelegate::HandleConflict(const SyncStatusCallback& callback) {
461 DCHECK(!drive_metadata_.resource_id().empty());
462 api_util()->GetResourceEntry(
463 drive_metadata_.resource_id(),
465 &LocalSyncDelegate::DidGetEntryForConflictResolution,
466 weak_factory_.GetWeakPtr(), callback));
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;
476 if (error != google_apis::HTTP_SUCCESS ||
477 entry->updated_time().is_null()) {
478 resolution = CONFLICT_RESOLUTION_LOCAL_WIN;
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;
488 remote_file_type = SYNC_FILE_TYPE_UNKNOWN;
490 resolution = conflict_resolution_resolver()->Resolve(
491 local_file_type, local_modification_time,
492 remote_file_type, remote_modification_time);
495 switch (resolution) {
496 case CONFLICT_RESOLUTION_MARK_CONFLICT:
497 HandleManualResolutionCase(callback);
499 case CONFLICT_RESOLUTION_LOCAL_WIN:
500 HandleLocalWinCase(callback);
502 case CONFLICT_RESOLUTION_REMOTE_WIN:
503 HandleRemoteWinCase(callback, remote_file_type);
505 case CONFLICT_RESOLUTION_UNKNOWN:
509 callback.Run(SYNC_STATUS_FAILED);
512 void LocalSyncDelegate::HandleManualResolutionCase(
513 const SyncStatusCallback& callback) {
514 if (drive_metadata_.conflicted()) {
515 callback.Run(SYNC_STATUS_HAS_CONFLICT);
519 has_drive_metadata_ = true;
520 sync_service_->MarkConflict(
521 url_, &drive_metadata_,
522 base::Bind(&LocalSyncDelegate::DidMarkConflict,
523 weak_factory_.GetWeakPtr(), callback));
526 void LocalSyncDelegate::DidMarkConflict(
527 const SyncStatusCallback& callback,
528 SyncStatusCode status) {
529 DidApplyLocalChange(callback, google_apis::HTTP_CONFLICT, status);
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());
538 DCHECK(!drive_metadata_.resource_id().empty());
539 if (!has_drive_metadata_) {
540 StartOver(callback, SYNC_STATUS_OK);
544 ResetMetadataForStartOver(base::Bind(&LocalSyncDelegate::StartOver,
545 weak_factory_.GetWeakPtr(), callback));
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);
557 void LocalSyncDelegate::StartOver(const SyncStatusCallback& callback,
558 SyncStatusCode status) {
559 if (status != SYNC_STATUS_OK) {
560 callback.Run(status);
564 remote_change_handler()->RemoveChangeForURL(url_);
566 // Return the control back to the sync service once.
567 callback.Run(SYNC_STATUS_RETRY);
571 LocalSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
572 google_apis::GDataErrorCode error) {
573 return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
576 DriveMetadataStore* LocalSyncDelegate::metadata_store() {
577 return sync_service_->metadata_store_.get();
580 APIUtilInterface* LocalSyncDelegate::api_util() {
581 return sync_service_->api_util_.get();
584 RemoteChangeHandler* LocalSyncDelegate::remote_change_handler() {
585 return &sync_service_->remote_change_handler_;
588 ConflictResolutionResolver* LocalSyncDelegate::conflict_resolution_resolver() {
589 return &sync_service_->conflict_resolution_resolver_;
592 } // namespace drive_backend
593 } // namespace sync_file_system