| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/engine/conflict_resolver.h" | 5 #include "chrome/browser/sync/engine/conflict_resolver.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <set> | 9 #include <set> |
| 10 | 10 |
| 11 #include "base/location.h" | 11 #include "base/location.h" |
| 12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 13 #include "chrome/browser/sync/engine/syncer.h" | 13 #include "chrome/browser/sync/engine/syncer.h" |
| 14 #include "chrome/browser/sync/engine/syncer_util.h" | 14 #include "chrome/browser/sync/engine/syncer_util.h" |
| 15 #include "chrome/browser/sync/protocol/nigori_specifics.pb.h" |
| 15 #include "chrome/browser/sync/protocol/service_constants.h" | 16 #include "chrome/browser/sync/protocol/service_constants.h" |
| 16 #include "chrome/browser/sync/sessions/status_controller.h" | 17 #include "chrome/browser/sync/sessions/status_controller.h" |
| 17 #include "chrome/browser/sync/syncable/directory_manager.h" | 18 #include "chrome/browser/sync/syncable/directory_manager.h" |
| 18 #include "chrome/browser/sync/syncable/syncable.h" | 19 #include "chrome/browser/sync/syncable/syncable.h" |
| 20 #include "chrome/browser/sync/util/cryptographer.h" |
| 19 | 21 |
| 20 using std::map; | 22 using std::map; |
| 21 using std::set; | 23 using std::set; |
| 22 using syncable::BaseTransaction; | 24 using syncable::BaseTransaction; |
| 23 using syncable::Directory; | 25 using syncable::Directory; |
| 24 using syncable::Entry; | 26 using syncable::Entry; |
| 25 using syncable::Id; | 27 using syncable::Id; |
| 26 using syncable::MutableEntry; | 28 using syncable::MutableEntry; |
| 27 using syncable::ScopedDirLookup; | 29 using syncable::ScopedDirLookup; |
| 28 using syncable::WriteTransaction; | 30 using syncable::WriteTransaction; |
| 29 | 31 |
| 30 namespace browser_sync { | 32 namespace browser_sync { |
| 31 | 33 |
| 32 using sessions::ConflictProgress; | 34 using sessions::ConflictProgress; |
| 33 using sessions::StatusController; | 35 using sessions::StatusController; |
| 34 | 36 |
| 35 namespace { | 37 namespace { |
| 36 | 38 |
| 37 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; | 39 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; |
| 38 | 40 |
| 39 // Enumeration of different conflict resolutions. Used for histogramming. | 41 // Enumeration of different conflict resolutions. Used for histogramming. |
| 40 enum SimpleConflictResolutions { | 42 enum SimpleConflictResolutions { |
| 41 OVERWRITE_LOCAL, // Resolved by overwriting local changes. | 43 OVERWRITE_LOCAL, // Resolved by overwriting local changes. |
| 42 OVERWRITE_SERVER, // Resolved by overwriting server changes. | 44 OVERWRITE_SERVER, // Resolved by overwriting server changes. |
| 43 UNDELETE, // Resolved by undeleting local item. | 45 UNDELETE, // Resolved by undeleting local item. |
| 44 IGNORE_ENCRYPTION, // Resolved by ignoring an encryption-only server | 46 IGNORE_ENCRYPTION, // Resolved by ignoring an encryption-only server |
| 45 // change. TODO(zea): implement and use this. | 47 // change. TODO(zea): implement and use this. |
| 48 NIGORI_MERGE, // Resolved by merging nigori nodes. |
| 46 CONFLICT_RESOLUTION_SIZE, | 49 CONFLICT_RESOLUTION_SIZE, |
| 47 }; | 50 }; |
| 48 | 51 |
| 49 } // namespace | 52 } // namespace |
| 50 | 53 |
| 51 ConflictResolver::ConflictResolver() { | 54 ConflictResolver::ConflictResolver() { |
| 52 } | 55 } |
| 53 | 56 |
| 54 ConflictResolver::~ConflictResolver() { | 57 ConflictResolver::~ConflictResolver() { |
| 55 } | 58 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 71 // TODO(chron): This is really a general property clobber. We clobber | 74 // TODO(chron): This is really a general property clobber. We clobber |
| 72 // the server side property. Perhaps we should actually do property merging. | 75 // the server side property. Perhaps we should actually do property merging. |
| 73 DVLOG(1) << "Resolving conflict by ignoring server changes:" << entry; | 76 DVLOG(1) << "Resolving conflict by ignoring server changes:" << entry; |
| 74 entry->Put(syncable::BASE_VERSION, entry->Get(syncable::SERVER_VERSION)); | 77 entry->Put(syncable::BASE_VERSION, entry->Get(syncable::SERVER_VERSION)); |
| 75 entry->Put(syncable::IS_UNAPPLIED_UPDATE, false); | 78 entry->Put(syncable::IS_UNAPPLIED_UPDATE, false); |
| 76 } | 79 } |
| 77 | 80 |
| 78 ConflictResolver::ProcessSimpleConflictResult | 81 ConflictResolver::ProcessSimpleConflictResult |
| 79 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, | 82 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
| 80 const Id& id, | 83 const Id& id, |
| 84 const Cryptographer* cryptographer, |
| 81 StatusController* status) { | 85 StatusController* status) { |
| 82 MutableEntry entry(trans, syncable::GET_BY_ID, id); | 86 MutableEntry entry(trans, syncable::GET_BY_ID, id); |
| 83 // Must be good as the entry won't have been cleaned up. | 87 // Must be good as the entry won't have been cleaned up. |
| 84 CHECK(entry.good()); | 88 CHECK(entry.good()); |
| 85 // If an update fails, locally we have to be in a set or unsynced. We're not | 89 // If an update fails, locally we have to be in a set or unsynced. We're not |
| 86 // in a set here, so we must be unsynced. | 90 // in a set here, so we must be unsynced. |
| 87 if (!entry.Get(syncable::IS_UNSYNCED)) { | 91 if (!entry.Get(syncable::IS_UNSYNCED)) { |
| 88 return NO_SYNC_PROGRESS; | 92 return NO_SYNC_PROGRESS; |
| 89 } | 93 } |
| 90 | 94 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 117 // be nice if we could route this back to ModelAssociator code to pick one | 121 // be nice if we could route this back to ModelAssociator code to pick one |
| 118 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) | 122 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) |
| 119 // are easily mergeable. | 123 // are easily mergeable. |
| 120 // See http://crbug.com/77339. | 124 // See http://crbug.com/77339. |
| 121 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) == | 125 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) == |
| 122 entry.Get(syncable::SERVER_NON_UNIQUE_NAME); | 126 entry.Get(syncable::SERVER_NON_UNIQUE_NAME); |
| 123 bool parent_matches = entry.Get(syncable::PARENT_ID) == | 127 bool parent_matches = entry.Get(syncable::PARENT_ID) == |
| 124 entry.Get(syncable::SERVER_PARENT_ID); | 128 entry.Get(syncable::SERVER_PARENT_ID); |
| 125 bool entry_deleted = entry.Get(syncable::IS_DEL); | 129 bool entry_deleted = entry.Get(syncable::IS_DEL); |
| 126 | 130 |
| 127 if (!entry_deleted && name_matches && parent_matches) { | 131 // We manually merge nigori data. |
| 132 if (entry.GetModelType() == syncable::NIGORI) { |
| 133 // Create a new set of specifics based on the server specifics (which |
| 134 // preserves their encryption keys). |
| 135 sync_pb::EntitySpecifics specifics = |
| 136 entry.Get(syncable::SERVER_SPECIFICS); |
| 137 sync_pb::NigoriSpecifics* nigori = |
| 138 specifics.MutableExtension(sync_pb::nigori); |
| 139 // Store the merged set of encrypted types (cryptographer->Update(..) will |
| 140 // have merged the local types already). |
| 141 cryptographer->UpdateNigoriFromEncryptedTypes(nigori); |
| 142 // The local set of keys is already merged with the server's set within |
| 143 // the cryptographer. If we don't have pending keys we can store the |
| 144 // merged set back immediately. Else we preserve the server keys and will |
| 145 // update the nigori when the user provides the pending passphrase via |
| 146 // SetPassphrase(..). |
| 147 if (cryptographer->is_ready()) { |
| 148 cryptographer->GetKeys(nigori->mutable_encrypted()); |
| 149 } |
| 150 // TODO(zea): Find a better way of doing this. As it stands, we have to |
| 151 // update this code whenever we add a new non-cryptographer related field |
| 152 // to the nigori node. |
| 153 if (entry.Get(syncable::SPECIFICS).GetExtension(sync_pb::nigori) |
| 154 .sync_tabs()) { |
| 155 nigori->set_sync_tabs(true); |
| 156 } |
| 157 entry.Put(syncable::SPECIFICS, specifics); |
| 158 DVLOG(1) << "Resovling simple conflict, merging nigori nodes: " << entry; |
| 159 status->increment_num_server_overwrites(); |
| 160 OverwriteServerChanges(trans, &entry); |
| 161 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 162 NIGORI_MERGE, |
| 163 CONFLICT_RESOLUTION_SIZE); |
| 164 } else if (!entry_deleted && name_matches && parent_matches) { |
| 128 // TODO(zea): We may prefer to choose the local changes over the server | 165 // TODO(zea): We may prefer to choose the local changes over the server |
| 129 // if we know the local changes happened before (or vice versa). | 166 // if we know the local changes happened before (or vice versa). |
| 130 // See http://crbug.com/76596 | 167 // See http://crbug.com/76596 |
| 131 DVLOG(1) << "Resolving simple conflict, ignoring local changes for:" | 168 DVLOG(1) << "Resolving simple conflict, ignoring local changes for:" |
| 132 << entry; | 169 << entry; |
| 133 IgnoreLocalChanges(&entry); | 170 IgnoreLocalChanges(&entry); |
| 134 status->increment_num_local_overwrites(); | 171 status->increment_num_local_overwrites(); |
| 135 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 172 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 136 OVERWRITE_LOCAL, | 173 OVERWRITE_LOCAL, |
| 137 CONFLICT_RESOLUTION_SIZE); | 174 CONFLICT_RESOLUTION_SIZE); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 << server_update << entry; | 223 << server_update << entry; |
| 187 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 224 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 188 UNDELETE, | 225 UNDELETE, |
| 189 CONFLICT_RESOLUTION_SIZE); | 226 CONFLICT_RESOLUTION_SIZE); |
| 190 } | 227 } |
| 191 return SYNC_PROGRESS; | 228 return SYNC_PROGRESS; |
| 192 } | 229 } |
| 193 } | 230 } |
| 194 | 231 |
| 195 bool ConflictResolver::ResolveSimpleConflicts( | 232 bool ConflictResolver::ResolveSimpleConflicts( |
| 196 const ScopedDirLookup& dir, | 233 syncable::WriteTransaction* trans, |
| 234 const Cryptographer* cryptographer, |
| 197 const ConflictProgress& progress, | 235 const ConflictProgress& progress, |
| 198 sessions::StatusController* status) { | 236 sessions::StatusController* status) { |
| 199 WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); | |
| 200 bool forward_progress = false; | 237 bool forward_progress = false; |
| 201 // First iterate over simple conflict items (those that belong to no set). | 238 // First iterate over simple conflict items (those that belong to no set). |
| 202 set<Id>::const_iterator conflicting_item_it; | 239 set<Id>::const_iterator conflicting_item_it; |
| 203 for (conflicting_item_it = progress.ConflictingItemsBegin(); | 240 for (conflicting_item_it = progress.ConflictingItemsBegin(); |
| 204 conflicting_item_it != progress.ConflictingItemsEnd(); | 241 conflicting_item_it != progress.ConflictingItemsEnd(); |
| 205 ++conflicting_item_it) { | 242 ++conflicting_item_it) { |
| 206 Id id = *conflicting_item_it; | 243 Id id = *conflicting_item_it; |
| 207 map<Id, ConflictSet*>::const_iterator item_set_it = | 244 map<Id, ConflictSet*>::const_iterator item_set_it = |
| 208 progress.IdToConflictSetFind(id); | 245 progress.IdToConflictSetFind(id); |
| 209 if (item_set_it == progress.IdToConflictSetEnd() || | 246 if (item_set_it == progress.IdToConflictSetEnd() || |
| 210 0 == item_set_it->second) { | 247 0 == item_set_it->second) { |
| 211 // We have a simple conflict. | 248 // We have a simple conflict. |
| 212 switch (ProcessSimpleConflict(&trans, id, status)) { | 249 switch (ProcessSimpleConflict(trans, id, cryptographer, status)) { |
| 213 case NO_SYNC_PROGRESS: | 250 case NO_SYNC_PROGRESS: |
| 214 break; | 251 break; |
| 215 case SYNC_PROGRESS: | 252 case SYNC_PROGRESS: |
| 216 forward_progress = true; | 253 forward_progress = true; |
| 217 break; | 254 break; |
| 218 } | 255 } |
| 219 } | 256 } |
| 220 } | 257 } |
| 221 return forward_progress; | 258 return forward_progress; |
| 222 } | 259 } |
| 223 | 260 |
| 224 bool ConflictResolver::ResolveConflicts(const ScopedDirLookup& dir, | 261 bool ConflictResolver::ResolveConflicts(syncable::WriteTransaction* trans, |
| 262 const Cryptographer* cryptographer, |
| 225 const ConflictProgress& progress, | 263 const ConflictProgress& progress, |
| 226 sessions::StatusController* status) { | 264 sessions::StatusController* status) { |
| 227 // TODO(rlarocque): A good amount of code related to the resolution of | 265 // TODO(rlarocque): A good amount of code related to the resolution of |
| 228 // conflict sets has been deleted here. This was done because the code had | 266 // conflict sets has been deleted here. This was done because the code had |
| 229 // not been used in years. An unrelated bug fix accidentally re-enabled the | 267 // not been used in years. An unrelated bug fix accidentally re-enabled the |
| 230 // code, forcing us to make a decision about what we should do with the code. | 268 // code, forcing us to make a decision about what we should do with the code. |
| 231 // We decided to do the safe thing and delete it for now. This restores the | 269 // We decided to do the safe thing and delete it for now. This restores the |
| 232 // behaviour we've relied on for quite some time. We should think about what | 270 // behaviour we've relied on for quite some time. We should think about what |
| 233 // that code was trying to do and consider re-enabling parts of it. | 271 // that code was trying to do and consider re-enabling parts of it. |
| 234 | 272 |
| 235 if (progress.ConflictSetsSize() > 0) { | 273 if (progress.ConflictSetsSize() > 0) { |
| 236 DVLOG(1) << "Detected " << progress.IdToConflictSetSize() | 274 DVLOG(1) << "Detected " << progress.IdToConflictSetSize() |
| 237 << " non-simple conflicting entries in " << progress.ConflictSetsSize() | 275 << " non-simple conflicting entries in " << progress.ConflictSetsSize() |
| 238 << " unprocessed conflict sets."; | 276 << " unprocessed conflict sets."; |
| 239 } | 277 } |
| 240 | 278 |
| 241 return ResolveSimpleConflicts(dir, progress, status); | 279 return ResolveSimpleConflicts(trans, cryptographer, progress, status); |
| 242 } | 280 } |
| 243 | 281 |
| 244 } // namespace browser_sync | 282 } // namespace browser_sync |
| OLD | NEW |