1 // Copyright 2017 The Chromium Authors. All rights reserved.
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 "chrome/browser/metrics/tab_stats_data_store.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_list.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/common/pref_names.h"
14 #include "components/prefs/pref_service.h"
15 #include "content/public/browser/web_contents.h"
16 
17 namespace metrics {
18 
19 namespace {
20 
21 // Computes a new, unique, TabID.
GetNewTabId()22 TabStatsDataStore::TabID GetNewTabId() {
23   static TabStatsDataStore::TabID web_contents_id = 0U;
24   return ++web_contents_id;
25 }
26 
27 }  // namespace
28 
TabsStats()29 TabStatsDataStore::TabsStats::TabsStats()
30     : total_tab_count(0U),
31       total_tab_count_max(0U),
32       max_tab_per_window(0U),
33       window_count(0U),
34       window_count_max(0U) {
35 }
36 TabStatsDataStore::TabsStats::TabsStats(const TabsStats& other) = default;
37 
TabStatsDataStore(PrefService * pref_service)38 TabStatsDataStore::TabStatsDataStore(PrefService* pref_service)
39     : pref_service_(pref_service) {
40   DCHECK(pref_service);
41   tab_stats_.total_tab_count_max =
42       pref_service->GetInteger(::prefs::kTabStatsTotalTabCountMax);
43   tab_stats_.max_tab_per_window =
44       pref_service->GetInteger(::prefs::kTabStatsMaxTabsPerWindow);
45   tab_stats_.window_count_max =
46       pref_service->GetInteger(::prefs::kTabStatsWindowCountMax);
47 }
48 
~TabStatsDataStore()49 TabStatsDataStore::~TabStatsDataStore() {}
50 
OnWindowAdded()51 void TabStatsDataStore::OnWindowAdded() {
52   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
53   tab_stats_.window_count++;
54   UpdateWindowCountMaxIfNeeded();
55 }
56 
OnWindowRemoved()57 void TabStatsDataStore::OnWindowRemoved() {
58   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
59   DCHECK_GT(tab_stats_.window_count, 0U);
60   tab_stats_.window_count--;
61 }
62 
OnTabAdded(content::WebContents * web_contents)63 void TabStatsDataStore::OnTabAdded(content::WebContents* web_contents) {
64   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
65   DCHECK(web_contents);
66   DCHECK(!base::Contains(existing_tabs_, web_contents));
67   ++tab_stats_.total_tab_count;
68   TabID tab_id = GetNewTabId();
69   existing_tabs_.insert(std::make_pair(web_contents, tab_id));
70   for (auto& interval_map : interval_maps_) {
71     AddTabToIntervalMap(web_contents, tab_id,
72                         /* existed_before_interval */ false,
73                         interval_map.get());
74   }
75   UpdateTotalTabCountMaxIfNeeded();
76 }
77 
OnTabRemoved(content::WebContents * web_contents)78 void TabStatsDataStore::OnTabRemoved(content::WebContents* web_contents) {
79   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
80   DCHECK(web_contents);
81   DCHECK(base::Contains(existing_tabs_, web_contents));
82   DCHECK_GT(tab_stats_.total_tab_count, 0U);
83   --tab_stats_.total_tab_count;
84   TabID web_contents_id = GetTabID(web_contents);
85   existing_tabs_.erase(web_contents);
86   for (auto& interval_map : interval_maps_) {
87     auto iter = interval_map->find(web_contents_id);
88     DCHECK(iter != interval_map->end());
89     iter->second.exists_currently = false;
90   }
91 }
92 
OnTabReplaced(content::WebContents * old_contents,content::WebContents * new_contents)93 void TabStatsDataStore::OnTabReplaced(content::WebContents* old_contents,
94                                       content::WebContents* new_contents) {
95   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
96   DCHECK(old_contents);
97   DCHECK(new_contents);
98   DCHECK(base::Contains(existing_tabs_, old_contents));
99   DCHECK_GT(tab_stats_.total_tab_count, 0U);
100   TabID old_contents_id = existing_tabs_[old_contents];
101   existing_tabs_.erase(old_contents);
102   existing_tabs_[new_contents] = old_contents_id;
103 }
104 
UpdateMaxTabsPerWindowIfNeeded(size_t value)105 void TabStatsDataStore::UpdateMaxTabsPerWindowIfNeeded(size_t value) {
106   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
107   if (value <= tab_stats_.max_tab_per_window)
108     return;
109   tab_stats_.max_tab_per_window = value;
110   pref_service_->SetInteger(::prefs::kTabStatsMaxTabsPerWindow, value);
111 }
112 
ResetMaximumsToCurrentState()113 void TabStatsDataStore::ResetMaximumsToCurrentState() {
114   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115   // Set the maximums to 0 and call the Update* functions to reset it to the
116   // current value and update the pref registry.
117   tab_stats_.max_tab_per_window = 0;
118   tab_stats_.window_count_max = 0;
119   tab_stats_.total_tab_count_max = 0;
120   UpdateTotalTabCountMaxIfNeeded();
121   UpdateWindowCountMaxIfNeeded();
122 
123   // Iterates over the list of browsers to find the one with the maximum number
124   // of tabs opened.
125   BrowserList* browser_list = BrowserList::GetInstance();
126   for (Browser* browser : *browser_list) {
127     UpdateMaxTabsPerWindowIfNeeded(
128         static_cast<size_t>(browser->tab_strip_model()->count()));
129   }
130 }
131 
OnTabInteraction(content::WebContents * web_contents)132 void TabStatsDataStore::OnTabInteraction(content::WebContents* web_contents) {
133   DCHECK(base::Contains(existing_tabs_, web_contents));
134   TabID web_contents_id = GetTabID(web_contents);
135   // Mark the tab as interacted with in all the intervals.
136   for (auto& interval_map : interval_maps_) {
137     DCHECK(base::Contains(*interval_map, web_contents_id));
138     (*interval_map)[web_contents_id].interacted_during_interval = true;
139   }
140 }
141 
OnTabAudible(content::WebContents * web_contents)142 void TabStatsDataStore::OnTabAudible(content::WebContents* web_contents) {
143   OnTabAudibleOrVisible(web_contents);
144 }
145 
OnTabVisible(content::WebContents * web_contents)146 void TabStatsDataStore::OnTabVisible(content::WebContents* web_contents) {
147   OnTabAudibleOrVisible(web_contents);
148 }
149 
150 TabStatsDataStore::TabsStateDuringIntervalMap*
AddInterval()151 TabStatsDataStore::AddInterval() {
152   // Creates the interval and initialize its data.
153   std::unique_ptr<TabsStateDuringIntervalMap> interval_map =
154       std::make_unique<TabsStateDuringIntervalMap>();
155   ResetIntervalData(interval_map.get());
156   interval_maps_.emplace_back(std::move(interval_map));
157   return interval_maps_.back().get();
158 }
159 
ResetIntervalData(TabsStateDuringIntervalMap * interval_map)160 void TabStatsDataStore::ResetIntervalData(
161     TabsStateDuringIntervalMap* interval_map) {
162   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
163   DCHECK(interval_map);
164   interval_map->clear();
165   for (auto& iter : existing_tabs_)
166     AddTabToIntervalMap(iter.first, GetTabID(iter.first), true, interval_map);
167 }
168 
GetTabIDForTesting(content::WebContents * web_contents)169 base::Optional<TabStatsDataStore::TabID> TabStatsDataStore::GetTabIDForTesting(
170     content::WebContents* web_contents) {
171   if (!base::Contains(existing_tabs_, web_contents))
172     return base::nullopt;
173   return GetTabID(web_contents);
174 }
175 
UpdateTotalTabCountMaxIfNeeded()176 void TabStatsDataStore::UpdateTotalTabCountMaxIfNeeded() {
177   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
178   if (tab_stats_.total_tab_count <= tab_stats_.total_tab_count_max)
179     return;
180   tab_stats_.total_tab_count_max = tab_stats_.total_tab_count;
181   pref_service_->SetInteger(::prefs::kTabStatsTotalTabCountMax,
182                             tab_stats_.total_tab_count_max);
183 }
184 
UpdateWindowCountMaxIfNeeded()185 void TabStatsDataStore::UpdateWindowCountMaxIfNeeded() {
186   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
187   if (tab_stats_.window_count <= tab_stats_.window_count_max)
188     return;
189   tab_stats_.window_count_max = tab_stats_.window_count;
190   pref_service_->SetInteger(::prefs::kTabStatsWindowCountMax,
191                             tab_stats_.window_count_max);
192 }
193 
AddTabToIntervalMap(content::WebContents * web_contents,TabID tab_id,bool existed_before_interval,TabsStateDuringIntervalMap * interval_map)194 void TabStatsDataStore::AddTabToIntervalMap(
195     content::WebContents* web_contents,
196     TabID tab_id,
197     bool existed_before_interval,
198     TabsStateDuringIntervalMap* interval_map) {
199   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
200   DCHECK(interval_map);
201   DCHECK(web_contents);
202   bool visible_or_audible =
203       web_contents->GetVisibility() == content::Visibility::VISIBLE ||
204       web_contents->IsCurrentlyAudible();
205 
206   auto& tab_state = (*interval_map)[tab_id];
207   tab_state.existed_before_interval = existed_before_interval;
208   tab_state.exists_currently = true;
209   tab_state.visible_or_audible_during_interval = visible_or_audible;
210   tab_state.interacted_during_interval = false;
211 }
212 
GetTabID(content::WebContents * web_contents)213 TabStatsDataStore::TabID TabStatsDataStore::GetTabID(
214     content::WebContents* web_contents) {
215   DCHECK(base::Contains(existing_tabs_, web_contents));
216   return existing_tabs_[web_contents];
217 }
218 
OnTabAudibleOrVisible(content::WebContents * web_contents)219 void TabStatsDataStore::OnTabAudibleOrVisible(
220     content::WebContents* web_contents) {
221   DCHECK(base::Contains(existing_tabs_, web_contents));
222   TabID web_contents_id = GetTabID(web_contents);
223   // Mark the tab as visible or audible in all the intervals.
224   for (auto& interval_map : interval_maps_) {
225     DCHECK(base::Contains(*interval_map, web_contents_id));
226     (*interval_map)[web_contents_id].visible_or_audible_during_interval = true;
227   }
228 }
229 
230 }  // namespace metrics
231