Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / conflict_resolver.cc
blobf2ed33a616f85b682addd6c607654c6e3e303168
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/conflict_resolver.h"
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"
22 namespace sync_file_system {
23 namespace drive_backend {
25 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context)
26 : sync_context_(sync_context),
27 weak_ptr_factory_(this) {
30 ConflictResolver::~ConflictResolver() {
33 void ConflictResolver::Run(const SyncStatusCallback& callback) {
34 if (!IsContextReady()) {
35 NOTREACHED();
36 callback.Run(SYNC_STATUS_FAILED);
37 return;
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;
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;
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());
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;
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;
78 parents_to_remove_.push_back(parent_tracker.file_id());
80 DetachFromNonPrimaryParents(callback);
81 return;
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();
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);
104 RemoveNonPrimaryFiles(callback);
105 return;
108 callback.Run(SYNC_STATUS_NO_CONFLICT);
111 void ConflictResolver::DetachFromNonPrimaryParents(
112 const SyncStatusCallback& callback) {
113 DCHECK(!parents_to_remove_.empty());
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());
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;
137 if (!parents_to_remove_.empty()) {
138 DetachFromNonPrimaryParents(callback);
139 return;
142 callback.Run(SYNC_STATUS_OK);
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;
158 if (!primary) {
159 primary = file_metadata.Pass();
160 continue;
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);
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;
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;
183 continue;
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;
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;
201 if (primary)
202 return primary->file_id();
203 return std::string();
206 void ConflictResolver::RemoveNonPrimaryFiles(
207 const SyncStatusCallback& callback) {
208 DCHECK(!non_primary_file_ids_.empty());
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();
214 deleted_file_ids_.push_back(file_id);
215 DCHECK_NE(target_file_id_, file_id);
217 util::Log(logging::LOG_VERBOSE, FROM_HERE,
218 "[ConflictResolver] Remove non-primary file %s", file_id.c_str());
220 // TODO(tzik): Check if the file is a folder, and merge its contents into
221 // the folder identified by |target_file_id_|.
222 drive_service()->DeleteResource(
223 file_id, etag,
224 base::Bind(&ConflictResolver::DidRemoveFile,
225 weak_ptr_factory_.GetWeakPtr(),
226 callback, file_id));
229 void ConflictResolver::DidRemoveFile(const SyncStatusCallback& callback,
230 const std::string& file_id,
231 google_apis::GDataErrorCode error) {
232 if (error == google_apis::HTTP_PRECONDITION ||
233 error == google_apis::HTTP_CONFLICT) {
234 UpdateFileMetadata(file_id, callback);
235 return;
238 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
239 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) {
240 callback.Run(status);
241 return;
244 deleted_file_ids_.push_back(file_id);
245 if (!non_primary_file_ids_.empty()) {
246 RemoveNonPrimaryFiles(callback);
247 return;
250 metadata_database()->UpdateByDeletedRemoteFileList(
251 deleted_file_ids_, callback);
254 bool ConflictResolver::IsContextReady() {
255 return sync_context_->GetDriveService() &&
256 sync_context_->GetMetadataDatabase();
259 void ConflictResolver::UpdateFileMetadata(
260 const std::string& file_id,
261 const SyncStatusCallback& callback) {
262 drive_service()->GetResourceEntry(
263 file_id,
264 base::Bind(&ConflictResolver::DidGetRemoteMetadata,
265 weak_ptr_factory_.GetWeakPtr(), file_id, callback));
268 void ConflictResolver::DidGetRemoteMetadata(
269 const std::string& file_id,
270 const SyncStatusCallback& callback,
271 google_apis::GDataErrorCode error,
272 scoped_ptr<google_apis::ResourceEntry> entry) {
273 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
274 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) {
275 callback.Run(status);
276 return;
279 if (error != google_apis::HTTP_NOT_FOUND) {
280 metadata_database()->UpdateByDeletedRemoteFile(file_id, callback);
281 return;
284 if (!entry) {
285 NOTREACHED();
286 callback.Run(SYNC_STATUS_FAILED);
287 return;
290 metadata_database()->UpdateByFileResource(
291 *drive::util::ConvertResourceEntryToFileResource(*entry),
292 callback);
295 drive::DriveServiceInterface* ConflictResolver::drive_service() {
296 set_used_network(true);
297 return sync_context_->GetDriveService();
300 MetadataDatabase* ConflictResolver::metadata_database() {
301 return sync_context_->GetMetadataDatabase();
304 } // namespace drive_backend
305 } // namespace sync_file_system