1 // Copyright 2015 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 #ifndef CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_
6 #define CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_
7 
8 #include <stddef.h>
9 #include <map>
10 #include <memory>
11 #include <utility>
12 #include <vector>
13 
14 #include "base/scoped_observer.h"
15 #include "base/macros.h"
16 #include "chrome/browser/sessions/session_restore.h"
17 #include "chrome/browser/sessions/session_restore_delegate.h"
18 #include "content/public/browser/notification_observer.h"
19 #include "content/public/browser/notification_registrar.h"
20 #include "content/public/browser/render_widget_host.h"
21 #include "content/public/browser/render_widget_host_observer.h"
22 
23 namespace content {
24 class NavigationController;
25 }
26 
27 // SessionRestoreStatsCollector observes SessionRestore events ands records UMA
28 // accordingly.
29 //
30 // TODO(chrisha): Many of these metrics don't make sense to collect in the
31 // presence of an unavailable network, or when tabs are closed during loading.
32 // Rethink the collection in these cases.
33 class SessionRestoreStatsCollector : public content::NotificationObserver,
34                                      public content::RenderWidgetHostObserver {
35  public:
36   // Recorded in SessionRestore.ForegroundTabFirstPaint4.FinishReason metric.
37   // Values other than PAINT_FINISHED_UMA_DONE indicate why FirstPaint time
38   // was not recorded.
39   enum SessionRestorePaintFinishReasonUma {
40     // SessionRestore.ForegroundTabFirstPaint4_XX successfully recorded.
41     PAINT_FINISHED_UMA_DONE = 0,
42     // No tabs were visible the whole time before first paint.
43     PAINT_FINISHED_UMA_NO_COMPLETELY_VISIBLE_TABS = 1,
44     // No restored tabs were painted.
45     PAINT_FINISHED_UMA_NO_PAINT = 2,
46     // A non-restored tab was painted first.
47     PAINT_FINISHED_NON_RESTORED_TAB_PAINTED_FIRST = 3,
48     // The size of this enum. Must be the last entry.
49     PAINT_FINISHED_UMA_MAX = 4,
50   };
51 
52   // Houses all of the statistics gathered by the SessionRestoreStatsCollector
53   // while the underlying TabLoader is active. These statistics are all reported
54   // at once via the reporting delegate.
55   struct TabLoaderStats {
56     // Constructor that initializes everything to zero.
57     TabLoaderStats();
58 
59     // The number of tabs involved in all overlapping session restores being
60     // tracked by this SessionRestoreStatsCollector. This is used as suffix for
61     // the "SessionRestore.ForegroundTabFirstPaint4" histogram.
62     size_t tab_count;
63 
64     // The time elapsed between |restore_started| and reception of the first
65     // NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_VISUAL_PROPERTIES event for
66     // any of the tabs involved in the session restore. If this is zero it is
67     // because it has not been recorded (all restored tabs were closed or
68     // hidden before they were painted, or were never painted). Corresponds to
69     // "SessionRestore.ForegroundTabFirstPaint4" and its _XX variants.
70     base::TimeDelta foreground_tab_first_paint;
71 
72     // Whether we recorded |foreground_tab_first_paint| and if not, why.
73     SessionRestorePaintFinishReasonUma tab_first_paint_reason;
74   };
75 
76   // The StatsReportingDelegate is responsible for delivering statistics
77   // reported by the SessionRestoreStatsCollector.
78   class StatsReportingDelegate;
79 
80   // An implementation of StatsReportingDelegate for reporting via UMA.
81   class UmaStatsReportingDelegate;
82 
83   // Gets or creates an instance of SessionRestoreStatsCollector. An instance
84   // self-deletes once it has reported all stats. If an existing instance is
85   // returned, |restored_started| and |reporting_delegate| are ignored.
86   static SessionRestoreStatsCollector* GetOrCreateInstance(
87       base::TimeTicks restore_started,
88       std::unique_ptr<StatsReportingDelegate> reporting_delegate);
89 
90   // Tracks stats for restored tabs. Tabs from overlapping session restores can
91   // be tracked by the same SessionRestoreStatsCollector.
92   void TrackTabs(const std::vector<SessionRestoreDelegate::RestoredTab>& tabs);
93 
94  private:
95   friend class SessionRestoreStatsCollectorTest;
96 
97   // State that is tracked for a tab while it is being observed.
98   struct TabState {
99     explicit TabState(content::NavigationController* controller);
100 
101     // The NavigationController associated with the tab. This is the primary
102     // index for it and is never null.
103     content::NavigationController* controller;
104 
105     // True if the tab was ever hidden or occluded during the restore process.
106     bool was_hidden_or_occluded;
107 
108     // RenderWidgetHost* SessionRestoreStatsCollector is observing for this tab,
109     // if any.
110     content::RenderWidgetHost* observed_host;
111   };
112 
113   // Maps a NavigationController to its state. This is the primary map and
114   // physically houses the state.
115   using NavigationControllerMap =
116       std::map<content::NavigationController*, TabState>;
117 
118   // Constructs a SessionRestoreStatsCollector.
119   SessionRestoreStatsCollector(
120       const base::TimeTicks& restore_started,
121       std::unique_ptr<StatsReportingDelegate> reporting_delegate);
122 
123   ~SessionRestoreStatsCollector() override;
124 
125   // NotificationObserver method. This is the workhorse of the class and drives
126   // all state transitions.
127   void Observe(int type,
128                const content::NotificationSource& source,
129                const content::NotificationDetails& details) override;
130 
131   // RenderWidgetHostObserver:
132   void RenderWidgetHostVisibilityChanged(content::RenderWidgetHost* widget_host,
133                                          bool became_visible) override;
134 
135   void RenderWidgetHostDestroyed(
136       content::RenderWidgetHost* widget_host) override;
137 
138   // Called when a tab is no longer tracked. This is called by the 'Observe'
139   // notification callback. Takes care of unregistering all observers and
140   // removing the tab from all internal data structures.
141   void RemoveTab(content::NavigationController* tab);
142 
143   // Registers for relevant notifications for a tab and inserts the tab into
144   // to |tabs_tracked_| map. Return a pointer to the newly created TabState.
145   TabState* RegisterForNotifications(content::NavigationController* tab);
146 
147   // Returns the tab state, nullptr if not found.
148   TabState* GetTabState(content::NavigationController* tab);
149   TabState* GetTabState(content::RenderWidgetHost* tab);
150 
151   // Report stats and self-deletes.
152   void ReportStatsAndSelfDestroy();
153 
154   // Won't record time for foreground tab paint because a non-restored
155   // tab was painted first.
156   bool non_restored_tab_painted_first_;
157 
158   // Got first paint of tab that was hidden or occluded before being painted.
159   bool hidden_or_occluded_tab_ignored_;
160 
161   // The time the restore process started.
162   const base::TimeTicks restore_started_;
163 
164   // List of tracked tabs, mapped to their TabState.
165   NavigationControllerMap tabs_tracked_;
166 
167   // Notification registrar.
168   content::NotificationRegistrar registrar_;
169 
170   // Statistics gathered regarding the TabLoader.
171   TabLoaderStats tab_loader_stats_;
172 
173   // The reporting delegate used to report gathered statistics.
174   std::unique_ptr<StatsReportingDelegate> reporting_delegate_;
175 
176   ScopedObserver<content::RenderWidgetHost, content::RenderWidgetHostObserver>
177       observer_{this};
178 
179   DISALLOW_COPY_AND_ASSIGN(SessionRestoreStatsCollector);
180 };
181 
182 // An abstract reporting delegate is used as a testing seam.
183 class SessionRestoreStatsCollector::StatsReportingDelegate {
184  public:
StatsReportingDelegate()185   StatsReportingDelegate() {}
~StatsReportingDelegate()186   virtual ~StatsReportingDelegate() {}
187 
188   // Called when TabLoader has completed its work.
189   virtual void ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) = 0;
190 
191   // Called when a tab starts being tracked. Logs the relative time since last
192   // use of the tab.
193   virtual void ReportTabTimeSinceActive(base::TimeDelta elapsed) = 0;
194 
195  private:
196   DISALLOW_COPY_AND_ASSIGN(StatsReportingDelegate);
197 };
198 
199 // The default reporting delegate, which reports statistics via UMA.
200 class SessionRestoreStatsCollector::UmaStatsReportingDelegate
201     : public StatsReportingDelegate {
202  public:
203   UmaStatsReportingDelegate();
~UmaStatsReportingDelegate()204   ~UmaStatsReportingDelegate() override {}
205 
206   // StatsReportingDelegate:
207   void ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) override;
208   void ReportTabTimeSinceActive(base::TimeDelta elapsed) override;
209 
210  private:
211 
212   DISALLOW_COPY_AND_ASSIGN(UmaStatsReportingDelegate);
213 };
214 
215 #endif  // CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_
216