1 // Copyright 2019 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 CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_METRICS_H_
6 #define CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_METRICS_H_
7 
8 #include <bitset>
9 #include <set>
10 
11 #include "base/macros.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/optional.h"
14 #include "base/strings/string_piece.h"
15 #include "base/time/tick_clock.h"
16 #include "base/time/time.h"
17 #include "content/browser/renderer_host/should_swap_browsing_instance.h"
18 #include "content/common/content_export.h"
19 
20 namespace url {
21 class Origin;
22 }
23 
24 namespace content {
25 class BackForwardCacheCanStoreDocumentResult;
26 class NavigationEntryImpl;
27 class NavigationRequest;
28 class RenderFrameHostImpl;
29 struct LoadCommittedDetails;
30 
31 // Helper class for recording metrics around history navigations.
32 // Associated with a main frame document and shared between all
33 // NavigationEntries with the same document_sequence_number for the main
34 // document.
35 //
36 // TODO(altimin, crbug.com/933147): Remove this class after we are done
37 // with implementing back-forward cache.
38 class BackForwardCacheMetrics
39     : public base::RefCounted<BackForwardCacheMetrics> {
40  public:
41   // Please keep in sync with BackForwardCacheNotRestoredReason in
42   // tools/metrics/histograms/enums.xml. These values should not be renumbered.
43   enum class NotRestoredReason : uint8_t {
44     kNotMainFrame = 0,
45     // BackForwardCache is disabled due to low memory device, base::Feature or
46     // command line. Note that the more specific NotRestoredReasons
47     // kBackForwardCacheDisabledByLowMemory and
48     // kBackForwardCacheDisabledByCommandLine will also be set as other reasons
49     // along with this when appropriate.
50     kBackForwardCacheDisabled = 1,
51     kRelatedActiveContentsExist = 2,
52     kHTTPStatusNotOK = 3,
53     kSchemeNotHTTPOrHTTPS = 4,
54     kLoading = 5,
55     kWasGrantedMediaAccess = 6,
56     kBlocklistedFeatures = 7,
57     kDisableForRenderFrameHostCalled = 8,
58     kDomainNotAllowed = 9,
59     kHTTPMethodNotGET = 10,
60     kSubframeIsNavigating = 11,
61     kTimeout = 12,
62     kCacheLimit = 13,
63     kJavaScriptExecution = 14,
64     kRendererProcessKilled = 15,
65     kRendererProcessCrashed = 16,
66     // 17: Dialogs are no longer a reason to exclude from BackForwardCache
67     kGrantedMediaStreamAccess = 18,
68     kSchedulerTrackedFeatureUsed = 19,
69     kConflictingBrowsingInstance = 20,
70     kCacheFlushed = 21,
71     kServiceWorkerVersionActivation = 22,
72     kSessionRestored = 23,
73     kUnknown = 24,
74     kServiceWorkerPostMessage = 25,
75     kEnteredBackForwardCacheBeforeServiceWorkerHostAdded = 26,
76     kRenderFrameHostReused_SameSite = 27,
77     kRenderFrameHostReused_CrossSite = 28,
78     kNotMostRecentNavigationEntry = 29,
79     kServiceWorkerClaim = 30,
80     kIgnoreEventAndEvict = 31,
81     kHaveInnerContents = 32,
82     kTimeoutPuttingInCache = 33,
83     // BackForwardCache is disabled due to low memory device.
84     kBackForwardCacheDisabledByLowMemory = 34,
85     // BackForwardCache is disabled due to command-line switch (may include
86     // cases where the embedder disabled it due to, e.g., enterprise policy).
87     kBackForwardCacheDisabledByCommandLine = 35,
88     kFrameTreeNodeStateReset = 36,
89     kNetworkRequestDatapipeDrained = 37,
90     kNetworkRequestRedirected = 38,
91     kNetworkRequestTimeout = 39,
92     kNetworkExceedsBufferLimit = 40,
93     kNavigationCancelledWhileRestoring = 41,
94     kMaxValue = kNavigationCancelledWhileRestoring,
95   };
96 
97   using NotRestoredReasons =
98       std::bitset<static_cast<size_t>(NotRestoredReason::kMaxValue) + 1ul>;
99 
100   // Please keep in sync with BackForwardCacheHistoryNavigationOutcome in
101   // tools/metrics/histograms/enums.xml. These values should not be renumbered.
102   enum class HistoryNavigationOutcome {
103     kRestored = 0,
104     kNotRestored = 1,
105     kMaxValue = kNotRestored,
106   };
107 
108   // Please keep in sync with BackForwardCacheEvictedAfterDocumentRestoredReason
109   // in tools/metrics/histograms/enums.xml. These values should not be
110   // renumbered.
111   enum class EvictedAfterDocumentRestoredReason {
112     kRestored = 0,
113     kByJavaScript = 1,
114     kMaxValue = kByJavaScript,
115   };
116 
117   // Please keep in sync with BackForwardCacheReloadsAndHistoryNavigations
118   // in tools/metrics/histograms/enums.xml. These values should not be
119   // renumbered.
120   enum class ReloadsAndHistoryNavigations {
121     kHistoryNavigation = 0,
122     kReloadAfterHistoryNavigation = 1,
123     kMaxValue = kReloadAfterHistoryNavigation,
124   };
125 
126   // Please keep in sync with BackForwardCacheReloadsAfterHistoryNavigation
127   // in tools/metrics/histograms/enums.xml. These values should not be
128   // renumbered.
129   enum class ReloadsAfterHistoryNavigation {
130     kNotServedFromBackForwardCache = 0,
131     kServedFromBackForwardCache = 1,
132     kMaxValue = kServedFromBackForwardCache,
133   };
134 
135   // Creates a potential new metrics object for the navigation.
136   // Note that this object will not be used if the entry we are navigating to
137   // already has the BackForwardCacheMetrics object (which happens for history
138   // navigations).
139   //
140   // |document_sequence_number| is the sequence number of the document
141   // associated with the document associated with the navigating frame and it is
142   // ignored if the navigating frame is not a main one.
143   static scoped_refptr<BackForwardCacheMetrics>
144   CreateOrReuseBackForwardCacheMetrics(
145       NavigationEntryImpl* currently_committed_entry,
146       bool is_main_frame_navigation,
147       int64_t document_sequence_number);
148 
149   // Records when the page is evicted after the document is restored e.g. when
150   // the race condition by JavaScript happens.
151   static void RecordEvictedAfterDocumentRestored(
152       EvictedAfterDocumentRestoredReason reason);
153 
154   // Notifies that the main frame has started a navigation to an entry
155   // associated with |this|.
156   //
157   // This is the point in time that a back-forward cache hit could be shown to
158   // the user.
159   //
160   // Note that in some cases (synchronous renderer-initiated navigations
161   // which create navigation entry only when committed) this call might
162   // be missing, but they should not matter for bfcache.
163   void MainFrameDidStartNavigationToDocument();
164 
165   // Notifies that an associated entry has committed a navigation.
166   // |back_forward_cache_allowed| indicates whether back-forward cache is
167   // allowed for the URL of |navigation_request|.
168   void DidCommitNavigation(NavigationRequest* navigation_request,
169                            bool back_forward_cache_allowed);
170 
171   // Records when another navigation commits away from the most recent entry
172   // associated with |this|.  This is the point in time that the previous
173   // document could enter the back-forward cache.
174   // |new_main_document| points to the newly committed RFH, which might or might
175   // not be the same as the RFH for the old document.
176   void MainFrameDidNavigateAwayFromDocument(
177       RenderFrameHostImpl* new_main_document,
178       LoadCommittedDetails* details,
179       NavigationRequest* navigation);
180 
181   // Snapshots the state of the features active on the page before closing it.
182   // It should be called at the same time when the document might have been
183   // placed in the back-forward cache.
184   void RecordFeatureUsage(RenderFrameHostImpl* main_frame);
185 
186   // Marks when the page is not cached, or evicted. This information is useful
187   // e.g., to prioritize the tasks to improve cache-hit rate.
188   void MarkNotRestoredWithReason(
189       const BackForwardCacheCanStoreDocumentResult& can_store);
190 
191   // Injects a clock for mocking time.
192   // Should be called only from the UI thread.
193   CONTENT_EXPORT static void OverrideTimeForTesting(base::TickClock* clock);
194 
195  private:
196   friend class base::RefCounted<BackForwardCacheMetrics>;
197 
198   explicit BackForwardCacheMetrics(int64_t document_sequence_number);
199 
200   ~BackForwardCacheMetrics();
201 
202   // Recursively collects the feature usage information from the subtree
203   // of a given frame.
204   void CollectFeatureUsageFromSubtree(RenderFrameHostImpl* rfh,
205                                       const url::Origin& main_frame_origin);
206 
207   // Dumps the current recorded information.
208   // |back_forward_cache_allowed| indicates whether back-forward cache is
209   // allowed for the URL of |navigation_request|.
210   void RecordMetricsForHistoryNavigationCommit(
211       NavigationRequest* navigation,
212       bool back_forward_cache_allowed) const;
213 
214   // Record metrics for the number of reloads after history navigation. In
215   // particular we are interested in number of reloads after a restore from
216   // the back-forward cache as a proxy for detecting whether the page was
217   // broken or not.
218   void RecordHistogramForReloadsAndHistoryNavigations(
219       bool is_reload,
220       bool back_forward_cache_allowed) const;
221 
222   // Record additional reason why navigation was not served from bfcache which
223   // are known only at the commit time.
224   void UpdateNotRestoredReasonsForNavigation(NavigationRequest* navigation);
225 
226   bool ShouldRecordBrowsingInstanceNotSwappedReason() const;
227 
228   void RecordHistoryNavigationUkm(NavigationRequest* navigation);
229 
230   // Main frame document sequence number that identifies all NavigationEntries
231   // this metrics object is associated with.
232   const int64_t document_sequence_number_;
233 
234   // NavigationHandle's ID for the last main frame navigation. This is updated
235   // for a main frame, not-same-document navigation.
236   //
237   // Should not be confused with NavigationEntryId.
238   int64_t last_committed_cross_document_main_frame_navigation_id_ = -1;
239 
240   int64_t last_committed_navigation_entry_id_ = -1;
241 
242   uint64_t main_frame_features_ = 0;
243   // We record metrics for same-origin frames and cross-origin frames
244   // differently as we might want to apply different policies for them,
245   // especially for the things around web platform compatibility (e.g. ignore
246   // unload handlers in cross-origin iframes but not in same-origin). The
247   // details are still subject to metrics, however. NOTE: This is not related to
248   // which process these frames are hosted in.
249   uint64_t same_origin_frames_features_ = 0;
250   uint64_t cross_origin_frames_features_ = 0;
251 
252   base::Optional<base::TimeTicks> started_navigation_timestamp_;
253   base::Optional<base::TimeTicks> navigated_away_from_main_document_timestamp_;
254 
255   NotRestoredReasons not_restored_reasons_;
256   uint64_t blocklisted_features_ = 0;
257   base::Optional<ShouldSwapBrowsingInstance>
258       browsing_instance_not_swapped_reason_;
259 
260   // The reasons given at BackForwardCache::DisableForRenderFrameHost. These are
261   // a further breakdown of NotRestoredReason::kDisableForRenderFrameHostCalled.
262   std::set<std::string> disabled_reasons_;
263 
264   // This value is updated only for navigations which are not same-document and
265   // main-frame navigations.
266   bool previous_navigation_is_history_ = false;
267   bool previous_navigation_is_served_from_bfcache_ = false;
268 
269   base::Optional<base::TimeTicks> renderer_killed_timestamp_;
270 
271   DISALLOW_COPY_AND_ASSIGN(BackForwardCacheMetrics);
272 };
273 
274 }  // namespace content
275 
276 #endif  // CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_METRICS_H_
277