| OLD | NEW |
| (Empty) |
| 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_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/compiler_specific.h" | |
| 16 #include "base/gtest_prod_util.h" | |
| 17 #include "base/memory/scoped_ptr.h" | |
| 18 #include "base/memory/weak_ptr.h" | |
| 19 #include "base/string16.h" | |
| 20 #include "base/values.h" | |
| 21 #include "chrome/browser/extensions/extension_icon_manager.h" | |
| 22 #include "chrome/common/extensions/url_pattern_set.h" | |
| 23 #include "content/public/browser/notification_observer.h" | |
| 24 #include "content/public/browser/notification_registrar.h" | |
| 25 | |
| 26 | |
| 27 class Profile; | |
| 28 class SkBitmap; | |
| 29 | |
| 30 namespace content { | |
| 31 class WebContents; | |
| 32 struct ContextMenuParams; | |
| 33 } | |
| 34 | |
| 35 namespace extensions { | |
| 36 class Extension; | |
| 37 } | |
| 38 | |
| 39 // Represents a menu item added by an extension. | |
| 40 class ExtensionMenuItem { | |
| 41 public: | |
| 42 // A list of ExtensionMenuItem's. | |
| 43 typedef std::vector<ExtensionMenuItem*> List; | |
| 44 | |
| 45 // An Id uniquely identifies a context menu item registered by an extension. | |
| 46 struct Id { | |
| 47 Id(); | |
| 48 // Since the unique ID (uid or string_uid) is parsed from API arguments, | |
| 49 // the normal usage is to set the uid or string_uid immediately after | |
| 50 // construction. | |
| 51 Id(bool incognito, const std::string& extension_id); | |
| 52 ~Id(); | |
| 53 | |
| 54 bool operator==(const Id& other) const; | |
| 55 bool operator!=(const Id& other) const; | |
| 56 bool operator<(const Id& other) const; | |
| 57 | |
| 58 bool incognito; | |
| 59 std::string extension_id; | |
| 60 // Only one of uid or string_uid will be defined. | |
| 61 int uid; | |
| 62 std::string string_uid; | |
| 63 }; | |
| 64 | |
| 65 // For context menus, these are the contexts where an item can appear. | |
| 66 enum Context { | |
| 67 ALL = 1, | |
| 68 PAGE = 2, | |
| 69 SELECTION = 4, | |
| 70 LINK = 8, | |
| 71 EDITABLE = 16, | |
| 72 IMAGE = 32, | |
| 73 VIDEO = 64, | |
| 74 AUDIO = 128, | |
| 75 FRAME = 256, | |
| 76 }; | |
| 77 | |
| 78 // An item can be only one of these types. | |
| 79 enum Type { | |
| 80 NORMAL, | |
| 81 CHECKBOX, | |
| 82 RADIO, | |
| 83 SEPARATOR | |
| 84 }; | |
| 85 | |
| 86 // A list of Contexts for an item. | |
| 87 class ContextList { | |
| 88 public: | |
| 89 ContextList() : value_(0) {} | |
| 90 explicit ContextList(Context context) : value_(context) {} | |
| 91 ContextList(const ContextList& other) : value_(other.value_) {} | |
| 92 | |
| 93 void operator=(const ContextList& other) { | |
| 94 value_ = other.value_; | |
| 95 } | |
| 96 | |
| 97 bool operator==(const ContextList& other) const { | |
| 98 return value_ == other.value_; | |
| 99 } | |
| 100 | |
| 101 bool operator!=(const ContextList& other) const { | |
| 102 return !(*this == other); | |
| 103 } | |
| 104 | |
| 105 bool Contains(Context context) const { | |
| 106 return (value_ & context) > 0; | |
| 107 } | |
| 108 | |
| 109 void Add(Context context) { | |
| 110 value_ |= context; | |
| 111 } | |
| 112 | |
| 113 scoped_ptr<Value> ToValue() const { | |
| 114 return scoped_ptr<Value>(Value::CreateIntegerValue(value_)); | |
| 115 } | |
| 116 | |
| 117 bool Populate(const Value& value) { | |
| 118 int int_value; | |
| 119 if (!value.GetAsInteger(&int_value) || int_value < 0) | |
| 120 return false; | |
| 121 value_ = int_value; | |
| 122 return true; | |
| 123 } | |
| 124 | |
| 125 private: | |
| 126 uint32 value_; // A bitmask of Context values. | |
| 127 }; | |
| 128 | |
| 129 ExtensionMenuItem(const Id& id, | |
| 130 const std::string& title, | |
| 131 bool checked, | |
| 132 bool enabled, | |
| 133 Type type, | |
| 134 const ContextList& contexts); | |
| 135 virtual ~ExtensionMenuItem(); | |
| 136 | |
| 137 // Simple accessor methods. | |
| 138 bool incognito() const { return id_.incognito; } | |
| 139 const std::string& extension_id() const { return id_.extension_id; } | |
| 140 const std::string& title() const { return title_; } | |
| 141 const List& children() { return children_; } | |
| 142 const Id& id() const { return id_; } | |
| 143 Id* parent_id() const { return parent_id_.get(); } | |
| 144 int child_count() const { return children_.size(); } | |
| 145 ContextList contexts() const { return contexts_; } | |
| 146 Type type() const { return type_; } | |
| 147 bool checked() const { return checked_; } | |
| 148 bool enabled() const { return enabled_; } | |
| 149 const URLPatternSet& document_url_patterns() const { | |
| 150 return document_url_patterns_; | |
| 151 } | |
| 152 const URLPatternSet& target_url_patterns() const { | |
| 153 return target_url_patterns_; | |
| 154 } | |
| 155 | |
| 156 // Simple mutator methods. | |
| 157 void set_title(const std::string& new_title) { title_ = new_title; } | |
| 158 void set_contexts(ContextList contexts) { contexts_ = contexts; } | |
| 159 void set_type(Type type) { type_ = type; } | |
| 160 void set_enabled(bool enabled) { enabled_ = enabled; } | |
| 161 void set_document_url_patterns(const URLPatternSet& patterns) { | |
| 162 document_url_patterns_ = patterns; | |
| 163 } | |
| 164 void set_target_url_patterns(const URLPatternSet& patterns) { | |
| 165 target_url_patterns_ = patterns; | |
| 166 } | |
| 167 | |
| 168 // Returns the title with any instances of %s replaced by |selection|. The | |
| 169 // result will be no longer than |max_length|. | |
| 170 string16 TitleWithReplacement(const string16& selection, | |
| 171 size_t max_length) const; | |
| 172 | |
| 173 // Sets the checked state to |checked|. Returns true if successful. | |
| 174 bool SetChecked(bool checked); | |
| 175 | |
| 176 // Converts to Value for serialization to preferences. | |
| 177 scoped_ptr<base::DictionaryValue> ToValue() const; | |
| 178 | |
| 179 // Returns a new ExtensionMenuItem created from |value|, or NULL if there is | |
| 180 // an error. The caller takes ownership of the ExtensionMenuItem. | |
| 181 static ExtensionMenuItem* Populate(const std::string& extension_id, | |
| 182 const DictionaryValue& value, | |
| 183 std::string* error); | |
| 184 | |
| 185 // Sets any document and target URL patterns from |properties|. | |
| 186 bool PopulateURLPatterns(const base::DictionaryValue& properties, | |
| 187 const char* document_url_patterns_key, | |
| 188 const char* target_url_patterns_key, | |
| 189 std::string* error); | |
| 190 | |
| 191 protected: | |
| 192 friend class ExtensionMenuManager; | |
| 193 | |
| 194 // Takes ownership of |item| and sets its parent_id_. | |
| 195 void AddChild(ExtensionMenuItem* item); | |
| 196 | |
| 197 // Takes the child item from this parent. The item is returned and the caller | |
| 198 // then owns the pointer. | |
| 199 ExtensionMenuItem* ReleaseChild(const Id& child_id, bool recursive); | |
| 200 | |
| 201 // Recursively appends all descendant items (children, grandchildren, etc.) | |
| 202 // to the output |list|. | |
| 203 void GetFlattenedSubtree(ExtensionMenuItem::List* list); | |
| 204 | |
| 205 // Recursively removes all descendant items (children, grandchildren, etc.), | |
| 206 // returning the ids of the removed items. | |
| 207 std::set<Id> RemoveAllDescendants(); | |
| 208 | |
| 209 private: | |
| 210 // The unique id for this item. | |
| 211 Id id_; | |
| 212 | |
| 213 // What gets shown in the menu for this item. | |
| 214 std::string title_; | |
| 215 | |
| 216 Type type_; | |
| 217 | |
| 218 // This should only be true for items of type CHECKBOX or RADIO. | |
| 219 bool checked_; | |
| 220 | |
| 221 // If the item is enabled or not. | |
| 222 bool enabled_; | |
| 223 | |
| 224 // In what contexts should the item be shown? | |
| 225 ContextList contexts_; | |
| 226 | |
| 227 // If this item is a child of another item, the unique id of its parent. If | |
| 228 // this is a top-level item with no parent, this will be NULL. | |
| 229 scoped_ptr<Id> parent_id_; | |
| 230 | |
| 231 // Patterns for restricting what documents this item will appear for. This | |
| 232 // applies to the frame where the click took place. | |
| 233 URLPatternSet document_url_patterns_; | |
| 234 | |
| 235 // Patterns for restricting where items appear based on the src/href | |
| 236 // attribute of IMAGE/AUDIO/VIDEO/LINK tags. | |
| 237 URLPatternSet target_url_patterns_; | |
| 238 | |
| 239 // Any children this item may have. | |
| 240 List children_; | |
| 241 | |
| 242 DISALLOW_COPY_AND_ASSIGN(ExtensionMenuItem); | |
| 243 }; | |
| 244 | |
| 245 // This class keeps track of menu items added by extensions. | |
| 246 class ExtensionMenuManager | |
| 247 : public content::NotificationObserver, | |
| 248 public base::SupportsWeakPtr<ExtensionMenuManager> { | |
| 249 public: | |
| 250 explicit ExtensionMenuManager(Profile* profile); | |
| 251 virtual ~ExtensionMenuManager(); | |
| 252 | |
| 253 // Returns the ids of extensions which have menu items registered. | |
| 254 std::set<std::string> ExtensionIds(); | |
| 255 | |
| 256 // Returns a list of all the *top-level* menu items (added via AddContextItem) | |
| 257 // for the given extension id, *not* including child items (added via | |
| 258 // AddChildItem); although those can be reached via the top-level items' | |
| 259 // children. A view can then decide how to display these, including whether to | |
| 260 // put them into a submenu if there are more than 1. | |
| 261 const ExtensionMenuItem::List* MenuItems(const std::string& extension_id); | |
| 262 | |
| 263 // Adds a top-level menu item for an extension, requiring the |extension| | |
| 264 // pointer so it can load the icon for the extension. Takes ownership of | |
| 265 // |item|. Returns a boolean indicating success or failure. | |
| 266 bool AddContextItem(const extensions::Extension* extension, | |
| 267 ExtensionMenuItem* item); | |
| 268 | |
| 269 // Add an item as a child of another item which has been previously added, and | |
| 270 // takes ownership of |item|. Returns a boolean indicating success or failure. | |
| 271 bool AddChildItem(const ExtensionMenuItem::Id& parent_id, | |
| 272 ExtensionMenuItem* child); | |
| 273 | |
| 274 // Makes existing item with |child_id| a child of the item with |parent_id|. | |
| 275 // If the child item was already a child of another parent, this will remove | |
| 276 // it from that parent first. It is an error to try and move an item to be a | |
| 277 // child of one of its own descendants. It is legal to pass NULL for | |
| 278 // |parent_id|, which means the item should be moved to the top-level. | |
| 279 bool ChangeParent(const ExtensionMenuItem::Id& child_id, | |
| 280 const ExtensionMenuItem::Id* parent_id); | |
| 281 | |
| 282 // Removes a context menu item with the given id (whether it is a top-level | |
| 283 // item or a child of some other item), returning true if the item was found | |
| 284 // and removed or false otherwise. | |
| 285 bool RemoveContextMenuItem(const ExtensionMenuItem::Id& id); | |
| 286 | |
| 287 // Removes all items for the given extension id. | |
| 288 void RemoveAllContextItems(const std::string& extension_id); | |
| 289 | |
| 290 // Returns the item with the given |id| or NULL. | |
| 291 ExtensionMenuItem* GetItemById(const ExtensionMenuItem::Id& id) const; | |
| 292 | |
| 293 // Notify the ExtensionMenuManager that an item has been updated not through | |
| 294 // an explicit call into ExtensionMenuManager. For example, if an item is | |
| 295 // acquired by a call to GetItemById and changed, then this should be called. | |
| 296 // Returns true if the item was found or false otherwise. | |
| 297 bool ItemUpdated(const ExtensionMenuItem::Id& id); | |
| 298 | |
| 299 // Called when a menu item is clicked on by the user. | |
| 300 void ExecuteCommand(Profile* profile, | |
| 301 content::WebContents* web_contents, | |
| 302 const content::ContextMenuParams& params, | |
| 303 const ExtensionMenuItem::Id& menu_item_id); | |
| 304 | |
| 305 // This returns a bitmap of width/height kFaviconSize, loaded either from an | |
| 306 // entry specified in the extension's 'icon' section of the manifest, or a | |
| 307 // default extension icon. | |
| 308 const SkBitmap& GetIconForExtension(const std::string& extension_id); | |
| 309 | |
| 310 // Implements the content::NotificationObserver interface. | |
| 311 virtual void Observe(int type, const content::NotificationSource& source, | |
| 312 const content::NotificationDetails& details) OVERRIDE; | |
| 313 | |
| 314 // Stores the menu items for the extension in the state storage. | |
| 315 void WriteToStorage(const extensions::Extension* extension); | |
| 316 | |
| 317 // Reads menu items for the extension from the state storage. Any invalid | |
| 318 // items are ignored. | |
| 319 void ReadFromStorage(const std::string& extension_id, | |
| 320 scoped_ptr<base::Value> value); | |
| 321 | |
| 322 private: | |
| 323 FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, DeleteParent); | |
| 324 FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, RemoveOneByOne); | |
| 325 | |
| 326 // This is a helper function which takes care of de-selecting any other radio | |
| 327 // items in the same group (i.e. that are adjacent in the list). | |
| 328 void RadioItemSelected(ExtensionMenuItem* item); | |
| 329 | |
| 330 // Make sure that there is only one radio item selected at once in any run. | |
| 331 // If there are no radio items selected, then the first item in the run | |
| 332 // will get selected. If there are multiple radio items selected, then only | |
| 333 // the last one will get selcted. | |
| 334 void SanitizeRadioList(const ExtensionMenuItem::List& item_list); | |
| 335 | |
| 336 // Returns true if item is a descendant of an item with id |ancestor_id|. | |
| 337 bool DescendantOf(ExtensionMenuItem* item, | |
| 338 const ExtensionMenuItem::Id& ancestor_id); | |
| 339 | |
| 340 // We keep items organized by mapping an extension id to a list of items. | |
| 341 typedef std::map<std::string, ExtensionMenuItem::List> MenuItemMap; | |
| 342 MenuItemMap context_items_; | |
| 343 | |
| 344 // This lets us make lookup by id fast. It maps id to ExtensionMenuItem* for | |
| 345 // all items the menu manager knows about, including all children of top-level | |
| 346 // items. | |
| 347 std::map<ExtensionMenuItem::Id, ExtensionMenuItem*> items_by_id_; | |
| 348 | |
| 349 content::NotificationRegistrar registrar_; | |
| 350 | |
| 351 ExtensionIconManager icon_manager_; | |
| 352 | |
| 353 Profile* profile_; | |
| 354 | |
| 355 DISALLOW_COPY_AND_ASSIGN(ExtensionMenuManager); | |
| 356 }; | |
| 357 | |
| 358 #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MENU_MANAGER_H_ | |
| OLD | NEW |