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