| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // 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_factory.h" |
| |
| #include <cmath> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/files/file_util.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/task/task_traits.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/image_loader.h" |
| #include "extensions/common/manifest_handlers/icons_handler.h" |
| #include "skia/ext/image_operations.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| |
| namespace { |
| |
| float GetDeviceScaleFactor() { |
| display::Screen* screen = display::Screen::GetScreen(); |
| if (!screen) { |
| return 1.0f; |
| } |
| return screen->GetPrimaryDisplay().device_scale_factor(); |
| } |
| |
| int ConvertDipToPx(int dip) { |
| return base::saturated_cast<int>( |
| std::floor(static_cast<float>(dip) * GetDeviceScaleFactor())); |
| } |
| |
| std::vector<uint8_t> ReadExtensionResource( |
| extensions::ExtensionResource ext_resource) { |
| std::string data; |
| base::ReadFileToString(ext_resource.GetFilePath(), &data); |
| return std::vector<uint8_t>(data.begin(), data.end()); |
| } |
| |
| // Runs |callback| passing an IconValuePtr with a compressed image. |
| void RunCallbackWithCompressedData( |
| apps::mojom::Publisher::LoadIconCallback callback, |
| std::vector<uint8_t> data) { |
| apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New(); |
| iv->icon_compression = data.empty() |
| ? apps::mojom::IconCompression::kUnknown |
| : apps::mojom::IconCompression::kCompressed; |
| iv->compressed = std::move(data); |
| std::move(callback).Run(std::move(iv)); |
| } |
| |
| // Runs |callback| passing an IconValuePtr with an uncompressed image. |
| void RunCallbackWithUncompressedImageSkia( |
| apps::mojom::Publisher::LoadIconCallback callback, |
| const gfx::ImageSkia image) { |
| apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New(); |
| iv->icon_compression = apps::mojom::IconCompression::kUncompressed; |
| iv->uncompressed = image; |
| std::move(callback).Run(std::move(iv)); |
| } |
| |
| // Runs |callback| passing an IconValuePtr with an uncompressed image. |
| void RunCallbackWithUncompressedImage( |
| apps::mojom::Publisher::LoadIconCallback callback, |
| const gfx::Image& image) { |
| RunCallbackWithUncompressedImageSkia(std::move(callback), |
| image.AsImageSkia()); |
| } |
| |
| } // namespace |
| |
| namespace apps { |
| |
| void LoadIconFromExtension(apps::mojom::IconCompression icon_compression, |
| int size_hint_in_dip, |
| apps::mojom::Publisher::LoadIconCallback callback, |
| content::BrowserContext* context, |
| const std::string& extension_id) { |
| int size_hint_in_px = ConvertDipToPx(size_hint_in_dip); |
| |
| const extensions::Extension* extension = |
| extensions::ExtensionSystem::Get(context) |
| ->extension_service() |
| ->GetInstalledExtension(extension_id); |
| if (extension) { |
| extensions::ExtensionResource ext_resource = |
| extensions::IconsInfo::GetIconResource(extension, size_hint_in_px, |
| ExtensionIconSet::MATCH_BIGGER); |
| |
| // TODO(crbug.com/826982): at some point near here, we should call into |
| // chrome/browser/extensions/chrome_app_icon.h code to get e.g. graying out |
| // disabled icons, or rounding off corners. It's not entirely trivial. The |
| // ChromeAppIcon object expects to be long-lived, calling back into the |
| // ChromeAppIconDelegate e.g. when the extension's underlying icon changes, |
| // or when an enabled/disabled state change means graying or un-graying an |
| // extension's icon. |
| // |
| // For example, in chrome/browser/ui/app_list, ExtensionAppItem implements |
| // ChromeAppIconDelegate, as the ExtensionAppItem is a long-lived object. |
| // It is a model, in the model-view-controller sense. |
| // |
| // In contrast, this API here (a Mojo IPC API) is a request-response IPC, |
| // with nothing long-lived enough to be the ChromeAppIconDelegate. |
| |
| switch (icon_compression) { |
| case apps::mojom::IconCompression::kUnknown: |
| break; |
| |
| case apps::mojom::IconCompression::kUncompressed: |
| extensions::ImageLoader::Get(context)->LoadImageAsync( |
| extension, std::move(ext_resource), |
| gfx::Size(size_hint_in_px, size_hint_in_px), |
| base::BindOnce(&RunCallbackWithUncompressedImage, |
| std::move(callback))); |
| return; |
| |
| case apps::mojom::IconCompression::kCompressed: |
| base::PostTaskWithTraitsAndReplyWithResult( |
| FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, |
| base::BindOnce(&ReadExtensionResource, std::move(ext_resource)), |
| base::BindOnce(&RunCallbackWithCompressedData, |
| std::move(callback))); |
| return; |
| } |
| } |
| |
| std::move(callback).Run(apps::mojom::IconValue::New()); |
| } |
| |
| void LoadIconFromResource(apps::mojom::IconCompression icon_compression, |
| int size_hint_in_dip, |
| apps::mojom::Publisher::LoadIconCallback callback, |
| int resource_id) { |
| if (resource_id != 0) { |
| switch (icon_compression) { |
| case apps::mojom::IconCompression::kUnknown: |
| break; |
| |
| case apps::mojom::IconCompression::kUncompressed: { |
| gfx::ImageSkia* unscaled = |
| ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| resource_id); |
| RunCallbackWithUncompressedImageSkia( |
| std::move(callback), |
| gfx::ImageSkiaOperations::CreateResizedImage( |
| *unscaled, skia::ImageOperations::RESIZE_BEST, |
| gfx::Size(size_hint_in_dip, size_hint_in_dip))); |
| return; |
| } |
| |
| case apps::mojom::IconCompression::kCompressed: { |
| base::StringPiece data = |
| ui::ResourceBundle::GetSharedInstance().GetRawDataResource( |
| resource_id); |
| RunCallbackWithCompressedData( |
| std::move(callback), |
| std::vector<uint8_t>(data.begin(), data.end())); |
| return; |
| } |
| } |
| } |
| |
| std::move(callback).Run(apps::mojom::IconValue::New()); |
| } |
| |
| } // namespace apps |