// 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/strcat.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);
}

}  // 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 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
