blob: 7c58a7e0896d08921cd1039c70ba1a799aa8dde9 [file] [log] [blame]
[email protected]326e6f02014-06-20 04:53:371// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]ec7de0c5a2012-11-16 07:40:472// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]326e6f02014-06-20 04:53:375#include "extensions/browser/image_loader.h"
[email protected]ec7de0c5a2012-11-16 07:40:476
avic9cec102015-12-23 00:39:267#include <stddef.h>
8
[email protected]95fd2552013-07-04 21:19:249#include <map>
Nigel Taobd12215b2018-11-29 01:10:1810#include <utility>
[email protected]95fd2552013-07-04 21:19:2411#include <vector>
12
Sebastien Marchand6d0558fd2019-01-25 16:49:3713#include "base/bind.h"
[email protected]ec7de0c5a2012-11-16 07:40:4714#include "base/callback.h"
[email protected]c8b8587b2012-11-21 23:23:3215#include "base/compiler_specific.h"
thestig94712702014-09-10 07:46:5916#include "base/files/file_util.h"
[email protected]3ea1b182013-02-08 22:38:4117#include "base/strings/string_number_conversions.h"
Gabriel Charette44db1422018-08-06 11:19:3318#include "base/task/post_task.h"
[email protected]ec7de0c5a2012-11-16 07:40:4719#include "content/public/browser/browser_thread.h"
[email protected]326e6f02014-06-20 04:53:3720#include "extensions/browser/component_extension_resource_manager.h"
21#include "extensions/browser/extensions_browser_client.h"
22#include "extensions/browser/image_loader_factory.h"
[email protected]e4452d32013-11-15 23:07:4123#include "extensions/common/extension.h"
estade32426e02016-12-18 01:26:1724#include "extensions/common/manifest_handlers/icons_handler.h"
[email protected]ec7de0c5a2012-11-16 07:40:4725#include "skia/ext/image_operations.h"
estade32426e02016-12-18 01:26:1726#include "ui/base/layout.h"
[email protected]ec7de0c5a2012-11-16 07:40:4727#include "ui/base/resource/resource_bundle.h"
estade47ce132c2017-01-17 20:37:4728#include "ui/display/display.h"
29#include "ui/display/screen.h"
[email protected]da87eec22013-05-14 09:25:2830#include "ui/gfx/codec/png_codec.h"
[email protected]1d8e0f32014-03-17 06:39:1931#include "ui/gfx/image/image_family.h"
[email protected]ec7de0c5a2012-11-16 07:40:4732#include "ui/gfx/image/image_skia.h"
[email protected]ec7de0c5a2012-11-16 07:40:4733
34using content::BrowserThread;
estade32426e02016-12-18 01:26:1735
36namespace extensions {
[email protected]ec7de0c5a2012-11-16 07:40:4737
38namespace {
39
40bool ShouldResizeImageRepresentation(
41 ImageLoader::ImageRepresentation::ResizeCondition resize_method,
42 const gfx::Size& decoded_size,
43 const gfx::Size& desired_size) {
44 switch (resize_method) {
45 case ImageLoader::ImageRepresentation::ALWAYS_RESIZE:
46 return decoded_size != desired_size;
47 case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER:
48 return decoded_size.width() > desired_size.width() ||
49 decoded_size.height() > desired_size.height();
[email protected]1d8e0f32014-03-17 06:39:1950 case ImageLoader::ImageRepresentation::NEVER_RESIZE:
51 return false;
[email protected]ec7de0c5a2012-11-16 07:40:4752 default:
53 NOTREACHED();
54 return false;
55 }
56}
57
58SkBitmap ResizeIfNeeded(const SkBitmap& bitmap,
59 const ImageLoader::ImageRepresentation& image_info) {
60 gfx::Size original_size(bitmap.width(), bitmap.height());
61 if (ShouldResizeImageRepresentation(image_info.resize_condition,
62 original_size,
63 image_info.desired_size)) {
64 return skia::ImageOperations::Resize(
65 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
66 image_info.desired_size.width(), image_info.desired_size.height());
67 }
68
69 return bitmap;
70}
71
72void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
[email protected]54ee8192014-03-29 17:37:2473 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]ec7de0c5a2012-11-16 07:40:4774
75 gfx::ImageSkia image(
Lei Zhangcf30efc2017-10-04 21:31:2476 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
[email protected]ec7de0c5a2012-11-16 07:40:4777 image.MakeThreadSafe();
78 *bitmap = *image.bitmap();
79}
80
fdoray98a1e012017-02-18 16:21:3881void LoadImageBlocking(const ImageLoader::ImageRepresentation& image_info,
82 SkBitmap* bitmap) {
[email protected]ec7de0c5a2012-11-16 07:40:4783 // Read the file from disk.
84 std::string file_contents;
[email protected]650b2d52013-02-10 03:41:4585 base::FilePath path = image_info.resource.GetFilePath();
[email protected]82f84b92013-08-30 18:23:5086 if (path.empty() || !base::ReadFileToString(path, &file_contents)) {
[email protected]ec7de0c5a2012-11-16 07:40:4787 return;
88 }
89
[email protected]ec7de0c5a2012-11-16 07:40:4790 const unsigned char* data =
91 reinterpret_cast<const unsigned char*>(file_contents.data());
[email protected]ec7de0c5a2012-11-16 07:40:4792 // Note: This class only decodes bitmaps from extension resources. Chrome
93 // doesn't (for security reasons) directly load extension resources provided
94 // by the extension author, but instead decodes them in a separate
95 // locked-down utility process. Only if the decoding succeeds is the image
96 // saved from memory to disk and subsequently used in the Chrome UI.
97 // Chrome is therefore decoding bitmaps here that were generated by Chrome.
[email protected]da87eec22013-05-14 09:25:2898 gfx::PNGCodec::Decode(data, file_contents.length(), bitmap);
[email protected]ec7de0c5a2012-11-16 07:40:4799}
100
[email protected]1d8e0f32014-03-17 06:39:19101std::vector<SkBitmap> LoadResourceBitmaps(
102 const Extension* extension,
103 const std::vector<ImageLoader::ImageRepresentation>& info_list) {
104 // Loading resources has to happen on the UI thread. So do this first, and
105 // pass the rest of the work off as a blocking pool task.
106 std::vector<SkBitmap> bitmaps;
107 bitmaps.resize(info_list.size());
108
109 int i = 0;
jdoerriea1e1598b2018-10-10 09:10:37110 for (auto it = info_list.cbegin(); it != info_list.cend(); ++it, ++i) {
[email protected]1d8e0f32014-03-17 06:39:19111 DCHECK(it->resource.relative_path().empty() ||
112 extension->path() == it->resource.extension_root());
113
dpapad669a89702019-05-10 18:00:49114 int resource_id = 0;
[email protected]326e6f02014-06-20 04:53:37115 if (extension->location() == Manifest::COMPONENT) {
mukaiee458c92015-01-06 01:30:33116 const extensions::ComponentExtensionResourceManager* manager =
117 extensions::ExtensionsBrowserClient::Get()
118 ->GetComponentExtensionResourceManager();
dpapad669a89702019-05-10 18:00:49119 if (manager &&
120 manager->IsComponentExtensionResource(
121 extension->path(), it->resource.relative_path(), &resource_id)) {
122 DCHECK(!ui::ResourceBundle::GetSharedInstance().IsGzipped(resource_id));
123 LoadResourceOnUIThread(resource_id, &bitmaps[i]);
[email protected]326e6f02014-06-20 04:53:37124 }
[email protected]1d8e0f32014-03-17 06:39:19125 }
126 }
127 return bitmaps;
128}
129
[email protected]ec7de0c5a2012-11-16 07:40:47130} // namespace
131
[email protected]ec7de0c5a2012-11-16 07:40:47132////////////////////////////////////////////////////////////////////////////////
133// ImageLoader::ImageRepresentation
134
135ImageLoader::ImageRepresentation::ImageRepresentation(
136 const ExtensionResource& resource,
137 ResizeCondition resize_condition,
138 const gfx::Size& desired_size,
estade47ce132c2017-01-17 20:37:47139 float scale_factor)
[email protected]ec7de0c5a2012-11-16 07:40:47140 : resource(resource),
141 resize_condition(resize_condition),
142 desired_size(desired_size),
estade47ce132c2017-01-17 20:37:47143 scale_factor(scale_factor) {}
[email protected]ec7de0c5a2012-11-16 07:40:47144
145ImageLoader::ImageRepresentation::~ImageRepresentation() {
146}
147
148////////////////////////////////////////////////////////////////////////////////
149// ImageLoader::LoadResult
150
151struct ImageLoader::LoadResult {
152 LoadResult(const SkBitmap& bitmap,
153 const gfx::Size& original_size,
154 const ImageRepresentation& image_representation);
155 ~LoadResult();
156
157 SkBitmap bitmap;
158 gfx::Size original_size;
159 ImageRepresentation image_representation;
160};
161
162ImageLoader::LoadResult::LoadResult(
163 const SkBitmap& bitmap,
164 const gfx::Size& original_size,
165 const ImageLoader::ImageRepresentation& image_representation)
166 : bitmap(bitmap),
167 original_size(original_size),
168 image_representation(image_representation) {
169}
170
171ImageLoader::LoadResult::~LoadResult() {
172}
173
[email protected]f5bb7642013-11-23 19:03:53174namespace {
175
176// Need to be after ImageRepresentation and LoadResult are defined.
fdoray98a1e012017-02-18 16:21:38177std::vector<ImageLoader::LoadResult> LoadImagesBlocking(
[email protected]f5bb7642013-11-23 19:03:53178 const std::vector<ImageLoader::ImageRepresentation>& info_list,
179 const std::vector<SkBitmap>& bitmaps) {
[email protected]f5bb7642013-11-23 19:03:53180 std::vector<ImageLoader::LoadResult> load_result;
181
182 for (size_t i = 0; i < info_list.size(); ++i) {
183 const ImageLoader::ImageRepresentation& image = info_list[i];
184
185 // If we don't have a path there isn't anything we can do, just skip it.
186 if (image.resource.relative_path().empty())
187 continue;
188
189 SkBitmap bitmap;
190 if (bitmaps[i].isNull())
fdoray98a1e012017-02-18 16:21:38191 LoadImageBlocking(image, &bitmap);
[email protected]f5bb7642013-11-23 19:03:53192 else
193 bitmap = bitmaps[i];
194
195 // If the image failed to load, skip it.
196 if (bitmap.isNull() || bitmap.empty())
197 continue;
198
199 gfx::Size original_size(bitmap.width(), bitmap.height());
200 bitmap = ResizeIfNeeded(bitmap, image);
201
202 load_result.push_back(
203 ImageLoader::LoadResult(bitmap, original_size, image));
204 }
205
206 return load_result;
207}
208
209} // namespace
210
[email protected]ec7de0c5a2012-11-16 07:40:47211////////////////////////////////////////////////////////////////////////////////
212// ImageLoader
213
Jeremy Roman9fc2de62019-07-12 14:15:03214ImageLoader::ImageLoader() {}
[email protected]ec7de0c5a2012-11-16 07:40:47215
216ImageLoader::~ImageLoader() {
217}
218
219// static
[email protected]472522b2013-10-25 00:41:28220ImageLoader* ImageLoader::Get(content::BrowserContext* context) {
221 return ImageLoaderFactory::GetForBrowserContext(context);
[email protected]ec7de0c5a2012-11-16 07:40:47222}
223
[email protected]f5bb7642013-11-23 19:03:53224void ImageLoader::LoadImageAsync(const Extension* extension,
225 const ExtensionResource& resource,
226 const gfx::Size& max_size,
Nigel Taobd12215b2018-11-29 01:10:18227 ImageLoaderImageCallback callback) {
[email protected]ec7de0c5a2012-11-16 07:40:47228 std::vector<ImageRepresentation> info_list;
229 info_list.push_back(ImageRepresentation(
estade47ce132c2017-01-17 20:37:47230 resource, ImageRepresentation::RESIZE_WHEN_LARGER, max_size, 1.f));
Nigel Taobd12215b2018-11-29 01:10:18231 LoadImagesAsync(extension, info_list, std::move(callback));
[email protected]ec7de0c5a2012-11-16 07:40:47232}
233
estade32426e02016-12-18 01:26:17234void ImageLoader::LoadImageAtEveryScaleFactorAsync(
235 const Extension* extension,
236 const gfx::Size& dip_size,
Nigel Taobd12215b2018-11-29 01:10:18237 ImageLoaderImageCallback callback) {
estade32426e02016-12-18 01:26:17238 std::vector<ImageRepresentation> info_list;
estade47ce132c2017-01-17 20:37:47239
240 std::set<float> scales;
241 for (auto scale : ui::GetSupportedScaleFactors())
242 scales.insert(ui::GetScaleForScaleFactor(scale));
243
244 // There may not be a screen in unit tests.
vmpstr6d9996c82017-02-23 00:43:25245 auto* screen = display::Screen::GetScreen();
estade47ce132c2017-01-17 20:37:47246 if (screen) {
247 for (const auto& display : screen->GetAllDisplays())
248 scales.insert(display.device_scale_factor());
249 }
250
251 for (auto scale : scales) {
252 const gfx::Size px_size = gfx::ScaleToFlooredSize(dip_size, scale);
estade32426e02016-12-18 01:26:17253 ExtensionResource image = IconsInfo::GetIconResource(
254 extension, px_size.width(), ExtensionIconSet::MATCH_BIGGER);
255 info_list.push_back(ImageRepresentation(
256 image, ImageRepresentation::ALWAYS_RESIZE, px_size, scale));
257 }
Nigel Taobd12215b2018-11-29 01:10:18258 LoadImagesAsync(extension, info_list, std::move(callback));
estade32426e02016-12-18 01:26:17259}
260
[email protected]ec7de0c5a2012-11-16 07:40:47261void ImageLoader::LoadImagesAsync(
262 const Extension* extension,
263 const std::vector<ImageRepresentation>& info_list,
Nigel Taobd12215b2018-11-29 01:10:18264 ImageLoaderImageCallback callback) {
[email protected]54ee8192014-03-29 17:37:24265 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Sami Kyostilafc646682019-08-08 05:19:56266 base::PostTaskAndReplyWithResult(
267 FROM_HERE,
268 {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
Nigel Taobd12215b2018-11-29 01:10:18269 base::BindOnce(LoadImagesBlocking, info_list,
270 LoadResourceBitmaps(extension, info_list)),
271 base::BindOnce(&ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(),
272 std::move(callback)));
[email protected]1d8e0f32014-03-17 06:39:19273}
274
275void ImageLoader::LoadImageFamilyAsync(
estade32426e02016-12-18 01:26:17276 const Extension* extension,
[email protected]1d8e0f32014-03-17 06:39:19277 const std::vector<ImageRepresentation>& info_list,
Nigel Taobd12215b2018-11-29 01:10:18278 ImageLoaderImageFamilyCallback callback) {
[email protected]54ee8192014-03-29 17:37:24279 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Sami Kyostilafc646682019-08-08 05:19:56280 base::PostTaskAndReplyWithResult(
281 FROM_HERE,
282 {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
Nigel Taobd12215b2018-11-29 01:10:18283 base::BindOnce(LoadImagesBlocking, info_list,
284 LoadResourceBitmaps(extension, info_list)),
285 base::BindOnce(&ImageLoader::ReplyBackWithImageFamily,
286 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
[email protected]ec7de0c5a2012-11-16 07:40:47287}
288
Nigel Taobd12215b2018-11-29 01:10:18289void ImageLoader::ReplyBack(ImageLoaderImageCallback callback,
[email protected]f5bb7642013-11-23 19:03:53290 const std::vector<LoadResult>& load_result) {
[email protected]54ee8192014-03-29 17:37:24291 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]ec7de0c5a2012-11-16 07:40:47292
293 gfx::ImageSkia image_skia;
294
jdoerriea1e1598b2018-10-10 09:10:37295 for (auto it = load_result.cbegin(); it != load_result.cend(); ++it) {
[email protected]ec7de0c5a2012-11-16 07:40:47296 const SkBitmap& bitmap = it->bitmap;
297 const ImageRepresentation& image_rep = it->image_representation;
298
estade47ce132c2017-01-17 20:37:47299 image_skia.AddRepresentation(
300 gfx::ImageSkiaRep(bitmap, image_rep.scale_factor));
[email protected]ec7de0c5a2012-11-16 07:40:47301 }
302
303 gfx::Image image;
304 if (!image_skia.isNull()) {
305 image_skia.MakeThreadSafe();
306 image = gfx::Image(image_skia);
307 }
308
Nigel Taobd12215b2018-11-29 01:10:18309 std::move(callback).Run(image);
[email protected]ec7de0c5a2012-11-16 07:40:47310}
311
[email protected]1d8e0f32014-03-17 06:39:19312void ImageLoader::ReplyBackWithImageFamily(
Nigel Taobd12215b2018-11-29 01:10:18313 ImageLoaderImageFamilyCallback callback,
[email protected]1d8e0f32014-03-17 06:39:19314 const std::vector<LoadResult>& load_result) {
[email protected]54ee8192014-03-29 17:37:24315 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]1d8e0f32014-03-17 06:39:19316
317 std::map<std::pair<int, int>, gfx::ImageSkia> image_skia_map;
318 gfx::ImageFamily image_family;
319
jdoerriea1e1598b2018-10-10 09:10:37320 for (auto it = load_result.cbegin(); it != load_result.cend(); ++it) {
[email protected]1d8e0f32014-03-17 06:39:19321 const SkBitmap& bitmap = it->bitmap;
322 const ImageRepresentation& image_rep = it->image_representation;
323 const std::pair<int, int> key = std::make_pair(
324 image_rep.desired_size.width(), image_rep.desired_size.height());
325 // Create a new ImageSkia for this width/height, or add a representation to
326 // an existing ImageSkia with the same width/height.
327 image_skia_map[key].AddRepresentation(
estade47ce132c2017-01-17 20:37:47328 gfx::ImageSkiaRep(bitmap, image_rep.scale_factor));
[email protected]1d8e0f32014-03-17 06:39:19329 }
330
jdoerriea1e1598b2018-10-10 09:10:37331 for (auto it = image_skia_map.begin(); it != image_skia_map.end(); ++it) {
[email protected]1d8e0f32014-03-17 06:39:19332 it->second.MakeThreadSafe();
333 image_family.Add(it->second);
334 }
335
Nigel Taobd12215b2018-11-29 01:10:18336 std::move(callback).Run(std::move(image_family));
[email protected]1d8e0f32014-03-17 06:39:19337}
338
[email protected]ec7de0c5a2012-11-16 07:40:47339} // namespace extensions