// 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_ACTION_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_H_
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.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/extension_icon_set.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 SkBitmap;
namespace gfx {
class Canvas;
class Image;
class ImageSkia;
class Rect;
class Size;
}
// ExtensionAction encapsulates the state of a browser action, page action, or
// script badge.
// Instances can have both global and per-tab state. If a property does not have
// a per-tab value, the global value is used instead.
class ExtensionAction {
public:
// Use this ID to indicate the default state for properties that take a tab_id
// parameter.
static const int kDefaultTabId;
enum Appearance {
// The action icon is hidden.
INVISIBLE,
// The action is trying to get the user's attention but isn't yet
// running on the page. Currently only used for script badges.
WANTS_ATTENTION,
// The action icon is visible with its normal appearance.
ACTIVE,
};
// A fade-in animation.
class IconAnimation : public gfx::LinearAnimation {
public:
// Observes changes to icon animation state.
class Observer {
public:
virtual void OnIconChanged() = 0;
protected:
virtual ~Observer() {}
};
// A holder for an IconAnimation with a scoped observer.
class ScopedObserver {
public:
ScopedObserver(const base::WeakPtr<IconAnimation>& icon_animation,
Observer* observer);
~ScopedObserver();
// Gets the icon animation, or NULL if the reference has expired.
const IconAnimation* icon_animation() const {
return icon_animation_.get();
}
private:
base::WeakPtr<IconAnimation> icon_animation_;
Observer* observer_;
DISALLOW_COPY_AND_ASSIGN(ScopedObserver);
};
virtual ~IconAnimation();
// Returns the icon derived from the current animation state applied to
// |icon|. Ownership remains with this.
const SkBitmap& Apply(const SkBitmap& icon) const;
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private:
// Construct using ExtensionAction::RunIconAnimation().
friend class ExtensionAction;
IconAnimation();
base::WeakPtr<IconAnimation> AsWeakPtr();
// gfx::LinearAnimation implementation.
virtual void AnimateToState(double state) OVERRIDE;
// Device we use to paint icons to.
mutable scoped_ptr<SkBaseDevice> device_;
ObserverList<Observer> observers_;
base::WeakPtrFactory<IconAnimation> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(IconAnimation);
};
ExtensionAction(const std::string& extension_id,
extensions::ActionInfo::Type action_type,
const extensions::ActionInfo& manifest_data);
~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
const std::string& extension_id() const { return extension_id_; }
// What kind of action is this?
extensions::ActionInfo::Type action_type() const {
return action_type_;
}
// action id -- only used with legacy page actions API
std::string id() const { return id_; }
void set_id(const std::string& id) { id_ = id; }
bool has_changed() const { return has_changed_; }
void set_has_changed(bool value) { has_changed_ = value; }
// Set the url which the popup will load when the user clicks this action's
// icon. Setting an empty URL will disable the popup for a given tab.
void SetPopupUrl(int tab_id, const GURL& url);
// Use HasPopup() to see if a popup should be displayed.
bool HasPopup(int tab_id) const;
// Get the URL to display in a popup.
GURL GetPopupUrl(int tab_id) const;
// Set this action's title on a specific tab.
void SetTitle(int tab_id, const std::string& title) {
SetValue(&title_, tab_id, title);
}
// If tab |tab_id| has a set title, return it. Otherwise, return
// the default title.
std::string GetTitle(int tab_id) const { return GetValue(&title_, tab_id); }
// Icons are a bit different because the default value can be set to either a
// bitmap or a path. However, conceptually, there is only one default icon.
// Setting the default icon using a path clears the bitmap and vice-versa.
// To retrieve the icon for the extension action, use
// ExtensionActionIconFactory.
// Set this action's icon bitmap on a specific tab.
void SetIcon(int tab_id, const gfx::Image& image);
// Applies the attention and animation image transformations registered for
// the tab on the provided icon.
gfx::Image ApplyAttentionAndAnimation(const gfx::ImageSkia& icon,
int tab_id) const;
// Gets the icon that has been set using |SetIcon| for the tab.
gfx::ImageSkia GetExplicitlySetIcon(int tab_id) const;
// 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 {
return default_icon_.get();
}
// Set this action's badge text on a specific tab.
void SetBadgeText(int tab_id, const std::string& text) {
SetValue(&badge_text_, tab_id, text);
}
// Get the badge text for a tab, or the default if no badge text was set.
std::string GetBadgeText(int tab_id) const {
return GetValue(&badge_text_, tab_id);
}
// Set this action's badge text color on a specific tab.
void SetBadgeTextColor(int tab_id, SkColor text_color) {
SetValue(&badge_text_color_, tab_id, text_color);
}
// Get the text color for a tab, or the default color if no text color
// was set.
SkColor GetBadgeTextColor(int tab_id) const {
return GetValue(&badge_text_color_, tab_id);
}
// Set this action's badge background color on a specific tab.
void SetBadgeBackgroundColor(int tab_id, SkColor color) {
SetValue(&badge_background_color_, tab_id, color);
}
// Get the badge background color for a tab, or the default if no color
// was set.
SkColor GetBadgeBackgroundColor(int tab_id) const {
return GetValue(&badge_background_color_, tab_id);
}
// Set this action's badge visibility on a specific tab. This takes
// care of any appropriate transition animations. Returns true if
// the appearance has changed.
bool SetAppearance(int tab_id, Appearance value);
// The declarative appearance overrides a default appearance but is overridden
// by an appearance set directly on the tab.
void DeclarativeShow(int tab_id);
void UndoDeclarativeShow(int tab_id);
// Get the badge visibility for a tab, or the default badge visibility
// if none was set.
bool GetIsVisible(int tab_id) const {
return GetAppearance(tab_id) != INVISIBLE;
}
// True if the tab's action wants the user's attention.
bool WantsAttention(int tab_id) const {
return GetAppearance(tab_id) == WANTS_ATTENTION;
}
// Remove all tab-specific state.
void ClearAllValuesForTab(int tab_id);
// If the specified tab has a badge, paint it into the provided bounds.
void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds, int tab_id);
// Returns icon image with badge for specified tab.
gfx::ImageSkia GetIconWithBadge(const gfx::ImageSkia& icon,
int tab_id,
const gfx::Size& spacing) const;
// Gets a weak reference to the icon animation for a tab, if any. The
// reference will only have a value while the animation is running.
base::WeakPtr<IconAnimation> GetIconAnimation(int tab_id) const;
private:
// Runs an animation on a tab.
void RunIconAnimation(int tab_id);
// If the icon animation is running on tab |tab_id|, applies it to
// |orig| and returns the result. Otherwise, just returns |orig|.
gfx::ImageSkia ApplyIconAnimation(int tab_id,
const gfx::ImageSkia& orig) const;
// Returns width of the current icon for tab_id.
// TODO(tbarzic): The icon selection is done in ExtensionActionIconFactory.
// We should probably move this there too.
int GetIconWidth(int tab_id) const;
template <class T>
struct ValueTraits {
static T CreateEmpty() {
return T();
}
};
template<class T>
void SetValue(std::map<int, T>* map, int tab_id, const T& val) {
(*map)[tab_id] = val;
}
template<class Map>
static const typename Map::mapped_type* FindOrNull(
const Map* map,
const typename Map::key_type& key) {
typename Map::const_iterator iter = map->find(key);
if (iter == map->end())
return NULL;
return &iter->second;
}
template<class T>
T GetValue(const std::map<int, T>* map, int tab_id) const {
if (const T* tab_value = FindOrNull(map, tab_id)) {
return *tab_value;
} else if (const T* default_value = FindOrNull(map, kDefaultTabId)) {
return *default_value;
} else {
return ValueTraits<T>::CreateEmpty();
}
}
// Gets the appearance of |tab_id|. Returns the first of: a specific
// appearance set on the tab; a declarative appearance set on the tab; the
// default appearance set for all tabs; or INVISIBLE. Don't return this
// result to an extension's background page because the declarative state can
// leak information about hosts the extension doesn't have permission to
// access.
Appearance GetAppearance(int tab_id) const {
if (const Appearance* tab_appearance = FindOrNull(&appearance_, tab_id))
return *tab_appearance;
if (ContainsKey(declarative_show_count_, tab_id))
return ACTIVE;
if (const Appearance* default_appearance =
FindOrNull(&appearance_, kDefaultTabId))
return *default_appearance;
return INVISIBLE;
}
// The id for the extension this action belongs to (as defined in the
// extension manifest).
const std::string extension_id_;
const extensions::ActionInfo::Type action_type_;
// Each of these data items can have both a global state (stored with the key
// kDefaultTabId), or tab-specific state (stored with the tab_id as the key).
std::map<int, GURL> popup_url_;
std::map<int, std::string> title_;
std::map<int, gfx::ImageSkia> icon_;
std::map<int, std::string> badge_text_;
std::map<int, SkColor> badge_background_color_;
std::map<int, SkColor> badge_text_color_;
std::map<int, Appearance> appearance_;
// Declarative state exists for two reasons: First, we need to hide it from
// the extension's background/event page to avoid leaking data from hosts the
// extension doesn't have permission to access. Second, the action's state
// gets both reset and given its declarative values in response to a
// WebContentsObserver::DidNavigateMainFrame event, and there's no way to set
// those up to be called in the right order.
// Maps tab_id to the number of active (applied-but-not-reverted)
// declarativeContent.ShowPageAction actions.
std::map<int, int> declarative_show_count_;
// IconAnimations are destroyed by a delayed task on the UI message loop so
// that even if the Extension and ExtensionAction are destroyed on a non-UI
// thread, the animation will still only be touched from the UI thread. This
// causes the WeakPtr in this map to become NULL. GetIconAnimation() removes
// NULLs to prevent the map from growing without bound.
mutable std::map<int, base::WeakPtr<IconAnimation> > icon_animation_;
// ExtensionIconSet containing paths to bitmaps from which default icon's
// image representations will be selected.
scoped_ptr<const ExtensionIconSet> default_icon_;
// The id for the ExtensionAction, for example: "RssPageAction". This is
// needed for compat with an older version of the page actions API.
std::string id_;
// True if the ExtensionAction's settings have changed from what was
// specified in the manifest.
bool has_changed_;
DISALLOW_COPY_AND_ASSIGN(ExtensionAction);
};
template<>
struct ExtensionAction::ValueTraits<int> {
static int CreateEmpty() {
return -1;
}
};
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_H_