This source file includes following definitions.
- AddExtensionsActivityToMessage
- AddClientConfigParamsToMessage
- SetEntrySpecifics
- BuildCommitItem
- LogServerError
- GetResultingPostCommitName
- UpdateVersionAfterCommit
- ChangeIdAfterCommit
- UpdateServerFieldsAfterCommit
- ProcessSuccessfulCommitResponse
- ProcessSingleCommitResponse
#include "sync/engine/commit_util.h"
#include <limits>
#include <set>
#include <string>
#include <vector>
#include "base/strings/string_util.h"
#include "sync/engine/syncer_proto_util.h"
#include "sync/internal_api/public/base/unique_position.h"
#include "sync/protocol/bookmark_specifics.pb.h"
#include "sync/protocol/sync.pb.h"
#include "sync/sessions/sync_session.h"
#include "sync/syncable/directory.h"
#include "sync/syncable/entry.h"
#include "sync/syncable/model_neutral_mutable_entry.h"
#include "sync/syncable/syncable_base_transaction.h"
#include "sync/syncable/syncable_base_write_transaction.h"
#include "sync/syncable/syncable_changes_version.h"
#include "sync/syncable/syncable_proto_util.h"
#include "sync/syncable/syncable_util.h"
#include "sync/util/time.h"
using std::set;
using std::string;
using std::vector;
namespace syncer {
using sessions::SyncSession;
using syncable::Entry;
using syncable::IS_DEL;
using syncable::IS_UNAPPLIED_UPDATE;
using syncable::IS_UNSYNCED;
using syncable::Id;
using syncable::SPECIFICS;
using syncable::UNIQUE_POSITION;
namespace commit_util {
void AddExtensionsActivityToMessage(
ExtensionsActivity* activity,
ExtensionsActivity::Records* extensions_activity_buffer,
sync_pb::CommitMessage* message) {
activity->GetAndClearRecords(extensions_activity_buffer);
const ExtensionsActivity::Records& records = *extensions_activity_buffer;
for (ExtensionsActivity::Records::const_iterator it =
records.begin();
it != records.end(); ++it) {
sync_pb::ChromiumExtensionsActivity* activity_message =
message->add_extensions_activity();
activity_message->set_extension_id(it->second.extension_id);
activity_message->set_bookmark_writes_since_last_commit(
it->second.bookmark_write_count);
}
}
void AddClientConfigParamsToMessage(
ModelTypeSet enabled_types,
sync_pb::CommitMessage* message) {
sync_pb::ClientConfigParams* config_params = message->mutable_config_params();
for (ModelTypeSet::Iterator it = enabled_types.First(); it.Good(); it.Inc()) {
if (ProxyTypes().Has(it.Get()))
continue;
int field_number = GetSpecificsFieldNumberFromModelType(it.Get());
config_params->mutable_enabled_type_ids()->Add(field_number);
}
config_params->set_tabs_datatype_enabled(
enabled_types.Has(syncer::PROXY_TABS));
}
namespace {
void SetEntrySpecifics(const Entry& meta_entry,
sync_pb::SyncEntity* sync_entry) {
sync_entry->mutable_specifics()->CopyFrom(meta_entry.GetSpecifics());
sync_entry->set_folder(meta_entry.GetIsDir());
CHECK(!sync_entry->specifics().password().has_client_only_encrypted_data());
DCHECK_EQ(meta_entry.GetModelType(), GetModelType(*sync_entry));
}
}
void BuildCommitItem(
const syncable::Entry& meta_entry,
sync_pb::SyncEntity* sync_entry) {
syncable::Id id = meta_entry.GetId();
sync_entry->set_id_string(SyncableIdToProto(id));
string name = meta_entry.GetNonUniqueName();
CHECK(!name.empty());
base::TruncateUTF8ToByteSize(name, 255, &name);
sync_entry->set_name(name);
sync_entry->set_non_unique_name(name);
if (!meta_entry.GetUniqueClientTag().empty()) {
sync_entry->set_client_defined_unique_tag(
meta_entry.GetUniqueClientTag());
}
Id new_parent_id;
if (meta_entry.GetIsDel() &&
!meta_entry.GetParentId().ServerKnows()) {
new_parent_id = syncable::BaseTransaction::root_id();
} else {
new_parent_id = meta_entry.GetParentId();
}
sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id));
if (new_parent_id != meta_entry.GetServerParentId() &&
0 != meta_entry.GetBaseVersion() &&
syncable::CHANGES_VERSION != meta_entry.GetBaseVersion()) {
sync_entry->set_old_parent_id(
SyncableIdToProto(meta_entry.GetServerParentId()));
}
int64 version = meta_entry.GetBaseVersion();
if (syncable::CHANGES_VERSION == version || 0 == version) {
DCHECK(!id.ServerKnows() ||
!meta_entry.GetUniqueClientTag().empty())
<< meta_entry;
sync_entry->set_version(0);
} else {
DCHECK(id.ServerKnows()) << meta_entry;
sync_entry->set_version(meta_entry.GetBaseVersion());
}
sync_entry->set_ctime(TimeToProtoTime(meta_entry.GetCtime()));
sync_entry->set_mtime(TimeToProtoTime(meta_entry.GetMtime()));
if (meta_entry.GetIsDel()) {
sync_entry->set_deleted(true);
} else {
if (meta_entry.GetSpecifics().has_bookmark()) {
const Id& prev_id = meta_entry.GetPredecessorId();
string prev_id_string =
prev_id.IsRoot() ? string() : prev_id.GetServerId();
sync_entry->set_insert_after_item_id(prev_id_string);
sync_entry->set_position_in_parent(
meta_entry.GetUniquePosition().ToInt64());
meta_entry.GetUniquePosition().ToProto(
sync_entry->mutable_unique_position());
}
SetEntrySpecifics(meta_entry, sync_entry);
}
}
namespace {
void LogServerError(const sync_pb::CommitResponse_EntryResponse& res) {
if (res.has_error_message())
LOG(WARNING) << " " << res.error_message();
else
LOG(WARNING) << " No detailed error message returned from server";
}
const string& GetResultingPostCommitName(
const sync_pb::SyncEntity& committed_entry,
const sync_pb::CommitResponse_EntryResponse& entry_response) {
const string& response_name =
SyncerProtoUtil::NameFromCommitEntryResponse(entry_response);
if (!response_name.empty())
return response_name;
return SyncerProtoUtil::NameFromSyncEntity(committed_entry);
}
bool UpdateVersionAfterCommit(
const sync_pb::SyncEntity& committed_entry,
const sync_pb::CommitResponse_EntryResponse& entry_response,
const syncable::Id& pre_commit_id,
syncable::ModelNeutralMutableEntry* local_entry) {
int64 old_version = local_entry->GetBaseVersion();
int64 new_version = entry_response.version();
bool bad_commit_version = false;
if (committed_entry.deleted() &&
!local_entry->GetUniqueClientTag().empty()) {
new_version = 0;
} else if (!pre_commit_id.ServerKnows()) {
bad_commit_version = 0 == new_version;
} else {
bad_commit_version = old_version > new_version;
}
if (bad_commit_version) {
LOG(ERROR) << "Bad version in commit return for " << *local_entry
<< " new_id:" << SyncableIdFromProto(entry_response.id_string())
<< " new_version:" << entry_response.version();
return false;
}
local_entry->PutBaseVersion(new_version);
DVLOG(1) << "Commit is changing base version of " << local_entry->GetId()
<< " to: " << new_version;
local_entry->PutServerVersion(new_version);
return true;
}
bool ChangeIdAfterCommit(
const sync_pb::CommitResponse_EntryResponse& entry_response,
const syncable::Id& pre_commit_id,
syncable::ModelNeutralMutableEntry* local_entry) {
syncable::BaseWriteTransaction* trans = local_entry->base_write_transaction();
const syncable::Id& entry_response_id =
SyncableIdFromProto(entry_response.id_string());
if (entry_response_id != pre_commit_id) {
if (pre_commit_id.ServerKnows()) {
DVLOG(1) << " ID changed while committing an old entry. "
<< pre_commit_id << " became " << entry_response_id << ".";
}
syncable::ModelNeutralMutableEntry same_id(
trans,
syncable::GET_BY_ID,
entry_response_id);
if (same_id.good()) {
LOG(ERROR) << "ID clash with id " << entry_response_id
<< " during commit " << same_id;
return false;
}
ChangeEntryIDAndUpdateChildren(trans, local_entry, entry_response_id);
DVLOG(1) << "Changing ID to " << entry_response_id;
}
return true;
}
void UpdateServerFieldsAfterCommit(
const sync_pb::SyncEntity& committed_entry,
const sync_pb::CommitResponse_EntryResponse& entry_response,
syncable::ModelNeutralMutableEntry* local_entry) {
local_entry->PutServerIsDel(committed_entry.deleted());
if (committed_entry.deleted()) {
return;
}
local_entry->PutServerIsDir(
(committed_entry.folder() ||
committed_entry.bookmarkdata().bookmark_folder()));
local_entry->PutServerSpecifics(committed_entry.specifics());
local_entry->PutServerMtime(ProtoTimeToTime(committed_entry.mtime()));
local_entry->PutServerCtime(ProtoTimeToTime(committed_entry.ctime()));
if (committed_entry.has_unique_position()) {
local_entry->PutServerUniquePosition(
UniquePosition::FromProto(
committed_entry.unique_position()));
}
local_entry->PutServerParentId(local_entry->GetParentId());
local_entry->PutServerNonUniqueName(
GetResultingPostCommitName(committed_entry, entry_response));
if (local_entry->GetIsUnappliedUpdate()) {
local_entry->PutIsUnappliedUpdate(false);
}
}
void ProcessSuccessfulCommitResponse(
const sync_pb::SyncEntity& committed_entry,
const sync_pb::CommitResponse_EntryResponse& entry_response,
const syncable::Id& pre_commit_id,
syncable::ModelNeutralMutableEntry* local_entry,
bool syncing_was_set, set<syncable::Id>* deleted_folders) {
DCHECK(local_entry->GetIsUnsynced());
if (!UpdateVersionAfterCommit(committed_entry, entry_response, pre_commit_id,
local_entry)) {
LOG(ERROR) << "Bad version in commit return for " << *local_entry
<< " new_id:" << SyncableIdFromProto(entry_response.id_string())
<< " new_version:" << entry_response.version();
return;
}
if (!ChangeIdAfterCommit(entry_response, pre_commit_id, local_entry)) {
return;
}
UpdateServerFieldsAfterCommit(committed_entry, entry_response, local_entry);
if (syncing_was_set) {
local_entry->PutIsUnsynced(false);
}
if (local_entry->GetIsDir() && local_entry->GetIsDel()) {
deleted_folders->insert(local_entry->GetId());
}
}
}
sync_pb::CommitResponse::ResponseType
ProcessSingleCommitResponse(
syncable::BaseWriteTransaction* trans,
const sync_pb::CommitResponse_EntryResponse& server_entry,
const sync_pb::SyncEntity& commit_request_entry,
int64 metahandle,
set<syncable::Id>* deleted_folders) {
syncable::ModelNeutralMutableEntry local_entry(
trans,
syncable::GET_BY_HANDLE,
metahandle);
CHECK(local_entry.good());
bool syncing_was_set = local_entry.GetSyncing();
local_entry.PutSyncing(false);
sync_pb::CommitResponse::ResponseType response = server_entry.response_type();
if (!sync_pb::CommitResponse::ResponseType_IsValid(response)) {
LOG(ERROR) << "Commit response has unknown response type! Possibly out "
"of date client?";
return sync_pb::CommitResponse::INVALID_MESSAGE;
}
if (sync_pb::CommitResponse::TRANSIENT_ERROR == response) {
DVLOG(1) << "Transient Error Committing: " << local_entry;
LogServerError(server_entry);
return sync_pb::CommitResponse::TRANSIENT_ERROR;
}
if (sync_pb::CommitResponse::INVALID_MESSAGE == response) {
LOG(ERROR) << "Error Commiting: " << local_entry;
LogServerError(server_entry);
return response;
}
if (sync_pb::CommitResponse::CONFLICT == response) {
DVLOG(1) << "Conflict Committing: " << local_entry;
return response;
}
if (sync_pb::CommitResponse::RETRY == response) {
DVLOG(1) << "Retry Committing: " << local_entry;
return response;
}
if (sync_pb::CommitResponse::OVER_QUOTA == response) {
LOG(WARNING) << "Hit deprecated OVER_QUOTA Committing: " << local_entry;
return response;
}
if (!server_entry.has_id_string()) {
LOG(ERROR) << "Commit response has no id";
return sync_pb::CommitResponse::INVALID_MESSAGE;
}
DCHECK_EQ(sync_pb::CommitResponse::SUCCESS, response) << response;
const syncable::Id& server_entry_id =
SyncableIdFromProto(server_entry.id_string());
if (local_entry.GetId() != server_entry_id) {
Entry e(trans, syncable::GET_BY_ID, server_entry_id);
if (e.good()) {
LOG(ERROR)
<< "Got duplicate id when commiting id: "
<< local_entry.GetId()
<< ". Treating as an error return";
return sync_pb::CommitResponse::INVALID_MESSAGE;
}
}
if (server_entry.version() == 0) {
LOG(WARNING) << "Server returned a zero version on a commit response.";
}
ProcessSuccessfulCommitResponse(commit_request_entry, server_entry,
local_entry.GetId(), &local_entry, syncing_was_set, deleted_folders);
return response;
}
}
}