910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ui/base/file_icon_util.h"

#include <optional>
#include <string>
#include <string_view>
#include <utility>

#include "base/containers/fixed_flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ref.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/color/color_provider.h"
#include "ui/color/color_provider_manager.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/native_theme/native_theme.h"

namespace chromeos {
namespace {

constexpr int kIconDefaultDipSize = 20;

enum class ColorId {
  kBlue,
  kGreen,
  kGrey,
  kRed,
  kYellow,
  kFiletypeGsite,
  kFiletypePpt,
  kFiletypeSites,
  kFiletypeGform,
  kFiletypeExcel,
  kFiletypeWord,
};

struct IconParams {
  const raw_ref<const gfx::VectorIcon> icon;
  ColorId color_id;
};

// TODO(b/280519843): Return ui::ColorId instead of SkColor so we don't need to
// get ColorProvider from the util function.
SkColor ResolveColor(ColorId color_id, bool dark_background) {
  // Changes to this should be reflected in
  // ui/file_manager/file_manager/foreground/css/file_types.css.
  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
  auto* color_provider = ui::ColorProviderManager::Get().GetColorProviderFor(
      native_theme->GetColorProviderKey(nullptr));
  switch (color_id) {
    case ColorId::kBlue:
      return color_provider->GetColor(cros_tokens::kCrosSysProgress);
    case ColorId::kGreen:
      return color_provider->GetColor(cros_tokens::kCrosSysPositive);
    case ColorId::kGrey:
      return color_provider->GetColor(cros_tokens::kCrosSysOnSurface);
    case ColorId::kRed:
      return color_provider->GetColor(cros_tokens::kCrosSysError);
    case ColorId::kYellow:
      return color_provider->GetColor(cros_tokens::kCrosSysWarning);
    case ColorId::kFiletypePpt:
      return color_provider->GetColor(cros_tokens::kCrosSysFileMsPpt);
    case ColorId::kFiletypeGsite:
    case ColorId::kFiletypeSites:
      return color_provider->GetColor(cros_tokens::kCrosSysFileSite);
    case ColorId::kFiletypeGform:
      return color_provider->GetColor(cros_tokens::kCrosSysFileForm);
    case ColorId::kFiletypeExcel:
      return color_provider->GetColor(cros_tokens::kCrosSysFileMsExcel);
    case ColorId::kFiletypeWord:
      return color_provider->GetColor(cros_tokens::kCrosSysFileMsWord);
  }
}

const std::map<IconType, IconParams>& GetIconTypeToIconParamsMap() {
  // Changes to this map should be reflected in
  // ui/file_manager/file_manager/common/js/file_type.js.
  static const base::NoDestructor<std::map<IconType, IconParams>>
      icon_type_to_icon_params(
          {{IconType::kArchive,
            IconParams{raw_ref(kFiletypeArchiveIcon), ColorId::kGrey}},
           {IconType::kAudio,
            IconParams{raw_ref(kFiletypeAudioIcon), ColorId::kRed}},
           {IconType::kChart,
            IconParams{raw_ref(kFiletypeChartIcon), ColorId::kGreen}},
           {IconType::kDrive,
            IconParams{raw_ref(kFiletypeTeamDriveIcon), ColorId::kGrey}},
           {IconType::kExcel,
            IconParams{raw_ref(kFiletypeExcelIcon), ColorId::kFiletypeExcel}},
           {IconType::kFolder,
            IconParams{raw_ref(kFiletypeFolderIcon), ColorId::kGrey}},
           {IconType::kFolderShared,
            IconParams{raw_ref(kFiletypeSharedIcon), ColorId::kGrey}},
           {IconType::kGdoc,
            IconParams{raw_ref(kFiletypeGdocIcon), ColorId::kBlue}},
           {IconType::kGdraw,
            IconParams{raw_ref(kFiletypeGdrawIcon), ColorId::kRed}},
           {IconType::kGeneric,
            IconParams{raw_ref(kFiletypeGenericIcon), ColorId::kGrey}},
           {IconType::kGform,
            IconParams{raw_ref(kFiletypeGformIcon), ColorId::kFiletypeGform}},
           {IconType::kGmap,
            IconParams{raw_ref(kFiletypeGmapIcon), ColorId::kRed}},
           {IconType::kGsheet,
            IconParams{raw_ref(kFiletypeGsheetIcon), ColorId::kGreen}},
           {IconType::kGsite,
            IconParams{raw_ref(kFiletypeGsiteIcon), ColorId::kFiletypeGsite}},
           {IconType::kGmaillayout,
            IconParams{raw_ref(kFiletypeGmaillayoutIcon), ColorId::kRed}},
           {IconType::kGslide,
            IconParams{raw_ref(kFiletypeGslidesIcon), ColorId::kYellow}},
           {IconType::kGtable,
            IconParams{raw_ref(kFiletypeGtableIcon), ColorId::kGreen}},
           {IconType::kImage,
            IconParams{raw_ref(kFiletypeImageIcon), ColorId::kRed}},
           {IconType::kLinux,
            IconParams{raw_ref(kFiletypeLinuxIcon), ColorId::kGrey}},
           {IconType::kPdf,
            IconParams{raw_ref(kFiletypePdfIcon), ColorId::kRed}},
           {IconType::kPpt,
            IconParams{raw_ref(kFiletypePptIcon), ColorId::kFiletypePpt}},
           {IconType::kScript,
            IconParams{raw_ref(kFiletypeScriptIcon), ColorId::kBlue}},
           {IconType::kSites,
            IconParams{raw_ref(kFiletypeSitesIcon), ColorId::kFiletypeSites}},
           {IconType::kTini,
            IconParams{raw_ref(kFiletypeTiniIcon), ColorId::kBlue}},
           {IconType::kVideo,
            IconParams{raw_ref(kFiletypeVideoIcon), ColorId::kRed}},
           {IconType::kWord,
            IconParams{raw_ref(kFiletypeWordIcon), ColorId::kFiletypeWord}}});
  return *icon_type_to_icon_params;
}

const IconParams& GetIconParamsFromIconType(IconType icon) {
  const auto& icon_type_to_icon_params = GetIconTypeToIconParamsMap();
  const auto& it = icon_type_to_icon_params.find(icon);
  CHECK(it != icon_type_to_icon_params.end());

  return it->second;
}

gfx::ImageSkia GetVectorIconFromIconType(IconType icon,
                                         bool dark_background,
                                         std::optional<int> dip_size) {
  const IconParams& params = GetIconParamsFromIconType(icon);
  const gfx::IconDescription description(
      *params.icon, dip_size.value_or(kIconDefaultDipSize),
      ResolveColor(params.color_id, dark_background));

  return gfx::CreateVectorIcon(description);
}

}  // namespace

namespace internal {

IconType GetIconTypeForPath(const base::FilePath& filepath) {
  // Changes to this map should be reflected in
  // ui/file_manager/base/gn/file_types.json5
  static const auto extension_to_icon =
      base::MakeFixedFlatMap<std::string_view, IconType>({
          // Image
          {".JPEG", IconType::kImage},
          {".JPG", IconType::kImage},
          {".BMP", IconType::kImage},
          {".GIF", IconType::kImage},
          {".ICO", IconType::kImage},
          {".PNG", IconType::kImage},
          {".WEBP", IconType::kImage},
          {".TIFF", IconType::kImage},
          {".TIF", IconType::kImage},
          {".SVG", IconType::kImage},

          // Raw
          {".ARW", IconType::kImage},
          {".CR2", IconType::kImage},
          {".DNG", IconType::kImage},
          {".NEF", IconType::kImage},
          {".NRW", IconType::kImage},
          {".ORF", IconType::kImage},
          {".RAF", IconType::kImage},
          {".RW2", IconType::kImage},

          // Video
          {".3GP", IconType::kVideo},
          {".3GPP", IconType::kVideo},
          {".AVI", IconType::kVideo},
          {".MOV", IconType::kVideo},
          {".MKV", IconType::kVideo},
          {".MP4", IconType::kVideo},
          {".M4V", IconType::kVideo},
          {".MPG", IconType::kVideo},
          {".MPEG", IconType::kVideo},
          {".MPG4", IconType::kVideo},
          {".MPEG4", IconType::kVideo},
          {".OGM", IconType::kVideo},
          {".OGV", IconType::kVideo},
          {".OGX", IconType::kVideo},
          {".WEBM", IconType::kVideo},

          // Audio
          {".AMR", IconType::kAudio},
          {".FLAC", IconType::kAudio},
          {".MP3", IconType::kAudio},
          {".M4A", IconType::kAudio},
          {".OGA", IconType::kAudio},
          {".OGG", IconType::kAudio},
          {".WAV", IconType::kAudio},

          // Text
          {".TXT", IconType::kGeneric},

          // Archive
          {".7Z", IconType::kArchive},
          {".BZ", IconType::kArchive},
          {".BZ2", IconType::kArchive},
          {".CRX", IconType::kArchive},
          {".GZ", IconType::kArchive},
          {".ISO", IconType::kArchive},
          {".LZ", IconType::kArchive},
          {".LZMA", IconType::kArchive},
          {".LZO", IconType::kArchive},
          {".RAR", IconType::kArchive},
          {".TAR", IconType::kArchive},
          {".TAZ", IconType::kArchive},
          {".TB2", IconType::kArchive},
          {".TBZ", IconType::kArchive},
          {".TBZ2", IconType::kArchive},
          {".TGZ", IconType::kArchive},
          {".TLZ", IconType::kArchive},
          {".TLZMA", IconType::kArchive},
          {".TXZ", IconType::kArchive},
          {".TZ", IconType::kArchive},
          {".TZ2", IconType::kArchive},
          {".TZST", IconType::kArchive},
          {".XZ", IconType::kArchive},
          {".Z", IconType::kArchive},
          {".ZIP", IconType::kArchive},

          // Hosted doc
          {".GDOC", IconType::kGdoc},
          {".GSHEET", IconType::kGsheet},
          {".GSLIDES", IconType::kGslide},
          {".GDRAW", IconType::kGdraw},
          {".GTABLE", IconType::kGtable},
          {".GLINK", IconType::kGeneric},
          {".GFORM", IconType::kGform},
          {".GMAPS", IconType::kGmap},
          {".GSITE", IconType::kGsite},
          {".GMAILLAYOUT", IconType::kGmaillayout},

          // Other
          {".PDF", IconType::kPdf},
          {".HTM", IconType::kGeneric},
          {".HTML", IconType::kGeneric},
          {".MHT", IconType::kGeneric},
          {".MHTM", IconType::kGeneric},
          {".MHTML", IconType::kGeneric},
          {".SHTML", IconType::kGeneric},
          {".XHT", IconType::kGeneric},
          {".XHTM", IconType::kGeneric},
          {".XHTML", IconType::kGeneric},
          {".DOC", IconType::kWord},
          {".DOCX", IconType::kWord},
          {".PPT", IconType::kPpt},
          {".PPTX", IconType::kPpt},
          {".XLS", IconType::kExcel},
          {".XLSM", IconType::kExcel},
          {".XLSX", IconType::kExcel},
          {".TINI", IconType::kTini},
      });

  const auto it =
      extension_to_icon.find(base::ToUpperASCII(filepath.Extension()));
  if (it != extension_to_icon.end()) {
    return it->second;
  } else {
    return IconType::kGeneric;
  }
}

IconType GetIconTypeFromString(const std::string& icon_type_string) {
  static const base::NoDestructor<std::map<std::string, IconType>>
      type_string_to_icon_type(
          {{"archive", IconType::kArchive},
           {"audio", IconType::kAudio},
           {"chart", IconType::kChart},
           {"excel", IconType::kExcel},
           {"drive", IconType::kDrive},
           {"folder", IconType::kFolder},
           {"gdoc", IconType::kGdoc},
           {"gdraw", IconType::kGdraw},
           {"generic", IconType::kGeneric},
           {"gform", IconType::kGform},
           {"gmap", IconType::kGmap},
           {"gsheet", IconType::kGsheet},
           {"gsite", IconType::kGsite},
           {"gmaillayout", IconType::kGmaillayout},
           {"gslides", IconType::kGslide},
           {"gtable", IconType::kGtable},
           {"image", IconType::kImage},
           {"linux", IconType::kLinux},
           {"pdf", IconType::kPdf},
           {"ppt", IconType::kPpt},
           {"script", IconType::kScript},
           {"shared", IconType::kFolderShared},
           {"sites", IconType::kSites},
           // TODO(crbug.com/345312503): would img.zst work here?
           {"tini", IconType::kTini},
           {"video", IconType::kVideo},
           {"word", IconType::kWord}});

  const auto& icon_it = type_string_to_icon_type->find(icon_type_string);
  if (icon_it != type_string_to_icon_type->end())
    return icon_it->second;
  return IconType::kGeneric;
}

}  // namespace internal

const gfx::VectorIcon& GetIconForPath(const base::FilePath& filepath) {
  return *GetIconParamsFromIconType(internal::GetIconTypeForPath(filepath))
              .icon;
}

gfx::ImageSkia GetIconForPath(const base::FilePath& filepath,
                              bool dark_background,
                              std::optional<int> dip_size) {
  return GetVectorIconFromIconType(internal::GetIconTypeForPath(filepath),
                                   dark_background, dip_size);
}

gfx::ImageSkia GetChipIconForPath(const base::FilePath& filepath,
                                  bool dark_background) {
  return GetIconForPath(filepath, dark_background);
}

const gfx::VectorIcon& GetIconFromType(const std::string& icon_type) {
  return *GetIconParamsFromIconType(internal::GetIconTypeFromString(icon_type))
              .icon;
}

gfx::ImageSkia GetIconFromType(const std::string& icon_type,
                               bool dark_background) {
  return GetVectorIconFromIconType(internal::GetIconTypeFromString(icon_type),
                                   dark_background, std::nullopt);
}

gfx::ImageSkia GetIconFromType(IconType icon_type,
                               bool dark_background,
                               std::optional<int> dip_size) {
  return GetVectorIconFromIconType(icon_type, dark_background, dip_size);
}

SkColor GetIconColorForPath(const base::FilePath& filepath,
                            bool dark_background) {
  const auto& icon_type = internal::GetIconTypeForPath(filepath);
  const auto& icon_type_to_icon_params = GetIconTypeToIconParamsMap();
  const auto& it = icon_type_to_icon_params.find(icon_type);
  CHECK(it != icon_type_to_icon_params.end());

  return ResolveColor(it->second.color_id, dark_background);
}

}  // namespace chromeos