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