File: metrics_reporter.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 (281 lines) | stat: -rw-r--r-- 11,542 bytes parent folder | download | duplicates (5)
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
// Copyright 2020 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_FEED_CORE_V2_METRICS_REPORTER_H_
#define COMPONENTS_FEED_CORE_V2_METRICS_REPORTER_H_

#include <climits>
#include <map>
#include <optional>
#include <string_view>

#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/feed/core/proto/v2/store.pb.h"
#include "components/feed/core/v2/enums.h"
#include "components/feed/core/v2/feed_network.h"
#include "components/feed/core/v2/public/common_enums.h"
#include "components/feed/core/v2/public/stream_type.h"
#include "components/feed/core/v2/public/web_feed_subscriptions.h"
#include "components/feed/core/v2/types.h"

class PrefService;
namespace feedstore {
class Metadata;
}

namespace feed {
// If cached user setting info is older than this, it will not be reported.
constexpr base::TimeDelta kUserSettingsMaxAge = base::Days(14);

// Reports UMA metrics for feed.
// Note this is inherited only for testing.
class MetricsReporter {
 public:
  // For 'index_in_stream' parameters, when the card index is unknown.
  // This is most likely to happen when the action originates from the bottom
  // sheet.
  static const int kUnknownCardIndex = INT_MAX;

  class Delegate {
   public:
    // Calls `callback` with the number of Web Feeds for which the user is
    // subscribed.
    virtual void SubscribedWebFeedCount(
        base::OnceCallback<void(int)> callback) = 0;
    virtual void RegisterFeedUserSettingsFieldTrial(std::string_view group) = 0;
    virtual ContentOrder GetContentOrder(
        const StreamType& stream_type) const = 0;
  };

  explicit MetricsReporter(PrefService* profile_prefs);
  virtual ~MetricsReporter();
  MetricsReporter(const MetricsReporter&) = delete;
  MetricsReporter& operator=(const MetricsReporter&) = delete;

  // Two-step initialization, required for circular dependency.
  void Initialize(Delegate* delegate);

  void OnMetadataInitialized(bool isEnabledByEnterprisePolicy,
                             bool isFeedVisible,
                             bool isSignedIn,
                             bool isEnabled,
                             const feedstore::Metadata& metadata);

  // User interactions. See |FeedApi| for definitions.

  virtual void ContentSliceViewed(const StreamType& stream_type,
                                  int index_in_stream,
                                  int stream_slice_count);
  void FeedViewed(SurfaceId surface_id);
  void OpenAction(const StreamType& stream_type,
                  int index_in_stream,
                  OpenActionType action_type);
  void OpenVisitComplete(base::TimeDelta visit_time);
  void PageLoaded();
  void OtherUserAction(const StreamType& stream_type,
                       FeedUserActionType action_type);
  void OtherUserAction(FeedUserActionType action_type);
  // Report a period of time during which at least one content slice was visible
  // enough or covering enough of the viewport.
  void ReportStableContentSliceVisibilityTimeForGoodVisits(
      base::TimeDelta delta);

  // Indicates the user scrolled the feed by |distance_dp| and then stopped
  // scrolling.
  void StreamScrolled(const StreamType& stream_type, int distance_dp);
  void StreamScrollStart();

  // Called when the Feed surface is opened and closed.
  void SurfaceOpened(const StreamType& stream_type,
                     SurfaceId surface_id,
                     SingleWebFeedEntryPoint single_web_feed_entry_point =
                         SingleWebFeedEntryPoint::kOther);
  void SurfaceClosed(SurfaceId surface_id);

  // Network metrics.

  void NetworkRefreshRequestStarted(const StreamType& stream_type,
                                    ContentOrder content_order);
  static void NetworkRequestComplete(NetworkRequestType type,
                                     const NetworkResponseInfo& response_info);

  // Stream events.

  struct LoadStreamResultSummary {
    LoadStreamResultSummary();
    LoadStreamResultSummary(const LoadStreamResultSummary& src);
    ~LoadStreamResultSummary();
    LoadStreamStatus load_from_store_status = LoadStreamStatus::kNoStatus;
    LoadStreamStatus final_status = LoadStreamStatus::kNoStatus;
    bool is_initial_load = false;
    bool loaded_new_content_from_network = false;
    base::TimeDelta stored_content_age;
    ContentOrder content_order = ContentOrder::kUnspecified;
    std::optional<feedstore::Metadata::StreamMetadata> stream_metadata;
  };
  virtual void OnLoadStream(const StreamType& stream_type,
                            const LoadStreamResultSummary& result_summary,
                            const ContentStats& content_stats,
                            std::unique_ptr<LoadLatencyTimes> load_latencies);
  virtual void OnBackgroundRefresh(const StreamType& stream_type,
                                   LoadStreamStatus final_status);
  void OnManualRefresh(const StreamType& stream_type,
                       const feedstore::Metadata& metadata,
                       const ContentHashSet& content_hashes);
  virtual void OnLoadMoreBegin(const StreamType& stream_type,
                               SurfaceId surface_id);
  virtual void OnLoadMore(const StreamType& stream_type,
                          LoadStreamStatus final_status,
                          const ContentStats& content_stats);
  // Called each time the surface receives new content.
  void SurfaceReceivedContent(SurfaceId surface_id);
  // Called when Chrome is entering the background.
  void OnEnterBackground();

  static void OnImageFetched(const GURL& url, int net_error_or_http_status);
  static void OnResourceFetched(int net_error_or_http_status);

  // Actions upload.
  static void OnUploadActionsBatch(UploadActionsBatchStatus status);
  virtual void OnUploadActions(UploadActionsStatus status);

  static void ActivityLoggingEnabled(bool response_has_logging_enabled);
  static void NoticeCardFulfilled(bool response_has_notice_card);
  static void NoticeCardFulfilledObsolete(bool response_has_notice_card);

  // Web Feed events.
  void OnFollowAttempt(bool followed_with_id,
                       const WebFeedSubscriptions::FollowWebFeedResult& result);
  void OnUnfollowAttempt(
      const WebFeedSubscriptions::UnfollowWebFeedResult& status);
  void RefreshRecommendedWebFeedsAttempted(WebFeedRefreshStatus status,
                                           int recommended_web_feed_count);
  void RefreshSubscribedWebFeedsAttempted(bool subscriptions_were_stale,
                                          WebFeedRefreshStatus status,
                                          int subscribed_web_feed_count);
  void OnQueryAttempt(const WebFeedSubscriptions::QueryWebFeedResult& result);

  // Info card events.
  void OnInfoCardTrackViewStarted(const StreamType& stream_type,
                                  int info_card_type);
  void OnInfoCardViewed(const StreamType& stream_type, int info_card_type);
  void OnInfoCardClicked(const StreamType& stream_type, int info_card_type);
  void OnInfoCardDismissedExplicitly(const StreamType& stream_type,
                                     int info_card_type);
  void OnInfoCardStateReset(const StreamType& stream_type, int info_card_type);

  void ReportContentDuplication(bool is_duplicated_at_pos_1,
                                bool is_duplicated_at_pos_2,
                                bool is_duplicated_at_pos_3,
                                int duplicate_percentage_for_first_10,
                                int duplicate_percentaget_for_all);

 private:
  // State replicated for reporting per-stream-type metrics.
  struct StreamStats {
    bool engaged_simple_reported = false;
    bool engaged_reported = false;
    bool scrolled_reported = false;
  };
  struct SurfaceWaiting {
    explicit operator bool() const { return !wait_start.is_null(); }
    SurfaceWaiting();
    SurfaceWaiting(const feed::StreamType& stream_type,
                   base::TimeTicks wait_start);
    ~SurfaceWaiting();
    SurfaceWaiting(const SurfaceWaiting&);
    SurfaceWaiting(SurfaceWaiting&&);
    SurfaceWaiting& operator=(const SurfaceWaiting&);
    SurfaceWaiting& operator=(SurfaceWaiting&&);

    feed::StreamType stream_type;
    base::TimeTicks wait_start;
  };

  base::WeakPtr<MetricsReporter> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

  void ReportPersistentDataIfDayIsDone();
  void CardOpenBegin(const StreamType& stream_type);
  void CardOpenTimeout(base::TimeTicks start_ticks);
  void ReportCardOpenEndIfNeeded(bool success);
  void RecordEngagement(const StreamType& stream_type,
                        int scroll_distance_dp,
                        bool interacted);
  void LogContentStats(const StreamType& stream_type,
                       const ContentStats& content_stats);
  void TrackTimeSpentInFeed(bool interacted_or_scrolled);
  void RecordInteraction(const StreamType& stream_type);
  void ReportOpenFeedIfNeeded(SurfaceId surface_id, bool success);
  void ReportGetMoreIfNeeded(SurfaceId surface_id, bool success);
  void FinalizeMetrics();
  void FinalizeVisit();
  void ReportFollowCountOnLoad(bool content_shown, int subscription_count);

  StreamStats& ForStream(const StreamType& stream_type);

  raw_ptr<PrefService> profile_prefs_;
  raw_ptr<Delegate, DanglingUntriaged> delegate_ = nullptr;

  StreamStats for_you_stats_;
  StreamStats web_feed_stats_;
  StreamStats combined_stats_;

  // State below here is shared between all stream types.

  // Persistent data stored in prefs. Data is read in the constructor, and then
  // written back to prefs on backgrounding.
  PersistentMetricsData persistent_data_;

  base::TimeTicks visit_start_time_;

  // The time a surface was opened, for surfaces still waiting for content.
  std::map<SurfaceId, SurfaceWaiting> surfaces_waiting_for_content_;
  // The time a surface requested more content, for surfaces still waiting for
  // more content.
  std::map<SurfaceId, SurfaceWaiting> surfaces_waiting_for_more_content_;

  // Tracking ContentSuggestions.Feed.UserJourney.OpenCard.*:
  // We assume at most one card is opened at a time. The time the card was
  // tapped is stored here. Upon timeout, another open attempt, or
  // |ChromeStopping()|, the open is considered failed. Otherwise, if the
  // loading the page succeeds, the open is considered successful.
  SurfaceWaiting pending_open_;

  // For tracking time spent in the Feed.
  std::optional<base::TimeTicks> time_in_feed_start_;
  // For TimeSpentOnFeed.
  base::TimeDelta tracked_visit_time_in_feed_;
  // Non-null only directly after a stream load.
  std::unique_ptr<LoadLatencyTimes> load_latencies_;
  bool load_latencies_recorded_ = false;

  class GoodVisitState {
   public:
    explicit GoodVisitState(PersistentMetricsData& data);
    void OnScroll();
    void OnGoodExplicitInteraction();
    void OnOpenComplete(base::TimeDelta open_duration);
    void AddTimeInFeed(base::TimeDelta time);
    void ExtendOrStartNewVisit();

   private:
    void MaybeReportGoodVisit();
    void Reset();

    // Owned by MetricsReporter. Will live through the lifetime of
    // GoodVisitState.
    const raw_ref<PersistentMetricsData> data_;
  };
  GoodVisitState good_visit_state_;

  base::WeakPtrFactory<MetricsReporter> weak_ptr_factory_{this};
};
}  // namespace feed

#endif  // COMPONENTS_FEED_CORE_V2_METRICS_REPORTER_H_