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 EXTENSIONS_BROWSER_EXTENSION_ACTION_H_
6 #define EXTENSIONS_BROWSER_EXTENSION_ACTION_H_
7 
8 #include <map>
9 #include <memory>
10 #include <string>
11 #include <vector>
12 
13 #include "base/macros.h"
14 #include "base/stl_util.h"
15 #include "extensions/common/api/extension_action/action_info.h"
16 #include "extensions/common/constants.h"
17 #include "third_party/skia/include/core/SkColor.h"
18 #include "ui/gfx/image/image.h"
19 
20 class GURL;
21 
22 namespace gfx {
23 class Image;
24 class ImageSkia;
25 }  // namespace gfx
26 
27 namespace extensions {
28 class Extension;
29 class IconImage;
30 
31 // ExtensionAction encapsulates the state of a browser action or page action.
32 // Instances can have both global and per-tab state. If a property does not have
33 // a per-tab value, the global value is used instead.
34 class ExtensionAction {
35  public:
36   // The action that the UI should take after the ExtensionAction is clicked.
37   enum ShowAction {
38     ACTION_NONE,
39     ACTION_SHOW_POPUP,
40     // We don't need a SHOW_CONTEXT_MENU because that's handled separately in
41     // the UI.
42   };
43 
44   enum class IconParseResult {
45     kSuccess,
46     kDecodeFailure,
47     kUnpickleFailure,
48   };
49 
50   static extension_misc::ExtensionIcons ActionIconSize();
51 
52   // Returns the default icon to use when no other is available (the puzzle
53   // piece).
54   static gfx::Image FallbackIcon();
55 
56   // Use this ID to indicate the default state for properties that take a tab_id
57   // parameter.
58   static const int kDefaultTabId;
59 
60   ExtensionAction(const Extension& extension, const ActionInfo& manifest_data);
61   ~ExtensionAction();
62 
63   // extension id
extension_id()64   const std::string& extension_id() const { return extension_id_; }
65 
66   // What kind of action is this?
action_type()67   ActionInfo::Type action_type() const { return action_type_; }
68 
default_state()69   ActionInfo::DefaultState default_state() const { return default_state_; }
70 
71   // Set the url which the popup will load when the user clicks this action's
72   // icon.  Setting an empty URL will disable the popup for a given tab.
73   void SetPopupUrl(int tab_id, const GURL& url);
74 
75   // Use HasPopup() to see if a popup should be displayed.
76   bool HasPopup(int tab_id) const;
77 
78   // Get the URL to display in a popup.
79   GURL GetPopupUrl(int tab_id) const;
80 
81   // Set this action's title on a specific tab.
SetTitle(int tab_id,const std::string & title)82   void SetTitle(int tab_id, const std::string& title) {
83     SetValue(&title_, tab_id, title);
84   }
85 
86   // If tab |tab_id| has a set title, return it.  Otherwise, return
87   // the default title.
GetTitle(int tab_id)88   std::string GetTitle(int tab_id) const { return GetValue(&title_, tab_id); }
89 
90   // Icons are a bit different because the default value can be set to either a
91   // bitmap or a path. However, conceptually, there is only one default icon.
92   // Setting the default icon using a path clears the bitmap and vice-versa.
93   // To retrieve the icon for the extension action, use
94   // ExtensionActionIconFactory.
95 
96   // Set this action's icon bitmap on a specific tab.
97   void SetIcon(int tab_id, const gfx::Image& image);
98 
99   // Tries to parse |*icon| from a dictionary {"19": imageData19, "38":
100   // imageData38}, and returns the result of the parsing attempt.
101   static IconParseResult ParseIconFromCanvasDictionary(
102       const base::DictionaryValue& dict,
103       gfx::ImageSkia* icon);
104 
105   // Gets the icon that has been set using |SetIcon| for the tab.
106   gfx::Image GetExplicitlySetIcon(int tab_id) const;
107 
108   // Sets the icon for a tab, in a way that can't be read by the extension's
109   // javascript.  Multiple icons can be set at the same time; some icon with the
110   // highest priority will be used.
111   void DeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon);
112   void UndoDeclarativeSetIcon(int tab_id, int priority, const gfx::Image& icon);
113 
default_icon()114   const ExtensionIconSet* default_icon() const { return default_icon_.get(); }
115 
116   // Set this action's badge text on a specific tab.
SetBadgeText(int tab_id,const std::string & text)117   void SetBadgeText(int tab_id, const std::string& text) {
118     SetValue(&badge_text_, tab_id, text);
119   }
120 
121   // Clear this action's badge text on a specific tab.
ClearBadgeText(int tab_id)122   void ClearBadgeText(int tab_id) { badge_text_.erase(tab_id); }
123 
124   // Get the badge text that has been set using SetBadgeText for a tab, or the
125   // default if no badge text was set.
GetExplicitlySetBadgeText(int tab_id)126   std::string GetExplicitlySetBadgeText(int tab_id) const {
127     return GetValue(&badge_text_, tab_id);
128   }
129 
130   // Set this action's badge text color on a specific tab.
SetBadgeTextColor(int tab_id,SkColor text_color)131   void SetBadgeTextColor(int tab_id, SkColor text_color) {
132     SetValue(&badge_text_color_, tab_id, text_color);
133   }
134   // Get the text color for a tab, or the default color if no text color
135   // was set.
GetBadgeTextColor(int tab_id)136   SkColor GetBadgeTextColor(int tab_id) const {
137     return GetValue(&badge_text_color_, tab_id);
138   }
139 
140   // Set this action's badge background color on a specific tab.
SetBadgeBackgroundColor(int tab_id,SkColor color)141   void SetBadgeBackgroundColor(int tab_id, SkColor color) {
142     SetValue(&badge_background_color_, tab_id, color);
143   }
144   // Get the badge background color for a tab, or the default if no color
145   // was set.
GetBadgeBackgroundColor(int tab_id)146   SkColor GetBadgeBackgroundColor(int tab_id) const {
147     return GetValue(&badge_background_color_, tab_id);
148   }
149 
150   // Set this ExtensionAction's DNR matched action count on a specific tab.
SetDNRActionCount(int tab_id,int action_count)151   void SetDNRActionCount(int tab_id, int action_count) {
152     SetValue(&dnr_action_count_, tab_id, action_count);
153   }
154   // Get this ExtensionAction's DNR matched action count on a specific tab.
155   // Returns -1 if no entry is found.
GetDNRActionCount(int tab_id)156   int GetDNRActionCount(int tab_id) const {
157     return GetValue(&dnr_action_count_, tab_id);
158   }
159   // Clear this ExtensionAction's DNR matched action count for all tabs.
ClearDNRActionCountForAllTabs()160   void ClearDNRActionCountForAllTabs() { dnr_action_count_.clear(); }
161 
162   // Get the badge text displayed for a tab, calculated based on both
163   // |badge_text_| and |dnr_action_count_|. Returns in order of priority:
164   // - GetExplicitlySetBadgeText(tab_id) if it exists for the |tab_id|
165   // - GetDNRActionCount(tab_id) if there is at least one action for this tab
166   // - The default badge text, if set, otherwise: an empty string.
167   std::string GetDisplayBadgeText(int tab_id) const;
168 
169   // Returns whether this extension action is using the DNR action count as its
170   // badge text.
171   bool UseDNRActionCountAsBadgeText(int tab_id) const;
172 
173   // Set this action's badge visibility on a specific tab.  Returns true if
174   // the visibility has changed.
175   bool SetIsVisible(int tab_id, bool value);
176   // The declarative appearance overrides a default appearance but is overridden
177   // by an appearance set directly on the tab.
178   void DeclarativeShow(int tab_id);
179   void UndoDeclarativeShow(int tab_id);
180   const gfx::Image GetDeclarativeIcon(int tab_id) const;
181 
182   // Get the badge visibility for a tab, or the default badge visibility
183   // if none was set.
184   // Gets the visibility of |tab_id|.  Returns the first of: a specific
185   // visibility set on the tab; a declarative visibility set on the tab; the
186   // default visibility set for all tabs; or |false|.  Don't return this
187   // result to an extension's background page because the declarative state can
188   // leak information about hosts the extension doesn't have permission to
189   // access.
GetIsVisible(int tab_id)190   bool GetIsVisible(int tab_id) const {
191     if (const bool* tab_is_visible = FindOrNull(&is_visible_, tab_id))
192       return *tab_is_visible;
193 
194     if (base::Contains(declarative_show_count_, tab_id))
195       return true;
196 
197     if (const bool* default_is_visible =
198             FindOrNull(&is_visible_, kDefaultTabId))
199       return *default_is_visible;
200 
201     return false;
202   }
203 
204   // Remove all tab-specific state.
205   void ClearAllValuesForTab(int tab_id);
206 
207   // Sets the default IconImage for this action.
208   void SetDefaultIconImage(std::unique_ptr<IconImage> icon_image);
209 
210   // Returns the image to use as the default icon for the action. Can only be
211   // called after SetDefaultIconImage().
212   gfx::Image GetDefaultIconImage() const;
213 
214   // Returns the placeholder image for the extension.
215   gfx::Image GetPlaceholderIconImage() const;
216 
217   // Determine whether or not the ExtensionAction has a value set for the given
218   // |tab_id| for each property.
219   bool HasPopupUrl(int tab_id) const;
220   bool HasTitle(int tab_id) const;
221   bool HasBadgeText(int tab_id) const;
222   bool HasBadgeBackgroundColor(int tab_id) const;
223   bool HasBadgeTextColor(int tab_id) const;
224   bool HasIsVisible(int tab_id) const;
225   bool HasIcon(int tab_id) const;
226   bool HasDNRActionCount(int tab_id) const;
227 
default_icon_image()228   IconImage* default_icon_image() { return default_icon_image_.get(); }
229 
230   void SetDefaultIconForTest(std::unique_ptr<ExtensionIconSet> default_icon);
231 
232  private:
233   // Populates the action from the |extension| and |manifest_data|, filling in
234   // any missing values (like title or icons) as possible.
235   void Populate(const Extension& extension, const ActionInfo& manifest_data);
236 
237   // Returns width of the current icon for tab_id.
238   // TODO(tbarzic): The icon selection is done in ExtensionActionIconFactory.
239   // We should probably move this there too.
240   int GetIconWidth(int tab_id) const;
241 
242   template <class T>
243   struct ValueTraits {
CreateEmptyValueTraits244     static T CreateEmpty() { return T(); }
245   };
246 
247   template <class T>
SetValue(std::map<int,T> * map,int tab_id,const T & val)248   void SetValue(std::map<int, T>* map, int tab_id, const T& val) {
249     (*map)[tab_id] = val;
250   }
251 
252   template <class Map>
FindOrNull(const Map * map,const typename Map::key_type & key)253   static const typename Map::mapped_type* FindOrNull(
254       const Map* map,
255       const typename Map::key_type& key) {
256     typename Map::const_iterator iter = map->find(key);
257     if (iter == map->end())
258       return NULL;
259     return &iter->second;
260   }
261 
262   template <class T>
GetValue(const std::map<int,T> * map,int tab_id)263   T GetValue(const std::map<int, T>* map, int tab_id) const {
264     if (const T* tab_value = FindOrNull(map, tab_id)) {
265       return *tab_value;
266     } else if (const T* default_value = FindOrNull(map, kDefaultTabId)) {
267       return *default_value;
268     } else {
269       return ValueTraits<T>::CreateEmpty();
270     }
271   }
272 
273   // The id for the extension this action belongs to (as defined in the
274   // extension manifest).
275   const std::string extension_id_;
276 
277   // The name of the extension.
278   const std::string extension_name_;
279 
280   const ActionInfo::Type action_type_;
281   // The default state of the action.
282   const ActionInfo::DefaultState default_state_;
283 
284   // Each of these data items can have both a global state (stored with the key
285   // kDefaultTabId), or tab-specific state (stored with the tab_id as the key).
286   std::map<int, GURL> popup_url_;
287   std::map<int, std::string> title_;
288   std::map<int, gfx::Image> icon_;
289   std::map<int, std::string> badge_text_;
290   std::map<int, SkColor> badge_background_color_;
291   std::map<int, SkColor> badge_text_color_;
292   std::map<int, bool> is_visible_;
293 
294   // Declarative state exists for two reasons: First, we need to hide it from
295   // the extension's background/event page to avoid leaking data from hosts the
296   // extension doesn't have permission to access.  Second, the action's state
297   // gets both reset and given its declarative values in response to a
298   // WebContentsObserver::DidNavigateMainFrame event, and there's no way to set
299   // those up to be called in the right order.
300 
301   // Maps tab_id to the number of active (applied-but-not-reverted)
302   // declarativeContent.ShowAction actions.
303   std::map<int, int> declarative_show_count_;
304 
305   // declarative_icon_[tab_id][declarative_rule_priority] is a vector of icon
306   // images that are currently in effect
307   std::map<int, std::map<int, std::vector<gfx::Image>>> declarative_icon_;
308 
309   // Maps tab_id to the number of actions taken based on declarative net request
310   // rule matches on incoming requests. Overrides the default |badge_text_| for
311   // this extension if it has opted into setting the action count as badge text.
312   std::map<int, int> dnr_action_count_;
313 
314   // ExtensionIconSet containing paths to bitmaps from which default icon's
315   // image representations will be selected.
316   std::unique_ptr<ExtensionIconSet> default_icon_;
317 
318   // The default icon image, if |default_icon_| exists. Set via
319   // SetDefaultIconImage(). Since IconImages depend upon BrowserContexts, we
320   // don't have the ExtensionAction load it directly to keep this class's
321   // knowledge limited.
322   std::unique_ptr<IconImage> default_icon_image_;
323 
324   // The lazily-initialized image for a placeholder icon, in the event that the
325   // extension doesn't have its own icon. (Mutable to allow lazy init in
326   // GetDefaultIconImage().)
327   mutable gfx::Image placeholder_icon_image_;
328 
329   // The id for the ExtensionAction, for example: "RssPageAction". This is
330   // needed for compat with an older version of the page actions API.
331   std::string id_;
332 
333   DISALLOW_COPY_AND_ASSIGN(ExtensionAction);
334 };
335 
336 template <>
337 struct ExtensionAction::ValueTraits<int> {
338   static int CreateEmpty() { return -1; }
339 };
340 
341 }  // namespace extensions
342 
343 #endif  // EXTENSIONS_BROWSER_EXTENSION_ACTION_H_
344