| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/notifications/scheduler/internal/proto_conversion.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/notreached.h" |
| #include "base/strings/utf_string_conversions.h" |
| |
| namespace notifications { |
| |
| namespace { |
| |
| // Helper method to convert base::TimeDelta to integer for serialization. Loses |
| // precision beyond miliseconds. |
| int64_t TimeDeltaToMilliseconds(const base::TimeDelta& delta) { |
| return delta.InMilliseconds(); |
| } |
| |
| // Helper method to convert serialized time delta as integer to base::TimeDelta |
| // for deserialization. Loses precision beyond miliseconds. |
| base::TimeDelta MillisecondsToTimeDelta(int64_t serialized_delat_ms) { |
| return base::Milliseconds(serialized_delat_ms); |
| } |
| |
| // Helper method to convert base::Time to integer for serialization. Loses |
| // precision beyond miliseconds. |
| int64_t TimeToMilliseconds(const base::Time& time) { |
| return time.ToDeltaSinceWindowsEpoch().InMilliseconds(); |
| } |
| |
| // Helper method to convert serialized time as integer to base::Time for |
| // deserialization. Loses precision beyond miliseconds. |
| base::Time MillisecondsToTime(int64_t serialized_time_ms) { |
| return base::Time::FromDeltaSinceWindowsEpoch( |
| base::Milliseconds(serialized_time_ms)); |
| } |
| |
| // Converts SchedulerClientType to its associated enum in proto buffer. |
| proto::SchedulerClientType ToSchedulerClientType(SchedulerClientType type) { |
| switch (type) { |
| case SchedulerClientType::kTest1: |
| return proto::SchedulerClientType::TEST_1; |
| case SchedulerClientType::kTest2: |
| return proto::SchedulerClientType::TEST_2; |
| case SchedulerClientType::kTest3: |
| return proto::SchedulerClientType::TEST_3; |
| case SchedulerClientType::kUnknown: |
| return proto::SchedulerClientType::UNKNOWN; |
| case SchedulerClientType::kWebUI: |
| return proto::SchedulerClientType::WEBUI; |
| case SchedulerClientType::kChromeUpdate: |
| return proto::SchedulerClientType::CHROME_UPDATE; |
| case SchedulerClientType::kPrefetch: |
| return proto::SchedulerClientType::PREFETCH; |
| case SchedulerClientType::kReadingList: |
| return proto::SchedulerClientType::READING_LIST; |
| case SchedulerClientType::kFeatureGuide: |
| return proto::SchedulerClientType::FEATURE_GUIDE; |
| } |
| NOTREACHED(); |
| } |
| |
| // Converts SchedulerClientType from its associated enum in proto buffer. |
| SchedulerClientType FromSchedulerClientType( |
| proto::SchedulerClientType proto_type) { |
| switch (proto_type) { |
| case proto::SchedulerClientType::TEST_1: |
| return SchedulerClientType::kTest1; |
| case proto::SchedulerClientType::TEST_2: |
| return SchedulerClientType::kTest2; |
| case proto::SchedulerClientType::TEST_3: |
| return SchedulerClientType::kTest3; |
| case proto::SchedulerClientType::UNKNOWN: |
| return SchedulerClientType::kUnknown; |
| case proto::SchedulerClientType::WEBUI: |
| return SchedulerClientType::kWebUI; |
| case proto::SchedulerClientType::CHROME_UPDATE: |
| return SchedulerClientType::kChromeUpdate; |
| case proto::SchedulerClientType::PREFETCH: |
| return SchedulerClientType::kPrefetch; |
| case proto::SchedulerClientType::READING_LIST: |
| return SchedulerClientType::kReadingList; |
| case proto::SchedulerClientType::FEATURE_GUIDE: |
| return SchedulerClientType::kFeatureGuide; |
| } |
| NOTREACHED(); |
| } |
| |
| // Converts UserFeedback to its associated enum in proto buffer. |
| proto::Impression_UserFeedback ToUserFeedback(UserFeedback feedback) { |
| switch (feedback) { |
| case UserFeedback::kNoFeedback: |
| return proto::Impression_UserFeedback_NO_FEEDBACK; |
| case UserFeedback::kHelpful: |
| return proto::Impression_UserFeedback_HELPFUL; |
| case UserFeedback::kNotHelpful: |
| return proto::Impression_UserFeedback_NOT_HELPFUL; |
| case UserFeedback::kClick: |
| return proto::Impression_UserFeedback_CLICK; |
| case UserFeedback::kDismiss: |
| return proto::Impression_UserFeedback_DISMISS; |
| case UserFeedback::kIgnore: |
| return proto::Impression_UserFeedback_IGNORE; |
| } |
| NOTREACHED(); |
| } |
| |
| // Converts UserFeedback from its associated enum in proto buffer. |
| UserFeedback FromUserFeedback(proto::Impression_UserFeedback feedback) { |
| switch (feedback) { |
| case proto::Impression_UserFeedback_NO_FEEDBACK: |
| return UserFeedback::kNoFeedback; |
| case proto::Impression_UserFeedback_HELPFUL: |
| return UserFeedback::kHelpful; |
| case proto::Impression_UserFeedback_NOT_HELPFUL: |
| return UserFeedback::kNotHelpful; |
| case proto::Impression_UserFeedback_CLICK: |
| return UserFeedback::kClick; |
| case proto::Impression_UserFeedback_DISMISS: |
| return UserFeedback::kDismiss; |
| case proto::Impression_UserFeedback_IGNORE: |
| return UserFeedback::kIgnore; |
| } |
| NOTREACHED(); |
| } |
| |
| // Converts ImpressionResult to its associated enum in proto buffer. |
| proto::Impression_ImpressionResult ToImpressionResult(ImpressionResult result) { |
| switch (result) { |
| case ImpressionResult::kInvalid: |
| return proto::Impression_ImpressionResult_INVALID; |
| case ImpressionResult::kPositive: |
| return proto::Impression_ImpressionResult_POSITIVE; |
| case ImpressionResult::kNegative: |
| return proto::Impression_ImpressionResult_NEGATIVE; |
| case ImpressionResult::kNeutral: |
| return proto::Impression_ImpressionResult_NEUTRAL; |
| } |
| NOTREACHED(); |
| } |
| |
| // Converts ImpressionResult from its associated enum in proto buffer. |
| ImpressionResult FromImpressionResult( |
| proto::Impression_ImpressionResult result) { |
| switch (result) { |
| case proto::Impression_ImpressionResult_INVALID: |
| return ImpressionResult::kInvalid; |
| case proto::Impression_ImpressionResult_POSITIVE: |
| return ImpressionResult::kPositive; |
| case proto::Impression_ImpressionResult_NEGATIVE: |
| return ImpressionResult::kNegative; |
| case proto::Impression_ImpressionResult_NEUTRAL: |
| return ImpressionResult::kNeutral; |
| } |
| NOTREACHED(); |
| } |
| |
| proto::IconType ToIconType(IconType type) { |
| switch (type) { |
| case IconType::kUnknownType: |
| return proto::IconType::UNKNOWN_ICON_TYPE; |
| case IconType::kSmallIcon: |
| return proto::IconType::SMALL_ICON; |
| case IconType::kLargeIcon: |
| return proto::IconType::LARGE_ICON; |
| } |
| NOTREACHED(); |
| } |
| |
| IconType FromIconType(proto::IconType proto_type) { |
| switch (proto_type) { |
| case proto::IconType::UNKNOWN_ICON_TYPE: |
| return IconType::kUnknownType; |
| case proto::IconType::SMALL_ICON: |
| return IconType::kSmallIcon; |
| case proto::IconType::LARGE_ICON: |
| return IconType::kLargeIcon; |
| } |
| NOTREACHED(); |
| } |
| |
| proto::ActionButtonType ToActionButtonType(ActionButtonType type) { |
| switch (type) { |
| case ActionButtonType::kUnknownAction: |
| return proto::ActionButtonType::UNKNOWN_ACTION; |
| case ActionButtonType::kHelpful: |
| return proto::ActionButtonType::HELPFUL; |
| case ActionButtonType::kUnhelpful: |
| return proto::ActionButtonType::UNHELPFUL; |
| } |
| NOTREACHED(); |
| } |
| |
| ActionButtonType FromActionButtonType(proto::ActionButtonType proto_type) { |
| switch (proto_type) { |
| case proto::ActionButtonType::UNKNOWN_ACTION: |
| return ActionButtonType::kUnknownAction; |
| case proto::ActionButtonType::HELPFUL: |
| return ActionButtonType::kHelpful; |
| case proto::ActionButtonType::UNHELPFUL: |
| return ActionButtonType::kUnhelpful; |
| } |
| } |
| |
| // Converts NotificationData to proto buffer type. |
| void NotificationDataToProto(NotificationData* notification_data, |
| proto::NotificationData* proto) { |
| proto->set_title(base::UTF16ToUTF8(notification_data->title)); |
| proto->set_message(base::UTF16ToUTF8(notification_data->message)); |
| for (const auto& pair : notification_data->custom_data) { |
| auto* data = proto->add_custom_data(); |
| data->set_key(pair.first); |
| data->set_value(pair.second); |
| } |
| |
| for (const auto& button : notification_data->buttons) { |
| auto* proto_button = proto->add_buttons(); |
| proto_button->set_text(base::UTF16ToUTF8(button.text)); |
| proto_button->set_button_type(ToActionButtonType(button.type)); |
| proto_button->set_id(button.id); |
| } |
| } |
| |
| // Converts NotificationData from proto buffer type. |
| void NotificationDataFromProto(proto::NotificationData* proto, |
| NotificationData* notification_data) { |
| notification_data->title = base::UTF8ToUTF16(proto->title()); |
| notification_data->message = base::UTF8ToUTF16(proto->message()); |
| for (int i = 0; i < proto->custom_data_size(); ++i) { |
| const auto& pair = proto->custom_data(i); |
| notification_data->custom_data.emplace(pair.key(), pair.value()); |
| } |
| |
| for (int i = 0; i < proto->buttons_size(); ++i) { |
| NotificationData::Button button; |
| const auto& proto_button = proto->buttons(i); |
| button.text = base::UTF8ToUTF16(proto_button.text()); |
| button.type = FromActionButtonType(proto_button.button_type()); |
| button.id = proto_button.id(); |
| notification_data->buttons.emplace_back(button); |
| } |
| } |
| |
| // Converts ScheduleParams::Priority to proto buffer type. |
| proto::ScheduleParams_Priority ScheduleParamsPriorityToProto( |
| ScheduleParams::Priority priority) { |
| using Priority = ScheduleParams::Priority; |
| switch (priority) { |
| case Priority::kLow: |
| return proto::ScheduleParams_Priority_LOW; |
| case Priority::kNoThrottle: |
| return proto::ScheduleParams_Priority_NO_THROTTLE; |
| } |
| } |
| |
| // Converts ScheduleParams::Priority from proto buffer type. |
| ScheduleParams::Priority ScheduleParamsPriorityFromProto( |
| proto::ScheduleParams_Priority priority) { |
| using Priority = ScheduleParams::Priority; |
| switch (priority) { |
| case proto::ScheduleParams_Priority_LOW: |
| return Priority::kLow; |
| case proto::ScheduleParams_Priority_NO_THROTTLE: |
| return Priority::kNoThrottle; |
| } |
| } |
| |
| // Converts ScheduleParams to proto buffer type. |
| void ScheduleParamsToProto(ScheduleParams* params, |
| proto::ScheduleParams* proto) { |
| proto->set_priority(ScheduleParamsPriorityToProto(params->priority)); |
| |
| for (const auto& mapping : params->impression_mapping) { |
| auto* proto_impression_mapping = proto->add_impression_mapping(); |
| proto_impression_mapping->set_user_feedback(ToUserFeedback(mapping.first)); |
| proto_impression_mapping->set_impression_result( |
| ToImpressionResult(mapping.second)); |
| } |
| |
| if (params->deliver_time_start.has_value()) { |
| proto->set_deliver_time_start( |
| TimeToMilliseconds(params->deliver_time_start.value())); |
| } |
| |
| if (params->deliver_time_end.has_value()) { |
| proto->set_deliver_time_end( |
| TimeToMilliseconds(params->deliver_time_end.value())); |
| } |
| |
| if (params->ignore_timeout_duration.has_value()) { |
| proto->set_ignore_timeout_duration( |
| TimeDeltaToMilliseconds(params->ignore_timeout_duration.value())); |
| } |
| } |
| |
| // Converts ScheduleParams from proto buffer type. |
| void ScheduleParamsFromProto(proto::ScheduleParams* proto, |
| ScheduleParams* params) { |
| params->priority = ScheduleParamsPriorityFromProto(proto->priority()); |
| |
| for (int i = 0; i < proto->impression_mapping_size(); ++i) { |
| const auto& proto_impression_mapping = proto->impression_mapping(i); |
| auto user_feedback = |
| FromUserFeedback(proto_impression_mapping.user_feedback()); |
| auto impression_result = |
| FromImpressionResult(proto_impression_mapping.impression_result()); |
| params->impression_mapping[user_feedback] = impression_result; |
| } |
| if (proto->has_deliver_time_start()) { |
| params->deliver_time_start = |
| MillisecondsToTime(proto->deliver_time_start()); |
| } |
| if (proto->has_deliver_time_end()) { |
| params->deliver_time_end = MillisecondsToTime(proto->deliver_time_end()); |
| } |
| if (proto->has_ignore_timeout_duration()) { |
| params->ignore_timeout_duration = |
| MillisecondsToTimeDelta(proto->ignore_timeout_duration()); |
| } |
| } |
| |
| } // namespace |
| |
| void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto) { |
| proto->mutable_icon()->swap(entry->data); |
| } |
| |
| void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry) { |
| DCHECK(proto->has_icon()); |
| entry->data.swap(*proto->mutable_icon()); |
| } |
| |
| void ClientStateToProto(ClientState* client_state, |
| notifications::proto::ClientState* proto) { |
| proto->set_type(ToSchedulerClientType(client_state->type)); |
| proto->set_current_max_daily_show(client_state->current_max_daily_show); |
| |
| for (const auto& impression : client_state->impressions) { |
| auto* impression_ptr = proto->add_impressions(); |
| impression_ptr->set_create_time(TimeToMilliseconds(impression.create_time)); |
| impression_ptr->set_feedback(ToUserFeedback(impression.feedback)); |
| impression_ptr->set_impression(ToImpressionResult(impression.impression)); |
| impression_ptr->set_integrated(impression.integrated); |
| impression_ptr->set_guid(impression.guid); |
| |
| for (const auto& mapping : impression.impression_mapping) { |
| auto* proto_impression_mapping = impression_ptr->add_impression_mapping(); |
| proto_impression_mapping->set_user_feedback( |
| ToUserFeedback(mapping.first)); |
| proto_impression_mapping->set_impression_result( |
| ToImpressionResult(mapping.second)); |
| } |
| |
| for (const auto& pair : impression.custom_data) { |
| auto* data = impression_ptr->add_custom_data(); |
| data->set_key(pair.first); |
| data->set_value(pair.second); |
| } |
| |
| if (impression.ignore_timeout_duration.has_value()) { |
| impression_ptr->set_ignore_timeout_duration( |
| TimeDeltaToMilliseconds(impression.ignore_timeout_duration.value())); |
| } |
| } |
| |
| if (client_state->suppression_info.has_value()) { |
| const auto& suppression = *client_state->suppression_info; |
| auto* suppression_proto = proto->mutable_suppression_info(); |
| suppression_proto->set_last_trigger_time( |
| TimeToMilliseconds(suppression.last_trigger_time)); |
| suppression_proto->set_duration_ms( |
| TimeDeltaToMilliseconds(suppression.duration)); |
| suppression_proto->set_recover_goal(suppression.recover_goal); |
| } |
| |
| proto->set_negative_events_count(client_state->negative_events_count); |
| |
| if (client_state->last_negative_event_ts.has_value()) { |
| proto->set_last_negative_event_ts( |
| TimeToMilliseconds(client_state->last_negative_event_ts.value())); |
| } |
| |
| if (client_state->last_shown_ts.has_value()) { |
| proto->set_last_shown_ts( |
| TimeToMilliseconds(client_state->last_shown_ts.value())); |
| } |
| } |
| |
| void ClientStateFromProto(proto::ClientState* proto, |
| notifications::ClientState* client_state) { |
| DCHECK(proto->has_type()); |
| DCHECK(proto->has_current_max_daily_show()); |
| client_state->type = FromSchedulerClientType(proto->type()); |
| client_state->current_max_daily_show = proto->current_max_daily_show(); |
| |
| for (const auto& proto_impression : proto->impressions()) { |
| Impression impression; |
| DCHECK(proto_impression.has_create_time()); |
| impression.create_time = MillisecondsToTime(proto_impression.create_time()); |
| impression.feedback = FromUserFeedback(proto_impression.feedback()); |
| impression.impression = FromImpressionResult(proto_impression.impression()); |
| impression.integrated = proto_impression.integrated(); |
| impression.guid = proto_impression.guid(); |
| impression.type = client_state->type; |
| |
| if (proto_impression.has_ignore_timeout_duration()) |
| impression.ignore_timeout_duration = |
| MillisecondsToTimeDelta(proto_impression.ignore_timeout_duration()); |
| |
| for (int i = 0; i < proto_impression.impression_mapping_size(); ++i) { |
| const auto& proto_impression_mapping = |
| proto_impression.impression_mapping(i); |
| auto user_feedback = |
| FromUserFeedback(proto_impression_mapping.user_feedback()); |
| auto impression_result = |
| FromImpressionResult(proto_impression_mapping.impression_result()); |
| impression.impression_mapping[user_feedback] = impression_result; |
| } |
| |
| for (int i = 0; i < proto_impression.custom_data_size(); ++i) { |
| const auto& pair = proto_impression.custom_data(i); |
| impression.custom_data.emplace(pair.key(), pair.value()); |
| } |
| |
| client_state->impressions.emplace_back(std::move(impression)); |
| } |
| |
| if (proto->has_suppression_info()) { |
| const auto& proto_suppression = proto->suppression_info(); |
| DCHECK(proto_suppression.has_last_trigger_time()); |
| DCHECK(proto_suppression.has_duration_ms()); |
| DCHECK(proto_suppression.has_recover_goal()); |
| |
| SuppressionInfo suppression_info( |
| MillisecondsToTime(proto_suppression.last_trigger_time()), |
| MillisecondsToTimeDelta(proto_suppression.duration_ms())); |
| suppression_info.recover_goal = proto_suppression.recover_goal(); |
| client_state->suppression_info = std::move(suppression_info); |
| } |
| |
| client_state->negative_events_count = proto->negative_events_count(); |
| |
| if (proto->has_last_shown_ts()) { |
| client_state->last_shown_ts = MillisecondsToTime(proto->last_shown_ts()); |
| } |
| |
| if (proto->has_last_negative_event_ts()) { |
| client_state->last_negative_event_ts = |
| MillisecondsToTime(proto->last_negative_event_ts()); |
| } |
| } |
| |
| void NotificationEntryToProto(NotificationEntry* entry, |
| proto::NotificationEntry* proto) { |
| proto->set_type(ToSchedulerClientType(entry->type)); |
| proto->set_guid(entry->guid); |
| proto->set_create_time(TimeToMilliseconds(entry->create_time)); |
| auto* proto_notification_data = proto->mutable_notification_data(); |
| for (const auto& icon_type_uuid_pair : entry->icons_uuid) { |
| auto* proto_icons = proto_notification_data->add_icons_uuid(); |
| proto_icons->set_type(ToIconType(icon_type_uuid_pair.first)); |
| proto_icons->set_uuid(icon_type_uuid_pair.second); |
| } |
| NotificationDataToProto(&entry->notification_data, proto_notification_data); |
| |
| auto* proto_schedule_params = proto->mutable_schedule_params(); |
| ScheduleParamsToProto(&entry->schedule_params, proto_schedule_params); |
| } |
| |
| void NotificationEntryFromProto(proto::NotificationEntry* proto, |
| NotificationEntry* entry) { |
| entry->type = FromSchedulerClientType(proto->type()); |
| entry->guid = proto->guid(); |
| entry->create_time = MillisecondsToTime(proto->create_time()); |
| NotificationDataFromProto(proto->mutable_notification_data(), |
| &entry->notification_data); |
| ScheduleParamsFromProto(proto->mutable_schedule_params(), |
| &entry->schedule_params); |
| |
| for (int i = 0; i < proto->notification_data().icons_uuid_size(); i++) { |
| const auto& icon_uuid_pair = proto->notification_data().icons_uuid(i); |
| entry->icons_uuid.emplace(FromIconType(icon_uuid_pair.type()), |
| icon_uuid_pair.uuid()); |
| } |
| } |
| |
| } // namespace notifications |