| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/browser_actions.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| #include "base/check_deref.h" |
| #include "base/check_op.h" |
| #include "base/functional/bind.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/devtools/devtools_window.h" |
| #include "chrome/browser/prefs/incognito_mode_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search_engines/template_url_service_factory.h" |
| #include "chrome/browser/sharing_hub/sharing_hub_features.h" |
| #include "chrome/browser/ui/actions/chrome_action_id.h" |
| #include "chrome/browser/ui/actions/chrome_actions.h" |
| #include "chrome/browser/ui/autofill/address_bubbles_icon_controller.h" |
| #include "chrome/browser/ui/autofill/autofill_bubble_base.h" |
| #include "chrome/browser/ui/autofill/payments/save_payment_icon_controller.h" |
| #include "chrome/browser/ui/bookmarks/bookmark_utils.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_action_prefs_listener.h" |
| #include "chrome/browser/ui/browser_command_controller.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_element_identifiers.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_features.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" |
| #include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h" |
| #include "chrome/browser/ui/customize_chrome/side_panel_controller.h" |
| #include "chrome/browser/ui/intent_picker_tab_helper.h" |
| #include "chrome/browser/ui/lens/lens_overlay_controller.h" |
| #include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h" |
| #include "chrome/browser/ui/lens/lens_string_utils.h" |
| #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" |
| #include "chrome/browser/ui/passwords/passwords_model_delegate.h" |
| #include "chrome/browser/ui/performance_controls/memory_saver_bubble_controller.h" |
| #include "chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h" |
| #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble.h" |
| #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_toolbar_icon_controller.h" |
| #include "chrome/browser/ui/tabs/public/tab_features.h" |
| #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h" |
| #include "chrome/browser/ui/toolbar/cast/cast_toolbar_button_util.h" |
| #include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/views/commerce/discounts_page_action_view_controller.h" |
| #include "chrome/browser/ui/views/commerce/product_specifications_page_action_view_controller.h" |
| #include "chrome/browser/ui/views/file_system_access/file_system_access_bubble_controller.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/frame/toolbar_button_provider.h" |
| #include "chrome/browser/ui/views/media_router/cast_browser_controller.h" |
| #include "chrome/browser/ui/views/page_info/page_info_view_factory.h" |
| #include "chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_toolbar_bubble_controller.h" |
| #include "chrome/browser/ui/views/side_panel/comments/comments_side_panel_coordinator.h" |
| #include "chrome/browser/ui/views/side_panel/history/history_side_panel_coordinator.h" |
| #include "chrome/browser/ui/views/side_panel/history_clusters/history_clusters_side_panel_utils.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_action_callback.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_entry_key.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_enums.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_ui.h" |
| #include "chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.h" |
| #include "chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_coordinator.h" |
| #include "chrome/browser/ui/views/toolbar/pinned_action_toolbar_button_menu_model.h" |
| #include "chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container.h" |
| #include "chrome/browser/ui/views/zoom/zoom_view_controller.h" |
| #include "chrome/browser/ui/web_applications/app_browser_controller.h" |
| #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h" |
| #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/grit/branded_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/collaboration/public/messaging/activity_log.h" |
| #include "components/commerce/core/metrics/discounts_metric_collector.h" |
| #include "components/lens/lens_features.h" |
| #include "components/media_router/browser/media_router_dialog_controller.h" |
| #include "components/media_router/browser/media_router_metrics.h" |
| #include "components/omnibox/browser/vector_icons.h" |
| #include "components/policy/core/common/policy_pref_names.h" |
| #include "components/search_engines/template_url.h" |
| #include "components/search_engines/template_url_service.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/tabs/public/tab_interface.h" |
| #include "components/user_prefs/user_prefs.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "ui/accessibility/accessibility_features.h" |
| #include "ui/actions/actions.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/gfx/text_utils.h" |
| #include "ui/gfx/vector_icon_types.h" |
| #include "ui/menus/simple_menu_model.h" |
| #include "ui/views/view.h" |
| #include "ui/views/view_class_properties.h" |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| #include "chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.h" |
| #endif |
| |
| namespace { |
| |
| actions::ActionItem::ActionItemBuilder ChromeMenuAction( |
| actions::ActionItem::InvokeActionCallback callback, |
| actions::ActionId action_id, |
| int title_id, |
| int tooltip_id, |
| const gfx::VectorIcon& icon) { |
| return actions::ActionItem::Builder(callback) |
| .SetActionId(action_id) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(title_id))) |
| .SetTooltipText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(tooltip_id))) |
| .SetImage(ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon)) |
| .SetProperty(actions::kActionItemPinnableKey, |
| std::underlying_type_t<actions::ActionPinnableState>( |
| actions::ActionPinnableState::kPinnable)); |
| } |
| |
| actions::StatefulImageActionItem::StatefulImageActionItemBuilder |
| StatefulChromeMenuAction(actions::ActionItem::InvokeActionCallback callback, |
| actions::ActionId action_id, |
| int title_id, |
| int tooltip_id, |
| const gfx::VectorIcon& icon) { |
| ui::ImageModel image = ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon); |
| return actions::StatefulImageActionItem::Builder(callback) |
| .SetActionId(action_id) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(title_id))) |
| .SetTooltipText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(tooltip_id))) |
| .SetImage(image) |
| .SetStatefulImage(image) |
| .SetProperty(actions::kActionItemPinnableKey, |
| std::underlying_type_t<actions::ActionPinnableState>( |
| actions::ActionPinnableState::kPinnable)); |
| } |
| |
| actions::ActionItem::ActionItemBuilder SidePanelAction( |
| SidePanelEntryId id, |
| int title_id, |
| int tooltip_id, |
| const gfx::VectorIcon& icon, |
| actions::ActionId action_id, |
| BrowserWindowInterface* bwi, |
| bool is_pinnable) { |
| auto pinnable_state = |
| is_pinnable ? std::underlying_type_t<actions::ActionPinnableState>( |
| actions::ActionPinnableState::kPinnable) |
| : std::underlying_type_t<actions::ActionPinnableState>( |
| actions::ActionPinnableState::kNotPinnable); |
| return actions::ActionItem::Builder( |
| CreateToggleSidePanelActionCallback(SidePanelEntryKey(id), bwi)) |
| .SetActionId(action_id) |
| .SetText(l10n_util::GetStringUTF16(title_id)) |
| .SetTooltipText(l10n_util::GetStringUTF16(tooltip_id)) |
| .SetImage(ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon)) |
| .SetProperty(actions::kActionItemPinnableKey, pinnable_state); |
| } |
| } // namespace |
| |
| BrowserActions::BrowserActions(BrowserWindowInterface* bwi) |
| : bwi_(CHECK_DEREF(bwi)), profile_(CHECK_DEREF(bwi->GetProfile())) {} |
| |
| BrowserActions::~BrowserActions() { |
| browser_action_prefs_listener_.reset(); |
| |
| // Extract the unique ptr and destruct it after the raw_ptr to avoid a |
| // dangling pointer scenario. |
| std::unique_ptr<actions::ActionItem> owned_root_action_item = |
| actions::ActionManager::Get().RemoveAction(root_action_item_); |
| root_action_item_ = nullptr; |
| } |
| |
| // static |
| std::u16string BrowserActions::GetCleanTitleAndTooltipText( |
| std::u16string string) { |
| static constexpr std::u16string_view kEllipsisUnicode{u"\u2026"}; |
| static constexpr std::u16string_view kEllipsisText{u"..."}; |
| |
| const auto remove_ellipsis = [&string](const std::u16string_view ellipsis) { |
| const size_t ellipsis_pos = string.find(ellipsis); |
| if (ellipsis_pos != std::u16string::npos) { |
| string.erase(ellipsis_pos); |
| } |
| }; |
| remove_ellipsis(kEllipsisUnicode); |
| remove_ellipsis(kEllipsisText); |
| return gfx::RemoveAccelerator(string); |
| } |
| |
| void BrowserActions::InitializeBrowserActions() { |
| Profile* const profile = base::to_address(profile_); |
| TabStripModel* const tab_strip_model = bwi_->GetTabStripModel(); |
| BrowserWindowInterface* const bwi = base::to_address(bwi_); |
| const bool is_guest_session = profile_->IsGuestSession(); |
| |
| actions::ActionManager::Get().AddAction( |
| actions::ActionItem::Builder() |
| .CopyAddressTo(&root_action_item_) |
| .AddChildren( |
| SidePanelAction( |
| SidePanelEntryId::kBookmarks, IDS_BOOKMARK_MANAGER_TITLE, |
| IDS_BOOKMARK_MANAGER_TITLE, kBookmarksSidePanelRefreshIcon, |
| kActionSidePanelShowBookmarks, bwi, true), |
| SidePanelAction(SidePanelEntryId::kReadingList, |
| IDS_READ_LATER_TITLE, IDS_READ_LATER_TITLE, |
| kReadingListIcon, kActionSidePanelShowReadingList, |
| bwi, true), |
| SidePanelAction(SidePanelEntryId::kAboutThisSite, |
| IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE, |
| IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE, |
| PageInfoViewFactory::GetAboutThisSiteVectorIcon(), |
| kActionSidePanelShowAboutThisSite, bwi, false), |
| SidePanelAction(SidePanelEntryId::kCustomizeChrome, |
| IDS_SIDE_PANEL_CUSTOMIZE_CHROME_TITLE, |
| IDS_SIDE_PANEL_CUSTOMIZE_CHROME_TITLE, |
| vector_icons::kEditChromeRefreshIcon, |
| kActionSidePanelShowCustomizeChrome, bwi, false), |
| SidePanelAction(SidePanelEntryId::kShoppingInsights, |
| IDS_SHOPPING_INSIGHTS_SIDE_PANEL_TITLE, |
| IDS_SHOPPING_INSIGHTS_SIDE_PANEL_TITLE, |
| vector_icons::kShoppingBagIcon, |
| kActionSidePanelShowShoppingInsights, bwi, false), |
| SidePanelAction(SidePanelEntryId::kMerchantTrust, |
| IDS_MERCHANT_TRUST_SIDE_PANEL_TITLE, |
| IDS_MERCHANT_TRUST_SIDE_PANEL_TITLE, |
| vector_icons::kStorefrontIcon, |
| kActionSidePanelShowMerchantTrust, bwi, false)) |
| .Build()); |
| |
| if (side_panel::history_clusters:: |
| IsHistoryClustersSidePanelSupportedForProfile(profile) && |
| !HistorySidePanelCoordinator::IsSupported()) { |
| root_action_item_->AddChild( |
| SidePanelAction(SidePanelEntryId::kHistoryClusters, IDS_HISTORY_TITLE, |
| IDS_HISTORY_CLUSTERS_SHOW_SIDE_PANEL, |
| vector_icons::kHistoryChromeRefreshIcon, |
| kActionSidePanelShowHistoryCluster, bwi, true) |
| .Build()); |
| } |
| |
| if (HistorySidePanelCoordinator::IsSupported()) { |
| root_action_item_->AddChild( |
| SidePanelAction(SidePanelEntryId::kHistory, IDS_HISTORY_TITLE, |
| IDS_HISTORY_SHOW_SIDE_PANEL, |
| vector_icons::kHistoryChromeRefreshIcon, |
| kActionSidePanelShowHistory, bwi, true) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| SidePanelAction(SidePanelEntryId::kReadAnything, IDS_READING_MODE_TITLE, |
| IDS_READING_MODE_TITLE, kMenuBookChromeRefreshIcon, |
| kActionSidePanelShowReadAnything, bwi, true) |
| .Build()); |
| |
| if (lens::features::IsLensOverlayEnabled()) { |
| actions::ActionItem::InvokeActionCallback callback = base::BindRepeating( |
| [](base::WeakPtr<BrowserWindowInterface> bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| if (!bwi) { |
| return; |
| } |
| lens::LensOverlayEntryPointController::InvokeAction( |
| bwi->GetActiveTabInterface(), context); |
| }, |
| bwi->GetWeakPtr()); |
| const gfx::VectorIcon& icon = |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| vector_icons::kGoogleLensMonochromeLogoIcon; |
| #else |
| vector_icons::kSearchChromeRefreshIcon; |
| #endif |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder(callback) |
| .SetActionId(kActionSidePanelShowLensOverlayResults) |
| .SetText(l10n_util::GetStringUTF16( |
| lens::GetLensOverlayEntrypointLabelAltIds( |
| IDS_SHOW_LENS_OVERLAY))) |
| .SetTooltipText(l10n_util::GetStringUTF16( |
| lens::GetLensOverlayEntrypointLabelAltIds( |
| IDS_SIDE_PANEL_LENS_OVERLAY_TOOLBAR_TOOLTIP))) |
| .SetImage(ui::ImageModel::FromVectorIcon( |
| icon, ui::kColorIcon, ui::SimpleMenuModel::kDefaultIconSize)) |
| .SetProperty(actions::kActionItemPinnableKey, |
| std::underlying_type_t<actions::ActionPinnableState>( |
| actions::ActionPinnableState::kPinnable)) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::ShowOffersAndRewardsForPage(bwi); |
| }, |
| bwi)) |
| .SetActionId(kActionOffersAndRewardsForPage) |
| .SetText(l10n_util::GetStringUTF16( |
| IDS_AUTOFILL_OFFERS_REMINDER_ICON_TOOLTIP_TEXT)) |
| .SetTooltipText(l10n_util::GetStringUTF16( |
| IDS_AUTOFILL_OFFERS_REMINDER_ICON_TOOLTIP_TEXT)) |
| .SetImage(ui::ImageModel::FromVectorIcon( |
| kLocalOfferFlippedRefreshIcon, ui::kColorIcon, |
| ui::SimpleMenuModel::kDefaultIconSize)) |
| .Build()); |
| |
| // Create the lens action item. The icon and text are set appropriately in the |
| // lens side panel coordinator. They have default values here. |
| root_action_item_->AddChild( |
| SidePanelAction(SidePanelEntryId::kLens, IDS_LENS_DEFAULT_TITLE, |
| IDS_LENS_DEFAULT_TITLE, vector_icons::kImageSearchIcon, |
| kActionSidePanelShowLens, bwi, false) |
| .Build()); |
| |
| // TODO(crbug.com/435220196): Ideally this action would have |
| // MemorySaverBubbleController passed in as a dependency directly. |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* bubble_controller = |
| bwi->GetFeatures().memory_saver_bubble_controller(); |
| bubble_controller->InvokeAction(bwi, item); |
| }, |
| bwi)) |
| .SetActionId(kActionShowMemorySaverChip) |
| .SetText(l10n_util::GetStringUTF16(IDS_MEMORY_SAVER_CHIP_LABEL)) |
| .SetTooltipText( |
| l10n_util::GetStringUTF16(IDS_MEMORY_SAVER_CHIP_ACCNAME)) |
| .SetImage(ui::ImageModel::FromVectorIcon( |
| kPerformanceSpeedometerIcon, ui::kColorIcon, |
| ui::SimpleMenuModel::kDefaultIconSize)) |
| .SetEnabled(true) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| bwi->GetActiveTabInterface() |
| ->GetTabFeatures() |
| ->zoom_view_controller() |
| ->UpdateBubbleVisibility( |
| /*prefer_to_show_bubble=*/true, |
| /*from_user_gesture=*/true); |
| }, |
| bwi)) |
| .SetActionId(kActionZoomNormal) |
| .SetText(l10n_util::GetStringUTF16(IDS_ZOOM_NORMAL)) |
| .SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_ZOOM)) |
| .SetImage(ui::ImageModel::FromVectorIcon(kZoomInIcon)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* tab_helper = bwi->GetActiveTabInterface() |
| ->GetTabFeatures() |
| ->commerce_ui_tab_helper(); |
| CHECK(tab_helper); |
| |
| tab_helper->OnPriceInsightsIconClicked(); |
| }, |
| bwi)) |
| .SetActionId(kActionCommercePriceInsights) |
| // The tooltip text is used as a default text. The |
| // PriceInsightsPageActionViewController will override it based on its |
| // state. |
| .SetText(l10n_util::GetStringUTF16( |
| IDS_SHOPPING_INSIGHTS_ICON_TOOLTIP_TEXT)) |
| .SetTooltipText(l10n_util::GetStringUTF16( |
| IDS_SHOPPING_INSIGHTS_ICON_TOOLTIP_TEXT)) |
| .SetImage(ui::ImageModel::FromVectorIcon( |
| vector_icons::kShoppingBagRefreshIcon)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* tab_features = |
| bwi->GetActiveTabInterface()->GetTabFeatures(); |
| CHECK(tab_features); |
| |
| tab_features->commerce_discounts_page_action_view_controller() |
| ->MaybeShowBubble(/*from_user=*/true); |
| |
| auto* commerce_ui_tab_helper = |
| tab_features->commerce_ui_tab_helper(); |
| CHECK(commerce_ui_tab_helper); |
| |
| commerce::metrics::DiscountsMetricCollector:: |
| RecordDiscountsPageActionIconClicked( |
| commerce_ui_tab_helper->IsPageActionIconExpanded( |
| PageActionIconType::kDiscounts), |
| commerce_ui_tab_helper->GetDiscounts()); |
| }, |
| bwi)) |
| .SetActionId(kActionCommerceDiscounts) |
| .SetText(l10n_util::GetStringUTF16(IDS_DISCOUNT_ICON_EXPANDED_TEXT)) |
| .SetTooltipText( |
| l10n_util::GetStringUTF16(IDS_DISCOUNT_ICON_EXPANDED_TEXT)) |
| .SetImage( |
| ui::ImageModel::FromVectorIcon(vector_icons::kShoppingmodeIcon)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* tab_features = |
| bwi->GetActiveTabInterface()->GetTabFeatures(); |
| CHECK(tab_features); |
| |
| tab_features |
| ->commerce_product_specifications_page_action_view_controller() |
| ->ShowConfirmationToast(); |
| }, |
| bwi)) |
| .SetActionId(kActionCommerceProductSpecifications) |
| .SetText( |
| l10n_util::GetStringUTF16(IDS_COMPARE_PAGE_ACTION_ADD_DEFAULT)) |
| .SetTooltipText( |
| l10n_util::GetStringUTF16(IDS_COMPARE_PAGE_ACTION_ADD_DEFAULT)) |
| .SetImage(ui::ImageModel::FromVectorIcon( |
| omnibox::kProductSpecificationsAddIcon)) |
| .Build()); |
| |
| // Clicking the Mandatory Reauth page action is a no-op. This is because the |
| // icon is always shown with a dialog bubble. The expected behavior is to |
| // simply close this bubble, which happens automatically due to focus change |
| // when the user clicks the icon. Therefore, a `base::DoNothing()` callback is |
| // used. |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder(base::DoNothing()) |
| .SetActionId(kActionAutofillMandatoryReauth) |
| .SetTooltipText(l10n_util::GetStringUTF16( |
| IDS_AUTOFILL_MANDATORY_REAUTH_ICON_TOOLTIP)) |
| .SetImage( |
| ui::ImageModel::FromVectorIcon(kCreditCardChromeRefreshIcon)) |
| .Build()); |
| |
| //------- Chrome Menu Actions --------// |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](Profile* profile, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| CHECK(IncognitoModePrefs::IsIncognitoAllowed(profile)); |
| chrome::NewIncognitoWindow(profile); |
| }, |
| profile), |
| kActionNewIncognitoWindow, IDS_NEW_INCOGNITO_WINDOW, |
| IDS_NEW_INCOGNITO_WINDOW, kIncognitoRefreshMenuIcon) |
| .SetEnabled(IncognitoModePrefs::IsIncognitoAllowed(profile)) |
| .Build()); |
| |
| if (features::HasTabSearchToolbarButton()) { |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::ShowTabSearch(bwi); |
| }, |
| bwi), |
| kActionTabSearch, IDS_TAB_SEARCH_MENU, IDS_TAB_SEARCH_MENU, |
| vector_icons::kTabSearchIcon) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::Print(bwi); |
| }, |
| bwi), |
| kActionPrint, IDS_PRINT, IDS_PRINT, kPrintMenuIcon) |
| .SetEnabled(chrome::CanPrint(bwi)) |
| .Build()); |
| |
| const bool is_incognito = profile_->IsIncognitoProfile(); |
| root_action_item_->AddChild( |
| ChromeMenuAction(base::BindRepeating( |
| [](BrowserWindowInterface* bwi, bool is_incognito, |
| actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| Browser* const browser_for_opening_webui = |
| bwi->GetBrowserForMigrationOnly() |
| ->GetBrowserForOpeningWebUi(); |
| if (is_incognito) { |
| chrome::ShowIncognitoClearBrowsingDataDialog( |
| browser_for_opening_webui); |
| } else { |
| chrome::ShowClearBrowsingDataDialog( |
| browser_for_opening_webui); |
| } |
| }, |
| bwi, is_incognito), |
| kActionClearBrowsingData, IDS_CLEAR_BROWSING_DATA, |
| IDS_CLEAR_BROWSING_DATA, kTrashCanRefreshIcon) |
| .SetEnabled(is_incognito || |
| (!is_guest_session && !profile->IsSystemProfile())) |
| .Build()); |
| |
| if (chrome::CanOpenTaskManager()) { |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::OpenTaskManager(bwi); |
| }, |
| bwi), |
| kActionTaskManager, IDS_TASK_MANAGER, IDS_TASK_MANAGER, |
| kTaskManagerIcon) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::ToggleDevToolsWindow( |
| bwi, DevToolsToggleAction::Show(), |
| DevToolsOpenedByAction::kPinnedToolbarButton); |
| }, |
| bwi), |
| kActionDevTools, IDS_DEV_TOOLS, IDS_DEV_TOOLS, kDeveloperToolsIcon) |
| .Build()); |
| |
| if (send_tab_to_self::SendTabToSelfToolbarIconController::CanShowOnBrowser( |
| bwi)) { |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](send_tab_to_self::SendTabToSelfToolbarBubbleController* |
| bubble_controller, |
| TabStripModel* tab_strip_model, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| if (bubble_controller->IsBubbleShowing()) { |
| bubble_controller->HideBubble(); |
| } else { |
| send_tab_to_self::ShowBubble( |
| tab_strip_model->GetActiveWebContents()); |
| } |
| }, |
| bwi->GetFeatures().send_tab_to_self_toolbar_bubble_controller(), |
| tab_strip_model), |
| kActionSendTabToSelf, IDS_SEND_TAB_TO_SELF, IDS_SEND_TAB_TO_SELF, |
| kDevicesChromeRefreshIcon) |
| .SetEnabled(chrome::CanSendTabToSelf(bwi)) |
| .SetVisible(!sharing_hub::SharingIsDisabledByPolicy(profile)) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::ShowTranslateBubble(bwi); |
| }, |
| bwi), |
| kActionShowTranslate, IDS_SHOW_TRANSLATE, IDS_TOOLTIP_TRANSLATE, |
| kTranslateIcon) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::GenerateQRCode(bwi); |
| }, |
| bwi), |
| kActionQrCodeGenerator, IDS_APP_MENU_CREATE_QR_CODE, |
| IDS_APP_MENU_CREATE_QR_CODE, kQrCodeChromeRefreshIcon) |
| .SetEnabled(false) |
| .SetVisible(!sharing_hub::SharingIsDisabledByPolicy(profile)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, TabStripModel* tab_strip_model, |
| actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* controller = autofill::AddressBubblesIconController::Get( |
| tab_strip_model->GetActiveWebContents()); |
| if (controller && controller->GetBubbleView()) { |
| controller->GetBubbleView()->Hide(); |
| } else { |
| chrome::ShowAddresses(bwi); |
| } |
| }, |
| bwi, tab_strip_model), |
| kActionShowAddressesBubbleOrPage, |
| IDS_ADDRESSES_AND_MORE_SUBMENU_OPTION, |
| IDS_ADDRESSES_AND_MORE_SUBMENU_OPTION, |
| vector_icons::kLocationOnChromeRefreshIcon) |
| .SetEnabled(!is_guest_session) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, TabStripModel* tab_strip_model, |
| actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto hide_bubble = [tab_strip_model](int command_id) -> bool { |
| auto* controller = autofill::SavePaymentIconController::Get( |
| tab_strip_model->GetActiveWebContents(), command_id); |
| if (controller && controller->GetPaymentBubbleView()) { |
| controller->GetPaymentBubbleView()->Hide(); |
| return true; |
| } |
| return false; |
| }; |
| const bool bubble_hidden = |
| hide_bubble(IDC_SAVE_CREDIT_CARD_FOR_PAGE) || |
| hide_bubble(IDC_SAVE_IBAN_FOR_PAGE); |
| if (!bubble_hidden) { |
| chrome::ShowPaymentMethods(bwi); |
| } |
| }, |
| bwi, tab_strip_model), |
| kActionShowPaymentsBubbleOrPage, IDS_PAYMENT_METHOD_SUBMENU_OPTION, |
| IDS_PAYMENT_METHOD_SUBMENU_OPTION, kCreditCardChromeRefreshIcon) |
| .SetEnabled(!is_guest_session) |
| .Build()); |
| |
| // TODO(crbug.com/435220196): Ideally this action would have |
| // ChromeLabsCoordinator passed in as a dependency directly. |
| if (IsChromeLabsEnabled() && !bwi->GetAppBrowserController()) { |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| bwi->GetFeatures().chrome_labs_coordinator()->ShowOrHide(); |
| }, |
| bwi), |
| kActionShowChromeLabs, IDS_CHROMELABS, IDS_CHROMELABS, kScienceIcon) |
| .SetVisible(ShouldShowChromeLabsUI(profile)) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, TabStripModel* tab_strip_model, |
| actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| content::WebContents* const web_contents = |
| tab_strip_model->GetActiveWebContents(); |
| if (PasswordsModelDelegateFromWebContents(web_contents) |
| ->GetState() == password_manager::ui::INACTIVE_STATE) { |
| chrome::ShowPasswordManager(bwi); |
| } else { |
| auto* const controller = |
| ManagePasswordsUIController::FromWebContents( |
| web_contents); |
| if (controller->IsShowingBubble()) { |
| controller->HideBubble(); |
| } else { |
| chrome::ManagePasswordsForPage(bwi); |
| } |
| } |
| }, |
| bwi, tab_strip_model), |
| kActionShowPasswordsBubbleOrPage, IDS_VIEW_PASSWORDS, |
| IDS_VIEW_PASSWORDS, vector_icons::kPasswordManagerIcon) |
| .SetEnabled(!is_guest_session) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](TabStripModel* tab_strip_model, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| content::WebContents* const web_contents = |
| tab_strip_model->GetActiveWebContents(); |
| const GURL& url = chrome::GetURLToBookmark(web_contents); |
| IntentPickerTabHelper* const intent_picker_tab_helper = |
| IntentPickerTabHelper::FromWebContents(web_contents); |
| CHECK(intent_picker_tab_helper); |
| intent_picker_tab_helper->ShowIntentPickerBubbleOrLaunchApp( |
| url); |
| }, |
| tab_strip_model)) |
| .SetActionId(kActionShowIntentPicker) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(IDS_TOOLTIP_INTENT_PICKER_ICON))) |
| .SetTooltipText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(IDS_TOOLTIP_INTENT_PICKER_ICON))) |
| .SetImage(ui::ImageModel::FromVectorIcon(kOpenInNewChromeRefreshIcon, |
| ui::kColorIcon)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](TabStripModel* tab_strip_model, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| // Show the File System Access bubble if applicable for |
| // the current page state. |
| FileSystemAccessBubbleController::Show( |
| tab_strip_model->GetActiveWebContents()); |
| }, |
| tab_strip_model)) |
| .SetActionId(kActionShowFileSystemAccess) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16( |
| IDS_FILE_SYSTEM_ACCESS_WRITE_USAGE_TOOLTIP))) |
| .SetTooltipText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16( |
| IDS_FILE_SYSTEM_ACCESS_WRITE_USAGE_TOOLTIP))) |
| .SetImage(ui::ImageModel::FromVectorIcon(kFileSaveChromeRefreshIcon, |
| ui::kColorIcon)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, TabStripModel* tab_strip_model, |
| actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::CopyURL(bwi, tab_strip_model->GetActiveWebContents()); |
| }, |
| bwi, tab_strip_model), |
| kActionCopyUrl, IDS_APP_MENU_COPY_LINK, IDS_APP_MENU_COPY_LINK, |
| kLinkChromeRefreshIcon) |
| .SetEnabled(chrome::CanCopyUrl(bwi)) |
| .SetVisible(!sharing_hub::SharingIsDisabledByPolicy(profile)) |
| .Build()); |
| |
| // TODO(crbug.com/435220196): Ideally this action would have |
| // CastBrowserController passed in as a dependency directly. |
| actions::ActionItem* media_router_action; |
| root_action_item_->AddChild( |
| StatefulChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| // TODO(crbug.com/356468503): Figure out how to capture |
| // action invocation location. |
| auto* cast_browser_controller = |
| bwi->GetFeatures().cast_browser_controller(); |
| if (cast_browser_controller) { |
| cast_browser_controller->ToggleDialog(); |
| } |
| }, |
| bwi), |
| kActionRouteMedia, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE, |
| IDS_MEDIA_ROUTER_ICON_TOOLTIP_TEXT, kCastChromeRefreshIcon) |
| .SetEnabled(chrome::CanRouteMedia(bwi)) |
| .CopyAddressTo(&media_router_action) |
| .Build()); |
| CastToolbarButtonUtil::AddCastChildActions(media_router_action, bwi); |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // TODO(crbug.com/435220196): Ideally this action would have |
| // DownloadToolbarUIController passed in as a dependency directly. |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| bwi->GetFeatures().download_toolbar_ui_controller()->InvokeUI(); |
| }, |
| bwi), |
| kActionShowDownloads, IDS_SHOW_DOWNLOADS, IDS_TOOLTIP_DOWNLOAD_ICON, |
| kDownloadToolbarButtonChromeRefreshIcon) |
| .Build()); |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| if (tab_groups::SavedTabGroupUtils::SupportsSharedTabGroups()) { |
| root_action_item_->AddChild( |
| ChromeMenuAction( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::OpenFeedbackDialog( |
| bwi, feedback::kFeedbackSourceDesktopTabGroups, |
| /*description_template=*/std::string(), |
| /*category_tag=*/"tab_group_share"); |
| }, |
| bwi), |
| kActionSendSharedTabGroupFeedback, |
| IDS_DATA_SHARING_SHARED_GROUPS_FEEDBACK, |
| IDS_DATA_SHARING_SHARED_GROUPS_FEEDBACK, |
| vector_icons::kFeedbackIcon) |
| .Build()); |
| } |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| ToolbarButtonProvider* toolbar_button_provider = |
| bwi->GetBrowserForMigrationOnly() |
| ->GetBrowserView() |
| .toolbar_button_provider(); |
| CHECK(toolbar_button_provider); |
| |
| views::View* page_action_view = |
| toolbar_button_provider->GetPageActionView( |
| kActionShowCollaborationRecentActivity); |
| CHECK(page_action_view); |
| |
| tabs::TabInterface* tab = bwi->GetActiveTabInterface(); |
| CHECK(tab); |
| |
| Profile* profile = bwi->GetProfile(); |
| CHECK(profile); |
| |
| RecentActivityBubbleCoordinator* bubble_coordinator = |
| RecentActivityBubbleCoordinator::From(bwi); |
| CHECK(bubble_coordinator); |
| |
| const std::optional<tab_groups::TabGroupId> group = |
| tab->GetGroup(); |
| CHECK(group.has_value()); |
| |
| const tab_groups::TabGroupId group_id = group.value(); |
| int32_t tab_id = tab->GetHandle().raw_value(); |
| auto* web_contents = tab->GetContents(); |
| |
| const std::vector<collaboration::messaging::ActivityLogItem> |
| tab_activity_log = |
| tab_groups::SavedTabGroupUtils::GetRecentActivity( |
| profile, group_id, tab_id); |
| const std::vector<collaboration::messaging::ActivityLogItem> |
| group_activity_log = |
| tab_groups::SavedTabGroupUtils::GetRecentActivity( |
| profile, group_id); |
| |
| bubble_coordinator->ShowForCurrentTab( |
| page_action_view, web_contents, tab_activity_log, |
| group_activity_log, profile); |
| }, |
| bwi)) |
| .SetActionId(kActionShowCollaborationRecentActivity) |
| .SetImage(ui::ImageModel().FromVectorIcon( |
| kPersonFilledPaddedSmallIcon, ui::kColorIcon)) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* toolbar_button_provider = |
| bwi->GetBrowserForMigrationOnly() |
| ->GetBrowserView() |
| .toolbar_button_provider(); |
| if (toolbar_button_provider) { |
| toolbar_button_provider->GetPinnedToolbarActionsContainer() |
| ->UpdatePinnedStateAndAnnounce( |
| context.GetProperty(kActionIdKey), true); |
| } |
| }, |
| bwi)) |
| .SetActionId(kActionPinActionToToolbar) |
| .SetImage(ui::ImageModel::FromVectorIcon(kKeepIcon, ui::kColorIcon)) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16( |
| IDS_SIDE_PANEL_TOOLBAR_BUTTON_CXMENU_PIN))) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| auto* toolbar_button_provider = |
| bwi->GetBrowserForMigrationOnly() |
| ->GetBrowserView() |
| .toolbar_button_provider(); |
| if (toolbar_button_provider) { |
| toolbar_button_provider->GetPinnedToolbarActionsContainer() |
| ->UpdatePinnedStateAndAnnounce( |
| context.GetProperty(kActionIdKey), false); |
| } |
| }, |
| bwi)) |
| .SetActionId(kActionUnpinActionFromToolbar) |
| .SetImage( |
| ui::ImageModel::FromVectorIcon(kKeepOffIcon, ui::kColorIcon)) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16( |
| IDS_SIDE_PANEL_TOOLBAR_BUTTON_CXMENU_UNPIN))) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| chrome::ExecuteCommand(bwi, IDC_SHOW_CUSTOMIZE_CHROME_TOOLBAR); |
| }, |
| bwi)) |
| .SetActionId(kActionSidePanelShowCustomizeChromeToolbar) |
| .SetImage( |
| ui::ImageModel::FromVectorIcon(kSettingsMenuIcon, ui::kColorIcon)) |
| .SetText(BrowserActions::GetCleanTitleAndTooltipText( |
| l10n_util::GetStringUTF16(IDS_SHOW_CUSTOMIZE_CHROME_TOOLBAR))) |
| .Build()); |
| |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| web_app::ShowPwaInstallDialog(bwi); |
| }, |
| bwi)) |
| .SetActionId(kActionInstallPwa) |
| .SetImage(ui::ImageModel::FromVectorIcon( |
| kInstallDesktopChromeRefreshIcon, ui::kColorIcon)) |
| .SetProperty(actions::kActionItemPinnableKey, false) |
| // Text and TooltipText are not populated yet because they are |
| // dynamic. They depend on the current tab WebContents. |
| .Build()); |
| |
| // Actions that do not directly show up in chrome UI. |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| bwi->GetBrowserForMigrationOnly()->GetBrowserView().Cut(); |
| }, |
| bwi)) |
| .SetActionId(actions::kActionCut) |
| .Build()); |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| bwi->GetBrowserForMigrationOnly()->GetBrowserView().Copy(); |
| }, |
| bwi)) |
| .SetActionId(actions::kActionCopy) |
| .Build()); |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](BrowserWindowInterface* bwi, actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| bwi->GetBrowserForMigrationOnly()->GetBrowserView().Paste(); |
| }, |
| bwi)) |
| .SetActionId(actions::kActionPaste) |
| .Build()); |
| root_action_item_->AddChild( |
| actions::ActionItem::Builder( |
| base::BindRepeating( |
| [](chrome::BrowserCommandController* browser_command_controller, |
| actions::ActionItem* item, |
| actions::ActionInvocationContext context) { |
| browser_command_controller->ShowCustomizeChromeSidePanel( |
| CustomizeChromeSection::kFooter); |
| }, |
| bwi->GetFeatures().browser_command_controller())) |
| .SetActionId(kActionSidePanelShowCustomizeChromeFooter) |
| .Build()); |
| |
| if (CommentsSidePanelCoordinator::IsSupported()) { |
| root_action_item_->AddChild( |
| SidePanelAction(SidePanelEntryId::kComments, |
| IDS_COLLABORATION_SHARED_TAB_GROUPS_COMMENTS_TITLE, |
| IDS_COLLABORATION_SHARED_TAB_GROUPS_COMMENTS_TITLE, |
| vector_icons::kChatIcon, kActionSidePanelShowComments, |
| bwi, false) |
| .Build()); |
| } |
| |
| AddListeners(); |
| } |
| |
| void BrowserActions::AddListeners() { |
| browser_action_prefs_listener_ = std::make_unique<BrowserActionPrefsListener>( |
| base::to_address(profile_), this); |
| } |