blob: bc8d2253bc05635cb59fad8dbc5c353d7dccc126 [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.
#ifndef CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_H_
#define CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_H_
#include <stddef.h>
#include <vector>
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
#include "chrome/browser/extensions/extension_management.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "extensions/browser/extension_action.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension.h"
class Browser;
class PrefService;
class Profile;
class ExtensionsContainer;
namespace extensions {
class ExtensionActionManager;
class ExtensionMessageBubbleController;
} // namespace extensions
// 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
// ExtensionsContainer, which is responsible for the per-window layout.
class ToolbarActionsModel : public extensions::ExtensionActionAPI::Observer,
public extensions::ExtensionRegistryObserver,
public extensions::ExtensionManagement::Observer,
public content::NotificationObserver,
public KeyedService {
public:
using ActionId = std::string;
ToolbarActionsModel(Profile* profile,
extensions::ExtensionPrefs* extension_prefs);
ToolbarActionsModel(const ToolbarActionsModel&) = delete;
ToolbarActionsModel& operator=(const ToolbarActionsModel&) = delete;
~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 {
public:
// Signals that |id| has been added to the toolbar. This will
// *only* be called after the toolbar model has been initialized.
virtual void OnToolbarActionAdded(const ActionId& id) = 0;
// Signals that the given action with |id| has been removed from the
// toolbar.
virtual void OnToolbarActionRemoved(const ActionId& id) = 0;
// Signals that the browser action with |id| has been updated.
// This method covers lots of different extension updates and could be split
// in different methods if needed, such as
// `OnToolbarActionHostPermissionsUpdated`.
virtual void OnToolbarActionUpdated(const ActionId& id) = 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;
// Called whenever the pinned actions change.
virtual void OnToolbarPinnedActionsChanged() = 0;
protected:
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);
bool actions_initialized() const { return actions_initialized_; }
const base::flat_set<ActionId>& action_ids() const { return action_ids_; }
bool has_active_bubble() const { return has_active_bubble_; }
void set_has_active_bubble(bool has_active_bubble) {
has_active_bubble_ = has_active_bubble;
}
void SetActionVisibility(const ActionId& action_id, bool visible);
// Gets the ExtensionMessageBubbleController that should be shown for this
// profile, if any.
std::unique_ptr<extensions::ExtensionMessageBubbleController>
GetExtensionMessageBubbleController(Browser* browser);
// Returns the extension name corresponding to the `action_id`.
const std::u16string GetExtensionName(const ActionId& action_id) const;
// Returns true if `url` is restricted for all extensions with actions in the
// toolbar.ß
bool IsRestrictedUrl(const GURL& url) const;
// Returns true if the action is pinned to the toolbar.
bool IsActionPinned(const ActionId& action_id) const;
// Returns true if the action is force-pinned to the toolbar.
bool IsActionForcePinned(const ActionId& action_id) const;
// Move the pinned action for |action_id| to |target_index|.
void MovePinnedAction(const ActionId& action_id, size_t target_index);
// Returns the ordered list of ids of pinned actions.
const std::vector<ActionId>& pinned_action_ids() const {
return pinned_action_ids_;
}
private:
// 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(
extensions::ExtensionAction* extension_action,
content::WebContents* web_contents,
content::BrowserContext* browser_context) override;
// extensions::ExtensionManagement::Observer:
void OnExtensionManagementSettingsChanged() override;
// content::NotificationObserver:
void Observe(int notification_type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// To be called after the extension service is ready; gets loaded extensions
// from the ExtensionRegistry, their saved order from the pref service, and
// constructs |action_ids_| from these data. IncognitoPopulate() takes
// the shortcut - looking at the regular model's content and modifying it.
void InitializeActionList();
void Populate();
void IncognitoPopulate();
// Removes any preference for |action_id| and saves the model to prefs.
void RemovePref(const ActionId& action_id);
// Returns true if the given |extension| should be added to the toolbar.
bool ShouldAddExtension(const extensions::Extension* extension);
// Returns true if |action_id| is in the toolbar model.
bool HasAction(const ActionId& action_id) const;
// Adds |action_id| to the toolbar. If the action 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 actions.
void AddAction(const ActionId& action_id);
// Removes |action_id| from the toolbar.
void RemoveAction(const ActionId& action_id);
// Looks up and returns the extension with the given |id| in the set of
// enabled extensions.
const extensions::Extension* GetExtensionById(const ActionId& id) const;
// Updates |pinned_action_ids_| per GetFilteredPinnedActionIds() and notifies
// observers if they have changed.
void UpdatePinnedActionIds();
// Gets a list of pinned action ids that only contains that only contains IDs
// with a corresponding action in the model.
std::vector<ActionId> GetFilteredPinnedActionIds() const;
// Our observers.
base::ObserverList<Observer>::Unchecked observers_;
// The Profile this toolbar model is for.
raw_ptr<Profile> profile_;
raw_ptr<extensions::ExtensionPrefs> extension_prefs_;
raw_ptr<PrefService> prefs_;
// The ExtensionActionAPI object, cached for convenience.
raw_ptr<extensions::ExtensionActionAPI> extension_action_api_;
// The ExtensionRegistry object, cached for convenience.
raw_ptr<extensions::ExtensionRegistry> extension_registry_;
// The ExtensionActionManager, cached for convenience.
raw_ptr<extensions::ExtensionActionManager> extension_action_manager_;
// True if we've handled the initial EXTENSIONS_READY notification.
bool actions_initialized_;
// Collection of all action IDs (pinned and unpinned).
base::flat_set<ActionId> action_ids_;
// Ordered list of pinned action IDs, indicating the order actions should
// appear on the toolbar.
std::vector<ActionId> pinned_action_ids_;
// Whether or not there is an active ExtensionMessageBubbleController
// associated with the profile. There should only be one at a time.
bool has_active_bubble_;
base::ScopedObservation<extensions::ExtensionActionAPI,
extensions::ExtensionActionAPI::Observer>
extension_action_observation_{this};
// Listen to extension load, unloaded notifications.
base::ScopedObservation<extensions::ExtensionRegistry,
ExtensionRegistryObserver>
extension_registry_observation_{this};
// For observing pinned extensions changing.
PrefChangeRegistrar pref_change_registrar_;
base::ScopedObservation<extensions::ExtensionManagement,
extensions::ExtensionManagement::Observer>
extension_management_observation_{this};
// Registrar for receiving permission-related notifications.
content::NotificationRegistrar notification_registrar_;
base::WeakPtrFactory<ToolbarActionsModel> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_H_