Commit f19c7663 authored by rdevlin.cronin's avatar rdevlin.cronin Committed by Commit bot

[Extensions] Cache extension action icons

Cache extension action icons so that they don't have to be
reloaded each time a new view is created (i.e., each time a
new browser or the new overflow menu is opened).

BUG=452958

Review URL: https://codereview.chromium.org/885443004

Cr-Commit-Position: refs/heads/master@{#313724}
parent 758bbd8c
...@@ -30,7 +30,7 @@ class ExtensionIndicatorIcon : public StatusIconObserver, ...@@ -30,7 +30,7 @@ class ExtensionIndicatorIcon : public StatusIconObserver,
public ExtensionActionIconFactory::Observer { public ExtensionActionIconFactory::Observer {
public: public:
static ExtensionIndicatorIcon* Create(const Extension* extension, static ExtensionIndicatorIcon* Create(const Extension* extension,
const ExtensionAction* action, ExtensionAction* action,
Profile* profile, Profile* profile,
StatusTray* status_tray); StatusTray* status_tray);
~ExtensionIndicatorIcon() override; ~ExtensionIndicatorIcon() override;
...@@ -43,7 +43,7 @@ class ExtensionIndicatorIcon : public StatusIconObserver, ...@@ -43,7 +43,7 @@ class ExtensionIndicatorIcon : public StatusIconObserver,
private: private:
ExtensionIndicatorIcon(const Extension* extension, ExtensionIndicatorIcon(const Extension* extension,
const ExtensionAction* action, ExtensionAction* action,
Profile* profile, Profile* profile,
StatusTray* status_tray); StatusTray* status_tray);
...@@ -56,7 +56,7 @@ class ExtensionIndicatorIcon : public StatusIconObserver, ...@@ -56,7 +56,7 @@ class ExtensionIndicatorIcon : public StatusIconObserver,
ExtensionIndicatorIcon* ExtensionIndicatorIcon::Create( ExtensionIndicatorIcon* ExtensionIndicatorIcon::Create(
const Extension* extension, const Extension* extension,
const ExtensionAction* action, ExtensionAction* action,
Profile* profile, Profile* profile,
StatusTray* status_tray) { StatusTray* status_tray) {
scoped_ptr<ExtensionIndicatorIcon> extension_icon( scoped_ptr<ExtensionIndicatorIcon> extension_icon(
...@@ -96,7 +96,7 @@ void ExtensionIndicatorIcon::OnIconUpdated() { ...@@ -96,7 +96,7 @@ void ExtensionIndicatorIcon::OnIconUpdated() {
} }
ExtensionIndicatorIcon::ExtensionIndicatorIcon(const Extension* extension, ExtensionIndicatorIcon::ExtensionIndicatorIcon(const Extension* extension,
const ExtensionAction* action, ExtensionAction* action,
Profile* profile, Profile* profile,
StatusTray* status_tray) StatusTray* status_tray)
: extension_(extension), : extension_(extension),
...@@ -176,7 +176,7 @@ bool SystemIndicatorManager::SendClickEventToExtensionForTest( ...@@ -176,7 +176,7 @@ bool SystemIndicatorManager::SendClickEventToExtensionForTest(
void SystemIndicatorManager::CreateOrUpdateIndicator( void SystemIndicatorManager::CreateOrUpdateIndicator(
const Extension* extension, const Extension* extension,
const ExtensionAction* extension_action) { ExtensionAction* extension_action) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
SystemIndicatorMap::iterator it = system_indicators_.find(extension->id()); SystemIndicatorMap::iterator it = system_indicators_.find(extension->id());
if (it != system_indicators_.end()) { if (it != system_indicators_.end()) {
......
...@@ -61,7 +61,7 @@ class SystemIndicatorManager : public ExtensionRegistryObserver, ...@@ -61,7 +61,7 @@ class SystemIndicatorManager : public ExtensionRegistryObserver,
// the indicator if necessary. // the indicator if necessary.
void CreateOrUpdateIndicator( void CreateOrUpdateIndicator(
const Extension* extension, const Extension* extension,
const ExtensionAction* extension_action); ExtensionAction* extension_action);
// Causes the indicator for the given extension to be hidden. // Causes the indicator for the given extension to be hidden.
void RemoveIndicator(const std::string &extension_id); void RemoveIndicator(const std::string &extension_id);
......
...@@ -7,12 +7,13 @@ ...@@ -7,12 +7,13 @@
#include <algorithm> #include <algorithm>
#include "base/base64.h" #include "base/base64.h"
#include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "chrome/common/badge_util.h" #include "chrome/common/badge_util.h"
#include "chrome/common/icon_with_badge_image_source.h" #include "chrome/common/icon_with_badge_image_source.h"
#include "extensions/browser/extension_icon_image.h"
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/common/extension_icon_set.h"
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "grit/theme_resources.h" #include "grit/theme_resources.h"
#include "grit/ui_resources.h" #include "grit/ui_resources.h"
#include "ipc/ipc_message.h" #include "ipc/ipc_message.h"
...@@ -36,6 +37,29 @@ ...@@ -36,6 +37,29 @@
namespace { namespace {
// Returns the default icon image for extensions.
gfx::Image GetDefaultIcon() {
return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
IDR_EXTENSIONS_FAVICON);
}
// Given the extension action type, returns the size the extension action icon
// should have. The icon should be square, so only one dimension is
// returned.
int GetIconSizeForType(extensions::ActionInfo::Type type) {
switch (type) {
case extensions::ActionInfo::TYPE_BROWSER:
case extensions::ActionInfo::TYPE_PAGE:
case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR:
// TODO(dewittj) Report the actual icon size of the system
// indicator.
return extension_misc::EXTENSION_ICON_ACTION;
default:
NOTREACHED();
return 0;
}
}
class GetAttentionImageSource : public gfx::ImageSkiaSource { class GetAttentionImageSource : public gfx::ImageSkiaSource {
public: public:
explicit GetAttentionImageSource(const gfx::ImageSkia& icon) explicit GetAttentionImageSource(const gfx::ImageSkia& icon)
...@@ -76,61 +100,20 @@ const int ExtensionAction::kDefaultTabId = -1; ...@@ -76,61 +100,20 @@ const int ExtensionAction::kDefaultTabId = -1;
const int ExtensionAction::kPageActionIconMaxSize = const int ExtensionAction::kPageActionIconMaxSize =
extension_misc::EXTENSION_ICON_ACTION; extension_misc::EXTENSION_ICON_ACTION;
ExtensionAction::ExtensionAction(const std::string& extension_id, ExtensionAction::ExtensionAction(const extensions::Extension& extension,
extensions::ActionInfo::Type action_type, extensions::ActionInfo::Type action_type,
const extensions::ActionInfo& manifest_data) const extensions::ActionInfo& manifest_data)
: extension_id_(extension_id), action_type_(action_type) { : extension_id_(extension.id()), action_type_(action_type) {
// Page/script actions are hidden/disabled by default, and browser actions are // Page/script actions are hidden/disabled by default, and browser actions are
// visible/enabled by default. // visible/enabled by default.
SetIsVisible(kDefaultTabId, SetIsVisible(kDefaultTabId,
action_type == extensions::ActionInfo::TYPE_BROWSER); action_type == extensions::ActionInfo::TYPE_BROWSER);
SetTitle(kDefaultTabId, manifest_data.default_title); Populate(extension, manifest_data);
SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
if (!manifest_data.default_icon.empty()) {
set_default_icon(make_scoped_ptr(new ExtensionIconSet(
manifest_data.default_icon)));
}
set_id(manifest_data.id);
} }
ExtensionAction::~ExtensionAction() { ExtensionAction::~ExtensionAction() {
} }
scoped_ptr<ExtensionAction> ExtensionAction::CopyForTest() const {
scoped_ptr<ExtensionAction> copy(
new ExtensionAction(extension_id_, action_type_,
extensions::ActionInfo()));
copy->popup_url_ = popup_url_;
copy->title_ = title_;
copy->icon_ = icon_;
copy->badge_text_ = badge_text_;
copy->badge_background_color_ = badge_background_color_;
copy->badge_text_color_ = badge_text_color_;
copy->is_visible_ = is_visible_;
copy->id_ = id_;
if (default_icon_)
copy->default_icon_.reset(new ExtensionIconSet(*default_icon_));
return copy.Pass();
}
// static
int ExtensionAction::GetIconSizeForType(
extensions::ActionInfo::Type type) {
switch (type) {
case extensions::ActionInfo::TYPE_BROWSER:
case extensions::ActionInfo::TYPE_PAGE:
case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR:
// TODO(dewittj) Report the actual icon size of the system
// indicator.
return extension_misc::EXTENSION_ICON_ACTION;
default:
NOTREACHED();
return 0;
}
}
void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) { void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) {
// We store |url| even if it is empty, rather than removing a URL from the // We store |url| even if it is empty, rather than removing a URL from the
// map. If an extension has a default popup, and removes it for a tab via // map. If an extension has a default popup, and removes it for a tab via
...@@ -282,6 +265,28 @@ gfx::ImageSkia ExtensionAction::GetIconWithBadge( ...@@ -282,6 +265,28 @@ gfx::ImageSkia ExtensionAction::GetIconWithBadge(
icon.size()); icon.size());
} }
extensions::IconImage* ExtensionAction::LoadDefaultIconImage(
const extensions::Extension& extension,
content::BrowserContext* browser_context) {
if (default_icon_ && !default_icon_image_) {
default_icon_image_.reset(new extensions::IconImage(
browser_context,
&extension,
*default_icon(),
GetIconSizeForType(action_type_),
*GetDefaultIcon().ToImageSkia(),
nullptr));
}
return default_icon_image_.get();
}
gfx::ImageSkia ExtensionAction::GetDefaultIconImage() const {
// If we have a default icon, it should be loaded before trying to use it.
DCHECK(!default_icon_image_ == !default_icon_);
return default_icon_image_ ? default_icon_image_->image_skia() :
*GetDefaultIcon().ToImageSkia();
}
bool ExtensionAction::HasPopupUrl(int tab_id) const { bool ExtensionAction::HasPopupUrl(int tab_id) const {
return HasValue(popup_url_, tab_id); return HasValue(popup_url_, tab_id);
} }
...@@ -310,6 +315,52 @@ bool ExtensionAction::HasIcon(int tab_id) const { ...@@ -310,6 +315,52 @@ bool ExtensionAction::HasIcon(int tab_id) const {
return HasValue(icon_, tab_id); return HasValue(icon_, tab_id);
} }
void ExtensionAction::SetDefaultIconForTest(
scoped_ptr<ExtensionIconSet> default_icon) {
default_icon_ = default_icon.Pass();
}
void ExtensionAction::Populate(const extensions::Extension& extension,
const extensions::ActionInfo& manifest_data) {
// If the manifest doesn't specify a title, set it to |extension|'s name.
const std::string& title =
!manifest_data.default_title.empty() ? manifest_data.default_title :
extension.name();
SetTitle(kDefaultTabId, title);
SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
set_id(manifest_data.id);
// Initialize the specified icon set.
if (!manifest_data.default_icon.empty())
default_icon_.reset(new ExtensionIconSet(manifest_data.default_icon));
const ExtensionIconSet& extension_icons =
extensions::IconsInfo::GetIcons(&extension);
// Look for any other icons.
std::string largest_icon = extension_icons.Get(
extension_misc::EXTENSION_ICON_GIGANTOR,
ExtensionIconSet::MATCH_SMALLER);
if (!largest_icon.empty()) {
// We found an icon to use, so create an icon set if one doesn't exist.
if (!default_icon_)
default_icon_.reset(new ExtensionIconSet());
int largest_icon_size = extension_icons.GetIconSizeFromPath(largest_icon);
// Replace any missing extension action icons with the largest icon
// retrieved from |extension|'s manifest so long as the largest icon is
// larger than the current key.
for (int i = extension_misc::kNumExtensionActionIconSizes - 1;
i >= 0; --i) {
int size = extension_misc::kExtensionActionIconSizes[i].size;
if (default_icon_->Get(size, ExtensionIconSet::MATCH_BIGGER).empty()
&& largest_icon_size > size) {
default_icon_->Add(size, largest_icon);
break;
}
}
}
}
// Determines which icon would be returned by |GetIcon|, and returns its width. // Determines which icon would be returned by |GetIcon|, and returns its width.
int ExtensionAction::GetIconWidth(int tab_id) const { int ExtensionAction::GetIconWidth(int tab_id) const {
// If icon has been set, return its width. // If icon has been set, return its width.
...@@ -324,5 +375,5 @@ int ExtensionAction::GetIconWidth(int tab_id) const { ...@@ -324,5 +375,5 @@ int ExtensionAction::GetIconWidth(int tab_id) const {
// If no icon has been set and there is no default icon, we need favicon // If no icon has been set and there is no default icon, we need favicon
// width. // width.
return ui::ResourceBundle::GetSharedInstance().GetImageNamed( return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
IDR_EXTENSIONS_FAVICON).ToImageSkia()->width(); IDR_EXTENSIONS_FAVICON).Width();
} }
...@@ -9,21 +9,21 @@ ...@@ -9,21 +9,21 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/basictypes.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h" #include "base/stl_util.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "chrome/common/extensions/api/extension_action/action_info.h" #include "chrome/common/extensions/api/extension_action/action_info.h"
#include "extensions/common/extension_icon_set.h"
#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColor.h"
// TODO(robertphillips): change this to "class SkBaseDevice;"
#include "third_party/skia/include/core/SkDevice.h"
#include "ui/gfx/animation/linear_animation.h"
class GURL; class GURL;
class SkBitmap;
namespace content {
class BrowserContext;
}
namespace extensions {
class Extension;
class IconImage;
}
namespace gfx { namespace gfx {
class Canvas; class Canvas;
...@@ -53,20 +53,11 @@ class ExtensionAction { ...@@ -53,20 +53,11 @@ class ExtensionAction {
// Max size (both dimensions) for page actions. // Max size (both dimensions) for page actions.
static const int kPageActionIconMaxSize; static const int kPageActionIconMaxSize;
ExtensionAction(const std::string& extension_id, ExtensionAction(const extensions::Extension& extension,
extensions::ActionInfo::Type action_type, extensions::ActionInfo::Type action_type,
const extensions::ActionInfo& manifest_data); const extensions::ActionInfo& manifest_data);
~ExtensionAction(); ~ExtensionAction();
// Gets a copy of this, ownership passed to caller.
// It doesn't make sense to copy of an ExtensionAction except in tests.
scoped_ptr<ExtensionAction> CopyForTest() const;
// Given the extension action type, returns the size the extension action icon
// should have. The icon should be square, so only one dimension is
// returned.
static int GetIconSizeForType(extensions::ActionInfo::Type type);
// extension id // extension id
const std::string& extension_id() const { return extension_id_; } const std::string& extension_id() const { return extension_id_; }
...@@ -121,12 +112,6 @@ class ExtensionAction { ...@@ -121,12 +112,6 @@ class ExtensionAction {
void DeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon); void DeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon);
void UndoDeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon); void UndoDeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon);
// Non-tab-specific icon path. This is used to support the default_icon key of
// page and browser actions.
void set_default_icon(scoped_ptr<ExtensionIconSet> icon_set) {
default_icon_ = icon_set.Pass();
}
const ExtensionIconSet* default_icon() const { const ExtensionIconSet* default_icon() const {
return default_icon_.get(); return default_icon_.get();
} }
...@@ -202,6 +187,16 @@ class ExtensionAction { ...@@ -202,6 +187,16 @@ class ExtensionAction {
int tab_id, int tab_id,
const gfx::Size& spacing) const; const gfx::Size& spacing) const;
// Lazily loads and returns the default icon image, if one exists for the
// action.
extensions::IconImage* LoadDefaultIconImage(
const extensions::Extension& extension,
content::BrowserContext* browser_context);
// Returns the image to use as the default icon for the action. Can only be
// called after LoadDefaultIconImage().
gfx::ImageSkia GetDefaultIconImage() const;
// Determine whether or not the ExtensionAction has a value set for the given // Determine whether or not the ExtensionAction has a value set for the given
// |tab_id| for each property. // |tab_id| for each property.
bool HasPopupUrl(int tab_id) const; bool HasPopupUrl(int tab_id) const;
...@@ -212,7 +207,14 @@ class ExtensionAction { ...@@ -212,7 +207,14 @@ class ExtensionAction {
bool HasIsVisible(int tab_id) const; bool HasIsVisible(int tab_id) const;
bool HasIcon(int tab_id) const; bool HasIcon(int tab_id) const;
void SetDefaultIconForTest(scoped_ptr<ExtensionIconSet> default_icon);
private: private:
// Populates the action from the |extension| and |manifest_data|, filling in
// any missing values (like title or icons) as possible.
void Populate(const extensions::Extension& extension,
const extensions::ActionInfo& manifest_data);
// Returns width of the current icon for tab_id. // Returns width of the current icon for tab_id.
// TODO(tbarzic): The icon selection is done in ExtensionActionIconFactory. // TODO(tbarzic): The icon selection is done in ExtensionActionIconFactory.
// We should probably move this there too. // We should probably move this there too.
...@@ -284,7 +286,11 @@ class ExtensionAction { ...@@ -284,7 +286,11 @@ class ExtensionAction {
// ExtensionIconSet containing paths to bitmaps from which default icon's // ExtensionIconSet containing paths to bitmaps from which default icon's
// image representations will be selected. // image representations will be selected.
scoped_ptr<const ExtensionIconSet> default_icon_; scoped_ptr<ExtensionIconSet> default_icon_;
// The default icon image, if |default_icon_| exists.
// Lazily initialized via LoadDefaultIconImage().
scoped_ptr<extensions::IconImage> default_icon_image_;
// The id for the ExtensionAction, for example: "RssPageAction". This is // The id for the ExtensionAction, for example: "RssPageAction". This is
// needed for compat with an older version of the page actions API. // needed for compat with an older version of the page actions API.
......
...@@ -6,41 +6,25 @@ ...@@ -6,41 +6,25 @@
#include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "extensions/common/extension.h" #include "ui/gfx/image/image.h"
#include "extensions/common/extension_icon_set.h"
#include "grit/theme_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
using extensions::Extension; using extensions::Extension;
using extensions::IconImage; using extensions::IconImage;
namespace {
gfx::ImageSkia GetDefaultIcon() {
return *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
IDR_EXTENSIONS_FAVICON).ToImageSkia();
}
} // namespace
ExtensionActionIconFactory::ExtensionActionIconFactory( ExtensionActionIconFactory::ExtensionActionIconFactory(
Profile* profile, Profile* profile,
const Extension* extension, const Extension* extension,
const ExtensionAction* action, ExtensionAction* action,
Observer* observer) Observer* observer)
: extension_(extension), : extension_(extension),
action_(action), action_(action),
observer_(observer) { observer_(observer),
if (action_->default_icon()) { icon_image_observer_(this) {
default_icon_.reset(new IconImage( extensions::IconImage* default_icon_image =
profile, action->LoadDefaultIconImage(*extension, profile);
extension_, if (default_icon_image)
*action_->default_icon(), icon_image_observer_.Add(default_icon_image);
ExtensionAction::GetIconSizeForType(action_->action_type()),
GetDefaultIcon(),
this));
}
} }
ExtensionActionIconFactory::~ExtensionActionIconFactory() {} ExtensionActionIconFactory::~ExtensionActionIconFactory() {}
...@@ -51,9 +35,13 @@ void ExtensionActionIconFactory::OnExtensionIconImageChanged(IconImage* image) { ...@@ -51,9 +35,13 @@ void ExtensionActionIconFactory::OnExtensionIconImageChanged(IconImage* image) {
observer_->OnIconUpdated(); observer_->OnIconUpdated();
} }
void ExtensionActionIconFactory::OnExtensionIconImageDestroyed(
IconImage* image) {
icon_image_observer_.RemoveAll();
}
gfx::Image ExtensionActionIconFactory::GetIcon(int tab_id) { gfx::Image ExtensionActionIconFactory::GetIcon(int tab_id) {
gfx::ImageSkia base_icon = GetBaseIconFromAction(tab_id); return gfx::Image(GetBaseIconFromAction(tab_id));
return gfx::Image(base_icon);
} }
gfx::ImageSkia ExtensionActionIconFactory::GetBaseIconFromAction(int tab_id) { gfx::ImageSkia ExtensionActionIconFactory::GetBaseIconFromAction(int tab_id) {
...@@ -65,8 +53,5 @@ gfx::ImageSkia ExtensionActionIconFactory::GetBaseIconFromAction(int tab_id) { ...@@ -65,8 +53,5 @@ gfx::ImageSkia ExtensionActionIconFactory::GetBaseIconFromAction(int tab_id) {
if (!icon.isNull()) if (!icon.isNull())
return icon; return icon;
if (default_icon_) return action_->GetDefaultIconImage();
return default_icon_->image_skia();
return GetDefaultIcon();
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_ICON_FACTORY_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_ICON_FACTORY_H_
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/scoped_observer.h"
#include "extensions/browser/extension_icon_image.h" #include "extensions/browser/extension_icon_image.h"
class ExtensionAction; class ExtensionAction;
...@@ -33,12 +34,13 @@ class ExtensionActionIconFactory : public extensions::IconImage::Observer { ...@@ -33,12 +34,13 @@ class ExtensionActionIconFactory : public extensions::IconImage::Observer {
// Observer should outlive this. // Observer should outlive this.
ExtensionActionIconFactory(Profile* profile, ExtensionActionIconFactory(Profile* profile,
const extensions::Extension* extension, const extensions::Extension* extension,
const ExtensionAction* action, ExtensionAction* action,
Observer* observer); Observer* observer);
~ExtensionActionIconFactory() override; ~ExtensionActionIconFactory() override;
// extensions::IconImage override. // extensions::IconImage override.
void OnExtensionIconImageChanged(extensions::IconImage* image) override; void OnExtensionIconImageChanged(extensions::IconImage* image) override;
void OnExtensionIconImageDestroyed(extensions::IconImage* image) override;
// Gets the extension action icon for the tab. // Gets the extension action icon for the tab.
// If there is an icon set using |SetIcon|, that icon is returned. // If there is an icon set using |SetIcon|, that icon is returned.
...@@ -58,8 +60,9 @@ class ExtensionActionIconFactory : public extensions::IconImage::Observer { ...@@ -58,8 +60,9 @@ class ExtensionActionIconFactory : public extensions::IconImage::Observer {
const extensions::Extension* extension_; const extensions::Extension* extension_;
const ExtensionAction* action_; const ExtensionAction* action_;
Observer* observer_; Observer* observer_;
// Underlying icon image for the default icon.
scoped_ptr<extensions::IconImage> default_icon_; ScopedObserver<extensions::IconImage, extensions::IconImage::Observer>
icon_image_observer_;
DISALLOW_COPY_AND_ASSIGN(ExtensionActionIconFactory); DISALLOW_COPY_AND_ASSIGN(ExtensionActionIconFactory);
}; };
......
...@@ -263,7 +263,7 @@ TEST_F(ExtensionActionIconFactoryTest, DefaultIcon) { ...@@ -263,7 +263,7 @@ TEST_F(ExtensionActionIconFactoryTest, DefaultIcon) {
scoped_ptr<ExtensionIconSet> default_icon_set(new ExtensionIconSet()); scoped_ptr<ExtensionIconSet> default_icon_set(new ExtensionIconSet());
default_icon_set->Add(19, "icon.png"); default_icon_set->Add(19, "icon.png");
browser_action->set_default_icon(default_icon_set.Pass()); browser_action->SetDefaultIconForTest(default_icon_set.Pass());
ASSERT_TRUE(browser_action->default_icon()); ASSERT_TRUE(browser_action->default_icon());
ExtensionActionIconFactory icon_factory( ExtensionActionIconFactory icon_factory(
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/extensions_browser_client.h"
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/common/manifest_handlers/icons_handler.h"
namespace extensions { namespace extensions {
...@@ -85,41 +84,6 @@ void ExtensionActionManager::OnExtensionUnloaded( ...@@ -85,41 +84,6 @@ void ExtensionActionManager::OnExtensionUnloaded(
namespace { namespace {
// Loads resources missing from |action| (i.e. title, icons) from the "icons"
// key of |extension|'s manifest.
void PopulateMissingValues(const Extension& extension,
ExtensionAction* action) {
// If the title is missing from |action|, set it to |extension|'s name.
if (action->GetTitle(ExtensionAction::kDefaultTabId).empty())
action->SetTitle(ExtensionAction::kDefaultTabId, extension.name());
scoped_ptr<ExtensionIconSet> default_icon(new ExtensionIconSet());
if (action->default_icon())
*default_icon = *action->default_icon();
const ExtensionIconSet& extension_icons = IconsInfo::GetIcons(&extension);
std::string largest_icon = extension_icons.Get(
extension_misc::EXTENSION_ICON_GIGANTOR,
ExtensionIconSet::MATCH_SMALLER);
if (!largest_icon.empty()) {
int largest_icon_size = extension_icons.GetIconSizeFromPath(largest_icon);
// Replace any missing extension action icons with the largest icon
// retrieved from |extension|'s manifest so long as the largest icon is
// larger than the current key.
for (int i = extension_misc::kNumExtensionActionIconSizes - 1;
i >= 0; --i) {
int size = extension_misc::kExtensionActionIconSizes[i].size;
if (default_icon->Get(size, ExtensionIconSet::MATCH_BIGGER).empty()
&& largest_icon_size > size) {
default_icon->Add(size, largest_icon);
break;
}
}
action->set_default_icon(default_icon.Pass());
}
}
// Returns map[extension_id] if that entry exists. Otherwise, if // Returns map[extension_id] if that entry exists. Otherwise, if
// action_info!=NULL, creates an ExtensionAction from it, fills in the map, and // action_info!=NULL, creates an ExtensionAction from it, fills in the map, and
// returns that. Otherwise (action_info==NULL), returns NULL. // returns that. Otherwise (action_info==NULL), returns NULL.
...@@ -145,9 +109,8 @@ ExtensionAction* GetOrCreateOrNull( ...@@ -145,9 +109,8 @@ ExtensionAction* GetOrCreateOrNull(
} }
linked_ptr<ExtensionAction> action(new ExtensionAction( linked_ptr<ExtensionAction> action(new ExtensionAction(
extension.id(), action_type, *action_info)); extension, action_type, *action_info));
(*map)[extension.id()] = action; (*map)[extension.id()] = action;
PopulateMissingValues(extension, action.get());
return action.get(); return action.get();
} }
...@@ -181,8 +144,7 @@ scoped_ptr<ExtensionAction> ExtensionActionManager::GetBestFitAction( ...@@ -181,8 +144,7 @@ scoped_ptr<ExtensionAction> ExtensionActionManager::GetBestFitAction(
// with a blank ActionInfo. // with a blank ActionInfo.
// Populate any missing values from |extension|'s manifest. // Populate any missing values from |extension|'s manifest.
scoped_ptr<ExtensionAction> new_action(new ExtensionAction( scoped_ptr<ExtensionAction> new_action(new ExtensionAction(
extension.id(), type, info ? *info : ActionInfo())); extension, type, info ? *info : ActionInfo()));
PopulateMissingValues(extension, new_action.get());
return new_action.Pass(); return new_action.Pass();
} }
......
...@@ -5,114 +5,128 @@ ...@@ -5,114 +5,128 @@
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action.h"
#include "chrome/common/extensions/api/extension_action/action_info.h" #include "chrome/common/extensions/api/extension_action/action_info.h"
#include "extensions/common/test_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace extensions {
namespace { namespace {
using extensions::ActionInfo; scoped_ptr<ExtensionAction> CreateAction(ActionInfo::Type type,
const ActionInfo& action_info) {
scoped_refptr<const Extension> extension = test_util::CreateEmptyExtension();
return make_scoped_ptr(new ExtensionAction(*extension, type, action_info));
}
} // namespace
TEST(ExtensionActionTest, Title) { TEST(ExtensionActionTest, Title) {
ActionInfo action_info; ActionInfo action_info;
action_info.default_title = "Initial Title"; action_info.default_title = "Initial Title";
ExtensionAction action(std::string(), ActionInfo::TYPE_PAGE, action_info); scoped_ptr<ExtensionAction> action =
CreateAction(ActionInfo::TYPE_PAGE, action_info);
ASSERT_EQ("Initial Title", action.GetTitle(1));
action.SetTitle(ExtensionAction::kDefaultTabId, "foo"); ASSERT_EQ("Initial Title", action->GetTitle(1));
ASSERT_EQ("foo", action.GetTitle(1)); action->SetTitle(ExtensionAction::kDefaultTabId, "foo");
ASSERT_EQ("foo", action.GetTitle(100)); ASSERT_EQ("foo", action->GetTitle(1));
action.SetTitle(100, "bar"); ASSERT_EQ("foo", action->GetTitle(100));
ASSERT_EQ("foo", action.GetTitle(1)); action->SetTitle(100, "bar");
ASSERT_EQ("bar", action.GetTitle(100)); ASSERT_EQ("foo", action->GetTitle(1));
action.SetTitle(ExtensionAction::kDefaultTabId, "baz"); ASSERT_EQ("bar", action->GetTitle(100));
ASSERT_EQ("baz", action.GetTitle(1)); action->SetTitle(ExtensionAction::kDefaultTabId, "baz");
action.ClearAllValuesForTab(100); ASSERT_EQ("baz", action->GetTitle(1));
ASSERT_EQ("baz", action.GetTitle(100)); action->ClearAllValuesForTab(100);
ASSERT_EQ("baz", action->GetTitle(100));
} }
TEST(ExtensionActionTest, Visibility) { TEST(ExtensionActionTest, Visibility) {
ExtensionAction action(std::string(), ActionInfo::TYPE_PAGE, ActionInfo()); scoped_ptr<ExtensionAction> action =
CreateAction(ActionInfo::TYPE_PAGE, ActionInfo());
ASSERT_FALSE(action.GetIsVisible(1));
action.SetIsVisible(ExtensionAction::kDefaultTabId, true); ASSERT_FALSE(action->GetIsVisible(1));
ASSERT_TRUE(action.GetIsVisible(1)); action->SetIsVisible(ExtensionAction::kDefaultTabId, true);
ASSERT_TRUE(action.GetIsVisible(100)); ASSERT_TRUE(action->GetIsVisible(1));
ASSERT_TRUE(action->GetIsVisible(100));
action.SetIsVisible(ExtensionAction::kDefaultTabId, false);
ASSERT_FALSE(action.GetIsVisible(1)); action->SetIsVisible(ExtensionAction::kDefaultTabId, false);
ASSERT_FALSE(action.GetIsVisible(100)); ASSERT_FALSE(action->GetIsVisible(1));
action.SetIsVisible(100, true); ASSERT_FALSE(action->GetIsVisible(100));
ASSERT_FALSE(action.GetIsVisible(1)); action->SetIsVisible(100, true);
ASSERT_TRUE(action.GetIsVisible(100)); ASSERT_FALSE(action->GetIsVisible(1));
ASSERT_TRUE(action->GetIsVisible(100));
action.ClearAllValuesForTab(100);
ASSERT_FALSE(action.GetIsVisible(1)); action->ClearAllValuesForTab(100);
ASSERT_FALSE(action.GetIsVisible(100)); ASSERT_FALSE(action->GetIsVisible(1));
ASSERT_FALSE(action->GetIsVisible(100));
ExtensionAction browser_action(
std::string(), ActionInfo::TYPE_BROWSER, ActionInfo()); scoped_ptr<ExtensionAction> browser_action =
ASSERT_TRUE(browser_action.GetIsVisible(1)); CreateAction(ActionInfo::TYPE_BROWSER, ActionInfo());
ASSERT_TRUE(browser_action->GetIsVisible(1));
} }
TEST(ExtensionActionTest, Icon) { TEST(ExtensionActionTest, Icon) {
ActionInfo action_info; ActionInfo action_info;
action_info.default_icon.Add(16, "icon16.png"); action_info.default_icon.Add(16, "icon16.png");
ExtensionAction page_action( scoped_ptr<ExtensionAction> page_action =
std::string(), ActionInfo::TYPE_PAGE, action_info); CreateAction(ActionInfo::TYPE_PAGE, action_info);
ASSERT_TRUE(page_action.default_icon()); ASSERT_TRUE(page_action->default_icon());
EXPECT_EQ("icon16.png", EXPECT_EQ("icon16.png",
page_action.default_icon()->Get( page_action->default_icon()->Get(
16, ExtensionIconSet::MATCH_EXACTLY)); 16, ExtensionIconSet::MATCH_EXACTLY));
EXPECT_EQ("", EXPECT_EQ("",
page_action.default_icon()->Get( page_action->default_icon()->Get(
17, ExtensionIconSet::MATCH_BIGGER)); 17, ExtensionIconSet::MATCH_BIGGER));
} }
TEST(ExtensionActionTest, Badge) { TEST(ExtensionActionTest, Badge) {
ExtensionAction action(std::string(), ActionInfo::TYPE_PAGE, ActionInfo()); scoped_ptr<ExtensionAction> action =
ASSERT_EQ("", action.GetBadgeText(1)); CreateAction(ActionInfo::TYPE_PAGE, ActionInfo());
action.SetBadgeText(ExtensionAction::kDefaultTabId, "foo"); ASSERT_EQ("", action->GetBadgeText(1));
ASSERT_EQ("foo", action.GetBadgeText(1)); action->SetBadgeText(ExtensionAction::kDefaultTabId, "foo");
ASSERT_EQ("foo", action.GetBadgeText(100)); ASSERT_EQ("foo", action->GetBadgeText(1));
action.SetBadgeText(100, "bar"); ASSERT_EQ("foo", action->GetBadgeText(100));
ASSERT_EQ("foo", action.GetBadgeText(1)); action->SetBadgeText(100, "bar");
ASSERT_EQ("bar", action.GetBadgeText(100)); ASSERT_EQ("foo", action->GetBadgeText(1));
action.SetBadgeText(ExtensionAction::kDefaultTabId, "baz"); ASSERT_EQ("bar", action->GetBadgeText(100));
ASSERT_EQ("baz", action.GetBadgeText(1)); action->SetBadgeText(ExtensionAction::kDefaultTabId, "baz");
action.ClearAllValuesForTab(100); ASSERT_EQ("baz", action->GetBadgeText(1));
ASSERT_EQ("baz", action.GetBadgeText(100)); action->ClearAllValuesForTab(100);
ASSERT_EQ("baz", action->GetBadgeText(100));
} }
TEST(ExtensionActionTest, BadgeTextColor) { TEST(ExtensionActionTest, BadgeTextColor) {
ExtensionAction action(std::string(), ActionInfo::TYPE_PAGE, ActionInfo()); scoped_ptr<ExtensionAction> action =
ASSERT_EQ(0x00000000u, action.GetBadgeTextColor(1)); CreateAction(ActionInfo::TYPE_PAGE, ActionInfo());
action.SetBadgeTextColor(ExtensionAction::kDefaultTabId, 0xFFFF0000u); ASSERT_EQ(0x00000000u, action->GetBadgeTextColor(1));
ASSERT_EQ(0xFFFF0000u, action.GetBadgeTextColor(1)); action->SetBadgeTextColor(ExtensionAction::kDefaultTabId, 0xFFFF0000u);
ASSERT_EQ(0xFFFF0000u, action.GetBadgeTextColor(100)); ASSERT_EQ(0xFFFF0000u, action->GetBadgeTextColor(1));
action.SetBadgeTextColor(100, 0xFF00FF00); ASSERT_EQ(0xFFFF0000u, action->GetBadgeTextColor(100));
ASSERT_EQ(0xFFFF0000u, action.GetBadgeTextColor(1)); action->SetBadgeTextColor(100, 0xFF00FF00);
ASSERT_EQ(0xFF00FF00u, action.GetBadgeTextColor(100)); ASSERT_EQ(0xFFFF0000u, action->GetBadgeTextColor(1));
action.SetBadgeTextColor(ExtensionAction::kDefaultTabId, 0xFF0000FFu); ASSERT_EQ(0xFF00FF00u, action->GetBadgeTextColor(100));
ASSERT_EQ(0xFF0000FFu, action.GetBadgeTextColor(1)); action->SetBadgeTextColor(ExtensionAction::kDefaultTabId, 0xFF0000FFu);
action.ClearAllValuesForTab(100); ASSERT_EQ(0xFF0000FFu, action->GetBadgeTextColor(1));
ASSERT_EQ(0xFF0000FFu, action.GetBadgeTextColor(100)); action->ClearAllValuesForTab(100);
ASSERT_EQ(0xFF0000FFu, action->GetBadgeTextColor(100));
} }
TEST(ExtensionActionTest, BadgeBackgroundColor) { TEST(ExtensionActionTest, BadgeBackgroundColor) {
ExtensionAction action(std::string(), ActionInfo::TYPE_PAGE, ActionInfo()); scoped_ptr<ExtensionAction> action =
ASSERT_EQ(0x00000000u, action.GetBadgeBackgroundColor(1)); CreateAction(ActionInfo::TYPE_PAGE, ActionInfo());
action.SetBadgeBackgroundColor(ExtensionAction::kDefaultTabId, ASSERT_EQ(0x00000000u, action->GetBadgeBackgroundColor(1));
action->SetBadgeBackgroundColor(ExtensionAction::kDefaultTabId,
0xFFFF0000u); 0xFFFF0000u);
ASSERT_EQ(0xFFFF0000u, action.GetBadgeBackgroundColor(1)); ASSERT_EQ(0xFFFF0000u, action->GetBadgeBackgroundColor(1));
ASSERT_EQ(0xFFFF0000u, action.GetBadgeBackgroundColor(100)); ASSERT_EQ(0xFFFF0000u, action->GetBadgeBackgroundColor(100));
action.SetBadgeBackgroundColor(100, 0xFF00FF00); action->SetBadgeBackgroundColor(100, 0xFF00FF00);
ASSERT_EQ(0xFFFF0000u, action.GetBadgeBackgroundColor(1)); ASSERT_EQ(0xFFFF0000u, action->GetBadgeBackgroundColor(1));
ASSERT_EQ(0xFF00FF00u, action.GetBadgeBackgroundColor(100)); ASSERT_EQ(0xFF00FF00u, action->GetBadgeBackgroundColor(100));
action.SetBadgeBackgroundColor(ExtensionAction::kDefaultTabId, action->SetBadgeBackgroundColor(ExtensionAction::kDefaultTabId,
0xFF0000FFu); 0xFF0000FFu);
ASSERT_EQ(0xFF0000FFu, action.GetBadgeBackgroundColor(1)); ASSERT_EQ(0xFF0000FFu, action->GetBadgeBackgroundColor(1));
action.ClearAllValuesForTab(100); action->ClearAllValuesForTab(100);
ASSERT_EQ(0xFF0000FFu, action.GetBadgeBackgroundColor(100)); ASSERT_EQ(0xFF0000FFu, action->GetBadgeBackgroundColor(100));
} }
TEST(ExtensionActionTest, PopupUrl) { TEST(ExtensionActionTest, PopupUrl) {
...@@ -123,30 +137,31 @@ TEST(ExtensionActionTest, PopupUrl) { ...@@ -123,30 +137,31 @@ TEST(ExtensionActionTest, PopupUrl) {
ActionInfo action_info; ActionInfo action_info;
action_info.default_popup_url = url_foo; action_info.default_popup_url = url_foo;
ExtensionAction action(std::string(), ActionInfo::TYPE_PAGE, action_info); scoped_ptr<ExtensionAction> action =
CreateAction(ActionInfo::TYPE_PAGE, action_info);
ASSERT_EQ(url_foo, action.GetPopupUrl(1));
ASSERT_EQ(url_foo, action.GetPopupUrl(100)); ASSERT_EQ(url_foo, action->GetPopupUrl(1));
ASSERT_TRUE(action.HasPopup(1)); ASSERT_EQ(url_foo, action->GetPopupUrl(100));
ASSERT_TRUE(action.HasPopup(100)); ASSERT_TRUE(action->HasPopup(1));
ASSERT_TRUE(action->HasPopup(100));
action.SetPopupUrl(ExtensionAction::kDefaultTabId, url_unset);
ASSERT_EQ(url_unset, action.GetPopupUrl(1)); action->SetPopupUrl(ExtensionAction::kDefaultTabId, url_unset);
ASSERT_EQ(url_unset, action.GetPopupUrl(100)); ASSERT_EQ(url_unset, action->GetPopupUrl(1));
ASSERT_FALSE(action.HasPopup(1)); ASSERT_EQ(url_unset, action->GetPopupUrl(100));
ASSERT_FALSE(action.HasPopup(100)); ASSERT_FALSE(action->HasPopup(1));
ASSERT_FALSE(action->HasPopup(100));
action.SetPopupUrl(100, url_bar);
ASSERT_EQ(url_unset, action.GetPopupUrl(1)); action->SetPopupUrl(100, url_bar);
ASSERT_EQ(url_bar, action.GetPopupUrl(100)); ASSERT_EQ(url_unset, action->GetPopupUrl(1));
ASSERT_EQ(url_bar, action->GetPopupUrl(100));
action.SetPopupUrl(ExtensionAction::kDefaultTabId, url_baz);
ASSERT_EQ(url_baz, action.GetPopupUrl(1)); action->SetPopupUrl(ExtensionAction::kDefaultTabId, url_baz);
ASSERT_EQ(url_bar, action.GetPopupUrl(100)); ASSERT_EQ(url_baz, action->GetPopupUrl(1));
ASSERT_EQ(url_bar, action->GetPopupUrl(100));
action.ClearAllValuesForTab(100);
ASSERT_EQ(url_baz, action.GetPopupUrl(1)); action->ClearAllValuesForTab(100);
ASSERT_EQ(url_baz, action.GetPopupUrl(100)); ASSERT_EQ(url_baz, action->GetPopupUrl(1));
ASSERT_EQ(url_baz, action->GetPopupUrl(100));
} }
} // namespace } // namespace extensions
...@@ -134,13 +134,14 @@ IconImage::IconImage( ...@@ -134,13 +134,14 @@ IconImage::IconImage(
extension_(extension), extension_(extension),
icon_set_(icon_set), icon_set_(icon_set),
resource_size_in_dip_(resource_size_in_dip), resource_size_in_dip_(resource_size_in_dip),
observer_(observer),
source_(NULL), source_(NULL),
default_icon_(gfx::ImageSkiaOperations::CreateResizedImage( default_icon_(gfx::ImageSkiaOperations::CreateResizedImage(
default_icon, default_icon,
skia::ImageOperations::RESIZE_BEST, skia::ImageOperations::RESIZE_BEST,
gfx::Size(resource_size_in_dip, resource_size_in_dip))), gfx::Size(resource_size_in_dip, resource_size_in_dip))),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
if (observer)
AddObserver(observer);
gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip); gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip);
source_ = new Source(this, resource_size); source_ = new Source(this, resource_size);
image_skia_ = gfx::ImageSkia(source_, resource_size); image_skia_ = gfx::ImageSkia(source_, resource_size);
...@@ -150,7 +151,16 @@ IconImage::IconImage( ...@@ -150,7 +151,16 @@ IconImage::IconImage(
content::NotificationService::AllSources()); content::NotificationService::AllSources());
} }
void IconImage::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void IconImage::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
IconImage::~IconImage() { IconImage::~IconImage() {
FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageDestroyed(this));
source_->ResetHost(); source_->ResetHost();
} }
...@@ -216,8 +226,7 @@ void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) { ...@@ -216,8 +226,7 @@ void IconImage::OnImageLoaded(float scale, const gfx::Image& image_in) {
image_skia_.RemoveRepresentation(scale); image_skia_.RemoveRepresentation(scale);
image_skia_.AddRepresentation(rep); image_skia_.AddRepresentation(rep);
if (observer_) FOR_EACH_OBSERVER(Observer, observers_, OnExtensionIconImageChanged(this));
observer_->OnExtensionIconImageChanged(this);
} }
void IconImage::Observe(int type, void IconImage::Observe(int type,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/observer_list.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 "extensions/common/extension_icon_set.h" #include "extensions/common/extension_icon_set.h"
...@@ -33,12 +34,11 @@ namespace extensions { ...@@ -33,12 +34,11 @@ namespace extensions {
// A class that provides an ImageSkia for UI code to use. It handles extension // 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 // icon resource loading, screen scale factor change etc. UI code that uses
// extension icon should host this class and be its observer. ExtensionIconImage // extension icon should host this class. In painting code, UI code paints with
// should be outlived by the observer. In painting code, UI code paints with the // the ImageSkia provided by this class. If the required extension icon resource
// ImageSkia provided by this class. If required extension icon resource is not // is not already present, this class tries to load it and calls its observer
// already present, this class tries to load it and calls its observer interface // interface when the image get updated. Until the resource is loaded, the UI
// when the image get updated. Until the resource is loaded, the UI code will be // code will be provided with a blank, transparent image.
// provided with blank, transparent image.
// If the requested resource doesn't exist or can't be loaded and a default // If the requested resource doesn't exist or can't be loaded and a default
// icon was supplied in the constructor, icon image will be updated with the // icon was supplied in the constructor, icon image will be updated with the
// default icon's resource. // default icon's resource.
...@@ -56,6 +56,10 @@ class IconImage : public content::NotificationObserver { ...@@ -56,6 +56,10 @@ class IconImage : public content::NotificationObserver {
// is loaded and added to |image|. // is loaded and added to |image|.
virtual void OnExtensionIconImageChanged(IconImage* image) = 0; virtual void OnExtensionIconImageChanged(IconImage* image) = 0;
// Called when this object is deleted. Objects should observe this if there
// is a question about the lifetime of the icon image vs observer.
virtual void OnExtensionIconImageDestroyed(IconImage* image) {}
protected: protected:
virtual ~Observer() {} virtual ~Observer() {}
}; };
...@@ -73,6 +77,9 @@ class IconImage : public content::NotificationObserver { ...@@ -73,6 +77,9 @@ class IconImage : public content::NotificationObserver {
const gfx::ImageSkia& image_skia() const { return image_skia_; } const gfx::ImageSkia& image_skia() const { return image_skia_; }
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private: private:
class Source; class Source;
...@@ -81,7 +88,7 @@ class IconImage : public content::NotificationObserver { ...@@ -81,7 +88,7 @@ class IconImage : public content::NotificationObserver {
// method. // method.
// If representation loading is asynchronous, an empty image // If representation loading is asynchronous, an empty image
// representation is returned. When the representation gets loaded the // representation is returned. When the representation gets loaded the
// observer's |OnExtensionIconImageLoaded| will be called. // observers' OnExtensionIconImageLoaded() will be called.
gfx::ImageSkiaRep LoadImageForScaleFactor(ui::ScaleFactor scale_factor); gfx::ImageSkiaRep LoadImageForScaleFactor(ui::ScaleFactor scale_factor);
void OnImageLoaded(float scale_factor, const gfx::Image& image); void OnImageLoaded(float scale_factor, const gfx::Image& image);
...@@ -96,7 +103,7 @@ class IconImage : public content::NotificationObserver { ...@@ -96,7 +103,7 @@ class IconImage : public content::NotificationObserver {
const ExtensionIconSet& icon_set_; const ExtensionIconSet& icon_set_;
const int resource_size_in_dip_; const int resource_size_in_dip_;
Observer* observer_; ObserverList<Observer> observers_;
Source* source_; // Owned by ImageSkia storage. Source* source_; // Owned by ImageSkia storage.
gfx::ImageSkia image_skia_; gfx::ImageSkia image_skia_;
......
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