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
14namespace content {
15
16namespace {
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).
20const 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.
29enum 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
55void 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
63void 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.
72const 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.
200int 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
210void RecordDownloadCount(DownloadCountTypes type) {
211  UMA_HISTOGRAM_ENUMERATION(
212      "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
213}
214
215void RecordDownloadSource(DownloadSource source) {
216  UMA_HISTOGRAM_ENUMERATION(
217      "Download.Sources", source, DOWNLOAD_SOURCE_LAST_ENTRY);
218}
219
220void 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
232void 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
287void RecordMaliciousDownloadClassified(DownloadDangerType danger_type) {
288  UMA_HISTOGRAM_ENUMERATION("Download.MaliciousDownloadClassified",
289                            danger_type,
290                            DOWNLOAD_DANGER_TYPE_MAX);
291}
292
293void 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
305void 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
330void 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
335void RecordDownloadWriteLoopCount(int count) {
336  UMA_HISTOGRAM_ENUMERATION("Download.WriteLoopCount", count, 20);
337}
338
339void 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
369namespace {
370
371enum 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
389struct MimeTypeToDownloadContent {
390  const char* mime_type;
391  DownloadContent download_content;
392};
393
394static 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
411enum 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
422struct MimeTypeToDownloadImage {
423  const char* mime_type;
424  DownloadImage download_image;
425};
426
427static 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
436void 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
455void 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
487void 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
534void RecordFileThreadReceiveBuffers(size_t num_buffers) {
535    UMA_HISTOGRAM_CUSTOM_COUNTS(
536      "Download.FileThreadReceiveBuffers", num_buffers, 1,
537      100, 100);
538}
539
540void 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
550void 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
560void 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
568void 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
576void 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.
581void 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
594void 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
614void 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
626void RecordSavePackageEvent(SavePackageEvent event) {
627  UMA_HISTOGRAM_ENUMERATION("Download.SavePackage",
628                            event,
629                            SAVE_PACKAGE_LAST_ENTRY);
630}
631
632void 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