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

Side by Side Diff: chrome/browser/extensions/image_loader.cc

Issue 25050005: Refactored loading of applications / extensions icons. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added unit tests. Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/image_loader.h" 5 #include "chrome/browser/extensions/image_loader.h"
6 6
7 #include <map> 7 #include <map>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/base64.h"
10 #include "base/callback.h" 11 #include "base/callback.h"
11 #include "base/compiler_specific.h" 12 #include "base/compiler_specific.h"
12 #include "base/file_util.h" 13 #include "base/file_util.h"
13 #include "base/lazy_instance.h" 14 #include "base/lazy_instance.h"
14 #include "base/path_service.h" 15 #include "base/path_service.h"
15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/sequenced_worker_pool.h" 17 #include "base/threading/sequenced_worker_pool.h"
17 #include "chrome/browser/extensions/image_loader_factory.h" 18 #include "chrome/browser/extensions/image_loader_factory.h"
19 #include "chrome/browser/favicon/favicon_service.h"
20 #include "chrome/browser/favicon/favicon_service_factory.h"
18 #include "chrome/common/chrome_paths.h" 21 #include "chrome/common/chrome_paths.h"
19 #include "chrome/common/extensions/extension.h" 22 #include "chrome/common/extensions/extension.h"
23 #include "chrome/common/extensions/extension_icon_set.h"
24 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
25 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
20 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/common/url_constants.h"
21 #include "grit/chrome_unscaled_resources.h" 28 #include "grit/chrome_unscaled_resources.h"
22 #include "grit/component_extension_resources_map.h" 29 #include "grit/component_extension_resources_map.h"
23 #include "grit/theme_resources.h" 30 #include "grit/theme_resources.h"
24 #include "skia/ext/image_operations.h" 31 #include "skia/ext/image_operations.h"
25 #include "ui/base/resource/resource_bundle.h" 32 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/gfx/codec/png_codec.h" 33 #include "ui/gfx/codec/png_codec.h"
34 #include "ui/gfx/color_utils.h"
35 #include "ui/gfx/favicon_size.h"
27 #include "ui/gfx/image/image_skia.h" 36 #include "ui/gfx/image/image_skia.h"
37 #include "ui/gfx/size.h"
38 #include "ui/gfx/skbitmap_operations.h"
28 39
29 #if defined(USE_AURA) 40 #if defined(USE_AURA)
30 #include "ui/keyboard/keyboard_util.h" 41 #include "ui/keyboard/keyboard_util.h"
31 #endif 42 #endif
32 43
33 using content::BrowserThread; 44 using content::BrowserThread;
34 using extensions::Extension; 45 using extensions::Extension;
35 using extensions::ImageLoader; 46 using extensions::ImageLoader;
36 using extensions::Manifest; 47 using extensions::Manifest;
37 48
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 base::FilePath resource_path = base::FilePath().AppendASCII( 119 base::FilePath resource_path = base::FilePath().AppendASCII(
109 entries[i].name); 120 entries[i].name);
110 resource_path = resource_path.NormalizePathSeparators(); 121 resource_path = resource_path.NormalizePathSeparators();
111 122
112 DCHECK(path_to_resource_id->find(resource_path) == 123 DCHECK(path_to_resource_id->find(resource_path) ==
113 path_to_resource_id->end()); 124 path_to_resource_id->end());
114 (*path_to_resource_id)[resource_path] = entries[i].value; 125 (*path_to_resource_id)[resource_path] = entries[i].value;
115 } 126 }
116 } 127 }
117 128
129 // Returns a PNG image data URL of a given image.
130 GURL GetImageDataURL(const gfx::Image& image) {
131 if (image.IsEmpty())
132 return GURL();
133
134 scoped_refptr<base::RefCountedMemory> mem =
135 ImageLoader::BitmapToMemory(image.ToSkBitmap());
136 std::string contents_base64;
137 if (!base::Base64Encode(
138 std::string(reinterpret_cast<const char*>(mem->front()), mem->size()),
139 &contents_base64))
140 return GURL();
141
142 const char kDataURLPrefix[] = ":image/png;base64,";
143 return GURL(std::string(chrome::kDataScheme) + kDataURLPrefix +
144 contents_base64);
145 }
146
147 // Converts the image to grayscale.
148 SkBitmap DesaturateImage(const SkBitmap* image) {
149 color_utils::HSL shift = {-1, 0, 0.6};
150 return SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
151 }
152
153 // Creates an image from PNG data.
154 SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
155 SkBitmap* decoded = new SkBitmap();
156 bool success = gfx::PNGCodec::Decode(data, size, decoded);
157 DCHECK(success);
158 return decoded;
159 }
160
161 // Load an image from a resource given its identifier |resource_id|.
162 SkBitmap* GetImageByResourceId(int resource_id) {
163 std::string contents = ResourceBundle::GetSharedInstance()
164 .GetRawDataResourceForScale(resource_id,
165 ui::SCALE_FACTOR_100P).as_string();
166
167 // Convert and return it.
168 const unsigned char* data =
169 reinterpret_cast<const unsigned char*>(contents.data());
170 return ToBitmap(data, contents.length());
171 }
172
118 } // namespace 173 } // namespace
119 174
120 namespace extensions { 175 namespace extensions {
121 176
122 //////////////////////////////////////////////////////////////////////////////// 177 ////////////////////////////////////////////////////////////////////////////////
123 // ImageLoader::ImageRepresentation 178 // ImageLoader::ImageRepresentation
124 179
125 ImageLoader::ImageRepresentation::ImageRepresentation( 180 ImageLoader::ImageRepresentation::ImageRepresentation(
126 const ExtensionResource& resource, 181 const ExtensionResource& resource,
127 ResizeCondition resize_condition, 182 ResizeCondition resize_condition,
(...skipping 30 matching lines...) Expand all
158 original_size(original_size), 213 original_size(original_size),
159 image_representation(image_representation) { 214 image_representation(image_representation) {
160 } 215 }
161 216
162 ImageLoader::LoadResult::~LoadResult() { 217 ImageLoader::LoadResult::~LoadResult() {
163 } 218 }
164 219
165 //////////////////////////////////////////////////////////////////////////////// 220 ////////////////////////////////////////////////////////////////////////////////
166 // ImageLoader 221 // ImageLoader
167 222
168 ImageLoader::ImageLoader() 223 ImageLoader::ImageLoader(Profile* profile)
169 : weak_ptr_factory_(this) { 224 : weak_ptr_factory_(this), profile_(profile) {
170 } 225 }
171 226
172 ImageLoader::~ImageLoader() { 227 ImageLoader::~ImageLoader() {
173 } 228 }
174 229
175 // static 230 // static
176 ImageLoader* ImageLoader::Get(Profile* profile) { 231 ImageLoader* ImageLoader::Get(Profile* profile) {
177 return ImageLoaderFactory::GetForProfile(profile); 232 return ImageLoaderFactory::GetForProfile(profile);
178 } 233 }
179 234
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 395
341 gfx::Image image; 396 gfx::Image image;
342 if (!image_skia.isNull()) { 397 if (!image_skia.isNull()) {
343 image_skia.MakeThreadSafe(); 398 image_skia.MakeThreadSafe();
344 image = gfx::Image(image_skia); 399 image = gfx::Image(image_skia);
345 } 400 }
346 401
347 callback.Run(image); 402 callback.Run(image);
348 } 403 }
349 404
405 void ImageLoader::LoadExtensionIconAsync(
406 const extensions::Extension* extension,
407 int icon_size,
408 bool grayscale,
409 const base::Callback<void(const gfx::Image&)>& callback) {
410 // |extension| can be NULL. In this case, force loading of the default icon.
Finnur 2013/10/02 09:52:15 Originally I asked when it can be NULL. That part
dvh-g 2013/10/02 15:08:13 Done.
411 if (extension == NULL) {
412 LoadIconFailed(extension, icon_size, grayscale, callback);
413 return;
414 }
415
416 ExtensionResource resource = IconsInfo::GetIconResource(
417 extension, icon_size, ExtensionIconSet::MATCH_BIGGER);
418 LoadImageAsync(extension,
419 resource,
420 gfx::Size(icon_size, icon_size),
421 base::Bind(&ImageLoader::LoadExtensionIconDone,
422 base::Unretained(this),
423 extension,
424 icon_size,
425 grayscale,
426 callback));
427 }
428
429 void ImageLoader::LoadExtensionIconDone(
430 const extensions::Extension* extension,
431 int icon_size,
432 bool grayscale,
433 const base::Callback<void(const gfx::Image&)>& callback,
434 const gfx::Image& image) {
435 if (image.IsEmpty())
436 LoadIconFailed(extension, icon_size, grayscale, callback);
437 else
438 FinalizeImage(image, grayscale, callback);
439 }
440
441 void ImageLoader::LoadIconFailed(
442 const extensions::Extension* extension,
443 int icon_size,
444 bool grayscale,
445 const base::Callback<void(const gfx::Image&)>& callback) {
446 if ((extension != NULL) &&
447 (icon_size == extension_misc::EXTENSION_ICON_BITTY))
448 LoadFaviconImage(extension, icon_size, grayscale, callback);
449 else
450 LoadDefaultImage(extension, icon_size, grayscale, callback);
451 }
452
453 void ImageLoader::LoadFaviconImage(
454 const extensions::Extension* extension,
455 int icon_size,
456 bool grayscale,
457 const base::Callback<void(const gfx::Image&)>& callback) {
458 FaviconService* favicon_service = NULL;
459 if (profile_ != NULL)
460 favicon_service = FaviconServiceFactory::GetForProfile(
461 profile_, Profile::EXPLICIT_ACCESS);
462 // Fall back to the default icons if the service isn't available.
463 if (favicon_service == NULL) {
464 LoadDefaultImage(extension, icon_size, grayscale, callback);
465 return;
466 }
467
468 GURL favicon_url = AppLaunchInfo::GetFullLaunchURL(extension);
469 favicon_service->GetRawFaviconForURL(
470 FaviconService::FaviconForURLParams(
471 profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize),
472 ui::SCALE_FACTOR_100P,
473 base::Bind(&ImageLoader::OnFaviconDataAvailable,
474 base::Unretained(this),
475 extension,
476 icon_size,
477 grayscale,
478 callback),
479 &cancelable_task_tracker_);
480 }
481
482 void ImageLoader::OnFaviconDataAvailable(
483 const extensions::Extension* extension,
484 int icon_size,
485 bool grayscale,
486 const base::Callback<void(const gfx::Image&)>& callback,
487 const chrome::FaviconBitmapResult& bitmap_result) {
488 // Fallback to the default icon if there wasn't a favicon.
489 if (!bitmap_result.is_valid()) {
490 LoadDefaultImage(extension, icon_size, grayscale, callback);
491 return;
492 }
493
494 gfx::Image image = gfx::Image::CreateFrom1xBitmap(*ToBitmap(
495 bitmap_result.bitmap_data->front(), bitmap_result.bitmap_data->size()));
496 FinalizeImage(image, grayscale, callback);
497 }
498
499 void ImageLoader::LoadDefaultImage(
500 const extensions::Extension* extension,
501 int icon_size,
502 bool grayscale,
503 const base::Callback<void(const gfx::Image&)>& callback) {
504 content::BrowserThread::PostTask(
505 content::BrowserThread::FILE,
506 FROM_HERE,
507 base::Bind(&ImageLoader::LoadDefaultImageOnFileThread,
508 base::Unretained(this),
509 extension,
510 icon_size,
511 grayscale,
512 callback));
513 }
514
515 void ImageLoader::LoadDefaultImageOnFileThread(
516 const extensions::Extension* extension,
517 int icon_size,
518 bool grayscale,
519 const base::Callback<void(const gfx::Image&)>& callback) {
520 const SkBitmap* default_image = NULL;
521 if ((extension == NULL) || (extension->is_app()))
522 default_image = GetDefaultAppImage();
523 else
524 default_image = GetDefaultExtensionImage();
525
526 SkBitmap result_image;
527 if (icon_size == -1) {
528 // If a specific size was not requested.
529 result_image = *default_image;
530 } else {
531 SkBitmap resized_image(
532 skia::ImageOperations::Resize(*default_image,
533 skia::ImageOperations::RESIZE_LANCZOS3,
534 icon_size,
535 icon_size));
536 // There are cases where Resize returns an empty bitmap, for example if you
537 // ask for an image too large. In this case it is better to return the
538 // default image than returning nothing at all.
539 if (resized_image.empty())
540 resized_image = *default_image;
541 result_image = resized_image;
542 }
543
544 content::BrowserThread::PostTask(
545 content::BrowserThread::UI,
546 FROM_HERE,
547 base::Bind(&ImageLoader::LoadDefaultImageDone,
548 base::Unretained(this),
549 gfx::Image::CreateFrom1xBitmap(result_image),
550 grayscale,
551 callback));
552 }
553
554 void ImageLoader::LoadDefaultImageDone(
555 const gfx::Image& image,
556 bool grayscale,
557 const base::Callback<void(const gfx::Image&)>& callback) {
558 FinalizeImage(image, grayscale, callback);
559 }
560
561 void ImageLoader::FinalizeImage(
562 const gfx::Image& image,
563 bool grayscale,
564 const base::Callback<void(const gfx::Image&)>& callback) {
565 content::BrowserThread::PostTask(
566 content::BrowserThread::FILE,
567 FROM_HERE,
568 base::Bind(&ImageLoader::FinalizeImageOnFileThread,
569 base::Unretained(this),
570 image,
571 grayscale,
572 callback));
573 }
574
575 void ImageLoader::FinalizeImageOnFileThread(
576 const gfx::Image& image,
577 bool grayscale,
578 const base::Callback<void(const gfx::Image&)>& callback) {
579 SkBitmap bitmap;
580 if (grayscale)
581 bitmap = DesaturateImage(image.ToSkBitmap());
582 else
583 bitmap = *image.ToSkBitmap();
584
585 gfx::Image modifiedImage = gfx::Image::CreateFrom1xBitmap(bitmap);
586 content::BrowserThread::PostTask(content::BrowserThread::UI,
587 FROM_HERE,
588 base::Bind(&ImageLoader::FinalizeImageDone,
589 base::Unretained(this),
590 modifiedImage,
591 callback));
592 }
593
594 void ImageLoader::FinalizeImageDone(
595 const gfx::Image& image,
596 const base::Callback<void(const gfx::Image&)>& callback) {
597 callback.Run(image);
598 }
599
600 void ImageLoader::LoadExtensionIconDataURLAsync(
601 const extensions::Extension* extension,
602 int icon_size,
603 bool grayscale,
604 const base::Callback<void(const GURL&)>& callback) {
605 LoadExtensionIconAsync(extension,
606 icon_size,
607 grayscale,
608 base::Bind(&ImageLoader::OnIconAvailable,
609 base::Unretained(this),
610 callback));
611 }
612
613 void ImageLoader::OnIconAvailable(
614 const base::Callback<void(const GURL&)>& callback,
615 const gfx::Image& image) {
616 content::BrowserThread::PostTask(
617 content::BrowserThread::FILE,
618 FROM_HERE,
619 base::Bind(&ImageLoader::ConvertIconToURLOnFileThread,
620 base::Unretained(this),
621 image,
622 callback));
623 }
624
625 void ImageLoader::ConvertIconToURLOnFileThread(
626 const gfx::Image& image,
627 const base::Callback<void(const GURL&)>& callback) {
628 GURL url = GetImageDataURL(image);
629 content::BrowserThread::PostTask(
630 content::BrowserThread::UI,
631 FROM_HERE,
632 base::Bind(&ImageLoader::OnIconConvertedToURL,
633 base::Unretained(this),
634 url,
635 callback));
636 }
637
638 void ImageLoader::OnIconConvertedToURL(
639 const GURL& url,
640 const base::Callback<void(const GURL&)>& callback) {
641 callback.Run(url);
642 }
643
644 const SkBitmap* ImageLoader::GetDefaultAppImage() {
645 if (!default_app_icon_.get())
646 default_app_icon_.reset(GetImageByResourceId(IDR_APP_DEFAULT_ICON));
647
648 return default_app_icon_.get();
649 }
650
651 const SkBitmap* ImageLoader::GetDefaultExtensionImage() {
652 if (!default_extension_icon_.get()) {
653 default_extension_icon_.reset(
654 GetImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
655 }
656
657 return default_extension_icon_.get();
658 }
659
660 scoped_refptr<base::RefCountedMemory> ImageLoader::BitmapToMemory(
661 const SkBitmap* image) {
662 base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
663 gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
664 return image_bytes;
665 }
666
350 } // namespace extensions 667 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698