File: tab_stats_tracker.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 (395 lines) | stat: -rw-r--r-- 14,116 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
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
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_METRICS_TAB_STATS_TAB_STATS_TRACKER_H_
#define CHROME_BROWSER_METRICS_TAB_STATS_TAB_STATS_TRACKER_H_

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/functional/function_ref.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/power_monitor/power_observer.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/browser/metrics/tab_stats/tab_stats_data_store.h"
#include "components/metrics/daily_event.h"
#include "content/public/browser/web_contents_observer.h"

#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
#endif

class PrefRegistrySimple;
class PrefService;
class Profile;

#if BUILDFLAG(IS_ANDROID)
class TabModel;
#else
class Browser;
#endif

namespace content {
class WebContents;
}

namespace metrics {
FORWARD_DECLARE_TEST(TabStatsTrackerBrowserTest,
                     TabDeletionGetsHandledProperly);

// Class for tracking and recording the tabs and browser windows usage.
//
// This class is meant to be used as a singleton by calling the SetInstance
// method, e.g.:
//     TabStatsTracker::SetInstance(
//         std::make_unique<TabStatsTracker>(g_browser_process->local_state()));
class TabStatsTracker :
#if !BUILDFLAG(IS_ANDROID)
    public resource_coordinator::LifecycleUnitObserver,
#endif
    public base::PowerSuspendObserver {
 public:
  // Abstraction of a Browser + TabStripModel (on desktop) or a TabModel (on
  // Android).
  class TabStripInterface;

  // Constructor. |pref_service| must outlive this object.
  explicit TabStatsTracker(PrefService* pref_service);

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

  ~TabStatsTracker() override;

  // Sets the |TabStatsTracker| global instance.
  static void SetInstance(std::unique_ptr<TabStatsTracker> instance);
  // Clears and tears down the |TabStatsTracker| global instance.
  static void ClearInstance();

  // Returns the |TabStatsTracker| global instance. CHECKs there is an instance.
  static TabStatsTracker* GetInstance();
  // Returns whether there is a global instance.
  static bool HasInstance();

  // Registers a TabStatsObserver instance. Upon registering the initial state
  // of the observer is made to match the current browser/tab state.
  void AddObserverAndSetInitialState(TabStatsObserver* observer);

  void RemoveObserver(TabStatsObserver* observer) {
    tab_stats_observers_.RemoveObserver(observer);
  }

  // Registers prefs used to track tab stats.
  static void RegisterPrefs(PrefRegistrySimple* registry);

  // Accessors.
  const TabStatsDataStore::TabsStats& tab_stats() const;

  content::WebContentsObserver* GetWebContentsUsageObserverForTesting(
      content::WebContents* web_contents);

 protected:
  FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest,
                           TabDeletionGetsHandledProperly);
  FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest,
                           TabsAndWindowsAreCountedAccurately);
#if BUILDFLAG(IS_WIN)
  FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest,
                           TestCalculateAndRecordNativeWindowVisibilities);
#endif

  // The UmaStatsReportingDelegate is responsible for delivering statistics
  // reported by the TabStatsTracker via UMA.
  class UmaStatsReportingDelegate;

  // The observer that's used by |daily_event_| to report the metrics.
  class TabStatsDailyObserver : public DailyEvent::Observer {
   public:
    // Constructor. |reporting_delegate| and |data_store| must outlive this
    // object.
    TabStatsDailyObserver(UmaStatsReportingDelegate* reporting_delegate,
                          TabStatsDataStore* data_store)
        : reporting_delegate_(reporting_delegate), data_store_(data_store) {}

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

    ~TabStatsDailyObserver() override = default;

    // Callback called when the daily event happen.
    void OnDailyEvent(DailyEvent::IntervalType type) override;

   private:
    // The delegate used to report the metrics.
    raw_ptr<UmaStatsReportingDelegate, DanglingUntriaged> reporting_delegate_;

    // The data store that houses the metrics.
    raw_ptr<TabStatsDataStore> data_store_;
  };

  // Accessors, exposed for unittests:
  TabStatsDataStore* tab_stats_data_store() {
    return tab_stats_data_store_.get();
  }
  base::RepeatingTimer* daily_event_timer_for_testing() {
    return &daily_event_timer_;
  }
  DailyEvent* daily_event_for_testing() { return daily_event_.get(); }
  UmaStatsReportingDelegate* reporting_delegate_for_testing() {
    return reporting_delegate_.get();
  }
  base::RepeatingTimer* heartbeat_timer_for_testing() {
    return &heartbeat_timer_;
  }

  // Reset the |reporting_delegate_| object to |reporting_delegate|, for testing
  // purposes.
  void reset_reporting_delegate_for_testing(
      UmaStatsReportingDelegate* reporting_delegate) {
    reporting_delegate_.reset(reporting_delegate);
  }

  // Reset the DailyEvent object to |daily_event|, for testing purposes.
  void reset_daily_event_for_testing(DailyEvent* daily_event) {
    daily_event_.reset(daily_event);
  }

  void reset_data_store_for_testing(TabStatsDataStore* data_store) {
    tab_stats_data_store_.reset(data_store);
  }

  // base::PowerSuspendObserver:
  void OnResume() override;

#if !BUILDFLAG(IS_ANDROID)
  // resource_coordinator::LifecycleUnitObserver:
  void OnLifecycleUnitStateChanged(
      resource_coordinator::LifecycleUnit* lifecycle_unit,
      ::mojom::LifecycleUnitState previous_state,
      ::mojom::LifecycleUnitStateChangeReason reason) override;
#endif

  // Functions to call when a tab strip (or the Android equivalent) is added,
  // removed or modified.
  void OnTabStripAdded();
  void OnTabStripRemoved();
  void OnTabStripNewTabCount(size_t tab_count);

  // Functions to call to start tracking a new tab.
  void OnInitialOrInsertedTab(content::WebContents* web_contents);
  void OnTabReplaced(content::WebContents* old_contents,
                     content::WebContents* new_contents);

  // Functions to call when a WebContents get destroyed.
  void OnWebContentsDestroyed(content::WebContents* web_contents);

  // Function to call to report the tab heartbeat metrics.
  void OnHeartbeatEvent();

 private:
  // Observer used to be notified when the state of a WebContents changes or
  // when it's about to be destroyed.
  class WebContentsUsageObserver;

  // For access to |tab_stats_observers_|
  friend class WebContentsUsageObserver;

  // A class that watches for tabs to be added and removed. Abstracts away
  // tab strip differences on Android and desktop.
  class TabWatcher;

  // For access to OnTabStripAdded() and OnTabStripRemoved().
  friend class TabWatcher;

  // The delegate that reports the events.
  std::unique_ptr<UmaStatsReportingDelegate> reporting_delegate_;

  // The tab stats.
  std::unique_ptr<TabStatsDataStore> tab_stats_data_store_;

  // A daily event for collecting metrics once a day.
  std::unique_ptr<DailyEvent> daily_event_;

  // The list of registered observers.
  base::ObserverList<TabStatsObserver> tab_stats_observers_;

  // The timer used to periodically check if the daily event should be
  // triggered.
  base::RepeatingTimer daily_event_timer_;

  // The timer used to report the heartbeat metrics at regular interval.
  base::RepeatingTimer heartbeat_timer_;

  // The observers that track how the tabs are used.
  std::map<content::WebContents*, std::unique_ptr<WebContentsUsageObserver>>
      web_contents_usage_observers_;

  std::unique_ptr<TabWatcher> tab_watcher_;

  SEQUENCE_CHECKER(sequence_checker_);
};

// A Browser + TabStripModel (on desktop) or a TabModel (on Android).
// The TabStripInterface must not outlive the underlying model.
class TabStatsTracker::TabStripInterface {
 public:
#if BUILDFLAG(IS_ANDROID)
  using PlatformModel = TabModel;

  const TabModel* tab_model() const { return model_.get(); }
  TabModel* tab_model() { return model_.get(); }
#else
  using PlatformModel = Browser;

  const Browser* browser() const { return model_.get(); }
  Browser* browser() { return model_.get(); }
#endif

  explicit TabStripInterface(PlatformModel* model);
  ~TabStripInterface();

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

  // Calls `func` for each tab in the tab strip that has a non-null
  // WebContents. On Android, tabs will be skipped if their WebContents isn't
  // initialized yet.
  void ForEachWebContents(
      base::FunctionRef<void(content::WebContents*)> func) const;

  // Returns the count of tabs in this tab strip.
  size_t GetTabCount() const;

  // Returns the active tab for this tab strip. On Android this may return
  // nullptr if the tab's WebContents isn't initialized yet.
  content::WebContents* GetActiveWebContents() const;

  // Returns the tab at `index` of this tab strip. On Android this may return
  // nullptr if the tab's WebContents isn't initialized yet.
  content::WebContents* GetWebContentsAt(size_t index) const;

  // Returns the profile this tab strip is attached to.
  Profile* GetProfile() const;

  // Returns true if this tab strip is attached to a TYPE_NORMAL Browser.
  // Always returns true on Android.
  bool IsInNormalBrowser() const;

  // Activates the tab at `index` of this tab strip.
  void ActivateTabAtForTesting(size_t index);

  // Closes the tab at `index` of this tab strip.
  void CloseTabAtForTesting(size_t index);

  // Calls `func` for each existing Browser + TabStripModel (or TabModel on
  // Android).
  static void ForEach(base::FunctionRef<void(const TabStripInterface&)> func);

 private:
  raw_ptr<PlatformModel> model_;
};

// The reporting delegate, which reports metrics via UMA.
class TabStatsTracker::UmaStatsReportingDelegate {
 public:
  // The name of the histogram that records the number of tabs total at resume
  // from sleep/hibernate.
  static const char kNumberOfTabsOnResumeHistogramName[];

  // The name of the histogram that records the maximum number of tabs opened in
  // a day.
  static const char kMaxTabsInADayHistogramName[];

  // The name of the histogram that records the maximum number of tabs opened in
  // the same window in a day.
  static const char kMaxTabsPerWindowInADayHistogramName[];

  // The name of the histogram that records the maximum number of windows
  // opened in a day.
  static const char kMaxWindowsInADayHistogramName[];

  // The name of the histograms that records the current number of tabs/windows.
  static const char kTabCountHistogramName[];
  static const char kWindowCountHistogramName[];

  // The name of the histogram that records each window's width, in DIPs.
  static const char kWindowWidthHistogramName[];

  // The names of the histograms that record daily discard/reload counts caused
  // for each discard reason.
  static const char kDailyDiscardsExternalHistogramName[];
  static const char kDailyDiscardsUrgentHistogramName[];
  static const char kDailyDiscardsProactiveHistogramName[];
  static const char kDailyDiscardsSuggestedHistogramName[];
  static const char kDailyDiscardsFrozenWithGrowingMemoryHistogramName[];
  static const char kDailyReloadsExternalHistogramName[];
  static const char kDailyReloadsUrgentHistogramName[];
  static const char kDailyReloadsProactiveHistogramName[];
  static const char kDailyReloadsSuggestedHistogramName[];
  static const char kDailyReloadsFrozenWithGrowingMemoryHistogramName[];

  // The names of the histograms that record duplicate tab data.
  static const char kTabDuplicateCountSingleWindowHistogramName[];
  static const char kTabDuplicateCountAllProfileWindowsHistogramName[];
  static const char kTabDuplicatePercentageSingleWindowHistogramName[];
  static const char kTabDuplicatePercentageAllProfileWindowsHistogramName[];
  static const char
      kTabDuplicateExcludingFragmentsCountSingleWindowHistogramName[];
  static const char
      kTabDuplicateExcludingFragmentsCountAllProfileWindowsHistogramName[];
  static const char
      kTabDuplicateExcludingFragmentsPercentageSingleWindowHistogramName[];
  static const char
      kTabDuplicateExcludingFragmentsPercentageAllProfileWindowsHistogramName[];

  UmaStatsReportingDelegate() = default;

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

  virtual ~UmaStatsReportingDelegate() = default;

  // Called at resume from sleep/hibernate.
  void ReportTabCountOnResume(size_t tab_count);

  // Called once per day to report the metrics.
  void ReportDailyMetrics(const TabStatsDataStore::TabsStats& tab_stats);

  // Report the tab heartbeat metrics.
  void ReportHeartbeatMetrics(const TabStatsDataStore::TabsStats& tab_stats);

  // Calculate and report the metrics related to tab duplicates, which are
  // re-calculated each time rather than cached like the other metrics due to
  // their complexity. |exclude_fragments| will treat two tabs with the same
  // URL apart from trailing fragments as duplicates, otherwise will only treat
  // exact URL matches as duplicates.
  void ReportTabDuplicateMetrics(bool exclude_fragments);

 protected:
  // Checks if Chrome is running in background with no visible windows, virtual
  // for unittesting.
  virtual bool IsChromeBackgroundedWithoutWindows();

 private:
  struct DuplicateData {
    DuplicateData();
    DuplicateData(const DuplicateData&);
    ~DuplicateData();

    int duplicate_count;
    int tab_count;
    std::set<GURL> seen_urls;
  };
};

}  // namespace metrics

#endif  // CHROME_BROWSER_METRICS_TAB_STATS_TAB_STATS_TRACKER_H_