|  | // 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/protocol/proto_value_conversions.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <string> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/base64.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/values.h" | 
|  | #include "components/sync/base/unique_position.h" | 
|  | #include "components/sync/protocol/proto_visitors.h" | 
|  |  | 
|  | namespace syncer { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // ToValueVisitor is a VisitProtoFields()-compatible visitor that serializes | 
|  | // protos to base::DictionaryValues. To serialize a proto you call ToValue() | 
|  | // method: | 
|  | // | 
|  | //  ToValueVisitor visitor; | 
|  | //  auto value = visitor.ToValue(proto); | 
|  | // | 
|  | // By default all fields visited by VisitProtoFields() are serialized, but | 
|  | // there are several ways to customize that on per-field / per-proto basis: | 
|  | // | 
|  | // 1. If you want to change how fields of a particular proto type are | 
|  | //    serialized, customize Visit() method: | 
|  | // | 
|  | //    template <class P, class F> | 
|  | //    void Visit(const P& parent_proto, | 
|  | //               const char* field_name, const F& field); | 
|  | // | 
|  | //    By default Visit() serializes |field| and sets it to |value_| under | 
|  | //    |field_name| name. Default implementation is accessible via VisitImpl(). | 
|  | // | 
|  | //    For example here is how you would serialize only GreenProto::content | 
|  | //    for all GreenProto fields: | 
|  | // | 
|  | //    template <class P> | 
|  | //    void Visit(const P& parent_proto, | 
|  | //               const char* field_name, const sync_pb::GreenProto& field) { | 
|  | //      if (field.has_content()) { | 
|  | //        value_->Set(field_name, field.content()); | 
|  | //      } | 
|  | //    } | 
|  | // | 
|  | //    You can further fine-tune this method by specifying parent proto. For | 
|  | //    example let's say we don't want to serialize fields of type GreenProto | 
|  | //    that are contained in RedProto: | 
|  | // | 
|  | //    void Visit(const sync_pb::RedProto& parent_proto, | 
|  | //               const char* field_name, const sync_pb::GreenProto& field) {} | 
|  | // | 
|  | //    Note: Visit() method only called to serialize fields, and doesn't | 
|  | //    affect top level protos. I.e. ToValueVisitor().ToValue(GreenProto) | 
|  | //    won't call methods above. | 
|  | // | 
|  | // 2. If you want to change how proto itself is serialized, you need to | 
|  | //    customize ToValue() method: | 
|  | // | 
|  | //    template <class P> | 
|  | //    std::unique_ptr<base::DictionaryValue> ToValue(const P& proto) const; | 
|  | // | 
|  | //    By default ToValue() creates new instance of ToValueVisitor, calls | 
|  | //    VisitProtoFields(visitor, |proto|) and returns visitor's |value_|. | 
|  | //    Default implementation is accessible via ToValueImpl(). | 
|  | // | 
|  | //    For example let's say you want to clobber a sensitive field: | 
|  | // | 
|  | //    std::unique_ptr<base::DictionaryValue> ToValue( | 
|  | //        const sync_pb::GreenProto& proto) const { | 
|  | //      auto value = ToValueImpl(proto); | 
|  | //      value->SetString("secret", "<clobbered>"); | 
|  | //      return value; | 
|  | //    } | 
|  | // | 
|  | //    ToValue() doesn't have to return base::DictionaryValue though. It might | 
|  | //    be more appropriate to serialize GreenProto into a string instead: | 
|  | // | 
|  | //    std::unique_ptr<base::Value> ToValue( | 
|  | //        const sync_pb::GreenProto& proto) const { | 
|  | //      return std::make_unique<base::Value>(proto.content()); | 
|  | //    } | 
|  | // | 
|  | class ToValueVisitor { | 
|  | public: | 
|  | ToValueVisitor(bool include_specifics = true, | 
|  | base::DictionaryValue* value = nullptr) | 
|  | : value_(value) | 
|  | , include_specifics_(include_specifics) {} | 
|  |  | 
|  | template <class P> | 
|  | void VisitBytes(const P& parent_proto, | 
|  | const char* field_name, | 
|  | const std::string& field) { | 
|  | value_->Set(field_name, BytesToValue(field)); | 
|  | } | 
|  |  | 
|  | template <class P, class E> | 
|  | void VisitEnum(const P& parent_proto, const char* field_name, E field) { | 
|  | value_->Set(field_name, EnumToValue(field)); | 
|  | } | 
|  |  | 
|  | template <class P, class F> | 
|  | void Visit(const P& parent_proto, | 
|  | const char* field_name, | 
|  | const google::protobuf::RepeatedPtrField<F>& repeated_field) { | 
|  | if (!repeated_field.empty()) { | 
|  | std::unique_ptr<base::ListValue> list(new base::ListValue()); | 
|  | for (const auto& field : repeated_field) { | 
|  | list->Append(ToValue(field)); | 
|  | } | 
|  | value_->Set(field_name, std::move(list)); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <class P, class F> | 
|  | void Visit(const P& parent_proto, | 
|  | const char* field_name, | 
|  | const google::protobuf::RepeatedField<F>& repeated_field) { | 
|  | if (!repeated_field.empty()) { | 
|  | std::unique_ptr<base::ListValue> list(new base::ListValue()); | 
|  | for (const auto& field : repeated_field) { | 
|  | list->Append(ToValue(field)); | 
|  | } | 
|  | value_->Set(field_name, std::move(list)); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <class P, class F> | 
|  | void Visit(const P& parent_proto, const char* field_name, const F& field) { | 
|  | VisitImpl(parent_proto, field_name, field); | 
|  | } | 
|  |  | 
|  | template <class P> | 
|  | std::unique_ptr<base::DictionaryValue> ToValue(const P& proto) const { | 
|  | return ToValueImpl(proto); | 
|  | } | 
|  |  | 
|  | // Customizations | 
|  |  | 
|  | // ExperimentsSpecifics flags | 
|  | #define IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(Name) \ | 
|  | void Visit(const sync_pb::ExperimentsSpecifics&, \ | 
|  | const char* field_name, \ | 
|  | const sync_pb::Name& field) { \ | 
|  | if (field.has_enabled()) { \ | 
|  | Visit(field, field_name, field.enabled()); \ | 
|  | } \ | 
|  | } | 
|  | IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(KeystoreEncryptionFlags) | 
|  | IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(HistoryDeleteDirectives) | 
|  | IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(AutofillCullingFlags) | 
|  | IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(PreCommitUpdateAvoidanceFlags) | 
|  | IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(GcmChannelFlags) | 
|  | IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD(GcmInvalidationsFlags) | 
|  | #undef IMPLEMENT_VISIT_EXPERIMENT_ENABLED_FIELD | 
|  |  | 
|  | // EntitySpecifics | 
|  | template <class P> | 
|  | void Visit(const P& parent_proto, | 
|  | const char* field_name, | 
|  | const sync_pb::EntitySpecifics& field) { | 
|  | if (include_specifics_) { | 
|  | VisitImpl(parent_proto, field_name, field); | 
|  | } | 
|  | } | 
|  |  | 
|  | // EnhancedBookmarksFlags | 
|  | template <class P> | 
|  | void Visit(const P& parent_proto, | 
|  | const char* field_name, | 
|  | const sync_pb::EnhancedBookmarksFlags& field) { | 
|  | // Obsolete, don't visit | 
|  | } | 
|  |  | 
|  | // WalletSyncFlags | 
|  | template <class P> | 
|  | void Visit(const P& parent_proto, | 
|  | const char* field_name, | 
|  | const sync_pb::WalletSyncFlags& field) { | 
|  | // Obsolete, don't visit | 
|  | } | 
|  |  | 
|  | // PasswordSpecifics | 
|  | std::unique_ptr<base::DictionaryValue> ToValue( | 
|  | const sync_pb::PasswordSpecifics& proto) const { | 
|  | auto value = ToValueImpl(proto); | 
|  | value->Remove("client_only_encrypted_data", nullptr); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | // PasswordSpecificsData | 
|  | std::unique_ptr<base::DictionaryValue> ToValue( | 
|  | const sync_pb::PasswordSpecificsData& proto) const { | 
|  | auto value = ToValueImpl(proto); | 
|  | value->SetString("password_value", "<redacted>"); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | // AutofillWalletSpecifics | 
|  | std::unique_ptr<base::DictionaryValue> ToValue( | 
|  | const sync_pb::AutofillWalletSpecifics& proto) const { | 
|  | auto value = ToValueImpl(proto); | 
|  | if (proto.type() != sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS) { | 
|  | value->Remove("address", nullptr); | 
|  | } | 
|  | if (proto.type() != sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD) { | 
|  | value->Remove("masked_card", nullptr); | 
|  | } | 
|  | if (proto.type() != sync_pb::AutofillWalletSpecifics::CUSTOMER_DATA) { | 
|  | value->Remove("customer_data", nullptr); | 
|  | } | 
|  | return value; | 
|  | } | 
|  |  | 
|  | // UniquePosition | 
|  | std::unique_ptr<base::Value> ToValue( | 
|  | const sync_pb::UniquePosition& proto) const { | 
|  | UniquePosition pos = UniquePosition::FromProto(proto); | 
|  | return std::make_unique<base::Value>(pos.ToDebugString()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | template <class P> | 
|  | std::unique_ptr<base::DictionaryValue> ToValueImpl(const P& proto) const { | 
|  | auto value = std::make_unique<base::DictionaryValue>(); | 
|  | ToValueVisitor visitor(include_specifics_, value.get()); | 
|  | VisitProtoFields(visitor, proto); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | static std::unique_ptr<base::Value> BytesToValue(const std::string& bytes) { | 
|  | std::string bytes_base64; | 
|  | base::Base64Encode(bytes, &bytes_base64); | 
|  | return std::make_unique<base::Value>(bytes_base64); | 
|  | } | 
|  |  | 
|  | template <class E> | 
|  | static std::unique_ptr<base::Value> EnumToValue(E value) { | 
|  | return std::make_unique<base::Value>(ProtoEnumToString(value)); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::Value> ToValue(const std::string& value) const { | 
|  | return std::make_unique<base::Value>(value); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::Value> ToValue(int64_t value) const { | 
|  | return std::make_unique<base::Value>(base::Int64ToString(value)); | 
|  | } | 
|  | std::unique_ptr<base::Value> ToValue(uint64_t value) const { | 
|  | return std::make_unique<base::Value>(base::Int64ToString(value)); | 
|  | } | 
|  | std::unique_ptr<base::Value> ToValue(uint32_t value) const { | 
|  | return std::make_unique<base::Value>(base::Int64ToString(value)); | 
|  | } | 
|  | std::unique_ptr<base::Value> ToValue(int32_t value) const { | 
|  | return std::make_unique<base::Value>(base::Int64ToString(value)); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::Value> ToValue(bool value) const { | 
|  | return std::make_unique<base::Value>(value); | 
|  | } | 
|  | std::unique_ptr<base::Value> ToValue(float value) const { | 
|  | return std::make_unique<base::Value>(value); | 
|  | } | 
|  | std::unique_ptr<base::Value> ToValue(double value) const { | 
|  | return std::make_unique<base::Value>(value); | 
|  | } | 
|  |  | 
|  | // Needs to be here to see all ToValue() overloads above. | 
|  | template <class P, class F> | 
|  | void VisitImpl(P&, const char* field_name, const F& field) { | 
|  | value_->Set(field_name, ToValue(field)); | 
|  | } | 
|  |  | 
|  | base::DictionaryValue* value_; | 
|  | bool include_specifics_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #define IMPLEMENT_PROTO_TO_VALUE(Proto) \ | 
|  | std::unique_ptr<base::DictionaryValue> Proto##ToValue( \ | 
|  | const sync_pb::Proto& proto) { \ | 
|  | return ToValueVisitor().ToValue(proto); \ | 
|  | } | 
|  |  | 
|  | #define IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS(Proto) \ | 
|  | std::unique_ptr<base::DictionaryValue> Proto##ToValue( \ | 
|  | const sync_pb::Proto& proto, \ | 
|  | bool include_specifics) { \ | 
|  | return ToValueVisitor(include_specifics).ToValue(proto); \ | 
|  | } | 
|  |  | 
|  | IMPLEMENT_PROTO_TO_VALUE(AppListSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AppNotification) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AppNotificationSettings) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AppSettingSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AppSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ArcPackageSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ArticleSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AutofillProfileSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AutofillSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(AutofillWalletSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(BookmarkSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ClientConfigParams) | 
|  | IMPLEMENT_PROTO_TO_VALUE(DatatypeAssociationStats) | 
|  | IMPLEMENT_PROTO_TO_VALUE(DebugEventInfo) | 
|  | IMPLEMENT_PROTO_TO_VALUE(DebugInfo) | 
|  | IMPLEMENT_PROTO_TO_VALUE(DeviceInfoSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(DictionarySpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(EncryptedData) | 
|  | IMPLEMENT_PROTO_TO_VALUE(EntityMetadata) | 
|  | IMPLEMENT_PROTO_TO_VALUE(EntitySpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ExperimentsSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ExtensionSettingSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ExtensionSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(FaviconImageSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(FaviconTrackingSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(GlobalIdDirective) | 
|  | IMPLEMENT_PROTO_TO_VALUE(HistoryDeleteDirectiveSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(LinkedAppIconInfo) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ManagedUserSettingSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ManagedUserSharedSettingSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ManagedUserSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ManagedUserWhitelistSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(MountainShareSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(NavigationRedirect) | 
|  | IMPLEMENT_PROTO_TO_VALUE(NigoriSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PasswordSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PasswordSpecificsData) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PaymentsCustomerData) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PreferenceSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PrinterPPDReference) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PrinterSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(PriorityPreferenceSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ReadingListSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SearchEngineSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SessionHeader) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SessionSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SessionTab) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SessionWindow) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SyncCycleCompletedEventInfo) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SyncedNotificationAppInfoSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(SyncedNotificationSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(TabNavigation) | 
|  | IMPLEMENT_PROTO_TO_VALUE(ThemeSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(TimeRangeDirective) | 
|  | IMPLEMENT_PROTO_TO_VALUE(TypedUrlSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(UserConsentSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(UserEventSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(WalletMaskedCreditCard) | 
|  | IMPLEMENT_PROTO_TO_VALUE(WalletMetadataSpecifics) | 
|  | IMPLEMENT_PROTO_TO_VALUE(WalletPostalAddress) | 
|  | IMPLEMENT_PROTO_TO_VALUE(WifiCredentialSpecifics) | 
|  |  | 
|  | IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS(ClientToServerMessage) | 
|  | IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS(ClientToServerResponse) | 
|  | IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS(SyncEntity) | 
|  |  | 
|  | #undef IMPLEMENT_PROTO_TO_VALUE | 
|  | #undef IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS | 
|  |  | 
|  | }  // namespace syncer |