blob: 6ba6107bee69e0cd748aba4fb71d113a88d53ec7 [file] [log] [blame] [edit]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/uma_browsing_activity_observer.h"
#include <algorithm>
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/lifetime/termination_notification.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/upgrade_detector/upgrade_detector.h"
#include "components/search_engines/template_url_service.h"
#include "components/tab_groups/tab_group_visual_data.h"
#include "components/tabs/public/tab_group.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/gfx/range/range.h"
namespace {
UMABrowsingActivityObserver* g_uma_browsing_activity_observer_instance =
nullptr;
} // namespace
// static
void UMABrowsingActivityObserver::Init() {
DCHECK(!g_uma_browsing_activity_observer_instance);
// Must be created before any Browsers are.
DCHECK_EQ(0U, chrome::GetTotalBrowserCount());
g_uma_browsing_activity_observer_instance = new UMABrowsingActivityObserver;
}
UMABrowsingActivityObserver::UMABrowsingActivityObserver() {
subscription_ = browser_shutdown::AddAppTerminatingCallback(base::BindOnce(
&UMABrowsingActivityObserver::OnAppTerminating, base::Unretained(this)));
}
UMABrowsingActivityObserver::~UMABrowsingActivityObserver() = default;
void UMABrowsingActivityObserver::OnNavigationEntryCommitted(
content::WebContents* web_contents,
const content::LoadCommittedDetails& load_details) const {
// Track whether the page loaded is a search results page (SRP). Track
// the non-SRP navigations as well so there is a control.
base::RecordAction(base::UserMetricsAction("NavEntryCommitted"));
if (!load_details.is_navigation_to_different_page()) {
// Don't log for subframes or other trivial types.
return;
}
LogBrowserTabCount();
}
void UMABrowsingActivityObserver::OnAppTerminating() const {
LogTimeBeforeUpdate();
DCHECK_EQ(this, g_uma_browsing_activity_observer_instance);
delete g_uma_browsing_activity_observer_instance;
g_uma_browsing_activity_observer_instance = nullptr;
}
void UMABrowsingActivityObserver::LogTimeBeforeUpdate() const {
const base::Time upgrade_detected_time =
UpgradeDetector::GetInstance()->upgrade_detected_time();
if (upgrade_detected_time.is_null()) {
return;
}
const base::TimeDelta time_since_upgrade =
base::Time::Now() - upgrade_detected_time;
constexpr int kMaxDays = 30;
base::UmaHistogramExactLinear("UpgradeDetector.DaysBeforeUpgrade",
base::TimeDelta(time_since_upgrade).InDays(),
kMaxDays);
base::UmaHistogramCounts1000("UpgradeDetector.HoursBeforeUpgrade",
base::TimeDelta(time_since_upgrade).InHours());
}
void UMABrowsingActivityObserver::LogBrowserTabCount() const {
int tab_count = 0;
int tab_group_count = 0;
int collapsed_tab_group_count = 0;
int customized_tab_group_count = 0;
int pinned_tab_count = 0;
for (Browser* browser : *BrowserList::GetInstance()) {
// Record how many tabs each window has open.
UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerWindow",
browser->tab_strip_model()->count(), 1, 200,
50);
TabStripModel* const tab_strip_model = browser->tab_strip_model();
tab_count += tab_strip_model->count();
for (int i = 0; i < tab_strip_model->count(); ++i) {
if (tab_strip_model->IsTabPinned(i)) {
pinned_tab_count++;
}
}
if (tab_strip_model->group_model()) {
const std::vector<tab_groups::TabGroupId>& groups =
tab_strip_model->group_model()->ListTabGroups();
tab_group_count += groups.size();
for (const tab_groups::TabGroupId& group_id : groups) {
const TabGroup* const tab_group =
tab_strip_model->group_model()->GetTabGroup(group_id);
if (tab_group->IsCustomized() ||
!tab_group->visual_data()->title().empty()) {
++customized_tab_group_count;
}
if (tab_group->visual_data()->is_collapsed()) {
++collapsed_tab_group_count;
}
}
}
if (browser->IsActive()) {
// Record how many tabs the active window has open.
UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountActiveWindow",
browser->tab_strip_model()->count(), 1, 200,
50);
}
}
// Record how many tabs total are open (across all windows).
UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tab_count, 1, 200, 50);
// Record how many tab groups (including zero) are open across all windows.
UMA_HISTOGRAM_COUNTS_100("TabGroups.UserGroupCountPerLoad", tab_group_count);
UMA_HISTOGRAM_COUNTS_100("TabGroups.UserPinnedTabCountPerLoad",
std::min(pinned_tab_count, 100));
// Record how many tabs are in the current group. Records 0 if the active tab
// is not in a group.
const Browser* current_browser = BrowserList::GetInstance()->GetLastActive();
if (current_browser) {
TabStripModel* const tab_strip_model = current_browser->tab_strip_model();
if (tab_strip_model->group_model()) {
const std::optional<tab_groups::TabGroupId> active_group =
tab_strip_model->GetTabGroupForTab(tab_strip_model->active_index());
UMA_HISTOGRAM_COUNTS_100("Tabs.TabCountInGroupPerLoad",
active_group.has_value()
? tab_strip_model->group_model()
->GetTabGroup(active_group.value())
->ListTabs()
.length()
: 0);
}
}
// Record how many tab groups with a user-set name or color are open across
// all windows.
UMA_HISTOGRAM_COUNTS_100("TabGroups.UserCustomizedGroupCountPerLoad",
customized_tab_group_count);
// Record how many tab groups are collapsed across all windows.
UMA_HISTOGRAM_COUNTS_100("TabGroups.CollapsedGroupCountPerLoad",
collapsed_tab_group_count);
}
UMABrowsingActivityObserver::TabHelper::TabHelper(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<TabHelper>(*web_contents) {}
UMABrowsingActivityObserver::TabHelper::~TabHelper() = default;
void UMABrowsingActivityObserver::TabHelper::NavigationEntryCommitted(
const content::LoadCommittedDetails& load_details) {
// This is null in unit tests. Crash reports suggest it's possible for it to
// be null in production. See https://crbug.com/1510023 and
// https://crbug.com/1523758
if (!g_uma_browsing_activity_observer_instance) {
return;
}
g_uma_browsing_activity_observer_instance->OnNavigationEntryCommitted(
web_contents(), load_details);
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(UMABrowsingActivityObserver::TabHelper);