Fix emulator build error
[platform/framework/web/chromium-efl.git] / components / permissions / notifications_engagement_service.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/permissions/notifications_engagement_service.h"
6
7 #include "components/permissions/permissions_client.h"
8 #include "url/gurl.h"
9
10 namespace permissions {
11
12 namespace {
13
14 // For each origin that has the  |ContentSettingsType::NOTIFICATIONS|
15 // permission, we record the number of notifications that were displayed
16 // and interacted with. The data is stored in the website setting
17 // |NOTIFICATION_INTERACTIONS|  keyed to the same origin. The internal
18 // structure of this metadata is a dictionary:
19 //
20 //   {"1644163200": {"display_count": 3},  # Implied click_count = 0.
21 //    "1644768000": {"display_count": 6, "click_count": 1}}
22 //
23 // Currently, entries will be recorded daily.
24
25 constexpr char kEngagementKey[] = "click_count";
26 constexpr char kDisplayedKey[] = "display_count";
27
28 // Entries in notifications engagement expire after they become this old.
29 constexpr base::TimeDelta kMaxAge = base::Days(30);
30
31 // Discards notification interactions stored in `engagement` for time
32 // periods older than |kMaxAge|.
33 void EraseStaleEntries(base::Value::Dict& engagement) {
34   const base::Time cutoff = base::Time::Now() - kMaxAge;
35
36   for (auto it = engagement.begin(); it != engagement.end();) {
37     const auto& [key, value] = *it;
38
39     absl::optional<base::Time> last_time =
40         NotificationsEngagementService::ParsePeriodBeginFromBucketLabel(key);
41     if (!last_time.has_value() || last_time.value() < cutoff) {
42       it = engagement.erase(it);
43       continue;
44     }
45     ++it;
46   }
47 }
48 }  // namespace
49
50 NotificationsEngagementService::NotificationsEngagementService(
51     content::BrowserContext* context,
52     PrefService* pref_service)
53     : pref_service_(pref_service) {
54   settings_map_ =
55       permissions::PermissionsClient::Get()->GetSettingsMap(context);
56 }
57
58 void NotificationsEngagementService::Shutdown() {
59   settings_map_ = nullptr;
60 }
61
62 void NotificationsEngagementService::RecordNotificationDisplayed(
63     const GURL& url) {
64   IncrementCounts(url, 1 /*display_count_delta*/, 0 /*click_count_delta*/);
65 }
66
67 void NotificationsEngagementService::RecordNotificationDisplayed(
68     const GURL& url,
69     int display_count) {
70   IncrementCounts(url, display_count, 0 /*click_count_delta*/);
71 }
72
73 void NotificationsEngagementService::RecordNotificationInteraction(
74     const GURL& url) {
75   IncrementCounts(url, 0 /*display_count_delta*/, 1 /*click_count_delta*/);
76 }
77
78 void NotificationsEngagementService::IncrementCounts(const GURL& url,
79                                                      int display_count_delta,
80                                                      int click_count_delta) {
81   base::Value engagement_as_value = settings_map_->GetWebsiteSetting(
82       url, GURL(), ContentSettingsType::NOTIFICATION_INTERACTIONS);
83
84   base::Value::Dict engagement;
85   if (engagement_as_value.is_dict())
86     engagement = std::move(engagement_as_value).TakeDict();
87
88   std::string date = GetBucketLabel(base::Time::Now());
89   if (date == std::string())
90     return;
91
92   EraseStaleEntries(engagement);
93   base::Value::Dict* bucket = engagement.FindDict(date);
94   if (!bucket) {
95     bucket = &engagement.Set(date, base::Value::Dict())->GetDict();
96   }
97   if (display_count_delta) {
98     bucket->Set(kDisplayedKey, display_count_delta +
99                                    bucket->FindInt(kDisplayedKey).value_or(0));
100   }
101   if (click_count_delta) {
102     bucket->Set(
103         kEngagementKey,
104         click_count_delta + bucket->FindInt(kEngagementKey).value_or(0));
105   }
106
107   // Set the website setting of this origin with the updated |engagement|.
108   settings_map_->SetWebsiteSettingDefaultScope(
109       url, GURL(), ContentSettingsType::NOTIFICATION_INTERACTIONS,
110       base::Value(std::move(engagement)));
111 }
112
113 // static
114 std::string NotificationsEngagementService::GetBucketLabel(base::Time date) {
115   // For human-readability, return the UTC midnight on the same date as
116   // local midnight.
117   base::Time local_date = date.LocalMidnight();
118
119   base::Time::Exploded local_date_exploded;
120   local_date.LocalExplode(&local_date_exploded);
121   // Intentionally converting a locally exploded time, to an UTC time, so that
122   // the Midnight in UTC is on the same date the date on local time.
123   base::Time last_date;
124   bool converted = base::Time::FromUTCExploded(local_date_exploded, &last_date);
125
126   if (converted)
127     return base::NumberToString(last_date.base::Time::ToTimeT());
128
129   return std::string();
130 }
131
132 // static
133 absl::optional<base::Time>
134 NotificationsEngagementService::ParsePeriodBeginFromBucketLabel(
135     const std::string& label) {
136   int maybe_engagement_time;
137   base::Time local_period_begin;
138
139   // Store the time as local time.
140   if (base::StringToInt(label.c_str(), &maybe_engagement_time)) {
141     base::Time::Exploded date_exploded;
142     base::Time::FromTimeT(maybe_engagement_time).UTCExplode(&date_exploded);
143     if (base::Time::FromLocalExploded(date_exploded, &local_period_begin))
144       return local_period_begin;
145   }
146
147   return absl::nullopt;
148 }
149
150 }  // namespace permissions