| // 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/browser_window/public/browser_window_interface_iterator.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. | 
 |   BrowserWindowInterface* const current_browser = | 
 |       GetLastActiveBrowserWindowInterfaceWithAnyProfile(); | 
 |   if (current_browser) { | 
 |     TabStripModel* const tab_strip_model = current_browser->GetTabStripModel(); | 
 |     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); |