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

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: fix potential crash in ReplyBack part Created 8 years, 2 months 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/image_loading_tracker.h" 5 #include "chrome/browser/extensions/image_loader.h"
6 6
7 #include <string> 7 #include "base/callback.h"
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/file_util.h" 8 #include "base/file_util.h"
12 #include "base/path_service.h" 9 #include "base/path_service.h"
10 #include "base/memory/singleton.h"
11 #include "base/string_number_conversions.h"
13 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/threading/sequenced_worker_pool.h"
14 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
15 #include "chrome/common/chrome_notification_types.h" 13 #include "chrome/common/chrome_notification_types.h"
16 #include "chrome/common/chrome_paths.h" 14 #include "chrome/common/chrome_paths.h"
17 #include "chrome/common/extensions/extension.h" 15 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "chrome/common/extensions/extension_file_util.h"
20 #include "chrome/common/extensions/extension_resource.h"
21 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/notification_service.h" 17 #include "content/public/browser/notification_service.h"
23 #include "grit/component_extension_resources_map.h" 18 #include "grit/component_extension_resources_map.h"
24 #include "grit/theme_resources.h" 19 #include "grit/theme_resources.h"
25 #include "skia/ext/image_operations.h" 20 #include "skia/ext/image_operations.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/base/resource/resource_bundle.h" 21 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/image/image.h" 22 #include "ui/gfx/image/image_skia.h"
29 #include "ui/gfx/image/image_skia_rep.h"
30 #include "webkit/glue/image_decoder.h" 23 #include "webkit/glue/image_decoder.h"
31 24
32 using content::BrowserThread; 25 using content::BrowserThread;
33 using extensions::Extension; 26 using extensions::Extension;
27 using extensions::ImageLoader;
34 28
35 namespace { 29 namespace {
36 30
31 std::string SizeToString(const gfx::Size& max_size) {
32 return base::IntToString(max_size.width()) + "x" +
33 base::IntToString(max_size.height());
34 }
35
37 bool ShouldResizeImageRepresentation( 36 bool ShouldResizeImageRepresentation(
38 ImageLoadingTracker::ImageRepresentation::ResizeCondition resize_method, 37 ImageLoader::ImageRepresentation::ResizeCondition resize_method,
39 const gfx::Size& decoded_size, 38 const gfx::Size& decoded_size,
40 const gfx::Size& desired_size) { 39 const gfx::Size& desired_size) {
41 switch (resize_method) { 40 switch (resize_method) {
42 case ImageLoadingTracker::ImageRepresentation::ALWAYS_RESIZE: 41 case ImageLoader::ImageRepresentation::ALWAYS_RESIZE:
43 return decoded_size != desired_size; 42 return decoded_size != desired_size;
44 case ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER: 43 case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER:
45 return decoded_size.width() > desired_size.width() || 44 return decoded_size.width() > desired_size.width() ||
46 decoded_size.height() > desired_size.height(); 45 decoded_size.height() > desired_size.height();
47 default: 46 default:
48 NOTREACHED(); 47 NOTREACHED();
49 return false; 48 return false;
50 } 49 }
51 } 50 }
52 51
52 SkBitmap ResizeIfNeeded(const SkBitmap& bitmap,
53 const ImageLoader::ImageRepresentation& image_info) {
54 gfx::Size original_size(bitmap.width(), bitmap.height());
55 if (ShouldResizeImageRepresentation(image_info.resize_condition,
56 original_size,
57 image_info.desired_size)) {
58 return skia::ImageOperations::Resize(
59 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
60 image_info.desired_size.width(), image_info.desired_size.height());
61 }
62
63 return bitmap;
64 }
65
66 void LoadResourceOnUIThread(int resource_id, SkBitmap* bitmap) {
67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
68
69 gfx::ImageSkia image(
70 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id));
71 image.MakeThreadSafe();
72 *bitmap = *image.bitmap();
73 }
74
75 void LoadImageOnBlockingPool(const ImageLoader::ImageRepresentation& image_info,
76 SkBitmap* bitmap) {
77 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
78
79 // Read the file from disk.
80 std::string file_contents;
81 FilePath path = image_info.resource.GetFilePath();
82 if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) {
83 return;
84 }
85
86 // Decode the bitmap using WebKit's image decoder.
87 const unsigned char* data =
88 reinterpret_cast<const unsigned char*>(file_contents.data());
89 webkit_glue::ImageDecoder decoder;
90 // Note: This class only decodes bitmaps from extension resources. Chrome
91 // doesn't (for security reasons) directly load extension resources provided
92 // by the extension author, but instead decodes them in a separate
93 // locked-down utility process. Only if the decoding succeeds is the image
94 // saved from memory to disk and subsequently used in the Chrome UI.
95 // Chrome is therefore decoding bitmaps here that were generated by Chrome.
96 *bitmap = decoder.Decode(data, file_contents.length());
97 }
98
53 } // namespace 99 } // namespace
54 100
55 //////////////////////////////////////////////////////////////////////////////// 101 namespace extensions {
56 // ImageLoadingTracker::Observer
57
58 ImageLoadingTracker::Observer::~Observer() {}
59 102
60 //////////////////////////////////////////////////////////////////////////////// 103 ////////////////////////////////////////////////////////////////////////////////
61 // ImageLoadingTracker::ImageRepresentation 104 // ImageLoader::ImageRepresentation
62 105
63 ImageLoadingTracker::ImageRepresentation::ImageRepresentation( 106 ImageLoader::ImageRepresentation::ImageRepresentation(
64 const ExtensionResource& resource, 107 const ExtensionResource& resource,
65 ResizeCondition resize_method, 108 ResizeCondition resize_condition,
66 const gfx::Size& desired_size, 109 const gfx::Size& desired_size,
67 ui::ScaleFactor scale_factor) 110 ui::ScaleFactor scale_factor)
68 : resource(resource), 111 : resource(resource),
69 resize_method(resize_method), 112 resize_condition(resize_condition),
70 desired_size(desired_size), 113 desired_size(desired_size),
71 scale_factor(scale_factor) { 114 scale_factor(scale_factor) {
72 } 115 }
73 116
74 ImageLoadingTracker::ImageRepresentation::~ImageRepresentation() { 117 ImageLoader::ImageRepresentation::~ImageRepresentation() {
75 } 118 }
76 119
77 //////////////////////////////////////////////////////////////////////////////// 120 ////////////////////////////////////////////////////////////////////////////////
78 // ImageLoadingTracker::PendingLoadInfo 121 // ImageLoader::PendingLoadInfo
79 122
80 ImageLoadingTracker::PendingLoadInfo::PendingLoadInfo() 123 ImageLoader::PendingLoadInfo::PendingLoadInfo()
81 : extension(NULL), 124 : save_to_cache(true) {
82 cache(CACHE),
83 pending_count(0) {
84 } 125 }
85 126
86 ImageLoadingTracker::PendingLoadInfo::~PendingLoadInfo() {} 127 ImageLoader::PendingLoadInfo::~PendingLoadInfo() {}
87 128
88 //////////////////////////////////////////////////////////////////////////////// 129 ////////////////////////////////////////////////////////////////////////////////
89 // ImageLoadingTracker::ImageLoader 130 // ImageLoader::LoadResult
90 131
91 // A RefCounted class for loading bitmaps/image reps on the File thread and 132 ImageLoader::LoadResult::LoadResult(
92 // reporting back on the UI thread. 133 const SkBitmap& bitmap,
93 class ImageLoadingTracker::ImageLoader 134 const gfx::Size& original_size,
94 : public base::RefCountedThreadSafe<ImageLoader> { 135 const ImageLoader::ImageRepresentation& image_representation)
95 public: 136 : bitmap(bitmap),
96 explicit ImageLoader(ImageLoadingTracker* tracker) 137 original_size(original_size),
97 : tracker_(tracker) { 138 image_representation(image_representation) {
98 CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_)); 139 }
99 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
100 }
101 140
102 // Lets this class know that the tracker is no longer interested in the 141 ImageLoader::LoadResult::~LoadResult() {
103 // results. 142 }
104 void StopTracking() {
105 tracker_ = NULL;
106 }
107
108 // Instructs the loader to load a task on the blocking pool.
109 void LoadImage(const ImageRepresentation& image_info, int id) {
110 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
111 BrowserThread::PostBlockingPoolTask(
112 FROM_HERE,
113 base::Bind(&ImageLoader::LoadOnBlockingPool, this, image_info, id));
114 }
115
116 void LoadOnBlockingPool(const ImageRepresentation& image_info, int id) {
117 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
118
119 // Read the file from disk.
120 std::string file_contents;
121 FilePath path = image_info.resource.GetFilePath();
122 if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) {
123 ReportBack(NULL, image_info, gfx::Size(), id);
124 return;
125 }
126
127 // Decode the bitmap using WebKit's image decoder.
128 const unsigned char* data =
129 reinterpret_cast<const unsigned char*>(file_contents.data());
130 webkit_glue::ImageDecoder decoder;
131 scoped_ptr<SkBitmap> decoded(new SkBitmap());
132 // Note: This class only decodes bitmaps from extension resources. Chrome
133 // doesn't (for security reasons) directly load extension resources provided
134 // by the extension author, but instead decodes them in a separate
135 // locked-down utility process. Only if the decoding succeeds is the image
136 // saved from memory to disk and subsequently used in the Chrome UI.
137 // Chrome is therefore decoding bitmaps here that were generated by Chrome.
138 *decoded = decoder.Decode(data, file_contents.length());
139 if (decoded->empty()) {
140 ReportBack(NULL, image_info, gfx::Size(), id);
141 return; // Unable to decode.
142 }
143
144 gfx::Size original_size(decoded->width(), decoded->height());
145 *decoded = ResizeIfNeeded(*decoded, image_info);
146
147 ReportBack(decoded.release(), image_info, original_size, id);
148 }
149
150 // Instructs the loader to load a resource on the UI thread.
151 void LoadResource(const ImageRepresentation& image_info,
152 int id,
153 int resource_id) {
154 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
155
156 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
157 LoadResourceOnUIThread(image_info, id, resource_id);
158 return;
159 }
160
161 BrowserThread::PostTask(
162 BrowserThread::UI, FROM_HERE,
163 base::Bind(&ImageLoader::LoadResourceOnUIThread, this, image_info,
164 id, resource_id));
165 }
166
167 void LoadResourceOnUIThread(const ImageRepresentation& image_info,
168 int id,
169 int resource_id) {
170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
171
172 // Bundled image resources is only safe to be loaded on UI thread.
173 gfx::ImageSkia* image =
174 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
175 image->MakeThreadSafe();
176
177 BrowserThread::PostBlockingPoolTask(
178 FROM_HERE,
179 base::Bind(&ImageLoader::ResizeOnBlockingPool, this, image_info,
180 id, *image));
181 }
182
183 void ResizeOnBlockingPool(const ImageRepresentation& image_info,
184 int id,
185 const gfx::ImageSkia& image) {
186 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
187 // TODO(xiyuan): Clean up to use SkBitmap here and in LoadOnBlockingPool.
188 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
189 *bitmap = ResizeIfNeeded(*image.bitmap(), image_info);
190 ReportBack(bitmap.release(), image_info, image_info.desired_size, id);
191 }
192
193 void ReportBack(const SkBitmap* bitmap, const ImageRepresentation& image_info,
194 const gfx::Size& original_size, int id) {
195 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
196
197 BrowserThread::PostTask(
198 callback_thread_id_, FROM_HERE,
199 base::Bind(&ImageLoader::ReportOnCallingThread, this,
200 bitmap, image_info, original_size, id));
201 }
202
203 void ReportOnCallingThread(const SkBitmap* bitmap,
204 const ImageRepresentation& image_info,
205 const gfx::Size& original_size,
206 int id) {
207 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
208
209 if (tracker_)
210 tracker_->OnBitmapLoaded(bitmap, image_info, original_size, id, true);
211
212 if (bitmap)
213 delete bitmap;
214 }
215
216 private:
217 friend class base::RefCountedThreadSafe<ImageLoader>;
218 ~ImageLoader() {}
219
220 SkBitmap ResizeIfNeeded(const SkBitmap& bitmap,
221 const ImageRepresentation& image_info) {
222 gfx::Size original_size(bitmap.width(), bitmap.height());
223 if (ShouldResizeImageRepresentation(image_info.resize_method,
224 original_size,
225 image_info.desired_size)) {
226 return skia::ImageOperations::Resize(
227 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
228 image_info.desired_size.width(), image_info.desired_size.height());
229 }
230
231 return bitmap;
232 }
233
234 // The tracker we are loading the bitmap for. If NULL, it means the tracker is
235 // no longer interested in the reply.
236 ImageLoadingTracker* tracker_;
237
238 // The thread that we need to call back on to report that we are done.
239 BrowserThread::ID callback_thread_id_;
240
241 DISALLOW_COPY_AND_ASSIGN(ImageLoader);
242 };
243 143
244 //////////////////////////////////////////////////////////////////////////////// 144 ////////////////////////////////////////////////////////////////////////////////
245 // ImageLoadingTracker 145 // ImageLoader
246 146
247 ImageLoadingTracker::ImageLoadingTracker(Observer* observer) 147 // static
248 : observer_(observer), 148 ImageLoader* ImageLoader::GetInstance() {
249 next_id_(0) { 149 return Singleton<ImageLoader>::get();
150 }
151
152 ImageLoader::ImageLoader() {
250 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 153 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
251 content::NotificationService::AllSources()); 154 content::NotificationService::AllSources());
155 #if defined(OS_CHROMEOS)
156 const int kLauncherIconSizes[] = { extension_misc::EXTENSION_ICON_SMALL,
157 extension_misc::EXTENSION_ICON_SMALL * 2 };
158 SetImageSizesToCache(
159 std::set<int>(kLauncherIconSizes,
160 kLauncherIconSizes + arraysize(kLauncherIconSizes)));
161 #endif
252 } 162 }
253 163
254 ImageLoadingTracker::~ImageLoadingTracker() { 164 ImageLoader::~ImageLoader() {
255 // The loader is created lazily and is NULL if the tracker is destroyed before
256 // any valid image load tasks have been posted.
257 if (loader_)
258 loader_->StopTracking();
259 } 165 }
260 166
261 void ImageLoadingTracker::LoadImage(const Extension* extension, 167 // static
262 const ExtensionResource& resource, 168 bool ImageLoader::IsComponentExtensionResource(const Extension* extension,
263 const gfx::Size& max_size, 169 const FilePath& resource_path,
264 CacheParam cache) { 170 int* resource_id) {
265 std::vector<ImageRepresentation> info_list;
266 info_list.push_back(ImageRepresentation(
267 resource,
268 ImageRepresentation::RESIZE_WHEN_LARGER,
269 max_size,
270 ui::SCALE_FACTOR_100P));
271 LoadImages(extension, info_list, cache);
272 }
273
274 void ImageLoadingTracker::LoadImages(
275 const Extension* extension,
276 const std::vector<ImageRepresentation>& info_list,
277 CacheParam cache) {
278 PendingLoadInfo load_info;
279 load_info.extension = extension;
280 load_info.cache = cache;
281 load_info.extension_id = extension->id();
282 load_info.pending_count = info_list.size();
283 int id = next_id_++;
284 load_map_[id] = load_info;
285
286 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
287 it != info_list.end(); ++it) {
288 // If we don't have a path we don't need to do any further work, just
289 // respond back.
290 if (it->resource.relative_path().empty()) {
291 OnBitmapLoaded(NULL, *it, it->desired_size, id, false);
292 continue;
293 }
294
295 DCHECK(extension->path() == it->resource.extension_root());
296
297 // See if the extension has the bitmap already.
298 if (extension->HasCachedImage(it->resource, it->desired_size)) {
299 SkBitmap bitmap = extension->GetCachedImage(it->resource,
300 it->desired_size);
301 OnBitmapLoaded(&bitmap, *it, it->desired_size, id, false);
302 continue;
303 }
304
305 // Instruct the ImageLoader to load this on the File thread. LoadImage and
306 // LoadResource do not block.
307 if (!loader_)
308 loader_ = new ImageLoader(this);
309
310 int resource_id = -1;
311 if (IsComponentExtensionResource(extension, it->resource.relative_path(),
312 &resource_id))
313 loader_->LoadResource(*it, id, resource_id);
314 else
315 loader_->LoadImage(*it, id);
316 }
317 }
318
319 bool ImageLoadingTracker::IsComponentExtensionResource(
320 const Extension* extension,
321 const FilePath& resource_path,
322 int* resource_id) {
323 static const GritResourceMap kExtraComponentExtensionResources[] = { 171 static const GritResourceMap kExtraComponentExtensionResources[] = {
324 {"web_store/webstore_icon_128.png", IDR_WEBSTORE_ICON}, 172 {"web_store/webstore_icon_128.png", IDR_WEBSTORE_ICON},
325 {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16}, 173 {"web_store/webstore_icon_16.png", IDR_WEBSTORE_ICON_16},
326 {"chrome_app/product_logo_128.png", IDR_PRODUCT_LOGO_128}, 174 {"chrome_app/product_logo_128.png", IDR_PRODUCT_LOGO_128},
327 {"chrome_app/product_logo_16.png", IDR_PRODUCT_LOGO_16}, 175 {"chrome_app/product_logo_16.png", IDR_PRODUCT_LOGO_16},
328 }; 176 };
329 static const size_t kExtraComponentExtensionResourcesSize = 177 static const size_t kExtraComponentExtensionResourcesSize =
330 arraysize(kExtraComponentExtensionResources); 178 arraysize(kExtraComponentExtensionResources);
331 179
332 if (extension->location() != Extension::COMPONENT) 180 if (extension->location() != Extension::COMPONENT)
(...skipping 28 matching lines...) Expand all
361 resource_path = resource_path.NormalizePathSeparators(); 209 resource_path = resource_path.NormalizePathSeparators();
362 210
363 if (relative_path == resource_path) { 211 if (relative_path == resource_path) {
364 *resource_id = kExtraComponentExtensionResources[i].value; 212 *resource_id = kExtraComponentExtensionResources[i].value;
365 return true; 213 return true;
366 } 214 }
367 } 215 }
368 return false; 216 return false;
369 } 217 }
370 218
371 void ImageLoadingTracker::OnBitmapLoaded( 219 void ImageLoader::LoadImageAsync(
372 const SkBitmap* bitmap, 220 const Extension* extension,
373 const ImageRepresentation& image_info, 221 const ExtensionResource& resource,
374 const gfx::Size& original_size, 222 const gfx::Size& max_size,
375 int id, 223 const base::Callback<void(const gfx::Image&)>& callback) {
376 bool should_cache) { 224 std::vector<ImageRepresentation> info_list;
377 LoadMap::iterator load_map_it = load_map_.find(id); 225 info_list.push_back(ImageRepresentation(
378 DCHECK(load_map_it != load_map_.end()); 226 resource,
379 PendingLoadInfo* info = &load_map_it->second; 227 ImageRepresentation::RESIZE_WHEN_LARGER,
380 228 max_size,
381 // Save the pending results. 229 ui::SCALE_FACTOR_100P));
382 DCHECK_GT(info->pending_count, 0u); 230 LoadImagesAsync(extension, info_list, callback);
383 info->pending_count--; 231 }
384 if (bitmap) { 232
385 info->image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap, 233 void ImageLoader::LoadImagesAsync(
386 image_info.scale_factor)); 234 const Extension* extension,
387 } 235 const std::vector<ImageRepresentation>& info_list,
388 236 const base::Callback<void(const gfx::Image&)>& callback) {
389 // Add to the extension's bitmap cache if requested. 237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
390 DCHECK(info->cache != CACHE || info->extension); 238
391 if (should_cache && info->cache == CACHE && !image_info.resource.empty() && 239 // Loading an image from the cache and loading resources have to happen
392 !info->extension->HasCachedImage(image_info.resource, original_size)) { 240 // on the UI thread. So do those two things first, and pass the rest of the
393 info->extension->SetCachedImage(image_info.resource, 241 // work of as a blocking pool task.
394 bitmap ? *bitmap : SkBitmap(), 242
395 original_size); 243 std::vector<SkBitmap*> bitmaps;
396 } 244 bitmaps.resize(info_list.size());
397 245
398 // If all pending bitmaps are done then report back. 246 bool should_cache = false;
399 if (info->pending_count == 0) { 247
248 // Count how many images are loaded from the cache, so we can check if we got
249 // all images.
250 unsigned loadedFromCache = 0;
251 int i = 0;
252 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
253 it != info_list.end(); ++it, ++i) {
254 DCHECK(it->resource.relative_path().empty() ||
255 extension->path() == it->resource.extension_root());
256
257 should_cache |= ShouldCacheImage(*it);
258
259 const SkBitmap* bitmap = GetCachedImage(it->resource, it->desired_size);
260 if (bitmap) {
261 bitmaps[i] = new SkBitmap(*bitmap);
xiyuan 2012/10/10 20:30:22 Would using a SkBitmap* gives extra benefits? If n
Marijn Kruisselbrink 2012/10/10 22:33:03 I think initially I thought I needed to be able to
262 ++loadedFromCache;
263 continue;
264 }
265
266 int resource_id;
267 if (IsComponentExtensionResource(extension, it->resource.relative_path(),
268 &resource_id)) {
269 bitmaps[i] = new SkBitmap;
270 LoadResourceOnUIThread(resource_id, bitmaps[i]);
271 }
272 }
273
274 if (loadedFromCache == info_list.size()) {
275 gfx::ImageSkia image_skia;
276
277 for (unsigned i = 0; i < info_list.size(); ++i) {
278 image_skia.AddRepresentation(gfx::ImageSkiaRep(
279 *bitmaps[i], info_list[i].scale_factor));
280 delete bitmaps[i];
281 }
282
400 gfx::Image image; 283 gfx::Image image;
401 std::string extension_id = info->extension_id; 284 if (!image_skia.isNull()) {
402 285 image_skia.MakeThreadSafe();
403 if (!info->image_skia.isNull()) { 286 image = gfx::Image(image_skia);
404 info->image_skia.MakeThreadSafe(); 287 }
405 image = gfx::Image(info->image_skia); 288
406 } 289 callback.Run(image);
407 290 return;
408 load_map_.erase(load_map_it); 291 }
409 292
410 // ImageLoadingTracker might be deleted after the callback so don't do 293 PendingLoadInfo* load_info = NULL;
411 // anything after this statement. 294 if (should_cache) {
412 observer_->OnImageLoaded(image, extension_id, id); 295 load_info = new PendingLoadInfo;
413 } 296 load_info->extension_id = extension->id();
414 } 297 load_info->save_to_cache = true;
415 298 pending_.insert(load_info);
416 void ImageLoadingTracker::Observe(int type, 299 }
417 const content::NotificationSource& source, 300
418 const content::NotificationDetails& details) { 301 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
302 BrowserThread::PostBlockingPoolTask(
303 FROM_HERE,
304 base::Bind(&ImageLoader::LoadImagesOnBlockingPool, base::Unretained(this),
305 info_list, bitmaps, load_info, callback));
306
307 }
308
309 void ImageLoader::LoadImagesOnBlockingPool(
310 const std::vector<ImageRepresentation>& info_list,
311 const std::vector<SkBitmap*>& bitmaps,
312 PendingLoadInfo* load_info,
313 const base::Callback<void(const gfx::Image&)>& callback) {
314 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
315
316 gfx::ImageSkia image_skia;
317
318 std::vector<LoadResult> load_result;
319
320 int i = 0;
321 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
322 it != info_list.end(); ++it, ++i) {
323 // If we don't have a path there isn't anything we can do, just skip it.
324 if (it->resource.relative_path().empty())
325 continue;
326
327 SkBitmap bitmap;
328 if (bitmaps[i]) {
329 bitmap = *bitmaps[i];
330 delete bitmaps[i];
331 } else {
332 LoadImageOnBlockingPool(*it, &bitmap);
333 }
334
335 // If the image failed to load, skip it.
336 if (bitmap.isNull() || bitmap.empty())
337 continue;
338
339 gfx::Size original_size(bitmap.width(), bitmap.height());
340 bitmap = ResizeIfNeeded(bitmap, *it);
341
342 load_result.push_back(LoadResult(bitmap, original_size, *it));
343 }
344
345 gfx::Image image;
346
347 if (!image_skia.isNull()) {
348 image_skia.MakeThreadSafe();
349 image = gfx::Image(image_skia);
350 }
351
352 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
353 base::Bind(&ImageLoader::ReplyBack,
354 base::Unretained(this), load_result,
355 load_info, callback));
356 }
357
358 void ImageLoader::ReplyBack(
359 const std::vector<LoadResult>& load_result,
360 PendingLoadInfo* load_info,
361 const base::Callback<void(const gfx::Image&)>& callback) {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363
364 gfx::ImageSkia image_skia;
365
366 for (std::vector<LoadResult>::const_iterator it = load_result.begin();
367 it != load_result.end(); ++it) {
368 const SkBitmap& bitmap = it->bitmap;
369 const gfx::Size& original_size = it->original_size;
370 const ImageRepresentation& image_rep = it->image_representation;
371
372 image_skia.AddRepresentation(gfx::ImageSkiaRep(
373 bitmap, image_rep.scale_factor));
374
375 // maybe cache
376 if (load_info && load_info->save_to_cache) {
377 const FilePath& path = image_rep.resource.relative_path();
378 gfx::Size actual_size(bitmap.width(), bitmap.height());
379 std::string location;
380 if (actual_size != original_size)
381 location = SizeToString(actual_size);
382 SetCachedImageImpl(load_info->extension_id,
383 ImageCacheKey(path, location), bitmap);
384 }
385 }
386
387 if (load_info)
388 pending_.erase(load_info);
389
390 delete load_info;
391
392 gfx::Image image;
393 if (!image_skia.isNull()) {
394 image_skia.MakeThreadSafe();
395 image = gfx::Image(image_skia);
396 }
397
398 callback.Run(image);
399 }
400
401 void ImageLoader::Observe(int type,
402 const content::NotificationSource& source,
403 const content::NotificationDetails& details) {
419 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED); 404 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED);
405 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
420 406
421 const Extension* extension = 407 const Extension* extension =
422 content::Details<extensions::UnloadedExtensionInfo>(details)->extension; 408 content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
409 const std::string& extension_id = extension->id();
423 410
424 // Remove reference to this extension from all pending load entries. This 411 // Remove reference to this extension from all pending load entries. This
425 // ensures we don't attempt to cache the bitmap when the load completes. 412 // ensures we don't attempt to cache the bitmap when the load completes.
426 for (LoadMap::iterator i = load_map_.begin(); i != load_map_.end(); ++i) { 413 for (LoadSet::iterator i = pending_.begin(); i != pending_.end(); ++i) {
427 PendingLoadInfo* info = &i->second; 414 PendingLoadInfo* info = *i;
428 if (info->extension == extension) { 415 if (info->extension_id == extension_id) {
429 info->extension = NULL; 416 info->save_to_cache = false;
430 info->cache = DONT_CACHE; 417 }
431 } 418 }
432 } 419 }
433 } 420
421 void ImageLoader::SetCachedImageImpl(const std::string& extension_id,
422 const ImageCacheKey& key,
423 const SkBitmap& image) {
424 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
425 image_cache_[extension_id][key] = image;
426 }
427
428 const SkBitmap* ImageLoader::GetCachedImage(const ExtensionResource& source,
429 const gfx::Size& max_size) const {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431
432 std::map<std::string, ImageCache>::const_iterator it =
433 image_cache_.find(source.extension_id());
434 if (it == image_cache_.end())
435 return NULL;
436
437 const ImageCache& cache = it->second;
438
439 const FilePath& path = source.relative_path();
440
441 // Look for exact size match.
442 ImageCache::const_iterator i = cache.find(
443 ImageCacheKey(path, SizeToString(max_size)));
444 if (i != cache.end())
445 return &(i->second);
446
447 // If we have the original size version cached, return that if it's small
448 // enough.
449 i = cache.find(ImageCacheKey(path, std::string()));
450 if (i != cache.end()) {
451 const SkBitmap& image = i->second;
452 if (image.width() <= max_size.width() &&
453 image.height() <= max_size.height()) {
454 return &(i->second);
455 }
456 }
457
458 return NULL;
459 }
460
461 bool ImageLoader::ShouldCacheImage(
462 const ImageRepresentation& image_info) const {
463 return image_info.desired_size.width() == image_info.desired_size.height() &&
464 image_sizes_to_cache_.find(image_info.desired_size.width()) !=
465 image_sizes_to_cache_.end();
466 }
467
468 void ImageLoader::SetImageSizesToCache(const std::set<int> image_sizes_) {
469 image_sizes_to_cache_ = image_sizes_;
470 }
471
472 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698