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 // TODO(zea): Find a better way of doing this. As it stands, we have to | |
tim (not reviewing)
2011/12/20 17:54:05
Maybe move this comment down to the set_sync_tabs
Nicolas Zea
2011/12/20 19:54:10
Done.
| |
133 // update this code whenever we add a new non-cryptographer related field to | |
134 // the nigori node. | |
135 if (entry.GetModelType() == syncable::NIGORI) { | |
136 // Create a new set of specifics based on the server specifics (which | |
137 // preserves their encryption keys). | |
138 sync_pb::EntitySpecifics specifics = | |
139 entry.Get(syncable::SERVER_SPECIFICS); | |
140 sync_pb::NigoriSpecifics* nigori = | |
141 specifics.MutableExtension(sync_pb::nigori); | |
142 // Store the merged set of encrypted types (cryptographer->Update(..) will | |
143 // have merged the local types already). | |
144 cryptographer->UpdateNigoriFromEncryptedTypes(nigori); | |
145 // The local set of keys is already merged with the server's set within | |
146 // the cryptographer. If we don't have pending keys we can store the | |
147 // merged set back immediately. Else we preserve the server keys and will | |
148 // update the nigori when the user provides the pending passphrase via | |
149 // SetPassphrase(..). | |
150 if (cryptographer->is_ready()) { | |
151 cryptographer->GetKeys(nigori->mutable_encrypted()); | |
152 } | |
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 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
453 // TODO(sync): If we're stuck for a while we need to alert the user, clear | 490 // TODO(sync): If we're stuck for a while we need to alert the user, clear |
454 // cache or reset syncing. At the very least we should stop trying something | 491 // cache or reset syncing. At the very least we should stop trying something |
455 // that's obviously not working. | 492 // that's obviously not working. |
456 } | 493 } |
457 | 494 |
458 bool ConflictResolver::ResolveSimpleConflicts( | 495 bool ConflictResolver::ResolveSimpleConflicts( |
459 const ScopedDirLookup& dir, | 496 const ScopedDirLookup& dir, |
460 const ConflictProgress& progress, | 497 const ConflictProgress& progress, |
461 sessions::StatusController* status) { | 498 sessions::StatusController* status) { |
462 WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); | 499 WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); |
500 Cryptographer* cryptographer = dir.manager()->GetCryptographer(&trans); | |
tim (not reviewing)
2011/12/20 17:54:05
Is manager() or the cryptographer really not avail
Nicolas Zea
2011/12/20 19:54:10
Neither the status controller nor the conflict pro
| |
463 bool forward_progress = false; | 501 bool forward_progress = false; |
464 // First iterate over simple conflict items (those that belong to no set). | 502 // First iterate over simple conflict items (those that belong to no set). |
465 set<Id>::const_iterator conflicting_item_it; | 503 set<Id>::const_iterator conflicting_item_it; |
466 for (conflicting_item_it = progress.ConflictingItemsBegin(); | 504 for (conflicting_item_it = progress.ConflictingItemsBegin(); |
467 conflicting_item_it != progress.ConflictingItemsEnd(); | 505 conflicting_item_it != progress.ConflictingItemsEnd(); |
468 ++conflicting_item_it) { | 506 ++conflicting_item_it) { |
469 Id id = *conflicting_item_it; | 507 Id id = *conflicting_item_it; |
470 map<Id, ConflictSet*>::const_iterator item_set_it = | 508 map<Id, ConflictSet*>::const_iterator item_set_it = |
471 progress.IdToConflictSetFind(id); | 509 progress.IdToConflictSetFind(id); |
472 if (item_set_it == progress.IdToConflictSetEnd() || | 510 if (item_set_it == progress.IdToConflictSetEnd() || |
473 0 == item_set_it->second) { | 511 0 == item_set_it->second) { |
474 // We have a simple conflict. | 512 // We have a simple conflict. |
475 switch (ProcessSimpleConflict(&trans, id, status)) { | 513 switch (ProcessSimpleConflict(&trans, id, cryptographer, status)) { |
476 case NO_SYNC_PROGRESS: | 514 case NO_SYNC_PROGRESS: |
477 { | 515 { |
478 int conflict_count = (simple_conflict_count_map_[id] += 2); | 516 int conflict_count = (simple_conflict_count_map_[id] += 2); |
479 LogAndSignalIfConflictStuck(&trans, conflict_count, | 517 LogAndSignalIfConflictStuck(&trans, conflict_count, |
480 &id, &id + 1, status); | 518 &id, &id + 1, status); |
481 break; | 519 break; |
482 } | 520 } |
483 case SYNC_PROGRESS: | 521 case SYNC_PROGRESS: |
484 forward_progress = true; | 522 forward_progress = true; |
485 break; | 523 break; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
539 conflict_set_count_map_.erase(i++); | 577 conflict_set_count_map_.erase(i++); |
540 // METRIC self resolved conflict sets ++. | 578 // METRIC self resolved conflict sets ++. |
541 } else { | 579 } else { |
542 ++i; | 580 ++i; |
543 } | 581 } |
544 } | 582 } |
545 return rv; | 583 return rv; |
546 } | 584 } |
547 | 585 |
548 } // namespace browser_sync | 586 } // namespace browser_sync |
OLD | NEW |