Home | History | Annotate | Download | only in download
      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 "content/browser/download/download_stats.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "base/metrics/sparse_histogram.h"
      9 #include "base/strings/string_util.h"
     10 #include "content/browser/download/download_resource_handler.h"
     11 #include "content/public/browser/download_interrupt_reasons.h"
     12 #include "net/http/http_content_disposition.h"
     13 
     14 namespace content {
     15 
     16 namespace {
     17 
     18 // All possible error codes from the network module. Note that the error codes
     19 // are all positive (since histograms expect positive sample values).
     20 const int kAllInterruptReasonCodes[] = {
     21 #define INTERRUPT_REASON(label, value) (value),
     22 #include "content/public/browser/download_interrupt_reason_values.h"
     23 #undef INTERRUPT_REASON
     24 };
     25 
     26 // These values are based on net::HttpContentDisposition::ParseResult values.
     27 // Values other than HEADER_PRESENT and IS_VALID are only measured if |IS_VALID|
     28 // is true.
     29 enum ContentDispositionCountTypes {
     30   // Count of downloads which had a Content-Disposition headers. The total
     31   // number of downloads is measured by UNTHROTTLED_COUNT.
     32   CONTENT_DISPOSITION_HEADER_PRESENT = 0,
     33 
     34   // At least one of 'name', 'filename' or 'filenae*' attributes were valid and
     35   // yielded a non-empty filename.
     36   CONTENT_DISPOSITION_IS_VALID,
     37 
     38   // The following enum values correspond to
     39   // net::HttpContentDisposition::ParseResult.
     40   CONTENT_DISPOSITION_HAS_DISPOSITION_TYPE,
     41   CONTENT_DISPOSITION_HAS_UNKNOWN_TYPE,
     42   CONTENT_DISPOSITION_HAS_NAME,
     43   CONTENT_DISPOSITION_HAS_FILENAME,
     44   CONTENT_DISPOSITION_HAS_EXT_FILENAME,
     45   CONTENT_DISPOSITION_HAS_NON_ASCII_STRINGS,
     46   CONTENT_DISPOSITION_HAS_PERCENT_ENCODED_STRINGS,
     47   CONTENT_DISPOSITION_HAS_RFC2047_ENCODED_STRINGS,
     48 
     49   // Only have the 'name' attribute is present.
     50   CONTENT_DISPOSITION_HAS_NAME_ONLY,
     51 
     52   CONTENT_DISPOSITION_LAST_ENTRY
     53 };
     54 
     55 void RecordContentDispositionCount(ContentDispositionCountTypes type,
     56                                    bool record) {
     57   if (!record)
     58     return;
     59   UMA_HISTOGRAM_ENUMERATION(
     60       "Download.ContentDisposition", type, CONTENT_DISPOSITION_LAST_ENTRY);
     61 }
     62 
     63 void RecordContentDispositionCountFlag(
     64     ContentDispositionCountTypes type,
     65     int flags_to_test,
     66     net::HttpContentDisposition::ParseResultFlags flag) {
     67   RecordContentDispositionCount(type, (flags_to_test & flag) == flag);
     68 }
     69 
     70 // Do not insert, delete, or reorder; this is being histogrammed. Append only.
     71 // All of the download_extensions.cc file types should be in this list.
     72 const base::FilePath::CharType* kDangerousFileTypes[] = {
     73   FILE_PATH_LITERAL(".ad"),
     74   FILE_PATH_LITERAL(".ade"),
     75   FILE_PATH_LITERAL(".adp"),
     76   FILE_PATH_LITERAL(".ah"),
     77   FILE_PATH_LITERAL(".apk"),
     78   FILE_PATH_LITERAL(".app"),
     79   FILE_PATH_LITERAL(".application"),
     80   FILE_PATH_LITERAL(".asp"),
     81   FILE_PATH_LITERAL(".asx"),
     82   FILE_PATH_LITERAL(".bas"),
     83   FILE_PATH_LITERAL(".bash"),
     84   FILE_PATH_LITERAL(".bat"),
     85   FILE_PATH_LITERAL(".cfg"),
     86   FILE_PATH_LITERAL(".chi"),
     87   FILE_PATH_LITERAL(".chm"),
     88   FILE_PATH_LITERAL(".class"),
     89   FILE_PATH_LITERAL(".cmd"),
     90   FILE_PATH_LITERAL(".com"),
     91   FILE_PATH_LITERAL(".command"),
     92   FILE_PATH_LITERAL(".crt"),
     93   FILE_PATH_LITERAL(".crx"),
     94   FILE_PATH_LITERAL(".csh"),
     95   FILE_PATH_LITERAL(".deb"),
     96   FILE_PATH_LITERAL(".dex"),
     97   FILE_PATH_LITERAL(".dll"),
     98   FILE_PATH_LITERAL(".drv"),
     99   FILE_PATH_LITERAL(".exe"),
    100   FILE_PATH_LITERAL(".fxp"),
    101   FILE_PATH_LITERAL(".grp"),
    102   FILE_PATH_LITERAL(".hlp"),
    103   FILE_PATH_LITERAL(".hta"),
    104   FILE_PATH_LITERAL(".htm"),
    105   FILE_PATH_LITERAL(".html"),
    106   FILE_PATH_LITERAL(".htt"),
    107   FILE_PATH_LITERAL(".inf"),
    108   FILE_PATH_LITERAL(".ini"),
    109   FILE_PATH_LITERAL(".ins"),
    110   FILE_PATH_LITERAL(".isp"),
    111   FILE_PATH_LITERAL(".jar"),
    112   FILE_PATH_LITERAL(".jnlp"),
    113   FILE_PATH_LITERAL(".user.js"),
    114   FILE_PATH_LITERAL(".js"),
    115   FILE_PATH_LITERAL(".jse"),
    116   FILE_PATH_LITERAL(".ksh"),
    117   FILE_PATH_LITERAL(".lnk"),
    118   FILE_PATH_LITERAL(".local"),
    119   FILE_PATH_LITERAL(".mad"),
    120   FILE_PATH_LITERAL(".maf"),
    121   FILE_PATH_LITERAL(".mag"),
    122   FILE_PATH_LITERAL(".mam"),
    123   FILE_PATH_LITERAL(".manifest"),
    124   FILE_PATH_LITERAL(".maq"),
    125   FILE_PATH_LITERAL(".mar"),
    126   FILE_PATH_LITERAL(".mas"),
    127   FILE_PATH_LITERAL(".mat"),
    128   FILE_PATH_LITERAL(".mau"),
    129   FILE_PATH_LITERAL(".mav"),
    130   FILE_PATH_LITERAL(".maw"),
    131   FILE_PATH_LITERAL(".mda"),
    132   FILE_PATH_LITERAL(".mdb"),
    133   FILE_PATH_LITERAL(".mde"),
    134   FILE_PATH_LITERAL(".mdt"),
    135   FILE_PATH_LITERAL(".mdw"),
    136   FILE_PATH_LITERAL(".mdz"),
    137   FILE_PATH_LITERAL(".mht"),
    138   FILE_PATH_LITERAL(".mhtml"),
    139   FILE_PATH_LITERAL(".mmc"),
    140   FILE_PATH_LITERAL(".mof"),
    141   FILE_PATH_LITERAL(".msc"),
    142   FILE_PATH_LITERAL(".msh"),
    143   FILE_PATH_LITERAL(".mshxml"),
    144   FILE_PATH_LITERAL(".msi"),
    145   FILE_PATH_LITERAL(".msp"),
    146   FILE_PATH_LITERAL(".mst"),
    147   FILE_PATH_LITERAL(".ocx"),
    148   FILE_PATH_LITERAL(".ops"),
    149   FILE_PATH_LITERAL(".pcd"),
    150   FILE_PATH_LITERAL(".pif"),
    151   FILE_PATH_LITERAL(".pkg"),
    152   FILE_PATH_LITERAL(".pl"),
    153   FILE_PATH_LITERAL(".plg"),
    154   FILE_PATH_LITERAL(".prf"),
    155   FILE_PATH_LITERAL(".prg"),
    156   FILE_PATH_LITERAL(".pst"),
    157   FILE_PATH_LITERAL(".py"),
    158   FILE_PATH_LITERAL(".pyc"),
    159   FILE_PATH_LITERAL(".pyw"),
    160   FILE_PATH_LITERAL(".rb"),
    161   FILE_PATH_LITERAL(".reg"),
    162   FILE_PATH_LITERAL(".rpm"),
    163   FILE_PATH_LITERAL(".scf"),
    164   FILE_PATH_LITERAL(".scr"),
    165   FILE_PATH_LITERAL(".sct"),
    166   FILE_PATH_LITERAL(".sh"),
    167   FILE_PATH_LITERAL(".shar"),
    168   FILE_PATH_LITERAL(".shb"),
    169   FILE_PATH_LITERAL(".shs"),
    170   FILE_PATH_LITERAL(".shtm"),
    171   FILE_PATH_LITERAL(".shtml"),
    172   FILE_PATH_LITERAL(".spl"),
    173   FILE_PATH_LITERAL(".svg"),
    174   FILE_PATH_LITERAL(".swf"),
    175   FILE_PATH_LITERAL(".sys"),
    176   FILE_PATH_LITERAL(".tcsh"),
    177   FILE_PATH_LITERAL(".url"),
    178   FILE_PATH_LITERAL(".vb"),
    179   FILE_PATH_LITERAL(".vbe"),
    180   FILE_PATH_LITERAL(".vbs"),
    181   FILE_PATH_LITERAL(".vsd"),
    182   FILE_PATH_LITERAL(".vsmacros"),
    183   FILE_PATH_LITERAL(".vss"),
    184   FILE_PATH_LITERAL(".vst"),
    185   FILE_PATH_LITERAL(".vsw"),
    186   FILE_PATH_LITERAL(".ws"),
    187   FILE_PATH_LITERAL(".wsc"),
    188   FILE_PATH_LITERAL(".wsf"),
    189   FILE_PATH_LITERAL(".wsh"),
    190   FILE_PATH_LITERAL(".xbap"),
    191   FILE_PATH_LITERAL(".xht"),
    192   FILE_PATH_LITERAL(".xhtm"),
    193   FILE_PATH_LITERAL(".xhtml"),
    194   FILE_PATH_LITERAL(".xml"),
    195   FILE_PATH_LITERAL(".xsl"),
    196   FILE_PATH_LITERAL(".xslt")
    197 };
    198 
    199 // Maps extensions to their matching UMA histogram int value.
    200 int GetDangerousFileType(const base::FilePath& file_path) {
    201   for (size_t i = 0; i < arraysize(kDangerousFileTypes); ++i) {
    202     if (file_path.MatchesExtension(kDangerousFileTypes[i]))
    203       return i + 1;
    204   }
    205   return 0;  // Unknown extension.
    206 }
    207 
    208 } // namespace
    209 
    210 void RecordDownloadCount(DownloadCountTypes type) {
    211   UMA_HISTOGRAM_ENUMERATION(
    212       "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
    213 }
    214 
    215 void RecordDownloadSource(DownloadSource source) {
    216   UMA_HISTOGRAM_ENUMERATION(
    217       "Download.Sources", source, DOWNLOAD_SOURCE_LAST_ENTRY);
    218 }
    219 
    220 void RecordDownloadCompleted(const base::TimeTicks& start, int64 download_len) {
    221   RecordDownloadCount(COMPLETED_COUNT);
    222   UMA_HISTOGRAM_LONG_TIMES("Download.Time", (base::TimeTicks::Now() - start));
    223   int64 max = 1024 * 1024 * 1024;  // One Terabyte.
    224   download_len /= 1024;  // In Kilobytes
    225   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.DownloadSize",
    226                               download_len,
    227                               1,
    228                               max,
    229                               256);
    230 }
    231 
    232 void RecordDownloadInterrupted(DownloadInterruptReason reason,
    233                                int64 received,
    234                                int64 total) {
    235   RecordDownloadCount(INTERRUPTED_COUNT);
    236   UMA_HISTOGRAM_CUSTOM_ENUMERATION(
    237       "Download.InterruptedReason",
    238       reason,
    239       base::CustomHistogram::ArrayToCustomRanges(
    240           kAllInterruptReasonCodes, arraysize(kAllInterruptReasonCodes)));
    241 
    242   // The maximum should be 2^kBuckets, to have the logarithmic bucket
    243   // boundaries fall on powers of 2.
    244   static const int kBuckets = 30;
    245   static const int64 kMaxKb = 1 << kBuckets;  // One Terabyte, in Kilobytes.
    246   int64 delta_bytes = total - received;
    247   bool unknown_size = total <= 0;
    248   int64 received_kb = received / 1024;
    249   int64 total_kb = total / 1024;
    250   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedReceivedSizeK",
    251                               received_kb,
    252                               1,
    253                               kMaxKb,
    254                               kBuckets);
    255   if (!unknown_size) {
    256     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedTotalSizeK",
    257                                 total_kb,
    258                                 1,
    259                                 kMaxKb,
    260                                 kBuckets);
    261     if (delta_bytes == 0) {
    262       RecordDownloadCount(INTERRUPTED_AT_END_COUNT);
    263       UMA_HISTOGRAM_CUSTOM_ENUMERATION(
    264           "Download.InterruptedAtEndReason",
    265           reason,
    266           base::CustomHistogram::ArrayToCustomRanges(
    267               kAllInterruptReasonCodes,
    268               arraysize(kAllInterruptReasonCodes)));
    269     } else if (delta_bytes > 0) {
    270       UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedOverrunBytes",
    271                                   delta_bytes,
    272                                   1,
    273                                   kMaxKb,
    274                                   kBuckets);
    275     } else {
    276       UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedUnderrunBytes",
    277                                   -delta_bytes,
    278                                   1,
    279                                   kMaxKb,
    280                                   kBuckets);
    281     }
    282   }
    283 
    284   UMA_HISTOGRAM_BOOLEAN("Download.InterruptedUnknownSize", unknown_size);
    285 }
    286 
    287 void RecordMaliciousDownloadClassified(DownloadDangerType danger_type) {
    288   UMA_HISTOGRAM_ENUMERATION("Download.MaliciousDownloadClassified",
    289                             danger_type,
    290                             DOWNLOAD_DANGER_TYPE_MAX);
    291 }
    292 
    293 void RecordDangerousDownloadAccept(DownloadDangerType danger_type,
    294                                    const base::FilePath& file_path) {
    295   UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated",
    296                             danger_type,
    297                             DOWNLOAD_DANGER_TYPE_MAX);
    298   if (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE) {
    299     UMA_HISTOGRAM_SPARSE_SLOWLY(
    300         "Download.DangerousFile.DangerousDownloadValidated",
    301         GetDangerousFileType(file_path));
    302   }
    303 }
    304 
    305 void RecordDangerousDownloadDiscard(DownloadDiscardReason reason,
    306                                     DownloadDangerType danger_type,
    307                                     const base::FilePath& file_path) {
    308   switch (reason) {
    309     case DOWNLOAD_DISCARD_DUE_TO_USER_ACTION:
    310       UMA_HISTOGRAM_ENUMERATION(
    311           "Download.UserDiscard", danger_type, DOWNLOAD_DANGER_TYPE_MAX);
    312       if (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE) {
    313         UMA_HISTOGRAM_SPARSE_SLOWLY("Download.DangerousFile.UserDiscard",
    314                                     GetDangerousFileType(file_path));
    315       }
    316       break;
    317     case DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN:
    318       UMA_HISTOGRAM_ENUMERATION(
    319           "Download.Discard", danger_type, DOWNLOAD_DANGER_TYPE_MAX);
    320       if (danger_type == DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE) {
    321         UMA_HISTOGRAM_SPARSE_SLOWLY("Download.DangerousFile.Discard",
    322                                     GetDangerousFileType(file_path));
    323       }
    324       break;
    325     default:
    326       NOTREACHED();
    327   }
    328 }
    329 
    330 void RecordDownloadWriteSize(size_t data_len) {
    331   int max = 1024 * 1024;  // One Megabyte.
    332   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.WriteSize", data_len, 1, max, 256);
    333 }
    334 
    335 void RecordDownloadWriteLoopCount(int count) {
    336   UMA_HISTOGRAM_ENUMERATION("Download.WriteLoopCount", count, 20);
    337 }
    338 
    339 void RecordAcceptsRanges(const std::string& accepts_ranges,
    340                          int64 download_len,
    341                          bool has_strong_validator) {
    342   int64 max = 1024 * 1024 * 1024;  // One Terabyte.
    343   download_len /= 1024;  // In Kilobytes
    344   static const int kBuckets = 50;
    345 
    346   if (LowerCaseEqualsASCII(accepts_ranges, "none")) {
    347     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.AcceptRangesNone.KBytes",
    348                                 download_len,
    349                                 1,
    350                                 max,
    351                                 kBuckets);
    352   } else if (LowerCaseEqualsASCII(accepts_ranges, "bytes")) {
    353     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.AcceptRangesBytes.KBytes",
    354                                 download_len,
    355                                 1,
    356                                 max,
    357                                 kBuckets);
    358     if (has_strong_validator)
    359       RecordDownloadCount(STRONG_VALIDATOR_AND_ACCEPTS_RANGES);
    360   } else {
    361     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.AcceptRangesMissingOrInvalid.KBytes",
    362                                 download_len,
    363                                 1,
    364                                 max,
    365                                 kBuckets);
    366   }
    367 }
    368 
    369 namespace {
    370 
    371 enum DownloadContent {
    372   DOWNLOAD_CONTENT_UNRECOGNIZED = 0,
    373   DOWNLOAD_CONTENT_TEXT = 1,
    374   DOWNLOAD_CONTENT_IMAGE = 2,
    375   DOWNLOAD_CONTENT_AUDIO = 3,
    376   DOWNLOAD_CONTENT_VIDEO = 4,
    377   DOWNLOAD_CONTENT_OCTET_STREAM = 5,
    378   DOWNLOAD_CONTENT_PDF = 6,
    379   DOWNLOAD_CONTENT_DOC = 7,
    380   DOWNLOAD_CONTENT_XLS = 8,
    381   DOWNLOAD_CONTENT_PPT = 9,
    382   DOWNLOAD_CONTENT_ARCHIVE = 10,
    383   DOWNLOAD_CONTENT_EXE = 11,
    384   DOWNLOAD_CONTENT_DMG = 12,
    385   DOWNLOAD_CONTENT_CRX = 13,
    386   DOWNLOAD_CONTENT_MAX = 14,
    387 };
    388 
    389 struct MimeTypeToDownloadContent {
    390   const char* mime_type;
    391   DownloadContent download_content;
    392 };
    393 
    394 static MimeTypeToDownloadContent kMapMimeTypeToDownloadContent[] = {
    395   {"application/octet-stream", DOWNLOAD_CONTENT_OCTET_STREAM},
    396   {"binary/octet-stream", DOWNLOAD_CONTENT_OCTET_STREAM},
    397   {"application/pdf", DOWNLOAD_CONTENT_PDF},
    398   {"application/msword", DOWNLOAD_CONTENT_DOC},
    399   {"application/vnd.ms-excel", DOWNLOAD_CONTENT_XLS},
    400   {"application/vns.ms-powerpoint", DOWNLOAD_CONTENT_PPT},
    401   {"application/zip", DOWNLOAD_CONTENT_ARCHIVE},
    402   {"application/x-gzip", DOWNLOAD_CONTENT_ARCHIVE},
    403   {"application/x-rar-compressed", DOWNLOAD_CONTENT_ARCHIVE},
    404   {"application/x-tar", DOWNLOAD_CONTENT_ARCHIVE},
    405   {"application/x-bzip", DOWNLOAD_CONTENT_ARCHIVE},
    406   {"application/x-exe", DOWNLOAD_CONTENT_EXE},
    407   {"application/x-apple-diskimage", DOWNLOAD_CONTENT_DMG},
    408   {"application/x-chrome-extension", DOWNLOAD_CONTENT_CRX},
    409 };
    410 
    411 enum DownloadImage {
    412   DOWNLOAD_IMAGE_UNRECOGNIZED = 0,
    413   DOWNLOAD_IMAGE_GIF = 1,
    414   DOWNLOAD_IMAGE_JPEG = 2,
    415   DOWNLOAD_IMAGE_PNG = 3,
    416   DOWNLOAD_IMAGE_TIFF = 4,
    417   DOWNLOAD_IMAGE_ICON = 5,
    418   DOWNLOAD_IMAGE_WEBP = 6,
    419   DOWNLOAD_IMAGE_MAX = 7,
    420 };
    421 
    422 struct MimeTypeToDownloadImage {
    423   const char* mime_type;
    424   DownloadImage download_image;
    425 };
    426 
    427 static MimeTypeToDownloadImage kMapMimeTypeToDownloadImage[] = {
    428   {"image/gif", DOWNLOAD_IMAGE_GIF},
    429   {"image/jpeg", DOWNLOAD_IMAGE_JPEG},
    430   {"image/png", DOWNLOAD_IMAGE_PNG},
    431   {"image/tiff", DOWNLOAD_IMAGE_TIFF},
    432   {"image/vnd.microsoft.icon", DOWNLOAD_IMAGE_ICON},
    433   {"image/webp", DOWNLOAD_IMAGE_WEBP},
    434 };
    435 
    436 void RecordDownloadImageType(const std::string& mime_type_string) {
    437   DownloadImage download_image = DOWNLOAD_IMAGE_UNRECOGNIZED;
    438 
    439   // Look up exact matches.
    440   for (size_t i = 0; i < arraysize(kMapMimeTypeToDownloadImage); ++i) {
    441     const MimeTypeToDownloadImage& entry = kMapMimeTypeToDownloadImage[i];
    442     if (mime_type_string == entry.mime_type) {
    443       download_image = entry.download_image;
    444       break;
    445     }
    446   }
    447 
    448   UMA_HISTOGRAM_ENUMERATION("Download.ContentImageType",
    449                             download_image,
    450                             DOWNLOAD_IMAGE_MAX);
    451 }
    452 
    453 }  // namespace
    454 
    455 void RecordDownloadMimeType(const std::string& mime_type_string) {
    456   DownloadContent download_content = DOWNLOAD_CONTENT_UNRECOGNIZED;
    457 
    458   // Look up exact matches.
    459   for (size_t i = 0; i < arraysize(kMapMimeTypeToDownloadContent); ++i) {
    460     const MimeTypeToDownloadContent& entry = kMapMimeTypeToDownloadContent[i];
    461     if (mime_type_string == entry.mime_type) {
    462       download_content = entry.download_content;
    463       break;
    464     }
    465   }
    466 
    467   // Do partial matches.
    468   if (download_content == DOWNLOAD_CONTENT_UNRECOGNIZED) {
    469     if (StartsWithASCII(mime_type_string, "text/", true)) {
    470       download_content = DOWNLOAD_CONTENT_TEXT;
    471     } else if (StartsWithASCII(mime_type_string, "image/", true)) {
    472       download_content = DOWNLOAD_CONTENT_IMAGE;
    473       RecordDownloadImageType(mime_type_string);
    474     } else if (StartsWithASCII(mime_type_string, "audio/", true)) {
    475       download_content = DOWNLOAD_CONTENT_AUDIO;
    476     } else if (StartsWithASCII(mime_type_string, "video/", true)) {
    477       download_content = DOWNLOAD_CONTENT_VIDEO;
    478     }
    479   }
    480 
    481   // Record the value.
    482   UMA_HISTOGRAM_ENUMERATION("Download.ContentType",
    483                             download_content,
    484                             DOWNLOAD_CONTENT_MAX);
    485 }
    486 
    487 void RecordDownloadContentDisposition(
    488     const std::string& content_disposition_string) {
    489   if (content_disposition_string.empty())
    490     return;
    491   net::HttpContentDisposition content_disposition(content_disposition_string,
    492                                                   std::string());
    493   int result = content_disposition.parse_result_flags();
    494 
    495   bool is_valid = !content_disposition.filename().empty();
    496   RecordContentDispositionCount(CONTENT_DISPOSITION_HEADER_PRESENT, true);
    497   RecordContentDispositionCount(CONTENT_DISPOSITION_IS_VALID, is_valid);
    498   if (!is_valid)
    499     return;
    500 
    501   RecordContentDispositionCountFlag(
    502       CONTENT_DISPOSITION_HAS_DISPOSITION_TYPE, result,
    503       net::HttpContentDisposition::HAS_DISPOSITION_TYPE);
    504   RecordContentDispositionCountFlag(
    505       CONTENT_DISPOSITION_HAS_UNKNOWN_TYPE, result,
    506       net::HttpContentDisposition::HAS_UNKNOWN_DISPOSITION_TYPE);
    507   RecordContentDispositionCountFlag(
    508       CONTENT_DISPOSITION_HAS_NAME, result,
    509       net::HttpContentDisposition::HAS_NAME);
    510   RecordContentDispositionCountFlag(
    511       CONTENT_DISPOSITION_HAS_FILENAME, result,
    512       net::HttpContentDisposition::HAS_FILENAME);
    513   RecordContentDispositionCountFlag(
    514       CONTENT_DISPOSITION_HAS_EXT_FILENAME, result,
    515       net::HttpContentDisposition::HAS_EXT_FILENAME);
    516   RecordContentDispositionCountFlag(
    517       CONTENT_DISPOSITION_HAS_NON_ASCII_STRINGS, result,
    518       net::HttpContentDisposition::HAS_NON_ASCII_STRINGS);
    519   RecordContentDispositionCountFlag(
    520       CONTENT_DISPOSITION_HAS_PERCENT_ENCODED_STRINGS, result,
    521       net::HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS);
    522   RecordContentDispositionCountFlag(
    523       CONTENT_DISPOSITION_HAS_RFC2047_ENCODED_STRINGS, result,
    524       net::HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS);
    525 
    526   RecordContentDispositionCount(
    527       CONTENT_DISPOSITION_HAS_NAME_ONLY,
    528       (result & (net::HttpContentDisposition::HAS_NAME |
    529                  net::HttpContentDisposition::HAS_FILENAME |
    530                  net::HttpContentDisposition::HAS_EXT_FILENAME)) ==
    531       net::HttpContentDisposition::HAS_NAME);
    532 }
    533 
    534 void RecordFileThreadReceiveBuffers(size_t num_buffers) {
    535     UMA_HISTOGRAM_CUSTOM_COUNTS(
    536       "Download.FileThreadReceiveBuffers", num_buffers, 1,
    537       100, 100);
    538 }
    539 
    540 void RecordBandwidth(double actual_bandwidth, double potential_bandwidth) {
    541   UMA_HISTOGRAM_CUSTOM_COUNTS(
    542       "Download.ActualBandwidth", actual_bandwidth, 1, 1000000000, 50);
    543   UMA_HISTOGRAM_CUSTOM_COUNTS(
    544       "Download.PotentialBandwidth", potential_bandwidth, 1, 1000000000, 50);
    545   UMA_HISTOGRAM_PERCENTAGE(
    546       "Download.BandwidthUsed",
    547       (int) ((actual_bandwidth * 100)/ potential_bandwidth));
    548 }
    549 
    550 void RecordOpen(const base::Time& end, bool first) {
    551   if (!end.is_null()) {
    552     UMA_HISTOGRAM_LONG_TIMES("Download.OpenTime", (base::Time::Now() - end));
    553     if (first) {
    554       UMA_HISTOGRAM_LONG_TIMES("Download.FirstOpenTime",
    555                               (base::Time::Now() - end));
    556     }
    557   }
    558 }
    559 
    560 void RecordClearAllSize(int size) {
    561   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.ClearAllSize",
    562                               size,
    563                               0/*min*/,
    564                               (1 << 10)/*max*/,
    565                               32/*num_buckets*/);
    566 }
    567 
    568 void RecordOpensOutstanding(int size) {
    569   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.OpensOutstanding",
    570                               size,
    571                               0/*min*/,
    572                               (1 << 10)/*max*/,
    573                               64/*num_buckets*/);
    574 }
    575 
    576 void RecordContiguousWriteTime(base::TimeDelta time_blocked) {
    577   UMA_HISTOGRAM_TIMES("Download.FileThreadBlockedTime", time_blocked);
    578 }
    579 
    580 // Record what percentage of the time we have the network flow controlled.
    581 void RecordNetworkBlockage(base::TimeDelta resource_handler_lifetime,
    582                            base::TimeDelta resource_handler_blocked_time) {
    583   int percentage = 0;
    584   // Avoid division by zero errors.
    585   if (resource_handler_blocked_time != base::TimeDelta()) {
    586     percentage =
    587         resource_handler_blocked_time * 100 / resource_handler_lifetime;
    588   }
    589 
    590   UMA_HISTOGRAM_COUNTS_100("Download.ResourceHandlerBlockedPercentage",
    591                            percentage);
    592 }
    593 
    594 void RecordFileBandwidth(size_t length,
    595                          base::TimeDelta disk_write_time,
    596                          base::TimeDelta elapsed_time) {
    597   size_t elapsed_time_ms = elapsed_time.InMilliseconds();
    598   if (0u == elapsed_time_ms)
    599     elapsed_time_ms = 1;
    600   size_t disk_write_time_ms = disk_write_time.InMilliseconds();
    601   if (0u == disk_write_time_ms)
    602     disk_write_time_ms = 1;
    603 
    604   UMA_HISTOGRAM_CUSTOM_COUNTS(
    605       "Download.BandwidthOverallBytesPerSecond",
    606       (1000 * length / elapsed_time_ms), 1, 50000000, 50);
    607   UMA_HISTOGRAM_CUSTOM_COUNTS(
    608       "Download.BandwidthDiskBytesPerSecond",
    609       (1000 * length / disk_write_time_ms), 1, 50000000, 50);
    610   UMA_HISTOGRAM_COUNTS_100("Download.DiskBandwidthUsedPercentage",
    611                            disk_write_time_ms * 100 / elapsed_time_ms);
    612 }
    613 
    614 void RecordDownloadFileRenameResultAfterRetry(
    615     base::TimeDelta time_since_first_failure,
    616     DownloadInterruptReason interrupt_reason) {
    617   if (interrupt_reason == DOWNLOAD_INTERRUPT_REASON_NONE) {
    618     UMA_HISTOGRAM_TIMES("Download.TimeToRenameSuccessAfterInitialFailure",
    619                         time_since_first_failure);
    620   } else {
    621     UMA_HISTOGRAM_TIMES("Download.TimeToRenameFailureAfterInitialFailure",
    622                         time_since_first_failure);
    623   }
    624 }
    625 
    626 void RecordSavePackageEvent(SavePackageEvent event) {
    627   UMA_HISTOGRAM_ENUMERATION("Download.SavePackage",
    628                             event,
    629                             SAVE_PACKAGE_LAST_ENTRY);
    630 }
    631 
    632 void RecordOriginStateOnResumption(bool is_partial,
    633                                    int state) {
    634   if (is_partial)
    635     UMA_HISTOGRAM_ENUMERATION("Download.OriginStateOnPartialResumption", state,
    636                               ORIGIN_STATE_ON_RESUMPTION_MAX);
    637   else
    638     UMA_HISTOGRAM_ENUMERATION("Download.OriginStateOnFullResumption", state,
    639                               ORIGIN_STATE_ON_RESUMPTION_MAX);
    640 }
    641 
    642 }  // namespace content
    643