OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "chrome/browser/page_load_metrics/page_load_tracker.h" | 5 #include "chrome/browser/page_load_metrics/page_load_tracker.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <ostream> | 8 #include <ostream> |
9 #include <string> | 9 #include <string> |
10 #include <utility> | 10 #include <utility> |
11 | 11 |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 15 #include "chrome/browser/page_load_metrics/browser_page_track_decider.h" |
15 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface.
h" | 16 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface.
h" |
16 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h" | 17 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h" |
17 #include "chrome/browser/prerender/prerender_contents.h" | 18 #include "chrome/browser/prerender/prerender_contents.h" |
18 #include "chrome/common/page_load_metrics/page_load_timing.h" | 19 #include "chrome/common/page_load_metrics/page_load_timing.h" |
19 #include "content/public/browser/navigation_details.h" | 20 #include "content/public/browser/navigation_details.h" |
20 #include "content/public/browser/navigation_handle.h" | 21 #include "content/public/browser/navigation_handle.h" |
| 22 #include "content/public/browser/render_frame_host.h" |
21 #include "content/public/browser/web_contents.h" | 23 #include "content/public/browser/web_contents.h" |
22 #include "content/public/browser/web_contents_observer.h" | 24 #include "content/public/browser/web_contents_observer.h" |
23 #include "content/public/common/browser_side_navigation_policy.h" | 25 #include "content/public/common/browser_side_navigation_policy.h" |
24 #include "ui/base/page_transition_types.h" | 26 #include "ui/base/page_transition_types.h" |
25 | 27 |
26 // This macro invokes the specified method on each observer, passing the | 28 // This macro invokes the specified method on each observer, passing the |
27 // variable length arguments as the method's arguments, and removes the observer | 29 // variable length arguments as the method's arguments, and removes the observer |
28 // from the list of observers if the given method returns STOP_OBSERVING. | 30 // from the list of observers if the given method returns STOP_OBSERVING. |
29 #define INVOKE_AND_PRUNE_OBSERVERS(observers, Method, ...) \ | 31 #define INVOKE_AND_PRUNE_OBSERVERS(observers, Method, ...) \ |
30 for (auto it = observers.begin(); it != observers.end();) { \ | 32 for (auto it = observers.begin(); it != observers.end();) { \ |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
270 timing.paint_timing.first_meaningful_paint)) { | 272 timing.paint_timing.first_meaningful_paint)) { |
271 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint | 273 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint |
272 << " for first_meaningful_paint " | 274 << " for first_meaningful_paint " |
273 << timing.paint_timing.first_meaningful_paint; | 275 << timing.paint_timing.first_meaningful_paint; |
274 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_MEANINGFUL_PAINT; | 276 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_MEANINGFUL_PAINT; |
275 } | 277 } |
276 | 278 |
277 return internal::VALID; | 279 return internal::VALID; |
278 } | 280 } |
279 | 281 |
| 282 // Updates *|inout_existing_value| with |optional_candidate_new_value|, if |
| 283 // either *|inout_existing_value| isn't set, or |optional_candidate_new_value| < |
| 284 // |inout_existing_value|. |
| 285 void MaybeUpdateTimeDelta( |
| 286 base::Optional<base::TimeDelta>* inout_existing_value, |
| 287 base::TimeDelta navigation_start_offset, |
| 288 const base::Optional<base::TimeDelta>& optional_candidate_new_value) { |
| 289 // If we don't get a new value, there's nothing to do |
| 290 if (!optional_candidate_new_value) |
| 291 return; |
| 292 |
| 293 // optional_candidate_new_value is relative to navigation start in its |
| 294 // frame. We need to adjust it to be relative to navigation start in the main |
| 295 // frame, so offset it by navigation_start_offset. |
| 296 base::TimeDelta candidate_new_value = |
| 297 navigation_start_offset + optional_candidate_new_value.value(); |
| 298 |
| 299 if (*inout_existing_value) { |
| 300 // If we have a new value, but it is after the existing value, then keep the |
| 301 // existing value. |
| 302 if (*inout_existing_value <= candidate_new_value) |
| 303 return; |
| 304 |
| 305 // We received a new timing event, but with a timestamp before the timestamp |
| 306 // of a timing update we had received previously. We expect this to happen |
| 307 // occasionally, as inter-frame updates can arrive out of order. Record a |
| 308 // histogram to track how frequently it happens, along with the magnitude |
| 309 // of the delta. |
| 310 PAGE_LOAD_HISTOGRAM("PageLoad.Internal.OutOfOrderInterFrameTiming", |
| 311 inout_existing_value->value() - candidate_new_value); |
| 312 } |
| 313 |
| 314 *inout_existing_value = candidate_new_value; |
| 315 } |
| 316 |
280 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { | 317 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { |
281 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, | 318 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, |
282 completed_after_background); | 319 completed_after_background); |
283 } | 320 } |
284 | 321 |
285 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, | 322 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, |
286 const PageLoadTiming& last_timing, | 323 const PageLoadTiming& last_timing, |
287 const PageLoadTiming& new_timing, | 324 const PageLoadTiming& new_timing, |
288 const PageLoadMetadata& last_metadata, | 325 const PageLoadMetadata& last_metadata, |
289 const PageLoadExtraInfo& extra_info) { | 326 const PageLoadExtraInfo& extra_info) { |
290 if (extra_info.main_frame_metadata.behavior_flags != | 327 if (extra_info.main_frame_metadata.behavior_flags != |
291 last_metadata.behavior_flags) | 328 last_metadata.behavior_flags) |
292 observer->OnLoadingBehaviorObserved(extra_info); | 329 observer->OnLoadingBehaviorObserved(extra_info); |
293 if (last_timing != new_timing) | 330 if (last_timing != new_timing) |
294 observer->OnTimingUpdate(new_timing, extra_info); | 331 observer->OnTimingUpdate(new_timing, extra_info); |
295 if (new_timing.document_timing.dom_content_loaded_event_start && | 332 if (new_timing.document_timing.dom_content_loaded_event_start && |
296 !last_timing.document_timing.dom_content_loaded_event_start) | 333 !last_timing.document_timing.dom_content_loaded_event_start) |
297 observer->OnDomContentLoadedEventStart(new_timing, extra_info); | 334 observer->OnDomContentLoadedEventStart(new_timing, extra_info); |
298 if (new_timing.document_timing.load_event_start && | 335 if (new_timing.document_timing.load_event_start && |
299 !last_timing.document_timing.load_event_start) | 336 !last_timing.document_timing.load_event_start) |
300 observer->OnLoadEventStart(new_timing, extra_info); | 337 observer->OnLoadEventStart(new_timing, extra_info); |
301 if (new_timing.document_timing.first_layout && | 338 if (new_timing.document_timing.first_layout && |
302 !last_timing.document_timing.first_layout) | 339 !last_timing.document_timing.first_layout) |
303 observer->OnFirstLayout(new_timing, extra_info); | 340 observer->OnFirstLayout(new_timing, extra_info); |
304 if (new_timing.paint_timing.first_paint && | 341 if (new_timing.paint_timing.first_paint && |
305 !last_timing.paint_timing.first_paint) | 342 !last_timing.paint_timing.first_paint) |
306 observer->OnFirstPaint(new_timing, extra_info); | 343 observer->OnFirstPaintInPage(new_timing, extra_info); |
307 if (new_timing.paint_timing.first_text_paint && | 344 if (new_timing.paint_timing.first_text_paint && |
308 !last_timing.paint_timing.first_text_paint) | 345 !last_timing.paint_timing.first_text_paint) |
309 observer->OnFirstTextPaint(new_timing, extra_info); | 346 observer->OnFirstTextPaintInPage(new_timing, extra_info); |
310 if (new_timing.paint_timing.first_image_paint && | 347 if (new_timing.paint_timing.first_image_paint && |
311 !last_timing.paint_timing.first_image_paint) | 348 !last_timing.paint_timing.first_image_paint) |
312 observer->OnFirstImagePaint(new_timing, extra_info); | 349 observer->OnFirstImagePaintInPage(new_timing, extra_info); |
313 if (new_timing.paint_timing.first_contentful_paint && | 350 if (new_timing.paint_timing.first_contentful_paint && |
314 !last_timing.paint_timing.first_contentful_paint) | 351 !last_timing.paint_timing.first_contentful_paint) |
315 observer->OnFirstContentfulPaint(new_timing, extra_info); | 352 observer->OnFirstContentfulPaintInPage(new_timing, extra_info); |
316 if (new_timing.paint_timing.first_meaningful_paint && | 353 if (new_timing.paint_timing.first_meaningful_paint && |
317 !last_timing.paint_timing.first_meaningful_paint) | 354 !last_timing.paint_timing.first_meaningful_paint) |
318 observer->OnFirstMeaningfulPaint(new_timing, extra_info); | 355 observer->OnFirstMeaningfulPaintInMainFrameDocument(new_timing, extra_info); |
319 if (new_timing.parse_timing.parse_start && | 356 if (new_timing.parse_timing.parse_start && |
320 !last_timing.parse_timing.parse_start) | 357 !last_timing.parse_timing.parse_start) |
321 observer->OnParseStart(new_timing, extra_info); | 358 observer->OnParseStart(new_timing, extra_info); |
322 if (new_timing.parse_timing.parse_stop && | 359 if (new_timing.parse_timing.parse_stop && |
323 !last_timing.parse_timing.parse_stop) | 360 !last_timing.parse_timing.parse_stop) |
324 observer->OnParseStop(new_timing, extra_info); | 361 observer->OnParseStop(new_timing, extra_info); |
325 } | 362 } |
326 | 363 |
327 } // namespace | 364 } // namespace |
328 | 365 |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 if (!failed_provisional_load_info_) | 420 if (!failed_provisional_load_info_) |
384 RecordInternalError(ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD); | 421 RecordInternalError(ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD); |
385 | 422 |
386 // Don't include any aborts that resulted in a new navigation, as the chain | 423 // Don't include any aborts that resulted in a new navigation, as the chain |
387 // length will be included in the aborter PageLoadTracker. | 424 // length will be included in the aborter PageLoadTracker. |
388 if (page_end_reason_ != END_RELOAD && | 425 if (page_end_reason_ != END_RELOAD && |
389 page_end_reason_ != END_FORWARD_BACK && | 426 page_end_reason_ != END_FORWARD_BACK && |
390 page_end_reason_ != END_NEW_NAVIGATION) { | 427 page_end_reason_ != END_NEW_NAVIGATION) { |
391 LogAbortChainHistograms(nullptr); | 428 LogAbortChainHistograms(nullptr); |
392 } | 429 } |
393 } else if (timing_.IsEmpty()) { | 430 } else if (merged_page_timing_.IsEmpty()) { |
394 RecordInternalError(ERR_NO_IPCS_RECEIVED); | 431 RecordInternalError(ERR_NO_IPCS_RECEIVED); |
395 } | 432 } |
396 | 433 |
397 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 434 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
398 for (const auto& observer : observers_) { | 435 for (const auto& observer : observers_) { |
399 if (failed_provisional_load_info_) { | 436 if (failed_provisional_load_info_) { |
400 observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info); | 437 observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info); |
401 } else if (did_commit_) { | 438 } else if (did_commit_) { |
402 observer->OnComplete(timing_, info); | 439 observer->OnComplete(merged_page_timing_, info); |
403 } | 440 } |
404 } | 441 } |
405 } | 442 } |
406 | 443 |
407 void PageLoadTracker::LogAbortChainHistograms( | 444 void PageLoadTracker::LogAbortChainHistograms( |
408 content::NavigationHandle* final_navigation) { | 445 content::NavigationHandle* final_navigation) { |
409 if (aborted_chain_size_ == 0) | 446 if (aborted_chain_size_ == 0) |
410 return; | 447 return; |
411 // Note that this could be broken out by this navigation's abort type, if more | 448 // Note that this could be broken out by this navigation's abort type, if more |
412 // granularity is needed. Add one to the chain size to count the current | 449 // granularity is needed. Add one to the chain size to count the current |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 // Only log the first time we background in a given page load. | 496 // Only log the first time we background in a given page load. |
460 if (background_time_.is_null()) { | 497 if (background_time_.is_null()) { |
461 // Make sure we either started in the foreground and haven't been | 498 // Make sure we either started in the foreground and haven't been |
462 // foregrounded yet, or started in the background and have already been | 499 // foregrounded yet, or started in the background and have already been |
463 // foregrounded. | 500 // foregrounded. |
464 DCHECK_EQ(started_in_foreground_, foreground_time_.is_null()); | 501 DCHECK_EQ(started_in_foreground_, foreground_time_.is_null()); |
465 background_time_ = base::TimeTicks::Now(); | 502 background_time_ = base::TimeTicks::Now(); |
466 ClampBrowserTimestampIfInterProcessTimeTickSkew(&background_time_); | 503 ClampBrowserTimestampIfInterProcessTimeTickSkew(&background_time_); |
467 } | 504 } |
468 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 505 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
469 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden, timing_, info); | 506 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden, merged_page_timing_, info); |
470 } | 507 } |
471 | 508 |
472 void PageLoadTracker::WebContentsShown() { | 509 void PageLoadTracker::WebContentsShown() { |
473 // Only log the first time we foreground in a given page load. | 510 // Only log the first time we foreground in a given page load. |
474 if (foreground_time_.is_null()) { | 511 if (foreground_time_.is_null()) { |
475 // Make sure we either started in the background and haven't been | 512 // Make sure we either started in the background and haven't been |
476 // backgrounded yet, or started in the foreground and have already been | 513 // backgrounded yet, or started in the foreground and have already been |
477 // backgrounded. | 514 // backgrounded. |
478 DCHECK_NE(started_in_foreground_, background_time_.is_null()); | 515 DCHECK_NE(started_in_foreground_, background_time_.is_null()); |
479 foreground_time_ = base::TimeTicks::Now(); | 516 foreground_time_ = base::TimeTicks::Now(); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 navigation_handle->GetWebContents()->GetContentsMimeType()); | 548 navigation_handle->GetWebContents()->GetContentsMimeType()); |
512 | 549 |
513 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); | 550 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); |
514 LogAbortChainHistograms(navigation_handle); | 551 LogAbortChainHistograms(navigation_handle); |
515 } | 552 } |
516 | 553 |
517 void PageLoadTracker::DidFinishSubFrameNavigation( | 554 void PageLoadTracker::DidFinishSubFrameNavigation( |
518 content::NavigationHandle* navigation_handle) { | 555 content::NavigationHandle* navigation_handle) { |
519 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnDidFinishSubFrameNavigation, | 556 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnDidFinishSubFrameNavigation, |
520 navigation_handle); | 557 navigation_handle); |
| 558 |
| 559 if (!navigation_handle->HasCommitted()) |
| 560 return; |
| 561 |
| 562 // We have a new committed navigation, so discard information about the |
| 563 // previously committed navigation. |
| 564 subframe_navigation_start_offset_.erase( |
| 565 navigation_handle->GetFrameTreeNodeId()); |
| 566 |
| 567 BrowserPageTrackDecider decider(embedder_interface_, |
| 568 navigation_handle->GetWebContents(), |
| 569 navigation_handle); |
| 570 if (!decider.ShouldTrack()) |
| 571 return; |
| 572 |
| 573 if (navigation_start_ > navigation_handle->NavigationStart()) { |
| 574 RecordInternalError(ERR_SUBFRAME_NAVIGATION_START_BEFORE_MAIN_FRAME); |
| 575 return; |
| 576 } |
| 577 base::TimeDelta navigation_delta = |
| 578 navigation_handle->NavigationStart() - navigation_start_; |
| 579 subframe_navigation_start_offset_.insert(std::make_pair( |
| 580 navigation_handle->GetFrameTreeNodeId(), navigation_delta)); |
521 } | 581 } |
522 | 582 |
523 void PageLoadTracker::FailedProvisionalLoad( | 583 void PageLoadTracker::FailedProvisionalLoad( |
524 content::NavigationHandle* navigation_handle, | 584 content::NavigationHandle* navigation_handle, |
525 base::TimeTicks failed_load_time) { | 585 base::TimeTicks failed_load_time) { |
526 DCHECK(!failed_provisional_load_info_); | 586 DCHECK(!failed_provisional_load_info_); |
527 failed_provisional_load_info_.reset(new FailedProvisionalLoadInfo( | 587 failed_provisional_load_info_.reset(new FailedProvisionalLoadInfo( |
528 failed_load_time - navigation_handle->NavigationStart(), | 588 failed_load_time - navigation_handle->NavigationStart(), |
529 navigation_handle->GetNetErrorCode())); | 589 navigation_handle->GetNetErrorCode())); |
530 } | 590 } |
(...skipping 11 matching lines...) Expand all Loading... |
542 } | 602 } |
543 | 603 |
544 void PageLoadTracker::FlushMetricsOnAppEnterBackground() { | 604 void PageLoadTracker::FlushMetricsOnAppEnterBackground() { |
545 if (!app_entered_background_) { | 605 if (!app_entered_background_) { |
546 RecordAppBackgroundPageLoadCompleted(false); | 606 RecordAppBackgroundPageLoadCompleted(false); |
547 app_entered_background_ = true; | 607 app_entered_background_ = true; |
548 } | 608 } |
549 | 609 |
550 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 610 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
551 INVOKE_AND_PRUNE_OBSERVERS(observers_, FlushMetricsOnAppEnterBackground, | 611 INVOKE_AND_PRUNE_OBSERVERS(observers_, FlushMetricsOnAppEnterBackground, |
552 timing_, info); | 612 merged_page_timing_, info); |
553 } | 613 } |
554 | 614 |
555 void PageLoadTracker::NotifyClientRedirectTo( | 615 void PageLoadTracker::NotifyClientRedirectTo( |
556 const PageLoadTracker& destination) { | 616 const PageLoadTracker& destination) { |
557 if (timing_.paint_timing.first_paint) { | 617 if (merged_page_timing_.paint_timing.first_paint) { |
558 base::TimeTicks first_paint_time = | 618 base::TimeTicks first_paint_time = |
559 navigation_start() + timing_.paint_timing.first_paint.value(); | 619 navigation_start() + |
| 620 merged_page_timing_.paint_timing.first_paint.value(); |
560 base::TimeDelta first_paint_to_navigation; | 621 base::TimeDelta first_paint_to_navigation; |
561 if (destination.navigation_start() > first_paint_time) | 622 if (destination.navigation_start() > first_paint_time) |
562 first_paint_to_navigation = | 623 first_paint_to_navigation = |
563 destination.navigation_start() - first_paint_time; | 624 destination.navigation_start() - first_paint_time; |
564 PAGE_LOAD_HISTOGRAM(internal::kClientRedirectFirstPaintToNavigation, | 625 PAGE_LOAD_HISTOGRAM(internal::kClientRedirectFirstPaintToNavigation, |
565 first_paint_to_navigation); | 626 first_paint_to_navigation); |
566 } else { | 627 } else { |
567 UMA_HISTOGRAM_BOOLEAN(internal::kClientRedirectWithoutPaint, true); | 628 UMA_HISTOGRAM_BOOLEAN(internal::kClientRedirectWithoutPaint, true); |
568 } | 629 } |
569 } | 630 } |
570 | 631 |
571 void PageLoadTracker::UpdateChildFrameMetadata( | 632 void PageLoadTracker::UpdateSubFrameTiming( |
572 const PageLoadMetadata& child_metadata) { | 633 content::RenderFrameHost* render_frame_host, |
573 // Merge the child loading behavior flags with any we've already observed, | 634 const PageLoadTiming& new_timing, |
574 // possibly from other child frames. | 635 const PageLoadMetadata& new_metadata) { |
575 const int last_child_loading_behavior_flags = | 636 UpdateSubFrameMetadata(new_metadata); |
576 child_frame_metadata_.behavior_flags; | 637 const auto it = subframe_navigation_start_offset_.find( |
577 child_frame_metadata_.behavior_flags |= child_metadata.behavior_flags; | 638 render_frame_host->GetFrameTreeNodeId()); |
578 if (last_child_loading_behavior_flags == child_frame_metadata_.behavior_flags) | 639 if (it == subframe_navigation_start_offset_.end()) { |
| 640 // We received timing information for an untracked load. Ignore it. |
| 641 return; |
| 642 } |
| 643 |
| 644 base::TimeDelta navigation_start_offset = it->second; |
| 645 const PageLoadTiming last_timing = merged_page_timing_; |
| 646 MergePaintTiming(navigation_start_offset, new_timing.paint_timing, |
| 647 false /* is_main_frame */); |
| 648 |
| 649 if (last_timing == merged_page_timing_) |
| 650 return; |
| 651 |
| 652 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
| 653 for (const auto& observer : observers_) { |
| 654 DispatchObserverTimingCallbacks(observer.get(), last_timing, |
| 655 merged_page_timing_, main_frame_metadata_, |
| 656 info); |
| 657 } |
| 658 } |
| 659 |
| 660 void PageLoadTracker::MergePaintTiming( |
| 661 base::TimeDelta navigation_start_offset, |
| 662 const page_load_metrics::PaintTiming& new_paint_timing, |
| 663 bool is_main_frame) { |
| 664 MaybeUpdateTimeDelta(&merged_page_timing_.paint_timing.first_paint, |
| 665 navigation_start_offset, new_paint_timing.first_paint); |
| 666 MaybeUpdateTimeDelta(&merged_page_timing_.paint_timing.first_text_paint, |
| 667 navigation_start_offset, |
| 668 new_paint_timing.first_text_paint); |
| 669 MaybeUpdateTimeDelta(&merged_page_timing_.paint_timing.first_image_paint, |
| 670 navigation_start_offset, |
| 671 new_paint_timing.first_image_paint); |
| 672 MaybeUpdateTimeDelta(&merged_page_timing_.paint_timing.first_contentful_paint, |
| 673 navigation_start_offset, |
| 674 new_paint_timing.first_contentful_paint); |
| 675 if (is_main_frame) { |
| 676 // first meaningful paint is only tracked in the main frame. |
| 677 merged_page_timing_.paint_timing.first_meaningful_paint = |
| 678 new_paint_timing.first_meaningful_paint; |
| 679 } |
| 680 } |
| 681 |
| 682 void PageLoadTracker::UpdateSubFrameMetadata( |
| 683 const PageLoadMetadata& subframe_metadata) { |
| 684 // Merge the subframe loading behavior flags with any we've already observed, |
| 685 // possibly from other subframes. |
| 686 const int last_subframe_loading_behavior_flags = |
| 687 subframe_metadata_.behavior_flags; |
| 688 subframe_metadata_.behavior_flags |= subframe_metadata.behavior_flags; |
| 689 if (last_subframe_loading_behavior_flags == subframe_metadata_.behavior_flags) |
579 return; | 690 return; |
580 | 691 |
581 PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo()); | 692 PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo()); |
582 for (const auto& observer : observers_) { | 693 for (const auto& observer : observers_) { |
583 observer->OnLoadingBehaviorObserved(extra_info); | 694 observer->OnLoadingBehaviorObserved(extra_info); |
584 } | 695 } |
585 } | 696 } |
586 | 697 |
587 void PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing, | 698 void PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing, |
588 const PageLoadMetadata& new_metadata) { | 699 const PageLoadMetadata& new_metadata) { |
589 // Throw away IPCs that are not relevant to the current navigation. | 700 // Throw away IPCs that are not relevant to the current navigation. |
590 // Two timing structures cannot refer to the same navigation if they indicate | 701 // Two timing structures cannot refer to the same navigation if they indicate |
591 // that a navigation started at different times, so a new timing struct with a | 702 // that a navigation started at different times, so a new timing struct with a |
592 // different start time from an earlier struct is considered invalid. | 703 // different start time from an earlier struct is considered invalid. |
593 const bool valid_timing_descendent = | 704 const bool valid_timing_descendent = |
594 timing_.navigation_start.is_null() || | 705 merged_page_timing_.navigation_start.is_null() || |
595 timing_.navigation_start == new_timing.navigation_start; | 706 merged_page_timing_.navigation_start == new_timing.navigation_start; |
596 if (!valid_timing_descendent) { | 707 if (!valid_timing_descendent) { |
597 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING_DESCENDENT); | 708 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING_DESCENDENT); |
598 return; | 709 return; |
599 } | 710 } |
600 | 711 |
601 // Ensure flags sent previously are still present in the new metadata fields. | 712 // Ensure flags sent previously are still present in the new metadata fields. |
602 const bool valid_behavior_descendent = | 713 const bool valid_behavior_descendent = |
603 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == | 714 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == |
604 main_frame_metadata_.behavior_flags; | 715 main_frame_metadata_.behavior_flags; |
605 if (!valid_behavior_descendent) { | 716 if (!valid_behavior_descendent) { |
606 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); | 717 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); |
607 return; | 718 return; |
608 } | 719 } |
609 internal::PageLoadTimingStatus status = IsValidPageLoadTiming(new_timing); | 720 internal::PageLoadTimingStatus status = IsValidPageLoadTiming(new_timing); |
610 UMA_HISTOGRAM_ENUMERATION(internal::kPageLoadTimingStatus, status, | 721 UMA_HISTOGRAM_ENUMERATION(internal::kPageLoadTimingStatus, status, |
611 internal::LAST_PAGE_LOAD_TIMING_STATUS); | 722 internal::LAST_PAGE_LOAD_TIMING_STATUS); |
612 if (status != internal::VALID) { | 723 if (status != internal::VALID) { |
613 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING); | 724 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING); |
614 return; | 725 return; |
615 } | 726 } |
616 | 727 |
617 DCHECK(did_commit_); // OnCommit() must be called first. | 728 DCHECK(did_commit_); // OnCommit() must be called first. |
618 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() | 729 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() |
619 // must be called before DispatchObserverTimingCallbacks, but its | 730 // must be called before DispatchObserverTimingCallbacks, but its |
620 // implementation depends on the state of main_frame_metadata_, so we need | 731 // implementation depends on the state of main_frame_metadata_, so we need |
621 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. | 732 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. |
622 // Thus, we make a copy of timing here, update timing_ and | 733 // Thus, we make a copy of timing here, update timing_ and |
623 // main_frame_metadata_, and then proceed to dispatch the observer timing | 734 // main_frame_metadata_, and then proceed to dispatch the observer timing |
624 // callbacks. | 735 // callbacks. |
625 const PageLoadTiming last_timing = timing_; | 736 const PageLoadTiming last_timing = merged_page_timing_; |
626 timing_ = new_timing; | 737 |
| 738 // Update the merged_page_timing_, making sure to merge the previously |
| 739 // observed |paint_timing|, which is tracked across all frames in the page. |
| 740 merged_page_timing_ = new_timing; |
| 741 merged_page_timing_.paint_timing = last_timing.paint_timing; |
| 742 MergePaintTiming(base::TimeDelta(), new_timing.paint_timing, |
| 743 true /* is_main_frame */); |
627 | 744 |
628 const PageLoadMetadata last_metadata = main_frame_metadata_; | 745 const PageLoadMetadata last_metadata = main_frame_metadata_; |
629 main_frame_metadata_ = new_metadata; | 746 main_frame_metadata_ = new_metadata; |
630 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 747 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
631 for (const auto& observer : observers_) { | 748 for (const auto& observer : observers_) { |
632 DispatchObserverTimingCallbacks(observer.get(), last_timing, new_timing, | 749 DispatchObserverTimingCallbacks(observer.get(), last_timing, |
633 last_metadata, info); | 750 merged_page_timing_, last_metadata, info); |
634 } | 751 } |
635 } | 752 } |
636 | 753 |
637 void PageLoadTracker::OnStartedResource( | 754 void PageLoadTracker::OnStartedResource( |
638 const ExtraRequestStartInfo& extra_request_start_info) { | 755 const ExtraRequestStartInfo& extra_request_start_info) { |
639 for (const auto& observer : observers_) { | 756 for (const auto& observer : observers_) { |
640 observer->OnStartedResource(extra_request_start_info); | 757 observer->OnStartedResource(extra_request_start_info); |
641 } | 758 } |
642 } | 759 } |
643 | 760 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
710 // page_end_reason_ == END_NONE implies page_end_user_initiated_info_ is not | 827 // page_end_reason_ == END_NONE implies page_end_user_initiated_info_ is not |
711 // user initiated. | 828 // user initiated. |
712 DCHECK(page_end_reason_ != END_NONE || | 829 DCHECK(page_end_reason_ != END_NONE || |
713 (!page_end_user_initiated_info_.browser_initiated && | 830 (!page_end_user_initiated_info_.browser_initiated && |
714 !page_end_user_initiated_info_.user_gesture && | 831 !page_end_user_initiated_info_.user_gesture && |
715 !page_end_user_initiated_info_.user_input_event)); | 832 !page_end_user_initiated_info_.user_input_event)); |
716 return PageLoadExtraInfo( | 833 return PageLoadExtraInfo( |
717 navigation_start_, first_background_time, first_foreground_time, | 834 navigation_start_, first_background_time, first_foreground_time, |
718 started_in_foreground_, user_initiated_info_, url(), start_url_, | 835 started_in_foreground_, user_initiated_info_, url(), start_url_, |
719 did_commit_, page_end_reason_, page_end_user_initiated_info_, | 836 did_commit_, page_end_reason_, page_end_user_initiated_info_, |
720 page_end_time, main_frame_metadata_, child_frame_metadata_); | 837 page_end_time, main_frame_metadata_, subframe_metadata_); |
721 } | 838 } |
722 | 839 |
723 bool PageLoadTracker::HasMatchingNavigationRequestID( | 840 bool PageLoadTracker::HasMatchingNavigationRequestID( |
724 const content::GlobalRequestID& request_id) const { | 841 const content::GlobalRequestID& request_id) const { |
725 DCHECK(request_id != content::GlobalRequestID()); | 842 DCHECK(request_id != content::GlobalRequestID()); |
726 return navigation_request_id_.has_value() && | 843 return navigation_request_id_.has_value() && |
727 navigation_request_id_.value() == request_id; | 844 navigation_request_id_.value() == request_id; |
728 } | 845 } |
729 | 846 |
730 void PageLoadTracker::NotifyPageEnd(PageEndReason page_end_reason, | 847 void PageLoadTracker::NotifyPageEnd(PageEndReason page_end_reason, |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
819 observer->MediaStartedPlaying(video_type, is_in_main_frame); | 936 observer->MediaStartedPlaying(video_type, is_in_main_frame); |
820 } | 937 } |
821 | 938 |
822 void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay, | 939 void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay, |
823 base::TimeDelta actual_delay) { | 940 base::TimeDelta actual_delay) { |
824 for (const auto& observer : observers_) | 941 for (const auto& observer : observers_) |
825 observer->OnNavigationDelayComplete(scheduled_delay, actual_delay); | 942 observer->OnNavigationDelayComplete(scheduled_delay, actual_delay); |
826 } | 943 } |
827 | 944 |
828 } // namespace page_load_metrics | 945 } // namespace page_load_metrics |
OLD | NEW |