• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "chrome/browser/extensions/extension_action.h"
6 
7 #include <algorithm>
8 
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "chrome/common/badge_util.h"
14 #include "chrome/common/icon_with_badge_image_source.h"
15 #include "extensions/common/constants.h"
16 #include "grit/theme_resources.h"
17 #include "grit/ui_resources.h"
18 #include "ipc/ipc_message.h"
19 #include "ipc/ipc_message_utils.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "third_party/skia/include/core/SkCanvas.h"
22 #include "third_party/skia/include/core/SkPaint.h"
23 #include "third_party/skia/include/effects/SkGradientShader.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/animation/animation_delegate.h"
26 #include "ui/gfx/canvas.h"
27 #include "ui/gfx/color_utils.h"
28 #include "ui/gfx/image/image.h"
29 #include "ui/gfx/image/image_skia.h"
30 #include "ui/gfx/image/image_skia_source.h"
31 #include "ui/gfx/ipc/gfx_param_traits.h"
32 #include "ui/gfx/rect.h"
33 #include "ui/gfx/size.h"
34 #include "ui/gfx/skbitmap_operations.h"
35 #include "url/gurl.h"
36 
37 namespace {
38 
39 class GetAttentionImageSource : public gfx::ImageSkiaSource {
40  public:
GetAttentionImageSource(const gfx::ImageSkia & icon)41   explicit GetAttentionImageSource(const gfx::ImageSkia& icon)
42       : icon_(icon) {}
43 
44   // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)45   virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
46     gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale);
47     color_utils::HSL shift = {-1, 0, 0.5};
48     return gfx::ImageSkiaRep(
49         SkBitmapOperations::CreateHSLShiftedBitmap(icon_rep.sk_bitmap(), shift),
50         icon_rep.scale());
51   }
52 
53  private:
54   const gfx::ImageSkia icon_;
55 };
56 
57 struct IconRepresentationInfo {
58   // Size as a string that will be used to retrieve a representation value from
59   // SetIcon function arguments.
60   const char* size_string;
61   // Scale factor for which the represantion should be used.
62   ui::ScaleFactor scale;
63 };
64 
65 const IconRepresentationInfo kIconSizes[] = {{"19", ui::SCALE_FACTOR_100P},
66                                              {"38", ui::SCALE_FACTOR_200P}};
67 
68 template <class T>
HasValue(const std::map<int,T> & map,int tab_id)69 bool HasValue(const std::map<int, T>& map, int tab_id) {
70   return map.find(tab_id) != map.end();
71 }
72 
73 }  // namespace
74 
75 const int ExtensionAction::kDefaultTabId = -1;
76 const int ExtensionAction::kPageActionIconMaxSize =
77     extension_misc::EXTENSION_ICON_ACTION;
78 
ExtensionAction(const std::string & extension_id,extensions::ActionInfo::Type action_type,const extensions::ActionInfo & manifest_data)79 ExtensionAction::ExtensionAction(const std::string& extension_id,
80                                  extensions::ActionInfo::Type action_type,
81                                  const extensions::ActionInfo& manifest_data)
82     : extension_id_(extension_id), action_type_(action_type) {
83   // Page/script actions are hidden/disabled by default, and browser actions are
84   // visible/enabled by default.
85   SetIsVisible(kDefaultTabId,
86                action_type == extensions::ActionInfo::TYPE_BROWSER);
87   SetTitle(kDefaultTabId, manifest_data.default_title);
88   SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
89   if (!manifest_data.default_icon.empty()) {
90     set_default_icon(make_scoped_ptr(new ExtensionIconSet(
91         manifest_data.default_icon)));
92   }
93   set_id(manifest_data.id);
94 }
95 
~ExtensionAction()96 ExtensionAction::~ExtensionAction() {
97 }
98 
CopyForTest() const99 scoped_ptr<ExtensionAction> ExtensionAction::CopyForTest() const {
100   scoped_ptr<ExtensionAction> copy(
101       new ExtensionAction(extension_id_, action_type_,
102                           extensions::ActionInfo()));
103   copy->popup_url_ = popup_url_;
104   copy->title_ = title_;
105   copy->icon_ = icon_;
106   copy->badge_text_ = badge_text_;
107   copy->badge_background_color_ = badge_background_color_;
108   copy->badge_text_color_ = badge_text_color_;
109   copy->is_visible_ = is_visible_;
110   copy->id_ = id_;
111 
112   if (default_icon_)
113     copy->default_icon_.reset(new ExtensionIconSet(*default_icon_));
114 
115   return copy.Pass();
116 }
117 
118 // static
GetIconSizeForType(extensions::ActionInfo::Type type)119 int ExtensionAction::GetIconSizeForType(
120     extensions::ActionInfo::Type type) {
121   switch (type) {
122     case extensions::ActionInfo::TYPE_BROWSER:
123     case extensions::ActionInfo::TYPE_PAGE:
124     case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR:
125       // TODO(dewittj) Report the actual icon size of the system
126       // indicator.
127       return extension_misc::EXTENSION_ICON_ACTION;
128     default:
129       NOTREACHED();
130       return 0;
131   }
132 }
133 
SetPopupUrl(int tab_id,const GURL & url)134 void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) {
135   // We store |url| even if it is empty, rather than removing a URL from the
136   // map.  If an extension has a default popup, and removes it for a tab via
137   // the API, we must remember that there is no popup for that specific tab.
138   // If we removed the tab's URL, GetPopupURL would incorrectly return the
139   // default URL.
140   SetValue(&popup_url_, tab_id, url);
141 }
142 
HasPopup(int tab_id) const143 bool ExtensionAction::HasPopup(int tab_id) const {
144   return !GetPopupUrl(tab_id).is_empty();
145 }
146 
GetPopupUrl(int tab_id) const147 GURL ExtensionAction::GetPopupUrl(int tab_id) const {
148   return GetValue(&popup_url_, tab_id);
149 }
150 
SetIcon(int tab_id,const gfx::Image & image)151 void ExtensionAction::SetIcon(int tab_id, const gfx::Image& image) {
152   SetValue(&icon_, tab_id, image.AsImageSkia());
153 }
154 
ParseIconFromCanvasDictionary(const base::DictionaryValue & dict,gfx::ImageSkia * icon)155 bool ExtensionAction::ParseIconFromCanvasDictionary(
156     const base::DictionaryValue& dict,
157     gfx::ImageSkia* icon) {
158   // Try to extract an icon for each known scale.
159   for (size_t i = 0; i < arraysize(kIconSizes); i++) {
160     const base::BinaryValue* image_data;
161     std::string binary_string64;
162     IPC::Message pickle;
163     if (dict.GetBinary(kIconSizes[i].size_string, &image_data)) {
164       pickle = IPC::Message(image_data->GetBuffer(), image_data->GetSize());
165     } else if (dict.GetString(kIconSizes[i].size_string, &binary_string64)) {
166       std::string binary_string;
167       if (!base::Base64Decode(binary_string64, &binary_string))
168         return false;
169       pickle = IPC::Message(binary_string.c_str(), binary_string.length());
170     } else {
171       continue;
172     }
173     PickleIterator iter(pickle);
174     SkBitmap bitmap;
175     if (!IPC::ReadParam(&pickle, &iter, &bitmap))
176       return false;
177     CHECK(!bitmap.isNull());
178     float scale = ui::GetScaleForScaleFactor(kIconSizes[i].scale);
179     icon->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
180   }
181   return true;
182 }
183 
GetExplicitlySetIcon(int tab_id) const184 gfx::ImageSkia ExtensionAction::GetExplicitlySetIcon(int tab_id) const {
185   return GetValue(&icon_, tab_id);
186 }
187 
SetIsVisible(int tab_id,bool new_visibility)188 bool ExtensionAction::SetIsVisible(int tab_id, bool new_visibility) {
189   const bool old_visibility = GetValue(&is_visible_, tab_id);
190 
191   if (old_visibility == new_visibility)
192     return false;
193 
194   SetValue(&is_visible_, tab_id, new_visibility);
195 
196   return true;
197 }
198 
DeclarativeShow(int tab_id)199 void ExtensionAction::DeclarativeShow(int tab_id) {
200   DCHECK_NE(tab_id, kDefaultTabId);
201   ++declarative_show_count_[tab_id];  // Use default initialization to 0.
202 }
203 
UndoDeclarativeShow(int tab_id)204 void ExtensionAction::UndoDeclarativeShow(int tab_id) {
205   int& show_count = declarative_show_count_[tab_id];
206   DCHECK_GT(show_count, 0);
207   if (--show_count == 0)
208     declarative_show_count_.erase(tab_id);
209 }
210 
DeclarativeSetIcon(int tab_id,int priority,const gfx::Image & icon)211 void ExtensionAction::DeclarativeSetIcon(int tab_id,
212                                          int priority,
213                                          const gfx::Image& icon) {
214   DCHECK_NE(tab_id, kDefaultTabId);
215   declarative_icon_[tab_id][priority].push_back(icon);
216 }
217 
UndoDeclarativeSetIcon(int tab_id,int priority,const gfx::Image & icon)218 void ExtensionAction::UndoDeclarativeSetIcon(int tab_id,
219                                              int priority,
220                                              const gfx::Image& icon) {
221   std::vector<gfx::Image>& icons = declarative_icon_[tab_id][priority];
222   for (std::vector<gfx::Image>::iterator it = icons.begin(); it != icons.end();
223        ++it) {
224     if (it->AsImageSkia().BackedBySameObjectAs(icon.AsImageSkia())) {
225       icons.erase(it);
226       return;
227     }
228   }
229 }
230 
GetDeclarativeIcon(int tab_id) const231 const gfx::ImageSkia ExtensionAction::GetDeclarativeIcon(int tab_id) const {
232   if (declarative_icon_.find(tab_id) != declarative_icon_.end() &&
233       !declarative_icon_.find(tab_id)->second.rbegin()->second.empty()) {
234     return declarative_icon_.find(tab_id)->second.rbegin()
235         ->second.back().AsImageSkia();
236   }
237   return gfx::ImageSkia();
238 }
239 
ClearAllValuesForTab(int tab_id)240 void ExtensionAction::ClearAllValuesForTab(int tab_id) {
241   popup_url_.erase(tab_id);
242   title_.erase(tab_id);
243   icon_.erase(tab_id);
244   badge_text_.erase(tab_id);
245   badge_text_color_.erase(tab_id);
246   badge_background_color_.erase(tab_id);
247   is_visible_.erase(tab_id);
248   // TODO(jyasskin): Erase the element from declarative_show_count_
249   // when the tab's closed.  There's a race between the
250   // LocationBarController and the ContentRulesRegistry on navigation,
251   // which prevents me from cleaning everything up now.
252 }
253 
PaintBadge(gfx::Canvas * canvas,const gfx::Rect & bounds,int tab_id)254 void ExtensionAction::PaintBadge(gfx::Canvas* canvas,
255                                  const gfx::Rect& bounds,
256                                  int tab_id) {
257   badge_util::PaintBadge(
258       canvas,
259       bounds,
260       GetBadgeText(tab_id),
261       GetBadgeTextColor(tab_id),
262       GetBadgeBackgroundColor(tab_id),
263       GetIconWidth(tab_id),
264       action_type());
265 }
266 
GetIconWithBadge(const gfx::ImageSkia & icon,int tab_id,const gfx::Size & spacing) const267 gfx::ImageSkia ExtensionAction::GetIconWithBadge(
268     const gfx::ImageSkia& icon,
269     int tab_id,
270     const gfx::Size& spacing) const {
271   if (tab_id < 0)
272     return icon;
273 
274   return gfx::ImageSkia(
275       new IconWithBadgeImageSource(icon,
276                                    icon.size(),
277                                    spacing,
278                                    GetBadgeText(tab_id),
279                                    GetBadgeTextColor(tab_id),
280                                    GetBadgeBackgroundColor(tab_id),
281                                    action_type()),
282      icon.size());
283 }
284 
HasPopupUrl(int tab_id) const285 bool ExtensionAction::HasPopupUrl(int tab_id) const {
286   return HasValue(popup_url_, tab_id);
287 }
288 
HasTitle(int tab_id) const289 bool ExtensionAction::HasTitle(int tab_id) const {
290   return HasValue(title_, tab_id);
291 }
292 
HasBadgeText(int tab_id) const293 bool ExtensionAction::HasBadgeText(int tab_id) const {
294   return HasValue(badge_text_, tab_id);
295 }
296 
HasBadgeBackgroundColor(int tab_id) const297 bool ExtensionAction::HasBadgeBackgroundColor(int tab_id) const {
298   return HasValue(badge_background_color_, tab_id);
299 }
300 
HasBadgeTextColor(int tab_id) const301 bool ExtensionAction::HasBadgeTextColor(int tab_id) const {
302   return HasValue(badge_text_color_, tab_id);
303 }
304 
HasIsVisible(int tab_id) const305 bool ExtensionAction::HasIsVisible(int tab_id) const {
306   return HasValue(is_visible_, tab_id);
307 }
308 
HasIcon(int tab_id) const309 bool ExtensionAction::HasIcon(int tab_id) const {
310   return HasValue(icon_, tab_id);
311 }
312 
313 // Determines which icon would be returned by |GetIcon|, and returns its width.
GetIconWidth(int tab_id) const314 int ExtensionAction::GetIconWidth(int tab_id) const {
315   // If icon has been set, return its width.
316   gfx::ImageSkia icon = GetValue(&icon_, tab_id);
317   if (!icon.isNull())
318     return icon.width();
319   // If there is a default icon, the icon width will be set depending on our
320   // action type.
321   if (default_icon_)
322     return GetIconSizeForType(action_type());
323 
324   // If no icon has been set and there is no default icon, we need favicon
325   // width.
326   return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
327           IDR_EXTENSIONS_FAVICON).ToImageSkia()->width();
328 }
329