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