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] = 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 | |
OLD | NEW |