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

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

Issue 2545593003: Additional heuristic user interaction attribution for page load metrics (Closed)
Patch Set: address comments Created 4 years 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 "chrome/browser/page_load_metrics/metrics_web_contents_observer.h" 5 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <ostream> 8 #include <ostream>
9 #include <string> 9 #include <string>
10 #include <utility> 10 #include <utility>
(...skipping 19 matching lines...) Expand all
30 #include "ipc/ipc_message.h" 30 #include "ipc/ipc_message.h"
31 #include "ipc/ipc_message_macros.h" 31 #include "ipc/ipc_message_macros.h"
32 #include "net/base/net_errors.h" 32 #include "net/base/net_errors.h"
33 #include "ui/base/page_transition_types.h" 33 #include "ui/base/page_transition_types.h"
34 34
35 DEFINE_WEB_CONTENTS_USER_DATA_KEY( 35 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
36 page_load_metrics::MetricsWebContentsObserver); 36 page_load_metrics::MetricsWebContentsObserver);
37 37
38 namespace page_load_metrics { 38 namespace page_load_metrics {
39 39
40 namespace {
41
42 UserInitiatedInfo CreateUserInitiatedInfo(
43 content::NavigationHandle* navigation_handle,
44 PageLoadTracker* committed_load) {
45 if (!navigation_handle->IsRendererInitiated())
46 return UserInitiatedInfo::BrowserInitiated();
47
48 return UserInitiatedInfo::RenderInitiated(
49 navigation_handle->HasUserGesture(),
50 committed_load &&
51 committed_load->input_tracker()->FindAndConsumeInputEventsBefore(
52 navigation_handle->NavigationStart()));
53 }
54
55 } // namespace
56
40 // static 57 // static
41 MetricsWebContentsObserver::MetricsWebContentsObserver( 58 MetricsWebContentsObserver::MetricsWebContentsObserver(
42 content::WebContents* web_contents, 59 content::WebContents* web_contents,
43 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface) 60 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface)
44 : content::WebContentsObserver(web_contents), 61 : content::WebContentsObserver(web_contents),
45 in_foreground_(false), 62 in_foreground_(false),
46 embedder_interface_(std::move(embedder_interface)), 63 embedder_interface_(std::move(embedder_interface)),
47 has_navigated_(false) { 64 has_navigated_(false) {
48 RegisterInputEventObserver(web_contents->GetRenderViewHost()); 65 RegisterInputEventObserver(web_contents->GetRenderViewHost());
49 } 66 }
50 67
51 MetricsWebContentsObserver* MetricsWebContentsObserver::CreateForWebContents( 68 MetricsWebContentsObserver* MetricsWebContentsObserver::CreateForWebContents(
52 content::WebContents* web_contents, 69 content::WebContents* web_contents,
53 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface) { 70 std::unique_ptr<PageLoadMetricsEmbedderInterface> embedder_interface) {
54 DCHECK(web_contents); 71 DCHECK(web_contents);
55 72
56 MetricsWebContentsObserver* metrics = FromWebContents(web_contents); 73 MetricsWebContentsObserver* metrics = FromWebContents(web_contents);
57 if (!metrics) { 74 if (!metrics) {
58 metrics = new MetricsWebContentsObserver(web_contents, 75 metrics = new MetricsWebContentsObserver(web_contents,
59 std::move(embedder_interface)); 76 std::move(embedder_interface));
60 web_contents->SetUserData(UserDataKey(), metrics); 77 web_contents->SetUserData(UserDataKey(), metrics);
61 } 78 }
62 return metrics; 79 return metrics;
63 } 80 }
64 81
65 MetricsWebContentsObserver::~MetricsWebContentsObserver() { 82 MetricsWebContentsObserver::~MetricsWebContentsObserver() {
66 // TODO(csharrison): Use a more user-initiated signal for CLOSE. 83 // TODO(csharrison): Use a more user-initiated signal for CLOSE.
67 NotifyAbortAllLoads(ABORT_CLOSE, false); 84 NotifyAbortAllLoads(ABORT_CLOSE, UserInitiatedInfo::NotUserInitiated());
68 } 85 }
69 86
70 void MetricsWebContentsObserver::RegisterInputEventObserver( 87 void MetricsWebContentsObserver::RegisterInputEventObserver(
71 content::RenderViewHost* host) { 88 content::RenderViewHost* host) {
72 if (host != nullptr) 89 if (host != nullptr)
73 host->GetWidget()->AddInputEventObserver(this); 90 host->GetWidget()->AddInputEventObserver(this);
74 } 91 }
75 92
76 void MetricsWebContentsObserver::UnregisterInputEventObserver( 93 void MetricsWebContentsObserver::UnregisterInputEventObserver(
77 content::RenderViewHost* host) { 94 content::RenderViewHost* host) {
(...skipping 22 matching lines...) Expand all
100 } 117 }
101 118
102 void MetricsWebContentsObserver::WillStartNavigationRequest( 119 void MetricsWebContentsObserver::WillStartNavigationRequest(
103 content::NavigationHandle* navigation_handle) { 120 content::NavigationHandle* navigation_handle) {
104 // Same-page navigations should never go through WillStartNavigationRequest. 121 // Same-page navigations should never go through WillStartNavigationRequest.
105 DCHECK(!navigation_handle->IsSamePage()); 122 DCHECK(!navigation_handle->IsSamePage());
106 123
107 if (!navigation_handle->IsInMainFrame()) 124 if (!navigation_handle->IsInMainFrame())
108 return; 125 return;
109 126
127 UserInitiatedInfo user_initiated_info(
128 CreateUserInitiatedInfo(navigation_handle, committed_load_.get()));
110 std::unique_ptr<PageLoadTracker> last_aborted = 129 std::unique_ptr<PageLoadTracker> last_aborted =
111 NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle); 130 NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle,
131 user_initiated_info);
112 132
113 int chain_size_same_url = 0; 133 int chain_size_same_url = 0;
114 int chain_size = 0; 134 int chain_size = 0;
115 if (last_aborted) { 135 if (last_aborted) {
116 if (last_aborted->MatchesOriginalNavigation(navigation_handle)) { 136 if (last_aborted->MatchesOriginalNavigation(navigation_handle)) {
117 chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1; 137 chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1;
118 } else if (last_aborted->aborted_chain_size_same_url() > 0) { 138 } else if (last_aborted->aborted_chain_size_same_url() > 0) {
119 LogAbortChainSameURLHistogram( 139 LogAbortChainSameURLHistogram(
120 last_aborted->aborted_chain_size_same_url()); 140 last_aborted->aborted_chain_size_same_url());
121 } 141 }
(...skipping 22 matching lines...) Expand all
144 // from the omnibox. 164 // from the omnibox.
145 DCHECK_GT(2ul, provisional_loads_.size()); 165 DCHECK_GT(2ul, provisional_loads_.size());
146 // Passing raw pointers to observers_ and embedder_interface_ is safe because 166 // Passing raw pointers to observers_ and embedder_interface_ is safe because
147 // the MetricsWebContentsObserver owns them both list and they are torn down 167 // the MetricsWebContentsObserver owns them both list and they are torn down
148 // after the PageLoadTracker. The PageLoadTracker does not hold on to 168 // after the PageLoadTracker. The PageLoadTracker does not hold on to
149 // committed_load_ or navigation_handle beyond the scope of the constructor. 169 // committed_load_ or navigation_handle beyond the scope of the constructor.
150 provisional_loads_.insert(std::make_pair( 170 provisional_loads_.insert(std::make_pair(
151 navigation_handle, 171 navigation_handle,
152 base::MakeUnique<PageLoadTracker>( 172 base::MakeUnique<PageLoadTracker>(
153 in_foreground_, embedder_interface_.get(), currently_committed_url, 173 in_foreground_, embedder_interface_.get(), currently_committed_url,
154 navigation_handle, chain_size, chain_size_same_url))); 174 navigation_handle, user_initiated_info, chain_size,
175 chain_size_same_url)));
155 } 176 }
156 177
157 void MetricsWebContentsObserver::OnRequestComplete( 178 void MetricsWebContentsObserver::OnRequestComplete(
158 content::ResourceType resource_type, 179 content::ResourceType resource_type,
159 bool was_cached, 180 bool was_cached,
160 int net_error) { 181 int net_error) {
161 // For simplicity, only count subresources. Navigations are hard to attribute 182 // For simplicity, only count subresources. Navigations are hard to attribute
162 // here because we won't have a committed load by the time data streams in 183 // here because we won't have a committed load by the time data streams in
163 // from the IO thread. 184 // from the IO thread.
164 if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME && 185 if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME &&
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
202 return; 223 return;
203 } 224 }
204 225
205 const bool should_track = 226 const bool should_track =
206 finished_nav && ShouldTrackNavigation(navigation_handle); 227 finished_nav && ShouldTrackNavigation(navigation_handle);
207 228
208 if (finished_nav && !should_track) 229 if (finished_nav && !should_track)
209 finished_nav->StopTracking(); 230 finished_nav->StopTracking();
210 231
211 if (navigation_handle->HasCommitted()) { 232 if (navigation_handle->HasCommitted()) {
233 UserInitiatedInfo user_initiated_info =
234 finished_nav
235 ? finished_nav->user_initiated_info()
236 : CreateUserInitiatedInfo(navigation_handle, committed_load_.get());
237
212 // Notify other loads that they may have been aborted by this committed 238 // Notify other loads that they may have been aborted by this committed
213 // load. is_certainly_browser_timestamp is set to false because 239 // load. is_certainly_browser_timestamp is set to false because
214 // NavigationStart() could be set in either the renderer or browser process. 240 // NavigationStart() could be set in either the renderer or browser process.
215 NotifyAbortAllLoadsWithTimestamp( 241 NotifyAbortAllLoadsWithTimestamp(
216 AbortTypeForPageTransition(navigation_handle->GetPageTransition()), 242 AbortTypeForPageTransition(navigation_handle->GetPageTransition()),
217 IsNavigationUserInitiated(navigation_handle), 243 user_initiated_info, navigation_handle->NavigationStart(), false);
218 navigation_handle->NavigationStart(), false);
219 244
220 if (should_track) { 245 if (should_track) {
221 HandleCommittedNavigationForTrackedLoad(navigation_handle, 246 HandleCommittedNavigationForTrackedLoad(navigation_handle,
222 std::move(finished_nav)); 247 std::move(finished_nav));
223 } else { 248 } else {
224 committed_load_.reset(); 249 committed_load_.reset();
225 } 250 }
226 } else if (should_track) { 251 } else if (should_track) {
227 HandleFailedNavigationForTrackedLoad(navigation_handle, 252 HandleFailedNavigationForTrackedLoad(navigation_handle,
228 std::move(finished_nav)); 253 std::move(finished_nav));
229 } 254 }
230 } 255 }
231 256
232 // Handle a pre-commit error. Navigations that result in an error page will be 257 // Handle a pre-commit error. Navigations that result in an error page will be
233 // ignored. 258 // ignored.
234 void MetricsWebContentsObserver::HandleFailedNavigationForTrackedLoad( 259 void MetricsWebContentsObserver::HandleFailedNavigationForTrackedLoad(
235 content::NavigationHandle* navigation_handle, 260 content::NavigationHandle* navigation_handle,
236 std::unique_ptr<PageLoadTracker> tracker) { 261 std::unique_ptr<PageLoadTracker> tracker) {
237 tracker->FailedProvisionalLoad(navigation_handle); 262 tracker->FailedProvisionalLoad(navigation_handle);
238 263
239 net::Error error = navigation_handle->GetNetErrorCode(); 264 net::Error error = navigation_handle->GetNetErrorCode();
240 265
241 // net::OK: This case occurs when the NavigationHandle finishes and reports 266 // net::OK: This case occurs when the NavigationHandle finishes and reports
242 // !HasCommitted(), but reports no net::Error. This should not occur 267 // !HasCommitted(), but reports no net::Error. This should not occur
243 // pre-PlzNavigate, but afterwards it should represent the navigation stopped 268 // pre-PlzNavigate, but afterwards it should represent the navigation stopped
244 // by the user before it was ready to commit. 269 // by the user before it was ready to commit.
245 // net::ERR_ABORTED: An aborted provisional load has error 270 // net::ERR_ABORTED: An aborted provisional load has error
246 // net::ERR_ABORTED. 271 // net::ERR_ABORTED.
247 if ((error == net::OK) || (error == net::ERR_ABORTED)) { 272 if ((error == net::OK) || (error == net::ERR_ABORTED)) {
248 tracker->NotifyAbort(ABORT_OTHER, false, base::TimeTicks::Now(), true); 273 tracker->NotifyAbort(ABORT_OTHER, UserInitiatedInfo::NotUserInitiated(),
274 base::TimeTicks::Now(), true);
249 aborted_provisional_loads_.push_back(std::move(tracker)); 275 aborted_provisional_loads_.push_back(std::move(tracker));
250 } 276 }
251 } 277 }
252 278
253 void MetricsWebContentsObserver::HandleCommittedNavigationForTrackedLoad( 279 void MetricsWebContentsObserver::HandleCommittedNavigationForTrackedLoad(
254 content::NavigationHandle* navigation_handle, 280 content::NavigationHandle* navigation_handle,
255 std::unique_ptr<PageLoadTracker> tracker) { 281 std::unique_ptr<PageLoadTracker> tracker) {
256 if (!IsNavigationUserInitiated(navigation_handle) && 282 if (!IsNavigationUserInitiated(navigation_handle) &&
257 (navigation_handle->GetPageTransition() & 283 (navigation_handle->GetPageTransition() &
258 ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0 && 284 ui::PAGE_TRANSITION_CLIENT_REDIRECT) != 0 &&
259 committed_load_) 285 committed_load_) {
286 // TODO(bmcquade): consider carrying the user_gesture bit forward to the
287 // redirected navigation.
260 committed_load_->NotifyClientRedirectTo(*tracker); 288 committed_load_->NotifyClientRedirectTo(*tracker);
289 }
261 290
262 committed_load_ = std::move(tracker); 291 committed_load_ = std::move(tracker);
263 committed_load_->Commit(navigation_handle); 292 committed_load_->Commit(navigation_handle);
264 } 293 }
265 294
266 void MetricsWebContentsObserver::NavigationStopped() { 295 void MetricsWebContentsObserver::NavigationStopped() {
267 // TODO(csharrison): Use a more user-initiated signal for STOP. 296 // TODO(csharrison): Use a more user-initiated signal for STOP.
268 NotifyAbortAllLoads(ABORT_STOP, false); 297 NotifyAbortAllLoads(ABORT_STOP, UserInitiatedInfo::NotUserInitiated());
269 } 298 }
270 299
271 void MetricsWebContentsObserver::OnInputEvent( 300 void MetricsWebContentsObserver::OnInputEvent(
272 const blink::WebInputEvent& event) { 301 const blink::WebInputEvent& event) {
273 // Ignore browser navigation or reload which comes with type Undefined. 302 // Ignore browser navigation or reload which comes with type Undefined.
274 if (event.type == blink::WebInputEvent::Type::Undefined) 303 if (event.type == blink::WebInputEvent::Type::Undefined)
275 return; 304 return;
276 305
277 if (committed_load_) 306 if (committed_load_)
278 committed_load_->OnInputEvent(event); 307 committed_load_->OnInputEvent(event);
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 return; 369 return;
341 } 370 }
342 371
343 // If this is a crash, eagerly log the aborted provisional loads and the 372 // If this is a crash, eagerly log the aborted provisional loads and the
344 // committed load. |provisional_loads_| don't need to be destroyed here 373 // committed load. |provisional_loads_| don't need to be destroyed here
345 // because their lifetime is tied to the NavigationHandle. 374 // because their lifetime is tied to the NavigationHandle.
346 committed_load_.reset(); 375 committed_load_.reset();
347 aborted_provisional_loads_.clear(); 376 aborted_provisional_loads_.clear();
348 } 377 }
349 378
350 void MetricsWebContentsObserver::NotifyAbortAllLoads(UserAbortType abort_type, 379 void MetricsWebContentsObserver::NotifyAbortAllLoads(
351 bool user_initiated) { 380 UserAbortType abort_type,
352 NotifyAbortAllLoadsWithTimestamp(abort_type, user_initiated, 381 UserInitiatedInfo user_initiated_info) {
382 NotifyAbortAllLoadsWithTimestamp(abort_type, user_initiated_info,
353 base::TimeTicks::Now(), true); 383 base::TimeTicks::Now(), true);
354 } 384 }
355 385
356 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp( 386 void MetricsWebContentsObserver::NotifyAbortAllLoadsWithTimestamp(
357 UserAbortType abort_type, 387 UserAbortType abort_type,
358 bool user_initiated, 388 UserInitiatedInfo user_initiated_info,
359 base::TimeTicks timestamp, 389 base::TimeTicks timestamp,
360 bool is_certainly_browser_timestamp) { 390 bool is_certainly_browser_timestamp) {
361 if (committed_load_) { 391 if (committed_load_) {
362 committed_load_->NotifyAbort(abort_type, user_initiated, timestamp, 392 committed_load_->NotifyAbort(abort_type, user_initiated_info, timestamp,
363 is_certainly_browser_timestamp); 393 is_certainly_browser_timestamp);
364 } 394 }
365 for (const auto& kv : provisional_loads_) { 395 for (const auto& kv : provisional_loads_) {
366 kv.second->NotifyAbort(abort_type, user_initiated, timestamp, 396 kv.second->NotifyAbort(abort_type, user_initiated_info, timestamp,
367 is_certainly_browser_timestamp); 397 is_certainly_browser_timestamp);
368 } 398 }
369 for (const auto& tracker : aborted_provisional_loads_) { 399 for (const auto& tracker : aborted_provisional_loads_) {
370 if (tracker->IsLikelyProvisionalAbort(timestamp)) { 400 if (tracker->IsLikelyProvisionalAbort(timestamp)) {
371 tracker->UpdateAbort(abort_type, user_initiated, timestamp, 401 tracker->UpdateAbort(abort_type, user_initiated_info, timestamp,
372 is_certainly_browser_timestamp); 402 is_certainly_browser_timestamp);
373 } 403 }
374 } 404 }
375 aborted_provisional_loads_.clear(); 405 aborted_provisional_loads_.clear();
376 } 406 }
377 407
378 std::unique_ptr<PageLoadTracker> 408 std::unique_ptr<PageLoadTracker>
379 MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation( 409 MetricsWebContentsObserver::NotifyAbortedProvisionalLoadsNewNavigation(
380 content::NavigationHandle* new_navigation) { 410 content::NavigationHandle* new_navigation,
411 UserInitiatedInfo user_initiated_info) {
381 // If there are multiple aborted loads that can be attributed to this one, 412 // If there are multiple aborted loads that can be attributed to this one,
382 // just count the latest one for simplicity. Other loads will fall into the 413 // just count the latest one for simplicity. Other loads will fall into the
383 // OTHER bucket, though there shouldn't be very many. 414 // OTHER bucket, though there shouldn't be very many.
384 if (aborted_provisional_loads_.size() == 0) 415 if (aborted_provisional_loads_.size() == 0)
385 return nullptr; 416 return nullptr;
386 if (aborted_provisional_loads_.size() > 1) 417 if (aborted_provisional_loads_.size() > 1)
387 RecordInternalError(ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS); 418 RecordInternalError(ERR_NAVIGATION_SIGNALS_MULIPLE_ABORTED_LOADS);
388 419
389 std::unique_ptr<PageLoadTracker> last_aborted_load = 420 std::unique_ptr<PageLoadTracker> last_aborted_load =
390 std::move(aborted_provisional_loads_.back()); 421 std::move(aborted_provisional_loads_.back());
391 aborted_provisional_loads_.pop_back(); 422 aborted_provisional_loads_.pop_back();
392 423
393 base::TimeTicks timestamp = new_navigation->NavigationStart(); 424 base::TimeTicks timestamp = new_navigation->NavigationStart();
394 if (last_aborted_load->IsLikelyProvisionalAbort(timestamp)) { 425 if (last_aborted_load->IsLikelyProvisionalAbort(timestamp)) {
395 last_aborted_load->UpdateAbort( 426 last_aborted_load->UpdateAbort(
396 AbortTypeForPageTransition(new_navigation->GetPageTransition()), 427 AbortTypeForPageTransition(new_navigation->GetPageTransition()),
397 IsNavigationUserInitiated(new_navigation), timestamp, false); 428 user_initiated_info, timestamp, false);
398 } 429 }
399 430
400 aborted_provisional_loads_.clear(); 431 aborted_provisional_loads_.clear();
401 return last_aborted_load; 432 return last_aborted_load;
402 } 433 }
403 434
404 void MetricsWebContentsObserver::OnTimingUpdated( 435 void MetricsWebContentsObserver::OnTimingUpdated(
405 content::RenderFrameHost* render_frame_host, 436 content::RenderFrameHost* render_frame_host,
406 const PageLoadTiming& timing, 437 const PageLoadTiming& timing,
407 const PageLoadMetadata& metadata) { 438 const PageLoadMetadata& metadata) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 content::NavigationHandle* navigation_handle) const { 473 content::NavigationHandle* navigation_handle) const {
443 DCHECK(navigation_handle->IsInMainFrame()); 474 DCHECK(navigation_handle->IsInMainFrame());
444 DCHECK(!navigation_handle->HasCommitted() || 475 DCHECK(!navigation_handle->HasCommitted() ||
445 !navigation_handle->IsSamePage()); 476 !navigation_handle->IsSamePage());
446 477
447 return BrowserPageTrackDecider(embedder_interface_.get(), web_contents(), 478 return BrowserPageTrackDecider(embedder_interface_.get(), web_contents(),
448 navigation_handle).ShouldTrack(); 479 navigation_handle).ShouldTrack();
449 } 480 }
450 481
451 } // namespace page_load_metrics 482 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698