blob: 98fe0ca8e4ad57b04d5b9b1745629e783d12a50e [file] [log] [blame]
// 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_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/download/bubble/download_bubble_prefs.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_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/commerce/commerce_ui_tab_helper.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/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/download/bubble/download_toolbar_ui_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/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/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/common/chrome_features.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.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_class_properties.h"
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,
Browser* browser,
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), browser))
.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(Browser& browser) : browser_(browser) {}
BrowserActions::~BrowserActions() {
// 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) {
const std::u16string ellipsis_unicode = u"\u2026";
const std::u16string ellipsis_text = u"...";
auto remove_ellipsis = [&string](const std::u16string ellipsis) {
size_t ellipsis_pos = string.find(ellipsis);
if (ellipsis_pos != std::u16string::npos) {
string.erase(ellipsis_pos);
}
};
remove_ellipsis(ellipsis_unicode);
remove_ellipsis(ellipsis_text);
return gfx::RemoveAccelerator(string);
}
void BrowserActions::InitializeBrowserActions() {
Profile* profile = browser_->profile();
Browser* browser = &(browser_.get());
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, browser, true),
SidePanelAction(SidePanelEntryId::kReadingList,
IDS_READ_LATER_TITLE, IDS_READ_LATER_TITLE,
kReadingListIcon, kActionSidePanelShowReadingList,
browser, true),
SidePanelAction(SidePanelEntryId::kAboutThisSite,
IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE,
IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE,
PageInfoViewFactory::GetAboutThisSiteVectorIcon(),
kActionSidePanelShowAboutThisSite, browser,
false),
SidePanelAction(SidePanelEntryId::kCustomizeChrome,
IDS_SIDE_PANEL_CUSTOMIZE_CHROME_TITLE,
IDS_SIDE_PANEL_CUSTOMIZE_CHROME_TITLE,
vector_icons::kEditChromeRefreshIcon,
kActionSidePanelShowCustomizeChrome, browser,
false),
SidePanelAction(SidePanelEntryId::kShoppingInsights,
IDS_SHOPPING_INSIGHTS_SIDE_PANEL_TITLE,
IDS_SHOPPING_INSIGHTS_SIDE_PANEL_TITLE,
vector_icons::kShoppingBagIcon,
kActionSidePanelShowShoppingInsights, browser,
false),
SidePanelAction(SidePanelEntryId::kMerchantTrust,
IDS_MERCHANT_TRUST_SIDE_PANEL_TITLE,
IDS_MERCHANT_TRUST_SIDE_PANEL_TITLE,
vector_icons::kStorefrontIcon,
kActionSidePanelShowMerchantTrust, browser,
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, browser, true)
.Build());
}
if (HistorySidePanelCoordinator::IsSupported()) {
root_action_item_->AddChild(
SidePanelAction(SidePanelEntryId::kHistory, IDS_HISTORY_TITLE,
IDS_HISTORY_SHOW_SIDE_PANEL,
vector_icons::kHistoryChromeRefreshIcon,
kActionSidePanelShowHistory, browser, true)
.Build());
}
root_action_item_->AddChild(
SidePanelAction(SidePanelEntryId::kReadAnything, IDS_READING_MODE_TITLE,
IDS_READING_MODE_TITLE, kMenuBookChromeRefreshIcon,
kActionSidePanelShowReadAnything, browser, true)
.Build());
if (lens::features::IsLensOverlayEnabled()) {
actions::ActionItem::InvokeActionCallback callback = base::BindRepeating(
[](base::WeakPtr<Browser> browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
if (!browser) {
return;
}
lens::LensOverlayEntryPointController::InvokeAction(
browser->GetActiveTabInterface(), context);
},
browser->AsWeakPtr());
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(IDS_SHOW_LENS_OVERLAY))
.SetTooltipText(l10n_util::GetStringUTF16(
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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::ShowOffersAndRewardsForPage(browser);
},
base::Unretained(browser)))
.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, browser, false)
.Build());
root_action_item_->AddChild(
actions::ActionItem::Builder(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto* bubble_controller =
browser->browser_window_features()
->memory_saver_bubble_controller();
bubble_controller->InvokeAction(browser, item);
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
browser->GetActiveTabInterface()
->GetTabFeatures()
->zoom_view_controller()
->UpdateBubbleVisibility(
/*prefer_to_show_bubble=*/true,
/*from_user_gesture=*/true);
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto* tab_helper = browser->GetActiveTabInterface()
->GetTabFeatures()
->commerce_ui_tab_helper();
CHECK(tab_helper);
tab_helper->OnPriceInsightsIconClicked();
},
base::Unretained(browser)))
.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());
//------- Chrome Menu Actions --------//
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
CHECK(IncognitoModePrefs::IsIncognitoAllowed(
browser->profile()));
chrome::NewIncognitoWindow(browser->profile());
},
base::Unretained(browser)),
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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::ShowTabSearch(browser);
},
base::Unretained(browser)),
kActionTabSearch, IDS_TAB_SEARCH_MENU,
IDS_TAB_SEARCH_MENU, vector_icons::kTabSearchIcon)
.Build());
}
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::Print(browser);
},
base::Unretained(browser)),
kActionPrint, IDS_PRINT, IDS_PRINT, kPrintMenuIcon)
.SetEnabled(chrome::CanPrint(browser))
.Build());
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
if (browser->profile()->IsIncognitoProfile()) {
chrome::ShowIncognitoClearBrowsingDataDialog(
browser->GetBrowserForOpeningWebUi());
} else {
chrome::ShowClearBrowsingDataDialog(
browser->GetBrowserForOpeningWebUi());
}
},
base::Unretained(browser)),
kActionClearBrowsingData, IDS_CLEAR_BROWSING_DATA,
IDS_CLEAR_BROWSING_DATA, kTrashCanRefreshIcon)
.SetEnabled(
profile->IsIncognitoProfile() ||
(!profile->IsGuestSession() && !profile->IsSystemProfile()))
.Build());
if (chrome::CanOpenTaskManager()) {
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::OpenTaskManager(browser);
},
base::Unretained(browser)),
kActionTaskManager, IDS_TASK_MANAGER, IDS_TASK_MANAGER,
kTaskManagerIcon)
.Build());
}
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::ToggleDevToolsWindow(
browser, DevToolsToggleAction::Show(),
DevToolsOpenedByAction::kPinnedToolbarButton);
},
base::Unretained(browser)),
kActionDevTools, IDS_DEV_TOOLS, IDS_DEV_TOOLS,
kDeveloperToolsIcon)
.Build());
if (send_tab_to_self::SendTabToSelfToolbarIconController::CanShowOnBrowser(
browser)) {
root_action_item_->AddChild(
ChromeMenuAction(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto* bubble_controller =
browser->browser_window_features()
->send_tab_to_self_toolbar_bubble_controller();
if (bubble_controller->IsBubbleShowing()) {
bubble_controller->HideBubble();
} else {
send_tab_to_self::ShowBubble(
browser->tab_strip_model()->GetActiveWebContents());
}
},
base::Unretained(browser)),
kActionSendTabToSelf, IDS_SEND_TAB_TO_SELF, IDS_SEND_TAB_TO_SELF,
kDevicesChromeRefreshIcon)
.SetEnabled(chrome::CanSendTabToSelf(browser))
.SetVisible(
!sharing_hub::SharingIsDisabledByPolicy(browser->profile()))
.Build());
}
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::ShowTranslateBubble(browser);
},
base::Unretained(browser)),
kActionShowTranslate, IDS_SHOW_TRANSLATE,
IDS_TOOLTIP_TRANSLATE, kTranslateIcon)
.Build());
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::GenerateQRCode(browser);
},
base::Unretained(browser)),
kActionQrCodeGenerator, IDS_APP_MENU_CREATE_QR_CODE,
IDS_APP_MENU_CREATE_QR_CODE, kQrCodeChromeRefreshIcon)
.SetEnabled(false)
.SetVisible(
!sharing_hub::SharingIsDisabledByPolicy(browser->profile()))
.Build());
root_action_item_->AddChild(
ChromeMenuAction(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto* controller = autofill::AddressBubblesIconController::Get(
browser->tab_strip_model()->GetActiveWebContents());
if (controller && controller->GetBubbleView()) {
controller->GetBubbleView()->Hide();
} else {
chrome::ShowAddresses(browser);
}
},
base::Unretained(browser)),
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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto hide_bubble = [&browser](int command_id) -> bool {
auto* controller = autofill::SavePaymentIconController::Get(
browser->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(browser);
}
},
base::Unretained(browser)),
kActionShowPaymentsBubbleOrPage, IDS_PAYMENT_METHOD_SUBMENU_OPTION,
IDS_PAYMENT_METHOD_SUBMENU_OPTION, kCreditCardChromeRefreshIcon)
.SetEnabled(!is_guest_session)
.Build());
if (IsChromeLabsEnabled() &&
!web_app::AppBrowserController::IsWebApp(browser)) {
// TODO(354758327): Update `ShouldShowChromeLabsUI()` to not require
// `model` as a parameter, then use to set visibility of action item.
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
browser->window()->ShowChromeLabs();
},
base::Unretained(browser)),
kActionShowChromeLabs, IDS_CHROMELABS, IDS_CHROMELABS,
kScienceIcon)
.SetVisible(false)
.Build());
}
root_action_item_->AddChild(
ChromeMenuAction(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
if (PasswordsModelDelegateFromWebContents(
browser->tab_strip_model()->GetActiveWebContents())
->GetState() == password_manager::ui::INACTIVE_STATE) {
chrome::ShowPasswordManager(browser);
} else {
content::WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
auto* controller =
ManagePasswordsUIController::FromWebContents(
web_contents);
if (controller->IsShowingBubble()) {
controller->HidePasswordBubble();
} else {
chrome::ManagePasswordsForPage(browser);
}
}
},
base::Unretained(browser)),
kActionShowPasswordsBubbleOrPage, IDS_VIEW_PASSWORDS,
IDS_VIEW_PASSWORDS, vector_icons::kPasswordManagerIcon)
.SetEnabled(!is_guest_session)
.Build());
root_action_item_->AddChild(
actions::ActionItem::Builder(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
content::WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
const GURL& url = chrome::GetURLToBookmark(web_contents);
IntentPickerTabHelper* intent_picker_tab_helper =
IntentPickerTabHelper::FromWebContents(web_contents);
CHECK(intent_picker_tab_helper);
intent_picker_tab_helper->ShowIntentPickerBubbleOrLaunchApp(
url);
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
// Show the File System Access bubble if applicable for
// the current page state.
FileSystemAccessBubbleController::Show(browser);
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::CopyURL(
browser,
browser->tab_strip_model()->GetActiveWebContents());
},
base::Unretained(browser)),
kActionCopyUrl, IDS_APP_MENU_COPY_LINK, IDS_APP_MENU_COPY_LINK,
kLinkChromeRefreshIcon)
.SetEnabled(chrome::CanCopyUrl(browser))
.SetVisible(
!sharing_hub::SharingIsDisabledByPolicy(browser->profile()))
.Build());
if (base::FeatureList::IsEnabled(features::kPinnedCastButton)) {
actions::ActionItem* media_router_action;
root_action_item_->AddChild(
StatefulChromeMenuAction(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
// TODO(crbug.com/356468503): Figure out how to capture
// action invocation location.
auto* cast_browser_controller =
browser->browser_window_features()
->cast_browser_controller();
if (cast_browser_controller) {
cast_browser_controller->ToggleDialog();
}
},
base::Unretained(browser)),
kActionRouteMedia, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE,
IDS_MEDIA_ROUTER_ICON_TOOLTIP_TEXT, kCastChromeRefreshIcon)
.SetEnabled(chrome::CanRouteMedia(browser))
.CopyAddressTo(&media_router_action)
.Build());
CastToolbarButtonUtil::AddCastChildActions(media_router_action, browser);
}
if (download::IsDownloadBubbleEnabled()) {
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
browser->GetFeatures()
.download_toolbar_ui_controller()
->InvokeUI();
},
base::Unretained(browser)),
kActionShowDownloads, IDS_SHOW_DOWNLOADS,
IDS_TOOLTIP_DOWNLOAD_ICON,
kDownloadToolbarButtonChromeRefreshIcon)
.Build());
}
if (tab_groups::SavedTabGroupUtils::SupportsSharedTabGroups()) {
root_action_item_->AddChild(
ChromeMenuAction(base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::OpenFeedbackDialog(
browser,
feedback::kFeedbackSourceDesktopTabGroups,
/*description_template=*/std::string(),
/*category_tag=*/"tab_group_share");
},
base::Unretained(browser)),
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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto* toolbar_button_provider =
BrowserView::GetBrowserViewForBrowser(browser)
->toolbar_button_provider();
if (toolbar_button_provider) {
toolbar_button_provider->GetPinnedToolbarActionsContainer()
->UpdatePinnedStateAndAnnounce(
context.GetProperty(kActionIdKey), true);
}
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
auto* toolbar_button_provider =
BrowserView::GetBrowserViewForBrowser(browser)
->toolbar_button_provider();
if (toolbar_button_provider) {
toolbar_button_provider->GetPinnedToolbarActionsContainer()
->UpdatePinnedStateAndAnnounce(
context.GetProperty(kActionIdKey), false);
}
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
chrome::ExecuteCommand(browser,
IDC_SHOW_CUSTOMIZE_CHROME_TOOLBAR);
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
web_app::ShowPwaInstallDialog(browser);
},
base::Unretained(browser)))
.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(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
browser->GetBrowserView().Cut();
},
base::Unretained(browser)))
.SetActionId(actions::kActionCut)
.Build());
root_action_item_->AddChild(
actions::ActionItem::Builder(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
browser->GetBrowserView().Copy();
},
base::Unretained(browser)))
.SetActionId(actions::kActionCopy)
.Build());
root_action_item_->AddChild(
actions::ActionItem::Builder(
base::BindRepeating(
[](Browser* browser, actions::ActionItem* item,
actions::ActionInvocationContext context) {
browser->GetBrowserView().Paste();
},
base::Unretained(browser)))
.SetActionId(actions::kActionPaste)
.Build());
AddListeners();
}
void BrowserActions::RemoveListeners() {
browser_action_prefs_listener_.reset();
}
void BrowserActions::AddListeners() {
browser_action_prefs_listener_ =
std::make_unique<BrowserActionPrefsListener>(browser_.get());
}