Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 |
| OLD | NEW |