blob: 662844d9c0287f7aefd0b10f3a59570e976d6330 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/scoped_observer.h"
#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/ui/toolbar/component_action_delegate.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension.h"
class Browser;
class ComponentToolbarActionsFactory;
class PrefService;
class Profile;
class ToolbarActionsBar;
class ToolbarActionViewController;
namespace extensions {
class ExtensionActionManager;
class ExtensionMessageBubbleController;
class ExtensionRegistry;
// Model for the browser actions toolbar. This is a per-profile instance, and
// manages the user's global preferences.
// Each browser window will attempt to show browser actions as specified by this
// model, but if the window is too narrow, actions may end up pushed into the
// overflow menu on a per-window basis. Callers interested in the arrangement of
// actions in a particular window should check that window's instance of
// ToolbarActionsBar, which is responsible for the per-window layout.
class ToolbarActionsModel : public extensions::ExtensionActionAPI::Observer,
public extensions::ExtensionRegistryObserver,
public KeyedService,
public ComponentActionDelegate {
// The different options for highlighting.
enum HighlightType {
// The different types of actions.
enum ActionType {
// An action id and its corresponding ActionType.
struct ToolbarItem {
ToolbarItem() : type(UNKNOWN_ACTION) {}
ToolbarItem(const std::string& action_id, ActionType action_type)
: id(action_id), type(action_type) {}
bool operator==(const ToolbarItem& other) const { return == id; }
std::string id;
ActionType type;
ToolbarActionsModel(Profile* profile,
extensions::ExtensionPrefs* extension_prefs);
~ToolbarActionsModel() override;
// A class which is informed of changes to the model; represents the view of
// MVC. Also used for signaling view changes such as showing extension popups.
// TODO(devlin): Should this really be an observer? It acts more like a
// delegate.
class Observer {
// Signals that |item| has been added to the toolbar at |index|. This will
// *only* be called after the toolbar model has been initialized.
virtual void OnToolbarActionAdded(const ToolbarItem& item, int index) = 0;
// Signals that the given action with |id| has been removed from the
// toolbar.
virtual void OnToolbarActionRemoved(const std::string& id) = 0;
// Signals that the given action with |id| has been moved to |index|.
// |index| is the desired *final* index of the action (that is, in the
// adjusted order, action should be at |index|).
virtual void OnToolbarActionMoved(const std::string& id, int index) = 0;
// Signals that the browser action with |id| has been updated.
virtual void OnToolbarActionUpdated(const std::string& id) = 0;
// Signals when the container needs to be redrawn because of a size change,
// and when the model has finished loading.
virtual void OnToolbarVisibleCountChanged() = 0;
// Signals that the model has entered or exited highlighting mode, or that
// the actions being highlighted have (probably*) changed. Highlighting
// mode indicates that only a subset of the toolbar actions are actively
// displayed, and those actions should be highlighted for extra emphasis.
// * probably, because if we are in highlight mode and receive a call to
// highlight a new set of actions, we do not compare the current set with
// the new set (and just assume the new set is different).
virtual void OnToolbarHighlightModeChanged(bool is_highlighting) = 0;
// Signals that the toolbar model has been initialized, so that if any
// observers were postponing animation during the initialization stage, they
// can catch up.
virtual void OnToolbarModelInitialized() = 0;
virtual ~Observer() {}
// Convenience function to get the ToolbarActionsModel for a Profile.
static ToolbarActionsModel* Get(Profile* profile);
// Adds or removes an observer.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Moves the given action with |id|'s icon to the given |index|.
void MoveActionIcon(const std::string& id, size_t index);
// Sets the number of action icons that should be visible.
// If count == size(), this will set the visible icon count to -1, meaning
// "show all actions".
void SetVisibleIconCount(size_t count);
// Note that this (and all_icons_visible()) are the global default, but are
// inappropriate for determining a specific window's state - for that, use
// the ToolbarActionsBar.
size_t visible_icon_count() const {
// We have guards around this because |visible_icon_count_| can be set by
// prefs/sync, and we want to ensure that the icon count returned is within
// bounds.
return visible_icon_count_ == -1
? toolbar_items().size()
: std::min(static_cast<size_t>(visible_icon_count_),
bool all_icons_visible() const {
return visible_icon_count() == toolbar_items().size();
bool actions_initialized() const { return actions_initialized_; }
std::vector<std::unique_ptr<ToolbarActionViewController>> CreateActions(
Browser* browser,
ToolbarActionsBar* bar);
std::unique_ptr<ToolbarActionViewController> CreateActionForItem(
Browser* browser,
ToolbarActionsBar* bar,
const ToolbarItem& item);
const std::vector<ToolbarItem>& toolbar_items() const {
return is_highlighting() ? highlighted_items_ : toolbar_items_;
bool is_highlighting() const { return highlight_type_ != HIGHLIGHT_NONE; }
HighlightType highlight_type() const { return highlight_type_; }
bool has_active_bubble() const { return has_active_bubble_; }
void set_has_active_bubble(bool has_active_bubble) {
has_active_bubble_ = has_active_bubble;
ComponentToolbarActionsFactory* component_actions_factory() {
return component_actions_factory_.get();
void SetActionVisibility(const std::string& action_id, bool visible);
// ComponentActionDelegate:
// AddComponentAction() is a no-op if |actions_initialized_| is false.
void AddComponentAction(const std::string& action_id) override;
void RemoveComponentAction(const std::string& action_id) override;
bool HasComponentAction(const std::string& action_id) const override;
void OnActionToolbarPrefChange();
// Highlights the actions specified by |action_ids|. This will cause
// the ToolbarModel to only display those actions.
// Highlighting mode is only entered if there is at least one action to be
// shown.
// Returns true if highlighting mode is entered, false otherwise.
bool HighlightActions(const std::vector<std::string>& action_ids,
HighlightType type);
// Stop highlighting actions. All actions can be shown again, and the
// number of visible icons will be reset to what it was before highlighting.
void StopHighlighting();
// Gets the ExtensionMessageBubbleController that should be shown for this
// profile, if any.
GetExtensionMessageBubbleController(Browser* browser);
// Sets the component action factory for this object. Used in tests.
void SetMockActionsFactoryForTest(
std::unique_ptr<ComponentToolbarActionsFactory> mock_factory);
// Callback when actions are ready.
void OnReady();
// ExtensionRegistryObserver:
void OnExtensionLoaded(content::BrowserContext* browser_context,
const extensions::Extension* extension) override;
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) override;
void OnExtensionUninstalled(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UninstallReason reason) override;
// ExtensionActionAPI::Observer:
void OnExtensionActionUpdated(
ExtensionAction* extension_action,
content::WebContents* web_contents,
content::BrowserContext* browser_context) override;
// To be called after the extension service is ready; gets loaded extensions
// from the ExtensionRegistry, their saved order from the pref service, and
// the initial set of component actions from the
// ComponentToolbarActionsFactory, and constructs |toolbar_items_| from these
// data. IncognitoPopulate() takes the shortcut - looking at the regular
// model's content and modifying it.
void InitializeActionList();
void Populate();
void IncognitoPopulate();
// Save the model to prefs.
void UpdatePrefs();
// Removes any preference for |item| and saves the model to prefs.
void RemovePref(const ToolbarItem& item);
// Finds the last known visible position of the icon for |action|. The value
// returned is a zero-based index into the vector of visible items.
size_t FindNewPositionFromLastKnownGood(const ToolbarItem& action);
// Returns true if the given |extension| should be added to the toolbar.
bool ShouldAddExtension(const extensions::Extension* extension);
// Adds or removes the given |extension| from the toolbar model.
void AddExtension(const extensions::Extension* extension);
void RemoveExtension(const extensions::Extension* extension);
// Returns true if |item| is in the toolbar model.
bool HasItem(const ToolbarItem& item) const;
// Adds |item| to the toolbar. If the item has an existing preference for
// toolbar position, that will be used to determine its location. Otherwise
// it will be placed at the end of the visible items. If the toolbar is in
// highlighting mode, the item will not be visible until highlighting mode is
// exited.
void AddItem(const ToolbarItem& item);
// Removes |item| from the toolbar. If the toolbar is in highlighting mode,
// the item is also removed from the highlighted list (if present).
void RemoveItem(const ToolbarItem& item);
// Looks up and returns the extension with the given |id| in the set of
// enabled extensions.
const extensions::Extension* GetExtensionById(const std::string& id) const;
// Returns true if the action is visible on the toolbar.
bool IsActionVisible(const std::string& action_id) const;
// Our observers.
base::ObserverList<Observer> observers_;
// The Profile this toolbar model is for.
Profile* profile_;
extensions::ExtensionPrefs* extension_prefs_;
PrefService* prefs_;
// The ExtensionActionAPI object, cached for convenience.
extensions::ExtensionActionAPI* extension_action_api_;
// The ExtensionRegistry object, cached for convenience.
extensions::ExtensionRegistry* extension_registry_;
// The ExtensionActionManager, cached for convenience.
extensions::ExtensionActionManager* extension_action_manager_;
std::unique_ptr<ComponentToolbarActionsFactory> component_actions_factory_;
// True if we've handled the initial EXTENSIONS_READY notification.
bool actions_initialized_;
// Ordered list of browser actions.
std::vector<ToolbarItem> toolbar_items_;
// List of browser actions which should be highlighted.
std::vector<ToolbarItem> highlighted_items_;
// The current type of highlight (with HIGHLIGHT_NONE indicating no current
// highlight).
HighlightType highlight_type_;
// A list of action ids ordered to correspond with their last known
// positions.
std::vector<std::string> last_known_positions_;
// The number of icons visible (the rest should be hidden in the overflow
// chevron). A value of -1 indicates that all icons should be visible.
// Instead of using this variable directly, use visible_icon_count() if
// possible.
// TODO(devlin): Make a new variable to indicate that all icons should be
// visible, instead of overloading this one.
int visible_icon_count_;
// Whether or not there is an active ExtensionMessageBubbleController
// associated with the profile. There should only be one at a time.
bool has_active_bubble_;
// Listen to extension load, unloaded notifications.
ScopedObserver<extensions::ExtensionRegistry, ExtensionRegistryObserver>
// For observing change of toolbar order preference by external entity (sync).
PrefChangeRegistrar pref_change_registrar_;
base::Closure pref_change_callback_;
base::WeakPtrFactory<ToolbarActionsModel> weak_ptr_factory_;