| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // TODO(bmcquade): delete this class in October 2016, as it is deprecated by the | |
| 6 // new PageLoad.* UMA histograms. | |
| 7 | |
| 8 #include "chrome/renderer/page_load_histograms.h" | |
| 9 | |
| 10 #include <stddef.h> | |
| 11 | |
| 12 #include <memory> | |
| 13 #include <string> | |
| 14 | |
| 15 #include "base/command_line.h" | |
| 16 #include "base/logging.h" | |
| 17 #include "base/metrics/field_trial.h" | |
| 18 #include "base/metrics/histogram.h" | |
| 19 #include "base/metrics/persistent_histogram_allocator.h" | |
| 20 #include "base/strings/string_number_conversions.h" | |
| 21 #include "base/strings/string_split.h" | |
| 22 #include "base/strings/string_util.h" | |
| 23 #include "base/strings/stringprintf.h" | |
| 24 #include "base/strings/utf_string_conversions.h" | |
| 25 #include "base/time/time.h" | |
| 26 #include "chrome/common/chrome_switches.h" | |
| 27 #include "chrome/renderer/searchbox/search_bouncer.h" | |
| 28 #include "content/public/common/content_constants.h" | |
| 29 #include "content/public/renderer/document_state.h" | |
| 30 #include "content/public/renderer/render_frame.h" | |
| 31 #include "content/public/renderer/render_thread.h" | |
| 32 #include "content/public/renderer/render_view.h" | |
| 33 #include "extensions/common/url_pattern.h" | |
| 34 #include "net/base/url_util.h" | |
| 35 #include "net/http/http_response_headers.h" | |
| 36 #include "third_party/WebKit/public/platform/URLConversion.h" | |
| 37 #include "third_party/WebKit/public/platform/WebURLRequest.h" | |
| 38 #include "third_party/WebKit/public/platform/WebURLResponse.h" | |
| 39 #include "third_party/WebKit/public/web/WebDocument.h" | |
| 40 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
| 41 #include "third_party/WebKit/public/web/WebPerformance.h" | |
| 42 #include "third_party/WebKit/public/web/WebView.h" | |
| 43 #include "url/gurl.h" | |
| 44 | |
| 45 #if defined(ENABLE_EXTENSIONS) | |
| 46 #include "chrome/renderer/extensions/chrome_extensions_renderer_client.h" | |
| 47 #include "extensions/renderer/dispatcher.h" | |
| 48 #endif | |
| 49 | |
| 50 using blink::WebDataSource; | |
| 51 using blink::WebLocalFrame; | |
| 52 using blink::WebPerformance; | |
| 53 using blink::WebString; | |
| 54 using base::Time; | |
| 55 using base::TimeDelta; | |
| 56 using content::DocumentState; | |
| 57 | |
| 58 const size_t kPLTCount = 100; | |
| 59 | |
| 60 namespace { | |
| 61 | |
| 62 // ID indicating that no GWS-Chrome joint experiment is active. | |
| 63 const int kNoExperiment = 0; | |
| 64 | |
| 65 // Max ID of GWS-Chrome joint experiment. If you change this value, please | |
| 66 // update PLT_HISTOGRAM_WITH_GWS_VARIANT accordingly. | |
| 67 const int kMaxExperimentID = 20; | |
| 68 | |
| 69 TimeDelta kPLTMin() { | |
| 70 return TimeDelta::FromMilliseconds(10); | |
| 71 } | |
| 72 TimeDelta kPLTMax() { | |
| 73 return TimeDelta::FromMinutes(10); | |
| 74 } | |
| 75 | |
| 76 // This function corresponds to PLT_HISTOGRAM macro invocation without caching. | |
| 77 // Use this for PLT histograms with dynamically generated names, which | |
| 78 // otherwise can't use the caching PLT_HISTOGRAM macro without code duplication. | |
| 79 void PltHistogramWithNoMacroCaching(const std::string& name, | |
| 80 const TimeDelta& sample) { | |
| 81 // The parameters should exacly match the parameters in | |
| 82 // UMA_HISTOGRAM_CUSTOM_TIMES macro. | |
| 83 base::HistogramBase* histogram_pointer = base::Histogram::FactoryTimeGet( | |
| 84 name, kPLTMin(), kPLTMax(), kPLTCount, | |
| 85 base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 86 histogram_pointer->AddTime(sample); | |
| 87 } | |
| 88 | |
| 89 // This records UMA corresponding to the PLT_HISTOGRAM macro without caching. | |
| 90 void PltHistogramWithGwsPreview(const char* name, | |
| 91 const TimeDelta& sample, | |
| 92 bool is_preview, | |
| 93 int experiment_id) { | |
| 94 std::string preview_suffix = is_preview ? "_Preview" : "_NoPreview"; | |
| 95 PltHistogramWithNoMacroCaching(name + preview_suffix, sample); | |
| 96 | |
| 97 if (experiment_id != kNoExperiment) { | |
| 98 std::string name_with_experiment_id = base::StringPrintf( | |
| 99 "%s%s_Experiment%d", name, preview_suffix.c_str(), experiment_id); | |
| 100 PltHistogramWithNoMacroCaching(name_with_experiment_id, sample); | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 #define PLT_HISTOGRAM(name, sample) \ | |
| 105 UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, kPLTMin(), kPLTMax(), kPLTCount); | |
| 106 | |
| 107 #define PLT_HISTOGRAM_WITH_GWS_VARIANT( \ | |
| 108 name, sample, came_from_websearch, websearch_chrome_joint_experiment_id, \ | |
| 109 is_preview) { \ | |
| 110 PLT_HISTOGRAM(name, sample); \ | |
| 111 if (came_from_websearch) { \ | |
| 112 PLT_HISTOGRAM(base::StringPrintf("%s_FromGWS", name), sample) \ | |
| 113 if (websearch_chrome_joint_experiment_id != kNoExperiment) { \ | |
| 114 std::string name_with_experiment_id = base::StringPrintf( \ | |
| 115 "%s_FromGWS_Experiment%d", \ | |
| 116 name, websearch_chrome_joint_experiment_id); \ | |
| 117 PltHistogramWithNoMacroCaching(name_with_experiment_id, sample); \ | |
| 118 } \ | |
| 119 } \ | |
| 120 PltHistogramWithGwsPreview(name, sample, is_preview, \ | |
| 121 websearch_chrome_joint_experiment_id); \ | |
| 122 } | |
| 123 | |
| 124 // Returns the scheme type of the given URL if its type is one for which we | |
| 125 // dump page load histograms. Otherwise returns NULL. | |
| 126 URLPattern::SchemeMasks GetSupportedSchemeType(const GURL& url) { | |
| 127 if (url.SchemeIs("http")) | |
| 128 return URLPattern::SCHEME_HTTP; | |
| 129 else if (url.SchemeIs("https")) | |
| 130 return URLPattern::SCHEME_HTTPS; | |
| 131 return static_cast<URLPattern::SchemeMasks>(0); | |
| 132 } | |
| 133 | |
| 134 // Helper function to check for string in 'via' header. Returns true if | |
| 135 // |via_value| is one of the values listed in the Via header. | |
| 136 bool ViaHeaderContains(WebLocalFrame* frame, const std::string& via_value) { | |
| 137 const char kViaHeaderName[] = "Via"; | |
| 138 std::vector<std::string> values; | |
| 139 // Multiple via headers have already been coalesced and hence each value | |
| 140 // separated by a comma corresponds to a proxy. The value added by a proxy is | |
| 141 // not expected to contain any commas. | |
| 142 // Example., Via: 1.0 Compression proxy, 1.1 Google Instant Proxy Preview | |
| 143 values = base::SplitString( | |
| 144 frame->dataSource()->response().httpHeaderField(kViaHeaderName).utf8(), | |
| 145 ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | |
| 146 return std::find(values.begin(), values.end(), via_value) != values.end(); | |
| 147 } | |
| 148 | |
| 149 // Returns true if the provided URL is a referrer string that came from | |
| 150 // a Google Web Search results page. This is a little non-deterministic | |
| 151 // because desktop and mobile websearch differ and sometimes just provide | |
| 152 // http://www.google.com/ as the referrer. In the case of /url we can be sure | |
| 153 // that it came from websearch but we will be generous and allow for cases | |
| 154 // where a non-Google URL was provided a bare Google URL as a referrer. | |
| 155 // The domain validation matches the code used by the prerenderer for similar | |
| 156 // purposes. | |
| 157 // TODO(pmeenan): Remove the fuzzy logic when the referrer is reliable | |
| 158 bool IsFromGoogleSearchResult(const GURL& url, const GURL& referrer) { | |
| 159 if (!base::StartsWith(referrer.host(), "www.google.", | |
| 160 base::CompareCase::SENSITIVE)) | |
| 161 return false; | |
| 162 if (base::StartsWith(referrer.path(), "/url", | |
| 163 base::CompareCase::SENSITIVE)) | |
| 164 return true; | |
| 165 bool is_possible_search_referrer = | |
| 166 referrer.path().empty() || referrer.path() == "/" || | |
| 167 base::StartsWith(referrer.path(), "/search", | |
| 168 base::CompareCase::SENSITIVE) || | |
| 169 base::StartsWith(referrer.path(), "/webhp", | |
| 170 base::CompareCase::SENSITIVE); | |
| 171 if (is_possible_search_referrer && | |
| 172 !base::StartsWith(url.host(), "www.google", | |
| 173 base::CompareCase::SENSITIVE)) | |
| 174 return true; | |
| 175 return false; | |
| 176 } | |
| 177 | |
| 178 // Extracts a Google Web Search and Chrome joint experiment ID from a referrer | |
| 179 // that came from a Google Web Search results page. An experiment ID is embedded | |
| 180 // in a query string as a "gcjeid=" parameter value. | |
| 181 int GetQueryStringBasedExperiment(const GURL& referrer) { | |
| 182 std::string value; | |
| 183 if (!net::GetValueForKeyInQuery(referrer, "gcjeid", &value)) | |
| 184 return kNoExperiment; | |
| 185 | |
| 186 int experiment_id; | |
| 187 if (!base::StringToInt(value, &experiment_id)) | |
| 188 return kNoExperiment; | |
| 189 if (0 < experiment_id && experiment_id <= kMaxExperimentID) | |
| 190 return experiment_id; | |
| 191 return kNoExperiment; | |
| 192 } | |
| 193 | |
| 194 void DumpHistograms(const WebPerformance& performance, | |
| 195 DocumentState* document_state, | |
| 196 bool came_from_websearch, | |
| 197 int websearch_chrome_joint_experiment_id, | |
| 198 bool is_preview, | |
| 199 URLPattern::SchemeMasks scheme_type) { | |
| 200 // This function records new histograms based on the Navigation Timing | |
| 201 // records. As such, the histograms should not depend on the deprecated timing | |
| 202 // information collected in DocumentState. However, here for some reason we | |
| 203 // check if document_state->request_time() is null. TODO(ppi): find out why | |
| 204 // and remove DocumentState from the parameter list. | |
| 205 Time request = document_state->request_time(); | |
| 206 | |
| 207 Time navigation_start = Time::FromDoubleT(performance.navigationStart()); | |
| 208 Time request_start = Time::FromDoubleT(performance.requestStart()); | |
| 209 Time response_start = Time::FromDoubleT(performance.responseStart()); | |
| 210 Time dom_content_loaded_start = | |
| 211 Time::FromDoubleT(performance.domContentLoadedEventStart()); | |
| 212 Time load_event_start = Time::FromDoubleT(performance.loadEventStart()); | |
| 213 Time load_event_end = Time::FromDoubleT(performance.loadEventEnd()); | |
| 214 Time begin = (request.is_null() ? navigation_start : request_start); | |
| 215 | |
| 216 DCHECK(!navigation_start.is_null()); | |
| 217 | |
| 218 // It is possible for a document to have navigation_start time, but no | |
| 219 // request_start. An example is doing a window.open, which synchronously | |
| 220 // loads "about:blank", then using document.write add a meta http-equiv | |
| 221 // refresh tag, which causes a navigation. In such case, we will arrive at | |
| 222 // this function with no request/response timing data and identical load | |
| 223 // start/end values. Avoid logging this case, as it doesn't add any | |
| 224 // meaningful information to the histogram. | |
| 225 if (request_start.is_null()) | |
| 226 return; | |
| 227 | |
| 228 // TODO(dominich): Investigate conditions under which |load_event_start| and | |
| 229 // |load_event_end| may be NULL as in the non-PT_ case below. Examples in | |
| 230 // http://crbug.com/112006. | |
| 231 // DCHECK(!load_event_start.is_null()); | |
| 232 // DCHECK(!load_event_end.is_null()); | |
| 233 | |
| 234 if (document_state->web_timing_histograms_recorded()) | |
| 235 return; | |
| 236 document_state->set_web_timing_histograms_recorded(true); | |
| 237 | |
| 238 // TODO(simonjam): There is no way to distinguish between abandonment and | |
| 239 // intentional Javascript navigation before the load event fires. | |
| 240 // TODO(dominich): Load type breakdown | |
| 241 if (!load_event_start.is_null()) { | |
| 242 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_BeginToFinishDoc", | |
| 243 load_event_start - begin, | |
| 244 came_from_websearch, | |
| 245 websearch_chrome_joint_experiment_id, | |
| 246 is_preview); | |
| 247 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_CommitToFinishDoc", | |
| 248 load_event_start - response_start, | |
| 249 came_from_websearch, | |
| 250 websearch_chrome_joint_experiment_id, | |
| 251 is_preview); | |
| 252 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_RequestToFinishDoc", | |
| 253 load_event_start - navigation_start, | |
| 254 came_from_websearch, | |
| 255 websearch_chrome_joint_experiment_id, | |
| 256 is_preview); | |
| 257 } | |
| 258 if (!load_event_end.is_null()) { | |
| 259 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_BeginToFinish", | |
| 260 load_event_end - begin, | |
| 261 came_from_websearch, | |
| 262 websearch_chrome_joint_experiment_id, | |
| 263 is_preview); | |
| 264 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_CommitToFinish", | |
| 265 load_event_end - response_start, | |
| 266 came_from_websearch, | |
| 267 websearch_chrome_joint_experiment_id, | |
| 268 is_preview); | |
| 269 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_RequestToFinish", | |
| 270 load_event_end - navigation_start, | |
| 271 came_from_websearch, | |
| 272 websearch_chrome_joint_experiment_id, | |
| 273 is_preview); | |
| 274 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_StartToFinish", | |
| 275 load_event_end - request_start, | |
| 276 came_from_websearch, | |
| 277 websearch_chrome_joint_experiment_id, | |
| 278 is_preview); | |
| 279 } | |
| 280 if (!load_event_start.is_null() && !load_event_end.is_null()) { | |
| 281 PLT_HISTOGRAM("PLT.PT_FinishDocToFinish", | |
| 282 load_event_end - load_event_start); | |
| 283 } | |
| 284 if (!dom_content_loaded_start.is_null()) { | |
| 285 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_RequestToDomContentLoaded", | |
| 286 dom_content_loaded_start - navigation_start, | |
| 287 came_from_websearch, | |
| 288 websearch_chrome_joint_experiment_id, | |
| 289 is_preview); | |
| 290 } | |
| 291 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_BeginToCommit", | |
| 292 response_start - begin, | |
| 293 came_from_websearch, | |
| 294 websearch_chrome_joint_experiment_id, | |
| 295 is_preview); | |
| 296 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_RequestToStart", | |
| 297 request_start - navigation_start, | |
| 298 came_from_websearch, | |
| 299 websearch_chrome_joint_experiment_id, | |
| 300 is_preview); | |
| 301 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_StartToCommit", | |
| 302 response_start - request_start, | |
| 303 came_from_websearch, | |
| 304 websearch_chrome_joint_experiment_id, | |
| 305 is_preview); | |
| 306 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.PT_RequestToCommit", | |
| 307 response_start - navigation_start, | |
| 308 came_from_websearch, | |
| 309 websearch_chrome_joint_experiment_id, | |
| 310 is_preview); | |
| 311 } | |
| 312 | |
| 313 bool WasWebRequestUsedBySomeExtensions() { | |
| 314 #if defined(ENABLE_EXTENSIONS) | |
| 315 return ChromeExtensionsRendererClient::GetInstance()->extension_dispatcher() | |
| 316 ->WasWebRequestUsedBySomeExtensions(); | |
| 317 #else | |
| 318 return false; | |
| 319 #endif | |
| 320 } | |
| 321 | |
| 322 // These histograms are based on the timing information collected in | |
| 323 // DocumentState. They should be transitioned to equivalents based on the | |
| 324 // Navigation Timing records (see DumpPerformanceTiming()) or dropped if not | |
| 325 // needed. Please do not add new metrics based on DocumentState. | |
| 326 void DumpDeprecatedHistograms(const WebPerformance& performance, | |
| 327 DocumentState* document_state, | |
| 328 bool came_from_websearch, | |
| 329 int websearch_chrome_joint_experiment_id, | |
| 330 bool is_preview, | |
| 331 URLPattern::SchemeMasks scheme_type) { | |
| 332 // If we've already dumped, do nothing. | |
| 333 // This simple bool works because we only dump for the main frame. | |
| 334 if (document_state->load_histograms_recorded()) | |
| 335 return; | |
| 336 | |
| 337 // Abort if any of these is missing. | |
| 338 Time start = document_state->start_load_time(); | |
| 339 Time commit = document_state->commit_load_time(); | |
| 340 Time navigation_start = | |
| 341 Time::FromDoubleT(performance.navigationStart()); | |
| 342 if (start.is_null() || commit.is_null() || navigation_start.is_null()) | |
| 343 return; | |
| 344 | |
| 345 // We properly handle null values for the next 3 variables. | |
| 346 Time request = document_state->request_time(); | |
| 347 Time first_paint = document_state->first_paint_time(); | |
| 348 Time first_paint_after_load = document_state->first_paint_after_load_time(); | |
| 349 Time finish_doc = document_state->finish_document_load_time(); | |
| 350 Time finish_all_loads = document_state->finish_load_time(); | |
| 351 | |
| 352 // Handle case where user hits "stop" or "back" before loading completely. | |
| 353 // Note that this makes abandoned page loads be recorded as if they were | |
| 354 // completed, polluting the metrics with artifically short completion times. | |
| 355 // We are not fixing this as these metrics are being dropped as deprecated. | |
| 356 if (finish_doc.is_null()) { | |
| 357 finish_doc = Time::Now(); | |
| 358 document_state->set_finish_document_load_time(finish_doc); | |
| 359 } | |
| 360 if (finish_all_loads.is_null()) { | |
| 361 finish_all_loads = Time::Now(); | |
| 362 document_state->set_finish_load_time(finish_all_loads); | |
| 363 } | |
| 364 | |
| 365 document_state->set_load_histograms_recorded(true); | |
| 366 | |
| 367 // Note: Client side redirects will have no request time. | |
| 368 Time begin = request.is_null() ? start : request; | |
| 369 TimeDelta begin_to_finish_doc = finish_doc - begin; | |
| 370 TimeDelta begin_to_finish_all_loads = finish_all_loads - begin; | |
| 371 TimeDelta start_to_finish_all_loads = finish_all_loads - start; | |
| 372 TimeDelta start_to_commit = commit - start; | |
| 373 | |
| 374 DocumentState::LoadType load_type = document_state->load_type(); | |
| 375 | |
| 376 // The above code sanitized all values of times, in preparation for creating | |
| 377 // actual histograms. The remainder of this code could be run at destructor | |
| 378 // time for the document_state, since all data is intact. | |
| 379 | |
| 380 // Aggregate PLT data across all link types. | |
| 381 UMA_HISTOGRAM_ENUMERATION("PLT.LoadType", load_type, | |
| 382 DocumentState::kLoadTypeMax); | |
| 383 PLT_HISTOGRAM("PLT.StartToCommit", start_to_commit); | |
| 384 PLT_HISTOGRAM("PLT.CommitToFinishDoc", finish_doc - commit); | |
| 385 PLT_HISTOGRAM("PLT.FinishDocToFinish", finish_all_loads - finish_doc); | |
| 386 PLT_HISTOGRAM("PLT.BeginToCommit", commit - begin); | |
| 387 PLT_HISTOGRAM("PLT.StartToFinish", start_to_finish_all_loads); | |
| 388 if (!request.is_null()) { | |
| 389 PLT_HISTOGRAM("PLT.RequestToStart", start - request); | |
| 390 PLT_HISTOGRAM("PLT.RequestToFinish", finish_all_loads - request); | |
| 391 } | |
| 392 PLT_HISTOGRAM("PLT.CommitToFinish", finish_all_loads - commit); | |
| 393 | |
| 394 std::unique_ptr<TimeDelta> begin_to_first_paint; | |
| 395 std::unique_ptr<TimeDelta> commit_to_first_paint; | |
| 396 if (!first_paint.is_null()) { | |
| 397 // 'first_paint' can be before 'begin' for an unknown reason. | |
| 398 // See bug http://crbug.com/125273 for details. | |
| 399 if (begin <= first_paint) { | |
| 400 begin_to_first_paint.reset(new TimeDelta(first_paint - begin)); | |
| 401 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.BeginToFirstPaint", | |
| 402 *begin_to_first_paint, | |
| 403 came_from_websearch, | |
| 404 websearch_chrome_joint_experiment_id, | |
| 405 is_preview); | |
| 406 } else { | |
| 407 // Track the frequency and magnitude of cases where first_paint precedes | |
| 408 // begin. The current hypothesis is that this is due to using the | |
| 409 // non-monotonic timer. If that's the case, we expect first_paint | |
| 410 // preceding begin to be rare, and the delta between values to be close to | |
| 411 // zero. This is a temporary addition that we will remove once we better | |
| 412 // understand the frequency and magnitude of first_paint preceding begin. | |
| 413 PLT_HISTOGRAM("PLT.BeginToFirstPaint_Negative", begin - first_paint); | |
| 414 } | |
| 415 | |
| 416 // Conditional was previously a DCHECK. Changed due to multiple bot | |
| 417 // failures, listed in crbug.com/383963 | |
| 418 if (commit <= first_paint) { | |
| 419 commit_to_first_paint.reset(new TimeDelta(first_paint - commit)); | |
| 420 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.CommitToFirstPaint", | |
| 421 *commit_to_first_paint, | |
| 422 came_from_websearch, | |
| 423 websearch_chrome_joint_experiment_id, | |
| 424 is_preview); | |
| 425 } | |
| 426 } | |
| 427 if (!first_paint_after_load.is_null()) { | |
| 428 // 'first_paint_after_load' can be before 'begin' for an unknown reason. | |
| 429 // See bug http://crbug.com/125273 for details. | |
| 430 if (begin <= first_paint_after_load) { | |
| 431 PLT_HISTOGRAM("PLT.BeginToFirstPaintAfterLoad", | |
| 432 first_paint_after_load - begin); | |
| 433 } | |
| 434 // Both following conditionals were previously DCHECKs. Changed due to | |
| 435 // multiple bot failures, listed in crbug.com/383963 | |
| 436 if (commit <= first_paint_after_load) { | |
| 437 PLT_HISTOGRAM("PLT.CommitToFirstPaintAfterLoad", | |
| 438 first_paint_after_load - commit); | |
| 439 } | |
| 440 if (finish_all_loads <= first_paint_after_load) { | |
| 441 PLT_HISTOGRAM("PLT.FinishToFirstPaintAfterLoad", | |
| 442 first_paint_after_load - finish_all_loads); | |
| 443 } | |
| 444 } | |
| 445 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.BeginToFinishDoc", begin_to_finish_doc, | |
| 446 came_from_websearch, | |
| 447 websearch_chrome_joint_experiment_id, | |
| 448 is_preview); | |
| 449 PLT_HISTOGRAM_WITH_GWS_VARIANT("PLT.BeginToFinish", begin_to_finish_all_loads, | |
| 450 came_from_websearch, | |
| 451 websearch_chrome_joint_experiment_id, | |
| 452 is_preview); | |
| 453 | |
| 454 // Load type related histograms. | |
| 455 switch (load_type) { | |
| 456 case DocumentState::UNDEFINED_LOAD: | |
| 457 PLT_HISTOGRAM("PLT.BeginToFinishDoc_UndefLoad", begin_to_finish_doc); | |
| 458 PLT_HISTOGRAM("PLT.BeginToFinish_UndefLoad", begin_to_finish_all_loads); | |
| 459 break; | |
| 460 case DocumentState::RELOAD: | |
| 461 PLT_HISTOGRAM("PLT.BeginToFinishDoc_Reload", begin_to_finish_doc); | |
| 462 PLT_HISTOGRAM("PLT.BeginToFinish_Reload", begin_to_finish_all_loads); | |
| 463 break; | |
| 464 case DocumentState::HISTORY_LOAD: | |
| 465 PLT_HISTOGRAM("PLT.BeginToFinishDoc_HistoryLoad", begin_to_finish_doc); | |
| 466 PLT_HISTOGRAM("PLT.BeginToFinish_HistoryLoad", begin_to_finish_all_loads); | |
| 467 break; | |
| 468 case DocumentState::NORMAL_LOAD: | |
| 469 PLT_HISTOGRAM("PLT.BeginToFinishDoc_NormalLoad", begin_to_finish_doc); | |
| 470 PLT_HISTOGRAM("PLT.BeginToFinish_NormalLoad", begin_to_finish_all_loads); | |
| 471 break; | |
| 472 case DocumentState::LINK_LOAD_NORMAL: | |
| 473 PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadNormal", | |
| 474 begin_to_finish_doc); | |
| 475 PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadNormal", | |
| 476 begin_to_finish_all_loads); | |
| 477 break; | |
| 478 case DocumentState::LINK_LOAD_RELOAD: | |
| 479 PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadReload", | |
| 480 begin_to_finish_doc); | |
| 481 PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadReload", | |
| 482 begin_to_finish_all_loads); | |
| 483 break; | |
| 484 case DocumentState::LINK_LOAD_CACHE_STALE_OK: | |
| 485 PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadStaleOk", | |
| 486 begin_to_finish_doc); | |
| 487 PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadStaleOk", | |
| 488 begin_to_finish_all_loads); | |
| 489 break; | |
| 490 case DocumentState::LINK_LOAD_CACHE_ONLY: | |
| 491 PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadCacheOnly", | |
| 492 begin_to_finish_doc); | |
| 493 PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadCacheOnly", | |
| 494 begin_to_finish_all_loads); | |
| 495 break; | |
| 496 default: | |
| 497 break; | |
| 498 } | |
| 499 | |
| 500 const bool use_webrequest_histogram = WasWebRequestUsedBySomeExtensions(); | |
| 501 if (use_webrequest_histogram) { | |
| 502 switch (load_type) { | |
| 503 case DocumentState::NORMAL_LOAD: | |
| 504 PLT_HISTOGRAM( | |
| 505 "PLT.BeginToFinish_NormalLoad_ExtensionWebRequest", | |
| 506 begin_to_finish_all_loads); | |
| 507 break; | |
| 508 case DocumentState::LINK_LOAD_NORMAL: | |
| 509 PLT_HISTOGRAM( | |
| 510 "PLT.BeginToFinish_LinkLoadNormal_ExtensionWebRequest", | |
| 511 begin_to_finish_all_loads); | |
| 512 break; | |
| 513 case DocumentState::LINK_LOAD_RELOAD: | |
| 514 PLT_HISTOGRAM( | |
| 515 "PLT.BeginToFinish_LinkLoadReload_ExtensionWebRequest", | |
| 516 begin_to_finish_all_loads); | |
| 517 break; | |
| 518 case DocumentState::LINK_LOAD_CACHE_STALE_OK: | |
| 519 PLT_HISTOGRAM( | |
| 520 "PLT.BeginToFinish_LinkLoadStaleOk_ExtensionWebRequest", | |
| 521 begin_to_finish_all_loads); | |
| 522 break; | |
| 523 default: | |
| 524 break; | |
| 525 } | |
| 526 } | |
| 527 } | |
| 528 | |
| 529 } // namespace | |
| 530 | |
| 531 PageLoadHistograms::PageLoadHistograms(content::RenderFrame* render_frame) | |
| 532 : content::RenderFrameObserver(render_frame), helper_(this) {} | |
| 533 | |
| 534 PageLoadHistograms::~PageLoadHistograms() { | |
| 535 } | |
| 536 | |
| 537 void PageLoadHistograms::Dump() { | |
| 538 WebLocalFrame* frame = render_frame()->GetWebFrame(); | |
| 539 | |
| 540 // We only dump histograms for main frames. | |
| 541 // In the future, it may be interesting to tag subframes and dump them too. | |
| 542 DCHECK(frame && !frame->parent()); | |
| 543 | |
| 544 // Only dump for supported schemes. | |
| 545 URLPattern::SchemeMasks scheme_type = | |
| 546 GetSupportedSchemeType(frame->document().url()); | |
| 547 if (scheme_type == 0) | |
| 548 return; | |
| 549 | |
| 550 // Don't dump stats for the NTP, as PageLoadHistograms should only be recorded | |
| 551 // for pages visited due to an explicit user navigation. | |
| 552 if (SearchBouncer::GetInstance()->IsNewTabPage(frame->document().url())) { | |
| 553 return; | |
| 554 } | |
| 555 | |
| 556 DocumentState* document_state = | |
| 557 DocumentState::FromDataSource(frame->dataSource()); | |
| 558 | |
| 559 bool came_from_websearch = IsFromGoogleSearchResult( | |
| 560 frame->document().url(), | |
| 561 blink::WebStringToGURL(frame->document().referrer())); | |
| 562 int websearch_chrome_joint_experiment_id = kNoExperiment; | |
| 563 bool is_preview = false; | |
| 564 if (came_from_websearch) { | |
| 565 websearch_chrome_joint_experiment_id = GetQueryStringBasedExperiment( | |
| 566 blink::WebStringToGURL(frame->document().referrer())); | |
| 567 is_preview = ViaHeaderContains(frame, "1.1 Google Instant Proxy Preview"); | |
| 568 } | |
| 569 | |
| 570 // Metrics based on the timing information recorded for the Navigation Timing | |
| 571 // API - http://www.w3.org/TR/navigation-timing/. | |
| 572 DumpHistograms(frame->performance(), document_state, came_from_websearch, | |
| 573 websearch_chrome_joint_experiment_id, is_preview, scheme_type); | |
| 574 | |
| 575 // Old metrics based on the timing information stored in DocumentState. These | |
| 576 // are deprecated and should go away. | |
| 577 DumpDeprecatedHistograms( | |
| 578 frame->performance(), document_state, came_from_websearch, | |
| 579 websearch_chrome_joint_experiment_id, is_preview, scheme_type); | |
| 580 | |
| 581 // Log the PLT to the info log. | |
| 582 LogPageLoadTime(document_state, frame->dataSource()); | |
| 583 | |
| 584 // If persistent histograms are not enabled, initiate a PostTask here to be | |
| 585 // sure that we send the histograms generated. Without this call, pages | |
| 586 // that don't have an on-close-handler might generate data that is lost if | |
| 587 // the renderer is shutdown abruptly (e.g. the user closed the tab). | |
| 588 // TODO(bcwhite): Remove completely when persistence is on-by-default. | |
| 589 if (!base::GlobalHistogramAllocator::Get()) { | |
| 590 content::RenderThread::Get()->UpdateHistograms( | |
| 591 content::kHistogramSynchronizerReservedSequenceNumber); | |
| 592 } | |
| 593 } | |
| 594 | |
| 595 void PageLoadHistograms::WillCommitProvisionalLoad() { | |
| 596 Dump(); | |
| 597 } | |
| 598 | |
| 599 void PageLoadHistograms::LogPageLoadTime(const DocumentState* document_state, | |
| 600 const WebDataSource* ds) const { | |
| 601 // Because this function gets called on every page load, | |
| 602 // take extra care to optimize it away if logging is turned off. | |
| 603 if (logging::LOG_INFO < logging::GetMinLogLevel()) | |
| 604 return; | |
| 605 | |
| 606 DCHECK(document_state); | |
| 607 DCHECK(ds); | |
| 608 GURL url(ds->request().url()); | |
| 609 Time start = document_state->start_load_time(); | |
| 610 Time finish = document_state->finish_load_time(); | |
| 611 // TODO(mbelshe): should we log more stats? | |
| 612 VLOG(1) << "PLT: " << (finish - start).InMilliseconds() << "ms " | |
| 613 << url.spec(); | |
| 614 } | |
| 615 | |
| 616 void PageLoadHistograms::OnDestruct() { | |
| 617 delete this; | |
| 618 } | |
| 619 | |
| 620 PageLoadHistograms::Helper::Helper(PageLoadHistograms* histograms) | |
| 621 : RenderViewObserver(histograms->render_frame()->GetRenderView()), | |
| 622 histograms_(histograms) { | |
| 623 } | |
| 624 | |
| 625 void PageLoadHistograms::Helper::ClosePage() { | |
| 626 histograms_->Dump(); | |
| 627 } | |
| 628 | |
| 629 void PageLoadHistograms::Helper::OnDestruct() { | |
| 630 } | |
| OLD | NEW |