| Index: components/sync/engine_impl/loopback_server/loopback_server.cc
|
| diff --git a/components/sync/engine_impl/loopback_server/loopback_server.cc b/components/sync/engine_impl/loopback_server/loopback_server.cc
|
| index 55ea0cd369c09a738f9bacd1b9797bc6cb50b821..b09b01f71942a8160a1389bb6e9ec25a69931216 100644
|
| --- a/components/sync/engine_impl/loopback_server/loopback_server.cc
|
| +++ b/components/sync/engine_impl/loopback_server/loopback_server.cc
|
| @@ -12,6 +12,7 @@
|
| #include "base/files/file_util.h"
|
| #include "base/guid.h"
|
| #include "base/logging.h"
|
| +#include "base/memory/ptr_util.h"
|
| #include "base/metrics/histogram_macros.h"
|
| #include "base/rand_util.h"
|
| #include "base/stl_util.h"
|
| @@ -52,104 +53,91 @@ static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks";
|
| static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks";
|
|
|
| // A filter used during GetUpdates calls to determine what information to
|
| -// send back to the client. There is a 1:1 correspondence between any given
|
| -// GetUpdates call and an UpdateSieve instance.
|
| +// send back to the client; filtering out old entities and tracking versions to
|
| +// use in response progress markers. Note that only the GetUpdatesMessage's
|
| +// from_progress_marker is used to determine this; legacy fields are ignored.
|
| class UpdateSieve {
|
| public:
|
| + explicit UpdateSieve(const sync_pb::GetUpdatesMessage& message)
|
| + : UpdateSieve(MessageToVersionMap(message)) {}
|
| ~UpdateSieve() {}
|
|
|
| - // Factory method for creating an UpdateSieve.
|
| - static std::unique_ptr<UpdateSieve> Create(
|
| - const sync_pb::GetUpdatesMessage& get_updates_message);
|
| -
|
| - // Sets the progress markers in |get_updates_response| given the progress
|
| - // markers from the original GetUpdatesMessage and |new_version| (the latest
|
| - // version in the entries sent back).
|
| - void UpdateProgressMarkers(
|
| - int64_t new_version,
|
| + // Sets the progress markers in |get_updates_response| based on the highest
|
| + // version between request progress markers and response entities.
|
| + void SetProgressMarkers(
|
| sync_pb::GetUpdatesResponse* get_updates_response) const {
|
| - ModelTypeToVersionMap::const_iterator it;
|
| - for (it = request_from_version_.begin(); it != request_from_version_.end();
|
| - ++it) {
|
| + for (const auto& kv : response_version_map_) {
|
| sync_pb::DataTypeProgressMarker* new_marker =
|
| get_updates_response->add_new_progress_marker();
|
| new_marker->set_data_type_id(
|
| - GetSpecificsFieldNumberFromModelType(it->first));
|
| -
|
| - int64_t version = std::max(new_version, it->second);
|
| - new_marker->set_token(base::Int64ToString(version));
|
| + GetSpecificsFieldNumberFromModelType(kv.first));
|
| + new_marker->set_token(base::Int64ToString(kv.second));
|
| }
|
| }
|
|
|
| // Determines whether the server should send an |entity| to the client as
|
| - // part of a GetUpdatesResponse.
|
| - bool ClientWantsItem(const LoopbackServerEntity& entity) const {
|
| + // part of a GetUpdatesResponse. Update internal tracking of max versions as a
|
| + // side effect which will later be used to set response progress markers.
|
| + bool ClientWantsItem(const LoopbackServerEntity& entity) {
|
| int64_t version = entity.GetVersion();
|
| - if (version <= min_version_) {
|
| - return false;
|
| - } else if (entity.IsDeleted()) {
|
| - return true;
|
| - }
|
| -
|
| - ModelTypeToVersionMap::const_iterator it =
|
| - request_from_version_.find(entity.GetModelType());
|
| -
|
| - return it == request_from_version_.end() ? false : it->second < version;
|
| + ModelType type = entity.GetModelType();
|
| + response_version_map_[type] =
|
| + std::max(response_version_map_[type], version);
|
| + auto it = request_version_map_.find(type);
|
| + return it == request_version_map_.end() ? false : it->second < version;
|
| }
|
|
|
| - // Returns the minimum version seen across all types.
|
| - int64_t GetMinVersion() const { return min_version_; }
|
| -
|
| private:
|
| using ModelTypeToVersionMap = std::map<ModelType, int64_t>;
|
|
|
| - // Creates an UpdateSieve.
|
| - UpdateSieve(const ModelTypeToVersionMap request_from_version,
|
| - const int64_t min_version)
|
| - : request_from_version_(request_from_version),
|
| - min_version_(min_version) {}
|
| -
|
| - // Maps data type IDs to the latest version seen for that type.
|
| - const ModelTypeToVersionMap request_from_version_;
|
| -
|
| - // The minimum version seen among all data types.
|
| - const int min_version_;
|
| -};
|
| + static UpdateSieve::ModelTypeToVersionMap MessageToVersionMap(
|
| + const sync_pb::GetUpdatesMessage& get_updates_message) {
|
| + CHECK_GT(get_updates_message.from_progress_marker_size(), 0)
|
| + << "A GetUpdates request must have at least one progress marker.";
|
| + ModelTypeToVersionMap request_version_map;
|
| +
|
| + for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) {
|
| + sync_pb::DataTypeProgressMarker marker =
|
| + get_updates_message.from_progress_marker(i);
|
| +
|
| + int64_t version = 0;
|
| + // Let the version remain zero if there is no token or an empty token (the
|
| + // first request for this type).
|
| + if (marker.has_token() && !marker.token().empty()) {
|
| + bool parsed = base::StringToInt64(marker.token(), &version);
|
| + CHECK(parsed) << "Unable to parse progress marker token.";
|
| + }
|
|
|
| -std::unique_ptr<UpdateSieve> UpdateSieve::Create(
|
| - const sync_pb::GetUpdatesMessage& get_updates_message) {
|
| - CHECK_GT(get_updates_message.from_progress_marker_size(), 0)
|
| - << "A GetUpdates request must have at least one progress marker.";
|
| -
|
| - UpdateSieve::ModelTypeToVersionMap request_from_version;
|
| - int64_t min_version = std::numeric_limits<int64_t>::max();
|
| - for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) {
|
| - sync_pb::DataTypeProgressMarker marker =
|
| - get_updates_message.from_progress_marker(i);
|
| -
|
| - int64_t version = 0;
|
| - // Let the version remain zero if there is no token or an empty token (the
|
| - // first request for this type).
|
| - if (marker.has_token() && !marker.token().empty()) {
|
| - bool parsed = base::StringToInt64(marker.token(), &version);
|
| - CHECK(parsed) << "Unable to parse progress marker token.";
|
| + ModelType model_type =
|
| + syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
|
| + DCHECK(request_version_map.find(model_type) == request_version_map.end());
|
| + request_version_map[model_type] = version;
|
| }
|
| - ModelType model_type =
|
| - syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
|
| - request_from_version[model_type] = version;
|
| -
|
| - if (version < min_version)
|
| - min_version = version;
|
| + return request_version_map;
|
| }
|
|
|
| - return std::unique_ptr<UpdateSieve>(
|
| - new UpdateSieve(request_from_version, min_version));
|
| -}
|
| + explicit UpdateSieve(const ModelTypeToVersionMap request_version_map)
|
| + : request_version_map_(request_version_map),
|
| + response_version_map_(request_version_map) {}
|
| +
|
| + // The largest versions the client has seen before this request, and is used
|
| + // to filter entities to send back to clients. The values in this map are not
|
| + // updated after being initially set. The presence of a type in this map is a
|
| + // proxy for the desire to receive results about this type.
|
| + const ModelTypeToVersionMap request_version_map_;
|
| +
|
| + // The largest versions seen between client and server, ultimately used to
|
| + // send progress markers back to the client.
|
| + ModelTypeToVersionMap response_version_map_;
|
| +};
|
|
|
| } // namespace
|
|
|
| LoopbackServer::LoopbackServer(const base::FilePath& persistent_file)
|
| - : version_(0), store_birthday_(0), persistent_file_(persistent_file) {
|
| + : version_(0),
|
| + store_birthday_(0),
|
| + persistent_file_(persistent_file),
|
| + observer_for_tests_(NULL) {
|
| Init();
|
| }
|
|
|
| @@ -290,7 +278,7 @@ bool LoopbackServer::HandleGetUpdatesRequest(
|
| // at once.
|
| response->set_changes_remaining(0);
|
|
|
| - std::unique_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates);
|
| + auto sieve = base::MakeUnique<UpdateSieve>(get_updates);
|
|
|
| // This folder is called "Synced Bookmarks" by sync and is renamed
|
| // "Mobile Bookmarks" by the mobile client UIs.
|
| @@ -301,7 +289,6 @@ bool LoopbackServer::HandleGetUpdatesRequest(
|
| }
|
|
|
| bool send_encryption_keys_based_on_nigori = false;
|
| - int64_t max_response_version = 0;
|
| for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
|
| ++it) {
|
| const LoopbackServerEntity& entity = *it->second;
|
| @@ -309,9 +296,6 @@ bool LoopbackServer::HandleGetUpdatesRequest(
|
| sync_pb::SyncEntity* response_entity = response->add_entries();
|
| entity.SerializeAsProto(response_entity);
|
|
|
| - max_response_version =
|
| - std::max(max_response_version, response_entity->version());
|
| -
|
| if (entity.GetModelType() == syncer::NIGORI) {
|
| send_encryption_keys_based_on_nigori =
|
| response_entity->specifics().nigori().passphrase_type() ==
|
| @@ -328,7 +312,7 @@ bool LoopbackServer::HandleGetUpdatesRequest(
|
| }
|
| }
|
|
|
| - sieve->UpdateProgressMarkers(max_response_version, response);
|
| + sieve->SetProgressMarkers(response);
|
| return true;
|
| }
|
|
|
| @@ -465,6 +449,9 @@ bool LoopbackServer::HandleCommitRequest(
|
| committed_model_types.Put(iter->second->GetModelType());
|
| }
|
|
|
| + if (observer_for_tests_)
|
| + observer_for_tests_->OnCommit(invalidator_client_id, committed_model_types);
|
| +
|
| return true;
|
| }
|
|
|
| @@ -473,6 +460,7 @@ void LoopbackServer::ClearServerData() {
|
| entities_.clear();
|
| keystore_keys_.clear();
|
| ++store_birthday_;
|
| + base::DeleteFile(persistent_file_, false);
|
| Init();
|
| }
|
|
|
| @@ -481,6 +469,74 @@ std::string LoopbackServer::GetStoreBirthday() const {
|
| return base::Int64ToString(store_birthday_);
|
| }
|
|
|
| +std::vector<sync_pb::SyncEntity> LoopbackServer::GetSyncEntitiesByModelType(
|
| + ModelType model_type) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + std::vector<sync_pb::SyncEntity> sync_entities;
|
| + for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
|
| + ++it) {
|
| + const LoopbackServerEntity& entity = *it->second;
|
| + if (!(entity.IsDeleted() || entity.IsPermanent()) &&
|
| + entity.GetModelType() == model_type) {
|
| + sync_pb::SyncEntity sync_entity;
|
| + entity.SerializeAsProto(&sync_entity);
|
| + sync_entities.push_back(sync_entity);
|
| + }
|
| + }
|
| + return sync_entities;
|
| +}
|
| +
|
| +std::unique_ptr<base::DictionaryValue>
|
| +LoopbackServer::GetEntitiesAsDictionaryValue() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + std::unique_ptr<base::DictionaryValue> dictionary(
|
| + new base::DictionaryValue());
|
| +
|
| + // Initialize an empty ListValue for all ModelTypes.
|
| + ModelTypeSet all_types = ModelTypeSet::All();
|
| + for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
|
| + dictionary->Set(ModelTypeToString(it.Get()), new base::ListValue());
|
| + }
|
| +
|
| + for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
|
| + ++it) {
|
| + const LoopbackServerEntity& entity = *it->second;
|
| + if (entity.IsDeleted() || entity.IsPermanent()) {
|
| + // Tombstones are ignored as they don't represent current data. Folders
|
| + // are also ignored as current verification infrastructure does not
|
| + // consider them.
|
| + continue;
|
| + }
|
| + base::ListValue* list_value;
|
| + if (!dictionary->GetList(ModelTypeToString(entity.GetModelType()),
|
| + &list_value)) {
|
| + return std::unique_ptr<base::DictionaryValue>();
|
| + }
|
| + // TODO(pvalenzuela): Store more data for each entity so additional
|
| + // verification can be performed. One example of additional verification
|
| + // is checking the correctness of the bookmark hierarchy.
|
| + list_value->AppendString(entity.GetName());
|
| + }
|
| +
|
| + return dictionary;
|
| +}
|
| +
|
| +bool LoopbackServer::ModifyEntitySpecifics(
|
| + const std::string& id,
|
| + const sync_pb::EntitySpecifics& updated_specifics) {
|
| + EntityMap::const_iterator iter = entities_.find(id);
|
| + if (iter == entities_.end() ||
|
| + iter->second->GetModelType() !=
|
| + GetModelTypeFromSpecifics(updated_specifics)) {
|
| + return false;
|
| + }
|
| +
|
| + LoopbackServerEntity* entity = iter->second.get();
|
| + entity->SetSpecifics(updated_specifics);
|
| + UpdateEntityVersion(entity);
|
| + return true;
|
| +}
|
| +
|
| void LoopbackServer::SerializeState(sync_pb::LoopbackServerProto* proto) const {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
|
|