Make sure webview uses embedder display DPI.
[chromium-blink-merge.git] / sync / engine / conflict_resolver.cc
blob074eb374607dbfe86d2bac330ea06e32b64607a6
1 // Copyright 2012 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 "sync/engine/conflict_resolver.h"
7 #include <list>
8 #include <set>
9 #include <string>
11 #include "base/metrics/histogram.h"
12 #include "sync/engine/conflict_util.h"
13 #include "sync/engine/syncer_util.h"
14 #include "sync/sessions/status_controller.h"
15 #include "sync/syncable/directory.h"
16 #include "sync/syncable/mutable_entry.h"
17 #include "sync/syncable/syncable_write_transaction.h"
18 #include "sync/util/cryptographer.h"
20 using std::list;
21 using std::set;
23 namespace syncer {
25 using sessions::StatusController;
26 using syncable::Directory;
27 using syncable::Entry;
28 using syncable::Id;
29 using syncable::MutableEntry;
30 using syncable::WriteTransaction;
32 ConflictResolver::ConflictResolver() {
35 ConflictResolver::~ConflictResolver() {
38 void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
39 const Id& id,
40 const Cryptographer* cryptographer,
41 StatusController* status) {
42 MutableEntry entry(trans, syncable::GET_BY_ID, id);
43 // Must be good as the entry won't have been cleaned up.
44 CHECK(entry.good());
46 // This function can only resolve simple conflicts. Simple conflicts have
47 // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set.
48 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) ||
49 !entry.Get(syncable::IS_UNSYNCED)) {
50 // This is very unusual, but it can happen in tests. We may be able to
51 // assert NOTREACHED() here when those tests are updated.
52 return;
55 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) {
56 // we've both deleted it, so lets just drop the need to commit/update this
57 // entry.
58 entry.Put(syncable::IS_UNSYNCED, false);
59 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false);
60 // we've made changes, but they won't help syncing progress.
61 // METRIC simple conflict resolved by merge.
62 return;
65 // This logic determines "client wins" vs. "server wins" strategy picking.
66 // By the time we get to this point, we rely on the following to be true:
67 // a) We can decrypt both the local and server data (else we'd be in
68 // conflict encryption and not attempting to resolve).
69 // b) All unsynced changes have been re-encrypted with the default key (
70 // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase,
71 // SetDecryptionPassphrase, or RefreshEncryption).
72 // c) Base_server_specifics having a valid datatype means that we received
73 // an undecryptable update that only changed specifics, and since then have
74 // not received any further non-specifics-only or decryptable updates.
75 // d) If the server_specifics match specifics, server_specifics are
76 // encrypted with the default key, and all other visible properties match,
77 // then we can safely ignore the local changes as redundant.
78 // e) Otherwise if the base_server_specifics match the server_specifics, no
79 // functional change must have been made server-side (else
80 // base_server_specifics would have been cleared), and we can therefore
81 // safely ignore the server changes as redundant.
82 // f) Otherwise, it's in general safer to ignore local changes, with the
83 // exception of deletion conflicts (choose to undelete) and conflicts
84 // where the non_unique_name or parent don't match.
85 if (!entry.Get(syncable::SERVER_IS_DEL)) {
86 // TODO(nick): The current logic is arbitrary; instead, it ought to be made
87 // consistent with the ModelAssociator behavior for a datatype. It would
88 // be nice if we could route this back to ModelAssociator code to pick one
89 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill)
90 // are easily mergeable.
91 // See http://crbug.com/77339.
92 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) ==
93 entry.Get(syncable::SERVER_NON_UNIQUE_NAME);
94 bool parent_matches = entry.Get(syncable::PARENT_ID) ==
95 entry.Get(syncable::SERVER_PARENT_ID);
96 bool entry_deleted = entry.Get(syncable::IS_DEL);
98 // The position check might fail spuriously if one of the positions was
99 // based on a legacy random suffix, rather than a deterministic one based on
100 // originator_cache_guid and originator_item_id. If an item is being
101 // modified regularly, it shouldn't take long for the suffix and position to
102 // be updated, so such false failures shouldn't be a problem for long.
104 // Lucky for us, it's OK to be wrong here. The position_matches check is
105 // allowed to return false negatives, as long as it returns no false
106 // positives.
107 bool position_matches = parent_matches &&
108 entry.Get(syncable::SERVER_UNIQUE_POSITION).Equals(
109 entry.Get(syncable::UNIQUE_POSITION));
110 const sync_pb::EntitySpecifics& specifics =
111 entry.Get(syncable::SPECIFICS);
112 const sync_pb::EntitySpecifics& server_specifics =
113 entry.Get(syncable::SERVER_SPECIFICS);
114 const sync_pb::EntitySpecifics& base_server_specifics =
115 entry.Get(syncable::BASE_SERVER_SPECIFICS);
116 std::string decrypted_specifics, decrypted_server_specifics;
117 bool specifics_match = false;
118 bool server_encrypted_with_default_key = false;
119 if (specifics.has_encrypted()) {
120 DCHECK(cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted()));
121 decrypted_specifics = cryptographer->DecryptToString(
122 specifics.encrypted());
123 } else {
124 decrypted_specifics = specifics.SerializeAsString();
126 if (server_specifics.has_encrypted()) {
127 server_encrypted_with_default_key =
128 cryptographer->CanDecryptUsingDefaultKey(
129 server_specifics.encrypted());
130 decrypted_server_specifics = cryptographer->DecryptToString(
131 server_specifics.encrypted());
132 } else {
133 decrypted_server_specifics = server_specifics.SerializeAsString();
135 if (decrypted_server_specifics == decrypted_specifics &&
136 server_encrypted_with_default_key == specifics.has_encrypted()) {
137 specifics_match = true;
139 bool base_server_specifics_match = false;
140 if (server_specifics.has_encrypted() &&
141 IsRealDataType(GetModelTypeFromSpecifics(base_server_specifics))) {
142 std::string decrypted_base_server_specifics;
143 if (!base_server_specifics.has_encrypted()) {
144 decrypted_base_server_specifics =
145 base_server_specifics.SerializeAsString();
146 } else {
147 decrypted_base_server_specifics = cryptographer->DecryptToString(
148 base_server_specifics.encrypted());
150 if (decrypted_server_specifics == decrypted_base_server_specifics)
151 base_server_specifics_match = true;
154 if (!entry_deleted && name_matches && parent_matches && specifics_match &&
155 position_matches) {
156 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring "
157 << "changes for: " << entry;
158 conflict_util::IgnoreConflict(&entry);
159 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
160 CHANGES_MATCH,
161 CONFLICT_RESOLUTION_SIZE);
162 } else if (base_server_specifics_match) {
163 DVLOG(1) << "Resolving simple conflict, ignoring server encryption "
164 << " changes for: " << entry;
165 status->increment_num_server_overwrites();
166 conflict_util::OverwriteServerChanges(&entry);
167 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
168 IGNORE_ENCRYPTION,
169 CONFLICT_RESOLUTION_SIZE);
170 } else if (entry_deleted || !name_matches || !parent_matches) {
171 // NOTE: The update application logic assumes that conflict resolution
172 // will never result in changes to the local hierarchy. The entry_deleted
173 // and !parent_matches cases here are critical to maintaining that
174 // assumption.
175 conflict_util::OverwriteServerChanges(&entry);
176 status->increment_num_server_overwrites();
177 DVLOG(1) << "Resolving simple conflict, overwriting server changes "
178 << "for: " << entry;
179 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
180 OVERWRITE_SERVER,
181 CONFLICT_RESOLUTION_SIZE);
182 } else {
183 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: "
184 << entry;
185 conflict_util::IgnoreLocalChanges(&entry);
186 status->increment_num_local_overwrites();
187 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
188 OVERWRITE_LOCAL,
189 CONFLICT_RESOLUTION_SIZE);
191 // Now that we've resolved the conflict, clear the prev server
192 // specifics.
193 entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics());
194 } else { // SERVER_IS_DEL is true
195 if (entry.Get(syncable::IS_DIR)) {
196 Directory::ChildHandles children;
197 trans->directory()->GetChildHandlesById(trans,
198 entry.Get(syncable::ID),
199 &children);
200 // If a server deleted folder has local contents it should be a hierarchy
201 // conflict. Hierarchy conflicts should not be processed by this
202 // function.
203 DCHECK(children.empty());
206 // The entry is deleted on the server but still exists locally.
207 // We undelete it by overwriting the server's tombstone with the local
208 // data.
209 conflict_util::OverwriteServerChanges(&entry);
210 status->increment_num_server_overwrites();
211 DVLOG(1) << "Resolving simple conflict, undeleting server entry: "
212 << entry;
213 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
214 UNDELETE,
215 CONFLICT_RESOLUTION_SIZE);
219 void ConflictResolver::ResolveConflicts(
220 syncable::WriteTransaction* trans,
221 const Cryptographer* cryptographer,
222 const std::set<syncable::Id>& simple_conflict_ids,
223 sessions::StatusController* status) {
224 // Iterate over simple conflict items.
225 set<Id>::const_iterator conflicting_item_it;
226 set<Id> processed_items;
227 for (conflicting_item_it = simple_conflict_ids.begin();
228 conflicting_item_it != simple_conflict_ids.end();
229 ++conflicting_item_it) {
230 Id id = *conflicting_item_it;
231 if (processed_items.count(id) > 0)
232 continue;
234 // We don't resolve conflicts for control types here.
235 Entry conflicting_node(trans, syncable::GET_BY_ID, id);
236 CHECK(conflicting_node.good());
237 if (IsControlType(
238 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) {
239 continue;
242 // We have a simple conflict. In order check if positions have changed,
243 // we need to process conflicting predecessors before successors. Traverse
244 // backwards through all continuous conflicting predecessors, building a
245 // stack of items to resolve in predecessor->successor order, then process
246 // each item individually.
247 list<Id> predecessors;
248 Id prev_id = id;
249 do {
250 predecessors.push_back(prev_id);
251 Entry entry(trans, syncable::GET_BY_ID, prev_id);
252 // Any entry in conflict must be valid.
253 CHECK(entry.good());
255 // We can't traverse over a delete item.
256 if (entry.Get(syncable::IS_DEL))
257 break;
259 Id new_prev_id = entry.GetPredecessorId();
260 if (new_prev_id == prev_id)
261 break;
262 prev_id = new_prev_id;
263 } while (processed_items.count(prev_id) == 0 &&
264 simple_conflict_ids.count(prev_id) > 0); // Excludes root.
265 while (!predecessors.empty()) {
266 id = predecessors.back();
267 predecessors.pop_back();
268 ProcessSimpleConflict(trans, id, cryptographer, status);
269 processed_items.insert(id);
272 return;
275 } // namespace syncer