blob: cfb20be65a49b470ad7575cd8dc36631d515729b [file] [log] [blame]
// Copyright 2019 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_VIEWS_EXTENSIONS_EXTENSIONS_TOOLBAR_CONTAINER_H_
#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_TOOLBAR_CONTAINER_H_
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ui/extensions/extensions_container.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/ui/views/extensions/extensions_request_access_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h"
#include "extensions/common/extension.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/views/widget/widget_observer.h"
class Browser;
class ExtensionsToolbarButton;
class ToolbarActionViewController;
class ExtensionsMenuCoordinator;
// Container for extensions shown in the toolbar. These include pinned
// extensions and extensions that are 'popped out' transitively to show dialogs
// or be called out to the user.
class ExtensionsToolbarContainer : public ToolbarIconContainerView,
public ExtensionsContainer,
public ToolbarActionView::Delegate,
public views::WidgetObserver {
METADATA_HEADER(ExtensionsToolbarContainer, ToolbarIconContainerView)
public:
using ToolbarIcons =
std::map<ToolbarActionsModel::ActionId, ToolbarActionView*>;
// Determines how the container displays - specifically whether the menu and
// popped out action can be hidden.
enum class DisplayMode {
// In normal mode, the menu icon and popped-out action is always visible.
// Normal mode is used for the main toolbar and in windows where there is
// always enough space to show at least two icons.
kNormal,
// In compact mode, one or both of the menu icon and popped-out action may
// be hidden if the available space does not allow for them. Compact mode is
// used in smaller windows (e.g. web apps) where
// there may not be enough space to display the buttons.
// TODO(crbug.com/40159931): Remove kCompact in favour of kAutoHide once the
// |kDesktopPWAsElidedExtensionsMenu| flag is removed.
kCompact,
// In auto hide mode the menu icon is hidden until
// extensions_button()->ToggleExtensionsMenu() is called by the embedder.
// This is used for windows that want to minimize the number of visible
// icons in their toolbar (e.g. web apps).
kAutoHide,
};
static void SetOnVisibleCallbackForTesting(base::OnceClosure callback);
ExtensionsMenuCoordinator* GetExtensionsMenuCoordinatorForTesting() {
return extensions_menu_coordinator_.get();
}
explicit ExtensionsToolbarContainer(
Browser* browser,
DisplayMode display_mode = DisplayMode::kNormal);
ExtensionsToolbarContainer(const ExtensionsToolbarContainer&) = delete;
ExtensionsToolbarContainer& operator=(const ExtensionsToolbarContainer&) =
delete;
~ExtensionsToolbarContainer() override;
// Creates toolbar actions and icons corresponding to the model. This is only
// called in the constructor or when the model initializes and should not be
// called for subsequent changes to the model.
void CreateActions();
// Adds the action view corresponding to `action_id` to the toolbar and
// updates the container visibility, reordering views if necessary.
void AddAction(const ToolbarActionsModel::ActionId& action_id);
// Removes the action view corresponding to `action_id` to the toolbar and
// updates the container visibility, reordering views if necessary.
void RemoveAction(const ToolbarActionsModel::ActionId& action_id);
// Updates the action view corresponding to `action_id` to the toolbar and
// updates the container visibility, reordering views if necessary.
void UpdateAction(const ToolbarActionsModel::ActionId& action_id);
// Adds the visible action views the toolbar and updates the container
// visibility, reordering views if necessary.
void UpdatePinnedActions();
// Updates `extensions_button_` icon given user `site_setting` and whether
// `is_restricted_url` in `web_contents`.
void UpdateExtensionsButton(
extensions::PermissionsManager::UserSiteSetting site_setting,
content::WebContents* web_contents,
bool is_restricted_url);
// Updates the `request_access_button_` given user `site_setting` for the
// current `web_contents`.
void UpdateRequestAccessButton(
extensions::PermissionsManager::UserSiteSetting site_setting,
content::WebContents* web_contents);
// Updates the container visibility and animation as needed.
void UpdateContainerVisibility();
// Updates the controls visibility.
void UpdateControlsVisibility();
ToolbarActionViewController* popup_owner_for_testing() {
return popup_owner_;
}
// Gets the extension menu button for the toolbar.
ExtensionsToolbarButton* GetExtensionsButton() const {
return extensions_button_;
}
// Get the request access button for the toolbar.
ExtensionsRequestAccessButton* GetRequestAccessButton() const {
return request_access_button_;
}
// Get the view corresponding to the extension |id|, if any.
ToolbarActionView* GetViewForId(const std::string& id);
// Pop out and show the extension corresponding to |extension_id|, then show
// the Widget when the icon is visible. If the icon is already visible the
// action will be posted immediately (not run synchronously).
void ShowWidgetForExtension(views::Widget* widget,
const std::string& extension_id);
// Check if the extensions menu is showing.
// TODO(crbug.com/40811196): This method will be removed once extensions menu
// under kExtensionsMenuAccessControl feature is fully rolled out and we can
// call directly into the menu coordinator.
bool IsExtensionsMenuShowing() const;
// Event handler for when the extensions menu is opened.
void OnMenuOpening();
// Event handler for when the extensions menu is closed.
void OnMenuClosed();
// Gets the widget that anchors to the extension (or is about to anchor to the
// extension, pending pop-out).
views::Widget* GetAnchoredWidgetForExtensionForTesting(
const std::string& extension_id);
std::optional<extensions::ExtensionId>
GetExtensionWithOpenContextMenuForTesting() {
return extension_with_open_context_menu_id_;
}
int GetNumberOfActionsForTesting() { return actions_.size(); }
ToolbarButton* GetCloseSidePanelButtonForTesting() {
return close_side_panel_button_;
}
// Called when the side panel state has changed for an extensions side panel
// to pop out button reflecting the side panel being open.
void UpdateSidePanelState(bool is_active);
// ToolbarIconContainerView:
void UpdateAllIcons() override;
bool GetDropFormats(int* formats,
std::set<ui::ClipboardFormatType>* format_types) override;
bool AreDropTypesRequired() override;
bool CanDrop(const ui::OSExchangeData& data) override;
void OnDragEntered(const ui::DropTargetEvent& event) override;
int OnDragUpdated(const ui::DropTargetEvent& event) override;
void OnDragExited() override;
views::View::DropCallback GetDropCallback(
const ui::DropTargetEvent& event) override;
void OnMouseExited(const ui::MouseEvent& event) override;
void OnMouseMoved(const ui::MouseEvent& event) override;
// ExtensionsContainer:
ToolbarActionViewController* GetActionForId(
const std::string& action_id) override;
std::optional<extensions::ExtensionId> GetPoppedOutActionId() const override;
void OnContextMenuShownFromToolbar(const std::string& action_id) override;
void OnContextMenuClosedFromToolbar() override;
bool IsActionVisibleOnToolbar(const std::string& action_id) const override;
void UndoPopOut() override;
void SetPopupOwner(ToolbarActionViewController* popup_owner) override;
void HideActivePopup() override;
bool CloseOverflowMenuIfOpen() override;
void PopOutAction(const extensions::ExtensionId& action_id,
base::OnceClosure closure) override;
bool ShowToolbarActionPopupForAPICall(const std::string& action_id,
ShowPopupCallback callback) override;
void ToggleExtensionsMenu() override;
bool HasAnyExtensions() const override;
void UpdateToolbarActionHoverCard(
ToolbarActionView* action_view,
ToolbarActionHoverCardUpdateType update_type) override;
void CollapseConfirmation() override;
// ToolbarActionView::Delegate:
content::WebContents* GetCurrentWebContents() override;
views::LabelButton* GetOverflowReferenceView() const override;
gfx::Size GetToolbarActionSize() override;
void MovePinnedActionBy(const std::string& action_id, int move_by) override;
void WriteDragDataForView(View* sender,
const gfx::Point& press_pt,
ui::OSExchangeData* data) override;
int GetDragOperationsForView(View* sender, const gfx::Point& p) override;
bool CanStartDragForView(View* sender,
const gfx::Point& press_pt,
const gfx::Point& p) override;
private:
friend class ToolbarActionHoverCardBubbleViewUITest;
// A struct representing the position and action being dragged.
struct DropInfo;
// Pairing of widgets associated with this container and the extension they
// are associated with. This is used to keep track of icons that are popped
// out due to a widget showing (or being queued to show).
struct AnchoredWidget {
raw_ptr<views::Widget> widget;
std::string extension_id;
};
// Hides the currently-showing extensions menu, if it exists.
// TODO(crbug.com/40811196): This method will be removed once extensions menu
// under kExtensionsMenuAccessControl feature is fully rolled out and we can
// call directly into the menu coordinator.
void HideExtensionsMenu();
// Determines whether an action must be visible (i.e. cannot be hidden for any
// reason). Returns true if the action is popped out or has an attached
// bubble.
bool ShouldForceVisibility(const std::string& extension_id) const;
// Updates the view's visibility state according to
// IsActionVisibleOnToolbar(). Note that IsActionVisibleOnToolbar() does not
// return View visibility but whether the action should be visible or not
// (according to pin and pop-out state).
void UpdateIconVisibility(const std::string& extension_id);
// Set |widget|'s anchor (to the corresponding extension) and then show it.
// Posted from |ShowWidgetForExtension|.
void AnchorAndShowWidgetImmediately(MayBeDangling<views::Widget> widget);
// Creates an action and toolbar button for the corresponding ID.
void CreateActionForId(const ToolbarActionsModel::ActionId& action_id);
// Sorts child views to display them in the correct order (pinned actions,
// popped out actions, other buttons).
void ReorderAllChildViews();
// Utility function for going from width to icon counts.
size_t WidthToIconCount(int x_offset);
ui::ImageModel GetExtensionIcon(ToolbarActionView* extension_view);
// Sets a pinned extension button's image to be shown/hidden.
void SetExtensionIconVisibility(ToolbarActionsModel::ActionId id,
bool visible);
// Returns whether the container should be showing, e.g. not if there are no
// extensions installed, nor if the container is inactive in kAutoHide mode.
bool ShouldContainerBeVisible() const;
// Queues up a call to UpdateContainerVisibility() for when the current layout
// animation ends.
void UpdateContainerVisibilityAfterAnimation();
// Triggers the side panel to close.
void CloseSidePanelButtonPressed();
// views::WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override;
// Moves the dragged extension `action_id`.
void MovePinnedAction(
const ToolbarActionsModel::ActionId& action_id,
size_t index,
base::ScopedClosureRunner cleanup,
const ui::DropTargetEvent& event,
ui::mojom::DragOperation& output_drag_op,
std::unique_ptr<ui::LayerTreeOwner> drag_image_layer_owner);
// Performs clean up after dragging.
void DragDropCleanup(
const ToolbarActionsModel::ActionId& dragged_extension_id);
// Updates the vector icon used when the PrefChangeRegistrar listens to a
// change. When the side panel should open to the right side of the browser
// the default vector icon is used. When the side panel should open to the
// left side of the browser the flipped vector icon is used.
void UpdateCloseSidePanelButtonIcon();
const raw_ptr<Browser> browser_;
const raw_ptr<ToolbarActionsModel> model_;
// Coordinator to show and hide the ExtensionsMenuView.
const std::unique_ptr<ExtensionsMenuCoordinator> extensions_menu_coordinator_;
const raw_ptr<ExtensionsToolbarButton, AcrossTasksDanglingUntriaged>
extensions_button_;
raw_ptr<ExtensionsRequestAccessButton, DanglingUntriaged>
request_access_button_ = nullptr;
DisplayMode display_mode_;
// Controller for showing the toolbar action hover card.
std::unique_ptr<ToolbarActionHoverCardController>
action_hover_card_controller_;
// TODO(pbos): Create actions and icons only for pinned / popped out actions
// (lazily). Currently code expects GetActionForId() to return actions for
// extensions that aren't visible.
// Actions for all extensions.
std::vector<std::unique_ptr<ToolbarActionViewController>> actions_;
// View for every action, does not imply pinned or currently shown.
ToolbarIcons icons_;
// Popped-out extension, if any.
std::optional<extensions::ExtensionId> popped_out_action_;
// The action that triggered the current popup, if any.
raw_ptr<ToolbarActionViewController> popup_owner_ = nullptr;
// Extension with an open context menu, if any.
std::optional<extensions::ExtensionId> extension_with_open_context_menu_id_;
// View for closing the extension side panel.
raw_ptr<ToolbarButton> close_side_panel_button_ = nullptr;
// Used to ensure the button remains highlighted while active.
std::optional<views::Button::ScopedAnchorHighlight>
close_side_panel_button_anchor_highlight_;
// The widgets currently popped out and, for each, the extension it is
// associated with. See AnchoredWidget.
std::vector<AnchoredWidget> anchored_widgets_;
// The DropInfo for the current drag-and-drop operation, or a null pointer if
// there is none.
std::unique_ptr<DropInfo> drop_info_;
// Observes and listens to side panel alignment changes.
PrefChangeRegistrar pref_change_registrar_;
base::WeakPtrFactory<ExtensionsToolbarContainer> weak_ptr_factory_{this};
base::WeakPtrFactory<ExtensionsToolbarContainer> drop_weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSIONS_TOOLBAR_CONTAINER_H_