Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_H_
      6 #define CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_H_
      7 
      8 #include <map>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/memory/linked_ptr.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/memory/scoped_vector.h"
     16 #include "base/memory/weak_ptr.h"
     17 #include "base/observer_list.h"
     18 #include "chrome/common/extensions/api/extension_action/action_info.h"
     19 #include "chrome/common/extensions/extension_icon_set.h"
     20 #include "third_party/skia/include/core/SkColor.h"
     21 // TODO(robertphillips): change this to "class SkBaseDevice;"
     22 #include "third_party/skia/include/core/SkDevice.h"
     23 #include "ui/gfx/animation/linear_animation.h"
     24 
     25 class GURL;
     26 class SkBitmap;
     27 
     28 namespace gfx {
     29 class Canvas;
     30 class Image;
     31 class ImageSkia;
     32 class Rect;
     33 class Size;
     34 }
     35 
     36 // ExtensionAction encapsulates the state of a browser action, page action, or
     37 // script badge.
     38 // Instances can have both global and per-tab state. If a property does not have
     39 // a per-tab value, the global value is used instead.
     40 class ExtensionAction {
     41  public:
     42   // Use this ID to indicate the default state for properties that take a tab_id
     43   // parameter.
     44   static const int kDefaultTabId;
     45 
     46   enum Appearance {
     47     // The action icon is hidden.
     48     INVISIBLE,
     49     // The action is trying to get the user's attention but isn't yet
     50     // running on the page.  Currently only used for script badges.
     51     WANTS_ATTENTION,
     52     // The action icon is visible with its normal appearance.
     53     ACTIVE,
     54   };
     55 
     56   // A fade-in animation.
     57   class IconAnimation : public gfx::LinearAnimation {
     58    public:
     59     // Observes changes to icon animation state.
     60     class Observer {
     61      public:
     62       virtual void OnIconChanged() = 0;
     63 
     64      protected:
     65       virtual ~Observer() {}
     66     };
     67 
     68     // A holder for an IconAnimation with a scoped observer.
     69     class ScopedObserver {
     70      public:
     71       ScopedObserver(const base::WeakPtr<IconAnimation>& icon_animation,
     72                      Observer* observer);
     73       ~ScopedObserver();
     74 
     75       // Gets the icon animation, or NULL if the reference has expired.
     76       const IconAnimation* icon_animation() const {
     77         return icon_animation_.get();
     78       }
     79 
     80      private:
     81       base::WeakPtr<IconAnimation> icon_animation_;
     82       Observer* observer_;
     83 
     84       DISALLOW_COPY_AND_ASSIGN(ScopedObserver);
     85     };
     86 
     87     virtual ~IconAnimation();
     88 
     89     // Returns the icon derived from the current animation state applied to
     90     // |icon|. Ownership remains with this.
     91     const SkBitmap& Apply(const SkBitmap& icon) const;
     92 
     93     void AddObserver(Observer* observer);
     94     void RemoveObserver(Observer* observer);
     95 
     96    private:
     97     // Construct using ExtensionAction::RunIconAnimation().
     98     friend class ExtensionAction;
     99     IconAnimation();
    100 
    101     base::WeakPtr<IconAnimation> AsWeakPtr();
    102 
    103     // gfx::LinearAnimation implementation.
    104     virtual void AnimateToState(double state) OVERRIDE;
    105 
    106     // Device we use to paint icons to.
    107     mutable scoped_ptr<SkBaseDevice> device_;
    108 
    109     ObserverList<Observer> observers_;
    110 
    111     base::WeakPtrFactory<IconAnimation> weak_ptr_factory_;
    112 
    113     DISALLOW_COPY_AND_ASSIGN(IconAnimation);
    114   };
    115 
    116   ExtensionAction(const std::string& extension_id,
    117                   extensions::ActionInfo::Type action_type,
    118                   const extensions::ActionInfo& manifest_data);
    119   ~ExtensionAction();
    120 
    121   // Gets a copy of this, ownership passed to caller.
    122   // It doesn't make sense to copy of an ExtensionAction except in tests.
    123   scoped_ptr<ExtensionAction> CopyForTest() const;
    124 
    125   // Given the extension action type, returns the size the extension action icon
    126   // should have. The icon should be square, so only one dimension is
    127   // returned.
    128   static int GetIconSizeForType(extensions::ActionInfo::Type type);
    129 
    130   // extension id
    131   const std::string& extension_id() const { return extension_id_; }
    132 
    133   // What kind of action is this?
    134   extensions::ActionInfo::Type action_type() const {
    135     return action_type_;
    136   }
    137 
    138   // action id -- only used with legacy page actions API
    139   std::string id() const { return id_; }
    140   void set_id(const std::string& id) { id_ = id; }
    141 
    142   bool has_changed() const { return has_changed_; }
    143   void set_has_changed(bool value) { has_changed_ = value; }
    144 
    145   // Set the url which the popup will load when the user clicks this action's
    146   // icon.  Setting an empty URL will disable the popup for a given tab.
    147   void SetPopupUrl(int tab_id, const GURL& url);
    148 
    149   // Use HasPopup() to see if a popup should be displayed.
    150   bool HasPopup(int tab_id) const;
    151 
    152   // Get the URL to display in a popup.
    153   GURL GetPopupUrl(int tab_id) const;
    154 
    155   // Set this action's title on a specific tab.
    156   void SetTitle(int tab_id, const std::string& title) {
    157     SetValue(&title_, tab_id, title);
    158   }
    159 
    160   // If tab |tab_id| has a set title, return it.  Otherwise, return
    161   // the default title.
    162   std::string GetTitle(int tab_id) const { return GetValue(&title_, tab_id); }
    163 
    164   // Icons are a bit different because the default value can be set to either a
    165   // bitmap or a path. However, conceptually, there is only one default icon.
    166   // Setting the default icon using a path clears the bitmap and vice-versa.
    167   // To retrieve the icon for the extension action, use
    168   // ExtensionActionIconFactory.
    169 
    170   // Set this action's icon bitmap on a specific tab.
    171   void SetIcon(int tab_id, const gfx::Image& image);
    172 
    173   // Applies the attention and animation image transformations registered for
    174   // the tab on the provided icon.
    175   gfx::Image ApplyAttentionAndAnimation(const gfx::ImageSkia& icon,
    176                                         int tab_id) const;
    177 
    178   // Gets the icon that has been set using |SetIcon| for the tab.
    179   gfx::ImageSkia GetExplicitlySetIcon(int tab_id) const;
    180 
    181   // Non-tab-specific icon path. This is used to support the default_icon key of
    182   // page and browser actions.
    183   void set_default_icon(scoped_ptr<ExtensionIconSet> icon_set) {
    184      default_icon_ = icon_set.Pass();
    185   }
    186 
    187   const ExtensionIconSet* default_icon() const {
    188     return default_icon_.get();
    189   }
    190 
    191   // Set this action's badge text on a specific tab.
    192   void SetBadgeText(int tab_id, const std::string& text) {
    193     SetValue(&badge_text_, tab_id, text);
    194   }
    195   // Get the badge text for a tab, or the default if no badge text was set.
    196   std::string GetBadgeText(int tab_id) const {
    197     return GetValue(&badge_text_, tab_id);
    198   }
    199 
    200   // Set this action's badge text color on a specific tab.
    201   void SetBadgeTextColor(int tab_id, SkColor text_color) {
    202     SetValue(&badge_text_color_, tab_id, text_color);
    203   }
    204   // Get the text color for a tab, or the default color if no text color
    205   // was set.
    206   SkColor GetBadgeTextColor(int tab_id) const {
    207     return GetValue(&badge_text_color_, tab_id);
    208   }
    209 
    210   // Set this action's badge background color on a specific tab.
    211   void SetBadgeBackgroundColor(int tab_id, SkColor color) {
    212     SetValue(&badge_background_color_, tab_id, color);
    213   }
    214   // Get the badge background color for a tab, or the default if no color
    215   // was set.
    216   SkColor GetBadgeBackgroundColor(int tab_id) const {
    217     return GetValue(&badge_background_color_, tab_id);
    218   }
    219 
    220   // Set this action's badge visibility on a specific tab.  This takes
    221   // care of any appropriate transition animations.  Returns true if
    222   // the appearance has changed.
    223   bool SetAppearance(int tab_id, Appearance value);
    224   // The declarative appearance overrides a default appearance but is overridden
    225   // by an appearance set directly on the tab.
    226   void DeclarativeShow(int tab_id);
    227   void UndoDeclarativeShow(int tab_id);
    228 
    229   // Get the badge visibility for a tab, or the default badge visibility
    230   // if none was set.
    231   bool GetIsVisible(int tab_id) const {
    232     return GetAppearance(tab_id) != INVISIBLE;
    233   }
    234 
    235   // True if the tab's action wants the user's attention.
    236   bool WantsAttention(int tab_id) const {
    237     return GetAppearance(tab_id) == WANTS_ATTENTION;
    238   }
    239 
    240   // Remove all tab-specific state.
    241   void ClearAllValuesForTab(int tab_id);
    242 
    243   // If the specified tab has a badge, paint it into the provided bounds.
    244   void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds, int tab_id);
    245 
    246   // Returns icon image with badge for specified tab.
    247   gfx::ImageSkia GetIconWithBadge(const gfx::ImageSkia& icon,
    248                                   int tab_id,
    249                                   const gfx::Size& spacing) const;
    250 
    251   // Gets a weak reference to the icon animation for a tab, if any. The
    252   // reference will only have a value while the animation is running.
    253   base::WeakPtr<IconAnimation> GetIconAnimation(int tab_id) const;
    254 
    255  private:
    256   // Runs an animation on a tab.
    257   void RunIconAnimation(int tab_id);
    258 
    259   // If the icon animation is running on tab |tab_id|, applies it to
    260   // |orig| and returns the result. Otherwise, just returns |orig|.
    261   gfx::ImageSkia ApplyIconAnimation(int tab_id,
    262                                     const gfx::ImageSkia& orig) const;
    263 
    264   // Returns width of the current icon for tab_id.
    265   // TODO(tbarzic): The icon selection is done in ExtensionActionIconFactory.
    266   // We should probably move this there too.
    267   int GetIconWidth(int tab_id) const;
    268 
    269   template <class T>
    270   struct ValueTraits {
    271     static T CreateEmpty() {
    272       return T();
    273     }
    274   };
    275 
    276   template<class T>
    277   void SetValue(std::map<int, T>* map, int tab_id, const T& val) {
    278     (*map)[tab_id] = val;
    279   }
    280 
    281   template<class Map>
    282   static const typename Map::mapped_type* FindOrNull(
    283       const Map* map,
    284       const typename Map::key_type& key) {
    285     typename Map::const_iterator iter = map->find(key);
    286     if (iter == map->end())
    287       return NULL;
    288     return &iter->second;
    289   }
    290 
    291   template<class T>
    292   T GetValue(const std::map<int, T>* map, int tab_id) const {
    293     if (const T* tab_value = FindOrNull(map, tab_id)) {
    294       return *tab_value;
    295     } else if (const T* default_value = FindOrNull(map, kDefaultTabId)) {
    296       return *default_value;
    297     } else {
    298       return ValueTraits<T>::CreateEmpty();
    299     }
    300   }
    301 
    302   // Gets the appearance of |tab_id|.  Returns the first of: a specific
    303   // appearance set on the tab; a declarative appearance set on the tab; the
    304   // default appearance set for all tabs; or INVISIBLE.  Don't return this
    305   // result to an extension's background page because the declarative state can
    306   // leak information about hosts the extension doesn't have permission to
    307   // access.
    308   Appearance GetAppearance(int tab_id) const {
    309     if (const Appearance* tab_appearance = FindOrNull(&appearance_, tab_id))
    310       return *tab_appearance;
    311 
    312     if (ContainsKey(declarative_show_count_, tab_id))
    313       return ACTIVE;
    314 
    315     if (const Appearance* default_appearance =
    316         FindOrNull(&appearance_, kDefaultTabId))
    317       return *default_appearance;
    318 
    319     return INVISIBLE;
    320   }
    321 
    322   // The id for the extension this action belongs to (as defined in the
    323   // extension manifest).
    324   const std::string extension_id_;
    325 
    326   const extensions::ActionInfo::Type action_type_;
    327 
    328   // Each of these data items can have both a global state (stored with the key
    329   // kDefaultTabId), or tab-specific state (stored with the tab_id as the key).
    330   std::map<int, GURL> popup_url_;
    331   std::map<int, std::string> title_;
    332   std::map<int, gfx::ImageSkia> icon_;
    333   std::map<int, std::string> badge_text_;
    334   std::map<int, SkColor> badge_background_color_;
    335   std::map<int, SkColor> badge_text_color_;
    336   std::map<int, Appearance> appearance_;
    337 
    338   // Declarative state exists for two reasons: First, we need to hide it from
    339   // the extension's background/event page to avoid leaking data from hosts the
    340   // extension doesn't have permission to access.  Second, the action's state
    341   // gets both reset and given its declarative values in response to a
    342   // WebContentsObserver::DidNavigateMainFrame event, and there's no way to set
    343   // those up to be called in the right order.
    344 
    345   // Maps tab_id to the number of active (applied-but-not-reverted)
    346   // declarativeContent.ShowPageAction actions.
    347   std::map<int, int> declarative_show_count_;
    348 
    349   // IconAnimations are destroyed by a delayed task on the UI message loop so
    350   // that even if the Extension and ExtensionAction are destroyed on a non-UI
    351   // thread, the animation will still only be touched from the UI thread.  This
    352   // causes the WeakPtr in this map to become NULL.  GetIconAnimation() removes
    353   // NULLs to prevent the map from growing without bound.
    354   mutable std::map<int, base::WeakPtr<IconAnimation> > icon_animation_;
    355 
    356   // ExtensionIconSet containing paths to bitmaps from which default icon's
    357   // image representations will be selected.
    358   scoped_ptr<const ExtensionIconSet> default_icon_;
    359 
    360   // The id for the ExtensionAction, for example: "RssPageAction". This is
    361   // needed for compat with an older version of the page actions API.
    362   std::string id_;
    363 
    364   // True if the ExtensionAction's settings have changed from what was
    365   // specified in the manifest.
    366   bool has_changed_;
    367 
    368   DISALLOW_COPY_AND_ASSIGN(ExtensionAction);
    369 };
    370 
    371 template<>
    372 struct ExtensionAction::ValueTraits<int> {
    373   static int CreateEmpty() {
    374     return -1;
    375   }
    376 };
    377 
    378 #endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_H_
    379