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