From 4bffcdda6c502993d9e00d1bbda1c503c59d5f60 Mon Sep 17 00:00:00 2001 From: sergei Date: Tue, 6 Dec 2022 16:20:56 +0300 Subject: [PATCH] Add copy clean link to the macOS application menu bar --- browser/brave_app_controller_mac.h | 25 +++++ browser/brave_app_controller_mac.mm | 103 ++++++++++++++++++ browser/brave_app_controller_mac_unittest.mm | 77 +++++++++++++ browser/sources.gni | 2 + browser/ui/browser_commands.cc | 8 ++ browser/ui/browser_commands.h | 2 +- browser/ui/views/frame/brave_browser_view.cc | 5 + browser/ui/views/frame/brave_browser_view.h | 1 + .../chrome/browser/chrome_browser_main_mac.mm | 10 ++ .../render_view_context_menu.cc | 7 +- .../browser/ui/cocoa/main_menu_builder.mm | 13 +++ .../ui/views/controls/textfield/textfield.cc | 2 +- .../ui/views/controls/textfield/textfield.h | 2 +- .../textfield/textfield_controller.cc | 2 +- .../controls/textfield/textfield_controller.h | 2 +- test/BUILD.gn | 1 + 16 files changed, 254 insertions(+), 8 deletions(-) create mode 100644 browser/brave_app_controller_mac.h create mode 100644 browser/brave_app_controller_mac.mm create mode 100644 browser/brave_app_controller_mac_unittest.mm create mode 100644 chromium_src/chrome/browser/chrome_browser_main_mac.mm diff --git a/browser/brave_app_controller_mac.h b/browser/brave_app_controller_mac.h new file mode 100644 index 000000000000..7b8683e13093 --- /dev/null +++ b/browser/brave_app_controller_mac.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_BRAVE_APP_CONTROLLER_MAC_H_ +#define BRAVE_BROWSER_BRAVE_APP_CONTROLLER_MAC_H_ + +#import "chrome/browser/app_controller_mac.h" + +// Manages logic to switch hotkey between copy and copy clean link item. +@interface BraveAppController : AppController { + NSMenuItem* _copyMenuItem; + NSMenuItem* _copyCleanLinkMenuItem; + absl::optional _hasSelectedURLForTesting; +} + +// Testing API. +- (void)setCopyMenuItemForTesting:(NSMenuItem*)menuItem; +- (void)setCopyCleanLinkMenuItemForTesting:(NSMenuItem*)menuItem; +- (void)setSelectedURLForTesting:(bool)selected; + +@end + +#endif // BRAVE_BROWSER_BRAVE_APP_CONTROLLER_MAC_H_ diff --git a/browser/brave_app_controller_mac.mm b/browser/brave_app_controller_mac.mm new file mode 100644 index 000000000000..76cc1fc507d0 --- /dev/null +++ b/browser/brave_app_controller_mac.mm @@ -0,0 +1,103 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#import "brave/browser/brave_app_controller_mac.h" + +#include "brave/app/brave_command_ids.h" +#include "brave/browser/ui/browser_commands.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/grit/generated_resources.h" + +@implementation BraveAppController + +- (void)mainMenuCreated { + [super mainMenuCreated]; + + NSMenu* editMenu = [[[NSApp mainMenu] itemWithTag:IDC_EDIT_MENU] submenu]; + _copyMenuItem = [editMenu itemWithTag:IDC_CONTENT_CONTEXT_COPY]; + DCHECK(_copyMenuItem); + [[_copyMenuItem menu] setDelegate:self]; + _copyCleanLinkMenuItem = [editMenu itemWithTag:IDC_COPY_CLEAN_LINK]; + DCHECK(_copyCleanLinkMenuItem); + [[_copyCleanLinkMenuItem menu] setDelegate:self]; +} + +- (void)dealloc { + [[_copyMenuItem menu] setDelegate:nil]; + [[_copyCleanLinkMenuItem menu] setDelegate:nil]; + [super dealloc]; +} + +- (Browser*)getBrowser { + return chrome::FindBrowserWithProfile([self lastProfileIfLoaded]); +} + +- (BOOL)shouldShowCleanLinkItem { + if (_hasSelectedURLForTesting.has_value()) { + return _hasSelectedURLForTesting.value(); + } + return brave::HasSelectedURL([self getBrowser]); +} + +- (void)setKeyEquivalentToItem:(NSMenuItem*)item { + auto* hootkeyItem = + item == _copyMenuItem ? _copyMenuItem : _copyCleanLinkMenuItem; + auto* noHootkeyItem = + item == _copyMenuItem ? _copyCleanLinkMenuItem : _copyMenuItem; + + [hootkeyItem setKeyEquivalent:@"c"]; + [hootkeyItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand]; + + [noHootkeyItem setKeyEquivalent:@""]; + [noHootkeyItem setKeyEquivalentModifierMask:0]; +} + +- (void)menuNeedsUpdate:(NSMenu*)menu { + if (menu != [_copyMenuItem menu] && menu != [_copyCleanLinkMenuItem menu]) { + [super menuNeedsUpdate:menu]; + return; + } + if ([self shouldShowCleanLinkItem]) { + [_copyCleanLinkMenuItem setHidden:NO]; + [self setKeyEquivalentToItem:_copyCleanLinkMenuItem]; + } else { + [_copyCleanLinkMenuItem setHidden:YES]; + [self setKeyEquivalentToItem:_copyMenuItem]; + } +} + +- (BOOL)validateUserInterfaceItem:(id)item { + NSInteger tag = [item tag]; + if (tag == IDC_COPY_CLEAN_LINK) { + return [self shouldShowCleanLinkItem]; + } + return [super validateUserInterfaceItem:item]; +} + +- (void)commandDispatch:(id)sender { + NSInteger tag = [sender tag]; + if (tag == IDC_COPY_CLEAN_LINK) { + chrome::ExecuteCommand([self getBrowser], IDC_COPY_CLEAN_LINK); + return; + } + + [super commandDispatch:sender]; +} + +- (void)setCopyMenuItemForTesting:(NSMenuItem*)menuItem { + _copyMenuItem = menuItem; +} + +- (void)setCopyCleanLinkMenuItemForTesting:(NSMenuItem*)menuItem { + _copyCleanLinkMenuItem = menuItem; +} +- (void)setSelectedURLForTesting:(bool)value { + _hasSelectedURLForTesting = value; +} + +@end // @implementation BraveAppController diff --git a/browser/brave_app_controller_mac_unittest.mm b/browser/brave_app_controller_mac_unittest.mm new file mode 100644 index 000000000000..212e2d871ea9 --- /dev/null +++ b/browser/brave_app_controller_mac_unittest.mm @@ -0,0 +1,77 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#import + +#import "brave/browser/brave_app_controller_mac.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/platform_test.h" + +class BraveAppControllerTest : public PlatformTest { + protected: + BraveAppControllerTest() {} + + void SetUp() override { + PlatformTest::SetUp(); + braveAppController_.reset([[BraveAppController alloc] init]); + copyMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@"" + action:0 + keyEquivalent:@""]); + [braveAppController_ setCopyMenuItemForTesting:copyMenuItem_]; + + copyCleanLinkMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@"" + action:0 + keyEquivalent:@""]); + [braveAppController_ + setCopyCleanLinkMenuItemForTesting:copyCleanLinkMenuItem_]; + } + + void TearDown() override { + [braveAppController_ setCopyMenuItemForTesting:nil]; + [braveAppController_ setCopyCleanLinkMenuItemForTesting:nil]; + PlatformTest::TearDown(); + } + + void CheckHotkeysOnCopyItem() { + [braveAppController_ setSelectedURLForTesting:false]; + + [braveAppController_ menuNeedsUpdate:[copyMenuItem_ menu]]; + + EXPECT_TRUE([[copyMenuItem_ keyEquivalent] isEqualToString:@"c"]); + EXPECT_EQ([copyMenuItem_ keyEquivalentModifierMask], + NSEventModifierFlagCommand); + + EXPECT_TRUE([[copyCleanLinkMenuItem_ keyEquivalent] isEqualToString:@""]); + EXPECT_EQ([copyCleanLinkMenuItem_ keyEquivalentModifierMask], 0UL); + EXPECT_TRUE([copyCleanLinkMenuItem_ isHidden]); + } + + void CheckHotkeysOnCleanLinkItem() { + [braveAppController_ setSelectedURLForTesting:true]; + + [braveAppController_ menuNeedsUpdate:[copyMenuItem_ menu]]; + + EXPECT_TRUE([[copyMenuItem_ keyEquivalent] isEqualToString:@""]); + EXPECT_EQ([copyMenuItem_ keyEquivalentModifierMask], 0UL); + + EXPECT_TRUE([[copyCleanLinkMenuItem_ keyEquivalent] isEqualToString:@"c"]); + EXPECT_EQ([copyCleanLinkMenuItem_ keyEquivalentModifierMask], + NSEventModifierFlagCommand); + EXPECT_FALSE([copyCleanLinkMenuItem_ isHidden]); + } + + base::scoped_nsobject braveAppController_; + base::scoped_nsobject copyMenuItem_; + base::scoped_nsobject copyCleanLinkMenuItem_; + content::BrowserTaskEnvironment task_environment_; +}; + +TEST_F(BraveAppControllerTest, OnlyCopyItem) { + CheckHotkeysOnCopyItem(); +} + +TEST_F(BraveAppControllerTest, CleanLinkItemAdded) { + CheckHotkeysOnCleanLinkItem(); +} diff --git a/browser/sources.gni b/browser/sources.gni index 3bc568ebe2e2..29dfcd8f8344 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -211,6 +211,8 @@ brave_chrome_browser_deps = [ if (is_mac) { brave_chrome_browser_sources += [ + "//brave/browser/brave_app_controller_mac.h", + "//brave/browser/brave_app_controller_mac.mm", "//brave/browser/brave_browser_main_parts_mac.h", "//brave/browser/brave_browser_main_parts_mac.mm", "//brave/browser/brave_shell_integration_mac.h", diff --git a/browser/ui/browser_commands.cc b/browser/ui/browser_commands.cc index 28d7fd3635f1..cddd425227d1 100644 --- a/browser/ui/browser_commands.cc +++ b/browser/ui/browser_commands.cc @@ -271,4 +271,12 @@ void ToggleActiveTabAudioMute(Browser* browser) { std::string()); } +bool HasSelectedURL(Browser* browser) { +#if defined(TOOLKIT_VIEWS) + return static_cast(browser->window())->HasSelectedURL(); +#else + return false; +#endif +} + } // namespace brave diff --git a/browser/ui/browser_commands.h b/browser/ui/browser_commands.h index 6639473355b3..bcb23cf6491d 100644 --- a/browser/ui/browser_commands.h +++ b/browser/ui/browser_commands.h @@ -10,7 +10,7 @@ class Browser; class GURL; namespace brave { - +bool HasSelectedURL(Browser* browser); void NewOffTheRecordWindowTor(Browser*); void NewTorConnectionForSite(Browser*); void AddNewProfile(); diff --git a/browser/ui/views/frame/brave_browser_view.cc b/browser/ui/views/frame/brave_browser_view.cc index a622e20895f3..6fa50deb7669 100644 --- a/browser/ui/views/frame/brave_browser_view.cc +++ b/browser/ui/views/frame/brave_browser_view.cc @@ -20,6 +20,7 @@ #include "brave/browser/ui/views/frame/vertical_tab_strip_region_view.h" #include "brave/browser/ui/views/frame/vertical_tab_strip_widget_delegate_view.h" #include "brave/browser/ui/views/location_bar/brave_location_bar_view.h" +#include "brave/browser/ui/views/omnibox/brave_omnibox_view_views.h" #include "brave/browser/ui/views/sidebar/sidebar_container_view.h" #include "brave/browser/ui/views/tabs/features.h" #include "brave/browser/ui/views/toolbar/bookmark_button.h" @@ -405,6 +406,10 @@ speedreader::SpeedreaderBubbleView* BraveBrowserView::ShowSpeedreaderBubble( #endif } +bool BraveBrowserView::HasSelectedURL() const { + return GetLocationBarView()->omnibox_view()->SelectedTextIsURL(); +} + WalletButton* BraveBrowserView::GetWalletButton() { return static_cast(toolbar())->wallet_button(); } diff --git a/browser/ui/views/frame/brave_browser_view.h b/browser/ui/views/frame/brave_browser_view.h index 4c2feb5717d3..bf9961f19409 100644 --- a/browser/ui/views/frame/brave_browser_view.h +++ b/browser/ui/views/frame/brave_browser_view.h @@ -84,6 +84,7 @@ class BraveBrowserView : public BrowserView { views::View* sidebar_host_view() { return sidebar_host_view_; } bool IsSidebarVisible() const; + bool HasSelectedURL() const; VerticalTabStripWidgetDelegateView* vertical_tab_strip_widget_delegate_view() { diff --git a/chromium_src/chrome/browser/chrome_browser_main_mac.mm b/chromium_src/chrome/browser/chrome_browser_main_mac.mm new file mode 100644 index 000000000000..42424c3f30b6 --- /dev/null +++ b/chromium_src/chrome/browser/chrome_browser_main_mac.mm @@ -0,0 +1,10 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "chrome/browser/chrome_browser_main_mac.h" +#import "brave/browser/brave_app_controller_mac.h" + +#define AppController BraveAppController +#include "src/chrome/browser/chrome_browser_main_mac.mm" diff --git a/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 4bc93f827533..1f71ac780c44 100644 --- a/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc @@ -1,6 +1,7 @@ -// Copyright 2018 The Brave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +/* Copyright (c) 2018 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "chrome/browser/renderer_context_menu/render_view_context_menu.h" diff --git a/chromium_src/chrome/browser/ui/cocoa/main_menu_builder.mm b/chromium_src/chrome/browser/ui/cocoa/main_menu_builder.mm index a5da8916dcbe..4881824f3d6c 100644 --- a/chromium_src/chrome/browser/ui/cocoa/main_menu_builder.mm +++ b/chromium_src/chrome/browser/ui/cocoa/main_menu_builder.mm @@ -5,6 +5,11 @@ #include "brave/app/brave_command_ids.h" #include "brave/grit/brave_generated_resources.h" +#include "chrome/grit/generated_resources.h" + +namespace { +constexpr int kPasteMacResourceId = IDS_PASTE_MAC; +} #define BRAVE_BUILD_FILE_MENU \ Item(IDS_NEW_OFFTHERECORD_WINDOW_TOR) \ @@ -14,5 +19,13 @@ Item(IDS_REPORT_BROKEN_SITE_MAC) \ .command_id(IDC_SHOW_BRAVE_WEBCOMPAT_REPORTER), +#undef IDS_PASTE_MAC +#define IDS_PASTE_MAC IDS_COPY_CLEAN_LINK) \ + .command_id(IDC_COPY_CLEAN_LINK) \ + .target(app_delegate), \ + Item(kPasteMacResourceId + #include "src/chrome/browser/ui/cocoa/main_menu_builder.mm" #undef BRAVE_WEBCOMPAT_REPORTER_MENU_ENTRY +#undef IDS_PASTE_MAC +#define IDS_PASTE_MAC kPasteMacResourceId diff --git a/chromium_src/ui/views/controls/textfield/textfield.cc b/chromium_src/ui/views/controls/textfield/textfield.cc index 0796f23f2db3..4dbfb4a26947 100644 --- a/chromium_src/ui/views/controls/textfield/textfield.cc +++ b/chromium_src/ui/views/controls/textfield/textfield.cc @@ -1,7 +1,7 @@ // Copyright (c) 2022 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. +// You can obtain one at https://mozilla.org/MPL/2.0/. #include "ui/views/controls/textfield/textfield.h" #include "base/logging.h" diff --git a/chromium_src/ui/views/controls/textfield/textfield.h b/chromium_src/ui/views/controls/textfield/textfield.h index e7ac38e79e2c..03c7d233e9d2 100644 --- a/chromium_src/ui/views/controls/textfield/textfield.h +++ b/chromium_src/ui/views/controls/textfield/textfield.h @@ -1,7 +1,7 @@ // Copyright (c) 2022 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at https://mozilla.org/MPL/2.0/. +// You can obtain one at https://mozilla.org/MPL/2.0/. #ifndef BRAVE_CHROMIUM_SRC_UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_ #define BRAVE_CHROMIUM_SRC_UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_ diff --git a/chromium_src/ui/views/controls/textfield/textfield_controller.cc b/chromium_src/ui/views/controls/textfield/textfield_controller.cc index b62dfbe3d700..8704392f332d 100644 --- a/chromium_src/ui/views/controls/textfield/textfield_controller.cc +++ b/chromium_src/ui/views/controls/textfield/textfield_controller.cc @@ -1,7 +1,7 @@ // Copyright (c) 2022 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at http://mozilla.org/MPL/2.0/. +// You can obtain one at https://mozilla.org/MPL/2.0/. #include "ui/views/controls/textfield/textfield_controller.h" diff --git a/chromium_src/ui/views/controls/textfield/textfield_controller.h b/chromium_src/ui/views/controls/textfield/textfield_controller.h index b6441c46d739..f57cc94d92c5 100644 --- a/chromium_src/ui/views/controls/textfield/textfield_controller.h +++ b/chromium_src/ui/views/controls/textfield/textfield_controller.h @@ -1,7 +1,7 @@ // Copyright (c) 2022 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, -// you can obtain one at http://mozilla.org/MPL/2.0/. +// You can obtain one at https://mozilla.org/MPL/2.0/. #ifndef BRAVE_CHROMIUM_SRC_UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_CONTROLLER_H_ #define BRAVE_CHROMIUM_SRC_UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_CONTROLLER_H_ diff --git a/test/BUILD.gn b/test/BUILD.gn index 9a4bd5f1edff..761ed6f79760 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -407,6 +407,7 @@ test("brave_unit_tests") { if (is_mac) { sources += [ + "//brave/browser/brave_app_controller_mac_unittest.mm", "//brave/chromium_src/chrome/browser/shell_integration_unittest_mac.cc", "//brave/chromium_src/chrome/common/chrome_constants_unittest_mac.cc", ]