summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordvh@chromium.org <dvh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 06:50:55 +0000
committerdvh@chromium.org <dvh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 06:50:55 +0000
commitdcc41abf828333397c7100044ff285afe0bafe03 (patch)
tree5ad8bd81b7231ca29759bb7d2c94463f737a8d4d
parent5488874f4eaaeaf5fd205b07695900acaf39c983 (diff)
downloadchromium_src-dcc41abf828333397c7100044ff285afe0bafe03.zip
chromium_src-dcc41abf828333397c7100044ff285afe0bafe03.tar.gz
chromium_src-dcc41abf828333397c7100044ff285afe0bafe03.tar.bz2
Refactored loading of applications / extensions icons.
Added ImageLoader::LoadExtensionIconAsync() and ImageLoader::LoadExtensionIconDataURLAsync(). Moved all apps / extension loading code to ImageLoader. BUG=297298, 297301, 304180 Review URL: https://codereview.chromium.org/26161002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227477 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/developer_private/developer_private_api.cc109
-rw-r--r--chrome/browser/extensions/api/developer_private/developer_private_api.h22
-rw-r--r--chrome/browser/extensions/extension_icon_image_unittest.cc5
-rw-r--r--chrome/browser/extensions/image_loader.cc332
-rw-r--r--chrome/browser/extensions/image_loader.h175
-rw-r--r--chrome/browser/extensions/image_loader_factory.cc2
-rw-r--r--chrome/browser/extensions/image_loader_unittest.cc145
-rw-r--r--chrome/browser/ui/webui/extensions/extension_icon_source.cc179
-rw-r--r--chrome/browser/ui/webui/extensions/extension_icon_source.h51
-rw-r--r--chrome/test/data/extensions/default_image_loading_tracker/app.json5
10 files changed, 719 insertions, 306 deletions
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 3f594d6..40b1612 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -27,6 +27,7 @@
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/extensions/image_loader.h"
#include "chrome/browser/extensions/management_policy.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
@@ -85,45 +86,6 @@ ExtensionUpdater* GetExtensionUpdater(Profile* profile) {
return profile->GetExtensionService()->updater();
}
-GURL GetImageURLFromData(std::string contents) {
- std::string contents_base64;
- if (!base::Base64Encode(contents, &contents_base64))
- return GURL();
-
- // TODO(dvh): make use of chrome::kDataScheme. Filed as crbug/297301.
- const char kDataURLPrefix[] = "data:image;base64,";
- return GURL(kDataURLPrefix + contents_base64);
-}
-
-GURL GetDefaultImageURL(developer_private::ItemType type) {
- int icon_resource_id;
- switch (type) {
- case developer::ITEM_TYPE_LEGACY_PACKAGED_APP:
- case developer::ITEM_TYPE_HOSTED_APP:
- case developer::ITEM_TYPE_PACKAGED_APP:
- icon_resource_id = IDR_APP_DEFAULT_ICON;
- break;
- default:
- icon_resource_id = IDR_EXTENSION_DEFAULT_ICON;
- break;
- }
-
- return GetImageURLFromData(
- ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
- icon_resource_id, ui::SCALE_FACTOR_100P).as_string());
-}
-
-// TODO(dvh): This code should be refactored and moved to
-// extensions::ImageLoader. Also a resize should be performed to avoid
-// potential huge URLs: crbug/297298.
-GURL ToDataURL(const base::FilePath& path, developer_private::ItemType type) {
- std::string contents;
- if (path.empty() || !base::ReadFileToString(path, &contents))
- return GetDefaultImageURL(type);
-
- return GetImageURLFromData(contents);
-}
-
std::vector<base::FilePath> ListFolder(const base::FilePath path) {
base::FileEnumerator files(path, false,
base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES);
@@ -374,27 +336,6 @@ scoped_ptr<developer::ItemInfo>
return info.Pass();
}
-void DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread(
- ItemInfoList item_list,
- const std::map<std::string, ExtensionResource> idToIcon) {
- for (ItemInfoList::iterator iter = item_list.begin();
- iter != item_list.end(); ++iter) {
- developer_private::ItemInfo* info = iter->get();
- std::map<std::string, ExtensionResource>::const_iterator resource_ptr
- = idToIcon.find(info->id);
- if (resource_ptr != idToIcon.end()) {
- info->icon_url =
- ToDataURL(resource_ptr->second.GetFilePath(), info->type).spec();
- }
- }
-
- results_ = developer::GetItemsInfo::Results::Create(item_list);
- content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
- base::Bind(&DeveloperPrivateGetItemsInfoFunction::SendResponse,
- this,
- true));
-}
-
void DeveloperPrivateGetItemsInfoFunction::
GetInspectablePagesForExtensionProcess(
const Extension* extension,
@@ -554,37 +495,55 @@ bool DeveloperPrivateGetItemsInfoFunction::RunImpl() {
items.InsertAll(*service->terminated_extensions());
}
- std::map<std::string, ExtensionResource> id_to_icon;
- ItemInfoList item_list;
-
for (ExtensionSet::const_iterator iter = items.begin();
iter != items.end(); ++iter) {
const Extension& item = *iter->get();
- ExtensionResource item_resource =
- IconsInfo::GetIconResource(&item,
- extension_misc::EXTENSION_ICON_MEDIUM,
- ExtensionIconSet::MATCH_BIGGER);
- id_to_icon[item.id()] = item_resource;
-
// Don't show component extensions and invisible apps.
if (item.ShouldNotBeVisible())
continue;
- item_list.push_back(make_linked_ptr<developer::ItemInfo>(
+ item_list_.push_back(make_linked_ptr<developer::ItemInfo>(
CreateItemInfo(
item, service->IsExtensionEnabled(item.id())).release()));
}
- content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
- base::Bind(&DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread,
- this,
- item_list,
- id_to_icon));
+ // Iterate over |item_list_| to request all the icons.
+ RequestNextIcon();
return true;
}
+void DeveloperPrivateGetItemsInfoFunction::RequestNextIcon() {
+ developer::ItemInfo* info = item_list_[icon_to_load_].get();
+ ExtensionService* service = profile()->GetExtensionService();
+ ImageLoader::Get(profile())->LoadExtensionIconDataURLAsync(
+ service->GetExtensionById(info->id, true),
+ extension_misc::EXTENSION_ICON_MEDIUM,
+ ExtensionIconSet::MATCH_BIGGER,
+ false,
+ base::Bind(&DeveloperPrivateGetItemsInfoFunction::LoadIconFinished,
+ this));
+}
+
+void DeveloperPrivateGetItemsInfoFunction::LoadIconFinished(const GURL& url) {
+ // Store the result.
+ developer::ItemInfo* info = item_list_[icon_to_load_].get();
+ info->icon_url = url.spec();
+ ++icon_to_load_;
+ if (icon_to_load_ < static_cast<int>(item_list_.size())) {
+ RequestNextIcon();
+ } else {
+ // All icons have been loaded: send the result.
+ results_ = developer::GetItemsInfo::Results::Create(item_list_);
+ SendResponse(true);
+ }
+}
+
+DeveloperPrivateGetItemsInfoFunction::DeveloperPrivateGetItemsInfoFunction()
+ : icon_to_load_(0) {
+}
+
DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {}
bool DeveloperPrivateAllowFileAccessFunction::RunImpl() {
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h
index ac5dc5c..41d5a7f 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.h
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -133,6 +133,8 @@ class DeveloperPrivateGetItemsInfoFunction : public AsyncExtensionFunction {
DECLARE_EXTENSION_FUNCTION("developerPrivate.getItemsInfo",
DEVELOPERPRIVATE_GETITEMSINFO)
+ DeveloperPrivateGetItemsInfoFunction();
+
protected:
virtual ~DeveloperPrivateGetItemsInfoFunction();
@@ -140,15 +142,19 @@ class DeveloperPrivateGetItemsInfoFunction : public AsyncExtensionFunction {
virtual bool RunImpl() OVERRIDE;
private:
+ // List of extensions/apps items to return. This list will be populated in a
+ // first step. Then, it will be used to keep track of items that need to be
+ // filled with icons.
+ ItemInfoList item_list_;
+
+ // Index of the extension/app in |item_list_| for which we need to load the
+ // icon.
+ int icon_to_load_;
scoped_ptr<developer::ItemInfo> CreateItemInfo(
const extensions::Extension& item,
bool item_is_enabled);
- void GetIconsOnFileThread(
- ItemInfoList item_list,
- std::map<std::string, ExtensionResource> itemIdToIconResourceMap);
-
// Helper that lists the current inspectable html pages for the extension.
void GetInspectablePagesForExtensionProcess(
const Extension* extension,
@@ -169,6 +175,14 @@ class DeveloperPrivateGetItemsInfoFunction : public AsyncExtensionFunction {
int render_view_id,
bool incognito,
bool generated_background_page);
+
+ // Request the icon of the extension/app at the index |icon_to_load_| in
+ // |item_list_|.
+ void RequestNextIcon();
+
+ // Called when each icon has been loaded by RequestNextIcon(). |url| is the
+ // data url containing the icon.
+ void LoadIconFinished(const GURL& url);
};
class DeveloperPrivateInspectFunction : public SyncExtensionFunction {
diff --git a/chrome/browser/extensions/extension_icon_image_unittest.cc b/chrome/browser/extensions/extension_icon_image_unittest.cc
index fd35ee9..1e73efa 100644
--- a/chrome/browser/extensions/extension_icon_image_unittest.cc
+++ b/chrome/browser/extensions/extension_icon_image_unittest.cc
@@ -88,7 +88,8 @@ class TestImageLoader {
int size) {
image_loaded_ = false;
- image_loader_.LoadImageAsync(
+ image_loader_.reset(new extensions::ImageLoader(NULL));
+ image_loader_->LoadImageAsync(
extension_, extension_->GetResource(path), gfx::Size(size, size),
base::Bind(&TestImageLoader::OnImageLoaded,
base::Unretained(this)));
@@ -111,7 +112,7 @@ class TestImageLoader {
bool waiting_;
bool image_loaded_;
gfx::Image image_;
- extensions::ImageLoader image_loader_;
+ scoped_ptr<extensions::ImageLoader> image_loader_;
DISALLOW_COPY_AND_ASSIGN(TestImageLoader);
};
diff --git a/chrome/browser/extensions/image_loader.cc b/chrome/browser/extensions/image_loader.cc
index 2c8f77c..bcab0fe 100644
--- a/chrome/browser/extensions/image_loader.cc
+++ b/chrome/browser/extensions/image_loader.cc
@@ -7,6 +7,7 @@
#include <map>
#include <vector>
+#include "base/base64.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
@@ -15,16 +16,26 @@
#include "base/strings/string_number_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/extensions/image_loader_factory.h"
+#include "chrome/browser/favicon/favicon_service.h"
+#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_icon_set.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/url_constants.h"
#include "grit/chrome_unscaled_resources.h"
#include "grit/component_extension_resources_map.h"
#include "grit/theme_resources.h"
#include "skia/ext/image_operations.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/favicon_size.h"
#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/skbitmap_operations.h"
#if defined(USE_AURA)
#include "ui/keyboard/keyboard_util.h"
@@ -115,6 +126,54 @@ void AddComponentResourceEntries(
}
}
+// Returns a PNG image data URL of a given image.
+GURL GetImageDataURL(const gfx::Image& image) {
+ if (image.IsEmpty())
+ return GURL();
+
+ scoped_refptr<base::RefCountedMemory> mem =
+ ImageLoader::BitmapToMemory(image.ToSkBitmap());
+ std::string contents_base64;
+ // TODO(dvh): contents_base64 calculation of the result of the function is
+ // sub-optimal: it's a concatenation of 3 strings and |contents_base64| can
+ // be huge. Additionally, the GURL is returned by copy.
+ // crbug/304759.
+ if (!base::Base64Encode(
+ std::string(reinterpret_cast<const char*>(mem->front()), mem->size()),
+ &contents_base64))
+ return GURL();
+
+ const char kDataURLPrefix[] = ":image/png;base64,";
+ return GURL(std::string(chrome::kDataScheme) + kDataURLPrefix +
+ contents_base64);
+}
+
+// Converts the image to grayscale.
+SkBitmap DesaturateImage(const SkBitmap* image) {
+ color_utils::HSL shift = {-1, 0, 0.6};
+ return SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
+}
+
+// Creates an image from PNG data.
+SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
+ SkBitmap* decoded = new SkBitmap();
+ bool success = gfx::PNGCodec::Decode(data, size, decoded);
+ DCHECK(success);
+ return decoded;
+}
+
+// Load an image from a resource given its identifier |resource_id|.
+SkBitmap* GetImageByResourceId(int resource_id) {
+ std::string contents = ResourceBundle::GetSharedInstance()
+ .GetRawDataResourceForScale(resource_id,
+ ui::SCALE_FACTOR_100P).as_string();
+
+ // Convert and return it.
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(contents.data());
+ return ToBitmap(data, contents.length());
+}
+
} // namespace
namespace extensions {
@@ -165,8 +224,8 @@ ImageLoader::LoadResult::~LoadResult() {
////////////////////////////////////////////////////////////////////////////////
// ImageLoader
-ImageLoader::ImageLoader()
- : weak_ptr_factory_(this) {
+ImageLoader::ImageLoader(Profile* profile)
+ : weak_ptr_factory_(this), profile_(profile) {
}
ImageLoader::~ImageLoader() {
@@ -347,4 +406,273 @@ void ImageLoader::ReplyBack(
callback.Run(image);
}
+void ImageLoader::LoadExtensionIconAsync(
+ const extensions::Extension* extension,
+ int icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ // |extension| can be NULL when the default icon is requested. In this case,
+ // fail the icon loading, which will result in the default icon being loaded.
+ if (extension == NULL) {
+ LoadIconFailed(extension, icon_size, grayscale, callback);
+ return;
+ }
+
+ ExtensionResource resource = IconsInfo::GetIconResource(
+ extension, icon_size, match);
+ LoadImageAsync(extension,
+ resource,
+ gfx::Size(icon_size, icon_size),
+ base::Bind(&ImageLoader::LoadExtensionIconDone,
+ base::Unretained(this),
+ extension,
+ icon_size,
+ grayscale,
+ callback));
+}
+
+void ImageLoader::LoadExtensionIconDone(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback,
+ const gfx::Image& image) {
+ if (image.IsEmpty())
+ LoadIconFailed(extension, icon_size, grayscale, callback);
+ else
+ FinalizeImage(image, grayscale, callback);
+}
+
+void ImageLoader::LoadIconFailed(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ if ((extension != NULL) &&
+ (icon_size == extension_misc::EXTENSION_ICON_BITTY))
+ LoadFaviconImage(extension, icon_size, grayscale, callback);
+ else
+ LoadDefaultImage(extension, icon_size, grayscale, callback);
+}
+
+void ImageLoader::LoadFaviconImage(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ FaviconService* favicon_service = NULL;
+ if (profile_ != NULL)
+ favicon_service = FaviconServiceFactory::GetForProfile(
+ profile_, Profile::EXPLICIT_ACCESS);
+ // Fall back to the default icons if the service isn't available.
+ if (favicon_service == NULL) {
+ LoadDefaultImage(extension, icon_size, grayscale, callback);
+ return;
+ }
+
+ GURL favicon_url = AppLaunchInfo::GetFullLaunchURL(extension);
+ favicon_service->GetRawFaviconForURL(
+ FaviconService::FaviconForURLParams(
+ profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize),
+ ui::SCALE_FACTOR_100P,
+ base::Bind(&ImageLoader::OnFaviconDataAvailable,
+ base::Unretained(this),
+ extension,
+ icon_size,
+ grayscale,
+ callback),
+ &cancelable_task_tracker_);
+}
+
+void ImageLoader::OnFaviconDataAvailable(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback,
+ const chrome::FaviconBitmapResult& bitmap_result) {
+ // Fallback to the default icon if there wasn't a favicon.
+ if (!bitmap_result.is_valid()) {
+ LoadDefaultImage(extension, icon_size, grayscale, callback);
+ return;
+ }
+
+ gfx::Image image = gfx::Image::CreateFrom1xBitmap(*ToBitmap(
+ bitmap_result.bitmap_data->front(), bitmap_result.bitmap_data->size()));
+ FinalizeImage(image, grayscale, callback);
+}
+
+void ImageLoader::LoadDefaultImage(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&ImageLoader::LoadDefaultImageOnFileThread,
+ base::Unretained(this),
+ extension,
+ icon_size,
+ grayscale,
+ callback));
+}
+
+void ImageLoader::LoadDefaultImageOnFileThread(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ const SkBitmap* default_image = NULL;
+ if ((extension == NULL) || (extension->is_app()))
+ default_image = GetDefaultAppImage();
+ else
+ default_image = GetDefaultExtensionImage();
+
+ SkBitmap result_image;
+ if (icon_size == -1) {
+ // If a specific size was not requested.
+ result_image = *default_image;
+ } else {
+ SkBitmap resized_image(
+ skia::ImageOperations::Resize(*default_image,
+ skia::ImageOperations::RESIZE_LANCZOS3,
+ icon_size,
+ icon_size));
+ // There are cases where Resize returns an empty bitmap, for example if you
+ // ask for an image too large. In this case it is better to return the
+ // default image than returning nothing at all.
+ if (resized_image.empty())
+ resized_image = *default_image;
+ result_image = resized_image;
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&ImageLoader::LoadDefaultImageDone,
+ base::Unretained(this),
+ gfx::Image::CreateFrom1xBitmap(result_image),
+ grayscale,
+ callback));
+}
+
+void ImageLoader::LoadDefaultImageDone(
+ const gfx::Image image,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ FinalizeImage(image, grayscale, callback);
+}
+
+void ImageLoader::FinalizeImage(
+ const gfx::Image& image,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&ImageLoader::FinalizeImageOnFileThread,
+ base::Unretained(this),
+ image,
+ grayscale,
+ callback));
+}
+
+void ImageLoader::FinalizeImageOnFileThread(
+ const gfx::Image image,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ SkBitmap bitmap;
+ if (grayscale)
+ bitmap = DesaturateImage(image.ToSkBitmap());
+ else
+ bitmap = *image.ToSkBitmap();
+
+ gfx::Image modifiedImage = gfx::Image::CreateFrom1xBitmap(bitmap);
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&ImageLoader::FinalizeImageDone,
+ base::Unretained(this),
+ modifiedImage,
+ callback));
+}
+
+void ImageLoader::FinalizeImageDone(
+ const gfx::Image image,
+ const base::Callback<void(const gfx::Image&)>& callback) {
+ callback.Run(image);
+}
+
+void ImageLoader::LoadExtensionIconDataURLAsync(
+ const extensions::Extension* extension,
+ int icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale,
+ const base::Callback<void(const GURL&)>& callback) {
+ LoadExtensionIconAsync(extension,
+ icon_size,
+ match,
+ grayscale,
+ base::Bind(&ImageLoader::OnIconAvailable,
+ base::Unretained(this),
+ callback));
+}
+
+void ImageLoader::OnIconAvailable(
+ const base::Callback<void(const GURL&)>& callback,
+ const gfx::Image& image) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&ImageLoader::ConvertIconToURLOnFileThread,
+ base::Unretained(this),
+ image,
+ callback));
+}
+
+void ImageLoader::ConvertIconToURLOnFileThread(
+ const gfx::Image image,
+ const base::Callback<void(const GURL&)>& callback) {
+ GURL url = GetImageDataURL(image);
+ // TODO(dvh): |url| can be huge and it's passed by copy to
+ // OnIconConvertedToURL(). It can be optimized.
+ // crbug/304759.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&ImageLoader::OnIconConvertedToURL,
+ base::Unretained(this),
+ url,
+ callback));
+}
+
+void ImageLoader::OnIconConvertedToURL(
+ const GURL url,
+ const base::Callback<void(const GURL&)>& callback) {
+ callback.Run(url);
+}
+
+const SkBitmap* ImageLoader::GetDefaultAppImage() {
+ if (!default_app_icon_.get())
+ default_app_icon_.reset(GetImageByResourceId(IDR_APP_DEFAULT_ICON));
+
+ return default_app_icon_.get();
+}
+
+const SkBitmap* ImageLoader::GetDefaultExtensionImage() {
+ if (!default_extension_icon_.get()) {
+ default_extension_icon_.reset(
+ GetImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
+ }
+
+ return default_extension_icon_.get();
+}
+
+scoped_refptr<base::RefCountedMemory> ImageLoader::BitmapToMemory(
+ const SkBitmap* image) {
+ base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
+ gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
+ return image_bytes;
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/image_loader.h b/chrome/browser/extensions/image_loader.h
index 5e6bc3e..9259c52 100644
--- a/chrome/browser/extensions/image_loader.h
+++ b/chrome/browser/extensions/image_loader.h
@@ -10,14 +10,25 @@
#include "base/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
+#include "chrome/common/cancelable_task_tracker.h"
+#include "chrome/common/extensions/extension_icon_set.h"
#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
#include "extensions/common/extension_resource.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/layout.h"
#include "ui/gfx/size.h"
+class GURL;
class Profile;
+namespace base {
+class RefCountedMemory;
+}
+
+namespace chrome {
+struct FaviconBitmapResult;
+}
+
namespace gfx {
class Image;
}
@@ -68,9 +79,6 @@ class ImageLoader : public BrowserContextKeyedService {
// a convenience wrapper around ImageLoaderFactory::GetForProfile.
static ImageLoader* Get(Profile* profile);
- ImageLoader();
- virtual ~ImageLoader();
-
// Checks whether image is a component extension resource. Returns false
// if a given |resource| does not have a corresponding image in bundled
// resources. Otherwise fills |resource_id|. This doesn't check if the
@@ -80,6 +88,13 @@ class ImageLoader : public BrowserContextKeyedService {
const base::FilePath& resource_path,
int* resource_id);
+ // Converts a bitmap image to a PNG representation.
+ static scoped_refptr<base::RefCountedMemory> BitmapToMemory(
+ const SkBitmap* image);
+
+ explicit ImageLoader(Profile* profile);
+ virtual ~ImageLoader();
+
// Specify image resource to load. If the loaded image is larger than
// |max_size| it will be resized to those dimensions. IMPORTANT NOTE: this
// function may call back your callback synchronously (ie before it returns)
@@ -96,11 +111,46 @@ class ImageLoader : public BrowserContextKeyedService {
// type.
void LoadImagesAsync(const extensions::Extension* extension,
const std::vector<ImageRepresentation>& info_list,
- const base::Callback<void(const gfx::Image&)>& callback);
+ const base::Callback<void(const gfx::Image&)>&
+ callback);
+
+ // Load the icon of the given |extension| and return it via |callback| as a
+ // gfx::Image. |extension| can be NULL to request the default extension icon.
+ // The size in pixels of the returned icon can be chosen with |icon_size| or
+ // -1, -1 if no resize is requested. The icon can also be converted to
+ // grayscale by setting |grayscale| to true. |match| optionally indicates if
+ // it should fallback to smaller or bigger size when choosing the icon source
+ // image.
+ void LoadExtensionIconAsync(const extensions::Extension* extension,
+ int icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>&
+ callback);
+
+ // Same as LoadExtensionIconAsync() above except the result icon is returned
+ // as a PNG image data URL.
+ void LoadExtensionIconDataURLAsync(const extensions::Extension* extension,
+ int icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale,
+ const base::Callback<void(const GURL&)>&
+ callback);
private:
base::WeakPtrFactory<ImageLoader> weak_ptr_factory_;
+ Profile* profile_;
+
+ // Task tracker when getting favicon.
+ CancelableTaskTracker cancelable_task_tracker_;
+
+ // Cache for the default app icon.
+ scoped_ptr<SkBitmap> default_app_icon_;
+
+ // Cache for the default extension icon.
+ scoped_ptr<SkBitmap> default_extension_icon_;
+
static void LoadImagesOnBlockingPool(
const std::vector<ImageRepresentation>& info_list,
const std::vector<SkBitmap>& bitmaps,
@@ -109,6 +159,123 @@ class ImageLoader : public BrowserContextKeyedService {
void ReplyBack(
const std::vector<LoadResult>* load_result,
const base::Callback<void(const gfx::Image&)>& callback);
+
+ // The sequence for LoadExtensionIconAsync() is the following:
+ // 1) It loads the icon image using LoadImageAsync().
+ // 2) When it finishes, LoadExtensionIconLoaded() will be called.
+ // 3) On success, it will call FinalizeImage(). If it failed, it will call
+ // LoadIconFailed(). See below for more on those methods.
+ void LoadExtensionIconDone(const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>&
+ callback,
+ const gfx::Image& image);
+
+ // Called when the extension doesn't have an icon. We fall back to multiple
+ // sources, using the following order:
+ // 1) The icons as listed in the extension manifests.
+ // 2) If a 16px icon and the extension has a launch URL, see if Chrome has a
+ // corresponding favicon.
+ // 3) If still no matches, load the default extension icon.
+ void LoadIconFailed(const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback);
+
+ // Loads the favicon image for the given |extension|. If the image does not
+ // exist, we fall back to the default image.
+ void LoadFaviconImage(const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>&
+ callback);
+
+ // FaviconService callback. It will call FinalizedImage() on success or try
+ // another fallback.
+ void OnFaviconDataAvailable(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback,
+ const chrome::FaviconBitmapResult& bitmap_result);
+
+ // The sequence for LoadDefaultImage() will be the following:
+ // 1) LoadDefaultImage() will invoke LoadDefaultImageOnFileThread() on the
+ // file thread.
+ // 2) LoadDefaultImageOnFileThread() will perform the work, then invoke
+ // LoadDefaultImageDone() on the UI thread.
+ void LoadDefaultImage(const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>&
+ callback);
+
+ // Loads the default image on the file thread.
+ void LoadDefaultImageOnFileThread(
+ const extensions::Extension* extension,
+ int icon_size,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback);
+
+ // When loading of default image is done, it will call FinalizeImage().
+ void LoadDefaultImageDone(const gfx::Image image,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>&
+ callback);
+
+ // Performs any remaining transformations (like desaturating the |image|),
+ // then returns the |image| to the |callback|.
+ //
+ // The sequence for FinalizeImage() will be the following:
+ // 1) FinalizeImage() will invoke FinalizeImageOnFileThread() on the file
+ // thread.
+ // 2) FinalizeImageOnFileThread() will perform the work, then invoke
+ // FinalizeImageDone() on the UI thread.
+ void FinalizeImage(const gfx::Image& image,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>& callback);
+
+ // Process the "finalize" operation on the file thread.
+ void FinalizeImageOnFileThread(const gfx::Image image,
+ bool grayscale,
+ const base::Callback<void(const gfx::Image&)>&
+ callback);
+
+ // Called when the "finalize" operation on the file thread is done.
+ void FinalizeImageDone(const gfx::Image image,
+ const base::Callback<void(const gfx::Image&)> &
+ callback);
+
+ // The sequence for LoadExtensionIconDataURLAsync() will be the following:
+ // 1) Call LoadExtensionIconAsync() to fetch the icon of the extension.
+ // 2) When the icon is loaded, OnIconAvailable() will be called and will
+ // invoke ConvertIconToURLOnFileThread() on the file thread.
+ // 3) OnIconConvertedToURL() will be called on the UI thread when it's done
+ // and will call the callback.
+ //
+ // LoadExtensionIconDataURLAsync() will use LoadExtensionIconAsync() to get
+ // the icon of the extension. The following method will be called when the
+ // image has been fetched.
+ void OnIconAvailable(const base::Callback<void(const GURL&)>& callback,
+ const gfx::Image& image);
+
+ // ConvertIconToURLOnFileThread() will convert the image to a PNG image data
+ // URL on the file thread.
+ void ConvertIconToURLOnFileThread(const gfx::Image image,
+ const base::Callback<void(const GURL&)>&
+ callback);
+
+ // This method will call the callback of LoadExtensionIconDataURLAsync() with
+ // the result.
+ void OnIconConvertedToURL(const GURL url,
+ const base::Callback<void(const GURL&)>& callback);
+
+ // Returns the bitmap for the default extension icon.
+ const SkBitmap* GetDefaultExtensionImage();
+
+ // Returns the bitmap for the default app icon.
+ const SkBitmap* GetDefaultAppImage();
};
} // namespace extensions
diff --git a/chrome/browser/extensions/image_loader_factory.cc b/chrome/browser/extensions/image_loader_factory.cc
index 6d08dd7..14dfbd5 100644
--- a/chrome/browser/extensions/image_loader_factory.cc
+++ b/chrome/browser/extensions/image_loader_factory.cc
@@ -32,7 +32,7 @@ ImageLoaderFactory::~ImageLoaderFactory() {
BrowserContextKeyedService* ImageLoaderFactory::BuildServiceInstanceFor(
content::BrowserContext* profile) const {
- return new ImageLoader;
+ return new ImageLoader(static_cast<Profile*>(profile));
}
bool ImageLoaderFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chrome/browser/extensions/image_loader_unittest.cc b/chrome/browser/extensions/image_loader_unittest.cc
index 05cea06..b1320d3 100644
--- a/chrome/browser/extensions/image_loader_unittest.cc
+++ b/chrome/browser/extensions/image_loader_unittest.cc
@@ -34,7 +34,6 @@ class ImageLoaderTest : public testing::Test {
public:
ImageLoaderTest()
: image_loaded_count_(0),
- quit_in_image_loaded_(false),
ui_thread_(BrowserThread::UI, &ui_loop_),
file_thread_(BrowserThread::FILE),
io_thread_(BrowserThread::IO) {
@@ -42,15 +41,18 @@ class ImageLoaderTest : public testing::Test {
void OnImageLoaded(const gfx::Image& image) {
image_loaded_count_++;
- if (quit_in_image_loaded_)
- base::MessageLoop::current()->Quit();
+ base::MessageLoop::current()->Quit();
image_ = image;
}
+ void OnImageURLLoaded(const GURL& url) {
+ image_loaded_count_++;
+ base::MessageLoop::current()->Quit();
+ url_ = url;
+ }
+
void WaitForImageLoad() {
- quit_in_image_loaded_ = true;
base::MessageLoop::current()->Run();
- quit_in_image_loaded_ = false;
}
int image_loaded_count() {
@@ -94,7 +96,10 @@ class ImageLoaderTest : public testing::Test {
Extension::NO_FLAGS, &error);
}
+ // Holds the image loaded by the test.
gfx::Image image_;
+ // Holds the data url retrieved by the test.
+ GURL url_;
private:
virtual void SetUp() OVERRIDE {
@@ -104,7 +109,6 @@ class ImageLoaderTest : public testing::Test {
}
int image_loaded_count_;
- bool quit_in_image_loaded_;
base::MessageLoop ui_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
@@ -123,7 +127,7 @@ TEST_F(ImageLoaderTest, LoadImage) {
ExtensionIconSet::MATCH_EXACTLY);
gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
extension_misc::EXTENSION_ICON_SMALLISH);
- ImageLoader loader;
+ ImageLoader loader(NULL);
loader.LoadImageAsync(extension.get(),
image_resource,
max_size,
@@ -156,7 +160,7 @@ TEST_F(ImageLoaderTest, DeleteExtensionWhileWaitingForCache) {
ExtensionIconSet::MATCH_EXACTLY);
gfx::Size max_size(extension_misc::EXTENSION_ICON_SMALLISH,
extension_misc::EXTENSION_ICON_SMALLISH);
- ImageLoader loader;
+ ImageLoader loader(NULL);
std::set<int> sizes;
sizes.insert(extension_misc::EXTENSION_ICON_SMALLISH);
loader.LoadImageAsync(extension.get(),
@@ -210,7 +214,7 @@ TEST_F(ImageLoaderTest, MultipleImages) {
ui::SCALE_FACTOR_NONE));
}
- ImageLoader loader;
+ ImageLoader loader(NULL);
loader.LoadImagesAsync(extension.get(), info_list,
base::Bind(&ImageLoaderTest::OnImageLoaded,
base::Unretained(this)));
@@ -258,3 +262,126 @@ TEST_F(ImageLoaderTest, IsComponentExtensionResource) {
ASSERT_EQ(IDR_FILE_MANAGER_ICON_16, resource_id);
#endif
}
+
+// Tests loading an image works correctly.
+TEST_F(ImageLoaderTest, LoadIcon) {
+ scoped_refptr<Extension> extension(CreateExtension(
+ "image_loading_tracker", Manifest::INVALID_LOCATION));
+ ASSERT_TRUE(extension.get() != NULL);
+
+ ImageLoader loader(NULL);
+ loader.LoadExtensionIconAsync(extension.get(),
+ extension_misc::EXTENSION_ICON_SMALLISH,
+ ExtensionIconSet::MATCH_BIGGER,
+ false,
+ base::Bind(&ImageLoaderTest::OnImageLoaded,
+ base::Unretained(this)));
+
+ // The image isn't cached, so we should not have received notification.
+ EXPECT_EQ(0, image_loaded_count());
+
+ WaitForImageLoad();
+
+ // We should have gotten the image.
+ EXPECT_EQ(1, image_loaded_count());
+
+ // Check that the image was loaded.
+ EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
+ image_.ToSkBitmap()->width());
+}
+
+// Tests loading an icon in grayscale works correctly.
+TEST_F(ImageLoaderTest, LoadIconGrayscale) {
+ scoped_refptr<Extension> extension(CreateExtension(
+ "image_loading_tracker", Manifest::INVALID_LOCATION));
+ ASSERT_TRUE(extension.get() != NULL);
+
+ ImageLoader loader(NULL);
+ loader.LoadExtensionIconAsync(extension.get(),
+ extension_misc::EXTENSION_ICON_SMALLISH,
+ ExtensionIconSet::MATCH_BIGGER,
+ true,
+ base::Bind(&ImageLoaderTest::OnImageLoaded,
+ base::Unretained(this)));
+
+ // The image isn't cached, so we should not have received notification.
+ EXPECT_EQ(0, image_loaded_count());
+
+ WaitForImageLoad();
+
+ // We should have gotten the image.
+ EXPECT_EQ(1, image_loaded_count());
+
+ // Check that the image was loaded.
+ EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
+ image_.ToSkBitmap()->width());
+}
+
+// Tests loading an icon URL works correctly.
+TEST_F(ImageLoaderTest, LoadIconURL) {
+ scoped_refptr<Extension> extension(CreateExtension(
+ "image_loading_tracker", Manifest::INVALID_LOCATION));
+ ASSERT_TRUE(extension.get() != NULL);
+
+ ImageLoader loader(NULL);
+ loader.LoadExtensionIconDataURLAsync(
+ extension.get(),
+ extension_misc::EXTENSION_ICON_SMALLISH,
+ ExtensionIconSet::MATCH_BIGGER,
+ true,
+ base::Bind(&ImageLoaderTest::OnImageURLLoaded, base::Unretained(this)));
+
+ // The image isn't cached, so we should not have received notification.
+ EXPECT_EQ(0, image_loaded_count());
+
+ WaitForImageLoad();
+
+ // We should have gotten the image.
+ EXPECT_EQ(1, image_loaded_count());
+}
+
+// Tests loading the default icon works correctly.
+TEST_F(ImageLoaderTest, LoadDefaultIcon) {
+ ImageLoader loader(NULL);
+ loader.LoadExtensionIconAsync(
+ NULL,
+ -1,
+ ExtensionIconSet::MATCH_BIGGER,
+ false,
+ base::Bind(&ImageLoaderTest::OnImageLoaded, base::Unretained(this)));
+
+ // The image isn't cached, so we should not have received notification.
+ EXPECT_EQ(0, image_loaded_count());
+
+ WaitForImageLoad();
+
+ // We should have gotten the image.
+ EXPECT_EQ(1, image_loaded_count());
+}
+
+// Tests loading the icon of an extension without icon works correctly.
+TEST_F(ImageLoaderTest, LoadExtensionWithoutIcon) {
+ scoped_refptr<Extension> extension(CreateExtension(
+ "default_image_loading_tracker", Manifest::INVALID_LOCATION));
+ ASSERT_TRUE(extension.get() != NULL);
+
+ ImageLoader loader(NULL);
+ loader.LoadExtensionIconAsync(extension.get(),
+ extension_misc::EXTENSION_ICON_SMALLISH,
+ ExtensionIconSet::MATCH_BIGGER,
+ false,
+ base::Bind(&ImageLoaderTest::OnImageLoaded,
+ base::Unretained(this)));
+
+ // The image isn't cached, so we should not have received notification.
+ EXPECT_EQ(0, image_loaded_count());
+
+ WaitForImageLoad();
+
+ // We should have gotten the image.
+ EXPECT_EQ(1, image_loaded_count());
+
+ // Check that the image was loaded.
+ EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALLISH,
+ image_.ToSkBitmap()->width());
+}
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.cc b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
index 22dcf45..6d8abd4 100644
--- a/chrome/browser/ui/webui/extensions/extension_icon_source.cc
+++ b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
@@ -39,28 +39,6 @@
namespace extensions {
-namespace {
-
-scoped_refptr<base::RefCountedMemory> BitmapToMemory(const SkBitmap* image) {
- base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
- gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
- return image_bytes;
-}
-
-SkBitmap DesaturateImage(const SkBitmap* image) {
- color_utils::HSL shift = {-1, 0, 0.6};
- return SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
-}
-
-SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
- SkBitmap* decoded = new SkBitmap();
- bool success = gfx::PNGCodec::Decode(data, size, decoded);
- DCHECK(success);
- return decoded;
-}
-
-} // namespace
-
ExtensionIconSource::ExtensionIconSource(Profile* profile) : profile_(profile) {
}
@@ -93,18 +71,6 @@ GURL ExtensionIconSource::GetIconURL(const Extension* extension,
return icon_url;
}
-// static
-SkBitmap* ExtensionIconSource::LoadImageByResourceId(int resource_id) {
- std::string contents = ResourceBundle::GetSharedInstance()
- .GetRawDataResourceForScale(resource_id,
- ui::SCALE_FACTOR_100P).as_string();
-
- // Convert and return it.
- const unsigned char* data =
- reinterpret_cast<const unsigned char*>(contents.data());
- return ToBitmap(data, contents.length());
-}
-
std::string ExtensionIconSource::GetSource() const {
return chrome::kChromeUIExtensionIconHost;
}
@@ -124,23 +90,12 @@ void ExtensionIconSource::StartDataRequest(
// the request data available for later.
static int next_id = 0;
if (!ParseData(path, ++next_id, callback)) {
- // If the request data cannot be parsed, request parameters will not be
- // added to |request_map_|.
- // Send back the default application icon (not resized or desaturated) as
- // the default response.
- callback.Run(BitmapToMemory(GetDefaultAppImage()).get());
- return;
+ // If the request data cannot be parsed, we will request anyway a default
+ // icon (not resized or desaturated).
+ SetData(next_id, callback, NULL, false, -1, ExtensionIconSet::MATCH_BIGGER);
}
- ExtensionIconRequest* request = GetData(next_id);
- ExtensionResource icon = IconsInfo::GetIconResource(
- request->extension, request->size, request->match);
-
- if (icon.relative_path().empty()) {
- LoadIconFailed(next_id);
- } else {
- LoadExtensionImage(icon, next_id);
- }
+ LoadExtensionImage(next_id);
}
ExtensionIconSource::~ExtensionIconSource() {
@@ -148,125 +103,21 @@ ExtensionIconSource::~ExtensionIconSource() {
STLDeleteValues(&request_map_);
}
-const SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
- if (!default_app_data_.get())
- default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));
-
- return default_app_data_.get();
-}
-
-const SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
- if (!default_extension_data_.get()) {
- default_extension_data_.reset(
- LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
- }
-
- return default_extension_data_.get();
-}
-
-void ExtensionIconSource::FinalizeImage(const SkBitmap* image,
- int request_id) {
- SkBitmap bitmap;
- ExtensionIconRequest* request = GetData(request_id);
- if (request->grayscale)
- bitmap = DesaturateImage(image);
- else
- bitmap = *image;
-
- request->callback.Run(BitmapToMemory(&bitmap).get());
- ClearData(request_id);
-}
-
-void ExtensionIconSource::LoadDefaultImage(int request_id) {
- ExtensionIconRequest* request = GetData(request_id);
- const SkBitmap* default_image = NULL;
-
- if (request->extension->is_app())
- default_image = GetDefaultAppImage();
- else
- default_image = GetDefaultExtensionImage();
-
- SkBitmap resized_image(skia::ImageOperations::Resize(
- *default_image, skia::ImageOperations::RESIZE_LANCZOS3,
- request->size, request->size));
-
- // There are cases where Resize returns an empty bitmap, for example if you
- // ask for an image too large. In this case it is better to return the default
- // image than returning nothing at all.
- if (resized_image.empty())
- resized_image = *default_image;
-
- FinalizeImage(&resized_image, request_id);
-}
-
-void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
- int request_id) {
- ExtensionIconRequest* request = GetData(request_id);
- ImageLoader::Get(profile_)->LoadImageAsync(
- request->extension, icon,
- gfx::Size(request->size, request->size),
- base::Bind(&ExtensionIconSource::OnImageLoaded, AsWeakPtr(), request_id));
-}
-
-void ExtensionIconSource::LoadFaviconImage(int request_id) {
- FaviconService* favicon_service =
- FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
- // Fall back to the default icons if the service isn't available.
- if (favicon_service == NULL) {
- LoadDefaultImage(request_id);
- return;
- }
-
- GURL favicon_url =
- AppLaunchInfo::GetFullLaunchURL(GetData(request_id)->extension);
- favicon_service->GetRawFaviconForURL(
- FaviconService::FaviconForURLParams(
- profile_, favicon_url, chrome::FAVICON, gfx::kFaviconSize),
- ui::SCALE_FACTOR_100P,
- base::Bind(&ExtensionIconSource::OnFaviconDataAvailable,
- base::Unretained(this), request_id),
- &cancelable_task_tracker_);
-}
-
-void ExtensionIconSource::OnFaviconDataAvailable(
- int request_id,
- const chrome::FaviconBitmapResult& bitmap_result) {
+void ExtensionIconSource::LoadExtensionImage(int request_id) {
ExtensionIconRequest* request = GetData(request_id);
-
- // Fallback to the default icon if there wasn't a favicon.
- if (!bitmap_result.is_valid()) {
- LoadDefaultImage(request_id);
- return;
- }
-
- if (!request->grayscale) {
- // If we don't need a grayscale image, then we can bypass FinalizeImage
- // to avoid unnecessary conversions.
- request->callback.Run(bitmap_result.bitmap_data.get());
- ClearData(request_id);
- } else {
- FinalizeImage(ToBitmap(bitmap_result.bitmap_data->front(),
- bitmap_result.bitmap_data->size()), request_id);
- }
-}
-
-void ExtensionIconSource::OnImageLoaded(int request_id,
- const gfx::Image& image) {
- if (image.IsEmpty())
- LoadIconFailed(request_id);
- else
- FinalizeImage(image.ToSkBitmap(), request_id);
+ ImageLoader::Get(profile_)->LoadExtensionIconAsync(
+ request->extension,
+ request->size,
+ request->match,
+ request->grayscale,
+ base::Bind(&ExtensionIconSource::OnIconLoaded, AsWeakPtr(), request_id));
}
-void ExtensionIconSource::LoadIconFailed(int request_id) {
+void ExtensionIconSource::OnIconLoaded(int request_id, const gfx::Image& image)
+{
ExtensionIconRequest* request = GetData(request_id);
- ExtensionResource icon = IconsInfo::GetIconResource(
- request->extension, request->size, request->match);
-
- if (request->size == extension_misc::EXTENSION_ICON_BITTY)
- LoadFaviconImage(request_id);
- else
- LoadDefaultImage(request_id);
+ request->callback.Run(ImageLoader::BitmapToMemory(image.ToSkBitmap()).get());
+ ClearData(request_id);
}
bool ExtensionIconSource::ParseData(
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.h b/chrome/browser/ui/webui/extensions/extension_icon_source.h
index 7a94d11..81fe956 100644
--- a/chrome/browser/ui/webui/extensions/extension_icon_source.h
+++ b/chrome/browser/ui/webui/extensions/extension_icon_source.h
@@ -84,44 +84,14 @@ class ExtensionIconSource : public content::URLDataSource,
virtual ~ExtensionIconSource();
- // Returns the bitmap for the default app image.
- const SkBitmap* GetDefaultAppImage();
-
- // Returns the bitmap for the default extension.
- const SkBitmap* GetDefaultExtensionImage();
-
- // Performs any remaining transformations (like desaturating the |image|),
- // then returns the |image| to the client and clears up any temporary data
- // associated with the |request_id|.
- void FinalizeImage(const SkBitmap* image, int request_id);
-
- // Loads the default image for |request_id| and returns to the client.
- void LoadDefaultImage(int request_id);
-
- // Loads the extension's |icon| for the given |request_id| and returns the
+ // Loads the extension's icon for the given |request_id| and returns the
// image to the client.
- void LoadExtensionImage(const ExtensionResource& icon,
- int request_id);
-
- // Loads the favicon image for the app associated with the |request_id|. If
- // the image does not exist, we fall back to the default image.
- void LoadFaviconImage(int request_id);
-
- // FaviconService callback
- void OnFaviconDataAvailable(
- int request_id,
- const chrome::FaviconBitmapResult& bitmap_result);
+ void LoadExtensionImage(int request_id);
- // ImageLoader callback
- void OnImageLoaded(int request_id, const gfx::Image& image);
-
- // Called when the extension doesn't have an icon. We fall back to multiple
- // sources, using the following order:
- // 1) The icons as listed in the extension / app manifests.
- // 2) If a 16px icon and the extension has a launch URL, see if Chrome
- // has a corresponding favicon.
- // 3) If still no matches, load the default extension / application icon.
- void LoadIconFailed(int request_id);
+ // LoadExtensionImage() will load the icon using
+ // ImageLoader::LoadExtensionIconAsync(). OnIconLoaded will be called when
+ // the icon has been loaded.
+ void OnIconLoaded(int request_id, const gfx::Image& image);
// Parses and savse an ExtensionIconRequest for the URL |path| for the
// specified |request_id|.
@@ -146,18 +116,9 @@ class ExtensionIconSource : public content::URLDataSource,
Profile* profile_;
- // Maps tracker ids to request ids.
- std::map<int, int> tracker_map_;
-
// Maps request_ids to ExtensionIconRequests.
std::map<int, ExtensionIconRequest*> request_map_;
- scoped_ptr<SkBitmap> default_app_data_;
-
- scoped_ptr<SkBitmap> default_extension_data_;
-
- CancelableTaskTracker cancelable_task_tracker_;
-
DISALLOW_COPY_AND_ASSIGN(ExtensionIconSource);
};
diff --git a/chrome/test/data/extensions/default_image_loading_tracker/app.json b/chrome/test/data/extensions/default_image_loading_tracker/app.json
new file mode 100644
index 0000000..23855be
--- /dev/null
+++ b/chrome/test/data/extensions/default_image_loading_tracker/app.json
@@ -0,0 +1,5 @@
+{
+ "name": "test",
+ "version": "1",
+ "manifest_version": 2
+}