Commit 74c938f1 authored by tbarzic@chromium.org's avatar tbarzic@chromium.org

taking over xiyuan's patch: http://codereview.chromium.org/10701087/

***************************************************************
chromeos: Fix pixelated icons in app list and launcher (part 2) 

Add an ExtensionIconImage, which loads image that supports DIP 
by having a special ImageSkiaSource that makes 
ImageLoadingTracker to load image for additional DIP scale. 

BUG=131738, 131739
TEST=None. Wait for the last CL to update laucher/app list code to use LoadImageInDIP to verify.

***************************************************************
For gypi files (I reckon this part is trivial enough)
TBR=ben@chromium.org

Review URL: https://chromiumcodereview.appspot.com/10825012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152407 0039d316-1c4b-4281-b951-d872f2087c98
parent 9a499a01
...@@ -113,14 +113,18 @@ void AppShortcutManager::SetShortcutCreationDisabledForTesting(bool disabled) { ...@@ -113,14 +113,18 @@ void AppShortcutManager::SetShortcutCreationDisabledForTesting(bool disabled) {
void AppShortcutManager::InstallApplicationShortcuts( void AppShortcutManager::InstallApplicationShortcuts(
const Extension* extension) { const Extension* extension) {
shortcut_info_ = ShortcutInfoForExtensionAndProfile(extension, profile_); shortcut_info_ = ShortcutInfoForExtensionAndProfile(extension, profile_);
std::vector<ImageLoadingTracker::ImageInfo> info_list;
std::vector<ImageLoadingTracker::ImageRepresentation> info_list;
for (size_t i = 0; i < arraysize(kDesiredSizes); ++i) { for (size_t i = 0; i < arraysize(kDesiredSizes); ++i) {
int size = kDesiredSizes[i]; int size = kDesiredSizes[i];
ExtensionResource resource = extension->GetIconResource( ExtensionResource resource = extension->GetIconResource(
size, ExtensionIconSet::MATCH_EXACTLY); size, ExtensionIconSet::MATCH_EXACTLY);
if (!resource.empty()) { if (!resource.empty()) {
info_list.push_back( info_list.push_back(ImageLoadingTracker::ImageRepresentation(
ImageLoadingTracker::ImageInfo(resource, gfx::Size(size, size))); resource,
ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER,
gfx::Size(size, size),
ui::SCALE_FACTOR_100P));
} }
} }
...@@ -137,8 +141,11 @@ void AppShortcutManager::InstallApplicationShortcuts( ...@@ -137,8 +141,11 @@ void AppShortcutManager::InstallApplicationShortcuts(
resource = extension->GetIconResource( resource = extension->GetIconResource(
size, ExtensionIconSet::MATCH_SMALLER); size, ExtensionIconSet::MATCH_SMALLER);
} }
info_list.push_back( info_list.push_back(ImageLoadingTracker::ImageRepresentation(
ImageLoadingTracker::ImageInfo(resource, gfx::Size(size, size))); resource,
ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER,
gfx::Size(size, size),
ui::SCALE_FACTOR_100P));
} }
// |icon_resources| may still be empty at this point, in which case LoadImage // |icon_resources| may still be empty at this point, in which case LoadImage
......
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_icon_image.h"
#include <vector>
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
#include "content/public/browser/notification_service.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia_source.h"
#include "ui/gfx/size.h"
namespace {
const int kMatchBiggerTreshold = 32;
ExtensionResource GetExtensionIconResource(
const extensions::Extension* extension,
const ExtensionIconSet& icons,
int size,
ExtensionIconSet::MatchType match_type) {
std::string path = icons.Get(size, match_type);
if (path.empty())
return ExtensionResource();
return extension->GetResource(path);
}
} // namespace
namespace extensions {
////////////////////////////////////////////////////////////////////////////////
// ExtensionIconImage::Source
class IconImage::Source : public gfx::ImageSkiaSource {
public:
explicit Source(IconImage* host);
virtual ~Source();
void ResetHost();
private:
// gfx::ImageSkiaSource overrides:
virtual gfx::ImageSkiaRep GetImageForScale(
ui::ScaleFactor scale_factor) OVERRIDE;
IconImage* host_;
DISALLOW_COPY_AND_ASSIGN(Source);
};
IconImage::Source::Source(IconImage* host) : host_(host) {
}
IconImage::Source::~Source() {
}
void IconImage::Source::ResetHost() {
host_ = NULL;
}
gfx::ImageSkiaRep IconImage::Source::GetImageForScale(
ui::ScaleFactor scale_factor) {
if (host_)
host_->LoadImageForScaleFactor(scale_factor);
return gfx::ImageSkiaRep();
}
////////////////////////////////////////////////////////////////////////////////
// ExtensionIconImage
IconImage::IconImage(
const Extension* extension,
const ExtensionIconSet& icon_set,
int resource_size_in_dip,
Observer* observer)
: extension_(extension),
icon_set_(icon_set),
resource_size_in_dip_(resource_size_in_dip),
desired_size_in_dip_(resource_size_in_dip, resource_size_in_dip),
observer_(observer),
source_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) {
source_ = new Source(this);
image_skia_ = gfx::ImageSkia(source_, desired_size_in_dip_);
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
content::NotificationService::AllSources());
}
IconImage::~IconImage() {
// |source_| could be NULL if resource does not exist.
if (source_)
source_->ResetHost();
}
void IconImage::LoadImageForScaleFactor(ui::ScaleFactor scale_factor) {
// Do nothing if extension is unloaded.
if (!extension_)
return;
const float scale = ui::GetScaleFactorScale(scale_factor);
const int resource_size_in_pixel =
static_cast<int>(resource_size_in_dip_ * scale);
ExtensionResource resource;
// We try loading bigger image only if resource size is >= 32.
if (resource_size_in_pixel >= kMatchBiggerTreshold) {
resource = GetExtensionIconResource(extension_, icon_set_,
resource_size_in_pixel, ExtensionIconSet::MATCH_BIGGER);
}
// If resource is not found by now, try matching smaller one.
if (resource.empty()) {
resource = GetExtensionIconResource(extension_, icon_set_,
resource_size_in_pixel, ExtensionIconSet::MATCH_SMALLER);
}
// If there is no resource found, bail out and notify observer of failure.
if (resource.empty()) {
if (observer_)
observer_->OnIconImageLoadFailed(this, scale_factor);
return;
}
int id = tracker_.next_id();
load_map_[id] = scale_factor;
std::vector<ImageLoadingTracker::ImageRepresentation> info_list;
info_list.push_back(ImageLoadingTracker::ImageRepresentation(
resource,
ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER,
desired_size_in_dip_.Scale(scale),
scale_factor));
tracker_.LoadImages(extension_, info_list, ImageLoadingTracker::DONT_CACHE);
}
void IconImage::OnImageLoaded(const gfx::Image& image,
const std::string& extension_id,
int index) {
LoadMap::iterator load_map_it = load_map_.find(index);
DCHECK(load_map_it != load_map_.end());
ui::ScaleFactor scale_factor = load_map_it->second;
load_map_.erase(load_map_it);
if (image.IsEmpty()) {
// There waas an error loading the image.
if (observer_)
observer_->OnIconImageLoadFailed(this, scale_factor);
return;
}
DCHECK(image.ToImageSkia()->HasRepresentation(scale_factor));
gfx::ImageSkiaRep rep = image.ToImageSkia()->GetRepresentation(scale_factor);
DCHECK(!rep.is_null());
image_skia_.AddRepresentation(rep);
if (observer_)
observer_->OnExtensionIconImageChanged(this);
}
void IconImage::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_UNLOADED);
const Extension* extension =
content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
if (extension_ == extension)
extension_ = NULL;
}
} // namespace extensions
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ICON_IMAGE_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ICON_IMAGE_H_
#include <map>
#include <string>
#include "base/basictypes.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/common/extensions/extension_icon_set.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "ui/gfx/image/image_skia.h"
namespace extensions {
class Extension;
}
namespace gfx {
class Size;
}
namespace extensions {
// A class that provides an ImageSkia for UI code to use. It handles extension
// icon resource loading, screen scale factor change etc. UI code that uses
// extension icon should host this class and be its observer. ExtensionIconImage
// should be outlived by the observer. In painting code, UI code paints with the
// ImageSkia provided by this class. If required extension icon resource is not
// present, this class uses ImageLoadingTracker to load it and call on its
// observer interface when the resource is loaded.
class IconImage : public ImageLoadingTracker::Observer,
public content::NotificationObserver {
public:
class Observer {
public:
// Invoked when a new image rep for an additional scale factor
// is loaded and added to |image|.
virtual void OnExtensionIconImageChanged(IconImage* image) = 0;
// Invoked when the icon image couldn't be loaded.
virtual void OnIconImageLoadFailed(IconImage* image,
ui::ScaleFactor scale_factor) = 0;
protected:
virtual ~Observer() {}
};
IconImage(const Extension* extension,
const ExtensionIconSet& icon_set,
int resource_size_in_dip,
Observer* observer);
virtual ~IconImage();
const gfx::ImageSkia& image_skia() const { return image_skia_; }
private:
class Source;
typedef std::map<int, ui::ScaleFactor> LoadMap;
// Loads bitmap for additional scale factor.
void LoadImageForScaleFactor(ui::ScaleFactor scale_factor);
// ImageLoadingTracker::Observer overrides:
virtual void OnImageLoaded(const gfx::Image& image,
const std::string& extension_id,
int index) OVERRIDE;
// content::NotificationObserver overrides:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
const Extension* extension_;
const ExtensionIconSet& icon_set_;
const int resource_size_in_dip_;
const gfx::Size desired_size_in_dip_;
Observer* observer_;
Source* source_; // Owned by ImageSkia storage.
gfx::ImageSkia image_skia_;
ImageLoadingTracker tracker_;
content::NotificationRegistrar registrar_;
LoadMap load_map_;
DISALLOW_COPY_AND_ASSIGN(IconImage);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ICON_IMAGE_H_
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_icon_image.h"
#include "base/json/json_file_value_serializer.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread;
using extensions::Extension;
using extensions::IconImage;
namespace {
class ExtensionIconImageTest : public testing::Test,
public IconImage::Observer {
public:
ExtensionIconImageTest()
: image_loaded_count_(0),
image_load_failure_count_(0),
quit_in_image_loaded_(false),
ui_thread_(BrowserThread::UI, &ui_loop_),
file_thread_(BrowserThread::FILE),
io_thread_(BrowserThread::IO) {
}
virtual ~ExtensionIconImageTest() {}
void WaitForImageLoad() {
// ExtensionIconImage may return synchronously, in which case there's
// nothing to wait for.
if (image_loaded_count_ > 0 || image_load_failure_count_ > 0)
return;
quit_in_image_loaded_ = true;
MessageLoop::current()->Run();
quit_in_image_loaded_ = false;
}
int ImageLoadedCount() {
int result = image_loaded_count_;
image_loaded_count_ = 0;
return result;
}
int ImageLoadFailureCount() {
int result = image_load_failure_count_;
image_load_failure_count_ = 0;
return result;
}
scoped_refptr<Extension> CreateExtension(const char* name,
Extension::Location location) {
// Create and load an extension.
FilePath test_file;
if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) {
EXPECT_FALSE(true);
return NULL;
}
test_file = test_file.AppendASCII("extensions").AppendASCII(name);
int error_code = 0;
std::string error;
JSONFileValueSerializer serializer(test_file.AppendASCII("app.json"));
scoped_ptr<DictionaryValue> valid_value(
static_cast<DictionaryValue*>(serializer.Deserialize(&error_code,
&error)));
EXPECT_EQ(0, error_code) << error;
if (error_code != 0)
return NULL;
EXPECT_TRUE(valid_value.get());
if (!valid_value.get())
return NULL;
return Extension::Create(test_file, location, *valid_value,
Extension::NO_FLAGS, &error);
}
// testing::Test overrides:
virtual void SetUp() OVERRIDE {
file_thread_.Start();
io_thread_.Start();
}
// IconImage::Delegate overrides:
virtual void OnExtensionIconImageChanged(IconImage* image) OVERRIDE {
image_loaded_count_++;
if (quit_in_image_loaded_)
MessageLoop::current()->Quit();
}
virtual void OnIconImageLoadFailed(IconImage* image,
ui::ScaleFactor scale_factor) OVERRIDE {
image_load_failure_count_++;
if (quit_in_image_loaded_)
MessageLoop::current()->Quit();
}
private:
int image_loaded_count_;
int image_load_failure_count_;
bool quit_in_image_loaded_;
MessageLoop ui_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
content::TestBrowserThread io_thread_;
DISALLOW_COPY_AND_ASSIGN(ExtensionIconImageTest);
};
} // namespace
TEST_F(ExtensionIconImageTest, Basic) {
scoped_refptr<Extension> extension(CreateExtension(
"extension_icon_image", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
IconImage image(
extension,
extension->icons(),
extension_misc::EXTENSION_ICON_BITTY,
this);
// No representations in |image_| yet.
gfx::ImageSkia::ImageSkiaReps image_reps = image.image_skia().image_reps();
ASSERT_EQ(0u, image_reps.size());
// Gets representation for a scale factor.
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_100P);
WaitForImageLoad();
EXPECT_EQ(1, ImageLoadedCount());
EXPECT_EQ(0, ImageLoadFailureCount());
// Gets representation for an additional scale factor.
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
WaitForImageLoad();
EXPECT_EQ(1, ImageLoadedCount());
EXPECT_EQ(0, ImageLoadFailureCount());
gfx::ImageSkiaRep image_rep =
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_100P);
EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY,
image_rep.pixel_width());
image_rep = image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
EXPECT_EQ(extension_misc::EXTENSION_ICON_SMALL,
image_rep.pixel_width());
}
// If we can't load icon with the exact size, but a bigger resource is
// available.
TEST_F(ExtensionIconImageTest, FallbackToBigger) {
scoped_refptr<Extension> extension(CreateExtension(
"extension_icon_image", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
IconImage image(
extension,
extension->icons(),
extension_misc::EXTENSION_ICON_BITTY,
this);
// Get representation for 2x.
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
WaitForImageLoad();
EXPECT_EQ(1, ImageLoadedCount());
EXPECT_EQ(0, ImageLoadFailureCount());
gfx::ImageSkiaRep image_rep =
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
// We should have found a bigger resource and it should have been resized.
EXPECT_EQ(ui::SCALE_FACTOR_200P, image_rep.scale_factor());
EXPECT_EQ(2 * extension_misc::EXTENSION_ICON_BITTY,
image_rep.pixel_width());
}
// There is no resource with either exact or bigger size, but there is a smaller
// resource.
TEST_F(ExtensionIconImageTest, FallbackToSmallerWhenNoBigger) {
scoped_refptr<Extension> extension(CreateExtension(
"extension_icon_image", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
IconImage image(
extension,
extension->icons(),
extension_misc::EXTENSION_ICON_SMALL,
this);
// Attempt to get representation for 2x.
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
WaitForImageLoad();
EXPECT_EQ(1, ImageLoadedCount());
EXPECT_EQ(0, ImageLoadFailureCount());
gfx::ImageSkiaRep image_rep =
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
// We should have loaded the biggest smaller resource. In this case the
// loaded resource should not be resized.
EXPECT_EQ(ui::SCALE_FACTOR_200P, image_rep.scale_factor());
EXPECT_EQ(extension_misc::EXTENSION_ICON_MEDIUM,
image_rep.pixel_width());
}
// There is no resource with exact size, but there is a smaller and a bigger
// one. Requested size is smaller than 32 though, so the smaller resource should
// be loaded.
TEST_F(ExtensionIconImageTest, FallbackToSmaller) {
scoped_refptr<Extension> extension(CreateExtension(
"extension_icon_image", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
IconImage image(
extension,
extension->icons(),
17,
this);
// Attempt to get representation for 1x.
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_100P);
WaitForImageLoad();
EXPECT_EQ(1, ImageLoadedCount());
EXPECT_EQ(0, ImageLoadFailureCount());
gfx::ImageSkiaRep image_rep =
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_100P);
// We should have loaded smaller (not resized) resource.
EXPECT_EQ(ui::SCALE_FACTOR_100P, image_rep.scale_factor());
EXPECT_EQ(extension_misc::EXTENSION_ICON_BITTY,
image_rep.pixel_width());
}
// If resource set is empty, failure should be reported.
TEST_F(ExtensionIconImageTest, NoResources) {
scoped_refptr<Extension> extension(CreateExtension(
"extension_icon_image", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL);
ExtensionIconSet empty_icon_set;
IconImage image(
extension,
empty_icon_set,
extension_misc::EXTENSION_ICON_SMALLISH,
this);
// Attempt to get representation for 2x.
image.image_skia().GetRepresentation(ui::SCALE_FACTOR_200P);
WaitForImageLoad();
EXPECT_EQ(0, ImageLoadedCount());
EXPECT_EQ(1, ImageLoadFailureCount());
}
...@@ -20,28 +20,78 @@ ...@@ -20,28 +20,78 @@
#include "grit/theme_resources.h" #include "grit/theme_resources.h"
#include "skia/ext/image_operations.h" #include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/image/image_skia_rep.h"
#include "webkit/glue/image_decoder.h" #include "webkit/glue/image_decoder.h"
using content::BrowserThread; using content::BrowserThread;
using extensions::Extension; using extensions::Extension;
namespace {
struct ComponentExtensionResource {
const char* extension_id;
const int resource_id;
};
const ComponentExtensionResource kSpecialComponentExtensionResources[] = {
{ extension_misc::kWebStoreAppId, IDR_WEBSTORE_ICON },
{ extension_misc::kChromeAppId, IDR_PRODUCT_LOGO_128 },
};
// Finds special component extension resource id for given extension id.
bool FindSpecialExtensionResourceId(const std::string& extension_id,
int* out_resource_id) {
for (size_t i = 0; i < arraysize(kSpecialComponentExtensionResources); ++i) {
if (extension_id == kSpecialComponentExtensionResources[i].extension_id) {
if (out_resource_id)
*out_resource_id = kSpecialComponentExtensionResources[i].resource_id;
return true;
}
}
return false;
}
bool ShouldResizeImageRepresentation(
ImageLoadingTracker::ImageRepresentation::ResizeCondition resize_method,
const gfx::Size& decoded_size,
const gfx::Size& desired_size) {
switch (resize_method) {
case ImageLoadingTracker::ImageRepresentation::ALWAYS_RESIZE:
return decoded_size != desired_size;
case ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER:
return decoded_size.width() > desired_size.width() ||
decoded_size.height() > desired_size.height();
default:
NOTREACHED();
return false;
}
}
} // namespace
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ImageLoadingTracker::Observer // ImageLoadingTracker::Observer
ImageLoadingTracker::Observer::~Observer() {} ImageLoadingTracker::Observer::~Observer() {}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ImageLoadingTracker::ImageInfo // ImageLoadingTracker::ImageRepresentation
ImageLoadingTracker::ImageInfo::ImageInfo( ImageLoadingTracker::ImageRepresentation::ImageRepresentation(
const ExtensionResource& resource, gfx::Size max_size) const ExtensionResource& resource,
: resource(resource), max_size(max_size) { ResizeCondition resize_method,
const gfx::Size& desired_size,
ui::ScaleFactor scale_factor)
: resource(resource),
resize_method(resize_method),
desired_size(desired_size),
scale_factor(scale_factor) {
} }
ImageLoadingTracker::ImageInfo::~ImageInfo() { ImageLoadingTracker::ImageRepresentation::~ImageRepresentation() {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
...@@ -58,8 +108,8 @@ ImageLoadingTracker::PendingLoadInfo::~PendingLoadInfo() {} ...@@ -58,8 +108,8 @@ ImageLoadingTracker::PendingLoadInfo::~PendingLoadInfo() {}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// ImageLoadingTracker::ImageLoader // ImageLoadingTracker::ImageLoader
// A RefCounted class for loading images on the File thread and reporting back // A RefCounted class for loading bitmaps/image reps on the File thread and
// on the UI thread. // reporting back on the UI thread.
class ImageLoadingTracker::ImageLoader class ImageLoadingTracker::ImageLoader
: public base::RefCountedThreadSafe<ImageLoader> { : public base::RefCountedThreadSafe<ImageLoader> {
public: public:
...@@ -76,106 +126,104 @@ class ImageLoadingTracker::ImageLoader ...@@ -76,106 +126,104 @@ class ImageLoadingTracker::ImageLoader
} }
// Instructs the loader to load a task on the File thread. // Instructs the loader to load a task on the File thread.
void LoadImage(const ExtensionResource& resource, void LoadImage(const ImageRepresentation& image_info, int id) {
const gfx::Size& max_size, DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
int id) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE));
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE, BrowserThread::FILE, FROM_HERE,
base::Bind(&ImageLoader::LoadOnFileThread, this, resource, base::Bind(&ImageLoader::LoadOnFileThread, this, image_info, id));
max_size, id));
} }
void LoadOnFileThread(const ExtensionResource& resource, void LoadOnFileThread(const ImageRepresentation& image_info, int id) {
const gfx::Size& max_size,
int id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
// Read the file from disk. // Read the file from disk.
std::string file_contents; std::string file_contents;
FilePath path = resource.GetFilePath(); FilePath path = image_info.resource.GetFilePath();
if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) { if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) {
ReportBack(NULL, resource, gfx::Size(), id); ReportBack(NULL, image_info, gfx::Size(), id, false);
return; return;
} }
// Decode the image using WebKit's image decoder. // Decode the bitmap using WebKit's image decoder.
const unsigned char* data = const unsigned char* data =
reinterpret_cast<const unsigned char*>(file_contents.data()); reinterpret_cast<const unsigned char*>(file_contents.data());
webkit_glue::ImageDecoder decoder; webkit_glue::ImageDecoder decoder;
scoped_ptr<SkBitmap> decoded(new SkBitmap()); scoped_ptr<SkBitmap> decoded(new SkBitmap());
// Note: This class only decodes images from extension resources. Chrome // Note: This class only decodes bitmaps from extension resources. Chrome
// doesn't (for security reasons) directly load extension resources provided // doesn't (for security reasons) directly load extension resources provided
// by the extension author, but instead decodes them in a separate // by the extension author, but instead decodes them in a separate
// locked-down utility process. Only if the decoding succeeds is the image // locked-down utility process. Only if the decoding succeeds is the image
// saved from memory to disk and subsequently used in the Chrome UI. // saved from memory to disk and subsequently used in the Chrome UI.
// Chrome is therefore decoding images here that were generated by Chrome. // Chrome is therefore decoding bitmaps here that were generated by Chrome.
*decoded = decoder.Decode(data, file_contents.length()); *decoded = decoder.Decode(data, file_contents.length());
if (decoded->empty()) { if (decoded->empty()) {
ReportBack(NULL, resource, gfx::Size(), id); ReportBack(NULL, image_info, gfx::Size(), id, false);
return; // Unable to decode. return; // Unable to decode.
} }
gfx::Size original_size(decoded->width(), decoded->height()); gfx::Size original_size(decoded->width(), decoded->height());
if (ShouldResizeImageRepresentation(image_info.resize_method,
if (decoded->width() > max_size.width() || original_size,
decoded->height() > max_size.height()) { image_info.desired_size)) {
// The bitmap is too big, re-sample.
*decoded = skia::ImageOperations::Resize( *decoded = skia::ImageOperations::Resize(
*decoded, skia::ImageOperations::RESIZE_LANCZOS3, *decoded, skia::ImageOperations::RESIZE_LANCZOS3,
max_size.width(), max_size.height()); image_info.desired_size.width(), image_info.desired_size.height());
} }
ReportBack(decoded.release(), resource, original_size, id); ReportBack(decoded.release(), image_info, original_size, id,
true /* delete bitmap */);
} }
// Instructs the loader to load a resource on the File thread. // Instructs the loader to load a resource on the File thread.
void LoadResource(const ExtensionResource& resource, void LoadResource(const ImageRepresentation& image_info,
const gfx::Size& max_size,
int id, int id,
int resource_id) { int resource_id) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE, BrowserThread::FILE, FROM_HERE,
base::Bind(&ImageLoader::LoadResourceOnFileThread, this, resource, base::Bind(&ImageLoader::LoadResourceOnFileThread, this, image_info,
max_size, id, resource_id)); id, resource_id));
} }
void LoadResourceOnFileThread(const ExtensionResource& resource, void LoadResourceOnFileThread(const ImageRepresentation& image_info,
const gfx::Size& max_size,
int id, int id,
int resource_id) { int resource_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
SkBitmap* image = ExtensionIconSource::LoadImageByResourceId( const SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetImageNamed(
resource_id); resource_id).ToSkBitmap();
ReportBack(image, resource, max_size, id); ReportBack(bitmap, image_info, image_info.desired_size, id,
false /* don't delete bitmap */);
} }
void ReportBack(SkBitmap* image, const ExtensionResource& resource, void ReportBack(const SkBitmap* bitmap, const ImageRepresentation& image_info,
const gfx::Size& original_size, int id) { const gfx::Size& original_size, int id, bool delete_bitmap) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
BrowserThread::PostTask( BrowserThread::PostTask(
callback_thread_id_, FROM_HERE, callback_thread_id_, FROM_HERE,
base::Bind(&ImageLoader::ReportOnUIThread, this, base::Bind(&ImageLoader::ReportOnCallingThread, this,
image, resource, original_size, id)); bitmap, image_info, original_size, id, delete_bitmap));
} }
void ReportOnUIThread(SkBitmap* image, const ExtensionResource& resource, void ReportOnCallingThread(const SkBitmap* bitmap,
const gfx::Size& original_size, int id) { const ImageRepresentation& image_info,
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); const gfx::Size& original_size,
int id,
bool delete_bitmap) {
DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
if (tracker_) if (tracker_)
tracker_->OnImageLoaded(image, resource, original_size, id, true); tracker_->OnBitmapLoaded(bitmap, image_info, original_size, id, true);
delete image; if (bitmap && delete_bitmap)
delete bitmap;
} }
private: private:
friend class base::RefCountedThreadSafe<ImageLoader>; friend class base::RefCountedThreadSafe<ImageLoader>;
~ImageLoader() {} ~ImageLoader() {}
// The tracker we are loading the image for. If NULL, it means the tracker is // The tracker we are loading the bitmap for. If NULL, it means the tracker is
// no longer interested in the reply. // no longer interested in the reply.
ImageLoadingTracker* tracker_; ImageLoadingTracker* tracker_;
...@@ -206,14 +254,19 @@ void ImageLoadingTracker::LoadImage(const Extension* extension, ...@@ -206,14 +254,19 @@ void ImageLoadingTracker::LoadImage(const Extension* extension,
const ExtensionResource& resource, const ExtensionResource& resource,
const gfx::Size& max_size, const gfx::Size& max_size,
CacheParam cache) { CacheParam cache) {
std::vector<ImageInfo> info_list; std::vector<ImageRepresentation> info_list;
info_list.push_back(ImageInfo(resource, max_size)); info_list.push_back(ImageRepresentation(
resource,
ImageRepresentation::RESIZE_WHEN_LARGER,
max_size,
ui::SCALE_FACTOR_100P));
LoadImages(extension, info_list, cache); LoadImages(extension, info_list, cache);
} }
void ImageLoadingTracker::LoadImages(const Extension* extension, void ImageLoadingTracker::LoadImages(
const std::vector<ImageInfo>& info_list, const Extension* extension,
CacheParam cache) { const std::vector<ImageRepresentation>& info_list,
CacheParam cache) {
PendingLoadInfo load_info; PendingLoadInfo load_info;
load_info.extension = extension; load_info.extension = extension;
load_info.cache = cache; load_info.cache = cache;
...@@ -222,37 +275,32 @@ void ImageLoadingTracker::LoadImages(const Extension* extension, ...@@ -222,37 +275,32 @@ void ImageLoadingTracker::LoadImages(const Extension* extension,
int id = next_id_++; int id = next_id_++;
load_map_[id] = load_info; load_map_[id] = load_info;
for (std::vector<ImageInfo>::const_iterator it = info_list.begin(); for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
it != info_list.end(); ++it) { it != info_list.end(); ++it) {
int resource_id = -1;
// Load resources for special component extensions. // Load resources for special component extensions.
if (load_info.extension_id == extension_misc::kWebStoreAppId) { if (FindSpecialExtensionResourceId(load_info.extension_id, &resource_id)) {
if (!loader_)
loader_ = new ImageLoader(this);
loader_->LoadResource(it->resource, it->max_size, id, IDR_WEBSTORE_ICON);
continue;
} else if (load_info.extension_id == extension_misc::kChromeAppId) {
if (!loader_) if (!loader_)
loader_ = new ImageLoader(this); loader_ = new ImageLoader(this);
loader_->LoadResource(it->resource, loader_->LoadResource(*it, id, resource_id);
it->max_size,
id,
IDR_PRODUCT_LOGO_128);
continue; continue;
} }
// If we don't have a path we don't need to do any further work, just // If we don't have a path we don't need to do any further work, just
// respond back. // respond back.
if (it->resource.relative_path().empty()) { if (it->resource.relative_path().empty()) {
OnImageLoaded(NULL, it->resource, it->max_size, id, false); OnBitmapLoaded(NULL, *it, it->desired_size, id, false);
continue; continue;
} }
DCHECK(extension->path() == it->resource.extension_root()); DCHECK(extension->path() == it->resource.extension_root());
// See if the extension has the image already. // See if the extension has the bitmap already.
if (extension->HasCachedImage(it->resource, it->max_size)) { if (extension->HasCachedImage(it->resource, it->desired_size)) {
SkBitmap image = extension->GetCachedImage(it->resource, it->max_size); SkBitmap bitmap = extension->GetCachedImage(it->resource,
OnImageLoaded(&image, it->resource, it->max_size, id, false); it->desired_size);
OnBitmapLoaded(&bitmap, *it, it->desired_size, id, false);
continue; continue;
} }
...@@ -261,11 +309,10 @@ void ImageLoadingTracker::LoadImages(const Extension* extension, ...@@ -261,11 +309,10 @@ void ImageLoadingTracker::LoadImages(const Extension* extension,
if (!loader_) if (!loader_)
loader_ = new ImageLoader(this); loader_ = new ImageLoader(this);
int resource_id;
if (IsComponentExtensionResource(extension, it->resource, resource_id)) if (IsComponentExtensionResource(extension, it->resource, resource_id))
loader_->LoadResource(it->resource, it->max_size, id, resource_id); loader_->LoadResource(*it, id, resource_id);
else else
loader_->LoadImage(it->resource, it->max_size, id); loader_->LoadImage(*it, id);
} }
} }
...@@ -293,9 +340,9 @@ bool ImageLoadingTracker::IsComponentExtensionResource( ...@@ -293,9 +340,9 @@ bool ImageLoadingTracker::IsComponentExtensionResource(
return false; return false;
} }
void ImageLoadingTracker::OnImageLoaded( void ImageLoadingTracker::OnBitmapLoaded(
SkBitmap* image, const SkBitmap* bitmap,
const ExtensionResource& resource, const ImageRepresentation& image_info,
const gfx::Size& original_size, const gfx::Size& original_size,
int id, int id,
bool should_cache) { bool should_cache) {
...@@ -304,38 +351,33 @@ void ImageLoadingTracker::OnImageLoaded( ...@@ -304,38 +351,33 @@ void ImageLoadingTracker::OnImageLoaded(
PendingLoadInfo* info = &load_map_it->second; PendingLoadInfo* info = &load_map_it->second;
// Save the pending results. // Save the pending results.
DCHECK(info->pending_count > 0); DCHECK_GT(info->pending_count, 0u);
info->pending_count--; info->pending_count--;
if (image) if (bitmap) {
info->bitmaps.push_back(*image); info->image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap,
image_info.scale_factor));
}
// Add to the extension's image cache if requested. // Add to the extension's bitmap cache if requested.
DCHECK(info->cache != CACHE || info->extension); DCHECK(info->cache != CACHE || info->extension);
if (should_cache && info->cache == CACHE && !resource.empty() && if (should_cache && info->cache == CACHE && !image_info.resource.empty() &&
!info->extension->HasCachedImage(resource, original_size)) { !info->extension->HasCachedImage(image_info.resource, original_size)) {
info->extension->SetCachedImage(resource, image ? *image : SkBitmap(), info->extension->SetCachedImage(image_info.resource,
bitmap ? *bitmap : SkBitmap(),
original_size); original_size);
} }
// If all pending images are done then report back. // If all pending bitmaps are done then report back.
if (info->pending_count == 0) { if (info->pending_count == 0) {
gfx::Image image; gfx::Image image;
std::string extension_id = info->extension_id; std::string extension_id = info->extension_id;
if (info->bitmaps.size() > 0) { if (!info->image_skia.empty())
gfx::ImageSkia image_skia; image = gfx::Image(info->image_skia);
for (std::vector<SkBitmap>::const_iterator it = info->bitmaps.begin();
it != info->bitmaps.end(); ++it) {
// TODO(pkotwicz): Do something better but ONLY when DIP is enabled.
image_skia.AddRepresentation(
gfx::ImageSkiaRep(*it, ui::SCALE_FACTOR_100P));
}
image = gfx::Image(image_skia);
}
load_map_.erase(load_map_it); load_map_.erase(load_map_it);
// ImageLoadingTracker might be deleted after the callback so don't // ImageLoadingTracker might be deleted after the callback so don't do
// anything after this statement. // anything after this statement.
observer_->OnImageLoaded(image, extension_id, id); observer_->OnImageLoaded(image, extension_id, id);
} }
...@@ -350,7 +392,7 @@ void ImageLoadingTracker::Observe(int type, ...@@ -350,7 +392,7 @@ void ImageLoadingTracker::Observe(int type,
content::Details<extensions::UnloadedExtensionInfo>(details)->extension; content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
// Remove reference to this extension from all pending load entries. This // Remove reference to this extension from all pending load entries. This
// ensures we don't attempt to cache the image when the load completes. // ensures we don't attempt to cache the bitmap when the load completes.
for (LoadMap::iterator i = load_map_.begin(); i != load_map_.end(); ++i) { for (LoadMap::iterator i = load_map_.begin(); i != load_map_.end(); ++i) {
PendingLoadInfo* info = &i->second; PendingLoadInfo* info = &i->second;
if (info->extension == extension) { if (info->extension == extension) {
......
...@@ -6,13 +6,18 @@ ...@@ -6,13 +6,18 @@
#define CHROME_BROWSER_EXTENSIONS_IMAGE_LOADING_TRACKER_H_ #define CHROME_BROWSER_EXTENSIONS_IMAGE_LOADING_TRACKER_H_
#include <map> #include <map>
#include <string>
#include <vector>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "chrome/common/extensions/extension_icon_set.h"
#include "chrome/common/extensions/extension_resource.h" #include "chrome/common/extensions/extension_resource.h"
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "ui/base/layout.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/size.h" #include "ui/gfx/size.h"
class SkBitmap; class SkBitmap;
...@@ -35,7 +40,7 @@ class Image; ...@@ -35,7 +40,7 @@ class Image;
// Observer::OnImageLoaded and call: // Observer::OnImageLoaded and call:
// tracker_.LoadImage(extension, resource, max_size, false); // tracker_.LoadImage(extension, resource, max_size, false);
// ... and wait for OnImageLoaded to be called back on you with a pointer to the // ... and wait for OnImageLoaded to be called back on you with a pointer to the
// SkBitmap loaded. // ImageSkia loaded.
// NOTE: if the image is available already (or the resource is not valid), the // NOTE: if the image is available already (or the resource is not valid), the
// Observer is notified immediately from the call to LoadImage. In other words, // Observer is notified immediately from the call to LoadImage. In other words,
// by the time LoadImage returns the observer has been notified. // by the time LoadImage returns the observer has been notified.
...@@ -62,14 +67,33 @@ class ImageLoadingTracker : public content::NotificationObserver { ...@@ -62,14 +67,33 @@ class ImageLoadingTracker : public content::NotificationObserver {
virtual ~Observer(); virtual ~Observer();
}; };
// Information about a single image to load from a extension resource. // Information about a singe image representation to load from an extension
struct ImageInfo { // resource.
ImageInfo(const ExtensionResource& resource, gfx::Size max_size); struct ImageRepresentation {
~ImageInfo(); // Enum values to indicate whether to resize loaded bitmap when it is larger
// than |desired_size| or always resize it.
enum ResizeCondition {
RESIZE_WHEN_LARGER,
ALWAYS_RESIZE,
};
ImageRepresentation(const ExtensionResource& resource,
ResizeCondition resize_method,
const gfx::Size& desired_size,
ui::ScaleFactor scale_factor);
~ImageRepresentation();
// Extension resource to load.
ExtensionResource resource; ExtensionResource resource;
// If the loaded image is larger than |max_size| it will be resized to those
// dimensions. ResizeCondition resize_method;
gfx::Size max_size;
// When |resize_method| is ALWAYS_RESIZE or when the loaded image is larger
// than |desired_size| it will be resized to these dimensions.
gfx::Size desired_size;
// |scale_factor| is used to construct the loaded gfx::ImageSkia.
ui::ScaleFactor scale_factor;
}; };
explicit ImageLoadingTracker(Observer* observer); explicit ImageLoadingTracker(Observer* observer);
...@@ -79,6 +103,8 @@ class ImageLoadingTracker : public content::NotificationObserver { ...@@ -79,6 +103,8 @@ class ImageLoadingTracker : public content::NotificationObserver {
// |max_size| it will be resized to those dimensions. IMPORTANT NOTE: this // |max_size| it will be resized to those dimensions. IMPORTANT NOTE: this
// function may call back your observer synchronously (ie before it returns) // function may call back your observer synchronously (ie before it returns)
// if the image was found in the cache. // if the image was found in the cache.
// Note this method loads a raw bitmap from the resource. All sizes given are
// assumed to be in pixels.
void LoadImage(const extensions::Extension* extension, void LoadImage(const extensions::Extension* extension,
const ExtensionResource& resource, const ExtensionResource& resource,
const gfx::Size& max_size, const gfx::Size& max_size,
...@@ -88,7 +114,7 @@ class ImageLoadingTracker : public content::NotificationObserver { ...@@ -88,7 +114,7 @@ class ImageLoadingTracker : public content::NotificationObserver {
// extension. This is used to load multiple resolutions of the same image // extension. This is used to load multiple resolutions of the same image
// type. // type.
void LoadImages(const extensions::Extension* extension, void LoadImages(const extensions::Extension* extension,
const std::vector<ImageInfo>& info_list, const std::vector<ImageRepresentation>& info_list,
CacheParam cache); CacheParam cache);
// Returns the ID used for the next image that is loaded. That is, the return // Returns the ID used for the next image that is loaded. That is, the return
...@@ -97,18 +123,19 @@ class ImageLoadingTracker : public content::NotificationObserver { ...@@ -97,18 +123,19 @@ class ImageLoadingTracker : public content::NotificationObserver {
int next_id() const { return next_id_; } int next_id() const { return next_id_; }
private: private:
// Information for pending image load operation for one or more images. // Information for pending resource load operation for one or more image
// representations.
struct PendingLoadInfo { struct PendingLoadInfo {
PendingLoadInfo(); PendingLoadInfo();
~PendingLoadInfo(); ~PendingLoadInfo();
const extensions::Extension* extension; const extensions::Extension* extension;
// This is cached separate from |extension| in case the extension in // This is cached separate from |extension| in case the extension is
// unloaded. // unloaded.
std::string extension_id; std::string extension_id;
CacheParam cache; CacheParam cache;
size_t pending_count; size_t pending_count;
std::vector<SkBitmap> bitmaps; gfx::ImageSkia image_skia;
}; };
// Maps an integer identifying a load request to a PendingLoadInfo. // Maps an integer identifying a load request to a PendingLoadInfo.
...@@ -116,14 +143,13 @@ class ImageLoadingTracker : public content::NotificationObserver { ...@@ -116,14 +143,13 @@ class ImageLoadingTracker : public content::NotificationObserver {
class ImageLoader; class ImageLoader;
// When an image has finished loaded and been resized on the file thread, it // Called on the calling thread when the bitmap finishes loading.
// is posted back to this method on the original thread. This method then // |bitmap| may be null if the image file failed to decode.
// calls the observer's OnImageLoaded and deletes the ImageLoadingTracker if void OnBitmapLoaded(const SkBitmap* bitmap,
// it was the last image in the list. The |original_size| should be the size const ImageRepresentation& image_info,
// of the image before any resizing was done. const gfx::Size& original_size,
// |image| may be null if the file failed to decode. int id,
void OnImageLoaded(SkBitmap* image, const ExtensionResource& resource, bool should_cache);
const gfx::Size& original_size, int id, bool should_cache);
// Checks whether image is a component extension resource. Returns false // Checks whether image is a component extension resource. Returns false
// if a given |resource| does not have a corresponding image in bundled // if a given |resource| does not have a corresponding image in bundled
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/extensions/image_loading_tracker.h"
#include "base/json/json_file_value_serializer.h" #include "base/json/json_file_value_serializer.h"
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
...@@ -198,14 +199,17 @@ TEST_F(ImageLoadingTrackerTest, MultipleImages) { ...@@ -198,14 +199,17 @@ TEST_F(ImageLoadingTrackerTest, MultipleImages) {
"image_loading_tracker", Extension::INVALID)); "image_loading_tracker", Extension::INVALID));
ASSERT_TRUE(extension.get() != NULL); ASSERT_TRUE(extension.get() != NULL);
std::vector<ImageLoadingTracker::ImageInfo> info_list; std::vector<ImageLoadingTracker::ImageRepresentation> info_list;
int sizes[] = {extension_misc::EXTENSION_ICON_SMALLISH, int sizes[] = {extension_misc::EXTENSION_ICON_SMALLISH,
extension_misc::EXTENSION_ICON_BITTY}; extension_misc::EXTENSION_ICON_BITTY};
for (size_t i = 0; i < arraysize(sizes); ++i) { for (size_t i = 0; i < arraysize(sizes); ++i) {
ExtensionResource resource = ExtensionResource resource =
extension->GetIconResource(sizes[i], ExtensionIconSet::MATCH_EXACTLY); extension->GetIconResource(sizes[i], ExtensionIconSet::MATCH_EXACTLY);
info_list.push_back(ImageLoadingTracker::ImageInfo( info_list.push_back(ImageLoadingTracker::ImageRepresentation(
resource, gfx::Size(sizes[i], sizes[i]))); resource,
ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER,
gfx::Size(sizes[i], sizes[i]),
ui::SCALE_FACTOR_NONE));
} }
ImageLoadingTracker loader(this); ImageLoadingTracker loader(this);
......
...@@ -366,6 +366,8 @@ ...@@ -366,6 +366,8 @@
'browser/extensions/extension_host.h', 'browser/extensions/extension_host.h',
'browser/extensions/extension_host_mac.h', 'browser/extensions/extension_host_mac.h',
'browser/extensions/extension_host_mac.mm', 'browser/extensions/extension_host_mac.mm',
'browser/extensions/extension_icon_image.cc',
'browser/extensions/extension_icon_image.h',
'browser/extensions/extension_icon_manager.cc', 'browser/extensions/extension_icon_manager.cc',
'browser/extensions/extension_icon_manager.h', 'browser/extensions/extension_icon_manager.h',
'browser/extensions/extension_info_map.cc', 'browser/extensions/extension_info_map.cc',
......
...@@ -1257,6 +1257,7 @@ ...@@ -1257,6 +1257,7 @@
'browser/extensions/extension_creator_filter_unittest.cc', 'browser/extensions/extension_creator_filter_unittest.cc',
'browser/extensions/extension_function_test_utils.cc', 'browser/extensions/extension_function_test_utils.cc',
'browser/extensions/extension_function_test_utils.h', 'browser/extensions/extension_function_test_utils.h',
'browser/extensions/extension_icon_image_unittest.cc',
'browser/extensions/extension_icon_manager_unittest.cc', 'browser/extensions/extension_icon_manager_unittest.cc',
'browser/extensions/extension_info_map_unittest.cc', 'browser/extensions/extension_info_map_unittest.cc',
'browser/extensions/extension_pref_value_map_unittest.cc', 'browser/extensions/extension_pref_value_map_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment