File: site_engagement_service.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (348 lines) | stat: -rw-r--r-- 14,549 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_SERVICE_H_
#define COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_SERVICE_H_

#include <memory>
#include <vector>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/browsing_data/core/browsing_data_utils.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/site_engagement/core/mojom/site_engagement_details.mojom.h"
#include "components/site_engagement/core/site_engagement_score_provider.h"
#include "components/webapps/common/web_app_id.h"
#include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom.h"
#include "ui/base/page_transition_types.h"

namespace base {
class Clock;
}

namespace webapps {
FORWARD_DECLARE_TEST(AppBannerManagerBrowserTest, WebAppBannerNeedsEngagement);
}

namespace content {
class BrowserContext;
class WebContents;
}  // namespace content

namespace web_app {
class WebAppEngagementBrowserTest;
}

class GURL;
class HostContentSettingsMap;
class PrefRegistrySimple;
class NotificationPermissionReviewServiceTest;
class SafetyHubCardDataHelperTest;

namespace site_engagement {

enum class EngagementType;
class SiteEngagementObserver;
class SiteEngagementScore;

#if BUILDFLAG(IS_ANDROID)
class SiteEngagementServiceAndroid;
#endif

// Stores and retrieves the engagement score of an origin.
//
// An engagement score is a non-negative double that represents how much a user
// has engaged with an origin - the higher it is, the more engagement the user
// has had with this site recently.
//
// User activity such as visiting the origin often, interacting with the origin,
// and adding it to the homescreen will increase the site engagement score. If
// a site's score does not increase for some time, it will decay, eventually
// reaching zero with further disuse.
//
// The SiteEngagementService object must be created and used on the UI thread
// only. Engagement scores may be queried in a read-only fashion from other
// threads using SiteEngagementService::GetScoreFromSettings, but use of this
// method is discouraged unless it is not possible to use the UI thread.
class SiteEngagementService : public KeyedService,
                              public SiteEngagementScoreProvider {
 public:
  // Sets of URLs that are used to filter engagement details.
  class URLSets {
   public:
    using Type = uint32_t;
    // Includes http:// and https:// sites.
    static constexpr Type HTTP = 1 << 0;
    // Includes chrome:// and chrome-untrusted:// sites.
    static constexpr Type WEB_UI = 1 << 1;
  };

  // The provider allows code agnostic to the embedder (e.g. in
  // //components) to retrieve the SiteEngagementService. It should be set by
  // each embedder that uses the SiteEngagementService, via SetServiceProvider.
  class ServiceProvider {
   public:
    ~ServiceProvider() = default;

    // Should always return a non null value, creating the service if it does
    // not exist.
    virtual SiteEngagementService* GetSiteEngagementService(
        content::BrowserContext* browser_context) = 0;
  };

  // WebContentsObserver that detects engagement triggering events and notifies
  // the service of them.
  class Helper;

  // The name of the site engagement variation field trial.
  static const char kEngagementParams[];

  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  // Sets and clears the service provider. These are separate functions to
  // enable better checking.
  static void SetServiceProvider(ServiceProvider* provider);
  static void ClearServiceProvider(ServiceProvider* provider);

  // Returns the site engagement service attached to this Browser Context. The
  // service exists in incognito mode; scores will be initialised using the
  // score from the Browser Context that the incognito session was created from,
  // and will increase and decrease as usual. Engagement earned or decayed in
  // incognito will not be persisted or reflected in the original Browser
  // Context.
  //
  // This method must be called on the UI thread.
  static SiteEngagementService* Get(content::BrowserContext* browser_context);

  // Returns the maximum possible amount of engagement that a site can accrue.
  static double GetMaxPoints();

  // Returns whether or not the site engagement service is enabled.
  static bool IsEnabled();

  // Returns the score for |origin| based on |settings|. Can be called on any
  // thread and does not cause any cleanup, decay, etc.
  //
  // Should only be used if you cannot create a SiteEngagementService (i.e. you
  // cannot run on the UI thread).
  static double GetScoreFromSettings(HostContentSettingsMap* settings,
                                     const GURL& origin);

  // Retrieves all details for origins within `url_set`. Can be called
  // from a background thread. `now` must be the current timestamp. Takes a
  // scoped_refptr to keep HostContentSettingsMap alive. See crbug.com/901287.
  static std::vector<mojom::SiteEngagementDetails> GetAllDetailsInBackground(
      base::Time now,
      scoped_refptr<HostContentSettingsMap> map,
      URLSets::Type url_set = URLSets::HTTP);

  // Returns whether |score| is at least the given |level| of engagement.
  static bool IsEngagementAtLeast(double score,
                                  blink::mojom::EngagementLevel level);

  explicit SiteEngagementService(content::BrowserContext* browser_context);

  SiteEngagementService(const SiteEngagementService&) = delete;
  SiteEngagementService& operator=(const SiteEngagementService&) = delete;

  ~SiteEngagementService() override;

  // Returns the engagement level of |url|.
  blink::mojom::EngagementLevel GetEngagementLevel(const GURL& url) const;

  // Returns an array of engagement score details for all origins that are
  // in `url_set` and have a score. A origin can have a score due to
  // direct engagement, or other factors that cause an engagement bonus to
  // be applied.
  //
  // Note that this method is quite expensive, so try to avoid calling it in
  // performance-critical code.
  std::vector<mojom::SiteEngagementDetails> GetAllDetails(
      URLSets::Type url_set = URLSets::HTTP) const;

  // Update the engagement score of |url| for a notification interaction.
  void HandleNotificationInteraction(const GURL& url);

  // Returns whether the engagement service has enough data to make meaningful
  // decisions. Clients should avoid using engagement in their heuristic until
  // this is true.
  bool IsBootstrapped() const;

  // Resets the base engagement for |url| to |score|, clearing daily limits. Any
  // bonus engagement that |url| has acquired is not affected by this method, so
  // the result of GetScore(|url|) may not be the same as |score|.
  void ResetBaseScoreForURL(const GURL& url, double score);

  // Update the last time |url| was opened from an installed shortcut (hosted in
  // |web_contents|) to be clock_->Now().
  void SetLastShortcutLaunchTime(content::WebContents* web_contents,
#if !BUILDFLAG(IS_ANDROID)
                                 const webapps::AppId& app_id,
#endif
                                 const GURL& url);

  // Returns the site engagement details for the specified |url|.
  mojom::SiteEngagementDetails GetDetails(const GURL& url) const;

  // Overridden from SiteEngagementScoreProvider.
  double GetScore(const GURL& url) const override;
  double GetTotalEngagementPoints() const override;

  // Just forwards calls AddPoints.
  void AddPointsForTesting(const GURL& url, double points);

  void SetClockForTesting(base::Clock* clock) { clock_ = clock; }

 protected:
  // Retrieves the SiteEngagementScore object for |origin|.
  SiteEngagementScore CreateEngagementScore(const GURL& origin) const;

  void SetLastEngagementTime(base::Time last_engagement_time) const;

  content::BrowserContext* browser_context() { return browser_context_; }
  const base::Clock& clock() { return *clock_; }

 private:
  friend class SiteEngagementObserver;
  friend class SiteEngagementServiceTest;
  friend class web_app::WebAppEngagementBrowserTest;
  friend class ::NotificationPermissionReviewServiceTest;
  friend class ::SafetyHubCardDataHelperTest;
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CheckHistograms);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CleanupEngagementScores);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
                           CleanupMovesScoreBackToNow);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
                           CleanupMovesScoreBackToRebase);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
                           CleanupEngagementScoresProportional);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetTotalNavigationPoints);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetTotalUserInputPoints);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, RestrictedToHTTPAndHTTPS);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, Observers);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, LastEngagementTime);
  FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
                           IncognitoEngagementService);
  FRIEND_TEST_ALL_PREFIXES(webapps::AppBannerManagerBrowserTest,
                           WebAppBannerNeedsEngagement);
  FRIEND_TEST_ALL_PREFIXES(AppBannerSettingsHelperTest, SiteEngagementTrigger);
  FRIEND_TEST_ALL_PREFIXES(HostedAppPWAOnlyTest, EngagementHistogram);

#if BUILDFLAG(IS_ANDROID)
  // Shim class to expose the service to Java.
  friend class SiteEngagementServiceAndroid;
  SiteEngagementServiceAndroid* GetAndroidService() const;
  void SetAndroidService(
      std::unique_ptr<SiteEngagementServiceAndroid> android_service);
#endif

  // Adds the specified number of points to the given origin, respecting the
  // maximum limits for the day and overall.
  void AddPoints(const GURL& url, double points);

  // Runs site engagement maintenance tasks.
  void AfterStartupTask();

  // Removes any origins which have decayed to 0 engagement. If
  // |update_last_engagement_time| is true, the last engagement time of all
  // origins is reset by calculating the delta between the last engagement event
  // recorded by the site engagement service and the origin. The origin's last
  // engagement time is then set to clock_->Now() - delta.
  //
  // If a user does not use the browser at all for some period of time,
  // engagement is not decayed, and the state is restored equivalent to how they
  // left it once they return.
  void CleanupEngagementScores(bool update_last_engagement_time) const;

  // Possibly records UMA metrics if we haven't recorded them lately.
  void MaybeRecordMetrics();

  // Actually records metrics for the engagement in |details|.
  void RecordMetrics(std::vector<mojom::SiteEngagementDetails>);

  // Returns true if we should record engagement for this URL. Currently,
  // engagement is only earned for HTTP and HTTPS.
  bool ShouldRecordEngagement(const GURL& url) const;

  // Get and set the last engagement time from prefs.
  base::Time GetLastEngagementTime() const;

  // Get the maximum decay period and the stale period for last engagement
  // times.
  base::TimeDelta GetMaxDecayPeriod() const;
  base::TimeDelta GetStalePeriod() const;

  // Returns the median engagement score of all recorded origins. |details| must
  // be sorted in ascending order of score.
  double GetMedianEngagementFromSortedDetails(
      const std::vector<mojom::SiteEngagementDetails>& details) const;

  // Update the engagement score of the origin loaded in |web_contents| for
  // media playing. The points awarded are discounted if the media is being
  // played in a non-visible tab.
  void HandleMediaPlaying(content::WebContents* web_contents, bool is_hidden);

  // Update the engagement score of the origin loaded in |web_contents| for
  // navigation.
  void HandleNavigation(content::WebContents* web_contents,
                        ui::PageTransition transition);

  // Update the engagement score of the origin loaded in |web_contents| for
  // time-on-site, based on user input.
  void HandleUserInput(content::WebContents* web_contents, EngagementType type);

  // Called when the engagement for |url| loaded in |web_contents| is changed,
  // due to an event of type |type|. Calls OnEngagementEvent in all observers.
  // |web_contents| may be null if the engagement has increased when |url| is
  // not in a tab, e.g. from a notification interaction. Also records
  // engagement-type metrics.
  void OnEngagementEvent(
      content::WebContents* web_contents,
      const GURL& url,
      EngagementType type,
      double old_score,
      const std::optional<webapps::AppId>& app_id_override = std::nullopt);

  // Returns true if the last engagement increasing event seen by the site
  // engagement service was sufficiently long ago that we need to reset all
  // scores to be relative to now. This ensures that users who do not use the
  // browser for an extended period of time do not have their engagement decay.
  bool IsLastEngagementStale() const;

  // Add and remove observers of this service.
  void AddObserver(SiteEngagementObserver* observer);
  void RemoveObserver(SiteEngagementObserver* observer);

  raw_ptr<content::BrowserContext, AcrossTasksDanglingUntriaged>
      browser_context_;

  // The clock used to vend times.
  raw_ptr<base::Clock> clock_;

#if BUILDFLAG(IS_ANDROID)
  std::unique_ptr<SiteEngagementServiceAndroid> android_service_;
#endif

  // Metrics are recorded at non-incognito browser startup, and then
  // approximately once per hour thereafter. Store the local time at which
  // metrics were previously uploaded: the first event which affects any
  // origin's engagement score after an hour has elapsed triggers the next
  // upload.
  base::Time last_metrics_time_;

  // A list of observers. When any origin registers an engagement-increasing
  // event, each observer's OnEngagementEvent method will be called.
  base::ObserverList<SiteEngagementObserver>::Unchecked observer_list_;

  base::WeakPtrFactory<SiteEngagementService> weak_factory_{this};
};

}  // namespace site_engagement

#endif  // COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_SERVICE_H_