Index: chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc |
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc |
similarity index 35% |
copy from chrome/browser/page_load_metrics/page_load_tracker.cc |
copy to chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc |
index dbbfff0fc38cd36c7d3043d3bc820b7fd8e1257c..1b9898e77c4872c9d25b8bde45c9e3424aeaada0 100644 |
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc |
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc |
@@ -1,114 +1,33 @@ |
-// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Copyright 2017 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/page_load_tracker.h" |
+#include "chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h" |
-#include <algorithm> |
#include <ostream> |
-#include <string> |
#include <utility> |
#include "base/logging.h" |
-#include "base/memory/ptr_util.h" |
#include "base/metrics/histogram_macros.h" |
+#include "base/optional.h" |
#include "chrome/browser/page_load_metrics/browser_page_track_decider.h" |
#include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface.h" |
#include "chrome/browser/page_load_metrics/page_load_metrics_util.h" |
-#include "chrome/browser/prerender/prerender_contents.h" |
-#include "chrome/common/page_load_metrics/page_load_timing.h" |
-#include "content/public/browser/navigation_details.h" |
+#include "chrome/browser/page_load_metrics/page_load_tracker.h" |
+#include "chrome/common/page_load_metrics/page_load_metrics_constants.h" |
#include "content/public/browser/navigation_handle.h" |
#include "content/public/browser/render_frame_host.h" |
-#include "content/public/browser/web_contents.h" |
-#include "content/public/browser/web_contents_observer.h" |
-#include "content/public/common/browser_side_navigation_policy.h" |
-#include "ui/base/page_transition_types.h" |
- |
-// This macro invokes the specified method on each observer, passing the |
-// variable length arguments as the method's arguments, and removes the observer |
-// from the list of observers if the given method returns STOP_OBSERVING. |
-#define INVOKE_AND_PRUNE_OBSERVERS(observers, Method, ...) \ |
- for (auto it = observers.begin(); it != observers.end();) { \ |
- if ((*it)->Method(__VA_ARGS__) == \ |
- PageLoadMetricsObserver::STOP_OBSERVING) { \ |
- it = observers.erase(it); \ |
- } else { \ |
- ++it; \ |
- } \ |
- } |
namespace page_load_metrics { |
namespace internal { |
-const char kErrorEvents[] = "PageLoad.Internal.ErrorCode"; |
-const char kAbortChainSizeReload[] = |
- "PageLoad.Internal.ProvisionalAbortChainSize.Reload"; |
-const char kAbortChainSizeForwardBack[] = |
- "PageLoad.Internal.ProvisionalAbortChainSize.ForwardBack"; |
-const char kAbortChainSizeNewNavigation[] = |
- "PageLoad.Internal.ProvisionalAbortChainSize.NewNavigation"; |
-const char kAbortChainSizeSameURL[] = |
- "PageLoad.Internal.ProvisionalAbortChainSize.SameURL"; |
-const char kAbortChainSizeNoCommit[] = |
- "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; |
-const char kClientRedirectFirstPaintToNavigation[] = |
- "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; |
-const char kClientRedirectWithoutPaint[] = |
- "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; |
-const char kPageLoadCompletedAfterAppBackground[] = |
- "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; |
-const char kPageLoadStartedInForeground[] = |
- "PageLoad.Internal.NavigationStartedInForeground"; |
-const char kPageLoadPrerender[] = "PageLoad.Internal.Prerender"; |
const char kPageLoadTimingStatus[] = "PageLoad.Internal.PageLoadTimingStatus"; |
const char kPageLoadTimingDispatchStatus[] = |
"PageLoad.Internal.PageLoadTimingStatus.AtTimingCallbackDispatch"; |
} // namespace internal |
-void RecordInternalError(InternalErrorLoadEvent event) { |
- UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); |
-} |
- |
-// TODO(csharrison): Add a case for client side redirects, which is what JS |
-// initiated window.location / window.history navigations get set to. |
-PageEndReason EndReasonForPageTransition(ui::PageTransition transition) { |
- if (transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) { |
- return END_CLIENT_REDIRECT; |
- } |
- if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD)) |
- return END_RELOAD; |
- if (transition & ui::PAGE_TRANSITION_FORWARD_BACK) |
- return END_FORWARD_BACK; |
- if (ui::PageTransitionIsNewNavigation(transition)) |
- return END_NEW_NAVIGATION; |
- NOTREACHED() |
- << "EndReasonForPageTransition received unexpected ui::PageTransition: " |
- << transition; |
- return END_OTHER; |
-} |
- |
-void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url) { |
- if (aborted_chain_size_same_url > 0) { |
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeSameURL, |
- aborted_chain_size_same_url); |
- } |
-} |
- |
-bool IsNavigationUserInitiated(content::NavigationHandle* handle) { |
- // TODO(crbug.com/617904): Browser initiated navigations should have |
- // HasUserGesture() set to true. In the meantime, we consider all |
- // browser-initiated navigations to be user initiated. |
- // |
- // TODO(crbug.com/637345): Some browser-initiated navigations incorrectly |
- // report that they are renderer-initiated. We will currently report that |
- // these navigations are not user initiated, when in fact they are user |
- // initiated. |
- return handle->HasUserGesture() || !handle->IsRendererInitiated(); |
-} |
- |
namespace { |
// Helper to allow use of Optional<> values in LOG() messages. |
@@ -316,251 +235,37 @@ void MaybeUpdateTimeDelta( |
*inout_existing_value = candidate_new_value; |
} |
-void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { |
- UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, |
- completed_after_background); |
-} |
- |
-void DispatchObserverTimingCallbacks( |
- PageLoadMetricsObserver* observer, |
- const mojom::PageLoadTiming& last_timing, |
- const mojom::PageLoadTiming& new_timing, |
- const mojom::PageLoadMetadata& last_metadata, |
- const PageLoadExtraInfo& extra_info) { |
- if (extra_info.main_frame_metadata.behavior_flags != |
- last_metadata.behavior_flags) |
- observer->OnLoadingBehaviorObserved(extra_info); |
- if (!last_timing.Equals(new_timing)) |
- observer->OnTimingUpdate(new_timing, extra_info); |
- if (new_timing.document_timing->dom_content_loaded_event_start && |
- !last_timing.document_timing->dom_content_loaded_event_start) |
- observer->OnDomContentLoadedEventStart(new_timing, extra_info); |
- if (new_timing.document_timing->load_event_start && |
- !last_timing.document_timing->load_event_start) |
- observer->OnLoadEventStart(new_timing, extra_info); |
- if (new_timing.document_timing->first_layout && |
- !last_timing.document_timing->first_layout) |
- observer->OnFirstLayout(new_timing, extra_info); |
- if (new_timing.paint_timing->first_paint && |
- !last_timing.paint_timing->first_paint) |
- observer->OnFirstPaintInPage(new_timing, extra_info); |
- if (new_timing.paint_timing->first_text_paint && |
- !last_timing.paint_timing->first_text_paint) |
- observer->OnFirstTextPaintInPage(new_timing, extra_info); |
- if (new_timing.paint_timing->first_image_paint && |
- !last_timing.paint_timing->first_image_paint) |
- observer->OnFirstImagePaintInPage(new_timing, extra_info); |
- if (new_timing.paint_timing->first_contentful_paint && |
- !last_timing.paint_timing->first_contentful_paint) |
- observer->OnFirstContentfulPaintInPage(new_timing, extra_info); |
- if (new_timing.paint_timing->first_meaningful_paint && |
- !last_timing.paint_timing->first_meaningful_paint) |
- observer->OnFirstMeaningfulPaintInMainFrameDocument(new_timing, extra_info); |
- if (new_timing.parse_timing->parse_start && |
- !last_timing.parse_timing->parse_start) |
- observer->OnParseStart(new_timing, extra_info); |
- if (new_timing.parse_timing->parse_stop && |
- !last_timing.parse_timing->parse_stop) |
- observer->OnParseStop(new_timing, extra_info); |
-} |
- |
} // namespace |
-PageLoadTracker::PageLoadTracker( |
- bool in_foreground, |
- PageLoadMetricsEmbedderInterface* embedder_interface, |
- const GURL& currently_committed_url, |
+PageLoadMetricsUpdateDispatcher::PageLoadMetricsUpdateDispatcher( |
+ PageLoadMetricsUpdateDispatcher::Client* client, |
content::NavigationHandle* navigation_handle, |
- UserInitiatedInfo user_initiated_info, |
- int aborted_chain_size, |
- int aborted_chain_size_same_url) |
- : did_stop_tracking_(false), |
- app_entered_background_(false), |
+ PageLoadMetricsEmbedderInterface* embedder_interface) |
+ : client_(client), |
+ embedder_interface_(embedder_interface), |
navigation_start_(navigation_handle->NavigationStart()), |
- url_(navigation_handle->GetURL()), |
- start_url_(navigation_handle->GetURL()), |
- did_commit_(false), |
- page_end_reason_(END_NONE), |
- page_end_user_initiated_info_(UserInitiatedInfo::NotUserInitiated()), |
- started_in_foreground_(in_foreground), |
- merged_page_timing_(CreatePageLoadTiming()), |
- last_dispatched_merged_page_timing_(CreatePageLoadTiming()), |
- last_dispatched_main_frame_metadata_(mojom::PageLoadMetadata::New()), |
- page_transition_(navigation_handle->GetPageTransition()), |
- user_initiated_info_(user_initiated_info), |
- aborted_chain_size_(aborted_chain_size), |
- aborted_chain_size_same_url_(aborted_chain_size_same_url), |
- embedder_interface_(embedder_interface) { |
- DCHECK(!navigation_handle->HasCommitted()); |
- embedder_interface_->RegisterObservers(this); |
- INVOKE_AND_PRUNE_OBSERVERS(observers_, OnStart, navigation_handle, |
- currently_committed_url, started_in_foreground_); |
- |
- UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadStartedInForeground, |
- started_in_foreground_); |
- const bool is_prerender = prerender::PrerenderContents::FromWebContents( |
- navigation_handle->GetWebContents()) != nullptr; |
- if (is_prerender) { |
- UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadPrerender, true); |
- } |
-} |
- |
-PageLoadTracker::~PageLoadTracker() { |
- if (app_entered_background_) { |
- RecordAppBackgroundPageLoadCompleted(true); |
- } |
- |
- if (did_stop_tracking_) |
- return; |
- |
- if (page_end_time_.is_null()) { |
- // page_end_time_ can be unset in some cases, such as when a navigation is |
- // aborted by a navigation that started before it. In these cases, set the |
- // end time to the current time. |
- RecordInternalError(ERR_NO_PAGE_LOAD_END_TIME); |
- NotifyPageEnd(END_OTHER, UserInitiatedInfo::NotUserInitiated(), |
- base::TimeTicks::Now(), true); |
- } |
- |
- if (!did_commit_) { |
- if (!failed_provisional_load_info_) |
- RecordInternalError(ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD); |
- |
- // Don't include any aborts that resulted in a new navigation, as the chain |
- // length will be included in the aborter PageLoadTracker. |
- if (page_end_reason_ != END_RELOAD && |
- page_end_reason_ != END_FORWARD_BACK && |
- page_end_reason_ != END_NEW_NAVIGATION) { |
- LogAbortChainHistograms(nullptr); |
- } |
- } else if (page_load_metrics::IsEmpty(*merged_page_timing_)) { |
- RecordInternalError(ERR_NO_IPCS_RECEIVED); |
- } |
- |
- const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
- for (const auto& observer : observers_) { |
- if (failed_provisional_load_info_) { |
- observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info); |
- } else if (did_commit_) { |
- observer->OnComplete(*merged_page_timing_, info); |
- } |
- } |
-} |
- |
-void PageLoadTracker::LogAbortChainHistograms( |
- content::NavigationHandle* final_navigation) { |
- if (aborted_chain_size_ == 0) |
- return; |
- // Note that this could be broken out by this navigation's abort type, if more |
- // granularity is needed. Add one to the chain size to count the current |
- // navigation. In the other cases, the current navigation is the final |
- // navigation (which commits). |
- if (!final_navigation) { |
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeNoCommit, |
- aborted_chain_size_ + 1); |
- LogAbortChainSameURLHistogram(aborted_chain_size_same_url_ + 1); |
- return; |
- } |
- |
- // The following is only executed for committing trackers. |
- DCHECK(did_commit_); |
- |
- // Note that histograms could be separated out by this commit's transition |
- // type, but for simplicity they will all be bucketed together. |
- LogAbortChainSameURLHistogram(aborted_chain_size_same_url_); |
+ current_merged_page_timing_(CreatePageLoadTiming()), |
+ pending_merged_page_timing_(CreatePageLoadTiming()), |
+ main_frame_metadata_(mojom::PageLoadMetadata::New()), |
+ subframe_metadata_(mojom::PageLoadMetadata::New()) {} |
- ui::PageTransition committed_transition = |
- final_navigation->GetPageTransition(); |
- switch (EndReasonForPageTransition(committed_transition)) { |
- case END_RELOAD: |
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeReload, |
- aborted_chain_size_); |
- return; |
- case END_FORWARD_BACK: |
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeForwardBack, |
- aborted_chain_size_); |
- return; |
- // TODO(csharrison): Refactor this code so it is based on the WillStart* |
- // code path instead of the committed load code path. Then, for every abort |
- // chain, log a histogram of the counts of each of these metrics. For now, |
- // merge client redirects with new navigations, which was (basically) the |
- // previous behavior. |
- case END_CLIENT_REDIRECT: |
- case END_NEW_NAVIGATION: |
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeNewNavigation, |
- aborted_chain_size_); |
- return; |
- default: |
- NOTREACHED() |
- << "LogAbortChainHistograms received unexpected ui::PageTransition: " |
- << committed_transition; |
- return; |
- } |
-} |
- |
-void PageLoadTracker::WebContentsHidden() { |
- // Only log the first time we background in a given page load. |
- if (background_time_.is_null()) { |
- // Make sure we either started in the foreground and haven't been |
- // foregrounded yet, or started in the background and have already been |
- // foregrounded. |
- DCHECK_EQ(started_in_foreground_, foreground_time_.is_null()); |
- background_time_ = base::TimeTicks::Now(); |
- ClampBrowserTimestampIfInterProcessTimeTickSkew(&background_time_); |
- } |
- const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
- INVOKE_AND_PRUNE_OBSERVERS(observers_, OnHidden, *merged_page_timing_, info); |
-} |
- |
-void PageLoadTracker::WebContentsShown() { |
- // Only log the first time we foreground in a given page load. |
- if (foreground_time_.is_null()) { |
- // Make sure we either started in the background and haven't been |
- // backgrounded yet, or started in the foreground and have already been |
- // backgrounded. |
- DCHECK_NE(started_in_foreground_, background_time_.is_null()); |
- foreground_time_ = base::TimeTicks::Now(); |
- ClampBrowserTimestampIfInterProcessTimeTickSkew(&foreground_time_); |
- } |
- |
- INVOKE_AND_PRUNE_OBSERVERS(observers_, OnShown); |
-} |
- |
-void PageLoadTracker::WillProcessNavigationResponse( |
- content::NavigationHandle* navigation_handle) { |
- DCHECK(!navigation_request_id_.has_value()); |
- navigation_request_id_ = navigation_handle->GetGlobalRequestID(); |
- DCHECK(navigation_request_id_.value() != content::GlobalRequestID()); |
-} |
- |
-void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) { |
- did_commit_ = true; |
- url_ = navigation_handle->GetURL(); |
- // Some transitions (like CLIENT_REDIRECT) are only known at commit time. |
- page_transition_ = navigation_handle->GetPageTransition(); |
- user_initiated_info_.user_gesture = navigation_handle->HasUserGesture(); |
- |
- INVOKE_AND_PRUNE_OBSERVERS( |
- observers_, ShouldObserveMimeType, |
- navigation_handle->GetWebContents()->GetContentsMimeType()); |
- |
- INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle); |
- LogAbortChainHistograms(navigation_handle); |
-} |
+PageLoadMetricsUpdateDispatcher::~PageLoadMetricsUpdateDispatcher() {} |
-void PageLoadTracker::DidCommitSameDocumentNavigation( |
- content::NavigationHandle* navigation_handle) { |
- for (const auto& observer : observers_) { |
- observer->OnCommitSameDocumentNavigation(navigation_handle); |
+void PageLoadMetricsUpdateDispatcher::UpdateMetrics( |
+ content::RenderFrameHost* render_frame_host, |
+ const mojom::PageLoadTiming& new_timing, |
+ const mojom::PageLoadMetadata& new_metadata) { |
+ if (render_frame_host->GetParent() == nullptr) { |
+ UpdateMainFrameMetadata(new_metadata); |
+ UpdateMainFrameTiming(new_timing); |
+ } else { |
+ UpdateSubFrameMetadata(new_metadata); |
+ UpdateSubFrameTiming(render_frame_host, new_timing); |
} |
} |
-void PageLoadTracker::DidFinishSubFrameNavigation( |
+void PageLoadMetricsUpdateDispatcher::DidFinishSubFrameNavigation( |
content::NavigationHandle* navigation_handle) { |
- for (const auto& observer : observers_) { |
- observer->OnDidFinishSubFrameNavigation(navigation_handle); |
- } |
- |
if (!navigation_handle->HasCommitted()) |
return; |
@@ -585,60 +290,9 @@ void PageLoadTracker::DidFinishSubFrameNavigation( |
navigation_handle->GetFrameTreeNodeId(), navigation_delta)); |
} |
-void PageLoadTracker::FailedProvisionalLoad( |
- content::NavigationHandle* navigation_handle, |
- base::TimeTicks failed_load_time) { |
- DCHECK(!failed_provisional_load_info_); |
- failed_provisional_load_info_.reset(new FailedProvisionalLoadInfo( |
- failed_load_time - navigation_handle->NavigationStart(), |
- navigation_handle->GetNetErrorCode())); |
-} |
- |
-void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) { |
- url_ = navigation_handle->GetURL(); |
- INVOKE_AND_PRUNE_OBSERVERS(observers_, OnRedirect, navigation_handle); |
-} |
- |
-void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) { |
- input_tracker_.OnInputEvent(event); |
- for (const auto& observer : observers_) { |
- observer->OnUserInput(event); |
- } |
-} |
- |
-void PageLoadTracker::FlushMetricsOnAppEnterBackground() { |
- if (!app_entered_background_) { |
- RecordAppBackgroundPageLoadCompleted(false); |
- app_entered_background_ = true; |
- } |
- |
- const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
- INVOKE_AND_PRUNE_OBSERVERS(observers_, FlushMetricsOnAppEnterBackground, |
- *merged_page_timing_, info); |
-} |
- |
-void PageLoadTracker::NotifyClientRedirectTo( |
- const PageLoadTracker& destination) { |
- if (merged_page_timing_->paint_timing->first_paint) { |
- base::TimeTicks first_paint_time = |
- navigation_start() + |
- merged_page_timing_->paint_timing->first_paint.value(); |
- base::TimeDelta first_paint_to_navigation; |
- if (destination.navigation_start() > first_paint_time) |
- first_paint_to_navigation = |
- destination.navigation_start() - first_paint_time; |
- PAGE_LOAD_HISTOGRAM(internal::kClientRedirectFirstPaintToNavigation, |
- first_paint_to_navigation); |
- } else { |
- UMA_HISTOGRAM_BOOLEAN(internal::kClientRedirectWithoutPaint, true); |
- } |
-} |
- |
-void PageLoadTracker::UpdateSubFrameTiming( |
+void PageLoadMetricsUpdateDispatcher::UpdateSubFrameTiming( |
content::RenderFrameHost* render_frame_host, |
- const mojom::PageLoadTiming& new_timing, |
- const mojom::PageLoadMetadata& new_metadata) { |
- UpdateSubFrameMetadata(new_metadata); |
+ const mojom::PageLoadTiming& new_timing) { |
const auto it = subframe_navigation_start_offset_.find( |
render_frame_host->GetFrameTreeNodeId()); |
if (it == subframe_navigation_start_offset_.end()) { |
@@ -653,67 +307,57 @@ void PageLoadTracker::UpdateSubFrameTiming( |
DispatchTimingUpdates(); |
} |
-void PageLoadTracker::MergePaintTiming( |
+void PageLoadMetricsUpdateDispatcher::MergePaintTiming( |
base::TimeDelta navigation_start_offset, |
const mojom::PaintTiming& new_paint_timing, |
bool is_main_frame) { |
- MaybeUpdateTimeDelta(&merged_page_timing_->paint_timing->first_paint, |
+ MaybeUpdateTimeDelta(&pending_merged_page_timing_->paint_timing->first_paint, |
navigation_start_offset, new_paint_timing.first_paint); |
- MaybeUpdateTimeDelta(&merged_page_timing_->paint_timing->first_text_paint, |
- navigation_start_offset, |
- new_paint_timing.first_text_paint); |
- MaybeUpdateTimeDelta(&merged_page_timing_->paint_timing->first_image_paint, |
- navigation_start_offset, |
- new_paint_timing.first_image_paint); |
MaybeUpdateTimeDelta( |
- &merged_page_timing_->paint_timing->first_contentful_paint, |
+ &pending_merged_page_timing_->paint_timing->first_text_paint, |
+ navigation_start_offset, new_paint_timing.first_text_paint); |
+ MaybeUpdateTimeDelta( |
+ &pending_merged_page_timing_->paint_timing->first_image_paint, |
+ navigation_start_offset, new_paint_timing.first_image_paint); |
+ MaybeUpdateTimeDelta( |
+ &pending_merged_page_timing_->paint_timing->first_contentful_paint, |
navigation_start_offset, new_paint_timing.first_contentful_paint); |
if (is_main_frame) { |
// first meaningful paint is only tracked in the main frame. |
- merged_page_timing_->paint_timing->first_meaningful_paint = |
+ pending_merged_page_timing_->paint_timing->first_meaningful_paint = |
new_paint_timing.first_meaningful_paint; |
} |
} |
-void PageLoadTracker::UpdateSubFrameMetadata( |
+void PageLoadMetricsUpdateDispatcher::UpdateSubFrameMetadata( |
const mojom::PageLoadMetadata& subframe_metadata) { |
// Merge the subframe loading behavior flags with any we've already observed, |
// possibly from other subframes. |
const int last_subframe_loading_behavior_flags = |
- subframe_metadata_.behavior_flags; |
- subframe_metadata_.behavior_flags |= subframe_metadata.behavior_flags; |
- if (last_subframe_loading_behavior_flags == subframe_metadata_.behavior_flags) |
+ subframe_metadata_->behavior_flags; |
+ subframe_metadata_->behavior_flags |= subframe_metadata.behavior_flags; |
+ if (last_subframe_loading_behavior_flags == |
+ subframe_metadata_->behavior_flags) |
return; |
- PageLoadExtraInfo extra_info(ComputePageLoadExtraInfo()); |
- for (const auto& observer : observers_) { |
- observer->OnLoadingBehaviorObserved(extra_info); |
- } |
+ client_->OnSubframeMetadataChanged(); |
} |
-void PageLoadTracker::UpdateTiming( |
- const mojom::PageLoadTiming& new_timing, |
- const mojom::PageLoadMetadata& new_metadata) { |
+void PageLoadMetricsUpdateDispatcher::UpdateMainFrameTiming( |
+ const mojom::PageLoadTiming& new_timing) { |
// Throw away IPCs that are not relevant to the current navigation. |
// Two timing structures cannot refer to the same navigation if they indicate |
// that a navigation started at different times, so a new timing struct with a |
// different start time from an earlier struct is considered invalid. |
const bool valid_timing_descendent = |
- merged_page_timing_->navigation_start.is_null() || |
- merged_page_timing_->navigation_start == new_timing.navigation_start; |
+ pending_merged_page_timing_->navigation_start.is_null() || |
+ pending_merged_page_timing_->navigation_start == |
+ new_timing.navigation_start; |
if (!valid_timing_descendent) { |
RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING_DESCENDENT); |
return; |
} |
- // Ensure flags sent previously are still present in the new metadata fields. |
- const bool valid_behavior_descendent = |
- (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == |
- main_frame_metadata_.behavior_flags; |
- if (!valid_behavior_descendent) { |
- RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); |
- return; |
- } |
internal::PageLoadTimingStatus status = IsValidPageLoadTiming(new_timing); |
UMA_HISTOGRAM_ENUMERATION(internal::kPageLoadTimingStatus, status, |
internal::LAST_PAGE_LOAD_TIMING_STATUS); |
@@ -722,38 +366,40 @@ void PageLoadTracker::UpdateTiming( |
return; |
} |
- DCHECK(did_commit_); // OnCommit() must be called first. |
- // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() |
- // must be called before DispatchObserverTimingCallbacks, but its |
- // implementation depends on the state of main_frame_metadata_, so we need |
- // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. |
- // Thus, we make a copy of timing here, update merged_page_timing_ and |
- // main_frame_metadata_, and then proceed to dispatch the observer timing |
- // callbacks. |
- const mojom::PaintTimingPtr last_paint_timing = |
- std::move(merged_page_timing_->paint_timing); |
- |
- // Update the merged_page_timing_, making sure to merge the previously |
+ mojom::PaintTimingPtr last_paint_timing = |
+ std::move(pending_merged_page_timing_->paint_timing); |
+ // Update the pending_merged_page_timing_, making sure to merge the previously |
// observed |paint_timing|, which is tracked across all frames in the page. |
- merged_page_timing_ = new_timing.Clone(); |
- merged_page_timing_->paint_timing = last_paint_timing.Clone(); |
+ pending_merged_page_timing_ = new_timing.Clone(); |
+ pending_merged_page_timing_->paint_timing = std::move(last_paint_timing); |
MergePaintTiming(base::TimeDelta(), *new_timing.paint_timing, |
true /* is_main_frame */); |
- main_frame_metadata_ = new_metadata; |
- |
DispatchTimingUpdates(); |
} |
-void PageLoadTracker::DispatchTimingUpdates() { |
- if (last_dispatched_merged_page_timing_->Equals(*merged_page_timing_) && |
- last_dispatched_main_frame_metadata_->Equals(main_frame_metadata_)) { |
+void PageLoadMetricsUpdateDispatcher::UpdateMainFrameMetadata( |
+ const mojom::PageLoadMetadata& new_metadata) { |
+ if (main_frame_metadata_->Equals(new_metadata)) |
+ return; |
+ |
+ // Ensure flags sent previously are still present in the new metadata fields. |
+ const bool valid_behavior_descendent = |
+ (main_frame_metadata_->behavior_flags & new_metadata.behavior_flags) == |
+ main_frame_metadata_->behavior_flags; |
+ if (!valid_behavior_descendent) { |
+ RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); |
return; |
} |
- if (merged_page_timing_->paint_timing->first_paint) { |
- if (!merged_page_timing_->parse_timing->parse_start || |
- !merged_page_timing_->document_timing->first_layout) { |
+ main_frame_metadata_ = new_metadata.Clone(); |
+ client_->OnMainFrameMetadataChanged(); |
+} |
+ |
+void PageLoadMetricsUpdateDispatcher::DispatchTimingUpdates() { |
+ if (pending_merged_page_timing_->paint_timing->first_paint) { |
+ if (!pending_merged_page_timing_->parse_timing->parse_start || |
+ !pending_merged_page_timing_->document_timing->first_layout) { |
// When merging paint events across frames, we can sometimes encounter |
// cases where we've received a first paint event for a child frame before |
// receiving required earlier events in the main frame, due to buffering |
@@ -768,210 +414,16 @@ void PageLoadTracker::DispatchTimingUpdates() { |
} |
} |
+ if (current_merged_page_timing_->Equals(*pending_merged_page_timing_)) |
+ return; |
+ current_merged_page_timing_ = pending_merged_page_timing_->Clone(); |
+ |
internal::PageLoadTimingStatus status = |
- IsValidPageLoadTiming(*merged_page_timing_); |
+ IsValidPageLoadTiming(*pending_merged_page_timing_); |
UMA_HISTOGRAM_ENUMERATION(internal::kPageLoadTimingDispatchStatus, status, |
internal::LAST_PAGE_LOAD_TIMING_STATUS); |
- const PageLoadExtraInfo info = ComputePageLoadExtraInfo(); |
- for (const auto& observer : observers_) { |
- DispatchObserverTimingCallbacks( |
- observer.get(), *last_dispatched_merged_page_timing_, |
- *merged_page_timing_, *last_dispatched_main_frame_metadata_, info); |
- } |
- last_dispatched_merged_page_timing_ = merged_page_timing_->Clone(); |
- last_dispatched_main_frame_metadata_ = main_frame_metadata_.Clone(); |
-} |
- |
-void PageLoadTracker::OnStartedResource( |
- const ExtraRequestStartInfo& extra_request_start_info) { |
- for (const auto& observer : observers_) { |
- observer->OnStartedResource(extra_request_start_info); |
- } |
-} |
- |
-void PageLoadTracker::OnLoadedResource( |
- const ExtraRequestCompleteInfo& extra_request_complete_info) { |
- for (const auto& observer : observers_) { |
- observer->OnLoadedResource(extra_request_complete_info); |
- } |
-} |
- |
-void PageLoadTracker::StopTracking() { |
- did_stop_tracking_ = true; |
- observers_.clear(); |
-} |
- |
-void PageLoadTracker::AddObserver( |
- std::unique_ptr<PageLoadMetricsObserver> observer) { |
- observers_.push_back(std::move(observer)); |
-} |
- |
-void PageLoadTracker::ClampBrowserTimestampIfInterProcessTimeTickSkew( |
- base::TimeTicks* event_time) { |
- DCHECK(event_time != nullptr); |
- // Windows 10 GCE bot non-deterministically failed because TimeTicks::Now() |
- // called in the browser process e.g. commit_time was less than |
- // navigation_start_ that was populated in the renderer process because the |
- // clock was not system-wide monotonic. |
- // Note that navigation_start_ can also be set in the browser process in |
- // some cases and in those cases event_time should never be < |
- // navigation_start_. If it is due to a code error and it gets clamped in this |
- // function, on high resolution systems it should lead to a dcheck failure. |
- |
- // TODO(shivanisha): Currently IsHighResolution is the best way to check |
- // if the clock is system-wide monotonic. However IsHighResolution |
- // does a broader check to see if the clock in use is high resolution |
- // which also implies it is system-wide monotonic (on Windows). |
- if (base::TimeTicks::IsHighResolution()) { |
- DCHECK(event_time->is_null() || *event_time >= navigation_start_); |
- return; |
- } |
- |
- if (!event_time->is_null() && *event_time < navigation_start_) { |
- RecordInternalError(ERR_INTER_PROCESS_TIME_TICK_SKEW); |
- *event_time = navigation_start_; |
- } |
-} |
- |
-PageLoadExtraInfo PageLoadTracker::ComputePageLoadExtraInfo() { |
- base::Optional<base::TimeDelta> first_background_time; |
- base::Optional<base::TimeDelta> first_foreground_time; |
- base::Optional<base::TimeDelta> page_end_time; |
- |
- if (!background_time_.is_null()) { |
- DCHECK_GE(background_time_, navigation_start_); |
- first_background_time = background_time_ - navigation_start_; |
- } |
- |
- if (!foreground_time_.is_null()) { |
- DCHECK_GE(foreground_time_, navigation_start_); |
- first_foreground_time = foreground_time_ - navigation_start_; |
- } |
- |
- if (page_end_reason_ != END_NONE) { |
- DCHECK_GE(page_end_time_, navigation_start_); |
- page_end_time = page_end_time_ - navigation_start_; |
- } else { |
- DCHECK(page_end_time_.is_null()); |
- } |
- |
- // page_end_reason_ == END_NONE implies page_end_user_initiated_info_ is not |
- // user initiated. |
- DCHECK(page_end_reason_ != END_NONE || |
- (!page_end_user_initiated_info_.browser_initiated && |
- !page_end_user_initiated_info_.user_gesture && |
- !page_end_user_initiated_info_.user_input_event)); |
- return PageLoadExtraInfo( |
- navigation_start_, first_background_time, first_foreground_time, |
- started_in_foreground_, user_initiated_info_, url(), start_url_, |
- did_commit_, page_end_reason_, page_end_user_initiated_info_, |
- page_end_time, main_frame_metadata_, subframe_metadata_); |
-} |
- |
-bool PageLoadTracker::HasMatchingNavigationRequestID( |
- const content::GlobalRequestID& request_id) const { |
- DCHECK(request_id != content::GlobalRequestID()); |
- return navigation_request_id_.has_value() && |
- navigation_request_id_.value() == request_id; |
-} |
- |
-void PageLoadTracker::NotifyPageEnd(PageEndReason page_end_reason, |
- UserInitiatedInfo user_initiated_info, |
- base::TimeTicks timestamp, |
- bool is_certainly_browser_timestamp) { |
- DCHECK_NE(page_end_reason, END_NONE); |
- // Use UpdatePageEnd to update an already notified PageLoadTracker. |
- if (page_end_reason_ != END_NONE) |
- return; |
- |
- UpdatePageEndInternal(page_end_reason, user_initiated_info, timestamp, |
- is_certainly_browser_timestamp); |
-} |
- |
-void PageLoadTracker::UpdatePageEnd(PageEndReason page_end_reason, |
- UserInitiatedInfo user_initiated_info, |
- base::TimeTicks timestamp, |
- bool is_certainly_browser_timestamp) { |
- DCHECK_NE(page_end_reason, END_NONE); |
- DCHECK_NE(page_end_reason, END_OTHER); |
- DCHECK_EQ(page_end_reason_, END_OTHER); |
- DCHECK(!page_end_time_.is_null()); |
- if (page_end_time_.is_null() || page_end_reason_ != END_OTHER) |
- return; |
- |
- // For some aborts (e.g. navigations), the initiated timestamp can be earlier |
- // than the timestamp that aborted the load. Taking the minimum gives the |
- // closest user initiated time known. |
- UpdatePageEndInternal(page_end_reason, user_initiated_info, |
- std::min(page_end_time_, timestamp), |
- is_certainly_browser_timestamp); |
-} |
- |
-bool PageLoadTracker::IsLikelyProvisionalAbort( |
- base::TimeTicks abort_cause_time) const { |
- // Note that |abort_cause_time - page_end_time_| can be negative. |
- return page_end_reason_ == END_OTHER && |
- (abort_cause_time - page_end_time_).InMilliseconds() < 100; |
-} |
- |
-bool PageLoadTracker::MatchesOriginalNavigation( |
- content::NavigationHandle* navigation_handle) { |
- // Neither navigation should have committed. |
- DCHECK(!navigation_handle->HasCommitted()); |
- DCHECK(!did_commit_); |
- return navigation_handle->GetURL() == start_url_; |
-} |
- |
-void PageLoadTracker::UpdatePageEndInternal( |
- PageEndReason page_end_reason, |
- UserInitiatedInfo user_initiated_info, |
- base::TimeTicks timestamp, |
- bool is_certainly_browser_timestamp) { |
- // When a provisional navigation commits, that navigation's start time is |
- // interpreted as the abort time for other provisional loads in the tab. |
- // However, this only makes sense if the committed load started after the |
- // aborted provisional loads started. Thus we ignore cases where the committed |
- // load started before the aborted provisional load, as this would result in |
- // recording a negative time-to-abort. The real issue here is that we have to |
- // infer the cause of aborts. It would be better if the navigation code could |
- // instead report the actual cause of an aborted navigation. See crbug/571647 |
- // for details. |
- if (timestamp < navigation_start_) { |
- RecordInternalError(ERR_END_BEFORE_NAVIGATION_START); |
- page_end_reason_ = END_NONE; |
- page_end_time_ = base::TimeTicks(); |
- return; |
- } |
- page_end_reason_ = page_end_reason; |
- page_end_time_ = timestamp; |
- // A client redirect can never be user initiated. Due to the way Blink |
- // implements user gesture tracking, where all events that occur within 1 |
- // second after a user interaction are considered to be triggered by user |
- // activation (based on HTML spec: |
- // https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation), |
- // these navs may sometimes be reported as user initiated by Blink. Thus, we |
- // explicitly filter these types of aborts out when deciding if the abort was |
- // user initiated. |
- if (page_end_reason != END_CLIENT_REDIRECT) |
- page_end_user_initiated_info_ = user_initiated_info; |
- |
- if (is_certainly_browser_timestamp) { |
- ClampBrowserTimestampIfInterProcessTimeTickSkew(&page_end_time_); |
- } |
-} |
- |
-void PageLoadTracker::MediaStartedPlaying( |
- const content::WebContentsObserver::MediaPlayerInfo& video_type, |
- bool is_in_main_frame) { |
- for (const auto& observer : observers_) |
- observer->MediaStartedPlaying(video_type, is_in_main_frame); |
-} |
- |
-void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay, |
- base::TimeDelta actual_delay) { |
- for (const auto& observer : observers_) |
- observer->OnNavigationDelayComplete(scheduled_delay, actual_delay); |
+ client_->OnTimingChanged(); |
} |
} // namespace page_load_metrics |