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