• 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/bind.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "chrome/common/badge_util.h"
13 #include "chrome/common/icon_with_badge_image_source.h"
14 #include "extensions/common/constants.h"
15 #include "grit/theme_resources.h"
16 #include "grit/ui_resources.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "third_party/skia/include/core/SkCanvas.h"
19 #include "third_party/skia/include/core/SkPaint.h"
20 #include "third_party/skia/include/effects/SkGradientShader.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/gfx/animation/animation_delegate.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/color_utils.h"
25 #include "ui/gfx/image/image.h"
26 #include "ui/gfx/image/image_skia.h"
27 #include "ui/gfx/image/image_skia_source.h"
28 #include "ui/gfx/rect.h"
29 #include "ui/gfx/size.h"
30 #include "ui/gfx/skbitmap_operations.h"
31 #include "url/gurl.h"
32 
33 namespace {
34 
35 class GetAttentionImageSource : public gfx::ImageSkiaSource {
36  public:
GetAttentionImageSource(const gfx::ImageSkia & icon)37   explicit GetAttentionImageSource(const gfx::ImageSkia& icon)
38       : icon_(icon) {}
39 
40   // gfx::ImageSkiaSource overrides:
GetImageForScale(float scale)41   virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
42     gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale);
43     color_utils::HSL shift = {-1, 0, 0.5};
44     return gfx::ImageSkiaRep(
45         SkBitmapOperations::CreateHSLShiftedBitmap(icon_rep.sk_bitmap(), shift),
46         icon_rep.scale());
47   }
48 
49  private:
50   const gfx::ImageSkia icon_;
51 };
52 
53 template <class T>
HasValue(const std::map<int,T> & map,int tab_id)54 bool HasValue(const std::map<int, T>& map, int tab_id) {
55   return map.find(tab_id) != map.end();
56 }
57 
58 }  // namespace
59 
60 const int ExtensionAction::kDefaultTabId = -1;
61 const int ExtensionAction::kPageActionIconMaxSize = 19;
62 
ExtensionAction(const std::string & extension_id,extensions::ActionInfo::Type action_type,const extensions::ActionInfo & manifest_data)63 ExtensionAction::ExtensionAction(const std::string& extension_id,
64                                  extensions::ActionInfo::Type action_type,
65                                  const extensions::ActionInfo& manifest_data)
66     : extension_id_(extension_id), action_type_(action_type) {
67   // Page/script actions are hidden/disabled by default, and browser actions are
68   // visible/enabled by default.
69   SetIsVisible(kDefaultTabId,
70                action_type == extensions::ActionInfo::TYPE_BROWSER);
71   SetTitle(kDefaultTabId, manifest_data.default_title);
72   SetPopupUrl(kDefaultTabId, manifest_data.default_popup_url);
73   if (!manifest_data.default_icon.empty()) {
74     set_default_icon(make_scoped_ptr(new ExtensionIconSet(
75         manifest_data.default_icon)));
76   }
77   set_id(manifest_data.id);
78 }
79 
~ExtensionAction()80 ExtensionAction::~ExtensionAction() {
81 }
82 
CopyForTest() const83 scoped_ptr<ExtensionAction> ExtensionAction::CopyForTest() const {
84   scoped_ptr<ExtensionAction> copy(
85       new ExtensionAction(extension_id_, action_type_,
86                           extensions::ActionInfo()));
87   copy->popup_url_ = popup_url_;
88   copy->title_ = title_;
89   copy->icon_ = icon_;
90   copy->badge_text_ = badge_text_;
91   copy->badge_background_color_ = badge_background_color_;
92   copy->badge_text_color_ = badge_text_color_;
93   copy->is_visible_ = is_visible_;
94   copy->id_ = id_;
95 
96   if (default_icon_)
97     copy->default_icon_.reset(new ExtensionIconSet(*default_icon_));
98 
99   return copy.Pass();
100 }
101 
102 // static
GetIconSizeForType(extensions::ActionInfo::Type type)103 int ExtensionAction::GetIconSizeForType(
104     extensions::ActionInfo::Type type) {
105   switch (type) {
106     case extensions::ActionInfo::TYPE_BROWSER:
107     case extensions::ActionInfo::TYPE_PAGE:
108     case extensions::ActionInfo::TYPE_SYSTEM_INDICATOR:
109       // TODO(dewittj) Report the actual icon size of the system
110       // indicator.
111       return extension_misc::EXTENSION_ICON_ACTION;
112     default:
113       NOTREACHED();
114       return 0;
115   }
116 }
117 
SetPopupUrl(int tab_id,const GURL & url)118 void ExtensionAction::SetPopupUrl(int tab_id, const GURL& url) {
119   // We store |url| even if it is empty, rather than removing a URL from the
120   // map.  If an extension has a default popup, and removes it for a tab via
121   // the API, we must remember that there is no popup for that specific tab.
122   // If we removed the tab's URL, GetPopupURL would incorrectly return the
123   // default URL.
124   SetValue(&popup_url_, tab_id, url);
125 }
126 
HasPopup(int tab_id) const127 bool ExtensionAction::HasPopup(int tab_id) const {
128   return !GetPopupUrl(tab_id).is_empty();
129 }
130 
GetPopupUrl(int tab_id) const131 GURL ExtensionAction::GetPopupUrl(int tab_id) const {
132   return GetValue(&popup_url_, tab_id);
133 }
134 
SetIcon(int tab_id,const gfx::Image & image)135 void ExtensionAction::SetIcon(int tab_id, const gfx::Image& image) {
136   SetValue(&icon_, tab_id, image.AsImageSkia());
137 }
138 
GetExplicitlySetIcon(int tab_id) const139 gfx::ImageSkia ExtensionAction::GetExplicitlySetIcon(int tab_id) const {
140   return GetValue(&icon_, tab_id);
141 }
142 
SetIsVisible(int tab_id,bool new_visibility)143 bool ExtensionAction::SetIsVisible(int tab_id, bool new_visibility) {
144   const bool old_visibility = GetValue(&is_visible_, tab_id);
145 
146   if (old_visibility == new_visibility)
147     return false;
148 
149   SetValue(&is_visible_, tab_id, new_visibility);
150 
151   return true;
152 }
153 
DeclarativeShow(int tab_id)154 void ExtensionAction::DeclarativeShow(int tab_id) {
155   DCHECK_NE(tab_id, kDefaultTabId);
156   ++declarative_show_count_[tab_id];  // Use default initialization to 0.
157 }
158 
UndoDeclarativeShow(int tab_id)159 void ExtensionAction::UndoDeclarativeShow(int tab_id) {
160   int& show_count = declarative_show_count_[tab_id];
161   DCHECK_GT(show_count, 0);
162   if (--show_count == 0)
163     declarative_show_count_.erase(tab_id);
164 }
165 
ClearAllValuesForTab(int tab_id)166 void ExtensionAction::ClearAllValuesForTab(int tab_id) {
167   popup_url_.erase(tab_id);
168   title_.erase(tab_id);
169   icon_.erase(tab_id);
170   badge_text_.erase(tab_id);
171   badge_text_color_.erase(tab_id);
172   badge_background_color_.erase(tab_id);
173   is_visible_.erase(tab_id);
174   // TODO(jyasskin): Erase the element from declarative_show_count_
175   // when the tab's closed.  There's a race between the
176   // PageActionController and the ContentRulesRegistry on navigation,
177   // which prevents me from cleaning everything up now.
178 }
179 
PaintBadge(gfx::Canvas * canvas,const gfx::Rect & bounds,int tab_id)180 void ExtensionAction::PaintBadge(gfx::Canvas* canvas,
181                                  const gfx::Rect& bounds,
182                                  int tab_id) {
183   badge_util::PaintBadge(
184       canvas,
185       bounds,
186       GetBadgeText(tab_id),
187       GetBadgeTextColor(tab_id),
188       GetBadgeBackgroundColor(tab_id),
189       GetIconWidth(tab_id),
190       action_type());
191 }
192 
GetIconWithBadge(const gfx::ImageSkia & icon,int tab_id,const gfx::Size & spacing) const193 gfx::ImageSkia ExtensionAction::GetIconWithBadge(
194     const gfx::ImageSkia& icon,
195     int tab_id,
196     const gfx::Size& spacing) const {
197   if (tab_id < 0)
198     return icon;
199 
200   return gfx::ImageSkia(
201       new IconWithBadgeImageSource(icon,
202                                    icon.size(),
203                                    spacing,
204                                    GetBadgeText(tab_id),
205                                    GetBadgeTextColor(tab_id),
206                                    GetBadgeBackgroundColor(tab_id),
207                                    action_type()),
208      icon.size());
209 }
210 
HasPopupUrl(int tab_id) const211 bool ExtensionAction::HasPopupUrl(int tab_id) const {
212   return HasValue(popup_url_, tab_id);
213 }
214 
HasTitle(int tab_id) const215 bool ExtensionAction::HasTitle(int tab_id) const {
216   return HasValue(title_, tab_id);
217 }
218 
HasBadgeText(int tab_id) const219 bool ExtensionAction::HasBadgeText(int tab_id) const {
220   return HasValue(badge_text_, tab_id);
221 }
222 
HasBadgeBackgroundColor(int tab_id) const223 bool ExtensionAction::HasBadgeBackgroundColor(int tab_id) const {
224   return HasValue(badge_background_color_, tab_id);
225 }
226 
HasBadgeTextColor(int tab_id) const227 bool ExtensionAction::HasBadgeTextColor(int tab_id) const {
228   return HasValue(badge_text_color_, tab_id);
229 }
230 
HasIsVisible(int tab_id) const231 bool ExtensionAction::HasIsVisible(int tab_id) const {
232   return HasValue(is_visible_, tab_id);
233 }
234 
HasIcon(int tab_id) const235 bool ExtensionAction::HasIcon(int tab_id) const {
236   return HasValue(icon_, tab_id);
237 }
238 
239 // Determines which icon would be returned by |GetIcon|, and returns its width.
GetIconWidth(int tab_id) const240 int ExtensionAction::GetIconWidth(int tab_id) const {
241   // If icon has been set, return its width.
242   gfx::ImageSkia icon = GetValue(&icon_, tab_id);
243   if (!icon.isNull())
244     return icon.width();
245   // If there is a default icon, the icon width will be set depending on our
246   // action type.
247   if (default_icon_)
248     return GetIconSizeForType(action_type());
249 
250   // If no icon has been set and there is no default icon, we need favicon
251   // width.
252   return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
253           IDR_EXTENSIONS_FAVICON).ToImageSkia()->width();
254 }
255