chromium/chrome/browser/apps/app_service/app_icon/app_icon_loader.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/apps/app_service/app_icon/app_icon_loader.h"

#include <memory>
#include <string_view>
#include <utility>

#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h"
#include "chrome/browser/apps/app_service/app_icon/dip_px_util.h"
#include "chrome/browser/apps/icon_standardizer.h"
#include "chrome/browser/extensions/chrome_app_icon.h"
#include "chrome/browser/extensions/chrome_app_icon_loader.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon_base/favicon_types.h"
#include "components/services/app_service/public/cpp/icon_types.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/component_extension_resource_manager.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/image_loader.h"
#include "extensions/common/constants.h"
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/grit/extensions_browser_resources.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/geometry/size.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/image/image_skia_rep.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/app_list/md_icon_normalizer.h"
#include "chrome/browser/ash/arc/icon_decode_request.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "chrome/browser/icon_transcoder/svg_icon_transcoder.h"
#include "chrome/grit/chrome_unscaled_resources.h"
#endif

namespace {

std::string ReadFileAsCompressedString(const base::FilePath path) {}

std::vector<uint8_t> ReadFileAsCompressedData(const base::FilePath path) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
apps::IconValuePtr ReadAdaptiveIconFiles(apps::AdaptiveIconPaths icon_paths) {
  TRACE_EVENT0("ui", "ReadAdaptiveIconFiles");
  base::AssertLongCPUWorkAllowed();

  auto iv = std::make_unique<apps::IconValue>();
  iv->icon_type = apps::IconType::kCompressed;

  // Save the raw icon for the non-adaptive icon, or the generated standard icon
  // done by the ARC side for the adaptive icon if missing the foreground and
  // background icon data.
  iv->compressed = ReadFileAsCompressedData(icon_paths.icon_path);

  // For the adaptive icon, save the foreground and background icon data.
  iv->foreground_icon_png_data =
      ReadFileAsCompressedData(icon_paths.foreground_icon_path);
  iv->background_icon_png_data =
      ReadFileAsCompressedData(icon_paths.background_icon_path);

  return iv;
}

apps::IconValuePtr ResizeAndCompressIconOnBackgroundThread(
    apps::IconValuePtr iv,
    float icon_scale,
    int icon_size_in_px,
    SkBitmap bitmap) {
  TRACE_EVENT0("ui", "ResizeAndCompressIconOnBackgroundThread");
  base::AssertLongCPUWorkAllowed();

  // Resize `bitmap` to match `icon_size_in_px`.
  if (bitmap.width() != icon_size_in_px) {
    bitmap = skia::ImageOperations::Resize(
        bitmap, skia::ImageOperations::RESIZE_LANCZOS3, icon_size_in_px,
        icon_size_in_px);
  }

  std::vector<uint8_t> encoded_image = apps::EncodeImageToPngBytes(
      apps::SkBitmapToImageSkia(bitmap, icon_scale), icon_scale);

  iv->compressed = std::move(encoded_image);
  return iv;
}

void ResizeAndCompressIcon(apps::IconValuePtr iv,
                           float icon_scale,
                           int icon_size_in_px,
                           apps::LoadIconCallback result_callback,
                           const SkBitmap& bitmap) {
  TRACE_EVENT0("ui", "ResizeAndCompressIcon");
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  if (bitmap.drawsNothing()) {
    // If decoding the compressed data failed, `iv` may still contain adaptive
    // icon data which can be used to finish the request successfully.
    std::move(result_callback).Run(std::move(iv));
    return;
  }

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::BindOnce(&ResizeAndCompressIconOnBackgroundThread, std::move(iv),
                     icon_scale, icon_size_in_px, bitmap),
      std::move(result_callback));
}

void DecodeAndResizeCompressedIcon(float icon_scale,
                                   int icon_size_in_px,
                                   apps::LoadIconCallback result_callback,
                                   apps::IconValuePtr iv) {
  TRACE_EVENT0("ui", "DecodeAndResizeCompressedIcon");
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  if (iv->compressed.empty()) {
    // If there's no compressed data, we don't need to decode and resize it.
    // `iv` may still contain adaptive icon data which can be used to finish the
    // request successfully.
    std::move(result_callback).Run(std::move(iv));
    return;
  }

  // We need to decompress and resize the compressed icon. `iv` still contains
  // adaptive icon data, so we keep that around to fill back in with the resized
  // icon data.
  std::vector<uint8_t> compressed_data = std::move(iv->compressed);

  apps::CompressedDataToSkBitmap(
      compressed_data,
      base::BindOnce(&ResizeAndCompressIcon, std::move(iv), icon_scale,
                     icon_size_in_px, std::move(result_callback)));
}

// Reads the raw icon data for the foreground and the background icon file. For
// the icon data from `icon_paths.icon_path`, we might resize it if the icon
// size doesn't match, because it could be shown as the compressed icon
// directly, without calling the adaptive icon Composite function to chop and
// resize.
void ReadFilesAndMaybeResize(apps::AdaptiveIconPaths icon_paths,
                             float icon_scale,
                             int icon_size_in_px,
                             apps::LoadIconCallback callback) {
  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::BindOnce(&ReadAdaptiveIconFiles, std::move(icon_paths)),
      base::BindOnce(&DecodeAndResizeCompressedIcon, icon_scale,
                     icon_size_in_px, std::move(callback)));
}

#endif

// Returns a callback that converts a gfx::Image to an ImageSkia.
base::OnceCallback<void(const gfx::Image&)> ImageToImageSkia(
    base::OnceCallback<void(gfx::ImageSkia)> callback) {}

base::OnceCallback<void(const favicon_base::FaviconRawBitmapResult&)>
FaviconResultToImageSkia(base::OnceCallback<void(gfx::ImageSkia)> callback,
                         float icon_scale) {}

std::optional<web_app::IconPurpose> GetIconPurpose(
    const std::string& web_app_id,
    const web_app::WebAppIconManager& icon_manager,
    int size_hint_in_dip) {}

apps::IconValuePtr ApplyEffects(apps::IconEffects icon_effects,
                                int size_hint_in_dip,
                                apps::IconValuePtr iv,
                                gfx::ImageSkia mask_image) {}

}  // namespace

namespace apps {

bool AdaptiveIconPaths::IsEmpty() {}

AppIconLoader::AppIconLoader(Profile* profile,
                             std::optional<std::string> app_id,
                             IconType icon_type,
                             int size_hint_in_dip,
                             bool is_placeholder_icon,
                             apps::IconEffects icon_effects,
                             int fallback_icon_resource,
                             LoadIconCallback callback)
    :{}

AppIconLoader::AppIconLoader(
    Profile* profile,
    std::optional<std::string> app_id,
    IconType icon_type,
    int size_hint_in_dip,
    bool is_placeholder_icon,
    apps::IconEffects icon_effects,
    int fallback_icon_resource,
    base::OnceCallback<void(LoadIconCallback)> fallback,
    LoadIconCallback callback)
    :{}

AppIconLoader::AppIconLoader(Profile* profile,
                             int size_hint_in_dip,
                             LoadIconCallback callback)
    :{}

AppIconLoader::AppIconLoader(
    int size_hint_in_dip,
    base::OnceCallback<void(const gfx::ImageSkia& icon)> callback)
    :{}

AppIconLoader::AppIconLoader(
    base::OnceCallback<void(const std::vector<gfx::ImageSkia>& icons)> callback)
    :{}

AppIconLoader::AppIconLoader(int size_hint_in_dip, LoadIconCallback callback)
    :{}

AppIconLoader::~AppIconLoader() {}

void AppIconLoader::ApplyIconEffects(IconEffects icon_effects,
                                     const std::optional<std::string>& app_id,
                                     IconValuePtr iv) {}

void AppIconLoader::ApplyBadges(IconEffects icon_effects,
                                const std::optional<std::string>& app_id,
                                IconValuePtr iv) {}

void AppIconLoader::LoadWebAppIcon(const std::string& web_app_id,
                                   const GURL& launch_url,
                                   web_app::WebAppIconManager& icon_manager) {}

void AppIconLoader::LoadExtensionIcon(const extensions::Extension* extension) {}

void AppIconLoader::LoadCompressedIconFromFile(const base::FilePath& path) {}

void AppIconLoader::LoadIconFromCompressedData(
    const std::string& compressed_icon_data) {}

void AppIconLoader::LoadIconFromResource(int icon_resource) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
void AppIconLoader::LoadArcIconPngData(
    const std::vector<uint8_t>& icon_png_data) {
  TRACE_EVENT0("ui", "AppIconLoader::LoadArcIconPngData");
  arc_icon_decode_request_ = CreateArcIconDecodeRequest(
      base::BindOnce(&AppIconLoader::ApplyBackgroundAndMask,
                     base::WrapRefCounted(this)),
      icon_png_data);
}

void AppIconLoader::LoadCompositeImages(
    const std::vector<uint8_t>& foreground_data,
    const std::vector<uint8_t>& background_data) {
  TRACE_EVENT0("ui", "AppIconLoader::LoadCompositeImages");
  arc_foreground_icon_decode_request_ = CreateArcIconDecodeRequest(
      base::BindOnce(&AppIconLoader::CompositeImagesAndApplyMask,
                     base::WrapRefCounted(this), true /* is_foreground */),
      foreground_data);

  arc_background_icon_decode_request_ = CreateArcIconDecodeRequest(
      base::BindOnce(&AppIconLoader::CompositeImagesAndApplyMask,
                     base::WrapRefCounted(this), false /* is_foreground */),
      background_data);
}

void AppIconLoader::LoadArcActivityIcons(
    const std::vector<arc::mojom::ActivityIconPtr>& icons) {
  TRACE_EVENT0("ui", "AppIconLoader::LoadArcActivityIcons");
  arc_activity_icons_.resize(icons.size());
  DCHECK_EQ(0U, count_);
  for (size_t i = 0; i < icons.size(); i++) {
    if (!icons[i] || !icons[i]->icon_png_data) {
      ++count_;
      continue;
    }

    constexpr size_t kMaxIconSizeInPx = 200;
    if (icons[i]->width > kMaxIconSizeInPx ||
        icons[i]->height > kMaxIconSizeInPx || icons[i]->width == 0 ||
        icons[i]->height == 0) {
      ++count_;
      continue;
    }

    apps::ArcRawIconPngDataToImageSkia(
        std::move(icons[i]->icon_png_data), icons[i]->width,
        base::BindOnce(&AppIconLoader::OnArcActivityIconLoaded,
                       base::WrapRefCounted(this), &arc_activity_icons_[i]));
  }

  if (count_ == arc_activity_icons_.size() && !image_skia_callback_.is_null()) {
    std::move(arc_activity_icons_callback_).Run(arc_activity_icons_);
  }
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_CHROMEOS)
void AppIconLoader::GetWebAppCompressedIconData(
    const std::string& web_app_id,
    ui::ResourceScaleFactor scale_factor,
    web_app::WebAppIconManager& icon_manager) {
  TRACE_EVENT0("ui", "AppIconLoader::GetWebAppCompressedIconData");
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  std::optional<web_app::IconPurpose> icon_purpose_to_read =
      GetIconPurpose(web_app_id, icon_manager, size_hint_in_dip_);

  if (!icon_purpose_to_read.has_value() || icon_type_ == IconType::kUnknown) {
    MaybeLoadFallbackOrCompleteEmpty();
    return;
  }

  icon_scale_ = ui::GetScaleForResourceScaleFactor(scale_factor);
  icon_size_in_px_ =
      apps_util::ConvertDipToPxForScale(size_hint_in_dip_, icon_scale_);

  auto size_and_purpose = icon_manager.FindIconMatchBigger(
      web_app_id, {*icon_purpose_to_read}, icon_size_in_px_);
  DCHECK(size_and_purpose.has_value());

  std::vector<int> icon_pixel_sizes;
  icon_pixel_sizes.emplace_back(size_and_purpose->size_px);
  icon_manager.ReadIcons(
      web_app_id, *icon_purpose_to_read, icon_pixel_sizes,
      base::BindOnce(&AppIconLoader::OnReadWebAppForCompressedIconData,
                     base::WrapRefCounted(this),
                     *icon_purpose_to_read == web_app::IconPurpose::MASKABLE));
}

void AppIconLoader::GetChromeAppCompressedIconData(
    const extensions::Extension* extension,
    ui::ResourceScaleFactor scale_factor) {
  TRACE_EVENT0("ui", "AppIconLoader::GetChromeAppCompressedIconData");
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  CHECK(profile_);

  if (!extension || icon_type_ == IconType::kUnknown) {
    MaybeLoadFallbackOrCompleteEmpty();
    return;
  }

  icon_scale_ = ui::GetScaleForResourceScaleFactor(scale_factor);
  extensions::ImageLoader::Get(profile_)->LoadImageAtEveryScaleFactorAsync(
      extension, gfx::Size(size_hint_in_dip_, size_hint_in_dip_),
      ImageToImageSkia(
          base::BindOnce(&AppIconLoader::OnReadChromeAppForCompressedIconData,
                         base::WrapRefCounted(this))));
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_CHROMEOS_ASH)
void AppIconLoader::GetArcAppCompressedIconData(
    const std::string& app_id,
    ArcAppListPrefs* arc_prefs,
    ui::ResourceScaleFactor scale_factor) {
  TRACE_EVENT0("ui", "AppIconLoader::GetArcAppCompressedIconData");
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(arc_prefs);

  icon_scale_ = ui::GetScaleForResourceScaleFactor(scale_factor);
  icon_size_in_px_ =
      apps_util::ConvertDipToPxForScale(size_hint_in_dip_, icon_scale_);

  AdaptiveIconPaths app_paths;
  const ArcAppIconDescriptor descriptor(size_hint_in_dip_, scale_factor);
  if (arc_prefs->IsDefault(app_id)) {
    // Get the icon paths for the default apps. If we can't fetch the raw icon
    // data from the ARC side, the icon paths for the default apps are used to
    // get the icon data.
    app_paths.icon_path =
        arc_prefs->MaybeGetIconPathForDefaultApp(app_id, descriptor);
    app_paths.foreground_icon_path =
        arc_prefs->MaybeGetForegroundIconPathForDefaultApp(app_id, descriptor);
    app_paths.background_icon_path =
        arc_prefs->MaybeGetBackgroundIconPathForDefaultApp(app_id, descriptor);
  } else {
    // For the migration scenario, as ARC may take some time to startup after
    // the user login, fetching the raw icon files from the ARC VM could fail.
    // So try to fetch the raw icon files from the ARC on-disk cache to
    // migrate the icon files from the ARC directory to the AppService
    // directory.
    app_paths.icon_path = arc_prefs->GetIconPath(app_id, descriptor);
    app_paths.foreground_icon_path =
        arc_prefs->GetForegroundIconPath(app_id, descriptor);
    app_paths.background_icon_path =
        arc_prefs->GetBackgroundIconPath(app_id, descriptor);
  }

  arc_prefs->RequestRawIconData(
      app_id, ArcAppIconDescriptor(size_hint_in_dip_, scale_factor),
      base::BindOnce(&AppIconLoader::OnGetArcAppCompressedIconData,
                     base::WrapRefCounted(this), std::move(app_paths)));
}

void AppIconLoader::GetGuestOSAppCompressedIconData(
    const std::string& app_id,
    ui::ResourceScaleFactor scale_factor) {
  TRACE_EVENT0("ui", "AppIconLoader::GetGuestOSAppCompressedIconData");
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  CHECK(profile_);

  auto* registry =
      guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile_);
  if (!registry) {
    std::move(callback_).Run(std::make_unique<apps::IconValue>());
    return;
  }

  icon_scale_ = ui::GetScaleForResourceScaleFactor(scale_factor);
  icon_size_in_px_ =
      apps_util::ConvertDipToPxForScale(size_hint_in_dip_, icon_scale_);

  // GuestOS may have the png icon file for the primary scale factor only.
  base::FilePath png_path = registry->GetIconPath(
      app_id, apps_util::GetPrimaryDisplayUIScaleFactor());
  base::FilePath svg_path = registry->GetIconPath(app_id, ui::kScaleFactorNone);

  registry->RequestIcon(
      app_id, scale_factor,
      base::BindOnce(&AppIconLoader::OnGetGuestOSAppCompressedIconData,
                     base::WrapRefCounted(this), png_path, svg_path));
}

void AppIconLoader::OnGetArcAppCompressedIconData(
    AdaptiveIconPaths app_icon_paths,
    arc::mojom::RawIconPngDataPtr icon) {
  TRACE_EVENT0("ui", "AppIconLoader::OnGetArcAppCompressedIconData");
  auto iv = std::make_unique<IconValue>();
  if (!icon || !icon->icon_png_data.has_value()) {
    // If we can't find `app_icon_paths`, return the empty icon value.
    if (app_icon_paths.IsEmpty()) {
      std::move(callback_).Run(std::move(iv));
      return;
    }

    // Get the raw icon data from `app_icon_paths`.
    ReadFilesAndMaybeResize(std::move(app_icon_paths), icon_scale_,
                            icon_size_in_px_, std::move(callback_));
    return;
  }

  iv->icon_type = IconType::kCompressed;

  // Save the raw icon for the non-adaptive icon, or the generated standard icon
  // done by the ARC side for the adaptive icon if missing the foreground and
  // background icon data.
  iv->compressed = std::move(icon->icon_png_data.value());
  if (icon->is_adaptive_icon) {
    // For the adaptive icon, save the foreground and background icon data.
    iv->foreground_icon_png_data =
        std::move(icon->foreground_icon_png_data.value());
    iv->background_icon_png_data =
        std::move(icon->background_icon_png_data.value());
  }
  std::move(callback_).Run(std::move(iv));
}

void AppIconLoader::OnGetGuestOSAppCompressedIconData(base::FilePath png_path,
                                                      base::FilePath svg_path,
                                                      std::string icon_data) {
  TRACE_EVENT0("ui", "AppIconLoader::OnGetGuestOSAppCompressedIconData");
  if (!icon_data.empty()) {
    std::vector<uint8_t> data(icon_data.begin(), icon_data.end());
    CompressedDataToSkBitmap(
        data,
        base::BindOnce(&AppIconLoader::OnGetCompressedIconDataWithSkBitmap,
                       base::WrapRefCounted(this), /*is_maskable_icon=*/false));
    return;
  }

  // For the migration scenario, when migrate from the GuestOS loading icon to
  // the AppService unified icon loading, as the GuestOS can't startup after the
  // user login, fetching the raw icon files from the GuestOS VM could fail.
  // So try to fetch the raw icon files from the GuestOS on-disk cache to
  // migrate the icon files from the GuestOS directory to the AppService
  // directory.
  if (!png_path.empty()) {
    // Set `png_path` as null to ensure no infinite loops in
    // OnGetGuestOSAppCompressedIconData.
    base::ThreadPool::PostTaskAndReplyWithResult(
        FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
        base::BindOnce(&ReadFileAsCompressedString, png_path),
        base::BindOnce(&AppIconLoader::OnGetGuestOSAppCompressedIconData,
                       base::WrapRefCounted(this),
                       /*png_path=*/base::FilePath(), svg_path));
    return;
  }

  if (!svg_path.empty()) {
    TranscodeIconFromSvg(std::move(svg_path), std::move(png_path));
    return;
  }

  std::move(callback_).Run(std::make_unique<apps::IconValue>());
}

void AppIconLoader::TranscodeIconFromSvg(base::FilePath svg_path,
                                         base::FilePath png_path) {
  TRACE_EVENT0("ui", "AppIconLoader::TranscodeIconFromSvg");
  if (!profile_) {
    // Profile has been destroyed.
    return;
  }

  gfx::Size kPreferredSize = gfx::Size(128, 128);
  if (!svg_icon_transcoder_) {
    svg_icon_transcoder_ = std::make_unique<SvgIconTranscoder>(profile_);
  }
  // Set `png_path` and `svg_path` as null to ensure no infinite loops in
  // OnGetGuestOSAppCompressedIconData.
  svg_icon_transcoder_->Transcode(
      std::move(svg_path), std::move(png_path), kPreferredSize,
      base::BindOnce(&AppIconLoader::OnGetGuestOSAppCompressedIconData,
                     base::WrapRefCounted(this), /*png_path=*/base::FilePath(),
                     /*svg_path=*/base::FilePath()));
}

std::unique_ptr<arc::IconDecodeRequest>
AppIconLoader::CreateArcIconDecodeRequest(
    base::OnceCallback<void(const gfx::ImageSkia& icon)> callback,
    const std::vector<uint8_t>& icon_png_data) {
  TRACE_EVENT0("ui", "AppIconLoader::CreateArcIconDecodeRequest");
  std::unique_ptr<arc::IconDecodeRequest> arc_icon_decode_request =
      std::make_unique<arc::IconDecodeRequest>(std::move(callback),
                                               size_hint_in_dip_);
  arc_icon_decode_request->StartWithOptions(icon_png_data);
  return arc_icon_decode_request;
}

void AppIconLoader::ApplyBackgroundAndMask(const gfx::ImageSkia& image) {
  TRACE_EVENT0("ui", "AppIconLoader::ApplyBackgroundAndMask");
  std::move(image_skia_callback_)
      .Run(gfx::ImageSkiaOperations::CreateResizedImage(
          apps::ApplyBackgroundAndMask(image),
          skia::ImageOperations::RESIZE_LANCZOS3,
          gfx::Size(size_hint_in_dip_, size_hint_in_dip_)));
}

void AppIconLoader::CompositeImagesAndApplyMask(bool is_foreground,
                                                const gfx::ImageSkia& image) {
  TRACE_EVENT0("ui", "AppIconLoader::CompositeImagesAndApplyMask");
  if (is_foreground) {
    foreground_is_set_ = true;
    foreground_image_ = image;
  } else {
    background_is_set_ = true;
    background_image_ = image;
  }

  if (!foreground_is_set_ || !background_is_set_ ||
      image_skia_callback_.is_null()) {
    return;
  }

  if (foreground_image_.isNull() || background_image_.isNull()) {
    std::move(image_skia_callback_).Run(gfx::ImageSkia());
    return;
  }

  std::move(image_skia_callback_)
      .Run(gfx::ImageSkiaOperations::CreateResizedImage(
          apps::CompositeImagesAndApplyMask(foreground_image_,
                                            background_image_),
          skia::ImageOperations::RESIZE_BEST,
          gfx::Size(size_hint_in_dip_, size_hint_in_dip_)));
}

void AppIconLoader::OnArcActivityIconLoaded(gfx::ImageSkia* arc_activity_icon,
                                            const gfx::ImageSkia& icon) {
  TRACE_EVENT0("ui", "AppIconLoader::OnArcActivityIconLoaded");
  DCHECK(arc_activity_icon);
  ++count_;
  *arc_activity_icon = icon;
  arc_activity_icon->MakeThreadSafe();

  if (count_ == arc_activity_icons_.size() &&
      !arc_activity_icons_callback_.is_null()) {
    std::move(arc_activity_icons_callback_).Run(arc_activity_icons_);
  }
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

void AppIconLoader::MaybeApplyEffectsAndComplete(const gfx::ImageSkia image) {}

void AppIconLoader::CompleteWithCompressed(bool is_maskable_icon,
                                           std::vector<uint8_t> data) {}

void AppIconLoader::CompleteWithUncompressed(IconValuePtr iv) {}

void AppIconLoader::CompleteWithIconValue(IconValuePtr iv) {}

// Callback for reading uncompressed web app icons.
void AppIconLoader::OnReadWebAppIcon(std::map<int, SkBitmap> icon_bitmaps) {}

void AppIconLoader::OnReadWebAppForCompressedIconData(
    bool is_maskable_icon,
    std::map<int, SkBitmap> icon_bitmaps) {}

void AppIconLoader::OnGetCompressedIconDataWithSkBitmap(
    bool is_maskable_icon,
    const SkBitmap& bitmap) {}

void AppIconLoader::OnReadChromeAppForCompressedIconData(gfx::ImageSkia image) {}

void AppIconLoader::MaybeLoadFallbackOrCompleteEmpty() {}

void AppIconLoader::OnProfileWillBeDestroyed(Profile* profile) {}

}  // namespace apps