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