Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(447)

Side by Side Diff: chrome/browser/extensions/image_loader.cc

Issue 11027044: Add a class to replace ImageLoadingTracker with a nicer API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add missing files Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/image_loader.h"
6
7 #include "base/callback.h"
8 #include "base/file_util.h"
9 #include "base/path_service.h"
10 #include "base/string_number_conversions.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "chrome/browser/extensions/image_loader_factory.h"
13 #include "chrome/common/chrome_paths.h"
14 #include "chrome/common/extensions/extension.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "grit/component_extension_resources_map.h"
17 #include "grit/theme_resources.h"
18 #include "skia/ext/image_operations.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/image/image_skia.h"
21 #include "webkit/glue/image_decoder.h"
22
23 using content::BrowserThread;
24 using extensions::Extension;
25 using extensions::ImageLoader;
26
27 namespace {
28
29 bool ShouldResizeImageRepresentation(
30 ImageLoader::ImageRepresentation::ResizeCondition resize_method,
31 const gfx::Size& decoded_size,
32 const gfx::Size& desired_size) {
33 switch (resize_method) {
34 case ImageLoader::ImageRepresentation::ALWAYS_RESIZE:
35 return decoded_size != desired_size;
36 case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER:
37 return decoded_size.width() > desired_size.width() ||
38 decoded_size.height() > desired_size.height();
39 default:
40 NOTREACHED();
41 return false;
42 }
43 }
44
45 SkBitmap ResizeIfNeeded(const SkBitmap& bitmap,
46 const ImageLoader::ImageRepresentation& image_info) {
47 gfx::Size original_size(bitmap.width(), bitmap.height());
48 if (ShouldResizeImageRepresentation(image_info.resize_condition,
49 original_size,
50 image_info.desired_size)) {
51 return skia::ImageOperations::Resize(
52 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
53 image_info.desired_size.width(), image_info.desired_size.height());
54 }
55
56 return bitmap;
57 }
58
59 void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61
62 gfx::ImageSkia image(
63 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
64 image.MakeThreadSafe();
65 *bitmap = *image.bitmap();
66 }
67
68 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info,
69 SkBitmap* bitmap) {
70 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
71
72 // Read the file from disk.
73 std::string file_contents;
74 FilePath path = image_info.resource.GetFilePath();
75 if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) {
76 return;
77 }
78
79 // Decode the bitmap using WebKit's image decoder.
80 const unsigned char* data =
81 reinterpret_cast<const unsigned char*>(file_contents.data());
82 webkit_glue::ImageDecoder decoder;
83 // Note: This class only decodes bitmaps from extension resources. Chrome
84 // doesn't (for security reasons) directly load extension resources provided
85 // by the extension author, but instead decodes them in a separate
86 // locked-down utility process. Only if the decoding succeeds is the image
87 // saved from memory to disk and subsequently used in the Chrome UI.
88 // Chrome is therefore decoding bitmaps here that were generated by Chrome.
89 *bitmap = decoder.Decode(data, file_contents.length());
90 }
91
92 } // namespace
93
94 namespace extensions {
95
96 ////////////////////////////////////////////////////////////////////////////////
97 // ImageLoader::ImageRepresentation
98
99 ImageLoader::ImageRepresentation::ImageRepresentation(
100 const ExtensionResource& resource,
101 ResizeCondition resize_condition,
102 const gfx::Size& desired_size,
103 ui::ScaleFactor scale_factor)
104 : resource(resource),
105 resize_condition(resize_condition),
106 desired_size(desired_size),
107 scale_factor(scale_factor) {
108 }
109
110 ImageLoader::ImageRepresentation::~ImageRepresentation() {
111 }
112
113 ////////////////////////////////////////////////////////////////////////////////
114 // ImageLoader::LoadResult
115
116 struct ImageLoader::LoadResult {
117 LoadResult(const SkBitmap& bitmap,
118 const gfx::Size& original_size,
119 const ImageRepresentation& image_representation);
120 ~LoadResult();
121
122 SkBitmap bitmap;
123 gfx::Size original_size;
124 ImageRepresentation image_representation;
125 };
126
127 ImageLoader::LoadResult::LoadResult(
128 const SkBitmap& bitmap,
129 const gfx::Size& original_size,
130 const ImageLoader::ImageRepresentation& image_representation)
131 : bitmap(bitmap),
132 original_size(original_size),
133 image_representation(image_representation) {
134 }
135
136 ImageLoader::LoadResult::~LoadResult() {
137 }
138
139 ////////////////////////////////////////////////////////////////////////////////
140 // ImageLoader
141
142 ImageLoader::ImageLoader() {
143 }
144
145 ImageLoader::~ImageLoader() {
146 }
147
148 // static
149 ImageLoader* ImageLoader::Get(Profile* profile) {
150 return ImageLoaderFactory::GetForProfile(profile);
151 }
152
153 // static
154 bool ImageLoader::IsComponentExtensionResource(const Extension* extension,
155 const FilePath& resource_path,
156 int* resource_id) {
157 static const GritResourceMap kExtraComponentExtensionResources[] = {
158 {"web_store/webstore_icon_128.png", IDR_WEBSTORE_ICON},
159 {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16},
160 {"chrome_app/product_logo_128.png", IDR_PRODUCT_LOGO_128},
161 {"chrome_app/product_logo_16.png", IDR_PRODUCT_LOGO_16},
162 {"settings_app/settings_app_icon_128.png", IDR_SETTINGS_APP_ICON_128},
163 {"settings_app/settings_app_icon_16.png", IDR_SETTINGS_APP_ICON_16},
164 };
165 static const size_t kExtraComponentExtensionResourcesSize =
166 arraysize(kExtraComponentExtensionResources);
167
168 if (extension->location() != Extension::COMPONENT)
169 return false;
170
171 FilePath directory_path = extension->path();
172 FilePath resources_dir;
173 FilePath relative_path;
174 if (!PathService::Get(chrome::DIR_RESOURCES, &resources_dir) ||
175 !resources_dir.AppendRelativePath(directory_path, &relative_path)) {
176 return false;
177 }
178 relative_path = relative_path.Append(resource_path);
179 relative_path = relative_path.NormalizePathSeparators();
180
181 // TODO(tc): Make a map of FilePath -> resource ids so we don't have to
182 // covert to FilePaths all the time. This will be more useful as we add
183 // more resources.
184 for (size_t i = 0; i < kComponentExtensionResourcesSize; ++i) {
185 FilePath resource_path =
186 FilePath().AppendASCII(kComponentExtensionResources[i].name);
187 resource_path = resource_path.NormalizePathSeparators();
188
189 if (relative_path == resource_path) {
190 *resource_id = kComponentExtensionResources[i].value;
191 return true;
192 }
193 }
194 for (size_t i = 0; i < kExtraComponentExtensionResourcesSize; ++i) {
195 FilePath resource_path =
196 FilePath().AppendASCII(kExtraComponentExtensionResources[i].name);
197 resource_path = resource_path.NormalizePathSeparators();
198
199 if (relative_path == resource_path) {
200 *resource_id = kExtraComponentExtensionResources[i].value;
201 return true;
202 }
203 }
204 return false;
205 }
206
207 void ImageLoader::LoadImageAsync(
208 const Extension* extension,
209 const ExtensionResource& resource,
210 const gfx::Size& max_size,
211 const base::Callback<void(const gfx::Image&)>& callback) {
212 std::vector<ImageRepresentation> info_list;
213 info_list.push_back(ImageRepresentation(
214 resource,
215 ImageRepresentation::RESIZE_WHEN_LARGER,
216 max_size,
217 ui::SCALE_FACTOR_100P));
218 LoadImagesAsync(extension, info_list, callback);
219 }
220
221 void ImageLoader::LoadImagesAsync(
222 const Extension* extension,
223 const std::vector<ImageRepresentation>& info_list,
224 const base::Callback<void(const gfx::Image&)>& callback) {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
226
227 // Loading an image from the cache and loading resources have to happen
228 // on the UI thread. So do those two things first, and pass the rest of the
229 // work of as a blocking pool task.
230
231 std::vector<SkBitmap> bitmaps;
232 bitmaps.resize(info_list.size());
233
234 int i = 0;
235 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
236 it != info_list.end(); ++it, ++i) {
237 DCHECK(it->resource.relative_path().empty() ||
238 extension->path() == it->resource.extension_root());
239
240 int resource_id;
241 if (IsComponentExtensionResource(extension, it->resource.relative_path(),
242 &resource_id)) {
243 LoadResourceOnUIThread(resource_id, &bitmaps[i]);
244 }
245 }
246
247 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
248 BrowserThread::PostBlockingPoolTask(
249 FROM_HERE,
250 base::Bind(&ImageLoader::LoadImagesOnBlockingPool, base::Unretained(this),
251 info_list, bitmaps, callback));
252 }
253
254 void ImageLoader::LoadImagesOnBlockingPool(
255 const std::vector<ImageRepresentation>& info_list,
256 const std::vector<SkBitmap>& bitmaps,
257 const base::Callback<void(const gfx::Image&)>& callback) {
258 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
259
260 gfx::ImageSkia image_skia;
261
262 std::vector<LoadResult> load_result;
263
264 int i = 0;
265 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
266 it != info_list.end(); ++it, ++i) {
267 // If we don't have a path there isn't anything we can do, just skip it.
268 if (it->resource.relative_path().empty())
269 continue;
270
271 SkBitmap bitmap;
272 if (!bitmaps[i].isNull()) {
273 bitmap = bitmaps[i];
274 } else {
275 LoadImageOnBlockingPool(*it, &bitmap);
276 }
277
278 // If the image failed to load, skip it.
279 if (bitmap.isNull() || bitmap.empty())
280 continue;
281
282 gfx::Size original_size(bitmap.width(), bitmap.height());
283 bitmap = ResizeIfNeeded(bitmap, *it);
284
285 load_result.push_back(LoadResult(bitmap, original_size, *it));
286 }
287
288 gfx::Image image;
289
290 if (!image_skia.isNull()) {
291 image_skia.MakeThreadSafe();
292 image = gfx::Image(image_skia);
293 }
294
295 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
296 base::Bind(&ImageLoader::ReplyBack,
297 base::Unretained(this), load_result,
298 callback));
299 }
300
301 void ImageLoader::ReplyBack(
302 const std::vector<LoadResult>& load_result,
303 const base::Callback<void(const gfx::Image&)>& callback) {
304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
305
306 gfx::ImageSkia image_skia;
307
308 for (std::vector<LoadResult>::const_iterator it = load_result.begin();
309 it != load_result.end(); ++it) {
310 const SkBitmap& bitmap = it->bitmap;
311 const ImageRepresentation& image_rep = it->image_representation;
312
313 image_skia.AddRepresentation(gfx::ImageSkiaRep(
314 bitmap, image_rep.scale_factor));
315 }
316
317 gfx::Image image;
318 if (!image_skia.isNull()) {
319 image_skia.MakeThreadSafe();
320 image = gfx::Image(image_skia);
321 }
322
323 callback.Run(image);
324 }
325
326 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698