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

Side by Side Diff: components/page_load_metrics/browser/metrics_web_contents_observer.cc

Issue 1384213002: Page Abort Events for relevant navigations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added new histogram suffix, comments, name changes Created 5 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" 5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "components/page_load_metrics/common/page_load_metrics_messages.h" 9 #include "components/page_load_metrics/common/page_load_metrics_messages.h"
10 #include "components/page_load_metrics/common/page_load_timing.h" 10 #include "components/page_load_metrics/common/page_load_timing.h"
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 } 57 }
58 58
59 } // namespace 59 } // namespace
60 60
61 #define PAGE_LOAD_HISTOGRAM(name, sample) \ 61 #define PAGE_LOAD_HISTOGRAM(name, sample) \
62 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \ 62 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \
63 base::TimeDelta::FromMilliseconds(10), \ 63 base::TimeDelta::FromMilliseconds(10), \
64 base::TimeDelta::FromMinutes(10), 100) 64 base::TimeDelta::FromMinutes(10), 100)
65 65
66 PageLoadTracker::PageLoadTracker(bool in_foreground) 66 PageLoadTracker::PageLoadTracker(bool in_foreground)
67 : has_commit_(false), started_in_foreground_(in_foreground) { 67 : has_commit_(false), started_in_foreground_(in_foreground) {}
68 RecordEvent(PAGE_LOAD_STARTED);
69 }
70 68
71 PageLoadTracker::~PageLoadTracker() { 69 PageLoadTracker::~PageLoadTracker() {
72 // Even a load that failed a provisional load should log
73 // that it aborted before first layout.
74 if (timing_.first_layout.is_zero())
75 RecordEvent(PAGE_LOAD_ABORTED_BEFORE_FIRST_LAYOUT);
76
77 if (has_commit_) 70 if (has_commit_)
78 RecordTimingHistograms(); 71 RecordTimingHistograms();
79 } 72 }
80 73
81 void PageLoadTracker::WebContentsHidden() { 74 void PageLoadTracker::WebContentsHidden() {
82 // Only log the first time we background in a given page load. 75 // Only log the first time we background in a given page load.
83 if (background_time_.is_null()) { 76 if (started_in_foreground_ && background_time_.is_null())
84 background_time_ = base::TimeTicks::Now(); 77 background_time_ = base::TimeTicks::Now();
85 } 78 }
79
80 void PageLoadTracker::WebContentsShown() {
81 // Only log the first time we foreground in a given page load.
82 // Don't log foreground time if we started foregrounded.
83 if (!started_in_foreground_ && foreground_time_.is_null())
84 foreground_time_ = base::TimeTicks::Now();
86 } 85 }
87 86
88 void PageLoadTracker::Commit() { 87 void PageLoadTracker::Commit() {
89 has_commit_ = true; 88 has_commit_ = true;
89 // This is one special case where we aren't checking if we backgrounded NOW,
90 // but at the start of the navigation. Tracking started_in_foreground_ for
91 // relevant loads is important data, and we can't filter by relevancy until
92 // post-commit. Background/Foreground at commit time doesn't mean much from
93 // the user's point of view.
94 RecordCommittedEvent(COMMITTED_LOAD_STARTED, !started_in_foreground_);
90 } 95 }
91 96
92 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& timing) { 97 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& new_timing) {
93 // Throw away IPCs that are not relevant to the current navigation. 98 // Throw away IPCs that are not relevant to the current navigation.
94 if (!timing_.navigation_start.is_null() && 99 // Two timing structures cannot refer to the same navigation if they indicate
95 timing_.navigation_start != timing.navigation_start) { 100 // that a navigation started at different times, so a new timing struct with a
96 // TODO(csharrison) uma log a counter here 101 // different start time from an earlier struct is considered invalid.
97 return false; 102 bool valid_timing_descendent =
98 } 103 timing_.navigation_start.is_null() ||
99 if (IsValidPageLoadTiming(timing)) { 104 timing_.navigation_start == new_timing.navigation_start;
100 timing_ = timing; 105 if (IsValidPageLoadTiming(new_timing) && valid_timing_descendent) {
106 timing_ = new_timing;
101 return true; 107 return true;
102 } 108 }
103 return false; 109 return false;
104 } 110 }
105 111
112 bool PageLoadTracker::HasBackgrounded() {
113 return !(started_in_foreground_ && background_time_.is_null());
114 }
115
106 void PageLoadTracker::RecordTimingHistograms() { 116 void PageLoadTracker::RecordTimingHistograms() {
107 DCHECK(has_commit_); 117 DCHECK(has_commit_);
108 // This method is similar to how blink converts TimeTicks to epoch time. 118 // This method is similar to how blink converts TimeTicks to epoch time.
109 // There may be slight inaccuracies due to inter-process timestamps, but 119 // There may be slight inaccuracies due to inter-process timestamps, but
110 // this solution is the best we have right now. 120 // this solution is the best we have right now.
111 base::TimeDelta background_delta; 121 base::TimeDelta background_delta;
112 if (started_in_foreground_) { 122 if (started_in_foreground_) {
113 background_delta = background_time_.is_null() 123 background_delta = background_time_.is_null()
114 ? base::TimeDelta::Max() 124 ? base::TimeDelta::Max()
115 : WallTimeFromTimeTicks(background_time_) - timing_.navigation_start; 125 : WallTimeFromTimeTicks(background_time_) - timing_.navigation_start;
(...skipping 12 matching lines...) Expand all
128 } 138 }
129 if (!timing_.load_event_start.is_zero()) { 139 if (!timing_.load_event_start.is_zero()) {
130 if (timing_.load_event_start < background_delta) { 140 if (timing_.load_event_start < background_delta) {
131 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired", 141 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired",
132 timing_.load_event_start); 142 timing_.load_event_start);
133 } else { 143 } else {
134 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired.BG", 144 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToLoadEventFired.BG",
135 timing_.load_event_start); 145 timing_.load_event_start);
136 } 146 }
137 } 147 }
138 if (!timing_.first_layout.is_zero()) { 148 if (timing_.first_layout.is_zero()) {
149 RecordCommittedEvent(COMMITTED_LOAD_FAILED_BEFORE_FIRST_LAYOUT,
150 HasBackgrounded());
151 } else {
139 if (timing_.first_layout < background_delta) { 152 if (timing_.first_layout < background_delta) {
140 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout", 153 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout",
141 timing_.first_layout); 154 timing_.first_layout);
142 RecordEvent(PAGE_LOAD_SUCCESSFUL_FIRST_LAYOUT_FOREGROUND); 155 RecordCommittedEvent(COMMITTED_LOAD_SUCCESSFUL_FIRST_LAYOUT, false);
143 } else { 156 } else {
144 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout.BG", 157 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstLayout.BG",
145 timing_.first_layout); 158 timing_.first_layout);
146 RecordEvent(PAGE_LOAD_SUCCESSFUL_FIRST_LAYOUT_BACKGROUND); 159 RecordCommittedEvent(COMMITTED_LOAD_SUCCESSFUL_FIRST_LAYOUT, true);
147 } 160 }
148 } 161 }
149 if (!timing_.first_text_paint.is_zero()) { 162 if (!timing_.first_text_paint.is_zero()) {
150 if (timing_.first_text_paint < background_delta) { 163 if (timing_.first_text_paint < background_delta) {
151 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint", 164 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint",
152 timing_.first_text_paint); 165 timing_.first_text_paint);
153 } else { 166 } else {
154 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint.BG", 167 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstTextPaint.BG",
155 timing_.first_text_paint); 168 timing_.first_text_paint);
156 } 169 }
157 } 170 }
171 // Log time to first foreground / time to first background. Log counts that we
172 // started a relevant page load in the foreground / background.
173 if (!background_time_.is_null()) {
174 PAGE_LOAD_HISTOGRAM("PageLoad.Timing2.NavigationToFirstBackground",
175 background_delta);
176 } else if (!foreground_time_.is_null()) {
177 PAGE_LOAD_HISTOGRAM(
178 "PageLoad.Timing2.NavigationToFirstForeground",
179 WallTimeFromTimeTicks(foreground_time_) - timing_.navigation_start);
180 }
158 } 181 }
159 182
160 void PageLoadTracker::RecordEvent(PageLoadEvent event) { 183 void PageLoadTracker::RecordProvisionalEvent(ProvisionalLoadEvent event) {
161 UMA_HISTOGRAM_ENUMERATION( 184 if (HasBackgrounded()) {
162 "PageLoad.EventCounts", event, PAGE_LOAD_LAST_ENTRY); 185 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Provisional.Background", event,
Bryan McQuade 2015/10/15 03:47:36 let's use 'BG' here rather than 'Background' for c
Charlie Harrison 2015/10/15 14:06:48 Haha I agree with you, though it's going against R
186 PROVISIONAL_LOAD_LAST_ENTRY);
187 } else {
188 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Provisional", event,
189 PROVISIONAL_LOAD_LAST_ENTRY);
190 }
191 }
192
193 // RecordCommittedEvent needs a backgrounded input because we need to special
194 // case a few events that need either precise timing measurements, or different
195 // logic than simply "Did I background before logging this event?"
196 void PageLoadTracker::RecordCommittedEvent(CommittedLoadEvent event,
197 bool backgrounded) {
198 if (backgrounded) {
199 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Committed.Background", event,
Bryan McQuade 2015/10/15 03:47:36 same - .BG for consistency
200 COMMITTED_LOAD_LAST_ENTRY);
201 } else {
202 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.Committed", event,
203 COMMITTED_LOAD_LAST_ENTRY);
204 }
163 } 205 }
164 206
165 MetricsWebContentsObserver::MetricsWebContentsObserver( 207 MetricsWebContentsObserver::MetricsWebContentsObserver(
166 content::WebContents* web_contents) 208 content::WebContents* web_contents)
167 : content::WebContentsObserver(web_contents), in_foreground_(false) {} 209 : content::WebContentsObserver(web_contents), in_foreground_(false) {}
168 210
169 MetricsWebContentsObserver::~MetricsWebContentsObserver() {} 211 MetricsWebContentsObserver::~MetricsWebContentsObserver() {}
170 212
171 bool MetricsWebContentsObserver::OnMessageReceived( 213 bool MetricsWebContentsObserver::OnMessageReceived(
172 const IPC::Message& message, 214 const IPC::Message& message,
(...skipping 23 matching lines...) Expand all
196 void MetricsWebContentsObserver::DidFinishNavigation( 238 void MetricsWebContentsObserver::DidFinishNavigation(
197 content::NavigationHandle* navigation_handle) { 239 content::NavigationHandle* navigation_handle) {
198 if (!navigation_handle->IsInMainFrame()) 240 if (!navigation_handle->IsInMainFrame())
199 return; 241 return;
200 242
201 scoped_ptr<PageLoadTracker> finished_nav( 243 scoped_ptr<PageLoadTracker> finished_nav(
202 provisional_loads_.take_and_erase(navigation_handle)); 244 provisional_loads_.take_and_erase(navigation_handle));
203 DCHECK(finished_nav); 245 DCHECK(finished_nav);
204 246
205 // Handle a pre-commit error here. Navigations that result in an error page 247 // Handle a pre-commit error here. Navigations that result in an error page
206 // will be ignored. 248 // will be ignored. Note that downloads/204s will result in HasCommitted()
249 // returning false.
207 if (!navigation_handle->HasCommitted()) { 250 if (!navigation_handle->HasCommitted()) {
208 finished_nav->RecordEvent(PAGE_LOAD_FAILED_BEFORE_COMMIT);
209 if (navigation_handle->GetNetErrorCode() == net::ERR_ABORTED) 251 if (navigation_handle->GetNetErrorCode() == net::ERR_ABORTED)
210 finished_nav->RecordEvent(PAGE_LOAD_ABORTED_BEFORE_COMMIT); 252 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_ABORTED);
253 else if (navigation_handle->GetNetErrorCode() != net::OK)
254 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_FAILED_NON_ABORT);
211 return; 255 return;
Bryan McQuade 2015/10/15 03:47:36 is it ever valid for HasCommitted() to be false an
Charlie Harrison 2015/10/15 14:06:48 After looking through the navigation code, I'm not
Bryan McQuade 2015/10/15 14:12:41 Sounds good. I'd propose adding an error enum (any
Charlie Harrison 2015/10/15 15:13:49 According to clamy, this will signal a user stoppi
Bryan McQuade 2015/10/15 15:17:55 great, thanks for investigating that. what do you
212 } 256 }
257 finished_nav->RecordProvisionalEvent(PROVISIONAL_LOAD_COMMITTED);
213 258
214 // Don't treat a same-page nav as a new page load. 259 // Don't treat a same-page nav as a new page load.
215 if (navigation_handle->IsSamePage()) 260 if (navigation_handle->IsSamePage())
216 return; 261 return;
217 262
218 // Eagerly log the previous UMA even if we don't care about the current 263 // Eagerly log the previous UMA even if we don't care about the current
219 // navigation. 264 // navigation.
220 committed_load_.reset(); 265 committed_load_.reset();
221 266
222 if (!IsRelevantNavigation(navigation_handle)) 267 if (!IsRelevantNavigation(navigation_handle))
223 return; 268 return;
224 269
225 committed_load_ = finished_nav.Pass(); 270 committed_load_ = finished_nav.Pass();
226 committed_load_->Commit(); 271 committed_load_->Commit();
227 } 272 }
228 273
229 void MetricsWebContentsObserver::WasShown() { 274 void MetricsWebContentsObserver::WasShown() {
230 in_foreground_ = true; 275 in_foreground_ = true;
276 if (committed_load_)
277 committed_load_->WebContentsShown();
278 for (const auto& kv : provisional_loads_) {
279 kv.second->WebContentsShown();
280 }
231 } 281 }
232 282
233 void MetricsWebContentsObserver::WasHidden() { 283 void MetricsWebContentsObserver::WasHidden() {
234 in_foreground_ = false; 284 in_foreground_ = false;
235 if (committed_load_) 285 if (committed_load_)
236 committed_load_->WebContentsHidden(); 286 committed_load_->WebContentsHidden();
237 for (const auto& kv : provisional_loads_) { 287 for (const auto& kv : provisional_loads_) {
238 kv.second->WebContentsHidden(); 288 kv.second->WebContentsHidden();
239 } 289 }
240 } 290 }
241 291
242 // This will occur when the process for the main RenderFrameHost exits, either 292 // This will occur when the process for the main RenderFrameHost exits, either
243 // normally or from a crash. We eagerly log data from the last committed load if 293 // normally or from a crash. We eagerly log data from the last committed load if
244 // we have one. 294 // we have one.
245 void MetricsWebContentsObserver::RenderProcessGone( 295 void MetricsWebContentsObserver::RenderProcessGone(
246 base::TerminationStatus status) { 296 base::TerminationStatus status) {
247 committed_load_.reset(); 297 committed_load_.reset();
248 } 298 }
249 299
250 void MetricsWebContentsObserver::OnTimingUpdated( 300 void MetricsWebContentsObserver::OnTimingUpdated(
251 content::RenderFrameHost* render_frame_host, 301 content::RenderFrameHost* render_frame_host,
252 const PageLoadTiming& timing) { 302 const PageLoadTiming& timing) {
253 if (!committed_load_) 303 bool error = false;
254 return; 304 if (!committed_load_) {
305 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
306 ERR_IPC_WITH_NO_COMMITTED_LOAD, ERR_LAST_ENTRY);
307 error = true;
308 }
255 309
256 // We may receive notifications from frames that have been navigated away 310 // We may receive notifications from frames that have been navigated away
257 // from. We simply ignore them. 311 // from. We simply ignore them.
258 if (render_frame_host != web_contents()->GetMainFrame()) 312 if (render_frame_host != web_contents()->GetMainFrame()) {
259 return; 313 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
314 ERR_IPC_FROM_WRONG_FRAME, ERR_LAST_ENTRY);
315 error = true;
316 }
260 317
261 // For urls like chrome://newtab, the renderer and browser disagree, 318 // For urls like chrome://newtab, the renderer and browser disagree,
262 // so we have to double check that the renderer isn't sending data from a 319 // so we have to double check that the renderer isn't sending data from a
263 // bad url like https://www.google.com/_/chrome/newtab. 320 // bad url like https://www.google.com/_/chrome/newtab.
264 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) 321 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
322 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
323 ERR_IPC_FROM_BAD_URL_SCHEME, ERR_LAST_ENTRY);
324 error = true;
325 }
326
327 if (error)
265 return; 328 return;
266 329
267 committed_load_->UpdateTiming(timing); 330 if (!committed_load_->UpdateTiming(timing)) {
331 // If the page load tracker cannot update its timing, something is wrong
332 // with the IPC (it's from another load, or it's invalid in some other way).
333 // We expect this to be a rare occurrence.
334 UMA_HISTOGRAM_ENUMERATION("PageLoad.Events.InternalError",
335 ERR_BAD_TIMING_IPC, ERR_LAST_ENTRY);
336 }
268 } 337 }
269 338
270 bool MetricsWebContentsObserver::IsRelevantNavigation( 339 bool MetricsWebContentsObserver::IsRelevantNavigation(
271 content::NavigationHandle* navigation_handle) { 340 content::NavigationHandle* navigation_handle) {
272 // The url we see from the renderer side is not always the same as what 341 // The url we see from the renderer side is not always the same as what
273 // we see from the browser side (e.g. chrome://newtab). We want to be 342 // we see from the browser side (e.g. chrome://newtab). We want to be
274 // sure here that we aren't logging UMA for internal pages. 343 // sure here that we aren't logging UMA for internal pages.
275 const GURL& browser_url = web_contents()->GetLastCommittedURL(); 344 const GURL& browser_url = web_contents()->GetLastCommittedURL();
276 return navigation_handle->IsInMainFrame() && 345 return navigation_handle->IsInMainFrame() &&
277 !navigation_handle->IsSamePage() && 346 !navigation_handle->IsSamePage() &&
278 !navigation_handle->IsErrorPage() && 347 !navigation_handle->IsErrorPage() &&
279 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() && 348 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() &&
280 browser_url.SchemeIsHTTPOrHTTPS(); 349 browser_url.SchemeIsHTTPOrHTTPS();
281 } 350 }
282 351
283 } // namespace page_load_metrics 352 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698