| 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> |
| (...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 observer->OnLoadingBehaviorObserved(extra_info); | 280 observer->OnLoadingBehaviorObserved(extra_info); |
| 281 } | 281 } |
| 282 | 282 |
| 283 } // namespace | 283 } // namespace |
| 284 | 284 |
| 285 PageLoadTracker::PageLoadTracker( | 285 PageLoadTracker::PageLoadTracker( |
| 286 bool in_foreground, | 286 bool in_foreground, |
| 287 PageLoadMetricsEmbedderInterface* embedder_interface, | 287 PageLoadMetricsEmbedderInterface* embedder_interface, |
| 288 const GURL& currently_committed_url, | 288 const GURL& currently_committed_url, |
| 289 content::NavigationHandle* navigation_handle, | 289 content::NavigationHandle* navigation_handle, |
| 290 UserInitiatedInfo user_initiated_info, |
| 290 int aborted_chain_size, | 291 int aborted_chain_size, |
| 291 int aborted_chain_size_same_url) | 292 int aborted_chain_size_same_url) |
| 292 : did_stop_tracking_(false), | 293 : did_stop_tracking_(false), |
| 293 app_entered_background_(false), | 294 app_entered_background_(false), |
| 294 navigation_start_(navigation_handle->NavigationStart()), | 295 navigation_start_(navigation_handle->NavigationStart()), |
| 295 start_url_(navigation_handle->GetURL()), | 296 start_url_(navigation_handle->GetURL()), |
| 296 abort_type_(ABORT_NONE), | 297 abort_type_(ABORT_NONE), |
| 297 abort_user_initiated_(false), | 298 abort_user_initiated_info_(UserInitiatedInfo::NotUserInitiated()), |
| 298 started_in_foreground_(in_foreground), | 299 started_in_foreground_(in_foreground), |
| 299 page_transition_(navigation_handle->GetPageTransition()), | 300 page_transition_(navigation_handle->GetPageTransition()), |
| 300 num_cache_requests_(0), | 301 num_cache_requests_(0), |
| 301 num_network_requests_(0), | 302 num_network_requests_(0), |
| 302 user_initiated_(IsNavigationUserInitiated(navigation_handle)), | 303 user_initiated_info_(user_initiated_info), |
| 303 aborted_chain_size_(aborted_chain_size), | 304 aborted_chain_size_(aborted_chain_size), |
| 304 aborted_chain_size_same_url_(aborted_chain_size_same_url), | 305 aborted_chain_size_same_url_(aborted_chain_size_same_url), |
| 305 embedder_interface_(embedder_interface) { | 306 embedder_interface_(embedder_interface) { |
| 306 DCHECK(!navigation_handle->HasCommitted()); | 307 DCHECK(!navigation_handle->HasCommitted()); |
| 307 if (embedder_interface_->IsPrerendering( | 308 if (embedder_interface_->IsPrerendering( |
| 308 navigation_handle->GetWebContents())) { | 309 navigation_handle->GetWebContents())) { |
| 309 DCHECK(!started_in_foreground_); | 310 DCHECK(!started_in_foreground_); |
| 310 // For the time being, we do not track prerenders. See crbug.com/648338 for | 311 // For the time being, we do not track prerenders. See crbug.com/648338 for |
| 311 // details. | 312 // details. |
| 312 StopTracking(); | 313 StopTracking(); |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 407 // Make sure we either started in the foreground and haven't been | 408 // Make sure we either started in the foreground and haven't been |
| 408 // foregrounded yet, or started in the background and have already been | 409 // foregrounded yet, or started in the background and have already been |
| 409 // foregrounded. | 410 // foregrounded. |
| 410 DCHECK_EQ(started_in_foreground_, foreground_time_.is_null()); | 411 DCHECK_EQ(started_in_foreground_, foreground_time_.is_null()); |
| 411 background_time_ = base::TimeTicks::Now(); | 412 background_time_ = base::TimeTicks::Now(); |
| 412 ClampBrowserTimestampIfInterProcessTimeTickSkew(&background_time_); | 413 ClampBrowserTimestampIfInterProcessTimeTickSkew(&background_time_); |
| 413 // Though most cases where a tab is backgrounded are user initiated, we | 414 // Though most cases where a tab is backgrounded are user initiated, we |
| 414 // can't be certain that we were backgrounded due to a user action. For | 415 // can't be certain that we were backgrounded due to a user action. For |
| 415 // example, on Android, the screen times out after a period of inactivity, | 416 // example, on Android, the screen times out after a period of inactivity, |
| 416 // resulting in a non-user-initiated backgrounding. | 417 // resulting in a non-user-initiated backgrounding. |
| 417 const bool abort_is_user_initiated = false; | 418 NotifyAbort(ABORT_BACKGROUND, UserInitiatedInfo::NotUserInitiated(), |
| 418 NotifyAbort(ABORT_BACKGROUND, abort_is_user_initiated, background_time_, | 419 background_time_, true); |
| 419 true); | |
| 420 } | 420 } |
| 421 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); | 421 const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
| 422 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden, timing_, info); | 422 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden, timing_, info); |
| 423 } | 423 } |
| 424 | 424 |
| 425 void PageLoadTracker::WebContentsShown() { | 425 void PageLoadTracker::WebContentsShown() { |
| 426 // Only log the first time we foreground in a given page load. | 426 // Only log the first time we foreground in a given page load. |
| 427 if (foreground_time_.is_null()) { | 427 if (foreground_time_.is_null()) { |
| 428 // Make sure we either started in the background and haven't been | 428 // Make sure we either started in the background and haven't been |
| 429 // backgrounded yet, or started in the foreground and have already been | 429 // backgrounded yet, or started in the foreground and have already been |
| 430 // backgrounded. | 430 // backgrounded. |
| 431 DCHECK_NE(started_in_foreground_, background_time_.is_null()); | 431 DCHECK_NE(started_in_foreground_, background_time_.is_null()); |
| 432 foreground_time_ = base::TimeTicks::Now(); | 432 foreground_time_ = base::TimeTicks::Now(); |
| 433 ClampBrowserTimestampIfInterProcessTimeTickSkew(&foreground_time_); | 433 ClampBrowserTimestampIfInterProcessTimeTickSkew(&foreground_time_); |
| 434 } | 434 } |
| 435 | 435 |
| 436 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnShown); | 436 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnShown); |
| 437 } | 437 } |
| 438 | 438 |
| 439 void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) { | 439 void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) { |
| 440 committed_url_ = navigation_handle->GetURL(); | 440 committed_url_ = navigation_handle->GetURL(); |
| 441 // Some transitions (like CLIENT_REDIRECT) are only known at commit time. | 441 // Some transitions (like CLIENT_REDIRECT) are only known at commit time. |
| 442 page_transition_ = navigation_handle->GetPageTransition(); | 442 page_transition_ = navigation_handle->GetPageTransition(); |
| 443 user_initiated_ = IsNavigationUserInitiated(navigation_handle); | 443 user_initiated_info_.user_gesture = navigation_handle->HasUserGesture(); |
| 444 | 444 |
| 445 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); | 445 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); |
| 446 LogAbortChainHistograms(navigation_handle); | 446 LogAbortChainHistograms(navigation_handle); |
| 447 } | 447 } |
| 448 | 448 |
| 449 void PageLoadTracker::FailedProvisionalLoad( | 449 void PageLoadTracker::FailedProvisionalLoad( |
| 450 content::NavigationHandle* navigation_handle) { | 450 content::NavigationHandle* navigation_handle) { |
| 451 DCHECK(!failed_provisional_load_info_); | 451 DCHECK(!failed_provisional_load_info_); |
| 452 failed_provisional_load_info_.reset(new FailedProvisionalLoadInfo( | 452 failed_provisional_load_info_.reset(new FailedProvisionalLoadInfo( |
| 453 base::TimeTicks::Now() - navigation_handle->NavigationStart(), | 453 base::TimeTicks::Now() - navigation_handle->NavigationStart(), |
| 454 navigation_handle->GetNetErrorCode())); | 454 navigation_handle->GetNetErrorCode())); |
| 455 } | 455 } |
| 456 | 456 |
| 457 void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) { | 457 void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) { |
| 458 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnRedirect, navigation_handle); | 458 INVOKE_AND_PRUNE_OBSERVERS(observers_, OnRedirect, navigation_handle); |
| 459 } | 459 } |
| 460 | 460 |
| 461 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { | 461 void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { |
| 462 input_tracker_.OnInputEvent(event); |
| 462 for (const auto& observer : observers_) { | 463 for (const auto& observer : observers_) { |
| 463 observer->OnUserInput(event); | 464 observer->OnUserInput(event); |
| 464 } | 465 } |
| 465 } | 466 } |
| 466 | 467 |
| 467 void PageLoadTracker::FlushMetricsOnAppEnterBackground() { | 468 void PageLoadTracker::FlushMetricsOnAppEnterBackground() { |
| 468 if (!app_entered_background_) { | 469 if (!app_entered_background_) { |
| 469 RecordAppBackgroundPageLoadCompleted(false); | 470 RecordAppBackgroundPageLoadCompleted(false); |
| 470 app_entered_background_ = true; | 471 app_entered_background_ = true; |
| 471 } | 472 } |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 588 first_foreground_time = foreground_time_ - navigation_start_; | 589 first_foreground_time = foreground_time_ - navigation_start_; |
| 589 } | 590 } |
| 590 | 591 |
| 591 if (abort_type_ != ABORT_NONE) { | 592 if (abort_type_ != ABORT_NONE) { |
| 592 DCHECK_GE(abort_time_, navigation_start_); | 593 DCHECK_GE(abort_time_, navigation_start_); |
| 593 time_to_abort = abort_time_ - navigation_start_; | 594 time_to_abort = abort_time_ - navigation_start_; |
| 594 } else { | 595 } else { |
| 595 DCHECK(abort_time_.is_null()); | 596 DCHECK(abort_time_.is_null()); |
| 596 } | 597 } |
| 597 | 598 |
| 598 // abort_type_ == ABORT_NONE implies !abort_user_initiated_. | 599 // abort_type_ == ABORT_NONE implies abort_user_initiated_info_ is not user |
| 599 DCHECK(abort_type_ != ABORT_NONE || !abort_user_initiated_); | 600 // initiated. |
| 601 DCHECK(abort_type_ != ABORT_NONE || |
| 602 (!abort_user_initiated_info_.browser_initiated && |
| 603 !abort_user_initiated_info_.user_gesture && |
| 604 !abort_user_initiated_info_.user_input_event)); |
| 600 return PageLoadExtraInfo( | 605 return PageLoadExtraInfo( |
| 601 first_background_time, first_foreground_time, started_in_foreground_, | 606 first_background_time, first_foreground_time, started_in_foreground_, |
| 602 user_initiated_, committed_url_, start_url_, abort_type_, | 607 user_initiated_info_, committed_url_, start_url_, abort_type_, |
| 603 abort_user_initiated_, time_to_abort, num_cache_requests_, | 608 abort_user_initiated_info_, time_to_abort, num_cache_requests_, |
| 604 num_network_requests_, metadata_); | 609 num_network_requests_, metadata_); |
| 605 } | 610 } |
| 606 | 611 |
| 607 void PageLoadTracker::NotifyAbort(UserAbortType abort_type, | 612 void PageLoadTracker::NotifyAbort(UserAbortType abort_type, |
| 608 bool user_initiated, | 613 UserInitiatedInfo user_initiated_info, |
| 609 base::TimeTicks timestamp, | 614 base::TimeTicks timestamp, |
| 610 bool is_certainly_browser_timestamp) { | 615 bool is_certainly_browser_timestamp) { |
| 611 DCHECK_NE(abort_type, ABORT_NONE); | 616 DCHECK_NE(abort_type, ABORT_NONE); |
| 612 // Use UpdateAbort to update an already notified PageLoadTracker. | 617 // Use UpdateAbort to update an already notified PageLoadTracker. |
| 613 if (abort_type_ != ABORT_NONE) | 618 if (abort_type_ != ABORT_NONE) |
| 614 return; | 619 return; |
| 615 | 620 |
| 616 UpdateAbortInternal(abort_type, user_initiated, timestamp, | 621 UpdateAbortInternal(abort_type, user_initiated_info, timestamp, |
| 617 is_certainly_browser_timestamp); | 622 is_certainly_browser_timestamp); |
| 618 } | 623 } |
| 619 | 624 |
| 620 void PageLoadTracker::UpdateAbort(UserAbortType abort_type, | 625 void PageLoadTracker::UpdateAbort(UserAbortType abort_type, |
| 621 bool user_initiated, | 626 UserInitiatedInfo user_initiated_info, |
| 622 base::TimeTicks timestamp, | 627 base::TimeTicks timestamp, |
| 623 bool is_certainly_browser_timestamp) { | 628 bool is_certainly_browser_timestamp) { |
| 624 DCHECK_NE(abort_type, ABORT_NONE); | 629 DCHECK_NE(abort_type, ABORT_NONE); |
| 625 DCHECK_NE(abort_type, ABORT_OTHER); | 630 DCHECK_NE(abort_type, ABORT_OTHER); |
| 626 DCHECK_EQ(abort_type_, ABORT_OTHER); | 631 DCHECK_EQ(abort_type_, ABORT_OTHER); |
| 627 | 632 |
| 628 // For some aborts (e.g. navigations), the initiated timestamp can be earlier | 633 // For some aborts (e.g. navigations), the initiated timestamp can be earlier |
| 629 // than the timestamp that aborted the load. Taking the minimum gives the | 634 // than the timestamp that aborted the load. Taking the minimum gives the |
| 630 // closest user initiated time known. | 635 // closest user initiated time known. |
| 631 UpdateAbortInternal(abort_type, user_initiated, | 636 UpdateAbortInternal(abort_type, user_initiated_info, |
| 632 std::min(abort_time_, timestamp), | 637 std::min(abort_time_, timestamp), |
| 633 is_certainly_browser_timestamp); | 638 is_certainly_browser_timestamp); |
| 634 } | 639 } |
| 635 | 640 |
| 636 bool PageLoadTracker::IsLikelyProvisionalAbort( | 641 bool PageLoadTracker::IsLikelyProvisionalAbort( |
| 637 base::TimeTicks abort_cause_time) const { | 642 base::TimeTicks abort_cause_time) const { |
| 638 // Note that |abort_cause_time - abort_time| can be negative. | 643 // Note that |abort_cause_time - abort_time| can be negative. |
| 639 return abort_type_ == ABORT_OTHER && | 644 return abort_type_ == ABORT_OTHER && |
| 640 (abort_cause_time - abort_time_).InMilliseconds() < 100; | 645 (abort_cause_time - abort_time_).InMilliseconds() < 100; |
| 641 } | 646 } |
| 642 | 647 |
| 643 bool PageLoadTracker::MatchesOriginalNavigation( | 648 bool PageLoadTracker::MatchesOriginalNavigation( |
| 644 content::NavigationHandle* navigation_handle) { | 649 content::NavigationHandle* navigation_handle) { |
| 645 // Neither navigation should have committed. | 650 // Neither navigation should have committed. |
| 646 DCHECK(!navigation_handle->HasCommitted()); | 651 DCHECK(!navigation_handle->HasCommitted()); |
| 647 DCHECK(committed_url_.is_empty()); | 652 DCHECK(committed_url_.is_empty()); |
| 648 return navigation_handle->GetURL() == start_url_; | 653 return navigation_handle->GetURL() == start_url_; |
| 649 } | 654 } |
| 650 | 655 |
| 651 void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type, | 656 void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type, |
| 652 bool user_initiated, | 657 UserInitiatedInfo user_initiated_info, |
| 653 base::TimeTicks timestamp, | 658 base::TimeTicks timestamp, |
| 654 bool is_certainly_browser_timestamp) { | 659 bool is_certainly_browser_timestamp) { |
| 655 // When a provisional navigation commits, that navigation's start time is | 660 // When a provisional navigation commits, that navigation's start time is |
| 656 // interpreted as the abort time for other provisional loads in the tab. | 661 // interpreted as the abort time for other provisional loads in the tab. |
| 657 // However, this only makes sense if the committed load started after the | 662 // However, this only makes sense if the committed load started after the |
| 658 // aborted provisional loads started. Thus we ignore cases where the committed | 663 // aborted provisional loads started. Thus we ignore cases where the committed |
| 659 // load started before the aborted provisional load, as this would result in | 664 // load started before the aborted provisional load, as this would result in |
| 660 // recording a negative time-to-abort. The real issue here is that we have to | 665 // recording a negative time-to-abort. The real issue here is that we have to |
| 661 // infer the cause of aborts. It would be better if the navigation code could | 666 // infer the cause of aborts. It would be better if the navigation code could |
| 662 // instead report the actual cause of an aborted navigation. See crbug/571647 | 667 // instead report the actual cause of an aborted navigation. See crbug/571647 |
| 663 // for details. | 668 // for details. |
| 664 if (timestamp < navigation_start_) { | 669 if (timestamp < navigation_start_) { |
| 665 RecordInternalError(ERR_ABORT_BEFORE_NAVIGATION_START); | 670 RecordInternalError(ERR_ABORT_BEFORE_NAVIGATION_START); |
| 666 abort_type_ = ABORT_NONE; | 671 abort_type_ = ABORT_NONE; |
| 667 abort_time_ = base::TimeTicks(); | 672 abort_time_ = base::TimeTicks(); |
| 668 return; | 673 return; |
| 669 } | 674 } |
| 670 abort_type_ = abort_type; | 675 abort_type_ = abort_type; |
| 671 abort_time_ = timestamp; | 676 abort_time_ = timestamp; |
| 672 // A client redirect can never be user initiated. Due to the way Blink | 677 // A client redirect can never be user initiated. Due to the way Blink |
| 673 // implements user gesture tracking, where all events that occur within 1 | 678 // implements user gesture tracking, where all events that occur within 1 |
| 674 // second after a user interaction are considered to be triggered by user | 679 // second after a user interaction are considered to be triggered by user |
| 675 // activation (based on HTML spec: | 680 // activation (based on HTML spec: |
| 676 // https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-a
ctivation), | 681 // https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-a
ctivation), |
| 677 // these navs may sometimes be reported as user initiated by Blink. Thus, we | 682 // these navs may sometimes be reported as user initiated by Blink. Thus, we |
| 678 // explicitly filter these types of aborts out when deciding if the abort was | 683 // explicitly filter these types of aborts out when deciding if the abort was |
| 679 // user initiated. | 684 // user initiated. |
| 680 abort_user_initiated_ = user_initiated && abort_type != ABORT_CLIENT_REDIRECT; | 685 if (abort_type != ABORT_CLIENT_REDIRECT) |
| 686 abort_user_initiated_info_ = user_initiated_info; |
| 681 | 687 |
| 682 if (is_certainly_browser_timestamp) { | 688 if (is_certainly_browser_timestamp) { |
| 683 ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_); | 689 ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_); |
| 684 } | 690 } |
| 685 } | 691 } |
| 686 | 692 |
| 687 } // namespace page_load_metrics | 693 } // namespace page_load_metrics |
| OLD | NEW |