• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ContextList()75     ContextList() : value_(0) {}
ContextList(Context context)76     explicit ContextList(Context context) : value_(context) {}
ContextList(const ContextList & other)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 
Contains(Context context)91     bool Contains(Context context) const {
92       return (value_ & context) > 0;
93     }
94 
Add(Context context)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.
extension_id()111   const std::string& extension_id() const { return id_.extension_id; }
title()112   const std::string& title() const { return title_; }
children()113   const List& children() { return children_; }
id()114   const Id& id() const { return id_; }
parent_id()115   Id* parent_id() const { return parent_id_.get(); }
child_count()116   int child_count() const { return children_.size(); }
contexts()117   ContextList contexts() const { return contexts_; }
type()118   Type type() const { return type_; }
checked()119   bool checked() const { return checked_; }
document_url_patterns()120   const ExtensionExtent& document_url_patterns() const {
121     return document_url_patterns_;
122   }
target_url_patterns()123   const ExtensionExtent& target_url_patterns() const {
124     return target_url_patterns_;
125   }
126 
127   // Simple mutator methods.
set_title(const std::string & new_title)128   void set_title(const std::string& new_title) { title_ = new_title; }
set_contexts(ContextList contexts)129   void set_contexts(ContextList contexts) { contexts_ = contexts; }
set_type(Type type)130   void set_type(Type type) { type_ = type; }
set_document_url_patterns(const ExtensionExtent & patterns)131   void set_document_url_patterns(const ExtensionExtent& patterns) {
132     document_url_patterns_ = patterns;
133   }
set_target_url_patterns(const ExtensionExtent & patterns)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