1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "extensions/browser/image_loader.h" 6 7 #include <map> 8 #include <vector> 9 10 #include "base/callback.h" 11 #include "base/compiler_specific.h" 12 #include "base/files/file_util.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/threading/sequenced_worker_pool.h" 15 #include "content/public/browser/browser_thread.h" 16 #include "extensions/browser/component_extension_resource_manager.h" 17 #include "extensions/browser/extensions_browser_client.h" 18 #include "extensions/browser/image_loader_factory.h" 19 #include "extensions/common/extension.h" 20 #include "skia/ext/image_operations.h" 21 #include "ui/base/resource/resource_bundle.h" 22 #include "ui/gfx/codec/png_codec.h" 23 #include "ui/gfx/image/image_family.h" 24 #include "ui/gfx/image/image_skia.h" 25 26 using content::BrowserThread; 27 using extensions::Extension; 28 using extensions::ExtensionsBrowserClient; 29 using extensions::ImageLoader; 30 using extensions::Manifest; 31 32 namespace { 33 34 bool ShouldResizeImageRepresentation( 35 ImageLoader::ImageRepresentation::ResizeCondition resize_method, 36 const gfx::Size& decoded_size, 37 const gfx::Size& desired_size) { 38 switch (resize_method) { 39 case ImageLoader::ImageRepresentation::ALWAYS_RESIZE: 40 return decoded_size != desired_size; 41 case ImageLoader::ImageRepresentation::RESIZE_WHEN_LARGER: 42 return decoded_size.width() > desired_size.width() || 43 decoded_size.height() > desired_size.height(); 44 case ImageLoader::ImageRepresentation::NEVER_RESIZE: 45 return false; 46 default: 47 NOTREACHED(); 48 return false; 49 } 50 } 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_CURRENTLY_ON(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 base::FilePath path = image_info.resource.GetFilePath(); 82 if (path.empty() || !base::ReadFileToString(path, &file_contents)) { 83 return; 84 } 85 86 const unsigned char* data = 87 reinterpret_cast<const unsigned char*>(file_contents.data()); 88 // Note: This class only decodes bitmaps from extension resources. Chrome 89 // doesn't (for security reasons) directly load extension resources provided 90 // by the extension author, but instead decodes them in a separate 91 // locked-down utility process. Only if the decoding succeeds is the image 92 // saved from memory to disk and subsequently used in the Chrome UI. 93 // Chrome is therefore decoding bitmaps here that were generated by Chrome. 94 gfx::PNGCodec::Decode(data, file_contents.length(), bitmap); 95 } 96 97 std::vector<SkBitmap> LoadResourceBitmaps( 98 const Extension* extension, 99 const std::vector<ImageLoader::ImageRepresentation>& info_list) { 100 // Loading resources has to happen on the UI thread. So do this first, and 101 // pass the rest of the work off as a blocking pool task. 102 std::vector<SkBitmap> bitmaps; 103 bitmaps.resize(info_list.size()); 104 105 int i = 0; 106 for (std::vector<ImageLoader::ImageRepresentation>::const_iterator 107 it = info_list.begin(); 108 it != info_list.end(); 109 ++it, ++i) { 110 DCHECK(it->resource.relative_path().empty() || 111 extension->path() == it->resource.extension_root()); 112 113 int resource_id; 114 if (extension->location() == Manifest::COMPONENT) { 115 extensions::ComponentExtensionResourceManager* manager = 116 extensions::ExtensionsBrowserClient::Get()-> 117 GetComponentExtensionResourceManager(); 118 if (manager && manager->IsComponentExtensionResource( 119 extension->path(), it->resource.relative_path(), &resource_id)) { 120 LoadResourceOnUIThread(resource_id, &bitmaps[i]); 121 } 122 } 123 } 124 return bitmaps; 125 } 126 127 } // namespace 128 129 namespace extensions { 130 131 //////////////////////////////////////////////////////////////////////////////// 132 // ImageLoader::ImageRepresentation 133 134 ImageLoader::ImageRepresentation::ImageRepresentation( 135 const ExtensionResource& resource, 136 ResizeCondition resize_condition, 137 const gfx::Size& desired_size, 138 ui::ScaleFactor scale_factor) 139 : resource(resource), 140 resize_condition(resize_condition), 141 desired_size(desired_size), 142 scale_factor(scale_factor) { 143 } 144 145 ImageLoader::ImageRepresentation::~ImageRepresentation() { 146 } 147 148 //////////////////////////////////////////////////////////////////////////////// 149 // ImageLoader::LoadResult 150 151 struct ImageLoader::LoadResult { 152 LoadResult(const SkBitmap& bitmap, 153 const gfx::Size& original_size, 154 const ImageRepresentation& image_representation); 155 ~LoadResult(); 156 157 SkBitmap bitmap; 158 gfx::Size original_size; 159 ImageRepresentation image_representation; 160 }; 161 162 ImageLoader::LoadResult::LoadResult( 163 const SkBitmap& bitmap, 164 const gfx::Size& original_size, 165 const ImageLoader::ImageRepresentation& image_representation) 166 : bitmap(bitmap), 167 original_size(original_size), 168 image_representation(image_representation) { 169 } 170 171 ImageLoader::LoadResult::~LoadResult() { 172 } 173 174 namespace { 175 176 // Need to be after ImageRepresentation and LoadResult are defined. 177 std::vector<ImageLoader::LoadResult> LoadImagesOnBlockingPool( 178 const std::vector<ImageLoader::ImageRepresentation>& info_list, 179 const std::vector<SkBitmap>& bitmaps) { 180 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 181 std::vector<ImageLoader::LoadResult> load_result; 182 183 for (size_t i = 0; i < info_list.size(); ++i) { 184 const ImageLoader::ImageRepresentation& image = info_list[i]; 185 186 // If we don't have a path there isn't anything we can do, just skip it. 187 if (image.resource.relative_path().empty()) 188 continue; 189 190 SkBitmap bitmap; 191 if (bitmaps[i].isNull()) 192 LoadImageOnBlockingPool(image, &bitmap); 193 else 194 bitmap = bitmaps[i]; 195 196 // If the image failed to load, skip it. 197 if (bitmap.isNull() || bitmap.empty()) 198 continue; 199 200 gfx::Size original_size(bitmap.width(), bitmap.height()); 201 bitmap = ResizeIfNeeded(bitmap, image); 202 203 load_result.push_back( 204 ImageLoader::LoadResult(bitmap, original_size, image)); 205 } 206 207 return load_result; 208 } 209 210 } // namespace 211 212 //////////////////////////////////////////////////////////////////////////////// 213 // ImageLoader 214 215 ImageLoader::ImageLoader() 216 : weak_ptr_factory_(this) { 217 } 218 219 ImageLoader::~ImageLoader() { 220 } 221 222 // static 223 ImageLoader* ImageLoader::Get(content::BrowserContext* context) { 224 return ImageLoaderFactory::GetForBrowserContext(context); 225 } 226 227 void ImageLoader::LoadImageAsync(const Extension* extension, 228 const ExtensionResource& resource, 229 const gfx::Size& max_size, 230 const ImageLoaderImageCallback& callback) { 231 std::vector<ImageRepresentation> info_list; 232 info_list.push_back(ImageRepresentation( 233 resource, 234 ImageRepresentation::RESIZE_WHEN_LARGER, 235 max_size, 236 ui::SCALE_FACTOR_100P)); 237 LoadImagesAsync(extension, info_list, callback); 238 } 239 240 void ImageLoader::LoadImagesAsync( 241 const Extension* extension, 242 const std::vector<ImageRepresentation>& info_list, 243 const ImageLoaderImageCallback& callback) { 244 DCHECK_CURRENTLY_ON(BrowserThread::UI); 245 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 246 base::PostTaskAndReplyWithResult( 247 BrowserThread::GetBlockingPool(), 248 FROM_HERE, 249 base::Bind(LoadImagesOnBlockingPool, 250 info_list, 251 LoadResourceBitmaps(extension, info_list)), 252 base::Bind( 253 &ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(), callback)); 254 } 255 256 void ImageLoader::LoadImageFamilyAsync( 257 const extensions::Extension* extension, 258 const std::vector<ImageRepresentation>& info_list, 259 const ImageLoaderImageFamilyCallback& callback) { 260 DCHECK_CURRENTLY_ON(BrowserThread::UI); 261 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 262 base::PostTaskAndReplyWithResult( 263 BrowserThread::GetBlockingPool(), 264 FROM_HERE, 265 base::Bind(LoadImagesOnBlockingPool, 266 info_list, 267 LoadResourceBitmaps(extension, info_list)), 268 base::Bind(&ImageLoader::ReplyBackWithImageFamily, 269 weak_ptr_factory_.GetWeakPtr(), 270 callback)); 271 } 272 273 void ImageLoader::ReplyBack(const ImageLoaderImageCallback& callback, 274 const std::vector<LoadResult>& load_result) { 275 DCHECK_CURRENTLY_ON(BrowserThread::UI); 276 277 gfx::ImageSkia image_skia; 278 279 for (std::vector<LoadResult>::const_iterator it = load_result.begin(); 280 it != load_result.end(); ++it) { 281 const SkBitmap& bitmap = it->bitmap; 282 const ImageRepresentation& image_rep = it->image_representation; 283 284 image_skia.AddRepresentation(gfx::ImageSkiaRep( 285 bitmap, 286 ui::GetScaleForScaleFactor(image_rep.scale_factor))); 287 } 288 289 gfx::Image image; 290 if (!image_skia.isNull()) { 291 image_skia.MakeThreadSafe(); 292 image = gfx::Image(image_skia); 293 } 294 295 callback.Run(image); 296 } 297 298 void ImageLoader::ReplyBackWithImageFamily( 299 const ImageLoaderImageFamilyCallback& callback, 300 const std::vector<LoadResult>& load_result) { 301 DCHECK_CURRENTLY_ON(BrowserThread::UI); 302 303 std::map<std::pair<int, int>, gfx::ImageSkia> image_skia_map; 304 gfx::ImageFamily image_family; 305 306 for (std::vector<LoadResult>::const_iterator it = load_result.begin(); 307 it != load_result.end(); 308 ++it) { 309 const SkBitmap& bitmap = it->bitmap; 310 const ImageRepresentation& image_rep = it->image_representation; 311 const std::pair<int, int> key = std::make_pair( 312 image_rep.desired_size.width(), image_rep.desired_size.height()); 313 // Create a new ImageSkia for this width/height, or add a representation to 314 // an existing ImageSkia with the same width/height. 315 image_skia_map[key].AddRepresentation( 316 gfx::ImageSkiaRep(bitmap, 317 ui::GetScaleForScaleFactor(image_rep.scale_factor))); 318 } 319 320 for (std::map<std::pair<int, int>, gfx::ImageSkia>::iterator it = 321 image_skia_map.begin(); 322 it != image_skia_map.end(); 323 ++it) { 324 it->second.MakeThreadSafe(); 325 image_family.Add(it->second); 326 } 327 328 callback.Run(image_family); 329 } 330 331 } // namespace extensions 332