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.
5 #ifndef CHROME_BROWSER_EXTENSIONS_MENU_MANAGER_H_
6 #define CHROME_BROWSER_EXTENSIONS_MENU_MANAGER_H_
13 #include "base/basictypes.h"
14 #include "base/compiler_specific.h"
15 #include "base/gtest_prod_util.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/scoped_observer.h"
19 #include "base/strings/string16.h"
20 #include "base/values.h"
21 #include "chrome/browser/extensions/extension_icon_manager.h"
22 #include "components/keyed_service/core/keyed_service.h"
23 #include "content/public/browser/notification_observer.h"
24 #include "content/public/browser/notification_registrar.h"
25 #include "extensions/browser/extension_registry_observer.h"
26 #include "extensions/common/url_pattern_set.h"
33 struct ContextMenuParams
;
36 namespace extensions
{
38 class ExtensionRegistry
;
41 // Represents a menu item added by an extension.
44 // A list of MenuItems.
45 typedef std::vector
<MenuItem
*> List
;
47 // Key used to identify which extension a menu item belongs to. A menu item
48 // can also belong to a <webview>, in which case |webview_embedder_process_id|
49 // and |webview_instance_id| will be non-zero. When two ExtensionKeys are
50 // compared, an empty |extension_id| will match any other extension ID. This
51 // allows menu items belonging to webviews to be found with only the two
52 // webview IDs when the extension ID is not known. This is currently done from
53 // ChromeExtensionsBrowserClient::CleanUpWebView().
55 std::string extension_id
;
56 int webview_embedder_process_id
;
57 int webview_instance_id
;
60 explicit ExtensionKey(const std::string
& extension_id
);
61 ExtensionKey(const std::string
& extension_id
,
62 int webview_embedder_process_id
,
63 int webview_instance_id
);
65 bool operator==(const ExtensionKey
& other
) const;
66 bool operator!=(const ExtensionKey
& other
) const;
67 bool operator<(const ExtensionKey
& other
) const;
72 // An Id uniquely identifies a context menu item registered by an extension.
75 // Since the unique ID (uid or string_uid) is parsed from API arguments,
76 // the normal usage is to set the uid or string_uid immediately after
78 Id(bool incognito
, const ExtensionKey
& extension_key
);
81 bool operator==(const Id
& other
) const;
82 bool operator!=(const Id
& other
) const;
83 bool operator<(const Id
& other
) const;
86 ExtensionKey extension_key
;
87 // Only one of uid or string_uid will be defined.
89 std::string string_uid
;
92 // For context menus, these are the contexts where an item can appear.
104 BROWSER_ACTION
= 1024,
108 // An item can be only one of these types.
116 // A list of Contexts for an item.
119 ContextList() : value_(0) {}
120 explicit ContextList(Context context
) : value_(context
) {}
121 ContextList(const ContextList
& other
) : value_(other
.value_
) {}
123 void operator=(const ContextList
& other
) {
124 value_
= other
.value_
;
127 bool operator==(const ContextList
& other
) const {
128 return value_
== other
.value_
;
131 bool operator!=(const ContextList
& other
) const {
132 return !(*this == other
);
135 bool Contains(Context context
) const {
136 return (value_
& context
) > 0;
139 void Add(Context context
) {
143 scoped_ptr
<base::Value
> ToValue() const {
144 return scoped_ptr
<base::Value
>(
145 new base::FundamentalValue(static_cast<int>(value_
)));
148 bool Populate(const base::Value
& value
) {
150 if (!value
.GetAsInteger(&int_value
) || int_value
< 0)
157 uint32 value_
; // A bitmask of Context values.
160 MenuItem(const Id
& id
,
161 const std::string
& title
,
165 const ContextList
& contexts
);
168 // Simple accessor methods.
169 bool incognito() const { return id_
.incognito
; }
170 const std::string
& extension_id() const {
171 return id_
.extension_key
.extension_id
;
173 const std::string
& title() const { return title_
; }
174 const List
& children() { return children_
; }
175 const Id
& id() const { return id_
; }
176 Id
* parent_id() const { return parent_id_
.get(); }
177 int child_count() const { return children_
.size(); }
178 const ContextList
& contexts() const { return contexts_
; }
179 Type
type() const { return type_
; }
180 bool checked() const { return checked_
; }
181 bool enabled() const { return enabled_
; }
182 const URLPatternSet
& document_url_patterns() const {
183 return document_url_patterns_
;
185 const URLPatternSet
& target_url_patterns() const {
186 return target_url_patterns_
;
189 // Simple mutator methods.
190 void set_title(const std::string
& new_title
) { title_
= new_title
; }
191 void set_contexts(ContextList contexts
) { contexts_
= contexts
; }
192 void set_type(Type type
) { type_
= type
; }
193 void set_enabled(bool enabled
) { enabled_
= enabled
; }
194 void set_document_url_patterns(const URLPatternSet
& patterns
) {
195 document_url_patterns_
= patterns
;
197 void set_target_url_patterns(const URLPatternSet
& patterns
) {
198 target_url_patterns_
= patterns
;
201 // Returns the title with any instances of %s replaced by |selection|. The
202 // result will be no longer than |max_length|.
203 base::string16
TitleWithReplacement(const base::string16
& selection
,
204 size_t max_length
) const;
206 // Sets the checked state to |checked|. Returns true if successful.
207 bool SetChecked(bool checked
);
209 // Converts to Value for serialization to preferences.
210 scoped_ptr
<base::DictionaryValue
> ToValue() const;
212 // Returns a new MenuItem created from |value|, or NULL if there is
213 // an error. The caller takes ownership of the MenuItem.
214 static MenuItem
* Populate(const std::string
& extension_id
,
215 const base::DictionaryValue
& value
,
218 // Sets any document and target URL patterns from |properties|.
219 bool PopulateURLPatterns(std::vector
<std::string
>* document_url_patterns
,
220 std::vector
<std::string
>* target_url_patterns
,
224 friend class MenuManager
;
226 // Takes ownership of |item| and sets its parent_id_.
227 void AddChild(MenuItem
* item
);
229 // Takes the child item from this parent. The item is returned and the caller
230 // then owns the pointer.
231 MenuItem
* ReleaseChild(const Id
& child_id
, bool recursive
);
233 // Recursively appends all descendant items (children, grandchildren, etc.)
234 // to the output |list|.
235 void GetFlattenedSubtree(MenuItem::List
* list
);
237 // Recursively removes all descendant items (children, grandchildren, etc.),
238 // returning the ids of the removed items.
239 std::set
<Id
> RemoveAllDescendants();
242 // The unique id for this item.
245 // What gets shown in the menu for this item.
250 // This should only be true for items of type CHECKBOX or RADIO.
253 // If the item is enabled or not.
256 // In what contexts should the item be shown?
257 ContextList contexts_
;
259 // If this item is a child of another item, the unique id of its parent. If
260 // this is a top-level item with no parent, this will be NULL.
261 scoped_ptr
<Id
> parent_id_
;
263 // Patterns for restricting what documents this item will appear for. This
264 // applies to the frame where the click took place.
265 URLPatternSet document_url_patterns_
;
267 // Patterns for restricting where items appear based on the src/href
268 // attribute of IMAGE/AUDIO/VIDEO/LINK tags.
269 URLPatternSet target_url_patterns_
;
271 // Any children this item may have.
274 DISALLOW_COPY_AND_ASSIGN(MenuItem
);
277 // This class keeps track of menu items added by extensions.
278 class MenuManager
: public content::NotificationObserver
,
279 public base::SupportsWeakPtr
<MenuManager
>,
281 public ExtensionRegistryObserver
{
283 static const char kOnContextMenus
[];
284 static const char kOnWebviewContextMenus
[];
286 MenuManager(content::BrowserContext
* context
, StateStore
* store_
);
287 ~MenuManager() override
;
289 // Convenience function to get the MenuManager for a browser context.
290 static MenuManager
* Get(content::BrowserContext
* context
);
292 // Returns the keys of extensions which have menu items registered.
293 std::set
<MenuItem::ExtensionKey
> ExtensionIds();
295 // Returns a list of all the *top-level* menu items (added via AddContextItem)
296 // for the given extension specified by |extension_key|, *not* including child
297 // items (added via AddChildItem); although those can be reached via the
298 // top-level items' children. A view can then decide how to display these,
299 // including whether to put them into a submenu if there are more than 1.
300 const MenuItem::List
* MenuItems(const MenuItem::ExtensionKey
& extension_key
);
302 // Adds a top-level menu item for an extension, requiring the |extension|
303 // pointer so it can load the icon for the extension. Takes ownership of
304 // |item|. Returns a boolean indicating success or failure.
305 bool AddContextItem(const Extension
* extension
, MenuItem
* item
);
307 // Add an item as a child of another item which has been previously added, and
308 // takes ownership of |item|. Returns a boolean indicating success or failure.
309 bool AddChildItem(const MenuItem::Id
& parent_id
,
312 // Makes existing item with |child_id| a child of the item with |parent_id|.
313 // If the child item was already a child of another parent, this will remove
314 // it from that parent first. It is an error to try and move an item to be a
315 // child of one of its own descendants. It is legal to pass NULL for
316 // |parent_id|, which means the item should be moved to the top-level.
317 bool ChangeParent(const MenuItem::Id
& child_id
,
318 const MenuItem::Id
* parent_id
);
320 // Removes a context menu item with the given id (whether it is a top-level
321 // item or a child of some other item), returning true if the item was found
322 // and removed or false otherwise.
323 bool RemoveContextMenuItem(const MenuItem::Id
& id
);
325 // Removes all items for the given extension specified by |extension_key|.
326 void RemoveAllContextItems(const MenuItem::ExtensionKey
& extension_key
);
328 // Returns the item with the given |id| or NULL.
329 MenuItem
* GetItemById(const MenuItem::Id
& id
) const;
331 // Notify the MenuManager that an item has been updated not through
332 // an explicit call into MenuManager. For example, if an item is
333 // acquired by a call to GetItemById and changed, then this should be called.
334 // Returns true if the item was found or false otherwise.
335 bool ItemUpdated(const MenuItem::Id
& id
);
337 // Called when a menu item is clicked on by the user.
338 void ExecuteCommand(content::BrowserContext
* context
,
339 content::WebContents
* web_contents
,
340 const content::ContextMenuParams
& params
,
341 const MenuItem::Id
& menu_item_id
);
343 // This returns a bitmap of width/height kFaviconSize, loaded either from an
344 // entry specified in the extension's 'icon' section of the manifest, or a
345 // default extension icon.
346 const SkBitmap
& GetIconForExtension(const std::string
& extension_id
);
348 // content::NotificationObserver implementation.
349 void Observe(int type
,
350 const content::NotificationSource
& source
,
351 const content::NotificationDetails
& details
) override
;
353 // ExtensionRegistryObserver implementation.
354 void OnExtensionLoaded(content::BrowserContext
* browser_context
,
355 const Extension
* extension
) override
;
356 void OnExtensionUnloaded(content::BrowserContext
* browser_context
,
357 const Extension
* extension
,
358 UnloadedExtensionInfo::Reason reason
) override
;
360 // Stores the menu items for the extension in the state storage.
361 void WriteToStorage(const Extension
* extension
,
362 const MenuItem::ExtensionKey
& extension_key
);
364 // Reads menu items for the extension from the state storage. Any invalid
365 // items are ignored.
366 void ReadFromStorage(const std::string
& extension_id
,
367 scoped_ptr
<base::Value
> value
);
369 // Removes all "incognito" "split" mode context items.
370 void RemoveAllIncognitoContextItems();
373 FRIEND_TEST_ALL_PREFIXES(MenuManagerTest
, DeleteParent
);
374 FRIEND_TEST_ALL_PREFIXES(MenuManagerTest
, RemoveOneByOne
);
376 // This is a helper function which takes care of de-selecting any other radio
377 // items in the same group (i.e. that are adjacent in the list).
378 void RadioItemSelected(MenuItem
* item
);
380 // Make sure that there is only one radio item selected at once in any run.
381 // If there are no radio items selected, then the first item in the run
382 // will get selected. If there are multiple radio items selected, then only
383 // the last one will get selcted.
384 void SanitizeRadioList(const MenuItem::List
& item_list
);
386 // Returns true if item is a descendant of an item with id |ancestor_id|.
387 bool DescendantOf(MenuItem
* item
, const MenuItem::Id
& ancestor_id
);
389 // We keep items organized by mapping ExtensionKey to a list of items.
390 typedef std::map
<MenuItem::ExtensionKey
, MenuItem::List
> MenuItemMap
;
391 MenuItemMap context_items_
;
393 // This lets us make lookup by id fast. It maps id to MenuItem* for
394 // all items the menu manager knows about, including all children of top-level
396 std::map
<MenuItem::Id
, MenuItem
*> items_by_id_
;
398 content::NotificationRegistrar registrar_
;
400 // Listen to extension load, unloaded notifications.
401 ScopedObserver
<ExtensionRegistry
, ExtensionRegistryObserver
>
402 extension_registry_observer_
;
404 ExtensionIconManager icon_manager_
;
406 content::BrowserContext
* browser_context_
;
408 // Owned by ExtensionSystem.
411 DISALLOW_COPY_AND_ASSIGN(MenuManager
);
414 } // namespace extensions
416 #endif // CHROME_BROWSER_EXTENSIONS_MENU_MANAGER_H_