blob: 957b4ee9885c675cc037c75b479a3edc2314e5ec [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/download/public/common/download_stats.h"
#include <map>
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/safe_browsing/buildflags.h"
#include "net/http/http_content_disposition.h"
#include "net/http/http_util.h"
namespace download {
namespace {
// All possible error codes from the network module. Note that the error codes
// are all positive (since histograms expect positive sample values).
const int kAllInterruptReasonCodes[] = {
#define INTERRUPT_REASON(label, value) (value),
#include "components/download/public/common/download_interrupt_reason_values.h"
#undef INTERRUPT_REASON
};
// These values are based on net::HttpContentDisposition::ParseResult values.
// Values other than HEADER_PRESENT and IS_VALID are only measured if |IS_VALID|
// is true.
enum ContentDispositionCountTypes {
// Count of downloads which had a Content-Disposition headers. The total
// number of downloads is measured by UNTHROTTLED_COUNT.
CONTENT_DISPOSITION_HEADER_PRESENT = 0,
// Either 'filename' or 'filename*' attributes were valid and
// yielded a non-empty filename.
CONTENT_DISPOSITION_IS_VALID,
// The following enum values correspond to
// net::HttpContentDisposition::ParseResult.
CONTENT_DISPOSITION_HAS_DISPOSITION_TYPE,
CONTENT_DISPOSITION_HAS_UNKNOWN_TYPE,
CONTENT_DISPOSITION_HAS_NAME, // Obsolete; kept for UMA compatiblity.
CONTENT_DISPOSITION_HAS_FILENAME,
CONTENT_DISPOSITION_HAS_EXT_FILENAME,
CONTENT_DISPOSITION_HAS_NON_ASCII_STRINGS,
CONTENT_DISPOSITION_HAS_PERCENT_ENCODED_STRINGS,
CONTENT_DISPOSITION_HAS_RFC2047_ENCODED_STRINGS,
CONTENT_DISPOSITION_HAS_NAME_ONLY, // Obsolete; kept for UMA compatiblity.
CONTENT_DISPOSITION_HAS_SINGLE_QUOTED_FILENAME,
CONTENT_DISPOSITION_LAST_ENTRY
};
// Helper method to calculate the bandwidth given the data length and time.
int64_t CalculateBandwidthBytesPerSecond(size_t length,
base::TimeDelta elapsed_time) {
int64_t elapsed_time_ms = elapsed_time.InMilliseconds();
if (0 == elapsed_time_ms)
elapsed_time_ms = 1;
return 1000 * static_cast<int64_t>(length) / elapsed_time_ms;
}
// Records a histogram with download source suffix.
std::string CreateHistogramNameWithSuffix(const std::string& name,
DownloadSource download_source) {
std::string suffix;
switch (download_source) {
case DownloadSource::UNKNOWN:
suffix = "UnknownSource";
break;
case DownloadSource::NAVIGATION:
suffix = "Navigation";
break;
case DownloadSource::DRAG_AND_DROP:
suffix = "DragAndDrop";
break;
case DownloadSource::FROM_RENDERER:
suffix = "FromRenderer";
break;
case DownloadSource::EXTENSION_API:
suffix = "ExtensionAPI";
break;
case DownloadSource::EXTENSION_INSTALLER:
suffix = "ExtensionInstaller";
break;
case DownloadSource::INTERNAL_API:
suffix = "InternalAPI";
break;
case DownloadSource::WEB_CONTENTS_API:
suffix = "WebContentsAPI";
break;
case DownloadSource::OFFLINE_PAGE:
suffix = "OfflinePage";
break;
case DownloadSource::CONTEXT_MENU:
suffix = "ContextMenu";
break;
case DownloadSource::RETRY:
suffix = "Retry";
break;
case DownloadSource::RETRY_FROM_BUBBLE:
suffix = "RetryFromBubble";
break;
case DownloadSource::TOOLBAR_MENU:
suffix = "ToolbarMenu";
break;
}
return name + "." + suffix;
}
void RecordConnectionType(
const std::string& name,
net::NetworkChangeNotifier::ConnectionType connection_type,
DownloadSource download_source) {
using ConnectionType = net::NetworkChangeNotifier::ConnectionType;
base::UmaHistogramExactLinear(name, connection_type,
ConnectionType::CONNECTION_LAST + 1);
base::UmaHistogramExactLinear(
CreateHistogramNameWithSuffix(name, download_source), connection_type,
ConnectionType::CONNECTION_LAST + 1);
}
} // namespace
void RecordDownloadCount(DownloadCountTypes type) {
UMA_HISTOGRAM_ENUMERATION("Download.Counts", type,
DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
}
void RecordDownloadCountWithSource(DownloadCountTypes type,
DownloadSource download_source) {
RecordDownloadCount(type);
std::string name =
CreateHistogramNameWithSuffix("Download.Counts", download_source);
base::UmaHistogramEnumeration(name, type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
}
void RecordNewDownloadStarted(
net::NetworkChangeNotifier::ConnectionType connection_type,
DownloadSource download_source) {
RecordDownloadCountWithSource(NEW_DOWNLOAD_COUNT, download_source);
RecordConnectionType("Download.NetworkConnectionType.StartNew",
connection_type, download_source);
}
void RecordDownloadCompleted(
int64_t download_len,
bool is_parallelizable,
net::NetworkChangeNotifier::ConnectionType connection_type,
DownloadSource download_source) {
RecordDownloadCountWithSource(COMPLETED_COUNT, download_source);
int64_t max = 1024 * 1024 * 1024; // One Terabyte.
download_len /= 1024; // In Kilobytes
UMA_HISTOGRAM_CUSTOM_COUNTS("Download.DownloadSize", download_len, 1, max,
256);
if (is_parallelizable) {
UMA_HISTOGRAM_CUSTOM_COUNTS("Download.DownloadSize.Parallelizable",
download_len, 1, max, 256);
}
}
void RecordDownloadInterrupted(DownloadInterruptReason reason,
int64_t received,
int64_t total,
bool is_parallelizable,
bool is_parallel_download_enabled,
DownloadSource download_source) {
RecordDownloadCountWithSource(INTERRUPTED_COUNT, download_source);
if (is_parallelizable) {
RecordParallelizableDownloadCount(INTERRUPTED_COUNT,
is_parallel_download_enabled);
}
std::vector<base::HistogramBase::Sample32> samples =
base::CustomHistogram::ArrayToCustomEnumRanges(kAllInterruptReasonCodes);
UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.InterruptedReason", reason,
samples);
std::string name = CreateHistogramNameWithSuffix("Download.InterruptedReason",
download_source);
base::HistogramBase* counter = base::CustomHistogram::FactoryGet(
name, samples, base::HistogramBase::kUmaTargetedHistogramFlag);
counter->Add(reason);
if (is_parallel_download_enabled) {
UMA_HISTOGRAM_CUSTOM_ENUMERATION(
"Download.InterruptedReason.ParallelDownload", reason, samples);
}
int64_t delta_bytes = total - received;
bool unknown_size = total <= 0;
if (!unknown_size) {
if (delta_bytes == 0) {
RecordDownloadCountWithSource(INTERRUPTED_AT_END_COUNT, download_source);
if (is_parallelizable) {
RecordParallelizableDownloadCount(INTERRUPTED_AT_END_COUNT,
is_parallel_download_enabled);
}
}
}
}
void RecordDangerousDownloadAccept(DownloadDangerType danger_type,
const base::FilePath& file_path) {
UMA_HISTOGRAM_ENUMERATION("Download.UserValidatedDangerousDownload",
danger_type, DOWNLOAD_DANGER_TYPE_MAX);
}
namespace {
int GetMimeTypeMatch(const std::string& mime_type_string,
std::map<std::string, int> mime_type_map) {
for (const auto& entry : mime_type_map) {
if (entry.first == mime_type_string) {
return entry.second;
}
}
return 0;
}
static std::map<std::string, DownloadContent>
getMimeTypeToDownloadContentMap() {
return {
{"application/octet-stream", DownloadContent::kOctetStream},
{"binary/octet-stream", DownloadContent::kOctetStream},
{"application/pdf", DownloadContent::kPdf},
{"application/msword", DownloadContent::kDocument},
{"application/"
"vnd.openxmlformats-officedocument.wordprocessingml.document",
DownloadContent::kDocument},
{"application/rtf", DownloadContent::kDocument},
{"application/vnd.oasis.opendocument.text", DownloadContent::kDocument},
{"application/vnd.google-apps.document", DownloadContent::kDocument},
{"application/vnd.ms-excel", DownloadContent::kSpreadSheet},
{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
DownloadContent::kSpreadSheet},
{"application/vnd.oasis.opendocument.spreadsheet",
DownloadContent::kSpreadSheet},
{"application/vnd.google-apps.spreadsheet",
DownloadContent::kSpreadSheet},
{"application/vns.ms-powerpoint", DownloadContent::kPresentation},
{"application/"
"vnd.openxmlformats-officedocument.presentationml.presentation",
DownloadContent::kPresentation},
{"application/vnd.oasis.opendocument.presentation",
DownloadContent::kPresentation},
{"application/vnd.google-apps.presentation",
DownloadContent::kPresentation},
{"application/zip", DownloadContent::kArchive},
{"application/x-gzip", DownloadContent::kArchive},
{"application/x-rar-compressed", DownloadContent::kArchive},
{"application/x-tar", DownloadContent::kArchive},
{"application/x-bzip", DownloadContent::kArchive},
{"application/x-bzip2", DownloadContent::kArchive},
{"application/x-7z-compressed", DownloadContent::kArchive},
{"application/x-exe", DownloadContent::kExecutable},
{"application/java-archive", DownloadContent::kExecutable},
{"application/vnd.apple.installer+xml", DownloadContent::kExecutable},
{"application/x-csh", DownloadContent::kExecutable},
{"application/x-sh", DownloadContent::kExecutable},
{"application/x-apple-diskimage", DownloadContent::kDmg},
{"application/x-chrome-extension", DownloadContent::kCrx},
{"application/xhtml+xml", DownloadContent::kWeb},
{"application/xml", DownloadContent::kWeb},
{"application/javascript", DownloadContent::kWeb},
{"application/json", DownloadContent::kWeb},
{"application/typescript", DownloadContent::kWeb},
{"application/vnd.mozilla.xul+xml", DownloadContent::kWeb},
{"application/vnd.amazon.ebook", DownloadContent::kEbook},
{"application/epub+zip", DownloadContent::kEbook},
{"application/vnd.android.package-archive", DownloadContent::kApk}};
}
// NOTE: Keep in sync with DownloadImageType in
// tools/metrics/histograms/enums.xml.
enum DownloadImage {
DOWNLOAD_IMAGE_UNRECOGNIZED = 0,
DOWNLOAD_IMAGE_GIF = 1,
DOWNLOAD_IMAGE_JPEG = 2,
DOWNLOAD_IMAGE_PNG = 3,
DOWNLOAD_IMAGE_TIFF = 4,
DOWNLOAD_IMAGE_ICON = 5,
DOWNLOAD_IMAGE_WEBP = 6,
DOWNLOAD_IMAGE_PSD = 7,
DOWNLOAD_IMAGE_SVG = 8,
DOWNLOAD_IMAGE_MAX = 9,
};
static std::map<std::string, int> getMimeTypeToDownloadImageMap() {
return {{"image/gif", DOWNLOAD_IMAGE_GIF},
{"image/jpeg", DOWNLOAD_IMAGE_JPEG},
{"image/png", DOWNLOAD_IMAGE_PNG},
{"image/tiff", DOWNLOAD_IMAGE_TIFF},
{"image/vnd.microsoft.icon", DOWNLOAD_IMAGE_ICON},
{"image/x-icon", DOWNLOAD_IMAGE_ICON},
{"image/webp", DOWNLOAD_IMAGE_WEBP},
{"image/vnd.adobe.photoshop", DOWNLOAD_IMAGE_PSD},
{"image/svg+xml", DOWNLOAD_IMAGE_SVG}};
}
void RecordDownloadImageType(const std::string& mime_type_string) {
DownloadImage download_image = DownloadImage(
GetMimeTypeMatch(mime_type_string, getMimeTypeToDownloadImageMap()));
UMA_HISTOGRAM_ENUMERATION("Download.ContentType.Image", download_image,
DOWNLOAD_IMAGE_MAX);
}
/** Text categories **/
// NOTE: Keep in sync with DownloadTextType in
// tools/metrics/histograms/enums.xml.
enum DownloadText {
DOWNLOAD_TEXT_UNRECOGNIZED = 0,
DOWNLOAD_TEXT_PLAIN = 1,
DOWNLOAD_TEXT_CSS = 2,
DOWNLOAD_TEXT_CSV = 3,
DOWNLOAD_TEXT_HTML = 4,
DOWNLOAD_TEXT_CALENDAR = 5,
DOWNLOAD_TEXT_MAX = 6,
};
static std::map<std::string, int> getMimeTypeToDownloadTextMap() {
return {{"text/plain", DOWNLOAD_TEXT_PLAIN},
{"text/css", DOWNLOAD_TEXT_CSS},
{"text/csv", DOWNLOAD_TEXT_CSV},
{"text/html", DOWNLOAD_TEXT_HTML},
{"text/calendar", DOWNLOAD_TEXT_CALENDAR}};
}
void RecordDownloadTextType(const std::string& mime_type_string) {
DownloadText download_text = DownloadText(
GetMimeTypeMatch(mime_type_string, getMimeTypeToDownloadTextMap()));
UMA_HISTOGRAM_ENUMERATION("Download.ContentType.Text", download_text,
DOWNLOAD_TEXT_MAX);
}
/* Audio categories */
// NOTE: Keep in sync with DownloadAudioType in
// tools/metrics/histograms/enums.xml.
enum DownloadAudio {
DOWNLOAD_AUDIO_UNRECOGNIZED = 0,
DOWNLOAD_AUDIO_AAC = 1,
DOWNLOAD_AUDIO_MIDI = 2,
DOWNLOAD_AUDIO_OGA = 3,
DOWNLOAD_AUDIO_WAV = 4,
DOWNLOAD_AUDIO_WEBA = 5,
DOWNLOAD_AUDIO_3GP = 6,
DOWNLOAD_AUDIO_3G2 = 7,
DOWNLOAD_AUDIO_MP3 = 8,
DOWNLOAD_AUDIO_MAX = 9,
};
static std::map<std::string, int> getMimeTypeToDownloadAudioMap() {
return {
{"audio/aac", DOWNLOAD_AUDIO_AAC}, {"audio/midi", DOWNLOAD_AUDIO_MIDI},
{"audio/ogg", DOWNLOAD_AUDIO_OGA}, {"audio/x-wav", DOWNLOAD_AUDIO_WAV},
{"audio/webm", DOWNLOAD_AUDIO_WEBA}, {"audio/3gpp", DOWNLOAD_AUDIO_3GP},
{"audio/3gpp2", DOWNLOAD_AUDIO_3G2}, {"audio/mp3", DOWNLOAD_AUDIO_MP3}};
}
void RecordDownloadAudioType(const std::string& mime_type_string) {
DownloadAudio download_audio = DownloadAudio(
GetMimeTypeMatch(mime_type_string, getMimeTypeToDownloadAudioMap()));
UMA_HISTOGRAM_ENUMERATION("Download.ContentType.Audio", download_audio,
DOWNLOAD_AUDIO_MAX);
}
/* Video categories */
// NOTE: Keep in sync with DownloadVideoType in
// tools/metrics/histograms/enums.xml.
enum DownloadVideo {
DOWNLOAD_VIDEO_UNRECOGNIZED = 0,
DOWNLOAD_VIDEO_AVI = 1,
DOWNLOAD_VIDEO_MPEG = 2,
DOWNLOAD_VIDEO_OGV = 3,
DOWNLOAD_VIDEO_WEBM = 4,
DOWNLOAD_VIDEO_3GP = 5,
DOWNLOAD_VIDEO_3G2 = 6,
DOWNLOAD_VIDEO_MP4 = 7,
DOWNLOAD_VIDEO_MOV = 8,
DOWNLOAD_VIDEO_WMV = 9,
DOWNLOAD_VIDEO_MAX = 10,
};
static std::map<std::string, int> getMimeTypeToDownloadVideoMap() {
return {{"video/x-msvideo", DOWNLOAD_VIDEO_AVI},
{"video/mpeg", DOWNLOAD_VIDEO_MPEG},
{"video/ogg", DOWNLOAD_VIDEO_OGV},
{"video/webm", DOWNLOAD_VIDEO_WEBM},
{"video/3gpp", DOWNLOAD_VIDEO_3GP},
{"video/3ggp2", DOWNLOAD_VIDEO_3G2},
{"video/mp4", DOWNLOAD_VIDEO_MP4},
{"video/quicktime", DOWNLOAD_VIDEO_MOV},
{"video/x-ms-wmv", DOWNLOAD_VIDEO_WMV}};
}
void RecordDownloadVideoType(const std::string& mime_type_string) {
DownloadVideo download_video = DownloadVideo(
GetMimeTypeMatch(mime_type_string, getMimeTypeToDownloadVideoMap()));
UMA_HISTOGRAM_ENUMERATION("Download.ContentType.Video", download_video,
DOWNLOAD_VIDEO_MAX);
}
// These histograms summarize download mime-types. The same data is recorded in
// a few places, as they exist to sanity-check and understand other metrics.
const char* const kDownloadMetricsVerificationNameItemSecure =
"Download.InsecureBlocking.Verification.Item.Secure";
const char* const kDownloadMetricsVerificationNameItemInsecure =
"Download.InsecureBlocking.Verification.Item.Insecure";
const char* const kDownloadMetricsVerificationNameItemOther =
"Download.InsecureBlocking.Verification.Item.Other";
const char* const kDownloadMetricsVerificationNameManagerSecure =
"Download.InsecureBlocking.Verification.Manager.Secure";
const char* const kDownloadMetricsVerificationNameManagerInsecure =
"Download.InsecureBlocking.Verification.Manager.Insecure";
const char* const kDownloadMetricsVerificationNameManagerOther =
"Download.InsecureBlocking.Verification.Manager.Other";
const char* GetDownloadValidationMetricName(
const DownloadMetricsCallsite& callsite,
const DownloadConnectionSecurity& state) {
DCHECK(callsite == DownloadMetricsCallsite::kDownloadItem ||
callsite == DownloadMetricsCallsite::kMixContentDownloadBlocking);
switch (state) {
case DOWNLOAD_SECURE:
case DOWNLOAD_TARGET_BLOB:
case DOWNLOAD_TARGET_DATA:
case DOWNLOAD_TARGET_FILE:
if (callsite == DownloadMetricsCallsite::kDownloadItem)
return kDownloadMetricsVerificationNameItemSecure;
return kDownloadMetricsVerificationNameManagerSecure;
case DOWNLOAD_TARGET_INSECURE:
case DOWNLOAD_REDIRECT_INSECURE:
case DOWNLOAD_REDIRECT_TARGET_INSECURE:
if (callsite == DownloadMetricsCallsite::kDownloadItem)
return kDownloadMetricsVerificationNameItemInsecure;
return kDownloadMetricsVerificationNameManagerInsecure;
case DOWNLOAD_TARGET_OTHER:
case DOWNLOAD_TARGET_FILESYSTEM:
case DOWNLOAD_TARGET_FTP:
if (callsite == DownloadMetricsCallsite::kDownloadItem)
return kDownloadMetricsVerificationNameItemOther;
return kDownloadMetricsVerificationNameManagerOther;
case DOWNLOAD_CONNECTION_SECURITY_MAX:
NOTREACHED();
}
NOTREACHED();
}
} // namespace
DownloadContent DownloadContentFromMimeType(const std::string& mime_type_string,
bool record_content_subcategory) {
DownloadContent download_content = DownloadContent::kUnrecognized;
for (const auto& entry : getMimeTypeToDownloadContentMap()) {
if (entry.first == mime_type_string) {
download_content = entry.second;
}
}
// Do partial matches.
if (download_content == DownloadContent::kUnrecognized) {
if (base::StartsWith(mime_type_string, "text/",
base::CompareCase::SENSITIVE)) {
download_content = DownloadContent::kText;
if (record_content_subcategory)
RecordDownloadTextType(mime_type_string);
} else if (base::StartsWith(mime_type_string, "image/",
base::CompareCase::SENSITIVE)) {
download_content = DownloadContent::kImage;
if (record_content_subcategory)
RecordDownloadImageType(mime_type_string);
} else if (base::StartsWith(mime_type_string, "audio/",
base::CompareCase::SENSITIVE)) {
download_content = DownloadContent::kAudio;
if (record_content_subcategory)
RecordDownloadAudioType(mime_type_string);
} else if (base::StartsWith(mime_type_string, "video/",
base::CompareCase::SENSITIVE)) {
download_content = DownloadContent::kVideo;
if (record_content_subcategory)
RecordDownloadVideoType(mime_type_string);
} else if (base::StartsWith(mime_type_string, "font/",
base::CompareCase::SENSITIVE)) {
download_content = DownloadContent::kFont;
}
}
return download_content;
}
void RecordDownloadMimeType(const std::string& mime_type_string,
bool is_transient) {
DownloadContent download_content =
DownloadContentFromMimeType(mime_type_string, true);
base::UmaHistogramEnumeration("Download.Start.ContentType", download_content);
#if BUILDFLAG(IS_ANDROID)
base::UmaHistogramEnumeration(
base::StrCat({"Download.Start.ContentType.",
is_transient ? "Transient" : "NonTransient"}),
download_content);
#endif // BUILDFLAG(IS_ANDROID)
}
void RecordDownloadMimeTypeForNormalProfile(const std::string& mime_type_string,
bool is_transient) {
DownloadContent download_content =
DownloadContentFromMimeType(mime_type_string, false);
base::UmaHistogramEnumeration("Download.Start.ContentType.NormalProfile",
download_content);
#if BUILDFLAG(IS_ANDROID)
base::UmaHistogramEnumeration(
base::StrCat({"Download.Start.ContentType.NormalProfile.",
is_transient ? "Transient" : "NonTransient"}),
download_content);
#endif // BUILDFLAG(IS_ANDROID)
}
void RecordFileBandwidth(size_t length,
base::TimeDelta elapsed_time) {
base::UmaHistogramCustomCounts(
"Download.BandwidthOverallBytesPerSecond2",
CalculateBandwidthBytesPerSecond(length, elapsed_time), 1,
200 * 1000 * 1000, 50);
}
void RecordParallelizableDownloadCount(DownloadCountTypes type,
bool is_parallel_download_enabled) {
std::string histogram_name = is_parallel_download_enabled
? "Download.Counts.ParallelDownload"
: "Download.Counts.ParallelizableDownload";
base::UmaHistogramEnumeration(histogram_name, type,
DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
}
namespace {
int g_parallel_download_creation_failure_count_ = 0;
}
void RecordParallelRequestCreationFailure(DownloadInterruptReason reason) {
// This used to log a metric; however there is a test that checks how many
// times that metric (and thus this method) was called. Ultimately that should
// be refactored; but for now instead of logging the metric, just increment
// a counter.
g_parallel_download_creation_failure_count_++;
}
int GetParallelRequestCreationFailureCountForTesting() {
return g_parallel_download_creation_failure_count_;
}
DownloadConnectionSecurity CheckDownloadConnectionSecurity(
const GURL& download_url,
const std::vector<GURL>& url_chain) {
DownloadConnectionSecurity state = DOWNLOAD_TARGET_OTHER;
if (download_url.SchemeIsHTTPOrHTTPS()) {
bool is_final_download_secure = download_url.SchemeIsCryptographic();
bool is_redirect_chain_secure = true;
if (url_chain.size() > std::size_t(1)) {
for (std::size_t i = std::size_t(0); i < url_chain.size() - 1; i++) {
if (!url_chain[i].SchemeIsCryptographic()) {
is_redirect_chain_secure = false;
break;
}
}
}
state = is_final_download_secure
? is_redirect_chain_secure ? DOWNLOAD_SECURE
: DOWNLOAD_REDIRECT_INSECURE
: is_redirect_chain_secure ? DOWNLOAD_TARGET_INSECURE
: DOWNLOAD_REDIRECT_TARGET_INSECURE;
} else if (download_url.SchemeIsBlob()) {
state = DOWNLOAD_TARGET_BLOB;
} else if (download_url.SchemeIs(url::kDataScheme)) {
state = DOWNLOAD_TARGET_DATA;
} else if (download_url.SchemeIsFile()) {
state = DOWNLOAD_TARGET_FILE;
} else if (download_url.SchemeIsFileSystem()) {
state = DOWNLOAD_TARGET_FILESYSTEM;
} else if (download_url.SchemeIs(url::kFtpScheme)) {
state = DOWNLOAD_TARGET_FTP;
}
return state;
}
void RecordDownloadValidationMetrics(DownloadMetricsCallsite callsite,
DownloadConnectionSecurity state,
DownloadContent file_type) {
base::UmaHistogramEnumeration(
GetDownloadValidationMetricName(callsite, state), file_type);
}
void RecordDownloadHttpResponseCode(int response_code,
bool is_background_mode) {
int status_code = net::HttpUtil::MapStatusCodeForHistogram(response_code);
std::vector<int> status_codes = net::HttpUtil::GetStatusCodesForHistogram();
UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.HttpResponseCode", status_code,
status_codes);
if (is_background_mode) {
UMA_HISTOGRAM_CUSTOM_ENUMERATION(
"Download.HttpResponseCode.BackgroundDownload", status_code,
status_codes);
}
}
void RecordInputStreamReadError(MojoResult mojo_result) {
InputStreamReadError error = InputStreamReadError::kUnknown;
switch (mojo_result) {
case MOJO_RESULT_INVALID_ARGUMENT:
error = InputStreamReadError::kInvalidArgument;
break;
case MOJO_RESULT_OUT_OF_RANGE:
error = InputStreamReadError::kOutOfRange;
break;
case MOJO_RESULT_BUSY:
error = InputStreamReadError::kBusy;
break;
default:
NOTREACHED();
}
base::UmaHistogramEnumeration("Download.InputStreamReadError", error);
}
#if BUILDFLAG(IS_ANDROID)
void RecordDuplicatePdfDownloadTriggered(bool open_inline) {
base::UmaHistogramBoolean("Download.DuplicatePdfDownloadTriggered",
open_inline);
}
#endif // BUILDFLAG(IS_ANDROID)
} // namespace download