blob: 938b5ac00dd961d2a028b9717f987c6411b6f526 [file] [log] [blame]
// Copyright (c) 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 "components/sync/syncable/entry_kernel.h"
#include <utility>
#include "base/json/string_escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/memory_usage_estimator.h"
#include "components/sync/base/cryptographer.h"
#include "components/sync/protocol/proto_memory_estimations.h"
#include "components/sync/protocol/proto_value_conversions.h"
#include "components/sync/syncable/syncable_columns.h"
#include "components/sync/syncable/syncable_enum_conversions.h"
namespace syncer {
namespace syncable {
EntryKernel::EntryKernel() : dirty_(false), memory_usage_(kMemoryUsageUnknown) {
// Everything else should already be default-initialized.
for (int i = 0; i < INT64_FIELDS_COUNT; ++i) {
int64_fields[i] = 0;
}
}
EntryKernel::EntryKernel(const EntryKernel& other) = default;
EntryKernel::~EntryKernel() {}
ModelType EntryKernel::GetModelType() const {
ModelType specifics_type = GetModelTypeFromSpecifics(ref(SPECIFICS));
if (specifics_type != UNSPECIFIED)
return specifics_type;
if (ref(ID).IsRoot())
return TOP_LEVEL_FOLDER;
// Loose check for server-created top-level folders that aren't
// bound to a particular model type.
if (!ref(UNIQUE_SERVER_TAG).empty() && ref(SERVER_IS_DIR))
return TOP_LEVEL_FOLDER;
return UNSPECIFIED;
}
ModelType EntryKernel::GetServerModelType() const {
ModelType specifics_type = GetModelTypeFromSpecifics(ref(SERVER_SPECIFICS));
if (specifics_type != UNSPECIFIED)
return specifics_type;
if (ref(ID).IsRoot())
return TOP_LEVEL_FOLDER;
// Loose check for server-created top-level folders that aren't
// bound to a particular model type.
if (!ref(UNIQUE_SERVER_TAG).empty() && ref(SERVER_IS_DIR))
return TOP_LEVEL_FOLDER;
return UNSPECIFIED;
}
bool EntryKernel::ShouldMaintainPosition() const {
// We maintain positions for all bookmarks, except those that are
// server-created top-level folders.
return TypeSupportsOrdering(GetModelTypeFromSpecifics(ref(SPECIFICS))) &&
!(!ref(UNIQUE_SERVER_TAG).empty() && ref(IS_DIR));
}
bool EntryKernel::ShouldMaintainHierarchy() const {
// We maintain hierarchy for bookmarks and top-level folders,
// but no other types. Note that the Nigori node consists of a single
// top-level folder, so it's included in this set.
return TypeSupportsHierarchy(GetModelTypeFromSpecifics(ref(SPECIFICS))) ||
(!ref(UNIQUE_SERVER_TAG).empty());
}
namespace {
// Utility function to loop through a set of enum values and add the
// field keys/values in the kernel to the given dictionary.
//
// V should be convertible to Value.
template <class T, class U, class V>
void SetFieldValues(const EntryKernel& kernel,
base::DictionaryValue* dictionary_value,
const char* (*enum_key_fn)(T),
std::unique_ptr<V> (*enum_value_fn)(U),
int field_key_min,
int field_key_max) {
DCHECK_LE(field_key_min, field_key_max);
for (int i = field_key_min; i <= field_key_max; ++i) {
T field = static_cast<T>(i);
const std::string& key = enum_key_fn(field);
std::unique_ptr<V> value = enum_value_fn(kernel.ref(field));
dictionary_value->Set(key, std::move(value));
}
}
void SetEncryptableProtoValues(const EntryKernel& kernel,
Cryptographer* cryptographer,
base::DictionaryValue* dictionary_value,
int field_key_min,
int field_key_max) {
DCHECK_LE(field_key_min, field_key_max);
for (int i = field_key_min; i <= field_key_max; ++i) {
ProtoField field = static_cast<ProtoField>(i);
const std::string& key = GetProtoFieldString(field);
std::unique_ptr<base::DictionaryValue> value;
sync_pb::EntitySpecifics decrypted;
const sync_pb::EncryptedData& encrypted = kernel.ref(field).encrypted();
if (cryptographer && kernel.ref(field).has_encrypted() &&
cryptographer->CanDecrypt(encrypted) &&
cryptographer->Decrypt(encrypted, &decrypted)) {
value = EntitySpecificsToValue(decrypted);
value->SetBoolean("encrypted", true);
} else {
value = EntitySpecificsToValue(kernel.ref(field));
}
dictionary_value->Set(key, std::move(value));
}
}
// Helper functions for SetFieldValues().
std::unique_ptr<base::Value> Int64ToValue(int64_t i) {
return std::make_unique<base::Value>(base::Int64ToString(i));
}
std::unique_ptr<base::Value> TimeToValue(const base::Time& t) {
return std::make_unique<base::Value>(GetTimeDebugString(t));
}
std::unique_ptr<base::Value> IdToValue(const Id& id) {
return id.ToValue();
}
std::unique_ptr<base::Value> BooleanToValue(bool bool_val) {
return std::make_unique<base::Value>(bool_val);
}
std::unique_ptr<base::Value> StringToValue(const std::string& str) {
return std::make_unique<base::Value>(str);
}
std::unique_ptr<base::Value> UniquePositionToValue(const UniquePosition& pos) {
return std::make_unique<base::Value>(pos.ToDebugString());
}
// Estimates memory usage of ProtoValuePtr<T> arrays where consecutive
// elements can share the same value.
template <class T, size_t N>
size_t EstimateSharedMemoryUsage(ProtoValuePtr<T> const (&ptrs)[N]) {
size_t memory_usage = 0;
const T* last_value = nullptr;
for (const auto& ptr : ptrs) {
if (last_value != &ptr.value()) {
memory_usage += EstimateMemoryUsage(ptr);
last_value = &ptr.value();
}
}
return memory_usage;
}
} // namespace
std::unique_ptr<base::DictionaryValue> EntryKernel::ToValue(
Cryptographer* cryptographer) const {
auto kernel_info = std::make_unique<base::DictionaryValue>();
kernel_info->SetBoolean("isDirty", is_dirty());
ModelType dataType = GetServerModelType();
if (!IsRealDataType(dataType))
dataType = GetModelType();
kernel_info->Set("modelType", ModelTypeToValue(dataType));
// Int64 fields.
SetFieldValues(*this, kernel_info.get(), &GetMetahandleFieldString,
&Int64ToValue, INT64_FIELDS_BEGIN, META_HANDLE);
SetFieldValues(*this, kernel_info.get(), &GetBaseVersionString, &Int64ToValue,
META_HANDLE + 1, BASE_VERSION);
SetFieldValues(*this, kernel_info.get(), &GetInt64FieldString, &Int64ToValue,
BASE_VERSION + 1, INT64_FIELDS_END - 1);
// Time fields.
SetFieldValues(*this, kernel_info.get(), &GetTimeFieldString, &TimeToValue,
TIME_FIELDS_BEGIN, TIME_FIELDS_END - 1);
// ID fields.
SetFieldValues(*this, kernel_info.get(), &GetIdFieldString, &IdToValue,
ID_FIELDS_BEGIN, ID_FIELDS_END - 1);
// Bit fields.
SetFieldValues(*this, kernel_info.get(), &GetIndexedBitFieldString,
&BooleanToValue, BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1);
SetFieldValues(*this, kernel_info.get(), &GetIsDelFieldString,
&BooleanToValue, INDEXED_BIT_FIELDS_END, IS_DEL);
SetFieldValues(*this, kernel_info.get(), &GetBitFieldString, &BooleanToValue,
IS_DEL + 1, BIT_FIELDS_END - 1);
// String fields.
{
// Pick out the function overload we want.
SetFieldValues(*this, kernel_info.get(), &GetStringFieldString,
&StringToValue, STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1);
}
// Proto fields.
SetEncryptableProtoValues(*this, cryptographer, kernel_info.get(),
PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
// UniquePosition fields
SetFieldValues(*this, kernel_info.get(), &GetUniquePositionFieldString,
&UniquePositionToValue, UNIQUE_POSITION_FIELDS_BEGIN,
UNIQUE_POSITION_FIELDS_END - 1);
// Bit temps.
SetFieldValues(*this, kernel_info.get(), &GetBitTempString, &BooleanToValue,
BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
return kernel_info;
}
size_t EntryKernel::EstimateMemoryUsage() const {
if (memory_usage_ == kMemoryUsageUnknown) {
using base::trace_event::EstimateMemoryUsage;
memory_usage_ = EstimateMemoryUsage(string_fields) +
EstimateSharedMemoryUsage(specifics_fields) +
EstimateMemoryUsage(id_fields) +
EstimateMemoryUsage(unique_position_fields);
}
return memory_usage_;
}
std::unique_ptr<base::ListValue> EntryKernelMutationMapToValue(
const EntryKernelMutationMap& mutations) {
std::unique_ptr<base::ListValue> list(new base::ListValue());
for (EntryKernelMutationMap::const_iterator it = mutations.begin();
it != mutations.end(); ++it) {
list->Append(EntryKernelMutationToValue(it->second));
}
return list;
}
std::unique_ptr<base::DictionaryValue> EntryKernelMutationToValue(
const EntryKernelMutation& mutation) {
auto dict = std::make_unique<base::DictionaryValue>();
dict->Set("original", mutation.original.ToValue(nullptr));
dict->Set("mutated", mutation.mutated.ToValue(nullptr));
return dict;
}
std::ostream& operator<<(std::ostream& os, const EntryKernel& entry_kernel) {
int i;
EntryKernel* const kernel = const_cast<EntryKernel*>(&entry_kernel);
for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
os << g_metas_columns[i].name << ": "
<< kernel->ref(static_cast<Int64Field>(i)) << ", ";
}
for (; i < TIME_FIELDS_END; ++i) {
os << g_metas_columns[i].name << ": "
<< GetTimeDebugString(kernel->ref(static_cast<TimeField>(i))) << ", ";
}
for (; i < ID_FIELDS_END; ++i) {
os << g_metas_columns[i].name << ": "
<< kernel->ref(static_cast<IdField>(i)) << ", ";
}
os << "Flags: ";
for (; i < BIT_FIELDS_END; ++i) {
if (kernel->ref(static_cast<BitField>(i)))
os << g_metas_columns[i].name << ", ";
}
for (; i < STRING_FIELDS_END; ++i) {
const std::string& field = kernel->ref(static_cast<StringField>(i));
os << g_metas_columns[i].name << ": " << field << ", ";
}
for (; i < PROTO_FIELDS_END; ++i) {
std::string escaped_str = base::EscapeBytesAsInvalidJSONString(
kernel->ref(static_cast<ProtoField>(i)).SerializeAsString(), false);
os << g_metas_columns[i].name << ": " << escaped_str << ", ";
}
for (; i < UNIQUE_POSITION_FIELDS_END; ++i) {
os << g_metas_columns[i].name << ": "
<< kernel->ref(static_cast<UniquePositionField>(i)).ToDebugString()
<< ", ";
}
os << "TempFlags: ";
for (; i < BIT_TEMPS_END; ++i) {
if (kernel->ref(static_cast<BitTemp>(i)))
os << "#" << i - BIT_TEMPS_BEGIN << ", ";
}
return os;
}
} // namespace syncable
} // namespace syncer