| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/metrics/tab_stats_data_store.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/web_contents.h" |
| |
| namespace metrics { |
| |
| namespace { |
| |
| // Computes a new, unique, TabID. |
| TabStatsDataStore::TabID GetNewTabId() { |
| static TabStatsDataStore::TabID web_contents_id = 0U; |
| return ++web_contents_id; |
| } |
| |
| } // namespace |
| |
| TabStatsDataStore::TabsStats::TabsStats() |
| : total_tab_count(0U), |
| total_tab_count_max(0U), |
| max_tab_per_window(0U), |
| window_count(0U), |
| window_count_max(0U) {} |
| |
| TabStatsDataStore::TabStatsDataStore(PrefService* pref_service) |
| : pref_service_(pref_service) { |
| DCHECK(pref_service); |
| tab_stats_.total_tab_count_max = |
| pref_service->GetInteger(::prefs::kTabStatsTotalTabCountMax); |
| tab_stats_.max_tab_per_window = |
| pref_service->GetInteger(::prefs::kTabStatsMaxTabsPerWindow); |
| tab_stats_.window_count_max = |
| pref_service->GetInteger(::prefs::kTabStatsWindowCountMax); |
| } |
| |
| TabStatsDataStore::~TabStatsDataStore() {} |
| |
| void TabStatsDataStore::OnWindowAdded() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| tab_stats_.window_count++; |
| UpdateWindowCountMaxIfNeeded(); |
| } |
| |
| void TabStatsDataStore::OnWindowRemoved() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_GT(tab_stats_.window_count, 0U); |
| tab_stats_.window_count--; |
| } |
| |
| void TabStatsDataStore::OnTabAdded(content::WebContents* web_contents) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(web_contents); |
| DCHECK(!base::ContainsKey(existing_tabs_, web_contents)); |
| ++tab_stats_.total_tab_count; |
| TabID tab_id = GetNewTabId(); |
| existing_tabs_.insert(std::make_pair(web_contents, tab_id)); |
| for (auto& interval_map : interval_maps_) { |
| AddTabToIntervalMap(web_contents, tab_id, |
| /* existed_before_interval */ false, |
| interval_map.get()); |
| } |
| UpdateTotalTabCountMaxIfNeeded(); |
| } |
| |
| void TabStatsDataStore::OnTabRemoved(content::WebContents* web_contents) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(web_contents); |
| DCHECK(base::ContainsKey(existing_tabs_, web_contents)); |
| DCHECK_GT(tab_stats_.total_tab_count, 0U); |
| --tab_stats_.total_tab_count; |
| TabID web_contents_id = GetTabID(web_contents); |
| existing_tabs_.erase(web_contents); |
| for (auto& interval_map : interval_maps_) { |
| auto iter = interval_map->find(web_contents_id); |
| DCHECK(iter != interval_map->end()); |
| iter->second.exists_currently = false; |
| } |
| } |
| |
| void TabStatsDataStore::OnTabReplaced(content::WebContents* old_contents, |
| content::WebContents* new_contents) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(old_contents); |
| DCHECK(new_contents); |
| DCHECK(base::ContainsKey(existing_tabs_, old_contents)); |
| DCHECK_GT(tab_stats_.total_tab_count, 0U); |
| TabID old_contents_id = existing_tabs_[old_contents]; |
| existing_tabs_.erase(old_contents); |
| existing_tabs_[new_contents] = old_contents_id; |
| } |
| |
| void TabStatsDataStore::UpdateMaxTabsPerWindowIfNeeded(size_t value) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (value <= tab_stats_.max_tab_per_window) |
| return; |
| tab_stats_.max_tab_per_window = value; |
| pref_service_->SetInteger(::prefs::kTabStatsMaxTabsPerWindow, value); |
| } |
| |
| void TabStatsDataStore::ResetMaximumsToCurrentState() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // Set the maximums to 0 and call the Update* functions to reset it to the |
| // current value and update the pref registry. |
| tab_stats_.max_tab_per_window = 0; |
| tab_stats_.window_count_max = 0; |
| tab_stats_.total_tab_count_max = 0; |
| UpdateTotalTabCountMaxIfNeeded(); |
| UpdateWindowCountMaxIfNeeded(); |
| |
| // Iterates over the list of browsers to find the one with the maximum number |
| // of tabs opened. |
| BrowserList* browser_list = BrowserList::GetInstance(); |
| for (Browser* browser : *browser_list) { |
| UpdateMaxTabsPerWindowIfNeeded( |
| static_cast<size_t>(browser->tab_strip_model()->count())); |
| } |
| } |
| |
| void TabStatsDataStore::OnTabInteraction(content::WebContents* web_contents) { |
| DCHECK(base::ContainsKey(existing_tabs_, web_contents)); |
| TabID web_contents_id = GetTabID(web_contents); |
| // Mark the tab as interacted with in all the intervals. |
| for (auto& interval_map : interval_maps_) { |
| DCHECK(base::ContainsKey(*interval_map, web_contents_id)); |
| (*interval_map)[web_contents_id].interacted_during_interval = true; |
| } |
| } |
| |
| void TabStatsDataStore::OnTabAudible(content::WebContents* web_contents) { |
| OnTabAudibleOrVisible(web_contents); |
| } |
| |
| void TabStatsDataStore::OnTabVisible(content::WebContents* web_contents) { |
| OnTabAudibleOrVisible(web_contents); |
| } |
| |
| TabStatsDataStore::TabsStateDuringIntervalMap* |
| TabStatsDataStore::AddInterval() { |
| // Creates the interval and initialize its data. |
| std::unique_ptr<TabsStateDuringIntervalMap> interval_map = |
| std::make_unique<TabsStateDuringIntervalMap>(); |
| ResetIntervalData(interval_map.get()); |
| interval_maps_.emplace_back(std::move(interval_map)); |
| return interval_maps_.back().get(); |
| } |
| |
| void TabStatsDataStore::ResetIntervalData( |
| TabsStateDuringIntervalMap* interval_map) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(interval_map); |
| interval_map->clear(); |
| for (auto& iter : existing_tabs_) |
| AddTabToIntervalMap(iter.first, GetTabID(iter.first), true, interval_map); |
| } |
| |
| base::Optional<TabStatsDataStore::TabID> TabStatsDataStore::GetTabIDForTesting( |
| content::WebContents* web_contents) { |
| if (!base::ContainsKey(existing_tabs_, web_contents)) |
| return base::nullopt; |
| return GetTabID(web_contents); |
| } |
| |
| void TabStatsDataStore::UpdateTotalTabCountMaxIfNeeded() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (tab_stats_.total_tab_count <= tab_stats_.total_tab_count_max) |
| return; |
| tab_stats_.total_tab_count_max = tab_stats_.total_tab_count; |
| pref_service_->SetInteger(::prefs::kTabStatsTotalTabCountMax, |
| tab_stats_.total_tab_count_max); |
| } |
| |
| void TabStatsDataStore::UpdateWindowCountMaxIfNeeded() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (tab_stats_.window_count <= tab_stats_.window_count_max) |
| return; |
| tab_stats_.window_count_max = tab_stats_.window_count; |
| pref_service_->SetInteger(::prefs::kTabStatsWindowCountMax, |
| tab_stats_.window_count_max); |
| } |
| |
| void TabStatsDataStore::AddTabToIntervalMap( |
| content::WebContents* web_contents, |
| TabID tab_id, |
| bool existed_before_interval, |
| TabsStateDuringIntervalMap* interval_map) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(interval_map); |
| DCHECK(web_contents); |
| bool visible_or_audible = |
| web_contents->GetVisibility() == content::Visibility::VISIBLE || |
| web_contents->IsCurrentlyAudible(); |
| |
| auto& tab_state = (*interval_map)[tab_id]; |
| tab_state.existed_before_interval = existed_before_interval; |
| tab_state.exists_currently = true; |
| tab_state.visible_or_audible_during_interval = visible_or_audible; |
| tab_state.interacted_during_interval = false; |
| } |
| |
| TabStatsDataStore::TabID TabStatsDataStore::GetTabID( |
| content::WebContents* web_contents) { |
| DCHECK(base::ContainsKey(existing_tabs_, web_contents)); |
| return existing_tabs_[web_contents]; |
| } |
| |
| void TabStatsDataStore::OnTabAudibleOrVisible( |
| content::WebContents* web_contents) { |
| DCHECK(base::ContainsKey(existing_tabs_, web_contents)); |
| TabID web_contents_id = GetTabID(web_contents); |
| // Mark the tab as visible or audible in all the intervals. |
| for (auto& interval_map : interval_maps_) { |
| DCHECK(base::ContainsKey(*interval_map, web_contents_id)); |
| (*interval_map)[web_contents_id].visible_or_audible_during_interval = true; |
| } |
| } |
| |
| } // namespace metrics |