| Index: components/sync/engine_impl/model_type_worker.cc
|
| diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
|
| index 25422f64b1d5263f3a9f4e73fd65495db997067b..c9a2ecaf39b9880bb55423a207d2eb90a151d5de 100644
|
| --- a/components/sync/engine_impl/model_type_worker.cc
|
| +++ b/components/sync/engine_impl/model_type_worker.cc
|
| @@ -14,6 +14,7 @@
|
| #include "base/guid.h"
|
| #include "base/logging.h"
|
| #include "base/memory/ptr_util.h"
|
| +#include "base/metrics/histogram.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "components/sync/base/time.h"
|
| #include "components/sync/engine/model_type_processor.h"
|
| @@ -43,10 +44,26 @@ ModelTypeWorker::ModelTypeWorker(
|
| nudge_handler_->NudgeForInitialDownload(type_);
|
| }
|
|
|
| - if (cryptographer_) {
|
| - DVLOG(1) << ModelTypeToString(type_) << ": Starting with encryption key "
|
| - << cryptographer_->GetDefaultNigoriKeyName();
|
| - OnCryptographerUpdated();
|
| + // This case handles the scenario where the processor has a serialized model
|
| + // type state that has already done its initial sync, and is going to be
|
| + // tracking metadata changes, however it does not have the most recent
|
| + // encryption key name. The cryptographer was updated while the worker was not
|
| + // around, and we're not going to recieve the normal UpdateCryptographer() or
|
| + // EncryptionAcceptedApplyUpdates() calls to drive this process.
|
| + //
|
| + // If |cryptographer_->is_ready()| is false, all the rest of this logic can be
|
| + // safely skipped, since |UpdateCryptographer(...)| must be called first and
|
| + // things should be driven normally after that.
|
| + //
|
| + // If |model_type_state_.initial_sync_done()| is false, |model_type_state_|
|
| + // may still need to be updated, since UpdateCryptographer() is never going to
|
| + // happen, but we can assume PassiveApplyUpdates(...) will push the state to
|
| + // the processor, and we should not push it now. In fact, doing so now would
|
| + // violate the processor's assumption that the first OnUpdateReceived is will
|
| + // be changing initial sync done to true.
|
| + if (cryptographer_ && cryptographer_->is_ready() &&
|
| + UpdateEncryptionKeyName() && model_type_state_.initial_sync_done()) {
|
| + ApplyPendingUpdates();
|
| }
|
| }
|
|
|
| @@ -149,6 +166,7 @@ SyncerError ModelTypeWorker::ProcessGetUpdatesResponse(
|
| data.specifics = specifics;
|
| response_data.entity = data.PassToPtr();
|
| entity->ReceiveEncryptedUpdate(response_data);
|
| + has_encrypted_updates_ = true;
|
| }
|
| }
|
|
|
| @@ -174,12 +192,32 @@ void ModelTypeWorker::PassiveApplyUpdates(StatusController* status) {
|
| ApplyPendingUpdates();
|
| }
|
|
|
| +void ModelTypeWorker::EncryptionAcceptedApplyUpdates() {
|
| + DCHECK(cryptographer_);
|
| + DCHECK(cryptographer_->is_ready());
|
| + // Reuse ApplyUpdates(...) to get its DCHECKs as well.
|
| + ApplyUpdates(nullptr);
|
| +}
|
| +
|
| void ModelTypeWorker::ApplyPendingUpdates() {
|
| - DVLOG(1) << ModelTypeToString(type_) << ": "
|
| - << base::StringPrintf("Delivering %" PRIuS " applicable updates.",
|
| - pending_updates_.size());
|
| - model_type_processor_->OnUpdateReceived(model_type_state_, pending_updates_);
|
| - pending_updates_.clear();
|
| + if (!BlockForEncryption()) {
|
| + DVLOG(1) << ModelTypeToString(type_) << ": "
|
| + << base::StringPrintf("Delivering %" PRIuS " applicable updates.",
|
| + pending_updates_.size());
|
| +
|
| + // If there are still encrypted updates left at this point, they're about to
|
| + // to be potentially lost if the progress marker is saved to disk. Typically
|
| + // the nigori update should arrive simultaneously with the first of the
|
| + // encrypted data. It is possible that non-immediately consistent updates do
|
| + // not follow this pattern.
|
| + UMA_HISTOGRAM_BOOLEAN("Sync.WorkerApplyHasEncryptedUpdates",
|
| + has_encrypted_updates_);
|
| + DCHECK(!has_encrypted_updates_);
|
| +
|
| + model_type_processor_->OnUpdateReceived(model_type_state_,
|
| + pending_updates_);
|
| + pending_updates_.clear();
|
| + }
|
| }
|
|
|
| void ModelTypeWorker::EnqueueForCommit(const CommitRequestDataList& list) {
|
| @@ -204,8 +242,6 @@ void ModelTypeWorker::EnqueueForCommit(const CommitRequestDataList& list) {
|
| std::unique_ptr<CommitContribution> ModelTypeWorker::GetContribution(
|
| size_t max_entries) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| - // There shouldn't be a GetUpdates in progress when a commit is triggered.
|
| - DCHECK(pending_updates_.empty());
|
|
|
| size_t space_remaining = max_entries;
|
| google::protobuf::RepeatedPtrField<sync_pb::SyncEntity> commit_entities;
|
| @@ -264,17 +300,14 @@ bool ModelTypeWorker::IsTypeInitialized() const {
|
| }
|
|
|
| bool ModelTypeWorker::CanCommitItems() const {
|
| - // We can't commit anything until we know the type's parent node.
|
| - // We'll get it in the first update response.
|
| - if (!IsTypeInitialized())
|
| - return false;
|
| -
|
| - // Don't commit if we should be encrypting but don't have the required keys.
|
| - if (cryptographer_ && !cryptographer_->is_ready()) {
|
| - return false;
|
| - }
|
| + // We can only commit if we've received the initial update response and aren't
|
| + // blocked by missing encryption keys.
|
| + return IsTypeInitialized() && !BlockForEncryption();
|
| +}
|
|
|
| - return true;
|
| +bool ModelTypeWorker::BlockForEncryption() const {
|
| + // Should be using encryption, but we do not have the keys.
|
| + return cryptographer_ && !cryptographer_->is_ready();
|
| }
|
|
|
| void ModelTypeWorker::AdjustCommitProto(sync_pb::SyncEntity* sync_entity) {
|
| @@ -317,21 +350,25 @@ void ModelTypeWorker::AdjustCommitProto(sync_pb::SyncEntity* sync_entity) {
|
|
|
| void ModelTypeWorker::OnCryptographerUpdated() {
|
| DCHECK(cryptographer_);
|
| + UpdateEncryptionKeyName();
|
| + DecryptedStoredEntities();
|
| +}
|
|
|
| - bool new_encryption_key = false;
|
| - UpdateResponseDataList response_datas;
|
| -
|
| +bool ModelTypeWorker::UpdateEncryptionKeyName() {
|
| const std::string& new_key_name = cryptographer_->GetDefaultNigoriKeyName();
|
| -
|
| - // Handle a change in encryption key.
|
| - if (model_type_state_.encryption_key_name() != new_key_name) {
|
| - DVLOG(1) << ModelTypeToString(type_) << ": Updating encryption key "
|
| - << model_type_state_.encryption_key_name() << " -> "
|
| - << new_key_name;
|
| - model_type_state_.set_encryption_key_name(new_key_name);
|
| - new_encryption_key = true;
|
| + const std::string& old_key_name = model_type_state_.encryption_key_name();
|
| + if (old_key_name == new_key_name) {
|
| + return false;
|
| }
|
|
|
| + DVLOG(1) << ModelTypeToString(type_) << ": Updating encryption key "
|
| + << old_key_name << " -> " << new_key_name;
|
| + model_type_state_.set_encryption_key_name(new_key_name);
|
| + return true;
|
| +}
|
| +
|
| +void ModelTypeWorker::DecryptedStoredEntities() {
|
| + has_encrypted_updates_ = false;
|
| for (const auto& kv : entities_) {
|
| WorkerEntityTracker* entity = kv.second.get();
|
| if (entity->HasEncryptedUpdate()) {
|
| @@ -355,21 +392,15 @@ void ModelTypeWorker::OnCryptographerUpdated() {
|
| decrypted_update.response_version = encrypted_update.response_version;
|
| decrypted_update.encryption_key_name =
|
| data.specifics.encrypted().key_name();
|
| - response_datas.push_back(decrypted_update);
|
| + pending_updates_.push_back(decrypted_update);
|
|
|
| entity->ClearEncryptedUpdate();
|
| }
|
| + } else {
|
| + has_encrypted_updates_ = true;
|
| }
|
| }
|
| }
|
| -
|
| - if (new_encryption_key || response_datas.size() > 0) {
|
| - DVLOG(1) << ModelTypeToString(type_) << ": "
|
| - << base::StringPrintf("Delivering encryption key and %" PRIuS
|
| - " decrypted updates.",
|
| - response_datas.size());
|
| - model_type_processor_->OnUpdateReceived(model_type_state_, response_datas);
|
| - }
|
| }
|
|
|
| bool ModelTypeWorker::DecryptSpecifics(const sync_pb::EntitySpecifics& in,
|
|
|