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

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

Powered by Google App Engine
This is Rietveld 408576698