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 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
6 
7 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
8 
9 using page_load_metrics::PageAbortReason;
10 
11 namespace internal {
12 
13 const char kHistogramAbortForwardBackBeforeCommit[] =
14     "PageLoad.Experimental.AbortTiming.ForwardBackNavigation.BeforeCommit";
15 const char kHistogramAbortReloadBeforeCommit[] =
16     "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit";
17 const char kHistogramAbortNewNavigationBeforeCommit[] =
18     "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit";
19 const char kHistogramAbortStopBeforeCommit[] =
20     "PageLoad.Experimental.AbortTiming.Stop.BeforeCommit";
21 const char kHistogramAbortCloseBeforeCommit[] =
22     "PageLoad.Experimental.AbortTiming.Close.BeforeCommit";
23 const char kHistogramAbortBackgroundBeforeCommit[] =
24     "PageLoad.Experimental.AbortTiming.Background.BeforeCommit";
25 const char kHistogramAbortOtherBeforeCommit[] =
26     "PageLoad.Experimental.AbortTiming.Other.BeforeCommit";
27 
28 const char kHistogramAbortForwardBackBeforePaint[] =
29     "PageLoad.Experimental.AbortTiming.ForwardBackNavigation.AfterCommit."
30     "BeforePaint";
31 const char kHistogramAbortReloadBeforePaint[] =
32     "PageLoad.Experimental.AbortTiming.Reload.AfterCommit.BeforePaint";
33 const char kHistogramAbortNewNavigationBeforePaint[] =
34     "PageLoad.Experimental.AbortTiming.NewNavigation.AfterCommit.BeforePaint";
35 const char kHistogramAbortStopBeforePaint[] =
36     "PageLoad.Experimental.AbortTiming.Stop.AfterCommit.BeforePaint";
37 const char kHistogramAbortCloseBeforePaint[] =
38     "PageLoad.Experimental.AbortTiming.Close.AfterCommit.BeforePaint";
39 const char kHistogramAbortBackgroundBeforePaint[] =
40     "PageLoad.Experimental.AbortTiming.Background.AfterCommit.BeforePaint";
41 
42 const char kHistogramAbortForwardBackDuringParse[] =
43     "PageLoad.Experimental.AbortTiming.ForwardBackNavigation.DuringParse";
44 const char kHistogramAbortReloadDuringParse[] =
45     "PageLoad.Experimental.AbortTiming.Reload.DuringParse";
46 const char kHistogramAbortNewNavigationDuringParse[] =
47     "PageLoad.Experimental.AbortTiming.NewNavigation.DuringParse";
48 const char kHistogramAbortStopDuringParse[] =
49     "PageLoad.Experimental.AbortTiming.Stop.DuringParse";
50 const char kHistogramAbortCloseDuringParse[] =
51     "PageLoad.Experimental.AbortTiming.Close.DuringParse";
52 const char kHistogramAbortBackgroundDuringParse[] =
53     "PageLoad.Experimental.AbortTiming.Background.DuringParse";
54 
55 }  // namespace internal
56 
57 namespace {
58 
RecordAbortBeforeCommit(const page_load_metrics::PageAbortInfo & abort_info)59 void RecordAbortBeforeCommit(
60     const page_load_metrics::PageAbortInfo& abort_info) {
61   switch (abort_info.reason) {
62     case PageAbortReason::ABORT_RELOAD:
63       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortReloadBeforeCommit,
64                           abort_info.time_to_abort);
65       if (abort_info.user_initiated_info.user_gesture) {
66         PAGE_LOAD_HISTOGRAM(
67             "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit."
68             "UserGesture",
69             abort_info.time_to_abort);
70       }
71       if (abort_info.user_initiated_info.browser_initiated) {
72         PAGE_LOAD_HISTOGRAM(
73             "PageLoad.Experimental.AbortTiming.Reload.BeforeCommit."
74             "BrowserInitiated",
75             abort_info.time_to_abort);
76       }
77       return;
78     case PageAbortReason::ABORT_FORWARD_BACK:
79       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortForwardBackBeforeCommit,
80                           abort_info.time_to_abort);
81       if (abort_info.user_initiated_info.user_gesture) {
82         PAGE_LOAD_HISTOGRAM(
83             "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
84             "BeforeCommit.UserGesture",
85             abort_info.time_to_abort);
86       }
87       if (abort_info.user_initiated_info.browser_initiated) {
88         PAGE_LOAD_HISTOGRAM(
89             "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
90             "BeforeCommit.BrowserInitiated",
91             abort_info.time_to_abort);
92       }
93       return;
94     case PageAbortReason::ABORT_NEW_NAVIGATION:
95       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortNewNavigationBeforeCommit,
96                           abort_info.time_to_abort);
97       if (abort_info.user_initiated_info.user_gesture) {
98         PAGE_LOAD_HISTOGRAM(
99             "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit."
100             "UserGesture",
101             abort_info.time_to_abort);
102       }
103       if (abort_info.user_initiated_info.browser_initiated) {
104         PAGE_LOAD_HISTOGRAM(
105             "PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit."
106             "BrowserInitiated",
107             abort_info.time_to_abort);
108       }
109       return;
110     case PageAbortReason::ABORT_STOP:
111       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortStopBeforeCommit,
112                           abort_info.time_to_abort);
113       return;
114     case PageAbortReason::ABORT_CLOSE:
115       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortCloseBeforeCommit,
116                           abort_info.time_to_abort);
117       return;
118     case PageAbortReason::ABORT_BACKGROUND:
119       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortBackgroundBeforeCommit,
120                           abort_info.time_to_abort);
121       return;
122     case PageAbortReason::ABORT_OTHER:
123       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortOtherBeforeCommit,
124                           abort_info.time_to_abort);
125       return;
126     case PageAbortReason::ABORT_NONE:
127       NOTREACHED();
128       return;
129   }
130   NOTREACHED();
131 }
132 
RecordAbortAfterCommitBeforePaint(const page_load_metrics::PageAbortInfo & abort_info)133 void RecordAbortAfterCommitBeforePaint(
134     const page_load_metrics::PageAbortInfo& abort_info) {
135   switch (abort_info.reason) {
136     case PageAbortReason::ABORT_RELOAD:
137       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortReloadBeforePaint,
138                           abort_info.time_to_abort);
139       if (abort_info.user_initiated_info.user_gesture) {
140         PAGE_LOAD_HISTOGRAM(
141             "PageLoad.Experimental.AbortTiming.Reload.AfterCommit.BeforePaint."
142             "UserGesture",
143             abort_info.time_to_abort);
144       }
145       if (abort_info.user_initiated_info.browser_initiated) {
146         PAGE_LOAD_HISTOGRAM(
147             "PageLoad.Experimental.AbortTiming.Reload.AfterCommit.BeforePaint."
148             "BrowserInitiated",
149             abort_info.time_to_abort);
150       }
151       return;
152     case PageAbortReason::ABORT_FORWARD_BACK:
153       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortForwardBackBeforePaint,
154                           abort_info.time_to_abort);
155       if (abort_info.user_initiated_info.user_gesture) {
156         PAGE_LOAD_HISTOGRAM(
157             "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
158             "AfterCommit.BeforePaint.UserGesture",
159             abort_info.time_to_abort);
160       }
161       if (abort_info.user_initiated_info.browser_initiated) {
162         PAGE_LOAD_HISTOGRAM(
163             "PageLoad.Experimental.AbortTiming.ForwardBackNavigation."
164             "AfterCommit.BeforePaint.BrowserInitiated",
165             abort_info.time_to_abort);
166       }
167       return;
168     case PageAbortReason::ABORT_NEW_NAVIGATION:
169       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortNewNavigationBeforePaint,
170                           abort_info.time_to_abort);
171       if (abort_info.user_initiated_info.user_gesture) {
172         PAGE_LOAD_HISTOGRAM(
173             "PageLoad.Experimental.AbortTiming.NewNavigation.AfterCommit."
174             "BeforePaint.UserGesture",
175             abort_info.time_to_abort);
176       }
177       if (abort_info.user_initiated_info.browser_initiated) {
178         PAGE_LOAD_HISTOGRAM(
179             "PageLoad.Experimental.AbortTiming.NewNavigation.AfterCommit."
180             "BeforePaint.BrowserInitiated",
181             abort_info.time_to_abort);
182       }
183       return;
184     case PageAbortReason::ABORT_STOP:
185       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortStopBeforePaint,
186                           abort_info.time_to_abort);
187       return;
188     case PageAbortReason::ABORT_CLOSE:
189       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortCloseBeforePaint,
190                           abort_info.time_to_abort);
191       return;
192     case PageAbortReason::ABORT_BACKGROUND:
193       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortBackgroundBeforePaint,
194                           abort_info.time_to_abort);
195       return;
196     case PageAbortReason::ABORT_OTHER:
197       // This is technically possible, though rare. See ~PageLoadTracker for an
198       // explanation.
199       return;
200     case PageAbortReason::ABORT_NONE:
201       NOTREACHED();
202       return;
203   }
204   NOTREACHED();
205 }
206 
RecordAbortDuringParse(const page_load_metrics::PageAbortInfo & abort_info)207 void RecordAbortDuringParse(
208     const page_load_metrics::PageAbortInfo& abort_info) {
209   switch (abort_info.reason) {
210     case PageAbortReason::ABORT_RELOAD:
211       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortReloadDuringParse,
212                           abort_info.time_to_abort);
213       return;
214     case PageAbortReason::ABORT_FORWARD_BACK:
215       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortForwardBackDuringParse,
216                           abort_info.time_to_abort);
217       return;
218     case PageAbortReason::ABORT_NEW_NAVIGATION:
219       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortNewNavigationDuringParse,
220                           abort_info.time_to_abort);
221       return;
222     case PageAbortReason::ABORT_STOP:
223       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortStopDuringParse,
224                           abort_info.time_to_abort);
225       return;
226     case PageAbortReason::ABORT_CLOSE:
227       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortCloseDuringParse,
228                           abort_info.time_to_abort);
229       return;
230     case PageAbortReason::ABORT_BACKGROUND:
231       PAGE_LOAD_HISTOGRAM(internal::kHistogramAbortBackgroundDuringParse,
232                           abort_info.time_to_abort);
233       return;
234     case PageAbortReason::ABORT_OTHER:
235       // This is technically possible, though rare. See ~PageLoadTracker for an
236       // explanation.
237       return;
238     case PageAbortReason::ABORT_NONE:
239       NOTREACHED();
240       return;
241   }
242   NOTREACHED();
243 }
244 
ShouldTrackMetrics(const page_load_metrics::PageLoadMetricsObserverDelegate & delegate,const page_load_metrics::PageAbortInfo & abort_info)245 bool ShouldTrackMetrics(
246     const page_load_metrics::PageLoadMetricsObserverDelegate& delegate,
247     const page_load_metrics::PageAbortInfo& abort_info) {
248   if (abort_info.reason == PageAbortReason::ABORT_NONE)
249     return false;
250 
251   // Don't log abort times if the page was backgrounded before the abort event.
252   if (!page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
253           abort_info.time_to_abort, delegate))
254     return false;
255 
256   return true;
257 }
258 
259 }  // namespace
260 
AbortsPageLoadMetricsObserver()261 AbortsPageLoadMetricsObserver::AbortsPageLoadMetricsObserver() {}
262 
OnComplete(const page_load_metrics::mojom::PageLoadTiming & timing)263 void AbortsPageLoadMetricsObserver::OnComplete(
264     const page_load_metrics::mojom::PageLoadTiming& timing) {
265   page_load_metrics::PageAbortInfo abort_info = GetPageAbortInfo(GetDelegate());
266   if (!ShouldTrackMetrics(GetDelegate(), abort_info))
267     return;
268 
269   // If we did not receive any timing IPCs from the render process, we can't
270   // know for certain if the page was truly aborted before paint, or if the
271   // abort happened before we received the IPC from the render process. Thus, we
272   // do not log aborts for these page loads. Tracked page loads that receive no
273   // timing IPCs are tracked via the ERR_NO_IPCS_RECEIVED error code in the
274   // PageLoad.Events.InternalError histogram, so we can keep track of how often
275   // this happens.
276   if (page_load_metrics::IsEmpty(timing))
277     return;
278 
279   if (timing.parse_timing->parse_start &&
280       abort_info.time_to_abort >= timing.parse_timing->parse_start &&
281       (!timing.parse_timing->parse_stop ||
282        timing.parse_timing->parse_stop >= abort_info.time_to_abort)) {
283     RecordAbortDuringParse(abort_info);
284   }
285   if (!timing.paint_timing->first_paint ||
286       timing.paint_timing->first_paint >= abort_info.time_to_abort) {
287     RecordAbortAfterCommitBeforePaint(abort_info);
288   }
289 }
290 
OnFailedProvisionalLoad(const page_load_metrics::FailedProvisionalLoadInfo & failed_load_info)291 void AbortsPageLoadMetricsObserver::OnFailedProvisionalLoad(
292     const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info) {
293   page_load_metrics::PageAbortInfo abort_info = GetPageAbortInfo(GetDelegate());
294   if (!ShouldTrackMetrics(GetDelegate(), abort_info))
295     return;
296 
297   RecordAbortBeforeCommit(abort_info);
298 }
299