Index: sync/engine/conflict_resolver.cc |
diff --git a/sync/engine/conflict_resolver.cc b/sync/engine/conflict_resolver.cc |
deleted file mode 100644 |
index d32460c5f29a67810871ed2b8cf1cac76f6e21e7..0000000000000000000000000000000000000000 |
--- a/sync/engine/conflict_resolver.cc |
+++ /dev/null |
@@ -1,296 +0,0 @@ |
-// Copyright 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "sync/engine/conflict_resolver.h" |
- |
-#include <list> |
-#include <set> |
-#include <string> |
- |
-#include "base/metrics/histogram.h" |
-#include "sync/engine/conflict_util.h" |
-#include "sync/engine/syncer_util.h" |
-#include "sync/internal_api/public/sessions/update_counters.h" |
-#include "sync/sessions/status_controller.h" |
-#include "sync/syncable/directory.h" |
-#include "sync/syncable/mutable_entry.h" |
-#include "sync/syncable/syncable_write_transaction.h" |
-#include "sync/util/cryptographer.h" |
- |
-using std::list; |
-using std::set; |
- |
-namespace syncer { |
- |
-using sessions::StatusController; |
-using syncable::Directory; |
-using syncable::Entry; |
-using syncable::Id; |
-using syncable::MutableEntry; |
-using syncable::WriteTransaction; |
- |
-namespace { |
- |
-// Returns true iff the set of attachment ids contained in attachment_metadata |
-// matches the set of ids contained in server_attachment_metadata. |
-bool AttachmentMetadataMatches(const MutableEntry& entity) { |
- const sync_pb::AttachmentMetadata& local = entity.GetAttachmentMetadata(); |
- const sync_pb::AttachmentMetadata& server = |
- entity.GetServerAttachmentMetadata(); |
- if (local.record_size() != server.record_size()) { |
- return false; |
- } |
- |
- // The order of records in local and server may be different so use a std::set |
- // to determine if they are equivalent. |
- std::set<std::string> local_ids; |
- for (int i = 0; i < local.record_size(); ++i) { |
- const sync_pb::AttachmentMetadataRecord& record = local.record(i); |
- DCHECK(record.is_on_server()); |
- local_ids.insert(record.id().SerializeAsString()); |
- } |
- for (int i = 0; i < server.record_size(); ++i) { |
- const sync_pb::AttachmentMetadataRecord& record = server.record(i); |
- DCHECK(record.is_on_server()); |
- if (local_ids.find(record.id().SerializeAsString()) == local_ids.end()) { |
- return false; |
- } |
- } |
- |
- return true; |
-} |
- |
-} // namespace |
- |
-ConflictResolver::ConflictResolver() { |
-} |
- |
-ConflictResolver::~ConflictResolver() { |
-} |
- |
-void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
- const Id& id, |
- const Cryptographer* cryptographer, |
- StatusController* status, |
- UpdateCounters* counters) { |
- MutableEntry entry(trans, syncable::GET_BY_ID, id); |
- // Must be good as the entry won't have been cleaned up. |
- CHECK(entry.good()); |
- |
- // This function can only resolve simple conflicts. Simple conflicts have |
- // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set. |
- if (!entry.GetIsUnappliedUpdate() || !entry.GetIsUnsynced()) { |
- // This is very unusual, but it can happen in tests. We may be able to |
- // assert NOTREACHED() here when those tests are updated. |
- return; |
- } |
- |
- if (entry.GetIsDel() && entry.GetServerIsDel()) { |
- // we've both deleted it, so lets just drop the need to commit/update this |
- // entry. |
- entry.PutIsUnsynced(false); |
- entry.PutIsUnappliedUpdate(false); |
- // we've made changes, but they won't help syncing progress. |
- // METRIC simple conflict resolved by merge. |
- return; |
- } |
- |
- // This logic determines "client wins" vs. "server wins" strategy picking. |
- // By the time we get to this point, we rely on the following to be true: |
- // a) We can decrypt both the local and server data (else we'd be in |
- // conflict encryption and not attempting to resolve). |
- // b) All unsynced changes have been re-encrypted with the default key ( |
- // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase, |
- // SetDecryptionPassphrase, or RefreshEncryption). |
- // c) Base_server_specifics having a valid datatype means that we received |
- // an undecryptable update that only changed specifics, and since then have |
- // not received any further non-specifics-only or decryptable updates. |
- // d) If the server_specifics match specifics, server_specifics are |
- // encrypted with the default key, and all other visible properties match, |
- // then we can safely ignore the local changes as redundant. |
- // e) Otherwise if the base_server_specifics match the server_specifics, no |
- // functional change must have been made server-side (else |
- // base_server_specifics would have been cleared), and we can therefore |
- // safely ignore the server changes as redundant. |
- // f) Otherwise, it's in general safer to ignore local changes, with the |
- // exception of deletion conflicts (choose to undelete) and conflicts |
- // where the non_unique_name or parent don't match. |
- // e) Except for the case of extensions and apps, where we want uninstalls to |
- // win over local modifications to avoid "back from the dead" reinstalls. |
- if (!entry.GetServerIsDel()) { |
- // TODO(nick): The current logic is arbitrary; instead, it ought to be made |
- // consistent with the ModelAssociator behavior for a datatype. It would |
- // be nice if we could route this back to ModelAssociator code to pick one |
- // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) |
- // are easily mergeable. |
- // See http://crbug.com/77339. |
- bool name_matches = entry.GetNonUniqueName() == |
- entry.GetServerNonUniqueName(); |
- // The parent is implicit type root folder or the parent ID matches. |
- bool parent_matches = entry.GetServerParentId().IsNull() || |
- entry.GetParentId() == entry.GetServerParentId(); |
- bool entry_deleted = entry.GetIsDel(); |
- // The position check might fail spuriously if one of the positions was |
- // based on a legacy random suffix, rather than a deterministic one based on |
- // originator_cache_guid and originator_item_id. If an item is being |
- // modified regularly, it shouldn't take long for the suffix and position to |
- // be updated, so such false failures shouldn't be a problem for long. |
- // |
- // Lucky for us, it's OK to be wrong here. The position_matches check is |
- // allowed to return false negatives, as long as it returns no false |
- // positives. |
- bool position_matches = parent_matches && |
- entry.GetServerUniquePosition().Equals(entry.GetUniquePosition()); |
- const sync_pb::EntitySpecifics& specifics = entry.GetSpecifics(); |
- const sync_pb::EntitySpecifics& server_specifics = |
- entry.GetServerSpecifics(); |
- const sync_pb::EntitySpecifics& base_server_specifics = |
- entry.GetBaseServerSpecifics(); |
- std::string decrypted_specifics, decrypted_server_specifics; |
- bool specifics_match = false; |
- bool server_encrypted_with_default_key = false; |
- if (specifics.has_encrypted()) { |
- DCHECK(cryptographer->CanDecryptUsingDefaultKey(specifics.encrypted())); |
- decrypted_specifics = cryptographer->DecryptToString( |
- specifics.encrypted()); |
- } else { |
- decrypted_specifics = specifics.SerializeAsString(); |
- } |
- if (server_specifics.has_encrypted()) { |
- server_encrypted_with_default_key = |
- cryptographer->CanDecryptUsingDefaultKey( |
- server_specifics.encrypted()); |
- decrypted_server_specifics = cryptographer->DecryptToString( |
- server_specifics.encrypted()); |
- } else { |
- decrypted_server_specifics = server_specifics.SerializeAsString(); |
- } |
- if (decrypted_server_specifics == decrypted_specifics && |
- server_encrypted_with_default_key == specifics.has_encrypted()) { |
- specifics_match = true; |
- } |
- bool base_server_specifics_match = false; |
- if (server_specifics.has_encrypted() && |
- IsRealDataType(GetModelTypeFromSpecifics(base_server_specifics))) { |
- std::string decrypted_base_server_specifics; |
- if (!base_server_specifics.has_encrypted()) { |
- decrypted_base_server_specifics = |
- base_server_specifics.SerializeAsString(); |
- } else { |
- decrypted_base_server_specifics = cryptographer->DecryptToString( |
- base_server_specifics.encrypted()); |
- } |
- if (decrypted_server_specifics == decrypted_base_server_specifics) |
- base_server_specifics_match = true; |
- } |
- |
- bool attachment_metadata_matches = AttachmentMetadataMatches(entry); |
- if (!entry_deleted && name_matches && parent_matches && specifics_match && |
- position_matches && attachment_metadata_matches) { |
- DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " |
- << "changes for: " << entry; |
- conflict_util::IgnoreConflict(&entry); |
- UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
- CHANGES_MATCH, |
- CONFLICT_RESOLUTION_SIZE); |
- } else if (base_server_specifics_match) { |
- DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
- << " changes for: " << entry; |
- status->increment_num_server_overwrites(); |
- counters->num_server_overwrites++; |
- conflict_util::OverwriteServerChanges(&entry); |
- UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
- IGNORE_ENCRYPTION, |
- CONFLICT_RESOLUTION_SIZE); |
- } else if (entry_deleted || !name_matches || !parent_matches) { |
- // NOTE: The update application logic assumes that conflict resolution |
- // will never result in changes to the local hierarchy. The entry_deleted |
- // and !parent_matches cases here are critical to maintaining that |
- // assumption. |
- conflict_util::OverwriteServerChanges(&entry); |
- status->increment_num_server_overwrites(); |
- counters->num_server_overwrites++; |
- DVLOG(1) << "Resolving simple conflict, overwriting server changes " |
- << "for: " << entry; |
- UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
- OVERWRITE_SERVER, |
- CONFLICT_RESOLUTION_SIZE); |
- } else { |
- DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " |
- << entry; |
- conflict_util::IgnoreLocalChanges(&entry); |
- status->increment_num_local_overwrites(); |
- counters->num_local_overwrites++; |
- UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
- OVERWRITE_LOCAL, |
- CONFLICT_RESOLUTION_SIZE); |
- } |
- // Now that we've resolved the conflict, clear the prev server |
- // specifics. |
- entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics()); |
- } else { // SERVER_IS_DEL is true |
- ModelType type = entry.GetModelType(); |
- if (type == EXTENSIONS || type == APPS) { |
- // Ignore local changes for extensions/apps when server had a delete, to |
- // avoid unwanted reinstall of an uninstalled extension. |
- DVLOG(1) << "Resolving simple conflict, ignoring local changes for " |
- << "extension/app: " << entry; |
- conflict_util::IgnoreLocalChanges(&entry); |
- status->increment_num_local_overwrites(); |
- counters->num_local_overwrites++; |
- UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
- OVERWRITE_LOCAL, |
- CONFLICT_RESOLUTION_SIZE); |
- } else { |
- if (entry.GetIsDir()) { |
- Directory::Metahandles children; |
- trans->directory()->GetChildHandlesById(trans, |
- entry.GetId(), |
- &children); |
- // If a server deleted folder has local contents it should be a |
- // hierarchy conflict. Hierarchy conflicts should not be processed by |
- // this function. |
- DCHECK(children.empty()); |
- } |
- |
- // The entry is deleted on the server but still exists locally. |
- // We undelete it by overwriting the server's tombstone with the local |
- // data. |
- conflict_util::OverwriteServerChanges(&entry); |
- status->increment_num_server_overwrites(); |
- counters->num_server_overwrites++; |
- DVLOG(1) << "Resolving simple conflict, undeleting server entry: " |
- << entry; |
- UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
- UNDELETE, |
- CONFLICT_RESOLUTION_SIZE); |
- } |
- } |
-} |
- |
-void ConflictResolver::ResolveConflicts( |
- syncable::WriteTransaction* trans, |
- const Cryptographer* cryptographer, |
- const std::set<syncable::Id>& simple_conflict_ids, |
- sessions::StatusController* status, |
- UpdateCounters* counters) { |
- // Iterate over simple conflict items. |
- set<Id>::const_iterator it; |
- for (it = simple_conflict_ids.begin(); |
- it != simple_conflict_ids.end(); |
- ++it) { |
- // We don't resolve conflicts for control types here. |
- Entry conflicting_node(trans, syncable::GET_BY_ID, *it); |
- CHECK(conflicting_node.good()); |
- if (IsControlType( |
- GetModelTypeFromSpecifics(conflicting_node.GetSpecifics()))) { |
- continue; |
- } |
- |
- ProcessSimpleConflict(trans, *it, cryptographer, status, counters); |
- } |
- return; |
-} |
- |
-} // namespace syncer |