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 "extensions/common/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
25class GURL;
26class SkBitmap;
27
28namespace gfx {
29class Canvas;
30class Image;
31class ImageSkia;
32class Rect;
33class Size;
34}
35
36// ExtensionAction encapsulates the state of a browser action or page action.
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  // The action that the UI should take after the ExtensionAction is clicked.
42  enum ShowAction {
43    ACTION_NONE,
44    ACTION_SHOW_POPUP,
45    // We don't need a SHOW_CONTEXT_MENU because that's handled separately in
46    // the UI.
47  };
48
49  // Use this ID to indicate the default state for properties that take a tab_id
50  // parameter.
51  static const int kDefaultTabId;
52
53  // Max size (both dimensions) for page actions.
54  static const int kPageActionIconMaxSize;
55
56  ExtensionAction(const std::string& extension_id,
57                  extensions::ActionInfo::Type action_type,
58                  const extensions::ActionInfo& manifest_data);
59  ~ExtensionAction();
60
61  // Gets a copy of this, ownership passed to caller.
62  // It doesn't make sense to copy of an ExtensionAction except in tests.
63  scoped_ptr<ExtensionAction> CopyForTest() const;
64
65  // Given the extension action type, returns the size the extension action icon
66  // should have. The icon should be square, so only one dimension is
67  // returned.
68  static int GetIconSizeForType(extensions::ActionInfo::Type type);
69
70  // extension id
71  const std::string& extension_id() const { return extension_id_; }
72
73  // What kind of action is this?
74  extensions::ActionInfo::Type action_type() const {
75    return action_type_;
76  }
77
78  // action id -- only used with legacy page actions API
79  std::string id() const { return id_; }
80  void set_id(const std::string& id) { id_ = id; }
81
82  // Set the url which the popup will load when the user clicks this action's
83  // icon.  Setting an empty URL will disable the popup for a given tab.
84  void SetPopupUrl(int tab_id, const GURL& url);
85
86  // Use HasPopup() to see if a popup should be displayed.
87  bool HasPopup(int tab_id) const;
88
89  // Get the URL to display in a popup.
90  GURL GetPopupUrl(int tab_id) const;
91
92  // Set this action's title on a specific tab.
93  void SetTitle(int tab_id, const std::string& title) {
94    SetValue(&title_, tab_id, title);
95  }
96
97  // If tab |tab_id| has a set title, return it.  Otherwise, return
98  // the default title.
99  std::string GetTitle(int tab_id) const { return GetValue(&title_, tab_id); }
100
101  // Icons are a bit different because the default value can be set to either a
102  // bitmap or a path. However, conceptually, there is only one default icon.
103  // Setting the default icon using a path clears the bitmap and vice-versa.
104  // To retrieve the icon for the extension action, use
105  // ExtensionActionIconFactory.
106
107  // Set this action's icon bitmap on a specific tab.
108  void SetIcon(int tab_id, const gfx::Image& image);
109
110  // Tries to parse |*icon| from a dictionary {"19": imageData19, "38":
111  // imageData38}, returning false if a value is corrupt.
112  static bool ParseIconFromCanvasDictionary(const base::DictionaryValue& dict,
113                                            gfx::ImageSkia* icon);
114
115  // Gets the icon that has been set using |SetIcon| for the tab.
116  gfx::ImageSkia GetExplicitlySetIcon(int tab_id) const;
117
118  // Sets the icon for a tab, in a way that can't be read by the extension's
119  // javascript.  Multiple icons can be set at the same time; some icon with the
120  // highest priority will be used.
121  void DeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon);
122  void UndoDeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon);
123
124  // Non-tab-specific icon path. This is used to support the default_icon key of
125  // page and browser actions.
126  void set_default_icon(scoped_ptr<ExtensionIconSet> icon_set) {
127     default_icon_ = icon_set.Pass();
128  }
129
130  const ExtensionIconSet* default_icon() const {
131    return default_icon_.get();
132  }
133
134  // Set this action's badge text on a specific tab.
135  void SetBadgeText(int tab_id, const std::string& text) {
136    SetValue(&badge_text_, tab_id, text);
137  }
138  // Get the badge text for a tab, or the default if no badge text was set.
139  std::string GetBadgeText(int tab_id) const {
140    return GetValue(&badge_text_, tab_id);
141  }
142
143  // Set this action's badge text color on a specific tab.
144  void SetBadgeTextColor(int tab_id, SkColor text_color) {
145    SetValue(&badge_text_color_, tab_id, text_color);
146  }
147  // Get the text color for a tab, or the default color if no text color
148  // was set.
149  SkColor GetBadgeTextColor(int tab_id) const {
150    return GetValue(&badge_text_color_, tab_id);
151  }
152
153  // Set this action's badge background color on a specific tab.
154  void SetBadgeBackgroundColor(int tab_id, SkColor color) {
155    SetValue(&badge_background_color_, tab_id, color);
156  }
157  // Get the badge background color for a tab, or the default if no color
158  // was set.
159  SkColor GetBadgeBackgroundColor(int tab_id) const {
160    return GetValue(&badge_background_color_, tab_id);
161  }
162
163  // Set this action's badge visibility on a specific tab.  Returns true if
164  // the visibility has changed.
165  bool SetIsVisible(int tab_id, bool value);
166  // The declarative appearance overrides a default appearance but is overridden
167  // by an appearance set directly on the tab.
168  void DeclarativeShow(int tab_id);
169  void UndoDeclarativeShow(int tab_id);
170  const gfx::ImageSkia GetDeclarativeIcon(int tab_id) const;
171
172  // Get the badge visibility for a tab, or the default badge visibility
173  // if none was set.
174  // Gets the visibility of |tab_id|.  Returns the first of: a specific
175  // visibility set on the tab; a declarative visibility set on the tab; the
176  // default visibility set for all tabs; or |false|.  Don't return this
177  // result to an extension's background page because the declarative state can
178  // leak information about hosts the extension doesn't have permission to
179  // access.
180  bool GetIsVisible(int tab_id) const {
181    if (const bool* tab_is_visible = FindOrNull(&is_visible_, tab_id))
182      return *tab_is_visible;
183
184    if (ContainsKey(declarative_show_count_, tab_id))
185      return true;
186
187    if (const bool* default_is_visible =
188        FindOrNull(&is_visible_, kDefaultTabId))
189      return *default_is_visible;
190
191    return false;
192  }
193
194  // Remove all tab-specific state.
195  void ClearAllValuesForTab(int tab_id);
196
197  // If the specified tab has a badge, paint it into the provided bounds.
198  void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds, int tab_id);
199
200  // Returns icon image with badge for specified tab.
201  gfx::ImageSkia GetIconWithBadge(const gfx::ImageSkia& icon,
202                                  int tab_id,
203                                  const gfx::Size& spacing) const;
204
205  // Determine whether or not the ExtensionAction has a value set for the given
206  // |tab_id| for each property.
207  bool HasPopupUrl(int tab_id) const;
208  bool HasTitle(int tab_id) const;
209  bool HasBadgeText(int tab_id) const;
210  bool HasBadgeBackgroundColor(int tab_id) const;
211  bool HasBadgeTextColor(int tab_id) const;
212  bool HasIsVisible(int tab_id) const;
213  bool HasIcon(int tab_id) const;
214
215 private:
216  // Returns width of the current icon for tab_id.
217  // TODO(tbarzic): The icon selection is done in ExtensionActionIconFactory.
218  // We should probably move this there too.
219  int GetIconWidth(int tab_id) const;
220
221  template <class T>
222  struct ValueTraits {
223    static T CreateEmpty() {
224      return T();
225    }
226  };
227
228  template<class T>
229  void SetValue(std::map<int, T>* map, int tab_id, const T& val) {
230    (*map)[tab_id] = val;
231  }
232
233  template<class Map>
234  static const typename Map::mapped_type* FindOrNull(
235      const Map* map,
236      const typename Map::key_type& key) {
237    typename Map::const_iterator iter = map->find(key);
238    if (iter == map->end())
239      return NULL;
240    return &iter->second;
241  }
242
243  template<class T>
244  T GetValue(const std::map<int, T>* map, int tab_id) const {
245    if (const T* tab_value = FindOrNull(map, tab_id)) {
246      return *tab_value;
247    } else if (const T* default_value = FindOrNull(map, kDefaultTabId)) {
248      return *default_value;
249    } else {
250      return ValueTraits<T>::CreateEmpty();
251    }
252  }
253
254  // The id for the extension this action belongs to (as defined in the
255  // extension manifest).
256  const std::string extension_id_;
257
258  const extensions::ActionInfo::Type action_type_;
259
260  // Each of these data items can have both a global state (stored with the key
261  // kDefaultTabId), or tab-specific state (stored with the tab_id as the key).
262  std::map<int, GURL> popup_url_;
263  std::map<int, std::string> title_;
264  std::map<int, gfx::ImageSkia> icon_;
265  std::map<int, std::string> badge_text_;
266  std::map<int, SkColor> badge_background_color_;
267  std::map<int, SkColor> badge_text_color_;
268  std::map<int, bool> is_visible_;
269
270  // Declarative state exists for two reasons: First, we need to hide it from
271  // the extension's background/event page to avoid leaking data from hosts the
272  // extension doesn't have permission to access.  Second, the action's state
273  // gets both reset and given its declarative values in response to a
274  // WebContentsObserver::DidNavigateMainFrame event, and there's no way to set
275  // those up to be called in the right order.
276
277  // Maps tab_id to the number of active (applied-but-not-reverted)
278  // declarativeContent.ShowPageAction actions.
279  std::map<int, int> declarative_show_count_;
280
281  // declarative_icon_[tab_id][declarative_rule_priority] is a vector of icon
282  // images that are currently in effect
283  std::map<int, std::map<int, std::vector<gfx::Image> > > declarative_icon_;
284
285  // ExtensionIconSet containing paths to bitmaps from which default icon's
286  // image representations will be selected.
287  scoped_ptr<const ExtensionIconSet> default_icon_;
288
289  // The id for the ExtensionAction, for example: "RssPageAction". This is
290  // needed for compat with an older version of the page actions API.
291  std::string id_;
292
293  DISALLOW_COPY_AND_ASSIGN(ExtensionAction);
294};
295
296template<>
297struct ExtensionAction::ValueTraits<int> {
298  static int CreateEmpty() {
299    return -1;
300  }
301};
302
303#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_ACTION_H_
304