blob: e5ceea6a026c2fd0c1bbf7f43be72b15de51d80d [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_EXTENSIONS_EXTENSIONS_TOOLBAR_VIEW_MODEL_H_
#define CHROME_BROWSER_UI_EXTENSIONS_EXTENSIONS_TOOLBAR_VIEW_MODEL_H_
#include "base/containers/flat_map.h"
#include "base/scoped_observation.h"
#include "chrome/browser/tab_list/tab_list_interface_observer.h"
#include "chrome/browser/ui/extensions/extension_action_delegate.h"
#include "chrome/browser/ui/extensions/extension_action_view_model.h"
#include "chrome/browser/ui/extensions/extensions_container.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_model.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "content/public/browser/web_contents_observer.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/buildflags/buildflags.h"
#include "ui/gfx/vector_icon_types.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
class TabListInterface;
// ViewModel for the ExtensionsToolbarDesktop. This class manages the business
// logic for the order and state of extension actions in the toolbar. It serves
// as the single source of truth for the ordering of the list of actions.
class ExtensionsToolbarViewModel
: public ExtensionsContainer,
public ToolbarActionsModel::Observer,
public content::WebContentsObserver,
public TabListInterfaceObserver,
public extensions::PermissionsManager::Observer {
public:
// Delegate used to retrieve platform-specific information.
class Delegate {
public:
// Creates the platform-specific action view model.
virtual std::unique_ptr<ExtensionActionViewModel> CreateActionViewModel(
const ToolbarActionsModel::ActionId& action_id,
ExtensionsContainer* extensions_container) = 0;
// Hides any actively showing popups.
virtual void HideActivePopup() = 0;
// Closes the extensions menu, if it was open.
virtual void CloseExtensionsMenuIfOpen() = 0;
// Returns whether a popup can be shown.
virtual bool CanShowToolbarActionPopupForAPICall(
const ToolbarActionsModel::ActionId& action_id) = 0;
// Toggle the Extensions menu (as if the user clicked the puzzle piece
// icon).
// TODO(crbug.com/473701535): Determine whether this method belongs in the
// delegate or the observer.
virtual void ToggleExtensionsMenu() = 0;
// Triggers the manage extensions IPH.
virtual void ShowManageExtensionsIPH() {}
protected:
virtual ~Delegate() = default;
};
// Observer used to notify platforms about changes to the model.
class Observer : public base::CheckedObserver {
public:
// Called after all actions are added to the model.
virtual void OnActionsInitialized() = 0;
// Called when an action is added to the model.
virtual void OnActionAdded(
const ToolbarActionsModel::ActionId& action_id) = 0;
// Called when an action is removed from the model.
virtual void OnActionRemoved(
const ToolbarActionsModel::ActionId& action_id) = 0;
// Called when an action in the model is updated.
virtual void OnActionUpdated(
const ToolbarActionsModel::ActionId& action_id) = 0;
// Called when the pinned actions in the model are changed.
virtual void OnPinnedActionsChanged() = 0;
// Called when the active WebContents is changed (e.g. tab change or page
// navigation). `is_same_document` is true if the change was due to a
// same-document navigation.
virtual void OnActiveWebContentsChanged(
bool is_same_document,
content::WebContents* web_contents) = 0;
// Called when the extensions that should be displayed in the request
// access button to be recomputed.
virtual void OnRequestAccessButtonParamsChanged(
content::WebContents* web_contents) {}
// Called when both the extensions button and the request access button
// should be updated.
virtual void OnToolbarControlStateUpdated() {}
};
enum class ExtensionsToolbarButtonState {
// All extensions have blocked access to the current site.
kAllExtensionsBlocked,
// At least one extension has access to the current site.
kAnyExtensionHasAccess,
kDefault,
};
// Holds the information for the request access button.
struct RequestAccessButtonParams {
RequestAccessButtonParams();
RequestAccessButtonParams(RequestAccessButtonParams&&);
RequestAccessButtonParams& operator=(RequestAccessButtonParams&&);
~RequestAccessButtonParams();
std::vector<extensions::ExtensionId> extension_ids;
std::u16string tooltip_text;
};
ExtensionsToolbarViewModel(Delegate* delegate,
BrowserWindowInterface* browser,
ToolbarActionsModel* actions_model);
ExtensionsToolbarViewModel(const ExtensionsToolbarViewModel&) = delete;
ExtensionsToolbarViewModel& operator=(ExtensionsToolbarViewModel&) = delete;
~ExtensionsToolbarViewModel() override;
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Returns the view model of the action if it exists, else a nullptr.
ToolbarActionViewModel* GetActionModelForId(
const ToolbarActionsModel::ActionId& action_id) const;
// Returns whether a drag can be started on an action.
bool IsActionDraggable(const ToolbarActionsModel::ActionId& action_id) const;
// Move the pinned action `action_id` to `target_index`.
void MovePinnedAction(const ToolbarActionsModel::ActionId& action_id,
size_t target_index);
// Move this pinned action `action_id` by the specified `move_by` amount.
void MovePinnedActionBy(const std::string& action_id, int move_by);
// Returns the sorted list of the IDs of all installed actions.
const base::flat_set<ToolbarActionsModel::ActionId>& GetAllActionIds() const;
// Returns the ordered list of ids of pinned actions.
const std::vector<ToolbarActionsModel::ActionId>& GetPinnedActionIds() const;
// Returns whether the actions are initialized.
bool AreActionsInitialized();
// Returns the icon for the toolbar button for the given state.
static const gfx::VectorIcon& GetToolbarButtonIcon(
ExtensionsToolbarButtonState state);
// Returns the accessible text for the toolbar button for the given state.
static std::u16string GetToolbarButtonAccessibleText(
ExtensionsToolbarButtonState state);
// Returns the tooltip text for the toolbar button for the given state.
static std::u16string GetToolbarButtonTooltipText(
ExtensionsToolbarButtonState state);
// Returns the state of the extensions toolbar button based on 'web_contents'.
ExtensionsToolbarButtonState GetButtonState(
content::WebContents& web_contents) const;
// Executes the default behavior associated with the action. This should only
// be called as a result of a user action.
void ExecuteUserAction(const ToolbarActionsModel::ActionId& action_id,
ToolbarActionViewModel::InvocationSource source);
// Grants site access to the given `extension_ids` for the `web_contents`.
void GrantSiteAccess(
content::WebContents* web_contents,
const std::vector<extensions::ExtensionId>& extension_ids);
// Returns RequestAccessButtonParams which contain information to be used in
// the button's tooltip.
RequestAccessButtonParams GetRequestAccessButtonParams(
content::WebContents* web_contents) const;
// ExtensionsContainer:
ToolbarActionViewModel* GetActionForId(const std::string& action_id) override;
void HideActivePopup() override;
void CloseExtensionsMenuIfOpen() override;
bool ShowToolbarActionPopupForAPICall(const std::string& action_id,
ShowPopupCallback callback) override;
void ToggleExtensionsMenu() override;
void ShowManageExtensionsIPH() override;
bool HasAnyExtensions() const override;
// ToolbarActionsModel::Observer:
void OnToolbarModelInitialized() override;
void OnToolbarActionAdded(
const ToolbarActionsModel::ActionId& action_id) override;
void OnToolbarActionRemoved(
const ToolbarActionsModel::ActionId& action_id) override;
void OnToolbarActionUpdated(
const ToolbarActionsModel::ActionId& action_id) override;
void OnToolbarPinnedActionsChanged() override;
// content::WebContentsObserver:
void DidFinishNavigation(content::NavigationHandle* handle) override;
// TabListInterfaceObserver:
void OnActiveTabChanged(TabListInterface& tab_list,
tabs::TabInterface* tab) override;
void OnTabListDestroyed(TabListInterface& tab_list) override;
// extensions::PermissionsManager::Observer:
void OnHostAccessRequestAdded(const extensions::ExtensionId& extension_id,
int tab_id) override;
void OnHostAccessRequestUpdated(const extensions::ExtensionId& extension_id,
int tab_id) override;
void OnHostAccessRequestRemoved(const extensions::ExtensionId& extension_id,
int tab_id) override;
void OnHostAccessRequestsCleared(int tab_id) override;
void OnHostAccessRequestDismissedByUser(const extensions::ExtensionId& id,
const url::Origin& origin) override;
void OnUserPermissionsSettingsChanged(
const extensions::PermissionsManager::UserPermissionsSettings& settings)
override;
void OnShowAccessRequestsInToolbarChanged(
const extensions::ExtensionId& extension_id,
bool can_show_requests) override;
private:
// Returns whether any of `actions` given have access to the `web_contents`.
bool AnyActionHasCurrentSiteAccess(content::WebContents& web_contents) const;
// Creates and appends an action model to `actions_` vector.
void AppendActionModel(const ToolbarActionsModel::ActionId& action_id);
// Returns the current web contents.
content::WebContents* GetCurrentWebContents() const;
// The corresponding browser window.
const raw_ptr<BrowserWindowInterface> browser_;
// The delegate to retrieve platform-specific information.
const raw_ptr<Delegate> delegate_;
const raw_ptr<ToolbarActionsModel> actions_model_;
base::ScopedObservation<ToolbarActionsModel, ToolbarActionsModel::Observer>
actions_model_observation_{this};
base::ScopedObservation<TabListInterface, TabListInterfaceObserver>
tab_list_observation_{this};
base::ScopedObservation<extensions::PermissionsManager,
extensions::PermissionsManager::Observer>
permissions_manager_observation_{this};
// The observers that handles platform-specific UI.
base::ObserverList<Observer> observers_;
// Actions for all extensions.
base::flat_map<ToolbarActionsModel::ActionId,
std::unique_ptr<ToolbarActionViewModel>>
actions_;
};
#endif // CHROME_BROWSER_UI_EXTENSIONS_EXTENSIONS_TOOLBAR_VIEW_MODEL_H_