| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h" |
| #include <string> |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/string_util.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/page_load_metrics/page_load_metrics_util.h" |
| #include "chrome/common/page_load_metrics/page_load_timing.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| |
| using page_load_metrics::PageAbortReason; |
| |
| namespace internal { |
| |
| const char kHistogramFromGWSDomContentLoaded[] = |
| "PageLoad.Clients.FromGoogleSearch.DocumentTiming." |
| "NavigationToDOMContentLoadedEventFired"; |
| const char kHistogramFromGWSLoad[] = |
| "PageLoad.Clients.FromGoogleSearch.DocumentTiming." |
| "NavigationToLoadEventFired"; |
| const char kHistogramFromGWSFirstPaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PaintTiming.NavigationToFirstPaint"; |
| const char kHistogramFromGWSFirstImagePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PaintTiming.NavigationToFirstImagePaint"; |
| const char kHistogramFromGWSFirstContentfulPaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PaintTiming." |
| "NavigationToFirstContentfulPaint"; |
| const char kHistogramFromGWSParseStartToFirstContentfulPaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PaintTiming." |
| "ParseStartToFirstContentfulPaint"; |
| const char kHistogramFromGWSParseDuration[] = |
| "PageLoad.Clients.FromGoogleSearch.ParseTiming.ParseDuration"; |
| const char kHistogramFromGWSParseStart[] = |
| "PageLoad.Clients.FromGoogleSearch.ParseTiming.NavigationToParseStart"; |
| const char kHistogramFromGWSFirstInputDelay[] = |
| "PageLoad.Clients.FromGoogleSearch.InteractiveTiming.FirstInputDelay2"; |
| |
| const char kHistogramFromGWSAbortNewNavigationBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.NewNavigation." |
| "BeforeCommit"; |
| const char kHistogramFromGWSAbortNewNavigationBeforePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.NewNavigation." |
| "AfterCommit.BeforePaint"; |
| const char kHistogramFromGWSAbortNewNavigationBeforeInteraction[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.NewNavigation." |
| "AfterPaint.BeforeInteraction"; |
| const char kHistogramFromGWSAbortStopBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Stop." |
| "BeforeCommit"; |
| const char kHistogramFromGWSAbortStopBeforePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Stop." |
| "AfterCommit.BeforePaint"; |
| const char kHistogramFromGWSAbortStopBeforeInteraction[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Stop." |
| "AfterPaint.BeforeInteraction"; |
| const char kHistogramFromGWSAbortCloseBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Close." |
| "BeforeCommit"; |
| const char kHistogramFromGWSAbortCloseBeforePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Close." |
| "AfterCommit.BeforePaint"; |
| const char kHistogramFromGWSAbortCloseBeforeInteraction[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Close." |
| "AfterPaint.BeforeInteraction"; |
| const char kHistogramFromGWSAbortOtherBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Other." |
| "BeforeCommit"; |
| const char kHistogramFromGWSAbortReloadBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Reload." |
| "BeforeCommit"; |
| const char kHistogramFromGWSAbortReloadBeforePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Reload." |
| "AfterCommit.BeforePaint"; |
| const char kHistogramFromGWSAbortReloadBeforeInteraction[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Reload." |
| "AfterPaint.Before1sDelayedInteraction"; |
| const char kHistogramFromGWSAbortForwardBackBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming." |
| "ForwardBackNavigation.BeforeCommit"; |
| const char kHistogramFromGWSAbortForwardBackBeforePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming." |
| "ForwardBackNavigation.AfterCommit.BeforePaint"; |
| const char kHistogramFromGWSAbortForwardBackBeforeInteraction[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming." |
| "ForwardBackNavigation.AfterPaint.Before1sDelayedInteraction"; |
| const char kHistogramFromGWSAbortBackgroundBeforeCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Background." |
| "BeforeCommit"; |
| const char kHistogramFromGWSAbortBackgroundBeforePaint[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Background." |
| "AfterCommit.BeforePaint"; |
| const char kHistogramFromGWSAbortBackgroundBeforeInteraction[] = |
| "PageLoad.Clients.FromGoogleSearch.Experimental.AbortTiming.Background." |
| "AfterPaint.BeforeInteraction"; |
| |
| const char kHistogramFromGWSForegroundDuration[] = |
| "PageLoad.Clients.FromGoogleSearch.PageTiming.ForegroundDuration"; |
| const char kHistogramFromGWSForegroundDurationAfterPaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PageTiming.ForegroundDuration." |
| "AfterPaint"; |
| const char kHistogramFromGWSForegroundDurationWithPaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PageTiming.ForegroundDuration." |
| "WithPaint"; |
| const char kHistogramFromGWSForegroundDurationWithoutPaint[] = |
| "PageLoad.Clients.FromGoogleSearch.PageTiming.ForegroundDuration." |
| "WithoutPaint"; |
| const char kHistogramFromGWSForegroundDurationNoCommit[] = |
| "PageLoad.Clients.FromGoogleSearch.PageTiming.ForegroundDuration.NoCommit"; |
| |
| } // namespace internal |
| |
| namespace { |
| |
| void LogCommittedAbortsBeforePaint(PageAbortReason abort_reason, |
| base::TimeDelta page_end_time) { |
| switch (abort_reason) { |
| case PageAbortReason::ABORT_STOP: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortStopBeforePaint, |
| page_end_time); |
| break; |
| case PageAbortReason::ABORT_CLOSE: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortCloseBeforePaint, |
| page_end_time); |
| break; |
| case PageAbortReason::ABORT_NEW_NAVIGATION: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortNewNavigationBeforePaint, |
| page_end_time); |
| break; |
| case PageAbortReason::ABORT_RELOAD: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortReloadBeforePaint, |
| page_end_time); |
| break; |
| case PageAbortReason::ABORT_FORWARD_BACK: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortForwardBackBeforePaint, |
| page_end_time); |
| break; |
| case PageAbortReason::ABORT_BACKGROUND: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortBackgroundBeforePaint, |
| page_end_time); |
| break; |
| default: |
| // These should only be logged for provisional aborts. |
| DCHECK_NE(abort_reason, PageAbortReason::ABORT_OTHER); |
| break; |
| } |
| } |
| |
| void LogAbortsAfterPaintBeforeInteraction( |
| const page_load_metrics::PageAbortInfo& abort_info) { |
| switch (abort_info.reason) { |
| case PageAbortReason::ABORT_STOP: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortStopBeforeInteraction, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_CLOSE: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortCloseBeforeInteraction, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_NEW_NAVIGATION: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortNewNavigationBeforeInteraction, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_RELOAD: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortReloadBeforeInteraction, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_FORWARD_BACK: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortForwardBackBeforeInteraction, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_BACKGROUND: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortBackgroundBeforeInteraction, |
| abort_info.time_to_abort); |
| break; |
| default: |
| // These should only be logged for provisional aborts. |
| DCHECK_NE(abort_info.reason, PageAbortReason::ABORT_OTHER); |
| break; |
| } |
| } |
| |
| void LogProvisionalAborts(const page_load_metrics::PageAbortInfo& abort_info) { |
| switch (abort_info.reason) { |
| case PageAbortReason::ABORT_STOP: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortStopBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_CLOSE: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortCloseBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_OTHER: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortOtherBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_NEW_NAVIGATION: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortNewNavigationBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_RELOAD: |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSAbortReloadBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_FORWARD_BACK: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortForwardBackBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| case PageAbortReason::ABORT_BACKGROUND: |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSAbortBackgroundBeforeCommit, |
| abort_info.time_to_abort); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void LogForegroundDurations( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& info, |
| base::TimeTicks app_background_time) { |
| base::Optional<base::TimeDelta> foreground_duration = |
| GetInitialForegroundDuration(info, app_background_time); |
| if (!foreground_duration) |
| return; |
| |
| if (info.did_commit) { |
| PAGE_LOAD_LONG_HISTOGRAM(internal::kHistogramFromGWSForegroundDuration, |
| foreground_duration.value()); |
| if (timing.paint_timing->first_paint && |
| timing.paint_timing->first_paint < foreground_duration) { |
| PAGE_LOAD_LONG_HISTOGRAM( |
| internal::kHistogramFromGWSForegroundDurationAfterPaint, |
| foreground_duration.value() - |
| timing.paint_timing->first_paint.value()); |
| PAGE_LOAD_LONG_HISTOGRAM( |
| internal::kHistogramFromGWSForegroundDurationWithPaint, |
| foreground_duration.value()); |
| } else { |
| PAGE_LOAD_LONG_HISTOGRAM( |
| internal::kHistogramFromGWSForegroundDurationWithoutPaint, |
| foreground_duration.value()); |
| } |
| } else { |
| PAGE_LOAD_LONG_HISTOGRAM( |
| internal::kHistogramFromGWSForegroundDurationNoCommit, |
| foreground_duration.value()); |
| } |
| } |
| |
| bool WasAbortedInForeground( |
| const page_load_metrics::PageLoadExtraInfo& info, |
| const page_load_metrics::PageAbortInfo& abort_info) { |
| if (!info.started_in_foreground || |
| abort_info.reason == PageAbortReason::ABORT_NONE) |
| return false; |
| |
| base::Optional<base::TimeDelta> time_to_abort(abort_info.time_to_abort); |
| if (WasStartedInForegroundOptionalEventInForeground(time_to_abort, info)) |
| return true; |
| |
| const base::TimeDelta time_to_first_background = |
| info.first_background_time.value(); |
| DCHECK_GT(abort_info.time_to_abort, time_to_first_background); |
| base::TimeDelta background_abort_delta = |
| abort_info.time_to_abort - time_to_first_background; |
| // Consider this a foregrounded abort if it occurred within 100ms of a |
| // background. This is needed for closing some tabs, where the signal for |
| // background is often slightly ahead of the signal for close. |
| if (background_abort_delta.InMilliseconds() < 100) |
| return true; |
| return false; |
| } |
| |
| bool WasAbortedBeforeInteraction( |
| const page_load_metrics::PageAbortInfo& abort_info, |
| const base::Optional<base::TimeDelta>& time_to_interaction) { |
| // These conditions should be guaranteed by the call to |
| // WasAbortedInForeground, which is called before WasAbortedBeforeInteraction |
| // gets invoked. |
| DCHECK(abort_info.reason != PageAbortReason::ABORT_NONE); |
| |
| if (!time_to_interaction) |
| return true; |
| // For the case the abort is a reload or forward_back. Since pull to |
| // reload / forward_back is the most common user case such aborts being |
| // triggered, add a sanitization threshold here: if the first user |
| // interaction are received before a reload / forward_back in a very |
| // short time, treat the interaction as a gesture to perform the abort. |
| |
| // Why 1000ms? |
| // 1000ms is enough to perform a pull to reload / forward_back gesture. |
| // It's also too short a time for a user to consume any content |
| // revealed by the interaction. |
| if (abort_info.reason == PageAbortReason::ABORT_RELOAD || |
| abort_info.reason == PageAbortReason::ABORT_FORWARD_BACK) { |
| return time_to_interaction.value() + |
| base::TimeDelta::FromMilliseconds(1000) > |
| abort_info.time_to_abort; |
| } else { |
| return time_to_interaction > abort_info.time_to_abort; |
| } |
| } |
| |
| } // namespace |
| |
| FromGWSPageLoadMetricsLogger::FromGWSPageLoadMetricsLogger() {} |
| |
| void FromGWSPageLoadMetricsLogger::SetPreviouslyCommittedUrl(const GURL& url) { |
| previously_committed_url_is_search_results_ = |
| page_load_metrics::IsGoogleSearchResultUrl(url); |
| previously_committed_url_is_search_redirector_ = |
| page_load_metrics::IsGoogleSearchRedirectorUrl(url); |
| } |
| |
| void FromGWSPageLoadMetricsLogger::SetProvisionalUrl(const GURL& url) { |
| provisional_url_has_search_hostname_ = |
| page_load_metrics::IsGoogleSearchHostname(url); |
| } |
| |
| FromGWSPageLoadMetricsObserver::FromGWSPageLoadMetricsObserver() {} |
| |
| page_load_metrics::PageLoadMetricsObserver::ObservePolicy |
| FromGWSPageLoadMetricsObserver::OnStart( |
| content::NavigationHandle* navigation_handle, |
| const GURL& currently_committed_url, |
| bool started_in_foreground) { |
| logger_.SetPreviouslyCommittedUrl(currently_committed_url); |
| logger_.SetProvisionalUrl(navigation_handle->GetURL()); |
| return CONTINUE_OBSERVING; |
| } |
| |
| page_load_metrics::PageLoadMetricsObserver::ObservePolicy |
| FromGWSPageLoadMetricsObserver::OnCommit( |
| content::NavigationHandle* navigation_handle, |
| ukm::SourceId source_id) { |
| // We'd like to also check navigation_handle->HasUserGesture() here, however |
| // this signal is not carried forward for navigations that open links in new |
| // tabs, so we look only at PAGE_TRANSITION_LINK. Back/forward navigations |
| // that were originally navigated from a link will continue to report a core |
| // type of link, so to filter out back/forward navs, we also check that the |
| // page transition is a new navigation. |
| logger_.set_navigation_initiated_via_link( |
| ui::PageTransitionCoreTypeIs(navigation_handle->GetPageTransition(), |
| ui::PAGE_TRANSITION_LINK) && |
| ui::PageTransitionIsNewNavigation( |
| navigation_handle->GetPageTransition())); |
| |
| logger_.SetNavigationStart(navigation_handle->NavigationStart()); |
| logger_.OnCommit(navigation_handle, source_id); |
| return CONTINUE_OBSERVING; |
| } |
| |
| page_load_metrics::PageLoadMetricsObserver::ObservePolicy |
| FromGWSPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.FlushMetricsOnAppEnterBackground(timing, extra_info); |
| return STOP_OBSERVING; |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnDomContentLoadedEventStart( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnDomContentLoadedEventStart(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnLoadEventStart( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnLoadEventStart(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnFirstPaintInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnFirstPaintInPage(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnFirstImagePaintInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnFirstImagePaintInPage(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnFirstContentfulPaintInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnFirstContentfulPaintInPage(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnFirstInputInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnFirstInputInPage(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnParseStart( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnParseStart(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnParseStop( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnParseStop(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnComplete( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnComplete(timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnFailedProvisionalLoad( |
| const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnFailedProvisionalLoad(failed_load_info, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsObserver::OnUserInput( |
| const blink::WebInputEvent& event, |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| logger_.OnUserInput(event, timing, extra_info); |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnCommit( |
| content::NavigationHandle* navigation_handle, |
| ukm::SourceId source_id) { |
| if (!ShouldLogPostCommitMetrics(navigation_handle->GetURL())) |
| return; |
| ukm::builders::PageLoad_FromGoogleSearch(source_id).Record( |
| ukm::UkmRecorder::Get()); |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnComplete( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (!ShouldLogPostCommitMetrics(extra_info.url)) |
| return; |
| |
| page_load_metrics::PageAbortInfo abort_info = GetPageAbortInfo(extra_info); |
| if (!WasAbortedInForeground(extra_info, abort_info)) |
| return; |
| |
| // If we did not receive any timing IPCs from the render process, we can't |
| // know for certain if the page was truly aborted before paint, or if the |
| // abort happened before we received the IPC from the render process. Thus, we |
| // do not log aborts for these page loads. Tracked page loads that receive no |
| // timing IPCs are tracked via the ERR_NO_IPCS_RECEIVED error code in the |
| // PageLoad.Events.InternalError histogram, so we can keep track of how often |
| // this happens. |
| if (page_load_metrics::IsEmpty(timing)) |
| return; |
| |
| if (!timing.paint_timing->first_paint || |
| timing.paint_timing->first_paint >= abort_info.time_to_abort) { |
| LogCommittedAbortsBeforePaint(abort_info.reason, abort_info.time_to_abort); |
| } else if (WasAbortedBeforeInteraction(abort_info, |
| first_user_interaction_after_paint_)) { |
| LogAbortsAfterPaintBeforeInteraction(abort_info); |
| } |
| |
| LogForegroundDurations(timing, extra_info, base::TimeTicks()); |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnFailedProvisionalLoad( |
| const page_load_metrics::FailedProvisionalLoadInfo& failed_load_info, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (!ShouldLogFailedProvisionalLoadMetrics()) |
| return; |
| |
| page_load_metrics::PageAbortInfo abort_info = GetPageAbortInfo(extra_info); |
| if (!WasAbortedInForeground(extra_info, abort_info)) |
| return; |
| |
| LogProvisionalAborts(abort_info); |
| |
| LogForegroundDurations(page_load_metrics::mojom::PageLoadTiming(), extra_info, |
| base::TimeTicks()); |
| } |
| |
| bool FromGWSPageLoadMetricsLogger::ShouldLogFailedProvisionalLoadMetrics() { |
| // See comment in ShouldLogPostCommitMetrics above the call to |
| // page_load_metrics::IsGoogleSearchHostname for more info on this if test. |
| if (provisional_url_has_search_hostname_) |
| return false; |
| |
| return previously_committed_url_is_search_results_ || |
| previously_committed_url_is_search_redirector_; |
| } |
| |
| bool FromGWSPageLoadMetricsLogger::ShouldLogPostCommitMetrics(const GURL& url) { |
| DCHECK(!url.is_empty()); |
| |
| // If this page has a URL on a known google search hostname, then it may be a |
| // page associated with search (either a search results page, or a search |
| // redirector url), so we should not log stats. We could try to detect only |
| // the specific known search URLs here, and log navigations to other pages on |
| // the google search hostname (for example, a search for 'about google' |
| // includes a result for https://www.google.com/about/), however, we assume |
| // these cases are relatively uncommon, and we run the risk of logging metrics |
| // for some search redirector URLs. Thus we choose the more conservative |
| // approach of ignoring all urls on known search hostnames. |
| if (page_load_metrics::IsGoogleSearchHostname(url)) |
| return false; |
| |
| // We're only interested in tracking navigations (e.g. clicks) initiated via |
| // links. Note that the redirector will mask these, so don't enforce this if |
| // the navigation came from a redirect url. TODO(csharrison): Use this signal |
| // for provisional loads when the content APIs allow for it. |
| if (previously_committed_url_is_search_results_ && |
| navigation_initiated_via_link_) { |
| return true; |
| } |
| |
| // If the navigation was via the search redirector, then the information about |
| // whether the navigation was from a link would have been associated with the |
| // navigation to the redirector, and not included in the redirected |
| // navigation. Therefore, do not require link navigation this case. |
| return previously_committed_url_is_search_redirector_; |
| } |
| |
| bool FromGWSPageLoadMetricsLogger::ShouldLogForegroundEventAfterCommit( |
| const base::Optional<base::TimeDelta>& event, |
| const page_load_metrics::PageLoadExtraInfo& info) { |
| DCHECK(info.did_commit) |
| << "ShouldLogForegroundEventAfterCommit called without committed URL."; |
| return ShouldLogPostCommitMetrics(info.url) && |
| WasStartedInForegroundOptionalEventInForeground(event, info); |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnDomContentLoadedEventStart( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit( |
| timing.document_timing->dom_content_loaded_event_start, extra_info)) { |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSDomContentLoaded, |
| timing.document_timing->dom_content_loaded_event_start.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnLoadEventStart( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit( |
| timing.document_timing->load_event_start, extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSLoad, |
| timing.document_timing->load_event_start.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnFirstPaintInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit(timing.paint_timing->first_paint, |
| extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSFirstPaint, |
| timing.paint_timing->first_paint.value()); |
| } |
| first_paint_triggered_ = true; |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnFirstImagePaintInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit( |
| timing.paint_timing->first_image_paint, extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSFirstImagePaint, |
| timing.paint_timing->first_image_paint.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnFirstContentfulPaintInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit( |
| timing.paint_timing->first_contentful_paint, extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSFirstContentfulPaint, |
| timing.paint_timing->first_contentful_paint.value()); |
| |
| // If we have a foreground paint, we should have a foreground parse start, |
| // since paints can't happen until after parsing starts. |
| DCHECK(WasStartedInForegroundOptionalEventInForeground( |
| timing.parse_timing->parse_start, extra_info)); |
| PAGE_LOAD_HISTOGRAM( |
| internal::kHistogramFromGWSParseStartToFirstContentfulPaint, |
| timing.paint_timing->first_contentful_paint.value() - |
| timing.parse_timing->parse_start.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnFirstInputInPage( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit( |
| timing.interactive_timing->first_input_delay, extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSFirstInputDelay, |
| timing.interactive_timing->first_input_delay.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnParseStart( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit(timing.parse_timing->parse_start, |
| extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSParseStart, |
| timing.parse_timing->parse_start.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnParseStop( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (ShouldLogForegroundEventAfterCommit(timing.parse_timing->parse_stop, |
| extra_info)) { |
| PAGE_LOAD_HISTOGRAM(internal::kHistogramFromGWSParseDuration, |
| timing.parse_timing->parse_stop.value() - |
| timing.parse_timing->parse_start.value()); |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::OnUserInput( |
| const blink::WebInputEvent& event, |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| if (first_paint_triggered_ && !first_user_interaction_after_paint_) { |
| DCHECK(!navigation_start_.is_null()); |
| first_user_interaction_after_paint_ = |
| base::TimeTicks::Now() - navigation_start_; |
| } |
| } |
| |
| void FromGWSPageLoadMetricsLogger::FlushMetricsOnAppEnterBackground( |
| const page_load_metrics::mojom::PageLoadTiming& timing, |
| const page_load_metrics::PageLoadExtraInfo& extra_info) { |
| LogForegroundDurations(timing, extra_info, base::TimeTicks::Now()); |
| } |