Chromium Code Reviews| 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 |