chromium/chromeos/ui/base/file_icon_util.cc

// 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/not_fatal_until.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/chromeos/styles/cros_styles.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.
  if (chromeos::features::IsJellyrollEnabled()) {
    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);
    }
  } else {
    switch (color_id) {
      case ColorId::kBlue:
        return cros_styles::ResolveColor(cros_styles::ColorName::kIconColorBlue,
                                         dark_background,
                                         /*use_debug_colors=*/false);
      case ColorId::kGreen:
        return cros_styles::ResolveColor(
            cros_styles::ColorName::kIconColorGreen, dark_background,
            /*use_debug_colors=*/false);
      case ColorId::kGrey:
        return cros_styles::ResolveColor(
            cros_styles::ColorName::kIconColorPrimary, dark_background,
            /*use_debug_colors=*/false);
      case ColorId::kRed:
        return cros_styles::ResolveColor(cros_styles::ColorName::kIconColorRed,
                                         dark_background,
                                         /*use_debug_colors=*/false);
      case ColorId::kYellow:
        return cros_styles::ResolveColor(
            cros_styles::ColorName::kIconColorYellow, dark_background,
            /*use_debug_colors=*/false);
      case ColorId::kFiletypePpt:
        return SkColorSetRGB(0xFF, 0x76, 0x37);
      case ColorId::kFiletypeGsite:
      case ColorId::kFiletypeSites:
        return dark_background ? SkColorSetRGB(0xBC, 0x9E, 0xFF)
                               : SkColorSetRGB(0x79, 0x6E, 0xEE);
      case ColorId::kFiletypeGform:
      case ColorId::kFiletypeExcel:
        // Before refresh23, they are mapped to kGreen.
        return cros_styles::ResolveColor(
            cros_styles::ColorName::kIconColorGreen, dark_background,
            /*use_debug_colors=*/false);
      case ColorId::kFiletypeWord:
        // Before refresh23, it's mapped to kBlue.
        return cros_styles::ResolveColor(cros_styles::ColorName::kIconColorBlue,
                                         dark_background,
                                         /*use_debug_colors=*/false);
    }
  }
}

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(), base::NotFatalUntil::M130);

  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},
                                {"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(), base::NotFatalUntil::M130);

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

}  // namespace chromeos