Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / conflict_resolver.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/conflict_resolver.h"
6
7 #include "base/callback.h"
8 #include "base/format_macros.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "chrome/browser/drive/drive_api_util.h"
12 #include "chrome/browser/drive/drive_service_interface.h"
13 #include "chrome/browser/drive/drive_uploader.h"
14 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h"
15 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
16 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
17 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
18 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
19 #include "chrome/browser/sync_file_system/logger.h"
20 #include "google_apis/drive/drive_api_parser.h"
21
22 namespace sync_file_system {
23 namespace drive_backend {
24
25 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context)
26     : sync_context_(sync_context),
27       weak_ptr_factory_(this) {
28 }
29
30 ConflictResolver::~ConflictResolver() {
31 }
32
33 void ConflictResolver::Run(const SyncStatusCallback& callback) {
34   if (!IsContextReady()) {
35     NOTREACHED();
36     callback.Run(SYNC_STATUS_FAILED);
37     return;
38   }
39
40   // Conflict resolution should be invoked on clean tree.
41   if (metadata_database()->GetNormalPriorityDirtyTracker(NULL) ||
42       metadata_database()->GetLowPriorityDirtyTracker(NULL)) {
43     NOTREACHED();
44     callback.Run(SYNC_STATUS_FAILED);
45     return;
46   }
47
48   TrackerSet trackers;
49   if (metadata_database()->GetMultiParentFileTrackers(
50           &target_file_id_, &trackers)) {
51     DCHECK_LT(1u, trackers.size());
52     if (!trackers.has_active()) {
53       NOTREACHED();
54       callback.Run(SYNC_STATUS_FAILED);
55       return;
56     }
57
58     util::Log(logging::LOG_VERBOSE, FROM_HERE,
59               "[ConflictResolver] Detected multi-parent trackers "
60               "(active tracker_id=%" PRId64 ")",
61               trackers.active_tracker()->tracker_id());
62
63     DCHECK(trackers.has_active());
64     for (TrackerSet::const_iterator itr = trackers.begin();
65          itr != trackers.end(); ++itr) {
66       const FileTracker& tracker = **itr;
67       if (tracker.active())
68         continue;
69
70       FileTracker parent_tracker;
71       bool should_success = metadata_database()->FindTrackerByTrackerID(
72           tracker.parent_tracker_id(), &parent_tracker);
73       if (!should_success) {
74         NOTREACHED();
75         callback.Run(SYNC_STATUS_FAILED);
76         return;
77       }
78       parents_to_remove_.push_back(parent_tracker.file_id());
79     }
80     DetachFromNonPrimaryParents(callback);
81     return;
82   }
83
84   if (metadata_database()->GetConflictingTrackers(&trackers)) {
85     target_file_id_ = PickPrimaryFile(trackers);
86     DCHECK(!target_file_id_.empty());
87     int64 primary_tracker_id = -1;
88     for (TrackerSet::const_iterator itr = trackers.begin();
89          itr != trackers.end(); ++itr) {
90       const FileTracker& tracker = **itr;
91       if (tracker.file_id() != target_file_id_) {
92         non_primary_file_ids_.push_back(
93             std::make_pair(tracker.file_id(), tracker.synced_details().etag()));
94       } else {
95         primary_tracker_id = tracker.tracker_id();
96       }
97     }
98
99     util::Log(logging::LOG_VERBOSE, FROM_HERE,
100               "[ConflictResolver] Detected %" PRIuS " conflicting trackers "
101               "(primary tracker_id=%" PRId64 ")",
102               non_primary_file_ids_.size(), primary_tracker_id);
103
104     RemoveNonPrimaryFiles(callback);
105     return;
106   }
107
108   callback.Run(SYNC_STATUS_NO_CONFLICT);
109 }
110
111 void ConflictResolver::DetachFromNonPrimaryParents(
112     const SyncStatusCallback& callback) {
113   DCHECK(!parents_to_remove_.empty());
114
115   // TODO(tzik): Check if ETag match is available for
116   // RemoteResourceFromDirectory.
117   std::string parent_folder_id = parents_to_remove_.back();
118   parents_to_remove_.pop_back();
119   drive_service()->RemoveResourceFromDirectory(
120       parent_folder_id, target_file_id_,
121       base::Bind(&ConflictResolver::DidDetachFromParent,
122                  weak_ptr_factory_.GetWeakPtr(),
123                  callback));
124   util::Log(logging::LOG_VERBOSE, FROM_HERE,
125             "[ConflictResolver] Detach %s from %s",
126             target_file_id_.c_str(), parent_folder_id.c_str());
127 }
128
129 void ConflictResolver::DidDetachFromParent(const SyncStatusCallback& callback,
130                                            google_apis::GDataErrorCode error) {
131   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
132   if (status != SYNC_STATUS_OK) {
133     callback.Run(status);
134     return;
135   }
136
137   if (!parents_to_remove_.empty()) {
138     DetachFromNonPrimaryParents(callback);
139     return;
140   }
141
142   callback.Run(SYNC_STATUS_OK);
143 }
144
145 std::string ConflictResolver::PickPrimaryFile(const TrackerSet& trackers) {
146   scoped_ptr<FileMetadata> primary;
147   for (TrackerSet::const_iterator itr = trackers.begin();
148        itr != trackers.end(); ++itr) {
149     const FileTracker& tracker = **itr;
150     scoped_ptr<FileMetadata> file_metadata(new FileMetadata);
151     bool should_success = metadata_database()->FindFileByFileID(
152         tracker.file_id(), file_metadata.get());
153     if (!should_success) {
154       NOTREACHED();
155       continue;
156     }
157
158     if (!primary) {
159       primary = file_metadata.Pass();
160       continue;
161     }
162
163     DCHECK(primary->details().file_kind() == FILE_KIND_FILE ||
164            primary->details().file_kind() == FILE_KIND_FOLDER);
165     DCHECK(file_metadata->details().file_kind() == FILE_KIND_FILE ||
166            file_metadata->details().file_kind() == FILE_KIND_FOLDER);
167
168     if (primary->details().file_kind() == FILE_KIND_FILE) {
169       if (file_metadata->details().file_kind() == FILE_KIND_FOLDER) {
170         // Prioritize folders over regular files.
171         primary = file_metadata.Pass();
172         continue;
173       }
174
175       DCHECK(file_metadata->details().file_kind() == FILE_KIND_FILE);
176       if (primary->details().modification_time() <
177           file_metadata->details().modification_time()) {
178         // Prioritize last write for regular files.
179         primary = file_metadata.Pass();
180         continue;
181       }
182
183       continue;
184     }
185
186     DCHECK(primary->details().file_kind() == FILE_KIND_FOLDER);
187     if (file_metadata->details().file_kind() == FILE_KIND_FILE) {
188       // Prioritize folders over regular files.
189       continue;
190     }
191
192     DCHECK(file_metadata->details().file_kind() == FILE_KIND_FOLDER);
193     if (primary->details().creation_time() >
194         file_metadata->details().creation_time()) {
195       // Prioritize first create for folders.
196       primary = file_metadata.Pass();
197       continue;
198     }
199   }
200
201   if (primary)
202     return primary->file_id();
203   return std::string();
204 }
205
206 void ConflictResolver::RemoveNonPrimaryFiles(
207     const SyncStatusCallback& callback) {
208   DCHECK(!non_primary_file_ids_.empty());
209
210   std::string file_id = non_primary_file_ids_.back().first;
211   std::string etag = non_primary_file_ids_.back().second;
212   non_primary_file_ids_.pop_back();
213
214   DCHECK_NE(target_file_id_, file_id);
215
216   util::Log(logging::LOG_VERBOSE, FROM_HERE,
217             "[ConflictResolver] Remove non-primary file %s", file_id.c_str());
218
219   // TODO(tzik): Check if the file is a folder, and merge its contents into
220   // the folder identified by |target_file_id_|.
221   drive_service()->DeleteResource(
222       file_id, etag,
223       base::Bind(&ConflictResolver::DidRemoveFile,
224                  weak_ptr_factory_.GetWeakPtr(),
225                  callback, file_id));
226 }
227
228 void ConflictResolver::DidRemoveFile(const SyncStatusCallback& callback,
229                                      const std::string& file_id,
230                                      google_apis::GDataErrorCode error) {
231   if (error == google_apis::HTTP_PRECONDITION ||
232       error == google_apis::HTTP_CONFLICT) {
233     UpdateFileMetadata(file_id, callback);
234     return;
235   }
236
237   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
238   if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) {
239     callback.Run(status);
240     return;
241   }
242
243   deleted_file_ids_.push_back(file_id);
244   if (!non_primary_file_ids_.empty()) {
245     RemoveNonPrimaryFiles(callback);
246     return;
247   }
248
249   metadata_database()->UpdateByDeletedRemoteFileList(
250       deleted_file_ids_, callback);
251 }
252
253 bool ConflictResolver::IsContextReady() {
254   return sync_context_->GetDriveService() &&
255       sync_context_->GetMetadataDatabase();
256 }
257
258 void ConflictResolver::UpdateFileMetadata(
259     const std::string& file_id,
260     const SyncStatusCallback& callback) {
261   drive_service()->GetResourceEntry(
262       file_id,
263       base::Bind(&ConflictResolver::DidGetRemoteMetadata,
264                  weak_ptr_factory_.GetWeakPtr(), file_id, callback));
265 }
266
267 void ConflictResolver::DidGetRemoteMetadata(
268     const std::string& file_id,
269     const SyncStatusCallback& callback,
270     google_apis::GDataErrorCode error,
271     scoped_ptr<google_apis::ResourceEntry> entry) {
272   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
273   if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) {
274     callback.Run(status);
275     return;
276   }
277
278   if (error != google_apis::HTTP_NOT_FOUND) {
279     metadata_database()->UpdateByDeletedRemoteFile(file_id, callback);
280     return;
281   }
282
283   if (!entry) {
284     NOTREACHED();
285     callback.Run(SYNC_STATUS_FAILED);
286     return;
287   }
288
289   metadata_database()->UpdateByFileResource(
290       *drive::util::ConvertResourceEntryToFileResource(*entry),
291       callback);
292 }
293
294 drive::DriveServiceInterface* ConflictResolver::drive_service() {
295   set_used_network(true);
296   return sync_context_->GetDriveService();
297 }
298
299 MetadataDatabase* ConflictResolver::metadata_database() {
300   return sync_context_->GetMetadataDatabase();
301 }
302
303 }  // namespace drive_backend
304 }  // namespace sync_file_system