| // Copyright 2012 The Chromium Authors |
| // 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/memory/raw_ptr.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "components/sync/base/unique_position.h" |
| #include "components/sync/protocol/account_setting_specifics.pb.h" |
| #include "components/sync/protocol/app_list_specifics.pb.h" |
| #include "components/sync/protocol/app_setting_specifics.pb.h" |
| #include "components/sync/protocol/app_specifics.pb.h" |
| #include "components/sync/protocol/arc_package_specifics.pb.h" |
| #include "components/sync/protocol/autofill_offer_specifics.pb.h" |
| #include "components/sync/protocol/autofill_specifics.pb.h" |
| #include "components/sync/protocol/bookmark_specifics.pb.h" |
| #include "components/sync/protocol/collaboration_group_specifics.pb.h" |
| #include "components/sync/protocol/contact_info_specifics.pb.h" |
| #include "components/sync/protocol/cookie_specifics.pb.h" |
| #include "components/sync/protocol/data_type_progress_marker.pb.h" |
| #include "components/sync/protocol/dictionary_specifics.pb.h" |
| #include "components/sync/protocol/entity_specifics.pb.h" |
| #include "components/sync/protocol/extension_setting_specifics.pb.h" |
| #include "components/sync/protocol/extension_specifics.pb.h" |
| #include "components/sync/protocol/history_delete_directive_specifics.pb.h" |
| #include "components/sync/protocol/history_specifics.pb.h" |
| #include "components/sync/protocol/nigori_specifics.pb.h" |
| #include "components/sync/protocol/os_preference_specifics.pb.h" |
| #include "components/sync/protocol/os_priority_preference_specifics.pb.h" |
| #include "components/sync/protocol/password_sharing_invitation_specifics.pb.h" |
| #include "components/sync/protocol/password_specifics.pb.h" |
| #include "components/sync/protocol/plus_address_setting_specifics.pb.h" |
| #include "components/sync/protocol/plus_address_specifics.pb.h" |
| #include "components/sync/protocol/preference_specifics.pb.h" |
| #include "components/sync/protocol/printer_specifics.pb.h" |
| #include "components/sync/protocol/printers_authorization_server_specifics.pb.h" |
| #include "components/sync/protocol/priority_preference_specifics.pb.h" |
| #include "components/sync/protocol/product_comparison_specifics.pb.h" |
| #include "components/sync/protocol/proto_visitors.h" |
| #include "components/sync/protocol/reading_list_specifics.pb.h" |
| #include "components/sync/protocol/saved_tab_group_specifics.pb.h" |
| #include "components/sync/protocol/search_engine_specifics.pb.h" |
| #include "components/sync/protocol/send_tab_to_self_specifics.pb.h" |
| #include "components/sync/protocol/session_specifics.pb.h" |
| #include "components/sync/protocol/shared_comment_specifics.pb.h" |
| #include "components/sync/protocol/sharing_message_specifics.pb.h" |
| #include "components/sync/protocol/sync.pb.h" |
| #include "components/sync/protocol/sync_entity.pb.h" |
| #include "components/sync/protocol/theme_specifics.pb.h" |
| #include "components/sync/protocol/typed_url_specifics.pb.h" |
| #include "components/sync/protocol/user_consent_specifics.pb.h" |
| #include "components/sync/protocol/user_event_specifics.pb.h" |
| #include "components/sync/protocol/web_apk_specifics.pb.h" |
| #include "components/sync/protocol/web_app_specifics.pb.h" |
| #include "components/sync/protocol/webauthn_credential_specifics.pb.h" |
| #include "components/sync/protocol/workspace_desk_specifics.pb.h" |
| |
| namespace syncer { |
| |
| namespace { |
| |
| // ToValueVisitor is a VisitProtoFields()-compatible visitor that serializes |
| // protos to base::Value. 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> |
| // base::Value 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 ToValueDictImpl(). |
| // |
| // For example let's say you want to clobber a sensitive field: |
| // |
| // base::Value ToValue(const sync_pb::GreenProto& proto) const { |
| // base::Value::Dict value = ToValueDictImpl(proto); |
| // value.Set("secret", "<clobbered>"); |
| // return base::Value(value); |
| // } |
| // |
| // ToValue() doesn't have to return a dictionary though. It might |
| // be more appropriate to serialize GreenProto into a string instead: |
| // |
| // base::Value ToValue(const sync_pb::GreenProto& proto) const { |
| // return base::Value(proto.content()); |
| // } |
| // |
| class ToValueVisitor { |
| public: |
| explicit ToValueVisitor(const ProtoValueConversionOptions& options = |
| ProtoValueConversionOptions(), |
| base::Value::Dict* value = nullptr) |
| : options_(options), value_(value) {} |
| |
| template <class P> |
| void VisitBytes(const P& parent_proto, |
| const char* field_name, |
| const std::string& field) { |
| value_->Set(field_name, |
| base::Base64Encode(base::as_bytes(base::span(field)))); |
| } |
| |
| template <class P> |
| void VisitBytes( |
| const P& parent_proto, |
| const char* field_name, |
| const google::protobuf::RepeatedPtrField<std::string>& repeated_field) { |
| if (!repeated_field.empty()) { |
| base::Value::List list; |
| for (const auto& field : repeated_field) { |
| list.Append(base::Base64Encode(base::as_byte_span(field))); |
| } |
| value_->Set(field_name, std::move(list)); |
| } |
| } |
| |
| template <class P> |
| void VisitSecret(const P& parent_proto, |
| const char* field_name, |
| const std::string& field) { |
| value_->Set(field_name, |
| base::StringPrintf("<%zu-byte secret>", field.size())); |
| } |
| |
| template <class P, class E> |
| void VisitEnum(const P& parent_proto, const char* field_name, E field) { |
| value_->Set(field_name, ProtoEnumToString(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()) { |
| base::Value::List list; |
| 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()) { |
| base::Value::List list; |
| 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> |
| base::Value ToValue(const P& proto) const { |
| return base::Value(ToValueDictImpl(proto)); |
| } |
| |
| // Customizations |
| |
| // EntitySpecifics |
| template <class P> |
| void Visit(const P& parent_proto, |
| const char* field_name, |
| const sync_pb::EntitySpecifics& field) { |
| if (options_.include_specifics) { |
| VisitImpl(parent_proto, field_name, field); |
| } |
| } |
| |
| // GetUpdateTriggers. |
| base::Value ToValue(const sync_pb::GetUpdateTriggers& proto) const { |
| base::Value::Dict dict = ToValueDictImpl(proto); |
| if (!options_.include_full_get_update_triggers) { |
| if (!proto.client_dropped_hints()) { |
| dict.Remove("client_dropped_hints"); |
| } |
| if (!proto.invalidations_out_of_sync()) { |
| dict.Remove("invalidations_out_of_sync"); |
| } |
| if (proto.local_modification_nudges() == 0) { |
| dict.Remove("local_modification_nudges"); |
| } |
| if (proto.datatype_refresh_nudges() == 0) { |
| dict.Remove("datatype_refresh_nudges"); |
| } |
| if (!proto.server_dropped_hints()) { |
| dict.Remove("server_dropped_hints"); |
| } |
| if (!proto.initial_sync_in_progress()) { |
| dict.Remove("initial_sync_in_progress"); |
| } |
| if (!proto.sync_for_resolve_conflict_in_progress()) { |
| dict.Remove("sync_for_resolve_conflict_in_progress"); |
| } |
| } |
| return base::Value(std::move(dict)); |
| } |
| |
| // AutofillWalletSpecifics |
| base::Value ToValue(const sync_pb::AutofillWalletSpecifics& proto) const { |
| base::Value::Dict dict = ToValueDictImpl(proto); |
| // TODO(crbug.com/40252694): consider whether the VISIT_SECRET macro in |
| // proto_visitors.h could replace this. |
| if (proto.type() != sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS) { |
| dict.Remove("address"); |
| } |
| if (proto.type() != sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD) { |
| dict.Remove("masked_card"); |
| } |
| if (proto.type() != sync_pb::AutofillWalletSpecifics::CUSTOMER_DATA) { |
| dict.Remove("customer_data"); |
| } |
| if (proto.type() != |
| sync_pb::AutofillWalletSpecifics::CREDIT_CARD_CLOUD_TOKEN_DATA) { |
| dict.Remove("cloud_token_data"); |
| } |
| if (proto.type() != sync_pb::AutofillWalletSpecifics::PAYMENT_INSTRUMENT) { |
| dict.Remove("payment_instrument"); |
| } |
| return base::Value(std::move(dict)); |
| } |
| |
| // UniquePosition |
| base::Value ToValue(const sync_pb::UniquePosition& proto) const { |
| UniquePosition pos = UniquePosition::FromProto(proto); |
| return base::Value(pos.ToDebugString()); |
| } |
| |
| private: |
| template <class P> |
| base::Value::Dict ToValueDictImpl(const P& proto) const { |
| base::Value::Dict dict; |
| ToValueVisitor visitor(options_, &dict); |
| VisitProtoFields(visitor, proto); |
| return dict; |
| } |
| |
| base::Value ToValue(const std::string& value) const { |
| return base::Value(value); |
| } |
| |
| base::Value ToValue(int64_t value) const { |
| return base::Value(base::NumberToString(value)); |
| } |
| base::Value ToValue(uint64_t value) const { |
| return base::Value(base::NumberToString(value)); |
| } |
| base::Value ToValue(uint32_t value) const { |
| return base::Value(base::NumberToString(value)); |
| } |
| base::Value ToValue(int32_t value) const { |
| return base::Value(base::NumberToString(value)); |
| } |
| |
| base::Value ToValue(bool value) const { return base::Value(value); } |
| base::Value ToValue(float value) const { return base::Value(value); } |
| base::Value ToValue(double value) const { return 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)); |
| } |
| |
| const ProtoValueConversionOptions options_; |
| const raw_ptr<base::Value::Dict> value_; |
| }; |
| |
| } // namespace |
| |
| #define IMPLEMENT_PROTO_TO_VALUE(Proto) \ |
| base::Value Proto##ToValue(const sync_pb::Proto& proto) { \ |
| return ToValueVisitor().ToValue(proto); \ |
| } |
| |
| #define IMPLEMENT_PROTO_TO_VALUE_WITH_OPTIONS(Proto) \ |
| base::Value Proto##ToValue(const sync_pb::Proto& proto, \ |
| const ProtoValueConversionOptions& options) { \ |
| return ToValueVisitor(options).ToValue(proto); \ |
| } |
| |
| IMPLEMENT_PROTO_TO_VALUE(AccountSettingSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AppListSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AppSettingSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AppSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(ArcPackageSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillOfferSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillProfileSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillValuableMetadataSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillWalletCredentialSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillWalletSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(AutofillWalletUsageSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(BankAccountDetails) |
| IMPLEMENT_PROTO_TO_VALUE(BookmarkSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(ClientConfigParams) |
| IMPLEMENT_PROTO_TO_VALUE(CollaborationGroupSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(ContactInfoSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(CookieSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(CrossUserSharingPublicKey) |
| IMPLEMENT_PROTO_TO_VALUE(DebugEventInfo) |
| IMPLEMENT_PROTO_TO_VALUE(DebugInfo) |
| IMPLEMENT_PROTO_TO_VALUE(DeviceDetails) |
| 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(EwalletDetails) |
| IMPLEMENT_PROTO_TO_VALUE(ExtensionSettingSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(ExtensionSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(GlobalIdDirective) |
| IMPLEMENT_PROTO_TO_VALUE(HistoryDeleteDirectiveSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(HistorySpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(IncomingPasswordSharingInvitationSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(LinkedAppIconInfo) |
| IMPLEMENT_PROTO_TO_VALUE(ManagedUserSettingSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(NavigationRedirect) |
| IMPLEMENT_PROTO_TO_VALUE(NigoriSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(OsPreferenceSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(OsPriorityPreferenceSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(OutgoingPasswordSharingInvitationSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PasswordSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PasswordSpecificsData) |
| IMPLEMENT_PROTO_TO_VALUE(PasswordSpecificsData_Notes) |
| IMPLEMENT_PROTO_TO_VALUE(PasswordSpecificsData_Notes_Note) |
| IMPLEMENT_PROTO_TO_VALUE(PaymentInstrument) |
| IMPLEMENT_PROTO_TO_VALUE(PaymentsCustomerData) |
| IMPLEMENT_PROTO_TO_VALUE(PlusAddressSettingSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PlusAddressSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PowerBookmarkSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PreferenceSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PrinterPPDReference) |
| IMPLEMENT_PROTO_TO_VALUE(PrinterSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PrintersAuthorizationServerSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(PriorityPreferenceSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(ProductComparisonSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(ReadingListSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SavedTabGroupSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SearchEngineSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SecurityEventSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SendTabToSelfPush) |
| IMPLEMENT_PROTO_TO_VALUE(SendTabToSelfSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SessionHeader) |
| IMPLEMENT_PROTO_TO_VALUE(SessionSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SessionTab) |
| IMPLEMENT_PROTO_TO_VALUE(SessionWindow) |
| IMPLEMENT_PROTO_TO_VALUE(SharedCommentSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SharingMessageSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(SyncCycleCompletedEventInfo) |
| IMPLEMENT_PROTO_TO_VALUE(TabNavigation) |
| IMPLEMENT_PROTO_TO_VALUE(ThemeSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(TimeRangeDirective) |
| IMPLEMENT_PROTO_TO_VALUE(TypedUrlSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(UnencryptedSharingMessage) |
| IMPLEMENT_PROTO_TO_VALUE(UrlDirective) |
| IMPLEMENT_PROTO_TO_VALUE(UserConsentSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(UserEventSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(WalletCreditCardCloudTokenData) |
| IMPLEMENT_PROTO_TO_VALUE(WalletMaskedCreditCard) |
| IMPLEMENT_PROTO_TO_VALUE(WalletMetadataSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(WalletPostalAddress) |
| IMPLEMENT_PROTO_TO_VALUE(WebApkSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(WebAppSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(WebauthnCredentialSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(WifiConfigurationSpecifics) |
| IMPLEMENT_PROTO_TO_VALUE(WorkspaceDeskSpecifics) |
| |
| IMPLEMENT_PROTO_TO_VALUE_WITH_OPTIONS(ClientToServerMessage) |
| IMPLEMENT_PROTO_TO_VALUE_WITH_OPTIONS(ClientToServerResponse) |
| IMPLEMENT_PROTO_TO_VALUE_WITH_OPTIONS(SyncEntity) |
| |
| #undef IMPLEMENT_PROTO_TO_VALUE |
| #undef IMPLEMENT_PROTO_TO_VALUE_WITH_OPTIONS |
| |
| } // namespace syncer |