Fix emulator build error
[platform/framework/web/chromium-efl.git] / components / browsing_topics / epoch_topics.cc
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/browsing_topics/epoch_topics.h"
6
7 #include "base/containers/contains.h"
8 #include "base/hash/legacy_hash.h"
9 #include "base/json/values_util.h"
10 #include "base/logging.h"
11 #include "base/numerics/checked_math.h"
12 #include "components/browsing_topics/common/semantic_tree.h"
13 #include "components/browsing_topics/util.h"
14 #include "third_party/blink/public/common/features.h"
15 #include "url/gurl.h"
16
17 namespace browsing_topics {
18
19 namespace {
20
21 const char kTopTopicsAndObservingDomainsNameKey[] =
22     "top_topics_and_observing_domains";
23 const char kPaddedTopTopicsStartIndexNameKey[] =
24     "padded_top_topics_start_index";
25 const char kConfigVersionNameKey[] = "config_version";
26 const char kTaxonomyVersionNameKey[] = "taxonomy_version";
27 const char kModelVersionNameKey[] = "model_version";
28 const char kCalculationTimeNameKey[] = "calculation_time";
29 // `taxonomy_size` is a deprecated key. Do not reuse.
30
31 bool ShouldUseRandomTopic(uint64_t random_or_top_topic_decision_hash) {
32   return base::checked_cast<int>(random_or_top_topic_decision_hash % 100) <
33          blink::features::kBrowsingTopicsUseRandomTopicProbabilityPercent.Get();
34 }
35
36 }  // namespace
37
38 EpochTopics::EpochTopics(base::Time calculation_time)
39     : calculation_time_(calculation_time) {}
40
41 EpochTopics::EpochTopics(
42     std::vector<TopicAndDomains> top_topics_and_observing_domains,
43     size_t padded_top_topics_start_index,
44     int config_version,
45     int taxonomy_version,
46     int64_t model_version,
47     base::Time calculation_time,
48     bool from_manually_triggered_calculation)
49     : top_topics_and_observing_domains_(
50           std::move(top_topics_and_observing_domains)),
51       padded_top_topics_start_index_(padded_top_topics_start_index),
52       config_version_(config_version),
53       taxonomy_version_(taxonomy_version),
54       model_version_(model_version),
55       calculation_time_(calculation_time),
56       from_manually_triggered_calculation_(
57           from_manually_triggered_calculation) {
58   DCHECK_EQ(base::checked_cast<int>(top_topics_and_observing_domains_.size()),
59             blink::features::kBrowsingTopicsNumberOfTopTopicsPerEpoch.Get());
60   DCHECK_LE(padded_top_topics_start_index,
61             top_topics_and_observing_domains_.size());
62   DCHECK_GT(config_version_, 0);
63   DCHECK_GT(taxonomy_version_, 0);
64   DCHECK_GT(model_version_, 0);
65 }
66
67 EpochTopics::EpochTopics(EpochTopics&&) = default;
68
69 EpochTopics& EpochTopics::operator=(EpochTopics&&) = default;
70
71 EpochTopics::~EpochTopics() = default;
72
73 // static
74 EpochTopics EpochTopics::FromDictValue(const base::Value::Dict& dict_value) {
75   const base::Value* calculation_time_value =
76       dict_value.Find(kCalculationTimeNameKey);
77   if (!calculation_time_value)
78     return EpochTopics(base::Time());
79
80   base::Time calculation_time =
81       base::ValueToTime(calculation_time_value).value();
82
83   std::vector<TopicAndDomains> top_topics_and_observing_domains;
84   const base::Value::List* top_topics_and_observing_domains_value =
85       dict_value.FindList(kTopTopicsAndObservingDomainsNameKey);
86   if (!top_topics_and_observing_domains_value)
87     return EpochTopics(calculation_time);
88
89   for (const base::Value& topic_and_observing_domains_value :
90        *top_topics_and_observing_domains_value) {
91     const base::Value::Dict* topic_and_observing_domains_dict_value =
92         topic_and_observing_domains_value.GetIfDict();
93     if (!topic_and_observing_domains_dict_value)
94       return EpochTopics(calculation_time);
95
96     top_topics_and_observing_domains.push_back(TopicAndDomains::FromDictValue(
97         *topic_and_observing_domains_dict_value));
98   }
99
100   if (top_topics_and_observing_domains.empty())
101     return EpochTopics(calculation_time);
102
103   absl::optional<int> padded_top_topics_start_index_value =
104       dict_value.FindInt(kPaddedTopTopicsStartIndexNameKey);
105   if (!padded_top_topics_start_index_value)
106     return EpochTopics(calculation_time);
107
108   size_t padded_top_topics_start_index =
109       static_cast<size_t>(*padded_top_topics_start_index_value);
110
111   absl::optional<int> config_version_value =
112       dict_value.FindInt(kConfigVersionNameKey);
113
114   // `kConfigVersionNameKey` is introduced after the initial release. Instead of
115   // treating it as an error and using a fresh epoch, we'll use version 1 which
116   // is the only valid version before this field is introduced.
117   int config_version = config_version_value ? *config_version_value : 1;
118
119   absl::optional<int> taxonomy_version_value =
120       dict_value.FindInt(kTaxonomyVersionNameKey);
121   if (!taxonomy_version_value)
122     return EpochTopics(calculation_time);
123
124   int taxonomy_version = *taxonomy_version_value;
125
126   const base::Value* model_version_value =
127       dict_value.Find(kModelVersionNameKey);
128   if (!model_version_value)
129     return EpochTopics(calculation_time);
130
131   absl::optional<int64_t> model_version_int64_value =
132       base::ValueToInt64(model_version_value);
133   if (!model_version_int64_value)
134     return EpochTopics(calculation_time);
135
136   int64_t model_version = *model_version_int64_value;
137
138   return EpochTopics(std::move(top_topics_and_observing_domains),
139                      padded_top_topics_start_index, config_version,
140                      taxonomy_version, model_version, calculation_time,
141                      /*from_manually_triggered_calculation=*/false);
142 }
143
144 base::Value::Dict EpochTopics::ToDictValue() const {
145   base::Value::List top_topics_and_observing_domains_list;
146   for (const TopicAndDomains& topic_and_domains :
147        top_topics_and_observing_domains_) {
148     top_topics_and_observing_domains_list.Append(
149         topic_and_domains.ToDictValue());
150   }
151
152   base::Value::Dict result_dict;
153   result_dict.Set(kTopTopicsAndObservingDomainsNameKey,
154                   std::move(top_topics_and_observing_domains_list));
155   result_dict.Set(kPaddedTopTopicsStartIndexNameKey,
156                   base::checked_cast<int>(padded_top_topics_start_index_));
157   result_dict.Set(kConfigVersionNameKey, config_version_);
158   result_dict.Set(kTaxonomyVersionNameKey, taxonomy_version_);
159   result_dict.Set(kModelVersionNameKey, base::Int64ToValue(model_version_));
160   result_dict.Set(kCalculationTimeNameKey,
161                   base::TimeToValue(calculation_time_));
162   return result_dict;
163 }
164
165 CandidateTopic EpochTopics::CandidateTopicForSite(
166     const std::string& top_domain,
167     const HashedDomain& hashed_context_domain,
168     ReadOnlyHmacKey hmac_key) const {
169   // The topics calculation failed, or the topics has been cleared.
170   if (empty())
171     return CandidateTopic::CreateInvalid();
172
173   uint64_t random_or_top_topic_decision_hash =
174       HashTopDomainForRandomOrTopTopicDecision(hmac_key, calculation_time_,
175                                                top_domain);
176
177   uint64_t top_topic_index_decision_hash =
178       HashTopDomainForTopTopicIndexDecision(hmac_key, calculation_time_,
179                                             top_domain);
180
181   size_t top_topic_index =
182       top_topic_index_decision_hash % top_topics_and_observing_domains_.size();
183
184   const TopicAndDomains& topic_and_observing_domains =
185       top_topics_and_observing_domains_[top_topic_index];
186
187   if (!topic_and_observing_domains.IsValid())
188     return CandidateTopic::CreateInvalid();
189
190   bool is_true_topic = (top_topic_index < padded_top_topics_start_index_);
191
192   bool should_be_filtered = !topic_and_observing_domains.hashed_domains().count(
193       hashed_context_domain);
194
195   if (ShouldUseRandomTopic(random_or_top_topic_decision_hash)) {
196     uint64_t random_topic_index_decision =
197         HashTopDomainForRandomTopicIndexDecision(hmac_key, calculation_time_,
198                                                  top_domain);
199     Topic topic = SemanticTree().GetRandomTopic(taxonomy_version(),
200                                                 random_topic_index_decision);
201     return CandidateTopic::Create(topic, /*is_true_topic=*/false,
202                                   should_be_filtered, config_version(),
203                                   taxonomy_version(), model_version());
204   }
205
206   return CandidateTopic::Create(
207       topic_and_observing_domains.topic(), is_true_topic, should_be_filtered,
208       config_version(), taxonomy_version(), model_version());
209 }
210
211 void EpochTopics::ClearTopics() {
212   top_topics_and_observing_domains_.clear();
213   padded_top_topics_start_index_ = 0;
214 }
215
216 void EpochTopics::ClearTopic(Topic topic) {
217   for (TopicAndDomains& top_topic_and_domains :
218        top_topics_and_observing_domains_) {
219     // Invalidate `topic_and_domains`. We cannot delete the entry from
220     // `top_topics_and_observing_domains_` because it would modify the list of
221     // topics, and would break the ability to return the same topic for the same
222     // site for the epoch .
223     if (top_topic_and_domains.topic() == topic) {
224       top_topic_and_domains = TopicAndDomains();
225       continue;
226     }
227     SemanticTree semantic_tree;
228     std::vector<Topic> top_topic_ancestors =
229         semantic_tree.GetAncestorTopics(top_topic_and_domains.topic());
230     if (base::Contains(top_topic_ancestors, topic)) {
231       top_topic_and_domains = TopicAndDomains();
232     }
233   }
234 }
235
236 void EpochTopics::ClearContextDomain(
237     const HashedDomain& hashed_context_domain) {
238   for (TopicAndDomains& topic_and_domains : top_topics_and_observing_domains_) {
239     topic_and_domains.ClearDomain(hashed_context_domain);
240   }
241 }
242
243 }  // namespace browsing_topics