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

Unified Diff: chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc

Issue 2904533002: Factor management of metrics updates into its own class. (Closed)
Patch Set: address comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698