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