| 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_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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 ExtensionIconSet::MatchType match, |
| 409 bool grayscale, |
| 410 const base::Callback<void(const gfx::Image&)>& callback) { |
| 411 // |extension| can be NULL when the default icon is requested. In this case, |
| 412 // fail the icon loading, which will result in the default icon being loaded. |
| 413 if (extension == NULL) { |
| 414 LoadIconFailed(extension, icon_size, grayscale, callback); |
| 415 return; |
| 416 } |
| 417 |
| 418 ExtensionResource resource = IconsInfo::GetIconResource( |
| 419 extension, icon_size, match); |
| 420 LoadImageAsync(extension, |
| 421 resource, |
| 422 gfx::Size(icon_size, icon_size), |
| 423 base::Bind(&ImageLoader::LoadExtensionIconDone, |
| 424 base::Unretained(this), |
| 425 extension, |
| 426 icon_size, |
| 427 grayscale, |
| 428 callback)); |
| 429 } |
| 430 |
| 431 void ImageLoader::LoadExtensionIconDone( |
| 432 const extensions::Extension* extension, |
| 433 int icon_size, |
| 434 bool grayscale, |
| 435 const base::Callback<void(const gfx::Image&)>& callback, |
| 436 const gfx::Image& image) { |
| 437 if (image.IsEmpty()) |
| 438 LoadIconFailed(extension, icon_size, grayscale, callback); |
| 439 else |
| 440 FinalizeImage(image, grayscale, callback); |
| 441 } |
| 442 |
| 443 void ImageLoader::LoadIconFailed( |
| 444 const extensions::Extension* extension, |
| 445 int icon_size, |
| 446 bool grayscale, |
| 447 const base::Callback<void(const gfx::Image&)>& callback) { |
| 448 if ((extension != NULL) && |
| 449 (icon_size == extension_misc::EXTENSION_ICON_BITTY)) |
| 450 LoadFaviconImage(extension, icon_size, grayscale, callback); |
| 451 else |
| 452 LoadDefaultImage(extension, icon_size, grayscale, callback); |
| 453 } |
| 454 |
| 455 void ImageLoader::LoadFaviconImage( |
| 456 const extensions::Extension* extension, |
| 457 int icon_size, |
| 458 bool grayscale, |
| 459 const base::Callback<void(const gfx::Image&)>& callback) { |
| 460 FaviconService* favicon_service = NULL; |
| 461 if (profile_ != NULL) |
| 462 favicon_service = FaviconServiceFactory::GetForProfile( |
| 463 profile_, Profile::EXPLICIT_ACCESS); |
| 464 // Fall back to the default icons if the service isn't available. |
| 465 if (favicon_service == NULL) { |
| 466 LoadDefaultImage(extension, icon_size, grayscale, callback); |
| 467 return; |
| 468 } |
| 469 |
| 470 GURL favicon_url = AppLaunchInfo::GetFullLaunchURL(extension); |
| 471 favicon_service->GetRawFaviconForURL( |
| 472 FaviconService::FaviconForURLParams( |
| 473 profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize), |
| 474 ui::SCALE_FACTOR_100P, |
| 475 base::Bind(&ImageLoader::OnFaviconDataAvailable, |
| 476 base::Unretained(this), |
| 477 extension, |
| 478 icon_size, |
| 479 grayscale, |
| 480 callback), |
| 481 &cancelable_task_tracker_); |
| 482 } |
| 483 |
| 484 void ImageLoader::OnFaviconDataAvailable( |
| 485 const extensions::Extension* extension, |
| 486 int icon_size, |
| 487 bool grayscale, |
| 488 const base::Callback<void(const gfx::Image&)>& callback, |
| 489 const chrome::FaviconBitmapResult& bitmap_result) { |
| 490 // Fallback to the default icon if there wasn't a favicon. |
| 491 if (!bitmap_result.is_valid()) { |
| 492 LoadDefaultImage(extension, icon_size, grayscale, callback); |
| 493 return; |
| 494 } |
| 495 |
| 496 gfx::Image image = gfx::Image::CreateFrom1xBitmap(*ToBitmap( |
| 497 bitmap_result.bitmap_data->front(), bitmap_result.bitmap_data->size())); |
| 498 FinalizeImage(image, grayscale, callback); |
| 499 } |
| 500 |
| 501 void ImageLoader::LoadDefaultImage( |
| 502 const extensions::Extension* extension, |
| 503 int icon_size, |
| 504 bool grayscale, |
| 505 const base::Callback<void(const gfx::Image&)>& callback) { |
| 506 content::BrowserThread::PostTask( |
| 507 content::BrowserThread::FILE, |
| 508 FROM_HERE, |
| 509 base::Bind(&ImageLoader::LoadDefaultImageOnFileThread, |
| 510 base::Unretained(this), |
| 511 extension, |
| 512 icon_size, |
| 513 grayscale, |
| 514 callback)); |
| 515 } |
| 516 |
| 517 void ImageLoader::LoadDefaultImageOnFileThread( |
| 518 const extensions::Extension* extension, |
| 519 int icon_size, |
| 520 bool grayscale, |
| 521 const base::Callback<void(const gfx::Image&)>& callback) { |
| 522 const SkBitmap* default_image = NULL; |
| 523 if ((extension == NULL) || (extension->is_app())) |
| 524 default_image = GetDefaultAppImage(); |
| 525 else |
| 526 default_image = GetDefaultExtensionImage(); |
| 527 |
| 528 SkBitmap result_image; |
| 529 if (icon_size == -1) { |
| 530 // If a specific size was not requested. |
| 531 result_image = *default_image; |
| 532 } else { |
| 533 SkBitmap resized_image( |
| 534 skia::ImageOperations::Resize(*default_image, |
| 535 skia::ImageOperations::RESIZE_LANCZOS3, |
| 536 icon_size, |
| 537 icon_size)); |
| 538 // There are cases where Resize returns an empty bitmap, for example if you |
| 539 // ask for an image too large. In this case it is better to return the |
| 540 // default image than returning nothing at all. |
| 541 if (resized_image.empty()) |
| 542 resized_image = *default_image; |
| 543 result_image = resized_image; |
| 544 } |
| 545 |
| 546 content::BrowserThread::PostTask( |
| 547 content::BrowserThread::UI, |
| 548 FROM_HERE, |
| 549 base::Bind(&ImageLoader::LoadDefaultImageDone, |
| 550 base::Unretained(this), |
| 551 gfx::Image::CreateFrom1xBitmap(result_image), |
| 552 grayscale, |
| 553 callback)); |
| 554 } |
| 555 |
| 556 void ImageLoader::LoadDefaultImageDone( |
| 557 const gfx::Image& image, |
| 558 bool grayscale, |
| 559 const base::Callback<void(const gfx::Image&)>& callback) { |
| 560 FinalizeImage(image, grayscale, callback); |
| 561 } |
| 562 |
| 563 void ImageLoader::FinalizeImage( |
| 564 const gfx::Image& image, |
| 565 bool grayscale, |
| 566 const base::Callback<void(const gfx::Image&)>& callback) { |
| 567 content::BrowserThread::PostTask( |
| 568 content::BrowserThread::FILE, |
| 569 FROM_HERE, |
| 570 base::Bind(&ImageLoader::FinalizeImageOnFileThread, |
| 571 base::Unretained(this), |
| 572 image, |
| 573 grayscale, |
| 574 callback)); |
| 575 } |
| 576 |
| 577 void ImageLoader::FinalizeImageOnFileThread( |
| 578 const gfx::Image& image, |
| 579 bool grayscale, |
| 580 const base::Callback<void(const gfx::Image&)>& callback) { |
| 581 SkBitmap bitmap; |
| 582 if (grayscale) |
| 583 bitmap = DesaturateImage(image.ToSkBitmap()); |
| 584 else |
| 585 bitmap = *image.ToSkBitmap(); |
| 586 |
| 587 gfx::Image modifiedImage = gfx::Image::CreateFrom1xBitmap(bitmap); |
| 588 content::BrowserThread::PostTask(content::BrowserThread::UI, |
| 589 FROM_HERE, |
| 590 base::Bind(&ImageLoader::FinalizeImageDone, |
| 591 base::Unretained(this), |
| 592 modifiedImage, |
| 593 callback)); |
| 594 } |
| 595 |
| 596 void ImageLoader::FinalizeImageDone( |
| 597 const gfx::Image& image, |
| 598 const base::Callback<void(const gfx::Image&)>& callback) { |
| 599 callback.Run(image); |
| 600 } |
| 601 |
| 602 void ImageLoader::LoadExtensionIconDataURLAsync( |
| 603 const extensions::Extension* extension, |
| 604 int icon_size, |
| 605 ExtensionIconSet::MatchType match, |
| 606 bool grayscale, |
| 607 const base::Callback<void(const GURL&)>& callback) { |
| 608 LoadExtensionIconAsync(extension, |
| 609 icon_size, |
| 610 match, |
| 611 grayscale, |
| 612 base::Bind(&ImageLoader::OnIconAvailable, |
| 613 base::Unretained(this), |
| 614 callback)); |
| 615 } |
| 616 |
| 617 void ImageLoader::OnIconAvailable( |
| 618 const base::Callback<void(const GURL&)>& callback, |
| 619 const gfx::Image& image) { |
| 620 content::BrowserThread::PostTask( |
| 621 content::BrowserThread::FILE, |
| 622 FROM_HERE, |
| 623 base::Bind(&ImageLoader::ConvertIconToURLOnFileThread, |
| 624 base::Unretained(this), |
| 625 image, |
| 626 callback)); |
| 627 } |
| 628 |
| 629 void ImageLoader::ConvertIconToURLOnFileThread( |
| 630 const gfx::Image& image, |
| 631 const base::Callback<void(const GURL&)>& callback) { |
| 632 GURL url = GetImageDataURL(image); |
| 633 content::BrowserThread::PostTask( |
| 634 content::BrowserThread::UI, |
| 635 FROM_HERE, |
| 636 base::Bind(&ImageLoader::OnIconConvertedToURL, |
| 637 base::Unretained(this), |
| 638 url, |
| 639 callback)); |
| 640 } |
| 641 |
| 642 void ImageLoader::OnIconConvertedToURL( |
| 643 const GURL& url, |
| 644 const base::Callback<void(const GURL&)>& callback) { |
| 645 callback.Run(url); |
| 646 } |
| 647 |
| 648 const SkBitmap* ImageLoader::GetDefaultAppImage() { |
| 649 if (!default_app_icon_.get()) |
| 650 default_app_icon_.reset(GetImageByResourceId(IDR_APP_DEFAULT_ICON)); |
| 651 |
| 652 return default_app_icon_.get(); |
| 653 } |
| 654 |
| 655 const SkBitmap* ImageLoader::GetDefaultExtensionImage() { |
| 656 if (!default_extension_icon_.get()) { |
| 657 default_extension_icon_.reset( |
| 658 GetImageByResourceId(IDR_EXTENSION_DEFAULT_ICON)); |
| 659 } |
| 660 |
| 661 return default_extension_icon_.get(); |
| 662 } |
| 663 |
| 664 scoped_refptr<base::RefCountedMemory> ImageLoader::BitmapToMemory( |
| 665 const SkBitmap* image) { |
| 666 base::RefCountedBytes* image_bytes = new base::RefCountedBytes; |
| 667 gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data()); |
| 668 return image_bytes; |
| 669 } |
| 670 |
| 350 } // namespace extensions | 671 } // namespace extensions |
| OLD | NEW |