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