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

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

Issue 1357403003: Separate page load metrics for backgrounded pages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Keep track of all provisional navigation handles + general refactor 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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
42 !timing.load_event_start.is_zero(), 42 !timing.load_event_start.is_zero(),
43 !timing.dom_content_loaded_event_start.is_zero() && 43 !timing.dom_content_loaded_event_start.is_zero() &&
44 timing.response_start <= timing.load_event_start && 44 timing.response_start <= timing.load_event_start &&
45 timing.dom_content_loaded_event_start <= timing.load_event_start); 45 timing.dom_content_loaded_event_start <= timing.load_event_start);
46 46
47 return true; 47 return true;
48 } 48 }
49 49
50 } // namespace 50 } // namespace
51 51
52 #define PAGE_LOAD_HISTOGRAM(name, sample) \
53 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \
54 base::TimeDelta::FromMilliseconds(10), \
55 base::TimeDelta::FromMinutes(10), 100);
56
57 PageLoadTracker::PageLoadTracker(content::NavigationHandle* navigation_handle,
58 bool in_foreground)
59 : navigation_handle_(navigation_handle),
60 page_load_stayed_in_foreground_(in_foreground) {}
Bryan McQuade 2015/09/24 15:14:11 also need to set initial values for has_commit_ an
Charlie Harrison 2015/09/24 15:48:21 Done.
61
62 PageLoadTracker::~PageLoadTracker() {
63 if (has_commit_)
64 RecordTimingHistograms();
65 }
66
67 void PageLoadTracker::WebContentsHidden() {
68 if (!has_finished_)
69 page_load_stayed_in_foreground_ = false;
70 }
71
72 void PageLoadTracker::Commit() {
73 has_commit_ = true;
74 }
75
76 void PageLoadTracker::Finish() {
77 has_finished_ = true;
78 }
79
80 bool PageLoadTracker::UpdateTiming(const PageLoadTiming& timing) {
81 // Throw away IPCs that are not relevant to the current navigation.
82 if (!timing_.navigation_start.is_null() &&
83 timing_.navigation_start != timing.navigation_start) {
84 // TODO(csharrison) uma log a counter here
85 return false;
86 }
87 if (IsValidPageLoadTiming(timing)) {
88 timing_ = timing;
89 return true;
90 }
91 return false;
92 }
93
94 void PageLoadTracker::RecordTimingHistograms() {
95 DCHECK(has_commit_);
96 if (!timing_.dom_content_loaded_event_start.is_zero()) {
97 if (page_load_stayed_in_foreground_) {
98 PAGE_LOAD_HISTOGRAM(
99 "PageLoad.Timing.NavigationToDOMContentLoadedEventFired",
100 timing_.dom_content_loaded_event_start);
101 } else {
102 PAGE_LOAD_HISTOGRAM(
103 "PageLoad.Timing.BG.NavigationToDOMContentLoadedEventFired",
104 timing_.dom_content_loaded_event_start);
105 }
106 }
107 if (!timing_.load_event_start.is_zero()) {
108 if (page_load_stayed_in_foreground_) {
109 PAGE_LOAD_HISTOGRAM("PageLoad.Timing.NavigationToLoadEventFired",
110 timing_.load_event_start);
111 } else {
112 PAGE_LOAD_HISTOGRAM("PageLoad.Timing.BG.NavigationToLoadEventFired",
113 timing_.load_event_start);
114 }
115 }
116 if (!timing_.first_layout.is_zero()) {
117 if (page_load_stayed_in_foreground_) {
118 PAGE_LOAD_HISTOGRAM("PageLoad.Timing.NavigationToFirstLayout",
119 timing_.first_layout);
120 } else {
121 PAGE_LOAD_HISTOGRAM("PageLoad.Timing.BG.NavigationToFirstLayout",
122 timing_.first_layout);
123 }
124 }
125 }
126
52 MetricsWebContentsObserver::MetricsWebContentsObserver( 127 MetricsWebContentsObserver::MetricsWebContentsObserver(
53 content::WebContents* web_contents) 128 content::WebContents* web_contents)
54 : content::WebContentsObserver(web_contents) {} 129 : content::WebContentsObserver(web_contents),
130 in_foreground_(false),
131 committed_load_(nullptr) {}
Bryan McQuade 2015/09/24 15:14:11 scoped_ptr has a default constructor which initial
Charlie Harrison 2015/09/24 15:48:21 Done.
55 132
56 // This object is tied to a single WebContents for its entire lifetime. 133 MetricsWebContentsObserver::~MetricsWebContentsObserver() {}
57 MetricsWebContentsObserver::~MetricsWebContentsObserver() {
58 RecordTimingHistograms();
59 }
60 134
61 bool MetricsWebContentsObserver::OnMessageReceived( 135 bool MetricsWebContentsObserver::OnMessageReceived(
62 const IPC::Message& message, 136 const IPC::Message& message,
63 content::RenderFrameHost* render_frame_host) { 137 content::RenderFrameHost* render_frame_host) {
64 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 138 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
65 bool handled = true; 139 bool handled = true;
66 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message, 140 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MetricsWebContentsObserver, message,
67 render_frame_host) 141 render_frame_host)
68 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated) 142 IPC_MESSAGE_HANDLER(PageLoadMetricsMsg_TimingUpdated, OnTimingUpdated)
69 IPC_MESSAGE_UNHANDLED(handled = false) 143 IPC_MESSAGE_UNHANDLED(handled = false)
70 IPC_END_MESSAGE_MAP() 144 IPC_END_MESSAGE_MAP()
71 return handled; 145 return handled;
72 } 146 }
73 147
74 void MetricsWebContentsObserver::DidCommitNavigation( 148 void MetricsWebContentsObserver::DidCommitNavigation(
75 content::NavigationHandle* navigation_handle) { 149 content::NavigationHandle* navigation_handle) {
76 if (navigation_handle->IsInMainFrame() && !navigation_handle->IsSamePage()) 150 if (!navigation_handle->IsInMainFrame())
Bryan McQuade 2015/09/24 15:14:11 why did the IsSamePage test go away?
77 RecordTimingHistograms(); 151 return;
78 if (IsRelevantNavigation(navigation_handle)) 152
79 current_timing_.reset(new PageLoadTiming()); 153 // If the provisional load is a relevant navigation (which we only know for
154 // sure after commit), then we update |committed_load_|. We always remove a
155 // provisional navigation from |provisional_loads_| on commit.
156 for (auto iter = provisional_loads_.begin();
Bryan McQuade 2015/09/24 15:14:11 if there is a real 1:1 relationship between Naviga
Charlie Harrison 2015/09/24 15:48:21 I considered this, but the PageLoadTracker has a l
Bryan McQuade 2015/09/24 15:55:54 Ah, I see. Let's chat about this in person (Monday
157 iter != provisional_loads_.end();) {
158 if (iter->NavigationHandle() == navigation_handle) {
159 if (IsRelevantNavigation(navigation_handle)) {
160 committed_load_.reset(new PageLoadTracker(*iter));
Bryan McQuade 2015/09/24 15:14:11 I'd rather avoid allocating a new instance of the
161 committed_load_->Commit();
162 }
163 iter = provisional_loads_.erase(iter);
164 } else {
165 ++iter;
166 }
167 }
80 } 168 }
81 169
82 // This will occur when the process for the main RenderFrameHost exits. 170 void MetricsWebContentsObserver::DidStartNavigation(
83 // This will happen with a normal exit or a crash. 171 content::NavigationHandle* navigation_handle) {
84 void MetricsWebContentsObserver::RenderProcessGone( 172 if (!navigation_handle->IsInMainFrame())
85 base::TerminationStatus status) { 173 return;
86 RecordTimingHistograms(); 174 DCHECK(provisional_loads_.size() < 2);
175 provisional_loads_.push_back(
Bryan McQuade 2015/09/24 15:14:11 based on the dcheck, it sounds like we can have up
Charlie Harrison 2015/09/24 15:48:21 Sure, this is documented in clamy's replies. There
Bryan McQuade 2015/09/24 15:55:54 ah, ok, cool. let's just make sure to add a commen
176 PageLoadTracker(navigation_handle, in_foreground_));
87 } 177 }
88 178
89 #define PAGE_LOAD_HISTOGRAM(name, sample) \ 179 void MetricsWebContentsObserver::DidFinishLoad(
90 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, \ 180 content::RenderFrameHost* render_frame_host,
91 base::TimeDelta::FromMilliseconds(10), \ 181 const GURL& validated_url) {
92 base::TimeDelta::FromMinutes(10), 100); 182 if (render_frame_host == web_contents()->GetMainFrame() && committed_load_)
183 committed_load_->Finish();
184 }
185
186 void MetricsWebContentsObserver::DidFailLoad(
187 content::RenderFrameHost* render_frame_host,
188 const GURL& validated_url,
189 int error_code,
190 const base::string16& error_description,
191 bool was_ignored_by_handler) {
192 if (render_frame_host == web_contents()->GetMainFrame() && committed_load_)
193 committed_load_->Finish();
194 }
195
196 void MetricsWebContentsObserver::WasShown() {
197 in_foreground_ = true;
198 }
199 void MetricsWebContentsObserver::WasHidden() {
200 in_foreground_ = false;
201 if (committed_load_)
202 committed_load_->WebContentsHidden();
203 for (PageLoadTracker& page_load : provisional_loads_)
204 page_load.WebContentsHidden();
205 }
93 206
94 void MetricsWebContentsObserver::OnTimingUpdated( 207 void MetricsWebContentsObserver::OnTimingUpdated(
95 content::RenderFrameHost* render_frame_host, 208 content::RenderFrameHost* render_frame_host,
96 const PageLoadTiming& timing) { 209 const PageLoadTiming& timing) {
97 if (!current_timing_)
98 return;
99
100 // We may receive notifications from frames that have been navigated away 210 // We may receive notifications from frames that have been navigated away
101 // from. We simply ignore them. 211 // from. We simply ignore them.
102 if (render_frame_host != web_contents()->GetMainFrame()) 212 if (render_frame_host != web_contents()->GetMainFrame())
103 return; 213 return;
104 214
105 // For urls like chrome://newtab, the renderer and browser disagree, 215 // For urls like chrome://newtab, the renderer and browser disagree,
106 // so we have to double check that the renderer isn't sending data from a 216 // so we have to double check that the renderer isn't sending data from a
107 // bad url like https://www.google.com/_/chrome/newtab. 217 // bad url like https://www.google.com/_/chrome/newtab.
108 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) 218 if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS())
109 return; 219 return;
110 220
111 // Throw away IPCs that are not relevant to the current navigation. 221 committed_load_->UpdateTiming(timing);
112 if (!current_timing_->navigation_start.is_null() &&
113 timing.navigation_start != current_timing_->navigation_start) {
114 // TODO(csharrison) uma log a counter here
115 return;
116 }
117
118 *current_timing_ = timing;
119 }
120
121 void MetricsWebContentsObserver::RecordTimingHistograms() {
122 if (!current_timing_ || !IsValidPageLoadTiming(*current_timing_))
123 return;
124
125 if (!current_timing_->dom_content_loaded_event_start.is_zero()) {
126 PAGE_LOAD_HISTOGRAM(
127 "PageLoad.Timing.NavigationToDOMContentLoadedEventFired",
128 current_timing_->dom_content_loaded_event_start);
129 }
130
131 if (!current_timing_->load_event_start.is_zero()) {
132 PAGE_LOAD_HISTOGRAM("PageLoad.Timing.NavigationToLoadEventFired",
133 current_timing_->load_event_start);
134 }
135
136 if (!current_timing_->first_layout.is_zero()) {
137 PAGE_LOAD_HISTOGRAM("PageLoad.Timing.NavigationToFirstLayout",
138 current_timing_->first_layout);
139 }
140 current_timing_.reset();
141 } 222 }
142 223
143 bool MetricsWebContentsObserver::IsRelevantNavigation( 224 bool MetricsWebContentsObserver::IsRelevantNavigation(
144 content::NavigationHandle* navigation_handle) { 225 content::NavigationHandle* navigation_handle) {
145 // The url we see from the renderer side is not always the same as what 226 // The url we see from the renderer side is not always the same as what
146 // we see from the browser side (e.g. chrome://newtab). We want to be 227 // we see from the browser side (e.g. chrome://newtab). We want to be
147 // sure here that we aren't logging UMA for internal pages. 228 // sure here that we aren't logging UMA for internal pages.
148 const GURL& browser_url = web_contents()->GetLastCommittedURL(); 229 const GURL& browser_url = web_contents()->GetLastCommittedURL();
149 return navigation_handle->IsInMainFrame() && 230 return navigation_handle->IsInMainFrame() &&
150 !navigation_handle->IsSamePage() && 231 !navigation_handle->IsSamePage() &&
151 navigation_handle->HasCommittedDocument() && 232 navigation_handle->HasCommittedDocument() &&
152 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() && 233 navigation_handle->GetURL().SchemeIsHTTPOrHTTPS() &&
153 browser_url.SchemeIsHTTPOrHTTPS(); 234 browser_url.SchemeIsHTTPOrHTTPS();
154 } 235 }
155 236
156 } // namespace page_load_metrics 237 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698