// Copyright 2015 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. #ifndef CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_ #define CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_ #include #include #include #include "base/callback_list.h" #include "base/macros.h" #include "base/time/tick_clock.h" #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/sessions/session_restore_delegate.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" namespace content { class NavigationController; class RenderWidgetHost; } // SessionRestoreStatsCollector observes SessionRestore events ands records UMA // accordingly. // // A SessionRestoreStatsCollector is tied to an instance of a session restore, // currently being instantianted and owned by the TabLoader. It has two main // phases to its life: // // 1. The session restore is active and ongoing (the TabLoader is still // scheduling tabs for loading). This phases ends when there are no // non-deferred tabs left to be loaded. During this phases statistics are // gathered in a structure before being emitted as UMA metrics at the end of // this phase. At this point the TabLoader ceases to exist and destroys it's // reference to the SessionRestoreStatsCollector. // 2. If any tabs have been deferred the SessionRestoreStatsCollector continues // tracking deferred tabs. This continues to observe the tabs to see which // (if any) of the deferred tabs are subsequently forced to be loaded by the // user. Since such tabs may exist until the end of the browsers life the // statistics are emitted immediately, or risk being lost entirely. When // there are no longer deferred tabs to track the // SessionRestoreStatsCollector will destroy itself. // // TODO(chrisha): Many of these metrics don't make sense to collect in the // presence of an unavailable network, or when tabs are closed during loading. // Rethink the collection in these cases. class SessionRestoreStatsCollector : public content::NotificationObserver, public base::RefCounted { public: // Houses all of the statistics gathered by the SessionRestoreStatsCollector // while the underlying TabLoader is active. These statistics are all reported // at once via the reporting delegate. struct TabLoaderStats { // Constructor that initializes everything to zero. TabLoaderStats(); // The number of tabs involved in all overlapping session restores being // tracked by this SessionRestoreStatsCollector. This corresponds to the // "SessionRestore.TabCount" metric and one bucket of the // "SessionRestore.TabActions" histogram. If any tabs were deferred it also // corresponds to the "SessionRestore.TabCount.MemoryPressure.Total" // histogram. size_t tab_count; // The number of restored tabs that were deferred. Corresponds to the // "SessionRestore.TabCount.MemoryPressure.Deferred" histogram. size_t tabs_deferred; // The number of tabs whose loading was automatically started because they // are active or explicitly caused to be loaded by the TabLoader. This // corresponds to one bucket of the "SessionRestore.TabActions" histogram // and the "SessionRestore.TabCount.MemoryPressure.LoadStarted". size_t tabs_load_started; // The number of tabs loaded automatically because they are active, or // explicitly caused to be loaded by the TabLoader. This corresponds to one // bucket of the "SessionRestore.TabActions" histogram, and the // "SessionRestore.TabCount.MemoryPressure.Loaded" histogram. size_t tabs_loaded; // The time elapsed between |restore_started| and reception of the first // NOTIFICATION_LOAD_STOP event for any of the active tabs involved in the // session restore. If this is zero it is because it has not been // recorded (all visible tabs were closed before they finished loading, or // the user switched to an already loaded tab before a visible session // restore tab finished loading). Corresponds to // "SessionRestore.ForegroundTabFirstLoaded" and its _XX variants. base::TimeDelta foreground_tab_first_loaded; // The time elapsed between |restore_started| and reception of the first // NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE event for any of // the tabs involved in the session restore. If this is zero it is because // it has not been recorded (all visible tabs were closed or switched away // from before they were painted). Corresponds to // "SessionRestore.ForegroundTabFirstPaint3" and its _XX variants. base::TimeDelta foreground_tab_first_paint; // The time taken for all non-deferred tabs to be loaded. This corresponds // to the "SessionRestore.AllTabsLoaded" metric and its _XX variants // (vaguely named for historical reasons, as it predates the concept of // deferred tabs). base::TimeDelta non_deferred_tabs_loaded; // The maximum number of tabs loading in parallel. This corresponds to the // "SessionRestore.ParallelTabLoads" metric. size_t parallel_tab_loads; }; // The StatsReportingDelegate is responsible for delivering statistics // reported by the SessionRestoreStatsCollector. class StatsReportingDelegate; // An implementation of StatsReportingDelegate for reporting via UMA. class UmaStatsReportingDelegate; // Constructs a SessionRestoreStatsCollector. SessionRestoreStatsCollector( const base::TimeTicks& restore_started, scoped_ptr reporting_delegate); // Adds new tabs to the list of tracked tabs. void TrackTabs(const std::vector& tabs); // Called to indicate that the loading of a tab has been deferred by session // restore. void DeferTab(content::NavigationController* tab); // Exposed for unittesting. const TabLoaderStats& tab_loader_stats() const { return tab_loader_stats_; } private: friend class TestSessionRestoreStatsCollector; friend class base::RefCounted; enum TabLoadingState { TAB_IS_NOT_LOADING, TAB_IS_LOADING, TAB_IS_LOADED }; // State that is tracked for a tab while it is being observed. struct TabState { explicit TabState(content::NavigationController* controller); // The NavigationController associated with the tab. This is the primary // index for it and is never null. content::NavigationController* controller; // Set to true if the tab has been deferred by the TabLoader. bool is_deferred; // The current loading state of the tab. TabLoadingState loading_state; }; // Maps a NavigationController to its state. This is the primary map and // physically houses the state. using NavigationControllerMap = std::map; ~SessionRestoreStatsCollector() override; // NotificationObserver method. This is the workhorse of the class and drives // all state transitions. void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) override; // Called when a tab is no longer tracked. This is called by the 'Observe' // notification callback. Takes care of unregistering all observers and // removing the tab from all internal data structures. void RemoveTab(content::NavigationController* tab); // Registers for relevant notifications for a tab and inserts the tab into // to tabs_tracked_ map. Return a pointer to the newly created TabState. TabState* RegisterForNotifications(content::NavigationController* tab); // Returns the tab state, nullptr if not found. TabState* GetTabState(content::NavigationController* tab); TabState* GetTabState(content::RenderWidgetHost* tab); // Marks a tab as loading. void MarkTabAsLoading(TabState* tab_state); // Checks to see if the SessionRestoreStatsCollector has finished collecting, // and if so, releases the self reference to the shared pointer. void ReleaseIfDoneTracking(); // Testing seam for configuring the tick clock in use. void set_tick_clock(scoped_ptr tick_clock) { tick_clock_ = std::move(tick_clock); } // Has ReleaseIfDoneTracking determined that there are no non-deferred tabs to // track? bool done_tracking_non_deferred_tabs_; // Has the time for foreground tab load been recorded? bool got_first_foreground_load_; // Has the time for foreground tab paint been recorded? bool got_first_paint_; // The time the restore process started. const base::TimeTicks restore_started_; // List of tracked tabs, mapped to their TabState. NavigationControllerMap tabs_tracked_; // Counts the number of non-deferred tabs that the // SessionRestoreStatsCollector is waiting to see load. size_t waiting_for_load_tab_count_; // Counts the current number of actively loading tabs. size_t loading_tab_count_; // Counts the current number of deferred tabs. size_t deferred_tab_count_; // Notification registrar. content::NotificationRegistrar registrar_; // Statistics gathered regarding the TabLoader. TabLoaderStats tab_loader_stats_; // The source of ticks used for taking timing information. This is // configurable as a testing seam. Defaults to using base::DefaultTickClock, // which in turn uses base::TimeTicks. scoped_ptr tick_clock_; // The reporting delegate used to report gathered statistics. scoped_ptr reporting_delegate_; // For keeping SessionRestoreStatsCollector alive while it is still working // even if no TabLoader references it. The object only lives on if it still // has deferred tabs remaining from an interrupted session restore. scoped_refptr this_retainer_; DISALLOW_COPY_AND_ASSIGN(SessionRestoreStatsCollector); }; // An abstract reporting delegate is used as a testing seam. class SessionRestoreStatsCollector::StatsReportingDelegate { public: StatsReportingDelegate() {} virtual ~StatsReportingDelegate() {} // Called when TabLoader has completed its work. virtual void ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) = 0; // Called when a tab has been deferred. virtual void ReportTabDeferred() = 0; // Called when a deferred tab has been loaded. virtual void ReportDeferredTabLoaded() = 0; private: DISALLOW_COPY_AND_ASSIGN(StatsReportingDelegate); }; // The default reporting delegate, which reports statistics via UMA. class SessionRestoreStatsCollector::UmaStatsReportingDelegate : public StatsReportingDelegate { public: UmaStatsReportingDelegate(); ~UmaStatsReportingDelegate() override {} // StatsReportingDelegate: void ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) override; void ReportTabDeferred() override; void ReportDeferredTabLoaded() override; private: // Has ReportTabDeferred been called? bool got_report_tab_deferred_; DISALLOW_COPY_AND_ASSIGN(UmaStatsReportingDelegate); }; #endif // CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_