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

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

Issue 2435233002: Factor PageLoadTracker into its own header and impl files. (Closed)
Patch Set: share IsNavigationUserInitiated impl Created 4 years, 2 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
« no previous file with comments | « chrome/browser/page_load_metrics/page_load_tracker.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/page_load_metrics/page_load_tracker.cc
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
similarity index 63%
copy from chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
copy to chrome/browser/page_load_metrics/page_load_tracker.cc
index c58e4d517126fcb62f499914f485d4707e4332f8..1ae615a77ed7268ffae00c73071a2e9829c972c1 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -1,38 +1,25 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// Copyright 2016 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/metrics_web_contents_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_tracker.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_macros.h"
#include "base/metrics/user_metrics.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/common/page_load_metrics/page_load_metrics_messages.h"
#include "chrome/common/page_load_metrics/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);
-
// 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.
@@ -70,6 +57,42 @@ const char kPageLoadCompletedAfterAppBackground[] =
} // 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.
+UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) {
+ if (transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
+ return ABORT_CLIENT_REDIRECT;
+ }
+ 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);
+ }
+}
+
+// TODO(crbug.com/617904): Browser initiated navigations should have
+// HasUserGesture() set to true. Update this once we get enough data from just
+// renderer initiated aborts.
+bool IsNavigationUserInitiated(content::NavigationHandle* handle) {
+ return handle->HasUserGesture();
+}
+
namespace {
// Helper to allow use of Optional<> values in LOG() messages.
@@ -215,40 +238,11 @@ bool IsValidPageLoadTiming(const PageLoadTiming& timing) {
return true;
}
-void RecordInternalError(InternalErrorLoadEvent event) {
- UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY);
-}
-
void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) {
UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground,
completed_after_background);
}
-// TODO(csharrison): Add a case for client side redirects, which is what JS
-// initiated window.location / window.history navigations get set to.
-UserAbortType AbortTypeForPageTransition(ui::PageTransition transition) {
- if (transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
- return ABORT_CLIENT_REDIRECT;
- }
- 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,
@@ -281,13 +275,6 @@ void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
observer->OnLoadingBehaviorObserved(extra_info);
}
-// TODO(crbug.com/617904): Browser initiated navigations should have
-// HasUserGesture() set to true. Update this once we get enough data from just
-// renderer initiated aborts.
-bool IsNavigationUserInitiated(content::NavigationHandle* handle) {
- return handle->HasUserGesture();
-}
-
} // namespace
PageLoadTracker::PageLoadTracker(
@@ -688,412 +675,4 @@ void PageLoadTracker::UpdateAbortInternal(UserAbortType abort_type,
}
}
-// 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() {
- // TODO(csharrison): Use a more user-initiated signal for CLOSE.
- NotifyAbortAllLoads(ABORT_CLOSE, false);
-}
-
-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::WillStartNavigationRequest(
- 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;
-
- // 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::MakeUnique<PageLoadTracker>(
- in_foreground_, embedder_interface_.get(), currently_committed_url,
- navigation_handle, chain_size, chain_size_same_url)));
-}
-
-void MetricsWebContentsObserver::OnRequestComplete(
- content::ResourceType resource_type,
- bool was_cached,
- int net_error) {
- // For simplicity, only count subresources. Navigations are hard to attribute
- // here because we won't have a committed load by the time data streams in
- // from the IO thread.
- if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME &&
- net_error != net::OK) {
- return;
- }
- if (!committed_load_)
- return;
- committed_load_->OnLoadedSubresource(was_cached);
-}
-
-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);
-
- // Ignore same-page navigations.
- if (navigation_handle->HasCommitted() && navigation_handle->IsSamePage()) {
- if (finished_nav)
- finished_nav->StopTracking();
- return;
- }
-
- // Ignore internally generated aborts for navigations with HTTP responses that
- // don't commit, such as HTTP 204 responses and downloads.
- if (!navigation_handle->HasCommitted() &&
- navigation_handle->GetNetErrorCode() == net::ERR_ABORTED &&
- navigation_handle->GetResponseHeaders()) {
- if (finished_nav)
- finished_nav->StopTracking();
- return;
- }
-
- const bool should_track =
- finished_nav && ShouldTrackNavigation(navigation_handle);
-
- if (finished_nav && !should_track)
- finished_nav->StopTracking();
-
- if (navigation_handle->HasCommitted()) {
- // Notify other loads that they may have been aborted by this committed
- // 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()),
- IsNavigationUserInitiated(navigation_handle),
- 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.
-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.
- if ((error == net::OK) || (error == net::ERR_ABORTED)) {
- tracker->NotifyAbort(ABORT_OTHER, false, 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() {
- // TODO(csharrison): Use a more user-initiated signal for STOP.
- NotifyAbortAllLoads(ABORT_STOP, false);
-}
-
-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::FlushMetricsOnAppEnterBackground() {
- // Signal to observers that we've been backgrounded, in cases where the
- // FlushMetricsOnAppEnterBackground callback gets invoked before the
- // associated WasHidden callback.
- WasHidden();
-
- if (committed_load_)
- committed_load_->FlushMetricsOnAppEnterBackground();
- for (const auto& kv : provisional_loads_) {
- kv.second->FlushMetricsOnAppEnterBackground();
- }
- for (const auto& tracker : aborted_provisional_loads_) {
- tracker->FlushMetricsOnAppEnterBackground();
- }
-}
-
-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,
- bool user_initiated) {
- NotifyAbortAllLoadsWithTimestamp(abort_type, user_initiated,
- base::TimeTicks::Now(), true);
-}
-
-void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp(
- UserAbortType abort_type,
- bool user_initiated,
- base::TimeTicks timestamp,
- bool is_certainly_browser_timestamp) {
- if (committed_load_) {
- committed_load_->NotifyAbort(abort_type, user_initiated, timestamp,
- is_certainly_browser_timestamp);
- }
- for (const auto& kv : provisional_loads_) {
- kv.second->NotifyAbort(abort_type, user_initiated, timestamp,
- is_certainly_browser_timestamp);
- }
- for (const auto& tracker : aborted_provisional_loads_) {
- if (tracker->IsLikelyProvisionalAbort(timestamp)) {
- tracker->UpdateAbort(abort_type, user_initiated, 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(
- AbortTypeForPageTransition(new_navigation->GetPageTransition()),
- IsNavigationUserInitiated(new_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) {
- // 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);
- return;
- }
-
- // While timings arriving for the wrong frame are expected, we do not expect
- // any of the errors below. Thus, we track occurrences of all errors below,
- // rather than returning early after encountering an error.
-
- bool error = false;
- if (!committed_load_) {
- RecordInternalError(ERR_IPC_WITH_NO_RELEVANT_LOAD);
- error = true;
- }
-
- 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());
- DCHECK(!navigation_handle->HasCommitted() ||
- !navigation_handle->IsSamePage());
-
- return BrowserPageTrackDecider(embedder_interface_.get(), web_contents(),
- navigation_handle).ShouldTrack();
-}
-
} // namespace page_load_metrics
« no previous file with comments | « chrome/browser/page_load_metrics/page_load_tracker.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698