Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: components/page_load_metrics/browser/metrics_web_contents_observer.cc

Issue 1476503004: [page_load_metrics] User Initiated Abort Tracking (Observer version) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@new_observer
Patch Set: rebase on #365097 Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" 5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
6 6
7 #include "base/location.h" 7 #include "base/location.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
10 #include "base/metrics/user_metrics.h" 10 #include "base/metrics/user_metrics.h"
11 #include "components/page_load_metrics/browser/page_load_metrics_util.h" 11 #include "components/page_load_metrics/browser/page_load_metrics_util.h"
12 #include "components/page_load_metrics/common/page_load_metrics_messages.h" 12 #include "components/page_load_metrics/common/page_load_metrics_messages.h"
13 #include "components/page_load_metrics/common/page_load_timing.h" 13 #include "components/page_load_metrics/common/page_load_timing.h"
14 #include "components/rappor/rappor_service.h" 14 #include "components/rappor/rappor_service.h"
15 #include "components/rappor/rappor_utils.h" 15 #include "components/rappor/rappor_utils.h"
16 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/navigation_details.h" 17 #include "content/public/browser/navigation_details.h"
18 #include "content/public/browser/navigation_handle.h" 18 #include "content/public/browser/navigation_handle.h"
19 #include "content/public/browser/render_frame_host.h" 19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/web_contents.h" 20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_observer.h" 21 #include "content/public/browser/web_contents_observer.h"
22 #include "content/public/browser/web_contents_user_data.h" 22 #include "content/public/browser/web_contents_user_data.h"
23 #include "ipc/ipc_message.h" 23 #include "ipc/ipc_message.h"
24 #include "ipc/ipc_message_macros.h" 24 #include "ipc/ipc_message_macros.h"
25 #include "ui/base/page_transition_types.h"
25 26
26 DEFINE_WEB_CONTENTS_USER_DATA_KEY( 27 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
27 page_load_metrics::MetricsWebContentsObserver); 28 page_load_metrics::MetricsWebContentsObserver);
28 29
29 namespace page_load_metrics { 30 namespace page_load_metrics {
30 31
31 namespace { 32 namespace {
32 33
33 // The url we see from the renderer side is not always the same as what 34 // The url we see from the renderer side is not always the same as what
34 // we see from the browser side (e.g. chrome://newtab). We want to be 35 // we see from the browser side (e.g. chrome://newtab). We want to be
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 return false; 75 return false;
75 } 76 }
76 77
77 return true; 78 return true;
78 } 79 }
79 80
80 void RecordInternalError(InternalErrorLoadEvent event) { 81 void RecordInternalError(InternalErrorLoadEvent event) {
81 UMA_HISTOGRAM_ENUMERATION(kErrorEvents, event, ERR_LAST_ENTRY); 82 UMA_HISTOGRAM_ENUMERATION(kErrorEvents, event, ERR_LAST_ENTRY);
82 } 83 }
83 84
85 UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) {
86 if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD))
87 return ABORT_RELOAD;
88 else if (transition & ui::PAGE_TRANSITION_FORWARD_BACK)
Alexei Svitkine (slow) 2015/12/14 22:14:40 Nit: No need for else if the above if already retu
Charlie Harrison 2015/12/14 23:16:30 Done.
89 return ABORT_FORWARD_BACK;
90 else if (ui::PageTransitionIsNewNavigation(transition))
91 return ABORT_NEW_NAVIGATION;
92 NOTREACHED();
93 return ABORT_OTHER;
94 }
95
84 // The number of buckets in the bitfield histogram. These buckets are described 96 // The number of buckets in the bitfield histogram. These buckets are described
85 // in rappor.xml in PageLoad.CoarseTiming.NavigationToFirstContentfulPaint. 97 // in rappor.xml in PageLoad.CoarseTiming.NavigationToFirstContentfulPaint.
86 // The bucket flag is defined by 1 << bucket_index, and is the bitfield 98 // The bucket flag is defined by 1 << bucket_index, and is the bitfield
87 // representing which timing bucket the page load falls into, i.e. 000010 99 // representing which timing bucket the page load falls into, i.e. 000010
88 // would be the bucket flag showing that the page took between 2 and 4 seconds 100 // would be the bucket flag showing that the page took between 2 and 4 seconds
89 // to load. 101 // to load.
90 const size_t kNumRapporHistogramBuckets = 6; 102 const size_t kNumRapporHistogramBuckets = 6;
91 103
92 uint64_t RapporHistogramBucketIndex(const base::TimeDelta& time) { 104 uint64_t RapporHistogramBucketIndex(const base::TimeDelta& time) {
93 int64 seconds = time.InSeconds(); 105 int64 seconds = time.InSeconds();
(...skipping 12 matching lines...) Expand all
106 118
107 } // namespace 119 } // namespace
108 120
109 PageLoadTracker::PageLoadTracker( 121 PageLoadTracker::PageLoadTracker(
110 bool in_foreground, 122 bool in_foreground,
111 PageLoadMetricsEmbedderInterface* embedder_interface, 123 PageLoadMetricsEmbedderInterface* embedder_interface,
112 content::NavigationHandle* navigation_handle) 124 content::NavigationHandle* navigation_handle)
113 : renderer_tracked_(false), 125 : renderer_tracked_(false),
114 has_commit_(false), 126 has_commit_(false),
115 navigation_start_(navigation_handle->NavigationStart()), 127 navigation_start_(navigation_handle->NavigationStart()),
128 abort_type_(ABORT_NONE),
116 started_in_foreground_(in_foreground), 129 started_in_foreground_(in_foreground),
117 embedder_interface_(embedder_interface) { 130 embedder_interface_(embedder_interface) {
118 embedder_interface_->RegisterObservers(this); 131 embedder_interface_->RegisterObservers(this);
119 for (const auto& observer : observers_) { 132 for (const auto& observer : observers_) {
120 observer->OnStart(navigation_handle); 133 observer->OnStart(navigation_handle);
121 } 134 }
122 } 135 }
123 136
124 PageLoadTracker::~PageLoadTracker() { 137 PageLoadTracker::~PageLoadTracker() {
125 if (has_commit_) {
126 RecordTimingHistograms();
127 RecordRappor();
128 }
129 PageLoadExtraInfo info = GetPageLoadMetricsInfo(); 138 PageLoadExtraInfo info = GetPageLoadMetricsInfo();
139 RecordRappor(info);
140 RecordTimingHistograms(info);
130 for (const auto& observer : observers_) { 141 for (const auto& observer : observers_) {
131 observer->OnComplete(timing_, info); 142 observer->OnComplete(timing_, info);
132 } 143 }
133 } 144 }
134 145
135 void PageLoadTracker::WebContentsHidden() { 146 void PageLoadTracker::WebContentsHidden() {
136 // Only log the first time we background in a given page load. 147 // Only log the first time we background in a given page load.
137 if (started_in_foreground_ && background_time_.is_null()) 148 if (started_in_foreground_ && background_time_.is_null())
138 background_time_ = base::TimeTicks::Now(); 149 background_time_ = base::TimeTicks::Now();
139 } 150 }
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 } 200 }
190 201
191 void PageLoadTracker::AddObserver( 202 void PageLoadTracker::AddObserver(
192 scoped_ptr<PageLoadMetricsObserver> observer) { 203 scoped_ptr<PageLoadMetricsObserver> observer) {
193 observers_.push_back(std::move(observer)); 204 observers_.push_back(std::move(observer));
194 } 205 }
195 206
196 PageLoadExtraInfo PageLoadTracker::GetPageLoadMetricsInfo() { 207 PageLoadExtraInfo PageLoadTracker::GetPageLoadMetricsInfo() {
197 base::TimeDelta first_background_time; 208 base::TimeDelta first_background_time;
198 base::TimeDelta first_foreground_time; 209 base::TimeDelta first_foreground_time;
210 base::TimeDelta time_to_abort;
199 if (!background_time_.is_null() && started_in_foreground_) 211 if (!background_time_.is_null() && started_in_foreground_)
200 first_background_time = background_time_ - navigation_start_; 212 first_background_time = background_time_ - navigation_start_;
201 if (!foreground_time_.is_null() && !started_in_foreground_) 213 if (!foreground_time_.is_null() && !started_in_foreground_)
202 first_foreground_time = foreground_time_ - navigation_start_; 214 first_foreground_time = foreground_time_ - navigation_start_;
215 if (abort_type_ != ABORT_NONE) {
216 DCHECK(!abort_time_.is_null());
217 time_to_abort = abort_time_ - navigation_start_;
218 }
203 return PageLoadExtraInfo(first_background_time, first_foreground_time, 219 return PageLoadExtraInfo(first_background_time, first_foreground_time,
204 started_in_foreground_, has_commit_); 220 started_in_foreground_, has_commit_, abort_type_,
221 time_to_abort);
205 } 222 }
206 223
207 const GURL& PageLoadTracker::committed_url() { 224 const GURL& PageLoadTracker::committed_url() {
208 DCHECK(has_commit_); 225 DCHECK(has_commit_);
209 return url_; 226 return url_;
210 } 227 }
211 228
212 // Blink calculates navigation start using TimeTicks, but converts to epoch time 229 void PageLoadTracker::NotifyAbort(UserAbortType abort_type,
213 // in its public API. Thus, to compare time values to navigation start, we 230 const base::TimeTicks& timestamp) {
214 // calculate the current time since the epoch using TimeTicks, and convert to 231 DCHECK_NE(abort_type, ABORT_NONE);
215 // Time. This method is similar to how blink converts TimeTicks to epoch time. 232 // Use UpdateAbort to update an already notified PageLoadTracker.
216 // There may be slight inaccuracies due to inter-process timestamps, but 233 if (abort_type_ != ABORT_NONE)
217 // this solution is the best we have right now. 234 return;
218 // 235
219 // returns a TimeDelta which is 236 abort_type_ = abort_type;
220 // - Infinity if we were never backgrounded 237 abort_time_ = timestamp;
221 // - null (TimeDelta()) if we started backgrounded
222 // - elapsed time to first background if we started in the foreground and
223 // backgrounded.
224 base::TimeDelta PageLoadTracker::GetBackgroundDelta() {
225 if (started_in_foreground_) {
226 return background_time_.is_null() ? base::TimeDelta::Max()
227 : background_time_ - navigation_start_;
228 }
229 return base::TimeDelta();
230 } 238 }
231 239
232 void PageLoadTracker::RecordTimingHistograms() { 240 void PageLoadTracker::UpdateAbort(UserAbortType abort_type,
233 DCHECK(has_commit_); 241 const base::TimeTicks& timestamp) {
234 if (!renderer_tracked()) 242 DCHECK_NE(abort_type, ABORT_NONE);
243 DCHECK_NE(abort_type, ABORT_OTHER);
244 DCHECK_EQ(abort_type_, ABORT_OTHER);
245
246 abort_type_ = abort_type;
247 // For some aborts (e.g. navigations), the initiated timestamp can be earlier
248 // than the timestamp that aborted the load. Taking the minimum gives the
249 // closest user initiated time known.
250 abort_time_ = std::min(abort_time_, timestamp);
251 }
252
253 void PageLoadTracker::RecordTimingHistograms(const PageLoadExtraInfo& info) {
254 if (!info.first_background_time.is_zero() &&
255 !EventOccurredInForeground(timing_.first_paint, info)) {
256 if (has_commit_) {
257 PAGE_LOAD_HISTOGRAM(kHistogramBackgroundBeforePaint,
258 info.first_background_time);
259 } else {
260 PAGE_LOAD_HISTOGRAM(kHistogramBackgroundBeforeCommit,
261 info.first_background_time);
262 }
263 }
264
265 // The rest of the histograms require the load to have commit and be relevant.
266 if (!has_commit_ || !renderer_tracked())
235 return; 267 return;
236 268
269 // The rest of the timing histograms require us to have received IPCs from the
270 // renderer. Record UMA for how often this occurs (usually for quickly aborted
271 // loads). For now, don't update observers if this is the case.
237 if (timing_.IsEmpty()) { 272 if (timing_.IsEmpty()) {
238 RecordInternalError(ERR_NO_IPCS_RECEIVED); 273 RecordInternalError(ERR_NO_IPCS_RECEIVED);
239 return; 274 return;
240 } 275 }
241 276
242 base::TimeDelta background_delta = GetBackgroundDelta();
243
244 if (!timing_.dom_content_loaded_event_start.is_zero()) { 277 if (!timing_.dom_content_loaded_event_start.is_zero()) {
245 if (timing_.dom_content_loaded_event_start < background_delta) { 278 if (EventOccurredInForeground(timing_.dom_content_loaded_event_start,
279 info)) {
246 PAGE_LOAD_HISTOGRAM(kHistogramDomContentLoaded, 280 PAGE_LOAD_HISTOGRAM(kHistogramDomContentLoaded,
247 timing_.dom_content_loaded_event_start); 281 timing_.dom_content_loaded_event_start);
248 } else { 282 } else {
249 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramDomContentLoaded, 283 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramDomContentLoaded,
250 timing_.dom_content_loaded_event_start); 284 timing_.dom_content_loaded_event_start);
251 } 285 }
252 } 286 }
253 if (!timing_.load_event_start.is_zero()) { 287 if (!timing_.load_event_start.is_zero()) {
254 if (timing_.load_event_start < background_delta) { 288 if (EventOccurredInForeground(timing_.load_event_start, info)) {
255 PAGE_LOAD_HISTOGRAM(kHistogramLoad, timing_.load_event_start); 289 PAGE_LOAD_HISTOGRAM(kHistogramLoad, timing_.load_event_start);
256 } else { 290 } else {
257 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramLoad, timing_.load_event_start); 291 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramLoad, timing_.load_event_start);
258 } 292 }
259 } 293 }
260 if (timing_.first_layout.is_zero()) { 294 if (timing_.first_layout.is_zero()) {
261 RecordCommittedEvent(RELEVANT_LOAD_FAILED_BEFORE_FIRST_LAYOUT, 295 RecordCommittedEvent(RELEVANT_LOAD_FAILED_BEFORE_FIRST_LAYOUT,
262 HasBackgrounded()); 296 HasBackgrounded());
263 } else { 297 } else {
264 if (timing_.first_layout < background_delta) { 298 if (EventOccurredInForeground(timing_.first_layout, info)) {
265 PAGE_LOAD_HISTOGRAM(kHistogramFirstLayout, timing_.first_layout); 299 PAGE_LOAD_HISTOGRAM(kHistogramFirstLayout, timing_.first_layout);
266 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, false); 300 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, false);
267 } else { 301 } else {
268 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstLayout, 302 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstLayout,
269 timing_.first_layout); 303 timing_.first_layout);
270 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, true); 304 RecordCommittedEvent(RELEVANT_LOAD_SUCCESSFUL_FIRST_LAYOUT, true);
271 } 305 }
272 } 306 }
273 if (!timing_.first_paint.is_zero()) { 307 if (!timing_.first_paint.is_zero()) {
274 if (timing_.first_paint < background_delta) { 308 if (EventOccurredInForeground(timing_.first_paint, info)) {
275 PAGE_LOAD_HISTOGRAM(kHistogramFirstPaint, timing_.first_paint); 309 PAGE_LOAD_HISTOGRAM(kHistogramFirstPaint, timing_.first_paint);
276 } else { 310 } else {
277 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstPaint, timing_.first_paint); 311 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstPaint, timing_.first_paint);
278 } 312 }
279 } 313 }
280 if (!timing_.first_text_paint.is_zero()) { 314 if (!timing_.first_text_paint.is_zero()) {
281 if (timing_.first_text_paint < background_delta) { 315 if (EventOccurredInForeground(timing_.first_text_paint, info)) {
282 PAGE_LOAD_HISTOGRAM(kHistogramFirstTextPaint, timing_.first_text_paint); 316 PAGE_LOAD_HISTOGRAM(kHistogramFirstTextPaint, timing_.first_text_paint);
283 } else { 317 } else {
284 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstTextPaint, 318 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstTextPaint,
285 timing_.first_text_paint); 319 timing_.first_text_paint);
286 } 320 }
287 } 321 }
288 if (!timing_.first_image_paint.is_zero()) { 322 if (!timing_.first_image_paint.is_zero()) {
289 if (timing_.first_image_paint < background_delta) { 323 if (EventOccurredInForeground(timing_.first_image_paint, info)) {
290 PAGE_LOAD_HISTOGRAM(kHistogramFirstImagePaint, timing_.first_image_paint); 324 PAGE_LOAD_HISTOGRAM(kHistogramFirstImagePaint, timing_.first_image_paint);
291 } else { 325 } else {
292 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstImagePaint, 326 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstImagePaint,
293 timing_.first_image_paint); 327 timing_.first_image_paint);
294 } 328 }
295 } 329 }
296 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_); 330 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_);
297 if (!first_contentful_paint.is_zero()) { 331 if (!first_contentful_paint.is_zero()) {
298 if (first_contentful_paint < background_delta) { 332 if (EventOccurredInForeground(first_contentful_paint, info)) {
299 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaint, 333 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaint,
300 first_contentful_paint); 334 first_contentful_paint);
301 // Bucket these histograms into high/low resolution clock systems. This 335 // Bucket these histograms into high/low resolution clock systems. This
302 // might point us to directions that will de-noise some UMA. 336 // might point us to directions that will de-noise some UMA.
303 if (base::TimeTicks::IsHighResolution()) { 337 if (base::TimeTicks::IsHighResolution()) {
304 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintHigh, 338 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintHigh,
305 first_contentful_paint); 339 first_contentful_paint);
306 } else { 340 } else {
307 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintLow, 341 PAGE_LOAD_HISTOGRAM(kHistogramFirstContentfulPaintLow,
308 first_contentful_paint); 342 first_contentful_paint);
309 } 343 }
310 } else { 344 } else {
311 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstContentfulPaint, 345 PAGE_LOAD_HISTOGRAM(kBackgroundHistogramFirstContentfulPaint,
312 first_contentful_paint); 346 first_contentful_paint);
313 } 347 }
314 } 348 }
315 349
316 // Log time to first foreground / time to first background. Log counts that we 350 // Log time to first foreground / time to first background. Log counts that we
317 // started a relevant page load in the foreground / background. 351 // started a relevant page load in the foreground / background.
318 if (!background_time_.is_null()) { 352 if (!info.first_background_time.is_zero())
319 PAGE_LOAD_HISTOGRAM(kHistogramFirstBackground, background_delta); 353 PAGE_LOAD_HISTOGRAM(kHistogramFirstBackground, info.first_background_time);
320 } else if (!foreground_time_.is_null()) { 354 else if (!info.first_foreground_time.is_zero())
321 PAGE_LOAD_HISTOGRAM(kHistogramFirstForeground, 355 PAGE_LOAD_HISTOGRAM(kHistogramFirstForeground, info.first_foreground_time);
322 foreground_time_ - navigation_start_);
323 }
324 } 356 }
325 357
326 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) { 358 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) {
327 if (HasBackgrounded()) { 359 if (HasBackgrounded()) {
328 UMA_HISTOGRAM_ENUMERATION(kBackgroundProvisionalEvents, event, 360 UMA_HISTOGRAM_ENUMERATION(kBackgroundProvisionalEvents, event,
329 PROVISIONAL_LOAD_LAST_ENTRY); 361 PROVISIONAL_LOAD_LAST_ENTRY);
330 } else { 362 } else {
331 UMA_HISTOGRAM_ENUMERATION(kProvisionalEvents, event, 363 UMA_HISTOGRAM_ENUMERATION(kProvisionalEvents, event,
332 PROVISIONAL_LOAD_LAST_ENTRY); 364 PROVISIONAL_LOAD_LAST_ENTRY);
333 } 365 }
334 } 366 }
335 367
336 // RecordCommittedEvent needs a backgrounded input because we need to special 368 // RecordCommittedEvent needs a backgrounded input because we need to special
337 // case a few events that need either precise timing measurements, or different 369 // case a few events that need either precise timing measurements, or different
338 // logic than simply "Did I background before logging this event?" 370 // logic than simply "Did I background before logging this event?"
339 void PageLoadTracker::RecordCommittedEvent(CommittedRelevantLoadEvent event, 371 void PageLoadTracker::RecordCommittedEvent(CommittedRelevantLoadEvent event,
340 bool backgrounded) { 372 bool backgrounded) {
341 if (backgrounded) { 373 if (backgrounded) {
342 UMA_HISTOGRAM_ENUMERATION(kBackgroundCommittedEvents, event, 374 UMA_HISTOGRAM_ENUMERATION(kBackgroundCommittedEvents, event,
343 RELEVANT_LOAD_LAST_ENTRY); 375 RELEVANT_LOAD_LAST_ENTRY);
344 } else { 376 } else {
345 UMA_HISTOGRAM_ENUMERATION(kCommittedEvents, event, 377 UMA_HISTOGRAM_ENUMERATION(kCommittedEvents, event,
346 RELEVANT_LOAD_LAST_ENTRY); 378 RELEVANT_LOAD_LAST_ENTRY);
347 } 379 }
348 } 380 }
349 381
350 void PageLoadTracker::RecordRappor() { 382 void PageLoadTracker::RecordRappor(const PageLoadExtraInfo& info) {
383 if (!info.has_commit)
384 return;
351 DCHECK(!committed_url().is_empty()); 385 DCHECK(!committed_url().is_empty());
352 rappor::RapporService* rappor_service = 386 rappor::RapporService* rappor_service =
353 embedder_interface_->GetRapporService(); 387 embedder_interface_->GetRapporService();
354 if (!rappor_service) 388 if (!rappor_service)
355 return; 389 return;
356 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_); 390 base::TimeDelta first_contentful_paint = GetFirstContentfulPaint(timing_);
357 // Log the eTLD+1 of sites that show poor loading performance. 391 // Log the eTLD+1 of sites that show poor loading performance.
358 if (!first_contentful_paint.is_zero() && 392 if (EventOccurredInForeground(first_contentful_paint, info)) {
359 first_contentful_paint < GetBackgroundDelta()) {
360 scoped_ptr<rappor::Sample> sample = 393 scoped_ptr<rappor::Sample> sample =
361 rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); 394 rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE);
362 sample->SetStringField( 395 sample->SetStringField(
363 "Domain", rappor::GetDomainAndRegistrySampleFromGURL(committed_url())); 396 "Domain", rappor::GetDomainAndRegistrySampleFromGURL(committed_url()));
364 uint64_t bucket_index = RapporHistogramBucketIndex(first_contentful_paint); 397 uint64_t bucket_index = RapporHistogramBucketIndex(first_contentful_paint);
365 sample->SetFlagsField("Bucket", uint64_t(1) << bucket_index, 398 sample->SetFlagsField("Bucket", uint64_t(1) << bucket_index,
366 kNumRapporHistogramBuckets); 399 kNumRapporHistogramBuckets);
367 // The IsSlow flag is just a one bit boolean if the first layout was > 10s. 400 // The IsSlow flag is just a one bit boolean if the first layout was > 10s.
368 sample->SetFlagsField("IsSlow", first_contentful_paint.InSecondsF() >= 10, 401 sample->SetFlagsField("IsSlow", first_contentful_paint.InSecondsF() >= 10,
369 1); 402 1);
(...skipping 17 matching lines...) Expand all
387 420
388 MetricsWebContentsObserver* metrics = FromWebContents(web_contents); 421 MetricsWebContentsObserver* metrics = FromWebContents(web_contents);
389 if (!metrics) { 422 if (!metrics) {
390 metrics = 423 metrics =
391 new MetricsWebContentsObserver(web_contents, embedder_interface.Pass()); 424 new MetricsWebContentsObserver(web_contents, embedder_interface.Pass());
392 web_contents->SetUserData(UserDataKey(), metrics); 425 web_contents->SetUserData(UserDataKey(), metrics);
393 } 426 }
394 return metrics; 427 return metrics;
395 } 428 }
396 429
397 MetricsWebContentsObserver::~MetricsWebContentsObserver() {} 430 MetricsWebContentsObserver::~MetricsWebContentsObserver() {
431 NotifyAbortAllLoads(ABORT_CLOSE);
432 }
398 433
399 bool MetricsWebContentsObserver::OnMessageReceived( 434 bool MetricsWebContentsObserver::OnMessageReceived(
400 const IPC::Message& message, 435 const IPC::Message& message,
401 content::RenderFrameHost* render_frame_host) { 436 content::RenderFrameHost* render_frame_host) {
402 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 437 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
403 bool handled = true; 438 bool handled = true;
404 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message, 439 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message,
405 render_frame_host) 440 render_frame_host)
406 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated) 441 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated)
407 IPC_MESSAGE_UNHANDLED(handled = false) 442 IPC_MESSAGE_UNHANDLED(handled = false)
(...skipping 30 matching lines...) Expand all
438 provisional_loads_.erase(navigation_handle); 473 provisional_loads_.erase(navigation_handle);
439 474
440 // There's a chance a navigation could have started before we were added to a 475 // There's a chance a navigation could have started before we were added to a
441 // tab. Bail out early if this is the case. 476 // tab. Bail out early if this is the case.
442 if (!finished_nav) 477 if (!finished_nav)
443 return; 478 return;
444 479
445 // Handle a pre-commit error here. Navigations that result in an error page 480 // Handle a pre-commit error here. Navigations that result in an error page
446 // will be ignored. Note that downloads/204s will result in HasCommitted() 481 // will be ignored. Note that downloads/204s will result in HasCommitted()
447 // returning false. 482 // returning false.
483 // TODO(csharrison): Track changes to NavigationHandle for signals when this
484 // is the case (HTTP response headers).
448 if (!navigation_handle->HasCommitted()) { 485 if (!navigation_handle->HasCommitted()) {
449 net::Error error = navigation_handle->GetNetErrorCode(); 486 net::Error error = navigation_handle->GetNetErrorCode();
450 finished_nav->RecordProvisionalEvent( 487 ProvisionalLoadEvent event = error == net::OK ? PROVISIONAL_LOAD_STOPPED
451 error == net::OK ? PROVISIONAL_LOAD_STOPPED
452 : error == net::ERR_ABORTED ? PROVISIONAL_LOAD_ERR_ABORTED 488 : error == net::ERR_ABORTED ? PROVISIONAL_LOAD_ERR_ABORTED
453 : PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT); 489 : PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT;
490 finished_nav->RecordProvisionalEvent(event);
491 if (event != PROVISIONAL_LOAD_ERR_FAILED_NON_ABORT) {
492 finished_nav->NotifyAbort(ABORT_OTHER, base::TimeTicks::Now());
493 aborted_provisional_loads_.push_back(std::move(finished_nav));
494 }
454 return; 495 return;
455 } 496 }
456 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_COMMITTED); 497 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_COMMITTED);
457 498
458 // Don't treat a same-page nav as a new page load. 499 // Don't treat a same-page nav as a new page load.
459 if (navigation_handle->IsSamePage()) 500 if (navigation_handle->IsSamePage())
460 return; 501 return;
461 502
503 // Notify other loads that they may have been aborted by this committed load.
504 // Note that by using the committed navigation start as the abort cause, we
505 // lose data on provisional loads that were aborted by other provisional
506 // loads. Those will either be listed as ABORT_OTHER or as being aborted by
507 // this load.
508 NotifyAbortAllLoadsWithTimestamp(
509 AbortTypeForPageTransition(navigation_handle->GetPageTransition()),
510 navigation_handle->NavigationStart());
511
462 committed_load_ = finished_nav.Pass(); 512 committed_load_ = finished_nav.Pass();
513 aborted_provisional_loads_.clear();
463 514
464 const GURL& browser_url = web_contents()->GetLastCommittedURL(); 515 const GURL& browser_url = web_contents()->GetLastCommittedURL();
465 const std::string& mime_type = web_contents()->GetContentsMimeType(); 516 const std::string& mime_type = web_contents()->GetContentsMimeType();
466 DCHECK(!browser_url.is_empty()); 517 DCHECK(!browser_url.is_empty());
467 DCHECK(!mime_type.empty()); 518 DCHECK(!mime_type.empty());
468 committed_load_->set_renderer_tracked( 519 committed_load_->set_renderer_tracked(
469 IsRelevantNavigation(navigation_handle, browser_url, mime_type)); 520 IsRelevantNavigation(navigation_handle, browser_url, mime_type));
470 521
471 committed_load_->Commit(navigation_handle); 522 committed_load_->Commit(navigation_handle);
472 } 523 }
473 524
525 void MetricsWebContentsObserver::NavigationStopped() {
526 NotifyAbortAllLoads(ABORT_STOP);
527 }
528
474 void MetricsWebContentsObserver::DidRedirectNavigation( 529 void MetricsWebContentsObserver::DidRedirectNavigation(
475 content::NavigationHandle* navigation_handle) { 530 content::NavigationHandle* navigation_handle) {
476 if (!navigation_handle->IsInMainFrame()) 531 if (!navigation_handle->IsInMainFrame())
477 return; 532 return;
478 auto it = provisional_loads_.find(navigation_handle); 533 auto it = provisional_loads_.find(navigation_handle);
479 if (it == provisional_loads_.end()) 534 if (it == provisional_loads_.end())
480 return; 535 return;
481 it->second->Redirect(navigation_handle); 536 it->second->Redirect(navigation_handle);
482 } 537 }
483 538
(...skipping 10 matching lines...) Expand all
494 in_foreground_ = false; 549 in_foreground_ = false;
495 if (committed_load_) 550 if (committed_load_)
496 committed_load_->WebContentsHidden(); 551 committed_load_->WebContentsHidden();
497 for (const auto& kv : provisional_loads_) { 552 for (const auto& kv : provisional_loads_) {
498 kv.second->WebContentsHidden(); 553 kv.second->WebContentsHidden();
499 } 554 }
500 } 555 }
501 556
502 // This will occur when the process for the main RenderFrameHost exits, either 557 // This will occur when the process for the main RenderFrameHost exits, either
503 // normally or from a crash. We eagerly log data from the last committed load if 558 // normally or from a crash. We eagerly log data from the last committed load if
504 // we have one. 559 // we have one. Don't notify aborts here because this is probably not user
560 // initiated. If it is (e.g. browser shutdown), other code paths will take care
561 // of notifying.
505 void MetricsWebContentsObserver::RenderProcessGone( 562 void MetricsWebContentsObserver::RenderProcessGone(
506 base::TerminationStatus status) { 563 base::TerminationStatus status) {
564 // Other code paths will be run for normal renderer shutdown.
565 if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
566 status == base::TERMINATION_STATUS_STILL_RUNNING) {
567 DCHECK_NE(status, base::TERMINATION_STATUS_STILL_RUNNING);
568 return;
569 }
570
571 // If this is a crash, eagerly log the aborted provisional loads and the
572 // committed load. |provisional_loads_| don't need to be destroyed here
573 // because their lifetime is tied to the NavigationHandle.
507 committed_load_.reset(); 574 committed_load_.reset();
575 aborted_provisional_loads_.clear();
576 }
577
578 void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type) {
579 NotifyAbortAllLoadsWithTimestamp(abort_type, base::TimeTicks::Now());
580 }
581
582 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp(
583 UserAbortType abort_type,
584 const base::TimeTicks& timestamp) {
585 if (committed_load_)
586 committed_load_->NotifyAbort(abort_type, timestamp);
587 for (const auto& kv : provisional_loads_) {
588 kv.second->NotifyAbort(abort_type, timestamp);
589 }
590 // If a better signal than ABORT_OTHER came in the last 100ms, treat it as the
591 // cause of the abort (some ABORT_OTHER signals occur before the true signal).
592 // Note that |abort_type| can only be ABORT_OTHER for provisional loads. While
593 // this heuristic is coarse, it works better and is simpler than other
594 // feasible methods. See https://goo.gl/WKRG98.
595 for (const auto& tracker : aborted_provisional_loads_) {
596 // Note that |timestamp - abort_time| can be negative.
597 if (tracker->abort_type() == ABORT_OTHER &&
598 (timestamp - tracker->abort_time()).InMilliseconds() < 100) {
599 tracker->UpdateAbort(abort_type, timestamp);
600 }
601 }
508 } 602 }
509 603
510 void MetricsWebContentsObserver::OnTimingUpdated( 604 void MetricsWebContentsObserver::OnTimingUpdated(
511 content::RenderFrameHost* render_frame_host, 605 content::RenderFrameHost* render_frame_host,
512 const PageLoadTiming& timing) { 606 const PageLoadTiming& timing) {
513 bool error = false; 607 bool error = false;
514 if (!committed_load_ || !committed_load_->renderer_tracked()) { 608 if (!committed_load_ || !committed_load_->renderer_tracked()) {
515 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD); 609 RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD);
516 error = true; 610 error = true;
517 } 611 }
(...skipping 18 matching lines...) Expand all
536 630
537 if (!committed_load_->UpdateTiming(timing)) { 631 if (!committed_load_->UpdateTiming(timing)) {
538 // If the page load tracker cannot update its timing, something is wrong 632 // If the page load tracker cannot update its timing, something is wrong
539 // with the IPC (it's from another load, or it's invalid in some other way). 633 // with the IPC (it's from another load, or it's invalid in some other way).
540 // We expect this to be a rare occurrence. 634 // We expect this to be a rare occurrence.
541 RecordInternalError(ERR_BAD_TIMING_IPC); 635 RecordInternalError(ERR_BAD_TIMING_IPC);
542 } 636 }
543 } 637 }
544 638
545 } // namespace page_load_metrics 639 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698