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

Unified Diff: components/page_load_metrics/browser/metrics_web_contents_observer.cc

Issue 2177743002: Migrate page_load_metrics out of components. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: migrate page_load_metrics_messages to common message generator Created 4 years, 5 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: components/page_load_metrics/browser/metrics_web_contents_observer.cc
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
deleted file mode 100644
index 0c586b7c65e6a9842bfa0a0f6fced893633f73f1..0000000000000000000000000000000000000000
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ /dev/null
@@ -1,960 +0,0 @@
-// 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 "components/page_load_metrics/browser/metrics_web_contents_observer.h"
-
-#include <algorithm>
-#include <ostream>
-#include <string>
-#include <utility>
-
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/user_metrics.h"
-#include "components/page_load_metrics/browser/page_load_metrics_util.h"
-#include "components/page_load_metrics/common/page_load_metrics_messages.h"
-#include "components/page_load_metrics/common/page_load_timing.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_details.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "ipc/ipc_message.h"
-#include "ipc/ipc_message_macros.h"
-#include "ui/base/page_transition_types.h"
-
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(
- page_load_metrics::MetricsWebContentsObserver);
-
-namespace page_load_metrics {
-
-namespace internal {
-
-const char kErrorEvents[] = "PageLoad.Events.InternalError";
-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 kCommitToCompleteNoTimingIPCs[] =
- "PageLoad.Internal.CommitToComplete.NoTimingIPCs";
-
-} // namespace internal
-
-namespace {
-
-// Helper to allow use of Optional<> values in LOG() messages.
-std::ostream& operator<<(std::ostream& os,
- const base::Optional<base::TimeDelta>& opt) {
- if (opt)
- os << opt.value();
- else
- os << "(unset)";
- return os;
-}
-
-// If second is non-zero, first must also be non-zero and less than or equal to
-// second.
-bool EventsInOrder(const base::Optional<base::TimeDelta>& first,
- const base::Optional<base::TimeDelta>& second) {
- if (!second) {
- return true;
- }
- return first && first <= second;
-}
-
-bool IsValidPageLoadTiming(const PageLoadTiming& timing) {
- if (timing.IsEmpty())
- return false;
-
- // If we have a non-empty timing, it should always have a navigation start.
- if (timing.navigation_start.is_null()) {
- NOTREACHED() << "Received null navigation_start.";
- return false;
- }
-
- // Verify proper ordering between the various timings.
-
- if (!EventsInOrder(timing.response_start, timing.dom_loading)) {
- // We sometimes get a zero response_start with a non-zero DOM loading. See
- // crbug.com/590212.
- DLOG(ERROR) << "Invalid response_start " << timing.response_start
- << " for dom_loading " << timing.dom_loading;
- return false;
- }
-
- if (!EventsInOrder(timing.response_start, timing.parse_start)) {
- // We sometimes get a zero response_start with a non-zero parse start. See
- // crbug.com/590212.
- DLOG(ERROR) << "Invalid response_start " << timing.response_start
- << " for parse_start " << timing.parse_start;
- return false;
- }
-
- if (!EventsInOrder(timing.parse_start, timing.parse_stop)) {
- NOTREACHED() << "Invalid parse_start " << timing.parse_start
- << " for parse_stop " << timing.parse_stop;
- return false;
- }
-
- if (timing.parse_stop) {
- const base::TimeDelta parse_duration =
- timing.parse_stop.value() - timing.parse_start.value();
- if (timing.parse_blocked_on_script_load_duration > parse_duration) {
- NOTREACHED() << "Invalid parse_blocked_on_script_load_duration "
- << timing.parse_blocked_on_script_load_duration
- << " for parse duration " << parse_duration;
- return false;
- }
- }
-
- if (timing.parse_blocked_on_script_load_from_document_write_duration >
- timing.parse_blocked_on_script_load_duration) {
- NOTREACHED()
- << "Invalid parse_blocked_on_script_load_from_document_write_duration "
- << timing.parse_blocked_on_script_load_from_document_write_duration
- << " for parse_blocked_on_script_load_duration "
- << timing.parse_blocked_on_script_load_duration;
- return false;
- }
-
- if (!EventsInOrder(timing.dom_loading,
- timing.dom_content_loaded_event_start)) {
- NOTREACHED() << "Invalid dom_loading " << timing.dom_loading
- << " for dom_content_loaded_event_start "
- << timing.dom_content_loaded_event_start;
- return false;
- }
-
- if (!EventsInOrder(timing.dom_content_loaded_event_start,
- timing.load_event_start)) {
- NOTREACHED() << "Invalid dom_content_loaded_event_start "
- << timing.dom_content_loaded_event_start
- << " for load_event_start " << timing.load_event_start;
- return false;
- }
-
- if (!EventsInOrder(timing.dom_loading, timing.first_layout)) {
- NOTREACHED() << "Invalid dom_loading " << timing.dom_loading
- << " for first_layout " << timing.first_layout;
- return false;
- }
-
- if (!EventsInOrder(timing.first_layout, timing.first_paint)) {
- NOTREACHED() << "Invalid first_layout " << timing.first_layout
- << " for first_paint " << timing.first_paint;
- return false;
- }
-
- if (!EventsInOrder(timing.first_paint, timing.first_text_paint)) {
- NOTREACHED() << "Invalid first_paint " << timing.first_paint
- << " for first_text_paint " << timing.first_text_paint;
- return false;
- }
-
- if (!EventsInOrder(timing.first_paint, timing.first_image_paint)) {
- NOTREACHED() << "Invalid first_paint " << timing.first_paint
- << " for first_image_paint " << timing.first_image_paint;
- return false;
- }
-
- if (!EventsInOrder(timing.first_paint, timing.first_contentful_paint)) {
- NOTREACHED() << "Invalid first_paint " << timing.first_paint
- << " for first_contentful_paint "
- << timing.first_contentful_paint;
- return false;
- }
-
- return true;
-}
-
-void RecordInternalError(InternalErrorLoadEvent event) {
- UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY);
-}
-
-UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) {
- if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD))
- return ABORT_RELOAD;
- if (transition & ui::PAGE_TRANSITION_FORWARD_BACK)
- return ABORT_FORWARD_BACK;
- if (ui::PageTransitionIsNewNavigation(transition))
- return ABORT_NEW_NAVIGATION;
- NOTREACHED()
- << "AbortTypeForPageTransition received unexpected ui::PageTransition: "
- << transition;
- return ABORT_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);
- }
-}
-
-void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
- const PageLoadTiming& last_timing,
- const PageLoadTiming& new_timing,
- const PageLoadMetadata& last_metadata,
- const PageLoadExtraInfo& extra_info) {
- if (last_timing != new_timing)
- observer->OnTimingUpdate(new_timing, extra_info);
- if (new_timing.dom_content_loaded_event_start &&
- !last_timing.dom_content_loaded_event_start)
- observer->OnDomContentLoadedEventStart(new_timing, extra_info);
- if (new_timing.load_event_start && !last_timing.load_event_start)
- observer->OnLoadEventStart(new_timing, extra_info);
- if (new_timing.first_layout && !last_timing.first_layout)
- observer->OnFirstLayout(new_timing, extra_info);
- if (new_timing.first_paint && !last_timing.first_paint)
- observer->OnFirstPaint(new_timing, extra_info);
- if (new_timing.first_text_paint && !last_timing.first_text_paint)
- observer->OnFirstTextPaint(new_timing, extra_info);
- if (new_timing.first_image_paint && !last_timing.first_image_paint)
- observer->OnFirstImagePaint(new_timing, extra_info);
- if (new_timing.first_contentful_paint && !last_timing.first_contentful_paint)
- observer->OnFirstContentfulPaint(new_timing, extra_info);
- if (new_timing.parse_start && !last_timing.parse_start)
- observer->OnParseStart(new_timing, extra_info);
- if (new_timing.parse_stop && !last_timing.parse_stop)
- observer->OnParseStop(new_timing, extra_info);
- if (extra_info.metadata.behavior_flags != last_metadata.behavior_flags)
- observer->OnLoadingBehaviorObserved(extra_info);
-}
-
-} // namespace
-
-PageLoadTracker::PageLoadTracker(
- bool in_foreground,
- PageLoadMetricsEmbedderInterface* embedder_interface,
- const GURL& currently_committed_url,
- content::NavigationHandle* navigation_handle,
- int aborted_chain_size,
- int aborted_chain_size_same_url)
- : did_stop_tracking_(false),
- navigation_start_(navigation_handle->NavigationStart()),
- url_(navigation_handle->GetURL()),
- abort_type_(ABORT_NONE),
- started_in_foreground_(in_foreground),
- 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);
- for (const auto& observer : observers_) {
- observer->OnStart(navigation_handle, currently_committed_url,
- started_in_foreground_);
- }
-}
-
-PageLoadTracker::~PageLoadTracker() {
- if (did_stop_tracking_)
- return;
-
- const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
- DCHECK_NE(static_cast<bool>(info.time_to_commit),
- static_cast<bool>(failed_provisional_load_info_));
- if (info.time_to_commit && timing_.IsEmpty()) {
- RecordInternalError(ERR_NO_IPCS_RECEIVED);
- const base::TimeTicks commit_time =
- navigation_start_ + info.time_to_commit.value();
- PAGE_LOAD_HISTOGRAM(internal::kCommitToCompleteNoTimingIPCs,
- base::TimeTicks::Now() - commit_time);
- }
- // Recall that trackers that are given ABORT_UNKNOWN_NAVIGATION have their
- // chain length added to the next navigation. Take care not to double count
- // them. Also do not double count committed loads, which call this already.
- if (commit_time_.is_null() && abort_type_ != ABORT_UNKNOWN_NAVIGATION)
- LogAbortChainHistograms(nullptr);
-
- for (const auto& observer : observers_) {
- if (failed_provisional_load_info_) {
- observer->OnFailedProvisionalLoad(*failed_provisional_load_info_, info);
- } else {
- observer->OnComplete(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(!commit_time_.is_null());
-
- // 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_);
-
- ui::PageTransition committed_transition =
- final_navigation->GetPageTransition();
- switch (AbortTypeForPageTransition(committed_transition)) {
- case ABORT_RELOAD:
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeReload,
- aborted_chain_size_);
- return;
- case ABORT_FORWARD_BACK:
- UMA_HISTOGRAM_COUNTS(internal::kAbortChainSizeForwardBack,
- aborted_chain_size_);
- return;
- case ABORT_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_);
- }
-
- for (const auto& observer : observers_)
- observer->OnHidden();
-}
-
-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_);
- }
-
- for (const auto& observer : observers_)
- observer->OnShown();
-}
-
-void PageLoadTracker::Commit(content::NavigationHandle* navigation_handle) {
- // TODO(bmcquade): To improve accuracy, consider adding commit time to
- // NavigationHandle. Taking a timestamp here should be close enough for now.
- commit_time_ = base::TimeTicks::Now();
- ClampBrowserTimestampIfInterProcessTimeTickSkew(&commit_time_);
- url_ = navigation_handle->GetURL();
- for (const auto& observer : observers_) {
- observer->OnCommit(navigation_handle);
- }
- LogAbortChainHistograms(navigation_handle);
-}
-
-void PageLoadTracker::FailedProvisionalLoad(
- content::NavigationHandle* navigation_handle) {
- DCHECK(!failed_provisional_load_info_);
- failed_provisional_load_info_.reset(new FailedProvisionalLoadInfo(
- base::TimeTicks::Now() - navigation_handle->NavigationStart(),
- navigation_handle->GetNetErrorCode()));
-}
-
-void PageLoadTracker::Redirect(content::NavigationHandle* navigation_handle) {
- for (const auto& observer : observers_) {
- observer->OnRedirect(navigation_handle);
- }
-}
-
-void PageLoadTracker::OnInputEvent(const blink::WebInputEvent& event) {
- for (const auto& observer : observers_) {
- observer->OnUserInput(event);
- }
-}
-
-void PageLoadTracker::NotifyClientRedirectTo(
- const PageLoadTracker& destination) {
- if (timing_.first_paint) {
- base::TimeTicks first_paint_time =
- navigation_start() + 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);
- }
-}
-
-bool PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing,
- const PageLoadMetadata& new_metadata) {
- // 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.
- bool valid_timing_descendent =
- timing_.navigation_start.is_null() ||
- timing_.navigation_start == new_timing.navigation_start;
- // Ensure flags sent previously are still present in the new metadata fields.
- bool valid_behavior_descendent =
- (metadata_.behavior_flags & new_metadata.behavior_flags) ==
- metadata_.behavior_flags;
- if (IsValidPageLoadTiming(new_timing) && valid_timing_descendent &&
- valid_behavior_descendent) {
- // There are some subtle ordering constraints here. GetPageLoadMetricsInfo()
- // must be called before DispatchObserverTimingCallbacks, but its
- // implementation depends on the state of metadata_, so we need to update
- // metadata_ before calling GetPageLoadMetricsInfo. Thus, we make a copy of
- // timing here, update timing_ and metadata_, and then proceed to dispatch
- // the observer timing callbacks.
- const PageLoadTiming last_timing = timing_;
- timing_ = new_timing;
-
- const PageLoadMetadata last_metadata = metadata_;
- metadata_ = new_metadata;
- const PageLoadExtraInfo info = ComputePageLoadExtraInfo();
- for (const auto& observer : observers_) {
- DispatchObserverTimingCallbacks(observer.get(), last_timing, new_timing,
- last_metadata, info);
- }
- return true;
- }
- return false;
-}
-
-void PageLoadTracker::StopTracking() {
- did_stop_tracking_ = true;
-}
-
-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> time_to_abort;
- base::Optional<base::TimeDelta> time_to_commit;
-
- 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 (abort_type_ != ABORT_NONE) {
- DCHECK_GE(abort_time_, navigation_start_);
- time_to_abort = abort_time_ - navigation_start_;
- } else {
- DCHECK(abort_time_.is_null());
- }
-
- if (!commit_time_.is_null()) {
- DCHECK_GE(commit_time_, navigation_start_);
- time_to_commit = commit_time_ - navigation_start_;
- }
-
- return PageLoadExtraInfo(
- first_background_time, first_foreground_time, started_in_foreground_,
- commit_time_.is_null() ? GURL() : url_, time_to_commit, abort_type_,
- time_to_abort, metadata_);
-}
-
-void PageLoadTracker::NotifyAbort(UserAbortType abort_type,
- base::TimeTicks timestamp,
- bool is_certainly_browser_timestamp) {
- DCHECK_NE(abort_type, ABORT_NONE);
- // Use UpdateAbort to update an already notified PageLoadTracker.
- if (abort_type_ != ABORT_NONE)
- return;
-
- UpdateAbortInternal(abort_type, timestamp, is_certainly_browser_timestamp);
-}
-
-void PageLoadTracker::UpdateAbort(UserAbortType abort_type,
- base::TimeTicks timestamp,
- bool is_certainly_browser_timestamp) {
- DCHECK_NE(abort_type, ABORT_NONE);
- DCHECK_NE(abort_type, ABORT_OTHER);
- DCHECK_EQ(abort_type_, ABORT_OTHER);
-
- // 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.
- UpdateAbortInternal(abort_type, std::min(abort_time_, timestamp),
- is_certainly_browser_timestamp);
-}
-
-bool PageLoadTracker::IsLikelyProvisionalAbort(
- base::TimeTicks abort_cause_time) {
- // Note that |abort_cause_time - abort_time| can be negative.
- return abort_type_ == ABORT_OTHER &&
- (abort_cause_time - abort_time_).InMilliseconds() < 100;
-}
-
-bool PageLoadTracker::MatchesOriginalNavigation(
- content::NavigationHandle* navigation_handle) {
- // Neither navigation should have committed.
- DCHECK(!navigation_handle->HasCommitted());
- DCHECK(commit_time_.is_null());
- return navigation_handle->GetURL() == url_;
-}
-
-void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type,
- 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_ABORT_BEFORE_NAVIGATION_START);
- abort_type_ = ABORT_NONE;
- abort_time_ = base::TimeTicks();
- return;
- }
- abort_type_ = abort_type;
- abort_time_ = timestamp;
-
- if (is_certainly_browser_timestamp) {
- ClampBrowserTimestampIfInterProcessTimeTickSkew(&abort_time_);
- }
-}
-
-// static
-MetricsWebContentsObserver::MetricsWebContentsObserver(
- content::WebContents* web_contents,
- std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface)
- : content::WebContentsObserver(web_contents),
- in_foreground_(false),
- embedder_interface_(std::move(embedder_interface)),
- has_navigated_(false) {
- RegisterInputEventObserver(web_contents->GetRenderViewHost());
-}
-
-MetricsWebContentsObserver* MetricsWebContentsObserver::CreateForWebContents(
- content::WebContents* web_contents,
- std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface) {
- DCHECK(web_contents);
-
- MetricsWebContentsObserver* metrics = FromWebContents(web_contents);
- if (!metrics) {
- metrics = new MetricsWebContentsObserver(web_contents,
- std::move(embedder_interface));
- web_contents->SetUserData(UserDataKey(), metrics);
- }
- return metrics;
-}
-
-MetricsWebContentsObserver::~MetricsWebContentsObserver() {
- NotifyAbortAllLoads(ABORT_CLOSE);
-}
-
-void MetricsWebContentsObserver::RegisterInputEventObserver(
- content::RenderViewHost* host) {
- if (host != nullptr)
- host->GetWidget()->AddInputEventObserver(this);
-}
-
-void MetricsWebContentsObserver::UnregisterInputEventObserver(
- content::RenderViewHost* host) {
- if (host != nullptr)
- host->GetWidget()->RemoveInputEventObserver(this);
-}
-
-void MetricsWebContentsObserver::RenderViewHostChanged(
- content::RenderViewHost* old_host,
- content::RenderViewHost* new_host) {
- UnregisterInputEventObserver(old_host);
- RegisterInputEventObserver(new_host);
-}
-
-bool MetricsWebContentsObserver::OnMessageReceived(
- const IPC::Message& message,
- content::RenderFrameHost* render_frame_host) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message,
- render_frame_host)
- IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
-}
-
-void MetricsWebContentsObserver::DidStartNavigation(
- content::NavigationHandle* navigation_handle) {
- if (!navigation_handle->IsInMainFrame())
- return;
-
- std::unique_ptr<PageLoadTracker> last_aborted =
- NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle);
-
- int chain_size_same_url = 0;
- int chain_size = 0;
- if (last_aborted) {
- if (last_aborted->MatchesOriginalNavigation(navigation_handle)) {
- chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1;
- } else if (last_aborted->aborted_chain_size_same_url() > 0) {
- LogAbortChainSameURLHistogram(
- last_aborted->aborted_chain_size_same_url());
- }
- chain_size = last_aborted->aborted_chain_size() + 1;
- }
-
- if (!ShouldTrackNavigation(navigation_handle))
- return;
-
- // TODO(bmcquade): add support for tracking prerendered pages when they become
- // visible.
- if (embedder_interface_->IsPrerendering(web_contents()))
- return;
-
- // Pass in the last committed url to the PageLoadTracker. If the MWCO has
- // never observed a committed load, use the last committed url from this
- // WebContent's opener. This is more accurate than using referrers due to
- // referrer sanitizing and origin referrers. Note that this could potentially
- // be inaccurate if the opener has since navigated.
- content::WebContents* opener = web_contents()->GetOpener();
- const GURL& opener_url =
- !has_navigated_ && opener
- ? web_contents()->GetOpener()->GetLastCommittedURL()
- : GURL::EmptyGURL();
- const GURL& currently_committed_url =
- committed_load_ ? committed_load_->committed_url() : opener_url;
- has_navigated_ = true;
-
- // We can have two provisional loads in some cases. E.g. a same-site
- // navigation can have a concurrent cross-process navigation started
- // from the omnibox.
- DCHECK_GT(2ul, provisional_loads_.size());
- // Passing raw pointers to observers_ and embedder_interface_ is safe because
- // the MetricsWebContentsObserver owns them both list and they are torn down
- // after the PageLoadTracker. The PageLoadTracker does not hold on to
- // committed_load_ or navigation_handle beyond the scope of the constructor.
- provisional_loads_.insert(std::make_pair(
- navigation_handle,
- base::WrapUnique(new PageLoadTracker(
- in_foreground_, embedder_interface_.get(), currently_committed_url,
- navigation_handle, chain_size, chain_size_same_url))));
-}
-
-const PageLoadExtraInfo
-MetricsWebContentsObserver::GetPageLoadExtraInfoForCommittedLoad() {
- DCHECK(committed_load_);
- return committed_load_->ComputePageLoadExtraInfo();
-}
-
-void MetricsWebContentsObserver::DidFinishNavigation(
- content::NavigationHandle* navigation_handle) {
- if (!navigation_handle->IsInMainFrame())
- return;
-
- std::unique_ptr<PageLoadTracker> finished_nav(
- std::move(provisional_loads_[navigation_handle]));
- provisional_loads_.erase(navigation_handle);
-
- const bool should_track =
- finished_nav && ShouldTrackNavigation(navigation_handle);
-
- if (finished_nav && !should_track)
- finished_nav->StopTracking();
-
- if (navigation_handle->HasCommitted()) {
- // Ignore same-page navigations.
- if (navigation_handle->IsSamePage())
- return;
-
- // Notify other loads that they may have been aborted by this committed
- // load. Note that by using the committed navigation start as the abort
- // cause, we lose data on provisional loads that were aborted by other
- // provisional loads. Those will either be listed as ABORT_OTHER or as being
- // aborted by this load.
- // is_certainly_browser_timestamp is set to false because NavigationStart()
- // could be set in either the renderer or browser process.
- NotifyAbortAllLoadsWithTimestamp(
- AbortTypeForPageTransition(navigation_handle->GetPageTransition()),
- navigation_handle->NavigationStart(), false);
-
- if (should_track) {
- HandleCommittedNavigationForTrackedLoad(navigation_handle,
- std::move(finished_nav));
- } else {
- committed_load_.reset();
- }
- } else if (should_track) {
- HandleFailedNavigationForTrackedLoad(navigation_handle,
- std::move(finished_nav));
- }
-}
-
-// Handle a pre-commit error. Navigations that result in an error page will be
-// ignored. Note that downloads/204s will result in HasCommitted() returning
-// false.
-// TODO(csharrison): Track changes to NavigationHandle for signals when this is
-// the case (HTTP response headers).
-void MetricsWebContentsObserver::HandleFailedNavigationForTrackedLoad(
- content::NavigationHandle* navigation_handle,
- std::unique_ptr<PageLoadTracker> tracker) {
- tracker->FailedProvisionalLoad(navigation_handle);
-
- net::Error error = navigation_handle->GetNetErrorCode();
-
- // net::OK: This case occurs when the NavigationHandle finishes and reports
- // !HasCommitted(), but reports no net::Error. This should not occur
- // pre-PlzNavigate, but afterwards it should represent the navigation stopped
- // by the user before it was ready to commit.
- // net::ERR_ABORTED: An aborted provisional load has error
- // net::ERR_ABORTED. Note that this can come from some non user-initiated
- // errors, such as downloads, or 204 responses. See crbug.com/542369.
- if ((error == net::OK) || (error == net::ERR_ABORTED)) {
- tracker->NotifyAbort(ABORT_OTHER, base::TimeTicks::Now(), true);
- aborted_provisional_loads_.push_back(std::move(tracker));
- }
-}
-
-void MetricsWebContentsObserver::HandleCommittedNavigationForTrackedLoad(
- content::NavigationHandle* navigation_handle,
- std::unique_ptr<PageLoadTracker> tracker) {
- if (!navigation_handle->HasUserGesture() &&
- (navigation_handle->GetPageTransition() &
- ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0 &&
- committed_load_)
- committed_load_->NotifyClientRedirectTo(*tracker);
-
- committed_load_ = std::move(tracker);
- committed_load_->Commit(navigation_handle);
-}
-
-void MetricsWebContentsObserver::NavigationStopped() {
- NotifyAbortAllLoads(ABORT_STOP);
-}
-
-void MetricsWebContentsObserver::OnInputEvent(
- const blink::WebInputEvent& event) {
- // Ignore browser navigation or reload which comes with type Undefined.
- if (event.type == blink::WebInputEvent::Type::Undefined)
- return;
-
- if (committed_load_)
- committed_load_->OnInputEvent(event);
-}
-
-void MetricsWebContentsObserver::DidRedirectNavigation(
- content::NavigationHandle* navigation_handle) {
- if (!navigation_handle->IsInMainFrame())
- return;
- auto it = provisional_loads_.find(navigation_handle);
- if (it == provisional_loads_.end())
- return;
- it->second->Redirect(navigation_handle);
-}
-
-void MetricsWebContentsObserver::WasShown() {
- if (in_foreground_)
- return;
- in_foreground_ = true;
- if (committed_load_)
- committed_load_->WebContentsShown();
- for (const auto& kv : provisional_loads_) {
- kv.second->WebContentsShown();
- }
-}
-
-void MetricsWebContentsObserver::WasHidden() {
- if (!in_foreground_)
- return;
- in_foreground_ = false;
- if (committed_load_)
- committed_load_->WebContentsHidden();
- for (const auto& kv : provisional_loads_) {
- kv.second->WebContentsHidden();
- }
-}
-
-// This will occur when the process for the main RenderFrameHost exits, either
-// normally or from a crash. We eagerly log data from the last committed load if
-// we have one. Don't notify aborts here because this is probably not user
-// initiated. If it is (e.g. browser shutdown), other code paths will take care
-// of notifying.
-void MetricsWebContentsObserver::RenderProcessGone(
- base::TerminationStatus status) {
- // Other code paths will be run for normal renderer shutdown. Note that we
- // sometimes get the STILL_RUNNING value on fast shutdown.
- if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
- status == base::TERMINATION_STATUS_STILL_RUNNING) {
- return;
- }
-
- // If this is a crash, eagerly log the aborted provisional loads and the
- // committed load. |provisional_loads_| don't need to be destroyed here
- // because their lifetime is tied to the NavigationHandle.
- committed_load_.reset();
- aborted_provisional_loads_.clear();
-}
-
-void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type) {
- NotifyAbortAllLoadsWithTimestamp(abort_type, base::TimeTicks::Now(), true);
-}
-
-void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp(
- UserAbortType abort_type,
- base::TimeTicks timestamp,
- bool is_certainly_browser_timestamp) {
- if (committed_load_)
- committed_load_->NotifyAbort(abort_type, timestamp,
- is_certainly_browser_timestamp);
- for (const auto& kv : provisional_loads_) {
- kv.second->NotifyAbort(abort_type, timestamp,
- is_certainly_browser_timestamp);
- }
- for (const auto& tracker : aborted_provisional_loads_) {
- if (tracker->IsLikelyProvisionalAbort(timestamp))
- tracker->UpdateAbort(abort_type, timestamp,
- is_certainly_browser_timestamp);
- }
- aborted_provisional_loads_.clear();
-}
-
-std::unique_ptr<PageLoadTracker>
-MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation(
- content::NavigationHandle* new_navigation) {
- // If there are multiple aborted loads that can be attributed to this one,
- // just count the latest one for simplicity. Other loads will fall into the
- // OTHER bucket, though there shouldn't be very many.
- if (aborted_provisional_loads_.size() == 0)
- return nullptr;
- if (aborted_provisional_loads_.size() > 1)
- RecordInternalError(ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS);
-
- std::unique_ptr<PageLoadTracker> last_aborted_load =
- std::move(aborted_provisional_loads_.back());
- aborted_provisional_loads_.pop_back();
-
- base::TimeTicks timestamp = new_navigation->NavigationStart();
- if (last_aborted_load->IsLikelyProvisionalAbort(timestamp))
- last_aborted_load->UpdateAbort(ABORT_UNKNOWN_NAVIGATION, timestamp, false);
-
- aborted_provisional_loads_.clear();
- return last_aborted_load;
-}
-
-void MetricsWebContentsObserver::OnTimingUpdated(
- content::RenderFrameHost* render_frame_host,
- const PageLoadTiming& timing,
- const PageLoadMetadata& metadata) {
- bool error = false;
- if (!committed_load_) {
- RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD);
- error = true;
- }
-
- // We may receive notifications from frames that have been navigated away
- // from. We simply ignore them.
- if (render_frame_host != web_contents()->GetMainFrame()) {
- RecordInternalError(ERR_IPC_FROM_WRONG_FRAME);
- error = true;
- }
-
- // For urls like chrome://newtab, the renderer and browser disagree,
- // so we have to double check that the renderer isn't sending data from a
- // bad url like https://www.google.com/_/chrome/newtab.
- if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
- RecordInternalError(ERR_IPC_FROM_BAD_URL_SCHEME);
- error = true;
- }
-
- if (error)
- return;
-
- if (!committed_load_->UpdateTiming(timing, metadata)) {
- // If the page load tracker cannot update its timing, something is wrong
- // with the IPC (it's from another load, or it's invalid in some other way).
- // We expect this to be a rare occurrence.
- RecordInternalError(ERR_BAD_TIMING_IPC);
- }
-}
-
-bool MetricsWebContentsObserver::ShouldTrackNavigation(
- content::NavigationHandle* navigation_handle) const {
- DCHECK(navigation_handle->IsInMainFrame());
- if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
- return false;
- if (embedder_interface_->IsNewTabPageUrl(navigation_handle->GetURL()))
- return false;
- if (navigation_handle->HasCommitted()) {
- if (navigation_handle->IsSamePage() || navigation_handle->IsErrorPage())
- return false;
- const std::string& mime_type = web_contents()->GetContentsMimeType();
- if (mime_type != "text/html" && mime_type != "application/xhtml+xml")
- return false;
- }
- return true;
-}
-
-} // namespace page_load_metrics

Powered by Google App Engine
This is Rietveld 408576698