1 // Copyright (c) 2011 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_MENU_MANAGER_H_ 6 #define CHROME_BROWSER_EXTENSIONS_EXTENSION_MENU_MANAGER_H_ 7 #pragma once 8 9 #include <map> 10 #include <set> 11 #include <string> 12 #include <vector> 13 14 #include "base/basictypes.h" 15 #include "base/gtest_prod_util.h" 16 #include "base/memory/scoped_ptr.h" 17 #include "base/string16.h" 18 #include "chrome/browser/extensions/extension_icon_manager.h" 19 #include "chrome/common/extensions/extension_extent.h" 20 #include "content/common/notification_observer.h" 21 #include "content/common/notification_registrar.h" 22 23 struct ContextMenuParams; 24 25 class Extension; 26 class Profile; 27 class SkBitmap; 28 class TabContents; 29 30 // Represents a menu item added by an extension. 31 class ExtensionMenuItem { 32 public: 33 // A list of ExtensionMenuItem's. 34 typedef std::vector<ExtensionMenuItem*> List; 35 36 // An Id uniquely identifies a context menu item registered by an extension. 37 struct Id { 38 Id(); 39 Id(Profile* profile, const std::string& extension_id, int uid); 40 ~Id(); 41 42 bool operator==(const Id& other) const; 43 bool operator!=(const Id& other) const; 44 bool operator<(const Id& other) const; 45 46 Profile* profile; 47 std::string extension_id; 48 int uid; 49 }; 50 51 // For context menus, these are the contexts where an item can appear. 52 enum Context { 53 ALL = 1, 54 PAGE = 2, 55 SELECTION = 4, 56 LINK = 8, 57 EDITABLE = 16, 58 IMAGE = 32, 59 VIDEO = 64, 60 AUDIO = 128, 61 FRAME = 256, 62 }; 63 64 // An item can be only one of these types. 65 enum Type { 66 NORMAL, 67 CHECKBOX, 68 RADIO, 69 SEPARATOR 70 }; 71 72 // A list of Contexts for an item. 73 class ContextList { 74 public: 75 ContextList() : value_(0) {} 76 explicit ContextList(Context context) : value_(context) {} 77 ContextList(const ContextList& other) : value_(other.value_) {} 78 79 void operator=(const ContextList& other) { 80 value_ = other.value_; 81 } 82 83 bool operator==(const ContextList& other) const { 84 return value_ == other.value_; 85 } 86 87 bool operator!=(const ContextList& other) const { 88 return !(*this == other); 89 } 90 91 bool Contains(Context context) const { 92 return (value_ & context) > 0; 93 } 94 95 void Add(Context context) { 96 value_ |= context; 97 } 98 99 private: 100 uint32 value_; // A bitmask of Context values. 101 }; 102 103 ExtensionMenuItem(const Id& id, 104 const std::string& title, 105 bool checked, 106 Type type, 107 const ContextList& contexts); 108 virtual ~ExtensionMenuItem(); 109 110 // Simple accessor methods. 111 const std::string& extension_id() const { return id_.extension_id; } 112 const std::string& title() const { return title_; } 113 const List& children() { return children_; } 114 const Id& id() const { return id_; } 115 Id* parent_id() const { return parent_id_.get(); } 116 int child_count() const { return children_.size(); } 117 ContextList contexts() const { return contexts_; } 118 Type type() const { return type_; } 119 bool checked() const { return checked_; } 120 const ExtensionExtent& document_url_patterns() const { 121 return document_url_patterns_; 122 } 123 const ExtensionExtent& target_url_patterns() const { 124 return target_url_patterns_; 125 } 126 127 // Simple mutator methods. 128 void set_title(const std::string& new_title) { title_ = new_title; } 129 void set_contexts(ContextList contexts) { contexts_ = contexts; } 130 void set_type(Type type) { type_ = type; } 131 void set_document_url_patterns(const ExtensionExtent& patterns) { 132 document_url_patterns_ = patterns; 133 } 134 void set_target_url_patterns(const ExtensionExtent& patterns) { 135 target_url_patterns_ = patterns; 136 } 137 138 // Returns the title with any instances of %s replaced by |selection|. The 139 // result will be no longer than |max_length|. 140 string16 TitleWithReplacement(const string16& selection, 141 size_t max_length) const; 142 143 // Set the checked state to |checked|. Returns true if successful. 144 bool SetChecked(bool checked); 145 146 protected: 147 friend class ExtensionMenuManager; 148 149 // Takes ownership of |item| and sets its parent_id_. 150 void AddChild(ExtensionMenuItem* item); 151 152 // Takes the child item from this parent. The item is returned and the caller 153 // then owns the pointer. 154 ExtensionMenuItem* ReleaseChild(const Id& child_id, bool recursive); 155 156 // Recursively removes all descendant items (children, grandchildren, etc.), 157 // returning the ids of the removed items. 158 std::set<Id> RemoveAllDescendants(); 159 160 private: 161 // The unique id for this item. 162 Id id_; 163 164 // What gets shown in the menu for this item. 165 std::string title_; 166 167 Type type_; 168 169 // This should only be true for items of type CHECKBOX or RADIO. 170 bool checked_; 171 172 // In what contexts should the item be shown? 173 ContextList contexts_; 174 175 // If this item is a child of another item, the unique id of its parent. If 176 // this is a top-level item with no parent, this will be NULL. 177 scoped_ptr<Id> parent_id_; 178 179 // Patterns for restricting what documents this item will appear for. This 180 // applies to the frame where the click took place. 181 ExtensionExtent document_url_patterns_; 182 183 // Patterns for restricting where items appear based on the src/href 184 // attribute of IMAGE/AUDIO/VIDEO/LINK tags. 185 ExtensionExtent target_url_patterns_; 186 187 // Any children this item may have. 188 List children_; 189 190 DISALLOW_COPY_AND_ASSIGN(ExtensionMenuItem); 191 }; 192 193 // This class keeps track of menu items added by extensions. 194 class ExtensionMenuManager : public NotificationObserver { 195 public: 196 // A bitmask of values from URLPattern::SchemeMasks indicating the schemes 197 // of pages where we'll show extension menu items. 198 static const int kAllowedSchemes; 199 200 ExtensionMenuManager(); 201 virtual ~ExtensionMenuManager(); 202 203 // Returns the ids of extensions which have menu items registered. 204 std::set<std::string> ExtensionIds(); 205 206 // Returns a list of all the *top-level* menu items (added via AddContextItem) 207 // for the given extension id, *not* including child items (added via 208 // AddChildItem); although those can be reached via the top-level items' 209 // children. A view can then decide how to display these, including whether to 210 // put them into a submenu if there are more than 1. 211 const ExtensionMenuItem::List* MenuItems(const std::string& extension_id); 212 213 // Adds a top-level menu item for an extension, requiring the |extension| 214 // pointer so it can load the icon for the extension. Takes ownership of 215 // |item|. Returns a boolean indicating success or failure. 216 bool AddContextItem(const Extension* extension, ExtensionMenuItem* item); 217 218 // Add an item as a child of another item which has been previously added, and 219 // takes ownership of |item|. Returns a boolean indicating success or failure. 220 bool AddChildItem(const ExtensionMenuItem::Id& parent_id, 221 ExtensionMenuItem* child); 222 223 // Makes existing item with |child_id| a child of the item with |parent_id|. 224 // If the child item was already a child of another parent, this will remove 225 // it from that parent first. It is an error to try and move an item to be a 226 // child of one of its own descendants. It is legal to pass NULL for 227 // |parent_id|, which means the item should be moved to the top-level. 228 bool ChangeParent(const ExtensionMenuItem::Id& child_id, 229 const ExtensionMenuItem::Id* parent_id); 230 231 // Removes a context menu item with the given id (whether it is a top-level 232 // item or a child of some other item), returning true if the item was found 233 // and removed or false otherwise. 234 bool RemoveContextMenuItem(const ExtensionMenuItem::Id& id); 235 236 // Removes all items for the given extension id. 237 void RemoveAllContextItems(const std::string& extension_id); 238 239 // Returns the item with the given |id| or NULL. 240 ExtensionMenuItem* GetItemById(const ExtensionMenuItem::Id& id) const; 241 242 // Called when a menu item is clicked on by the user. 243 void ExecuteCommand(Profile* profile, TabContents* tab_contents, 244 const ContextMenuParams& params, 245 const ExtensionMenuItem::Id& menuItemId); 246 247 // This returns a bitmap of width/height kFaviconSize, loaded either from an 248 // entry specified in the extension's 'icon' section of the manifest, or a 249 // default extension icon. 250 const SkBitmap& GetIconForExtension(const std::string& extension_id); 251 252 // Implements the NotificationObserver interface. 253 virtual void Observe(NotificationType type, const NotificationSource& source, 254 const NotificationDetails& details); 255 256 // Returns true if |url| has an allowed scheme for extension context menu 257 // items. This checks against kAllowedSchemes. 258 static bool HasAllowedScheme(const GURL& url); 259 260 private: 261 FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, DeleteParent); 262 FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, RemoveOneByOne); 263 264 // This is a helper function which takes care of de-selecting any other radio 265 // items in the same group (i.e. that are adjacent in the list). 266 void RadioItemSelected(ExtensionMenuItem* item); 267 268 // Returns true if item is a descendant of an item with id |ancestor_id|. 269 bool DescendantOf(ExtensionMenuItem* item, 270 const ExtensionMenuItem::Id& ancestor_id); 271 272 // We keep items organized by mapping an extension id to a list of items. 273 typedef std::map<std::string, ExtensionMenuItem::List> MenuItemMap; 274 MenuItemMap context_items_; 275 276 // This lets us make lookup by id fast. It maps id to ExtensionMenuItem* for 277 // all items the menu manager knows about, including all children of top-level 278 // items. 279 std::map<ExtensionMenuItem::Id, ExtensionMenuItem*> items_by_id_; 280 281 NotificationRegistrar registrar_; 282 283 ExtensionIconManager icon_manager_; 284 285 DISALLOW_COPY_AND_ASSIGN(ExtensionMenuManager); 286 }; 287 288 #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MENU_MANAGER_H_ 289