OLD | NEW |
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 Loading... |
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] = *bitmap; |
| 262 ++loadedFromCache; |
| 263 continue; |
| 264 } |
| 265 |
| 266 int resource_id; |
| 267 if (IsComponentExtensionResource(extension, it->resource.relative_path(), |
| 268 &resource_id)) { |
| 269 LoadResourceOnUIThread(resource_id, &bitmaps[i]); |
| 270 } |
| 271 } |
| 272 |
| 273 if (loadedFromCache == info_list.size()) { |
| 274 gfx::ImageSkia image_skia; |
| 275 |
| 276 for (unsigned i = 0; i < info_list.size(); ++i) { |
| 277 image_skia.AddRepresentation(gfx::ImageSkiaRep( |
| 278 bitmaps[i], info_list[i].scale_factor)); |
| 279 } |
| 280 |
400 gfx::Image image; | 281 gfx::Image image; |
401 std::string extension_id = info->extension_id; | 282 if (!image_skia.isNull()) { |
402 | 283 image_skia.MakeThreadSafe(); |
403 if (!info->image_skia.isNull()) { | 284 image = gfx::Image(image_skia); |
404 info->image_skia.MakeThreadSafe(); | 285 } |
405 image = gfx::Image(info->image_skia); | 286 |
406 } | 287 callback.Run(image); |
407 | 288 return; |
408 load_map_.erase(load_map_it); | 289 } |
409 | 290 |
410 // ImageLoadingTracker might be deleted after the callback so don't do | 291 PendingLoadInfo* load_info = NULL; |
411 // anything after this statement. | 292 if (should_cache) { |
412 observer_->OnImageLoaded(image, extension_id, id); | 293 load_info = new PendingLoadInfo; |
413 } | 294 load_info->extension_id = extension->id(); |
414 } | 295 load_info->save_to_cache = true; |
415 | 296 pending_.insert(load_info); |
416 void ImageLoadingTracker::Observe(int type, | 297 } |
417 const content::NotificationSource& source, | 298 |
418 const content::NotificationDetails& details) { | 299 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| 300 BrowserThread::PostBlockingPoolTask( |
| 301 FROM_HERE, |
| 302 base::Bind(&ImageLoader::LoadImagesOnBlockingPool, base::Unretained(this), |
| 303 info_list, bitmaps, load_info, callback)); |
| 304 } |
| 305 |
| 306 void ImageLoader::LoadImagesOnBlockingPool( |
| 307 const std::vector<ImageRepresentation>& info_list, |
| 308 const std::vector<SkBitmap>& bitmaps, |
| 309 PendingLoadInfo* load_info, |
| 310 const base::Callback<void(const gfx::Image&)>& callback) { |
| 311 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| 312 |
| 313 gfx::ImageSkia image_skia; |
| 314 |
| 315 std::vector<LoadResult> load_result; |
| 316 |
| 317 int i = 0; |
| 318 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin(); |
| 319 it != info_list.end(); ++it, ++i) { |
| 320 // If we don't have a path there isn't anything we can do, just skip it. |
| 321 if (it->resource.relative_path().empty()) |
| 322 continue; |
| 323 |
| 324 SkBitmap bitmap; |
| 325 if (!bitmaps[i].isNull()) { |
| 326 bitmap = bitmaps[i]; |
| 327 } else { |
| 328 LoadImageOnBlockingPool(*it, &bitmap); |
| 329 } |
| 330 |
| 331 // If the image failed to load, skip it. |
| 332 if (bitmap.isNull() || bitmap.empty()) |
| 333 continue; |
| 334 |
| 335 gfx::Size original_size(bitmap.width(), bitmap.height()); |
| 336 bitmap = ResizeIfNeeded(bitmap, *it); |
| 337 |
| 338 load_result.push_back(LoadResult(bitmap, original_size, *it)); |
| 339 } |
| 340 |
| 341 gfx::Image image; |
| 342 |
| 343 if (!image_skia.isNull()) { |
| 344 image_skia.MakeThreadSafe(); |
| 345 image = gfx::Image(image_skia); |
| 346 } |
| 347 |
| 348 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 349 base::Bind(&ImageLoader::ReplyBack, |
| 350 base::Unretained(this), load_result, |
| 351 load_info, callback)); |
| 352 } |
| 353 |
| 354 void ImageLoader::ReplyBack( |
| 355 const std::vector<LoadResult>& load_result, |
| 356 PendingLoadInfo* load_info, |
| 357 const base::Callback<void(const gfx::Image&)>& callback) { |
| 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 359 |
| 360 gfx::ImageSkia image_skia; |
| 361 |
| 362 for (std::vector<LoadResult>::const_iterator it = load_result.begin(); |
| 363 it != load_result.end(); ++it) { |
| 364 const SkBitmap& bitmap = it->bitmap; |
| 365 const gfx::Size& original_size = it->original_size; |
| 366 const ImageRepresentation& image_rep = it->image_representation; |
| 367 |
| 368 image_skia.AddRepresentation(gfx::ImageSkiaRep( |
| 369 bitmap, image_rep.scale_factor)); |
| 370 |
| 371 // maybe cache |
| 372 if (load_info && load_info->save_to_cache) { |
| 373 const FilePath& path = image_rep.resource.relative_path(); |
| 374 gfx::Size actual_size(bitmap.width(), bitmap.height()); |
| 375 std::string location; |
| 376 if (actual_size != original_size) |
| 377 location = SizeToString(actual_size); |
| 378 SetCachedImageImpl(load_info->extension_id, |
| 379 ImageCacheKey(path, location), bitmap); |
| 380 } |
| 381 } |
| 382 |
| 383 if (load_info) |
| 384 pending_.erase(load_info); |
| 385 |
| 386 delete load_info; |
| 387 |
| 388 gfx::Image image; |
| 389 if (!image_skia.isNull()) { |
| 390 image_skia.MakeThreadSafe(); |
| 391 image = gfx::Image(image_skia); |
| 392 } |
| 393 |
| 394 callback.Run(image); |
| 395 } |
| 396 |
| 397 void ImageLoader::Observe(int type, |
| 398 const content::NotificationSource& source, |
| 399 const content::NotificationDetails& details) { |
419 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED); | 400 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED); |
| 401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
420 | 402 |
421 const Extension* extension = | 403 const Extension* extension = |
422 content::Details<extensions::UnloadedExtensionInfo>(details)->extension; | 404 content::Details<extensions::UnloadedExtensionInfo>(details)->extension; |
| 405 const std::string& extension_id = extension->id(); |
423 | 406 |
424 // Remove reference to this extension from all pending load entries. This | 407 // 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. | 408 // 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) { | 409 for (LoadSet::iterator i = pending_.begin(); i != pending_.end(); ++i) { |
427 PendingLoadInfo* info = &i->second; | 410 PendingLoadInfo* info = *i; |
428 if (info->extension == extension) { | 411 if (info->extension_id == extension_id) { |
429 info->extension = NULL; | 412 info->save_to_cache = false; |
430 info->cache = DONT_CACHE; | 413 } |
431 } | 414 } |
432 } | 415 } |
433 } | 416 |
| 417 void ImageLoader::SetCachedImageImpl(const std::string& extension_id, |
| 418 const ImageCacheKey& key, |
| 419 const SkBitmap& image) { |
| 420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 421 image_cache_[extension_id][key] = image; |
| 422 } |
| 423 |
| 424 const SkBitmap* ImageLoader::GetCachedImage(const ExtensionResource& source, |
| 425 const gfx::Size& max_size) const { |
| 426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 427 |
| 428 std::map<std::string, ImageCache>::const_iterator it = |
| 429 image_cache_.find(source.extension_id()); |
| 430 if (it == image_cache_.end()) |
| 431 return NULL; |
| 432 |
| 433 const ImageCache& cache = it->second; |
| 434 |
| 435 const FilePath& path = source.relative_path(); |
| 436 |
| 437 // Look for exact size match. |
| 438 ImageCache::const_iterator i = cache.find( |
| 439 ImageCacheKey(path, SizeToString(max_size))); |
| 440 if (i != cache.end()) |
| 441 return &(i->second); |
| 442 |
| 443 // If we have the original size version cached, return that if it's small |
| 444 // enough. |
| 445 i = cache.find(ImageCacheKey(path, std::string())); |
| 446 if (i != cache.end()) { |
| 447 const SkBitmap& image = i->second; |
| 448 if (image.width() <= max_size.width() && |
| 449 image.height() <= max_size.height()) { |
| 450 return &(i->second); |
| 451 } |
| 452 } |
| 453 |
| 454 return NULL; |
| 455 } |
| 456 |
| 457 bool ImageLoader::ShouldCacheImage( |
| 458 const ImageRepresentation& image_info) const { |
| 459 return image_info.desired_size.width() == image_info.desired_size.height() && |
| 460 image_sizes_to_cache_.find(image_info.desired_size.width()) != |
| 461 image_sizes_to_cache_.end(); |
| 462 } |
| 463 |
| 464 void ImageLoader::SetImageSizesToCache(const std::set<int> image_sizes_) { |
| 465 image_sizes_to_cache_ = image_sizes_; |
| 466 } |
| 467 |
| 468 } // namespace extensions |
OLD | NEW |