Home | History | Annotate | Download | only in renderer
      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 #include "chrome/renderer/page_load_histograms.h"
      6 
      7 #include <string>
      8 
      9 #include "base/command_line.h"
     10 #include "base/logging.h"
     11 #include "base/metrics/field_trial.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/time/time.h"
     15 #include "chrome/common/chrome_switches.h"
     16 #include "chrome/renderer/chrome_content_renderer_client.h"
     17 #include "content/public/common/content_constants.h"
     18 #include "content/public/renderer/document_state.h"
     19 #include "content/public/renderer/render_thread.h"
     20 #include "content/public/renderer/render_view.h"
     21 #include "extensions/common/url_pattern.h"
     22 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     23 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     24 #include "third_party/WebKit/public/web/WebDocument.h"
     25 #include "third_party/WebKit/public/web/WebFrame.h"
     26 #include "third_party/WebKit/public/web/WebPerformance.h"
     27 #include "third_party/WebKit/public/web/WebView.h"
     28 #include "url/gurl.h"
     29 
     30 using WebKit::WebDataSource;
     31 using WebKit::WebFrame;
     32 using WebKit::WebPerformance;
     33 using WebKit::WebString;
     34 using base::Time;
     35 using base::TimeDelta;
     36 using content::DocumentState;
     37 
     38 const size_t kPLTCount = 100;
     39 
     40 namespace {
     41 
     42 TimeDelta kPLTMin() {
     43   return TimeDelta::FromMilliseconds(10);
     44 }
     45 TimeDelta kPLTMax() {
     46   return TimeDelta::FromMinutes(10);
     47 }
     48 
     49 #define PLT_HISTOGRAM(name, sample) \
     50     UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, kPLTMin(), kPLTMax(), kPLTCount);
     51 
     52 // In addition to PLT_HISTOGRAM, add the *_DataReductionProxy variant
     53 // conditionally. This macro runs only in one thread.
     54 #define PLT_HISTOGRAM_DRP(name, sample, data_reduction_proxy_was_used) \
     55   do {                                                                  \
     56     static base::HistogramBase* counter(NULL);                          \
     57     static base::HistogramBase* drp_counter(NULL);                      \
     58     if (!counter) {                                                     \
     59       DCHECK(drp_counter == NULL);                                      \
     60       counter = base::Histogram::FactoryTimeGet(                        \
     61           name, kPLTMin(), kPLTMax(), kPLTCount,                        \
     62           base::Histogram::kUmaTargetedHistogramFlag);                  \
     63     }                                                                   \
     64     counter->AddTime(sample);                                           \
     65     if (!data_reduction_proxy_was_used) break;                          \
     66     if (!drp_counter) {                                                 \
     67       drp_counter = base::Histogram::FactoryTimeGet(                    \
     68           std::string(name) + "_DataReductionProxy",                    \
     69           kPLTMin(), kPLTMax(), kPLTCount,                              \
     70           base::Histogram::kUmaTargetedHistogramFlag);                  \
     71     }                                                                   \
     72     drp_counter->AddTime(sample);                                       \
     73   } while (0)
     74 
     75 // Returns the scheme type of the given URL if its type is one for which we
     76 // dump page load histograms. Otherwise returns NULL.
     77 URLPattern::SchemeMasks GetSupportedSchemeType(const GURL& url) {
     78   if (url.SchemeIs("http"))
     79     return URLPattern::SCHEME_HTTP;
     80   else if (url.SchemeIs("https"))
     81     return URLPattern::SCHEME_HTTPS;
     82   return static_cast<URLPattern::SchemeMasks>(0);
     83 }
     84 
     85 // Returns true if the data reduction proxy was used. Note, this function will
     86 // produce a false positive if a page is fetched using SPDY and using a proxy,
     87 // and |kDatReductionProxyViaValue| is added to the Via header.
     88 // TODO(bengr): Plumb the hostname of the proxy from |HttpNetworkTransaction|
     89 // and check if it matches |SPDY_PROXY_AUTH_ORIGIN|.
     90 bool DataReductionProxyWasUsed(WebFrame* frame) {
     91 #if defined(SPDY_PROXY_AUTH_ORIGIN)
     92   const char kViaHeaderName[] = "Via";
     93   const char kDatReductionProxyViaValue[] = "1.1 Chrome Compression Proxy";
     94 
     95   DocumentState* document_state =
     96       DocumentState::FromDataSource(frame->dataSource());
     97   if (!document_state->was_fetched_via_proxy())
     98     return false;
     99 
    100   std::string via_header(UTF16ToUTF8(
    101       frame->dataSource()->response().httpHeaderField(kViaHeaderName)));
    102   return via_header.find(kDatReductionProxyViaValue) != std::string::npos;
    103 #endif
    104   return false;
    105 }
    106 
    107 void DumpPerformanceTiming(const WebPerformance& performance,
    108                            DocumentState* document_state,
    109                            bool data_reduction_proxy_was_used) {
    110   Time request = document_state->request_time();
    111 
    112   Time navigation_start = Time::FromDoubleT(performance.navigationStart());
    113   Time redirect_start = Time::FromDoubleT(performance.redirectStart());
    114   Time redirect_end = Time::FromDoubleT(performance.redirectEnd());
    115   Time fetch_start = Time::FromDoubleT(performance.fetchStart());
    116   Time domain_lookup_start = Time::FromDoubleT(performance.domainLookupStart());
    117   Time domain_lookup_end = Time::FromDoubleT(performance.domainLookupEnd());
    118   Time connect_start = Time::FromDoubleT(performance.connectStart());
    119   Time connect_end = Time::FromDoubleT(performance.connectEnd());
    120   Time request_start = Time::FromDoubleT(performance.requestStart());
    121   Time response_start = Time::FromDoubleT(performance.responseStart());
    122   Time response_end = Time::FromDoubleT(performance.responseEnd());
    123   Time dom_loading = Time::FromDoubleT(performance.domLoading());
    124   Time dom_interactive = Time::FromDoubleT(performance.domInteractive());
    125   Time dom_content_loaded_start =
    126       Time::FromDoubleT(performance.domContentLoadedEventStart());
    127   Time dom_content_loaded_end =
    128       Time::FromDoubleT(performance.domContentLoadedEventEnd());
    129   Time load_event_start = Time::FromDoubleT(performance.loadEventStart());
    130   Time load_event_end = Time::FromDoubleT(performance.loadEventEnd());
    131   Time begin = (request.is_null() ? navigation_start : request_start);
    132 
    133   DCHECK(!navigation_start.is_null());
    134 
    135   // It is possible for a document to have navigation_start time, but no
    136   // request_start. An example is doing a window.open, which synchronously
    137   // loads "about:blank", then using document.write add a meta http-equiv
    138   // refresh tag, which causes a navigation. In such case, we will arrive at
    139   // this function with no request/response timing data and identical load
    140   // start/end values. Avoid logging this case, as it doesn't add any
    141   // meaningful information to the histogram.
    142   if (request_start.is_null())
    143     return;
    144 
    145   // TODO(dominich): Investigate conditions under which |load_event_start| and
    146   // |load_event_end| may be NULL as in the non-PT_ case below. Examples in
    147   // http://crbug.com/112006.
    148   // DCHECK(!load_event_start.is_null());
    149   // DCHECK(!load_event_end.is_null());
    150 
    151   if (document_state->web_timing_histograms_recorded())
    152     return;
    153   document_state->set_web_timing_histograms_recorded(true);
    154 
    155   if (!redirect_start.is_null() && !redirect_end.is_null()) {
    156     PLT_HISTOGRAM_DRP("PLT.NT_Redirect", redirect_end - redirect_start,
    157                       data_reduction_proxy_was_used);
    158     PLT_HISTOGRAM_DRP(
    159         "PLT.NT_DelayBeforeFetchRedirect",
    160         (fetch_start - navigation_start) - (redirect_end - redirect_start),
    161         data_reduction_proxy_was_used);
    162   } else {
    163     PLT_HISTOGRAM_DRP("PLT.NT_DelayBeforeFetch",
    164                       fetch_start - navigation_start,
    165                       data_reduction_proxy_was_used);
    166   }
    167   PLT_HISTOGRAM_DRP("PLT.NT_DelayBeforeDomainLookup",
    168                     domain_lookup_start - fetch_start,
    169                     data_reduction_proxy_was_used);
    170   PLT_HISTOGRAM_DRP("PLT.NT_DomainLookup",
    171                     domain_lookup_end - domain_lookup_start,
    172                     data_reduction_proxy_was_used);
    173   PLT_HISTOGRAM_DRP("PLT.NT_DelayBeforeConnect",
    174                     connect_start - domain_lookup_end,
    175                     data_reduction_proxy_was_used);
    176   PLT_HISTOGRAM_DRP("PLT.NT_Connect", connect_end - connect_start,
    177                     data_reduction_proxy_was_used);
    178   PLT_HISTOGRAM_DRP("PLT.NT_DelayBeforeRequest",
    179                     request_start - connect_end,
    180                     data_reduction_proxy_was_used);
    181   PLT_HISTOGRAM_DRP("PLT.NT_Request", response_start - request_start,
    182                     data_reduction_proxy_was_used);
    183   PLT_HISTOGRAM_DRP("PLT.NT_Response", response_end - response_start,
    184                     data_reduction_proxy_was_used);
    185 
    186   if (!dom_loading.is_null()) {
    187     PLT_HISTOGRAM_DRP("PLT.NT_DelayBeforeDomLoading",
    188                       dom_loading - response_start,
    189                       data_reduction_proxy_was_used);
    190   }
    191   if (!dom_interactive.is_null() && !dom_loading.is_null()) {
    192     PLT_HISTOGRAM_DRP("PLT.NT_DomLoading",
    193                       dom_interactive - dom_loading,
    194                       data_reduction_proxy_was_used);
    195   }
    196   if (!dom_content_loaded_start.is_null() && !dom_interactive.is_null()) {
    197     PLT_HISTOGRAM_DRP("PLT.NT_DomInteractive",
    198                       dom_content_loaded_start - dom_interactive,
    199                       data_reduction_proxy_was_used);
    200   }
    201   if (!dom_content_loaded_start.is_null() &&
    202       !dom_content_loaded_end.is_null() ) {
    203     PLT_HISTOGRAM_DRP("PLT.NT_DomContentLoaded",
    204                       dom_content_loaded_end - dom_content_loaded_start,
    205                       data_reduction_proxy_was_used);
    206   }
    207   if (!dom_content_loaded_end.is_null() && !load_event_start.is_null()) {
    208     PLT_HISTOGRAM_DRP("PLT.NT_DelayBeforeLoadEvent",
    209                       load_event_start - dom_content_loaded_end,
    210                       data_reduction_proxy_was_used);
    211   }
    212 
    213   // TODO(simonjam): There is no way to distinguish between abandonment and
    214   // intentional Javascript navigation before the load event fires.
    215   // TODO(dominich): Load type breakdown
    216   if (!load_event_start.is_null()) {
    217     PLT_HISTOGRAM("PLT.PT_BeginToFinishDoc", load_event_start - begin);
    218     PLT_HISTOGRAM("PLT.PT_CommitToFinishDoc",
    219                   load_event_start - response_start);
    220     if (data_reduction_proxy_was_used) {
    221       PLT_HISTOGRAM("PLT.PT_BeginToFinishDoc_DataReductionProxy",
    222                     load_event_start - begin);
    223       PLT_HISTOGRAM("PLT.PT_CommitToFinishDoc_DataReductionProxy",
    224                     load_event_start - response_start);
    225     }
    226   }
    227   if (!load_event_end.is_null()) {
    228     PLT_HISTOGRAM("PLT.PT_BeginToFinish", load_event_end - begin);
    229     PLT_HISTOGRAM("PLT.PT_CommitToFinish", load_event_end - response_start);
    230     PLT_HISTOGRAM("PLT.PT_RequestToFinish", load_event_end - navigation_start);
    231     PLT_HISTOGRAM("PLT.PT_StartToFinish", load_event_end - request_start);
    232     if (data_reduction_proxy_was_used) {
    233       PLT_HISTOGRAM("PLT.PT_BeginToFinish_DataReductionProxy",
    234                     load_event_end - begin);
    235       PLT_HISTOGRAM("PLT.PT_CommitToFinish_DataReductionProxy",
    236                     load_event_end - response_start);
    237       PLT_HISTOGRAM("PLT.PT_RequestToFinish_DataReductionProxy",
    238                     load_event_end - navigation_start);
    239       PLT_HISTOGRAM("PLT.PT_StartToFinish_DataReductionProxy",
    240                     load_event_end - request_start);
    241     }
    242   }
    243   if (!load_event_start.is_null() && !load_event_end.is_null()) {
    244     PLT_HISTOGRAM("PLT.PT_FinishDocToFinish",
    245                   load_event_end - load_event_start);
    246     PLT_HISTOGRAM_DRP("PLT.NT_LoadEvent",
    247                       load_event_end - load_event_start,
    248                       data_reduction_proxy_was_used);
    249 
    250     if (data_reduction_proxy_was_used) {
    251       PLT_HISTOGRAM("PLT.PT_FinishDocToFinish_DataReductionProxy",
    252                     load_event_end - load_event_start);
    253     }
    254   }
    255   PLT_HISTOGRAM("PLT.PT_BeginToCommit", response_start - begin);
    256   PLT_HISTOGRAM("PLT.PT_RequestToStart", request_start - navigation_start);
    257   PLT_HISTOGRAM("PLT.PT_StartToCommit", response_start - request_start);
    258   if (data_reduction_proxy_was_used) {
    259     PLT_HISTOGRAM("PLT.PT_BeginToCommit_DataReductionProxy",
    260                   response_start - begin);
    261     PLT_HISTOGRAM("PLT.PT_RequestToStart_DataReductionProxy",
    262                   request_start - navigation_start);
    263     PLT_HISTOGRAM("PLT.PT_StartToCommit_DataReductionProxy",
    264                   response_start - request_start);
    265   }
    266 }
    267 
    268 enum MissingStartType {
    269   START_MISSING = 0x1,
    270   COMMIT_MISSING = 0x2,
    271   NAV_START_MISSING = 0x4,
    272   MISSING_START_TYPE_MAX = 0x8
    273 };
    274 
    275 enum AbandonType {
    276   FINISH_DOC_MISSING = 0x1,
    277   FINISH_ALL_LOADS_MISSING = 0x2,
    278   LOAD_EVENT_START_MISSING = 0x4,
    279   LOAD_EVENT_END_MISSING = 0x8,
    280   ABANDON_TYPE_MAX = 0x10
    281 };
    282 
    283 }  // namespace
    284 
    285 PageLoadHistograms::PageLoadHistograms(content::RenderView* render_view)
    286     : content::RenderViewObserver(render_view),
    287       cross_origin_access_count_(0),
    288       same_origin_access_count_(0) {
    289 }
    290 
    291 void PageLoadHistograms::Dump(WebFrame* frame) {
    292   // We only dump histograms for main frames.
    293   // In the future, it may be interesting to tag subframes and dump them too.
    294   if (!frame || frame->parent())
    295     return;
    296 
    297   // Only dump for supported schemes.
    298   URLPattern::SchemeMasks scheme_type =
    299       GetSupportedSchemeType(frame->document().url());
    300   if (scheme_type == 0)
    301     return;
    302 
    303   // Ignore multipart requests.
    304   if (frame->dataSource()->response().isMultipartPayload())
    305     return;
    306 
    307   DocumentState* document_state =
    308       DocumentState::FromDataSource(frame->dataSource());
    309 
    310   bool data_reduction_proxy_was_used = DataReductionProxyWasUsed(frame);
    311 
    312   // Times based on the Web Timing metrics.
    313   // http://www.w3.org/TR/navigation-timing/
    314   // TODO(tonyg, jar): We are in the process of vetting these metrics against
    315   // the existing ones. Once we understand any differences, we will standardize
    316   // on a single set of metrics.
    317   DumpPerformanceTiming(frame->performance(), document_state,
    318                         data_reduction_proxy_was_used);
    319 
    320   // If we've already dumped, do nothing.
    321   // This simple bool works because we only dump for the main frame.
    322   if (document_state->load_histograms_recorded())
    323     return;
    324 
    325   // Collect measurement times.
    326   Time start = document_state->start_load_time();
    327   Time commit = document_state->commit_load_time();
    328 
    329   // TODO(tonyg, jar): Start can be missing after an in-document navigation and
    330   // possibly other cases like a very premature abandonment of the page.
    331   // The PLT.MissingStart histogram should help us troubleshoot and then we can
    332   // remove this.
    333   Time navigation_start =
    334       Time::FromDoubleT(frame->performance().navigationStart());
    335   int missing_start_type = 0;
    336   missing_start_type |= start.is_null() ? START_MISSING : 0;
    337   missing_start_type |= commit.is_null() ? COMMIT_MISSING : 0;
    338   missing_start_type |= navigation_start.is_null() ? NAV_START_MISSING : 0;
    339   UMA_HISTOGRAM_ENUMERATION("PLT.MissingStart", missing_start_type,
    340                             MISSING_START_TYPE_MAX);
    341   if (missing_start_type)
    342     return;
    343 
    344   // We properly handle null values for the next 3 variables.
    345   Time request = document_state->request_time();
    346   Time first_paint = document_state->first_paint_time();
    347   Time first_paint_after_load = document_state->first_paint_after_load_time();
    348   Time finish_doc = document_state->finish_document_load_time();
    349   Time finish_all_loads = document_state->finish_load_time();
    350 
    351   // TODO(tonyg, jar): We suspect a bug in abandonment counting, this temporary
    352   // histogram should help us to troubleshoot.
    353   Time load_event_start =
    354       Time::FromDoubleT(frame->performance().loadEventStart());
    355   Time load_event_end = Time::FromDoubleT(frame->performance().loadEventEnd());
    356   int abandon_type = 0;
    357   abandon_type |= finish_doc.is_null() ? FINISH_DOC_MISSING : 0;
    358   abandon_type |= finish_all_loads.is_null() ? FINISH_ALL_LOADS_MISSING : 0;
    359   abandon_type |= load_event_start.is_null() ? LOAD_EVENT_START_MISSING : 0;
    360   abandon_type |= load_event_end.is_null() ? LOAD_EVENT_END_MISSING : 0;
    361   UMA_HISTOGRAM_ENUMERATION("PLT.AbandonType", abandon_type, ABANDON_TYPE_MAX);
    362 
    363   // Handle case where user hits "stop" or "back" before loading completely.
    364   bool abandoned_page = finish_doc.is_null();
    365   if (abandoned_page) {
    366     finish_doc = Time::Now();
    367     document_state->set_finish_document_load_time(finish_doc);
    368   }
    369 
    370   // TODO(jar): We should really discriminate the definition of "abandon" more
    371   // finely.  We should have:
    372   // abandon_before_document_loaded
    373   // abandon_before_onload_fired
    374 
    375   if (finish_all_loads.is_null()) {
    376     finish_all_loads = Time::Now();
    377     document_state->set_finish_load_time(finish_all_loads);
    378   } else {
    379     DCHECK(!abandoned_page);  // How can the doc have finished but not the page?
    380     if (abandoned_page)
    381       return;  // Don't try to record a stat which is broken.
    382   }
    383 
    384   document_state->set_load_histograms_recorded(true);
    385 
    386   // Note: Client side redirects will have no request time.
    387   Time begin = request.is_null() ? start : request;
    388   TimeDelta begin_to_finish_doc = finish_doc - begin;
    389   TimeDelta begin_to_finish_all_loads = finish_all_loads - begin;
    390   TimeDelta start_to_finish_all_loads = finish_all_loads - start;
    391   TimeDelta start_to_commit = commit - start;
    392 
    393   DocumentState::LoadType load_type = document_state->load_type();
    394 
    395   // The above code sanitized all values of times, in preparation for creating
    396   // actual histograms.  The remainder of this code could be run at destructor
    397   // time for the document_state, since all data is intact.
    398 
    399   // Aggregate PLT data across all link types.
    400   UMA_HISTOGRAM_ENUMERATION("PLT.Abandoned", abandoned_page ? 1 : 0, 2);
    401   UMA_HISTOGRAM_ENUMERATION("PLT.LoadType", load_type,
    402       DocumentState::kLoadTypeMax);
    403   PLT_HISTOGRAM("PLT.StartToCommit", start_to_commit);
    404   PLT_HISTOGRAM("PLT.CommitToFinishDoc", finish_doc - commit);
    405   PLT_HISTOGRAM("PLT.FinishDocToFinish", finish_all_loads - finish_doc);
    406   PLT_HISTOGRAM("PLT.BeginToCommit", commit - begin);
    407   PLT_HISTOGRAM("PLT.StartToFinish", start_to_finish_all_loads);
    408   if (!request.is_null()) {
    409     PLT_HISTOGRAM("PLT.RequestToStart", start - request);
    410     PLT_HISTOGRAM("PLT.RequestToFinish", finish_all_loads - request);
    411   }
    412   PLT_HISTOGRAM("PLT.CommitToFinish", finish_all_loads - commit);
    413 
    414   scoped_ptr<TimeDelta> begin_to_first_paint;
    415   scoped_ptr<TimeDelta> commit_to_first_paint;
    416   if (!first_paint.is_null()) {
    417     // 'first_paint' can be before 'begin' for an unknown reason.
    418     // See bug http://crbug.com/125273 for details.
    419     if (begin <= first_paint) {
    420       begin_to_first_paint.reset(new TimeDelta(first_paint - begin));
    421       PLT_HISTOGRAM("PLT.BeginToFirstPaint", *begin_to_first_paint);
    422     }
    423     DCHECK(commit <= first_paint);
    424     commit_to_first_paint.reset(new TimeDelta(first_paint - commit));
    425     PLT_HISTOGRAM("PLT.CommitToFirstPaint", *commit_to_first_paint);
    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     DCHECK(commit <= first_paint_after_load);
    435     PLT_HISTOGRAM("PLT.CommitToFirstPaintAfterLoad",
    436         first_paint_after_load - commit);
    437     DCHECK(finish_all_loads <= first_paint_after_load);
    438     PLT_HISTOGRAM("PLT.FinishToFirstPaintAfterLoad",
    439         first_paint_after_load - finish_all_loads);
    440   }
    441   PLT_HISTOGRAM("PLT.BeginToFinishDoc", begin_to_finish_doc);
    442   PLT_HISTOGRAM("PLT.BeginToFinish", begin_to_finish_all_loads);
    443 
    444   // Load type related histograms.
    445   switch (load_type) {
    446     case DocumentState::UNDEFINED_LOAD:
    447       PLT_HISTOGRAM("PLT.BeginToFinishDoc_UndefLoad", begin_to_finish_doc);
    448       PLT_HISTOGRAM("PLT.BeginToFinish_UndefLoad", begin_to_finish_all_loads);
    449       break;
    450     case DocumentState::RELOAD:
    451       PLT_HISTOGRAM("PLT.BeginToFinishDoc_Reload", begin_to_finish_doc);
    452       PLT_HISTOGRAM("PLT.BeginToFinish_Reload", begin_to_finish_all_loads);
    453       break;
    454     case DocumentState::HISTORY_LOAD:
    455       PLT_HISTOGRAM("PLT.BeginToFinishDoc_HistoryLoad", begin_to_finish_doc);
    456       PLT_HISTOGRAM("PLT.BeginToFinish_HistoryLoad", begin_to_finish_all_loads);
    457       break;
    458     case DocumentState::NORMAL_LOAD:
    459       PLT_HISTOGRAM("PLT.BeginToFinishDoc_NormalLoad", begin_to_finish_doc);
    460       PLT_HISTOGRAM("PLT.BeginToFinish_NormalLoad", begin_to_finish_all_loads);
    461       break;
    462     case DocumentState::LINK_LOAD_NORMAL:
    463       PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadNormal",
    464           begin_to_finish_doc);
    465       PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadNormal",
    466                     begin_to_finish_all_loads);
    467       break;
    468     case DocumentState::LINK_LOAD_RELOAD:
    469       PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadReload",
    470            begin_to_finish_doc);
    471       PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadReload",
    472                     begin_to_finish_all_loads);
    473       break;
    474     case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    475       PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadStaleOk",
    476            begin_to_finish_doc);
    477       PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadStaleOk",
    478                     begin_to_finish_all_loads);
    479       break;
    480     case DocumentState::LINK_LOAD_CACHE_ONLY:
    481       PLT_HISTOGRAM("PLT.BeginToFinishDoc_LinkLoadCacheOnly",
    482            begin_to_finish_doc);
    483       PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadCacheOnly",
    484                     begin_to_finish_all_loads);
    485       break;
    486     default:
    487       break;
    488   }
    489 
    490   if (data_reduction_proxy_was_used) {
    491     UMA_HISTOGRAM_ENUMERATION(
    492         "PLT.Abandoned_SpdyProxy", abandoned_page ? 1 : 0, 2);
    493     PLT_HISTOGRAM("PLT.BeginToFinishDoc_SpdyProxy", begin_to_finish_doc);
    494     PLT_HISTOGRAM("PLT.BeginToFinish_SpdyProxy", begin_to_finish_all_loads);
    495   }
    496 
    497   if (document_state->was_prefetcher()) {
    498     PLT_HISTOGRAM("PLT.BeginToFinishDoc_ContentPrefetcher",
    499                   begin_to_finish_doc);
    500     PLT_HISTOGRAM("PLT.BeginToFinish_ContentPrefetcher",
    501                   begin_to_finish_all_loads);
    502   }
    503   if (document_state->was_referred_by_prefetcher()) {
    504     PLT_HISTOGRAM("PLT.BeginToFinishDoc_ContentPrefetcherReferrer",
    505                   begin_to_finish_doc);
    506     PLT_HISTOGRAM("PLT.BeginToFinish_ContentPrefetcherReferrer",
    507                   begin_to_finish_all_loads);
    508   }
    509   if (document_state->was_after_preconnect_request()) {
    510     PLT_HISTOGRAM("PLT.BeginToFinishDoc_AfterPreconnectRequest",
    511                   begin_to_finish_doc);
    512     PLT_HISTOGRAM("PLT.BeginToFinish_AfterPreconnectRequest",
    513                   begin_to_finish_all_loads);
    514   }
    515 
    516   // TODO(mpcomplete): remove the extension-related histograms after we collect
    517   // enough data. http://crbug.com/100411
    518   const bool use_adblock_histogram =
    519       chrome::ChromeContentRendererClient::IsAdblockInstalled();
    520   if (use_adblock_histogram) {
    521     UMA_HISTOGRAM_ENUMERATION(
    522         "PLT.Abandoned_ExtensionAdblock",
    523         abandoned_page ? 1 : 0, 2);
    524     switch (load_type) {
    525       case DocumentState::NORMAL_LOAD:
    526         PLT_HISTOGRAM(
    527             "PLT.BeginToFinish_NormalLoad_ExtensionAdblock",
    528             begin_to_finish_all_loads);
    529         break;
    530       case DocumentState::LINK_LOAD_NORMAL:
    531         PLT_HISTOGRAM(
    532             "PLT.BeginToFinish_LinkLoadNormal_ExtensionAdblock",
    533             begin_to_finish_all_loads);
    534         break;
    535       case DocumentState::LINK_LOAD_RELOAD:
    536         PLT_HISTOGRAM(
    537             "PLT.BeginToFinish_LinkLoadReload_ExtensionAdblock",
    538             begin_to_finish_all_loads);
    539         break;
    540       case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    541         PLT_HISTOGRAM(
    542             "PLT.BeginToFinish_LinkLoadStaleOk_ExtensionAdblock",
    543             begin_to_finish_all_loads);
    544         break;
    545       default:
    546         break;
    547     }
    548   }
    549 
    550   const bool use_adblockplus_histogram =
    551       chrome::ChromeContentRendererClient::IsAdblockPlusInstalled();
    552   if (use_adblockplus_histogram) {
    553     UMA_HISTOGRAM_ENUMERATION(
    554         "PLT.Abandoned_ExtensionAdblockPlus",
    555         abandoned_page ? 1 : 0, 2);
    556     switch (load_type) {
    557       case DocumentState::NORMAL_LOAD:
    558         PLT_HISTOGRAM(
    559             "PLT.BeginToFinish_NormalLoad_ExtensionAdblockPlus",
    560             begin_to_finish_all_loads);
    561         break;
    562       case DocumentState::LINK_LOAD_NORMAL:
    563         PLT_HISTOGRAM(
    564             "PLT.BeginToFinish_LinkLoadNormal_ExtensionAdblockPlus",
    565             begin_to_finish_all_loads);
    566         break;
    567       case DocumentState::LINK_LOAD_RELOAD:
    568         PLT_HISTOGRAM(
    569             "PLT.BeginToFinish_LinkLoadReload_ExtensionAdblockPlus",
    570             begin_to_finish_all_loads);
    571         break;
    572       case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    573         PLT_HISTOGRAM(
    574             "PLT.BeginToFinish_LinkLoadStaleOk_ExtensionAdblockPlus",
    575             begin_to_finish_all_loads);
    576         break;
    577       default:
    578         break;
    579     }
    580   }
    581 
    582   const bool use_webrequest_adblock_histogram =
    583       chrome::ChromeContentRendererClient::IsAdblockWithWebRequestInstalled();
    584   if (use_webrequest_adblock_histogram) {
    585     UMA_HISTOGRAM_ENUMERATION(
    586         "PLT.Abandoned_ExtensionWebRequestAdblock",
    587         abandoned_page ? 1 : 0, 2);
    588     switch (load_type) {
    589       case DocumentState::NORMAL_LOAD:
    590         PLT_HISTOGRAM(
    591             "PLT.BeginToFinish_NormalLoad_ExtensionWebRequestAdblock",
    592             begin_to_finish_all_loads);
    593         break;
    594       case DocumentState::LINK_LOAD_NORMAL:
    595         PLT_HISTOGRAM(
    596             "PLT.BeginToFinish_LinkLoadNormal_ExtensionWebRequestAdblock",
    597             begin_to_finish_all_loads);
    598         break;
    599       case DocumentState::LINK_LOAD_RELOAD:
    600         PLT_HISTOGRAM(
    601             "PLT.BeginToFinish_LinkLoadReload_ExtensionWebRequestAdblock",
    602             begin_to_finish_all_loads);
    603         break;
    604       case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    605         PLT_HISTOGRAM(
    606             "PLT.BeginToFinish_LinkLoadStaleOk_ExtensionWebRequestAdblock",
    607             begin_to_finish_all_loads);
    608         break;
    609       default:
    610         break;
    611     }
    612   }
    613 
    614   const bool use_webrequest_adblockplus_histogram =
    615       chrome::ChromeContentRendererClient::
    616           IsAdblockPlusWithWebRequestInstalled();
    617   if (use_webrequest_adblockplus_histogram) {
    618     UMA_HISTOGRAM_ENUMERATION(
    619         "PLT.Abandoned_ExtensionWebRequestAdblockPlus",
    620         abandoned_page ? 1 : 0, 2);
    621     switch (load_type) {
    622       case DocumentState::NORMAL_LOAD:
    623         PLT_HISTOGRAM(
    624             "PLT.BeginToFinish_NormalLoad_ExtensionWebRequestAdblockPlus",
    625             begin_to_finish_all_loads);
    626         break;
    627       case DocumentState::LINK_LOAD_NORMAL:
    628         PLT_HISTOGRAM(
    629             "PLT.BeginToFinish_LinkLoadNormal_ExtensionWebRequestAdblockPlus",
    630             begin_to_finish_all_loads);
    631         break;
    632       case DocumentState::LINK_LOAD_RELOAD:
    633         PLT_HISTOGRAM(
    634             "PLT.BeginToFinish_LinkLoadReload_ExtensionWebRequestAdblockPlus",
    635             begin_to_finish_all_loads);
    636         break;
    637       case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    638         PLT_HISTOGRAM(
    639             "PLT.BeginToFinish_LinkLoadStaleOk_ExtensionWebRequestAdblockPlus",
    640             begin_to_finish_all_loads);
    641         break;
    642       default:
    643         break;
    644     }
    645   }
    646 
    647   const bool use_webrequest_other_histogram =
    648       chrome::ChromeContentRendererClient::
    649           IsOtherExtensionWithWebRequestInstalled();
    650   if (use_webrequest_other_histogram) {
    651     UMA_HISTOGRAM_ENUMERATION(
    652         "PLT.Abandoned_ExtensionWebRequestOther",
    653         abandoned_page ? 1 : 0, 2);
    654     switch (load_type) {
    655       case DocumentState::NORMAL_LOAD:
    656         PLT_HISTOGRAM(
    657             "PLT.BeginToFinish_NormalLoad_ExtensionWebRequestOther",
    658             begin_to_finish_all_loads);
    659         break;
    660       case DocumentState::LINK_LOAD_NORMAL:
    661         PLT_HISTOGRAM(
    662             "PLT.BeginToFinish_LinkLoadNormal_ExtensionWebRequestOther",
    663             begin_to_finish_all_loads);
    664         break;
    665       case DocumentState::LINK_LOAD_RELOAD:
    666         PLT_HISTOGRAM(
    667             "PLT.BeginToFinish_LinkLoadReload_ExtensionWebRequestOther",
    668             begin_to_finish_all_loads);
    669         break;
    670       case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    671         PLT_HISTOGRAM(
    672             "PLT.BeginToFinish_LinkLoadStaleOk_ExtensionWebRequestOther",
    673             begin_to_finish_all_loads);
    674         break;
    675       default:
    676         break;
    677     }
    678   }
    679 
    680   // Record SpdyCwnd results.
    681   if (document_state->was_fetched_via_spdy()) {
    682     switch (load_type) {
    683       case DocumentState::LINK_LOAD_NORMAL:
    684         PLT_HISTOGRAM("PLT.BeginToFinish_LinkLoadNormal_cwndDynamic",
    685                       begin_to_finish_all_loads);
    686         PLT_HISTOGRAM("PLT.StartToFinish_LinkLoadNormal_cwndDynamic",
    687                       start_to_finish_all_loads);
    688         PLT_HISTOGRAM("PLT.StartToCommit_LinkLoadNormal_cwndDynamic",
    689                       start_to_commit);
    690         break;
    691       case DocumentState::NORMAL_LOAD:
    692         PLT_HISTOGRAM("PLT.BeginToFinish_NormalLoad_cwndDynamic",
    693                       begin_to_finish_all_loads);
    694         PLT_HISTOGRAM("PLT.StartToFinish_NormalLoad_cwndDynamic",
    695                       start_to_finish_all_loads);
    696         PLT_HISTOGRAM("PLT.StartToCommit_NormalLoad_cwndDynamic",
    697                       start_to_commit);
    698         break;
    699       default:
    700         break;
    701     }
    702   }
    703 
    704   // Record page load time and abandonment rates for proxy cases.
    705   if (document_state->was_fetched_via_proxy()) {
    706     if (scheme_type == URLPattern::SCHEME_HTTPS) {
    707       PLT_HISTOGRAM("PLT.StartToFinish.Proxy.https", start_to_finish_all_loads);
    708       UMA_HISTOGRAM_ENUMERATION("PLT.Abandoned.Proxy.https",
    709                                 abandoned_page ? 1 : 0, 2);
    710     } else {
    711       DCHECK(scheme_type == URLPattern::SCHEME_HTTP);
    712       PLT_HISTOGRAM("PLT.StartToFinish.Proxy.http", start_to_finish_all_loads);
    713       UMA_HISTOGRAM_ENUMERATION("PLT.Abandoned.Proxy.http",
    714                                 abandoned_page ? 1 : 0, 2);
    715     }
    716   } else {
    717     if (scheme_type == URLPattern::SCHEME_HTTPS) {
    718       PLT_HISTOGRAM("PLT.StartToFinish.NoProxy.https",
    719                     start_to_finish_all_loads);
    720       UMA_HISTOGRAM_ENUMERATION("PLT.Abandoned.NoProxy.https",
    721                                 abandoned_page ? 1 : 0, 2);
    722     } else {
    723       DCHECK(scheme_type == URLPattern::SCHEME_HTTP);
    724       PLT_HISTOGRAM("PLT.StartToFinish.NoProxy.http",
    725                     start_to_finish_all_loads);
    726       UMA_HISTOGRAM_ENUMERATION("PLT.Abandoned.NoProxy.http",
    727                                 abandoned_page ? 1 : 0, 2);
    728     }
    729   }
    730 
    731   // Histograms to determine if disabling overlapped TCP reads
    732   // has an impact on PLT.
    733   static const bool use_overlapped_read_histogram =
    734       base::FieldTrialList::TrialExists("OverlappedReadImpact");
    735   if (use_overlapped_read_histogram) {
    736     UMA_HISTOGRAM_ENUMERATION(
    737         base::FieldTrial::MakeName("PLT.Abandoned", "OverlappedReadImpact"),
    738         abandoned_page ? 1 : 0, 2);
    739     UMA_HISTOGRAM_ENUMERATION(
    740         base::FieldTrial::MakeName("PLT.LoadType", "OverlappedReadImpact"),
    741         load_type, DocumentState::kLoadTypeMax);
    742     switch (load_type) {
    743       case DocumentState::NORMAL_LOAD:
    744         PLT_HISTOGRAM(base::FieldTrial::MakeName(
    745             "PLT.BeginToFinish_NormalLoad", "OverlappedReadImpact"),
    746             begin_to_finish_all_loads);
    747         break;
    748       case DocumentState::LINK_LOAD_NORMAL:
    749         PLT_HISTOGRAM(base::FieldTrial::MakeName(
    750             "PLT.BeginToFinish_LinkLoadNormal", "OverlappedReadImpact"),
    751             begin_to_finish_all_loads);
    752         break;
    753       case DocumentState::LINK_LOAD_RELOAD:
    754         PLT_HISTOGRAM(base::FieldTrial::MakeName(
    755             "PLT.BeginToFinish_LinkLoadReload", "OverlappedReadImpact"),
    756             begin_to_finish_all_loads);
    757         break;
    758       case DocumentState::LINK_LOAD_CACHE_STALE_OK:
    759         PLT_HISTOGRAM(base::FieldTrial::MakeName(
    760             "PLT.BeginToFinish_LinkLoadStaleOk", "OverlappedReadImpact"),
    761             begin_to_finish_all_loads);
    762         break;
    763       default:
    764         break;
    765     }
    766   }
    767 
    768   // Site isolation metrics.
    769   UMA_HISTOGRAM_COUNTS("SiteIsolation.PageLoadsWithCrossSiteFrameAccess",
    770                        cross_origin_access_count_);
    771   UMA_HISTOGRAM_COUNTS("SiteIsolation.PageLoadsWithSameSiteFrameAccess",
    772                        same_origin_access_count_);
    773 
    774   // Log the PLT to the info log.
    775   LogPageLoadTime(document_state, frame->dataSource());
    776 
    777   // Since there are currently no guarantees that renderer histograms will be
    778   // sent to the browser, we initiate a PostTask here to be sure that we send
    779   // the histograms we generated.  Without this call, pages that don't have an
    780   // on-close-handler might generate data that is lost when the renderer is
    781   // shutdown abruptly (perchance because the user closed the tab).
    782   // TODO(jar) BUG=33233: This needs to be moved to a PostDelayedTask, and it
    783   // should post when the onload is complete, so that it doesn't interfere with
    784   // the next load.
    785   content::RenderThread::Get()->UpdateHistograms(
    786       content::kHistogramSynchronizerReservedSequenceNumber);
    787 }
    788 
    789 void PageLoadHistograms::ResetCrossFramePropertyAccess() {
    790   cross_origin_access_count_ = 0;
    791   same_origin_access_count_ = 0;
    792 }
    793 
    794 void PageLoadHistograms::FrameWillClose(WebFrame* frame) {
    795   Dump(frame);
    796 }
    797 
    798 void PageLoadHistograms::ClosePage() {
    799   // TODO(davemoore) This code should be removed once willClose() gets
    800   // called when a page is destroyed. page_load_histograms_.Dump() is safe
    801   // to call multiple times for the same frame, but it will simplify things.
    802   Dump(render_view()->GetWebView()->mainFrame());
    803   ResetCrossFramePropertyAccess();
    804 }
    805 
    806 void PageLoadHistograms::LogPageLoadTime(const DocumentState* document_state,
    807                                          const WebDataSource* ds) const {
    808   // Because this function gets called on every page load,
    809   // take extra care to optimize it away if logging is turned off.
    810   if (logging::LOG_INFO < logging::GetMinLogLevel())
    811     return;
    812 
    813   DCHECK(document_state);
    814   DCHECK(ds);
    815   GURL url(ds->request().url());
    816   Time start = document_state->start_load_time();
    817   Time finish = document_state->finish_load_time();
    818   // TODO(mbelshe): should we log more stats?
    819   VLOG(1) << "PLT: " << (finish - start).InMilliseconds() << "ms "
    820           << url.spec();
    821 }
    822