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_TOOLBAR_MODEL_H_
6#define CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_H_
7
8#include "base/compiler_specific.h"
9#include "base/observer_list.h"
10#include "base/prefs/pref_change_registrar.h"
11#include "base/scoped_observer.h"
12#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
13#include "chrome/browser/extensions/extension_action.h"
14#include "components/keyed_service/core/keyed_service.h"
15#include "content/public/browser/notification_observer.h"
16#include "content/public/browser/notification_registrar.h"
17#include "extensions/browser/extension_prefs.h"
18#include "extensions/browser/extension_registry_observer.h"
19#include "extensions/common/extension.h"
20
21class Browser;
22class PrefService;
23class Profile;
24
25namespace extensions {
26class ExtensionRegistry;
27class ExtensionSet;
28
29// Model for the browser actions toolbar.
30class ExtensionToolbarModel : public content::NotificationObserver,
31                              public ExtensionActionAPI::Observer,
32                              public ExtensionRegistryObserver,
33                              public KeyedService {
34 public:
35  ExtensionToolbarModel(Profile* profile, ExtensionPrefs* extension_prefs);
36  virtual ~ExtensionToolbarModel();
37
38  // A class which is informed of changes to the model; represents the view of
39  // MVC. Also used for signaling view changes such as showing extension popups.
40  // TODO(devlin): Should this really be an observer? It acts more like a
41  // delegate.
42  class Observer {
43   public:
44    // An extension has been added to the toolbar and should go at |index|.
45    virtual void ToolbarExtensionAdded(const Extension* extension,
46                                       int index) = 0;
47
48    // The given |extension| should be removed from the toolbar.
49    virtual void ToolbarExtensionRemoved(const Extension* extension) = 0;
50
51    // The given |extension| has been moved to |index|. |index| is the desired
52    // *final* index of the extension (that is, in the adjusted order, extension
53    // should be at |index|).
54    virtual void ToolbarExtensionMoved(const Extension* extension,
55                                       int index) = 0;
56
57    // Signals that the browser action for the given |extension| has been
58    // updated.
59    virtual void ToolbarExtensionUpdated(const Extension* extension) = 0;
60
61    // Signal the |extension| to show the popup now in the active window.
62    // If |grant_active_tab| is true, then active tab permissions should be
63    // given to the extension (only do this if this is through a user action).
64    // Returns true if a popup was slated to be shown.
65    virtual bool ShowExtensionActionPopup(const Extension* extension,
66                                          bool grant_active_tab) = 0;
67
68    // Signal when the container needs to be redrawn because of a size change,
69    // and when the model has finished loading.
70    virtual void ToolbarVisibleCountChanged() = 0;
71
72    // Signal that the model has entered or exited highlighting mode, or that
73    // the extensions being highlighted have (probably*) changed. Highlighting
74    // mode indicates that only a subset of the extensions are actively
75    // displayed, and those extensions should be highlighted for extra emphasis.
76    // * probably, because if we are in highlight mode and receive a call to
77    //   highlight a new set of extensions, we do not compare the current set
78    //   with the new set (and just assume the new set is different).
79    virtual void ToolbarHighlightModeChanged(bool is_highlighting) = 0;
80
81    // Returns the browser associated with the Observer.
82    virtual Browser* GetBrowser() = 0;
83
84   protected:
85    virtual ~Observer() {}
86  };
87
88  // Convenience function to get the ExtensionToolbarModel for a Profile.
89  static ExtensionToolbarModel* Get(Profile* profile);
90
91  // Add or remove an observer.
92  void AddObserver(Observer* observer);
93  void RemoveObserver(Observer* observer);
94
95  // Moves the given |extension|'s icon to the given |index|.
96  void MoveExtensionIcon(const Extension* extension, int index);
97
98  // Sets the number of extension icons that should be visible.
99  // If count == size(), this will set the visible icon count to -1, meaning
100  // "show all actions".
101  void SetVisibleIconCount(int count);
102
103  // As above, a return value of -1 represents "show all actions".
104  int GetVisibleIconCount() const { return visible_icon_count_; }
105
106  bool extensions_initialized() const { return extensions_initialized_; }
107
108  const ExtensionList& toolbar_items() const {
109    return is_highlighting_ ? highlighted_items_ : toolbar_items_;
110  }
111
112  bool is_highlighting() const { return is_highlighting_; }
113
114  // Utility functions for converting between an index into the list of
115  // incognito-enabled browser actions, and the list of all browser actions.
116  int IncognitoIndexToOriginal(int incognito_index);
117  int OriginalIndexToIncognito(int original_index);
118
119  void OnExtensionToolbarPrefChange();
120
121  // Finds the Observer associated with |browser| and tells it to display a
122  // popup for the given |extension|. If |grant_active_tab| is true, this
123  // grants active tab permissions to the |extension|; only do this because of
124  // a direct user action.
125  bool ShowExtensionActionPopup(const Extension* extension,
126                                Browser* browser,
127                                bool grant_active_tab);
128
129  // Ensures that the extensions in the |extension_ids| list are visible on the
130  // toolbar. This might mean they need to be moved to the front (if they are in
131  // the overflow bucket).
132  void EnsureVisibility(const ExtensionIdList& extension_ids);
133
134  // Highlight the extensions specified by |extension_ids|. This will cause
135  // the ToolbarModel to only display those extensions.
136  // Highlighting mode is only entered if there is at least one extension to
137  // be shown.
138  // Returns true if highlighting mode is entered, false otherwise.
139  bool HighlightExtensions(const ExtensionIdList& extension_ids);
140
141  // Stop highlighting extensions. All extensions can be shown again, and the
142  // number of visible icons will be reset to what it was before highlighting.
143  void StopHighlighting();
144
145  // Sets the number of visible icons and notifies all observers of the change.
146  void SetVisibleIconCountForTest(size_t visible_icons);
147
148 private:
149  // content::NotificationObserver:
150  virtual void Observe(int type,
151                       const content::NotificationSource& source,
152                       const content::NotificationDetails& details) OVERRIDE;
153
154  // Callback when extensions are ready.
155  void OnReady();
156
157  // ExtensionRegistryObserver:
158  virtual void OnExtensionLoaded(content::BrowserContext* browser_context,
159                                 const Extension* extension) OVERRIDE;
160  virtual void OnExtensionUnloaded(
161      content::BrowserContext* browser_context,
162      const Extension* extension,
163      UnloadedExtensionInfo::Reason reason) OVERRIDE;
164  virtual void OnExtensionUninstalled(
165      content::BrowserContext* browser_context,
166      const Extension* extension,
167      extensions::UninstallReason reason) OVERRIDE;
168
169  // ExtensionActionAPI::Observer:
170  virtual void OnExtensionActionUpdated(
171      ExtensionAction* extension_action,
172      content::WebContents* web_contents,
173      content::BrowserContext* browser_context) OVERRIDE;
174
175  // To be called after the extension service is ready; gets loaded extensions
176  // from the extension service and their saved order from the pref service
177  // and constructs |toolbar_items_| from these data.
178  void InitializeExtensionList(const ExtensionSet& extensions);
179  void Populate(const ExtensionIdList& positions,
180                const ExtensionSet& extensions);
181
182  // Save the model to prefs.
183  void UpdatePrefs();
184
185  // Updates |extension|'s browser action visibility pref if the browser action
186  // is in the overflow menu and should be considered hidden.
187  void MaybeUpdateVisibilityPref(const Extension* extension, int index);
188
189  // Calls MaybeUpdateVisibilityPref() for each extension in |toolbar_items|.
190  void MaybeUpdateVisibilityPrefs();
191
192  // Finds the last known visible position of the icon for an |extension|. The
193  // value returned is a zero-based index into the vector of visible items.
194  size_t FindNewPositionFromLastKnownGood(const Extension* extension);
195
196  // Returns true if the given |extension| should be added to the toolbar.
197  bool ShouldAddExtension(const Extension* extension);
198
199  // Adds or removes the given |extension| from the toolbar model.
200  void AddExtension(const Extension* extension);
201  void RemoveExtension(const Extension* extension);
202
203  // Our observers.
204  ObserverList<Observer> observers_;
205
206  // The Profile this toolbar model is for.
207  Profile* profile_;
208
209  ExtensionPrefs* extension_prefs_;
210  PrefService* prefs_;
211
212  // True if we've handled the initial EXTENSIONS_READY notification.
213  bool extensions_initialized_;
214
215  // If true, we include all extensions in the toolbar model. If false, we only
216  // include browser actions.
217  bool include_all_extensions_;
218
219  // Ordered list of browser action buttons.
220  ExtensionList toolbar_items_;
221
222  // List of browser action buttons which should be highlighted.
223  ExtensionList highlighted_items_;
224
225  // Indication whether or not we are currently in highlight mode; typically,
226  // this is equivalent to !highlighted_items_.empty(), but can be different
227  // if we are exiting highlight mode due to no longer having highlighted items.
228  bool is_highlighting_;
229
230  // The number of icons which were visible before highlighting a subset, in
231  // order to restore the count when finished.
232  int old_visible_icon_count_;
233
234  ExtensionIdList last_known_positions_;
235
236  // The number of icons visible (the rest should be hidden in the overflow
237  // chevron).
238  int visible_icon_count_;
239
240  content::NotificationRegistrar registrar_;
241
242  ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer>
243      extension_action_observer_;
244
245  // Listen to extension load, unloaded notifications.
246  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
247      extension_registry_observer_;
248
249  // For observing change of toolbar order preference by external entity (sync).
250  PrefChangeRegistrar pref_change_registrar_;
251  base::Closure pref_change_callback_;
252
253  base::WeakPtrFactory<ExtensionToolbarModel> weak_ptr_factory_;
254
255  DISALLOW_COPY_AND_ASSIGN(ExtensionToolbarModel);
256};
257
258}  // namespace extensions
259
260#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_H_
261