From f2b5a87115d910d06bf99430afd5cfa31bb2f42f Mon Sep 17 00:00:00 2001 From: Jay Harris Date: Wed, 25 Jan 2023 10:30:41 +1300 Subject: [PATCH] Layout Working modifiers WIP command info Scaffholding webui Maybe get things set up for webui Loading okay WIP Display shortcuts Add filtering Use util Filter out non-accelerated Show more keycodes Add more keys More key names Deps fixes Name cleanup shortcuts ==> commands Search by shortcut Try execute Execution cleanup Improve filtering Add confirm prompt Add feature Don't add webui unless flag is set WIP WIP command cleanup Presubmit Add more commands Add BrowserTest Includes Cleanup for PR --- app/BUILD.gn | 10 +- app/command_utils.cc | 216 ++++++++++++++ app/command_utils.h | 25 ++ app/command_utils_browsertest.cc | 35 +++ app/command_utils_unittest.cc | 24 ++ browser/about_flags.cc | 13 + browser/brave_content_browser_client.cc | 7 + browser/sources.gni | 1 + browser/ui/BUILD.gn | 4 + browser/ui/views/frame/brave_browser_view.cc | 27 +- browser/ui/views/frame/brave_browser_view.h | 4 + .../webui/brave_web_ui_controller_factory.cc | 7 + browser/ui/webui/commands_ui.cc | 93 ++++++ browser/ui/webui/commands_ui.h | 46 +++ components/commands/DEPS | 3 + components/commands/browser/BUILD.gn | 17 ++ .../commands/browser/resources/BUILD.gn | 26 ++ .../commands/browser/resources/commands.html | 21 ++ .../commands/browser/resources/commands.tsx | 90 ++++++ .../browser/resources/commands_resources.grdp | 4 + .../browser/resources/components/Command.tsx | 79 +++++ .../commands/browser/resources/tsconfig.json | 9 + .../browser/resources/utils/accelerator.ts | 7 + .../commands/browser/resources/utils/match.ts | 19 ++ components/commands/common/BUILD.gn | 28 ++ components/commands/common/commands.mojom | 23 ++ components/commands/common/features.cc | 12 + components/commands/common/features.h | 15 + components/commands/common/key_names.cc | 282 ++++++++++++++++++ components/commands/common/key_names.h | 20 ++ components/constants/webui_url_constants.cc | 1 + components/constants/webui_url_constants.h | 1 + components/resources/BUILD.gn | 4 + .../resources/brave_components_resources.grd | 1 + resources/resource_ids.spec | 4 + test/BUILD.gn | 4 + 36 files changed, 1171 insertions(+), 11 deletions(-) create mode 100644 app/command_utils.cc create mode 100644 app/command_utils.h create mode 100644 app/command_utils_browsertest.cc create mode 100644 app/command_utils_unittest.cc create mode 100644 browser/ui/webui/commands_ui.cc create mode 100644 browser/ui/webui/commands_ui.h create mode 100644 components/commands/DEPS create mode 100644 components/commands/browser/BUILD.gn create mode 100644 components/commands/browser/resources/BUILD.gn create mode 100644 components/commands/browser/resources/commands.html create mode 100644 components/commands/browser/resources/commands.tsx create mode 100644 components/commands/browser/resources/commands_resources.grdp create mode 100644 components/commands/browser/resources/components/Command.tsx create mode 100644 components/commands/browser/resources/tsconfig.json create mode 100644 components/commands/browser/resources/utils/accelerator.ts create mode 100644 components/commands/browser/resources/utils/match.ts create mode 100644 components/commands/common/BUILD.gn create mode 100644 components/commands/common/commands.mojom create mode 100644 components/commands/common/features.cc create mode 100644 components/commands/common/features.h create mode 100644 components/commands/common/key_names.cc create mode 100644 components/commands/common/key_names.h diff --git a/app/BUILD.gn b/app/BUILD.gn index 936e92f32230..665aa48651d7 100644 --- a/app/BUILD.gn +++ b/app/BUILD.gn @@ -12,7 +12,15 @@ import("//build/config/features.gni") import("//build/config/locales.gni") source_set("command_ids") { - sources = [ "brave_command_ids.h" ] + sources = [ + "brave_command_ids.h", + "command_utils.cc", + "command_utils.h", + ] + deps = [ + "//base", + "//chrome/app:command_ids", + ] } brave_grit("brave_generated_resources_grit") { diff --git a/app/command_utils.cc b/app/command_utils.cc new file mode 100644 index 000000000000..f03c18152e14 --- /dev/null +++ b/app/command_utils.cc @@ -0,0 +1,216 @@ +// Copyright (c) 2023 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 "brave/app/command_utils.h" + +#include +#include +#include + +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "brave/app/brave_command_ids.h" +#include "chrome/app/chrome_command_ids.h" + +namespace commands { +namespace { +std::string GetName(std::string raw_name) { + auto words = base::SplitString(raw_name, "_", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + for (auto& word : words) { + if (word.size() == 1) { + continue; + } + word = word[0] + base::ToLowerASCII(word.substr(1)); + } + return base::JoinString(words, " "); +} + +} // namespace + +std::map GetCommandInfo() { + static const std::map kCommands { + // Navigation commands. + ADD_UNTRANSLATED_COMMAND(BACK), ADD_UNTRANSLATED_COMMAND(FORWARD), + ADD_UNTRANSLATED_COMMAND(RELOAD), + ADD_UNTRANSLATED_COMMAND(RELOAD_BYPASSING_CACHE), + ADD_UNTRANSLATED_COMMAND(RELOAD_CLEARING_CACHE), + ADD_UNTRANSLATED_COMMAND(HOME), ADD_UNTRANSLATED_COMMAND(STOP), + + // Window management commands + ADD_UNTRANSLATED_COMMAND(NEW_WINDOW), + ADD_UNTRANSLATED_COMMAND(NEW_INCOGNITO_WINDOW), + ADD_UNTRANSLATED_COMMAND(CLOSE_WINDOW), + ADD_UNTRANSLATED_COMMAND(NEW_TAB), ADD_UNTRANSLATED_COMMAND(CLOSE_TAB), + ADD_UNTRANSLATED_COMMAND(SELECT_NEXT_TAB), + ADD_UNTRANSLATED_COMMAND(SELECT_PREVIOUS_TAB), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_0), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_1), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_2), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_3), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_4), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_5), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_6), + ADD_UNTRANSLATED_COMMAND(SELECT_TAB_7), + ADD_UNTRANSLATED_COMMAND(SELECT_LAST_TAB), + ADD_UNTRANSLATED_COMMAND(MOVE_TAB_TO_NEW_WINDOW), + ADD_UNTRANSLATED_COMMAND(DUPLICATE_TAB), + ADD_UNTRANSLATED_COMMAND(RESTORE_TAB), + ADD_UNTRANSLATED_COMMAND(FULLSCREEN), ADD_UNTRANSLATED_COMMAND(EXIT), + ADD_UNTRANSLATED_COMMAND(MOVE_TAB_NEXT), + ADD_UNTRANSLATED_COMMAND(MOVE_TAB_PREVIOUS), + ADD_UNTRANSLATED_COMMAND(SEARCH), + ADD_UNTRANSLATED_COMMAND(DEBUG_FRAME_TOGGLE), + ADD_UNTRANSLATED_COMMAND(WINDOW_MENU), + ADD_UNTRANSLATED_COMMAND(MINIMIZE_WINDOW), + ADD_UNTRANSLATED_COMMAND(MAXIMIZE_WINDOW), + ADD_UNTRANSLATED_COMMAND(NAME_WINDOW), + ADD_UNTRANSLATED_COMMAND(RESTORE_WINDOW), + +#if BUILDFLAG(IS_LINUX) + ADD_UNTRANSLATED_COMMAND(USE_SYSTEM_TITLE_BAR), +#endif + + // Web app window commands + ADD_UNTRANSLATED_COMMAND(OPEN_IN_PWA_WINDOW), + ADD_UNTRANSLATED_COMMAND(COPY_URL), + ADD_UNTRANSLATED_COMMAND(SITE_SETTINGS), + ADD_UNTRANSLATED_COMMAND(WEB_APP_MENU_APP_INFO), + + // Page related commands + ADD_UNTRANSLATED_COMMAND(BOOKMARK_THIS_TAB), + ADD_UNTRANSLATED_COMMAND(BOOKMARK_ALL_TABS), + ADD_UNTRANSLATED_COMMAND(VIEW_SOURCE), ADD_UNTRANSLATED_COMMAND(PRINT), + ADD_UNTRANSLATED_COMMAND(SAVE_PAGE), + ADD_UNTRANSLATED_COMMAND(EMAIL_PAGE_LOCATION), + ADD_UNTRANSLATED_COMMAND(BASIC_PRINT), + ADD_UNTRANSLATED_COMMAND(TRANSLATE_PAGE), + ADD_UNTRANSLATED_COMMAND(WINDOW_MUTE_SITE), + ADD_UNTRANSLATED_COMMAND(WINDOW_PIN_TAB), + ADD_UNTRANSLATED_COMMAND(WINDOW_GROUP_TAB), + ADD_UNTRANSLATED_COMMAND(QRCODE_GENERATOR), + ADD_UNTRANSLATED_COMMAND(WINDOW_CLOSE_TABS_TO_RIGHT), + ADD_UNTRANSLATED_COMMAND(WINDOW_CLOSE_OTHER_TABS), + ADD_UNTRANSLATED_COMMAND(NEW_TAB_TO_RIGHT), + + // Page manipulation for specific tab + ADD_UNTRANSLATED_COMMAND(MUTE_TARGET_SITE), + ADD_UNTRANSLATED_COMMAND(PIN_TARGET_TAB), + ADD_UNTRANSLATED_COMMAND(GROUP_TARGET_TAB), + ADD_UNTRANSLATED_COMMAND(DUPLICATE_TARGET_TAB), + + // Edit + ADD_UNTRANSLATED_COMMAND(CUT), ADD_UNTRANSLATED_COMMAND(COPY), + ADD_UNTRANSLATED_COMMAND(PASTE), ADD_UNTRANSLATED_COMMAND(EDIT_MENU), + + // Find + ADD_UNTRANSLATED_COMMAND(FIND), ADD_UNTRANSLATED_COMMAND(FIND_NEXT), + ADD_UNTRANSLATED_COMMAND(FIND_PREVIOUS), + ADD_UNTRANSLATED_COMMAND(CLOSE_FIND_OR_STOP), + + // Zoom + ADD_UNTRANSLATED_COMMAND(ZOOM_MENU), + ADD_UNTRANSLATED_COMMAND(ZOOM_PLUS), + ADD_UNTRANSLATED_COMMAND(ZOOM_NORMAL), + ADD_UNTRANSLATED_COMMAND(ZOOM_MINUS), + ADD_UNTRANSLATED_COMMAND(ZOOM_PERCENT_DISPLAY), + + // Focus + ADD_UNTRANSLATED_COMMAND(FOCUS_TOOLBAR), + ADD_UNTRANSLATED_COMMAND(FOCUS_LOCATION), + ADD_UNTRANSLATED_COMMAND(FOCUS_SEARCH), + ADD_UNTRANSLATED_COMMAND(FOCUS_MENU_BAR), + ADD_UNTRANSLATED_COMMAND(FOCUS_NEXT_PANE), + ADD_UNTRANSLATED_COMMAND(FOCUS_PREVIOUS_PANE), + ADD_UNTRANSLATED_COMMAND(FOCUS_BOOKMARKS), + ADD_UNTRANSLATED_COMMAND(FOCUS_INACTIVE_POPUP_FOR_ACCESSIBILITY), + ADD_UNTRANSLATED_COMMAND(FOCUS_WEB_CONTENTS_PANE), + + // UI bits + ADD_UNTRANSLATED_COMMAND(OPEN_FILE), + ADD_UNTRANSLATED_COMMAND(CREATE_SHORTCUT), + ADD_UNTRANSLATED_COMMAND(DEVELOPER_MENU), + ADD_UNTRANSLATED_COMMAND(DEV_TOOLS), + ADD_UNTRANSLATED_COMMAND(DEV_TOOLS_CONSOLE), + ADD_UNTRANSLATED_COMMAND(TASK_MANAGER), + ADD_UNTRANSLATED_COMMAND(DEV_TOOLS_DEVICES), + ADD_UNTRANSLATED_COMMAND(FEEDBACK), + ADD_UNTRANSLATED_COMMAND(SHOW_BOOKMARK_BAR), + ADD_UNTRANSLATED_COMMAND(SHOW_HISTORY), + ADD_UNTRANSLATED_COMMAND(SHOW_BOOKMARK_MANAGER), + ADD_UNTRANSLATED_COMMAND(SHOW_DOWNLOADS), + ADD_UNTRANSLATED_COMMAND(CLEAR_BROWSING_DATA), + ADD_UNTRANSLATED_COMMAND(IMPORT_SETTINGS), + ADD_UNTRANSLATED_COMMAND(OPTIONS), + ADD_UNTRANSLATED_COMMAND(EDIT_SEARCH_ENGINES), + ADD_UNTRANSLATED_COMMAND(VIEW_PASSWORDS), + ADD_UNTRANSLATED_COMMAND(ABOUT), + ADD_UNTRANSLATED_COMMAND(HELP_PAGE_VIA_KEYBOARD), + ADD_UNTRANSLATED_COMMAND(SHOW_APP_MENU), + ADD_UNTRANSLATED_COMMAND(MANAGE_EXTENSIONS), + ADD_UNTRANSLATED_COMMAND(DEV_TOOLS_INSPECT), + ADD_UNTRANSLATED_COMMAND(BOOKMARKS_MENU), + ADD_UNTRANSLATED_COMMAND(SHOW_AVATAR_MENU), + ADD_UNTRANSLATED_COMMAND(TOGGLE_REQUEST_TABLET_SITE), + ADD_UNTRANSLATED_COMMAND(DEV_TOOLS_TOGGLE), + ADD_UNTRANSLATED_COMMAND(TAKE_SCREENSHOT), + ADD_UNTRANSLATED_COMMAND(TOGGLE_FULLSCREEN_TOOLBAR), + ADD_UNTRANSLATED_COMMAND(INSTALL_PWA), + ADD_UNTRANSLATED_COMMAND(PASTE_AND_GO), + ADD_UNTRANSLATED_COMMAND(SHOW_FULL_URLS), + ADD_UNTRANSLATED_COMMAND(CARET_BROWSING_TOGGLE), + ADD_UNTRANSLATED_COMMAND(TOGGLE_QUICK_COMMANDS), + + // Media + ADD_UNTRANSLATED_COMMAND(CONTENT_CONTEXT_PLAYPAUSE), + ADD_UNTRANSLATED_COMMAND(CONTENT_CONTEXT_MUTE), + ADD_UNTRANSLATED_COMMAND(CONTENT_CONTEXT_LOOP), + ADD_UNTRANSLATED_COMMAND(CONTENT_CONTEXT_CONTROLS), + +#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + // Screen AI Visual Annotations. + ADD_UNTRANSLATED_COMMAND(RUN_SCREEN_AI_VISUAL_ANNOTATIONS), +#endif + + // Tab search + ADD_UNTRANSLATED_COMMAND(TAB_SEARCH), + ADD_UNTRANSLATED_COMMAND(TAB_SEARCH_CLOSE), + + // Brave Commands + ADD_UNTRANSLATED_COMMAND(SHOW_BRAVE_REWARDS), + ADD_UNTRANSLATED_COMMAND(NEW_TOR_CONNECTION_FOR_SITE), + ADD_UNTRANSLATED_COMMAND(NEW_OFFTHERECORD_WINDOW_TOR), + ADD_UNTRANSLATED_COMMAND(SHOW_BRAVE_SYNC), + ADD_UNTRANSLATED_COMMAND(SHOW_BRAVE_WALLET), + ADD_UNTRANSLATED_COMMAND(ADD_NEW_PROFILE), + ADD_UNTRANSLATED_COMMAND(OPEN_GUEST_PROFILE), + ADD_UNTRANSLATED_COMMAND(SHOW_BRAVE_WALLET_PANEL), + ADD_UNTRANSLATED_COMMAND(SHOW_BRAVE_VPN_PANEL), + ADD_UNTRANSLATED_COMMAND(TOGGLE_BRAVE_VPN_TOOLBAR_BUTTON), + ADD_UNTRANSLATED_COMMAND(MANAGE_BRAVE_VPN_PLAN), + ADD_UNTRANSLATED_COMMAND(TOGGLE_BRAVE_VPN), + ADD_UNTRANSLATED_COMMAND(COPY_CLEAN_LINK), + ADD_UNTRANSLATED_COMMAND(SIDEBAR_TOGGLE_POSITION), + ADD_UNTRANSLATED_COMMAND(TOGGLE_TAB_MUTE) + }; + return kCommands; +} + +std::vector GetCommands() { + const auto& info = GetCommandInfo(); + std::vector result; + for (const auto& [id, _] : info) { + result.push_back(id); + } + return result; +} + +std::string GetCommandName(int command_id) { + const auto& info = GetCommandInfo(); + auto it = info.find(command_id); + CHECK(it != info.end()); + return it->second; +} +} // namespace commands diff --git a/app/command_utils.h b/app/command_utils.h new file mode 100644 index 000000000000..fcb80ab299b2 --- /dev/null +++ b/app/command_utils.h @@ -0,0 +1,25 @@ +// Copyright (c) 2023 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_APP_COMMAND_UTILS_H_ +#define BRAVE_APP_COMMAND_UTILS_H_ + +#include +#include + +#define ADD_UNTRANSLATED_COMMAND(name) \ + { \ + IDC_##name, { \ + GetName(#name) \ + } \ + } + +namespace commands { + +std::vector GetCommands(); +std::string GetCommandName(int command_id); +} // namespace commands + +#endif // BRAVE_APP_COMMAND_UTILS_H_ diff --git a/app/command_utils_browsertest.cc b/app/command_utils_browsertest.cc new file mode 100644 index 000000000000..62aabcc427a7 --- /dev/null +++ b/app/command_utils_browsertest.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2023 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 "brave/app/command_utils.h" + +#include "base/test/scoped_feature_list.h" +#include "brave/components/commands/common/features.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/test/browser_test.h" + +class CommandUtilsBrowserTest : public InProcessBrowserTest { + public: + void SetUp() override { + features_.InitAndEnableFeature(commands::features::kBraveCommandsFeature); + InProcessBrowserTest::SetUp(); + } + + private: + base::test::ScopedFeatureList features_; +}; + +IN_PROC_BROWSER_TEST_F(CommandUtilsBrowserTest, + AllCommandsShouldBeExecutableWithoutCrash) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"))); + auto commands = commands::GetCommands(); + for (const auto& command : commands) { + LOG(ERROR) << "Executing command: " << commands::GetCommandName(command); + chrome::ExecuteCommand(browser(), command); + LOG(ERROR) << "Executed!"; + } +} diff --git a/app/command_utils_unittest.cc b/app/command_utils_unittest.cc new file mode 100644 index 000000000000..fdc7e97af14b --- /dev/null +++ b/app/command_utils_unittest.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2023 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 "brave/app/command_utils.h" + +#include "base/ranges/algorithm.h" +#include "chrome/browser/ui/views/accelerator_table.h" +#include "gtest/gtest.h" + +// Note: If this test fails because an accelerated command isn't present just +// add the missing command to |commands::GetCommandInfo| in command_utils.h. +TEST(CommandUtilsUnitTest, AllAcceleratedCommandsShouldBeAvailable) { + auto accelerators = GetAcceleratorList(); + auto commands = commands::GetCommands(); + + for (const auto& accelerator : accelerators) { + EXPECT_NE(base::ranges::find(commands, accelerator.command_id), + commands.end()) + << "Accelerated command '" << accelerator.command_id + << "' was not present in the list of commands."; + } +} diff --git a/browser/about_flags.cc b/browser/about_flags.cc index a74ee5bb3c09..1aac269060ff 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -19,6 +19,7 @@ #include "brave/components/brave_today/common/features.h" #include "brave/components/brave_vpn/common/buildflags/buildflags.h" #include "brave/components/brave_wallet/common/features.h" +#include "brave/components/commands/common/features.h" #include "brave/components/de_amp/common/features.h" #include "brave/components/debounce/common/features.h" #include "brave/components/google_sign_in_permission/features.h" @@ -208,6 +209,10 @@ constexpr char kBraveBlockScreenFingerprintingDescription[] = "Prevents JavaScript and CSS from learning the user's screen dimensions " "or window position."; +constexpr char kBraveCommandsName[] = "Brave Commands"; +constexpr char kBraveCommandsDescription[] = + "Enable experimental page for viewing and executing commands in Brave"; + constexpr char kBraveTorWindowsHttpsOnlyName[] = "Use HTTPS-Only Mode in Private Windows with Tor"; constexpr char kBraveTorWindowsHttpsOnlyDescription[] = @@ -611,6 +616,13 @@ constexpr char kRestrictEventSourcePoolDescription[] = #define PLAYLIST_FEATURE_ENTRIES #endif +#define BRAVE_COMMANDS_FEATURE_ENTRIES \ + {"brave-commands", \ + flag_descriptions::kBraveCommandsName, \ + flag_descriptions::kBraveCommandsDescription, \ + kOsDesktop, \ + FEATURE_VALUE_TYPE(commands::features::kBraveCommandsFeature)}, + #if defined(TOOLKIT_VIEWS) #define BRAVE_VERTICAL_TABS_FEATURE_ENTRY \ {"brave-vertical-tabs", \ @@ -840,6 +852,7 @@ constexpr char kRestrictEventSourcePoolDescription[] = SPEEDREADER_FEATURE_ENTRIES \ BRAVE_FEDERATED_FEATURE_ENTRIES \ PLAYLIST_FEATURE_ENTRIES \ + BRAVE_COMMANDS_FEATURE_ENTRIES \ BRAVE_VERTICAL_TABS_FEATURE_ENTRY \ BRAVE_BACKGROUND_VIDEO_PLAYBACK_ANDROID \ BRAVE_SAFE_BROWSING_ANDROID \ diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 849a42d70622..d8555436e3d6 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -33,6 +33,7 @@ #include "brave/browser/profiles/brave_renderer_updater_factory.h" #include "brave/browser/profiles/profile_util.h" #include "brave/browser/skus/skus_service_factory.h" +#include "brave/browser/ui/webui/commands_ui.h" #include "brave/components/brave_ads/common/features.h" #include "brave/components/brave_federated/features.h" #include "brave/components/brave_rewards/browser/rewards_protocol_handler.h" @@ -56,6 +57,8 @@ #include "brave/components/brave_wallet/browser/solana_provider_impl.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" #include "brave/components/brave_webtorrent/browser/buildflags/buildflags.h" +#include "brave/components/commands/common/commands.mojom.h" +#include "brave/components/commands/common/features.h" #include "brave/components/constants/pref_names.h" #include "brave/components/constants/webui_url_constants.h" #include "brave/components/cosmetic_filters/browser/cosmetic_filters_resources.h" @@ -481,6 +484,10 @@ void BraveContentBrowserClient::RegisterWebUIInterfaceBrokers( .Add(); } #endif + + if (base::FeatureList::IsEnabled(commands::features::kBraveCommandsFeature)) { + registry.ForWebUI().Add(); + } } bool BraveContentBrowserClient::AllowWorkerFingerprinting( diff --git a/browser/sources.gni b/browser/sources.gni index eaf2de5caa8b..9bc1a9baaff8 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -158,6 +158,7 @@ brave_chrome_browser_deps = [ "//brave/components/brave_wallet/common:mojom", "//brave/components/brave_wayback_machine/buildflags", "//brave/components/brave_webtorrent/browser/buildflags", + "//brave/components/commands/browser", "//brave/components/constants", "//brave/components/cosmetic_filters/browser", "//brave/components/cosmetic_filters/common:mojom", diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index f038d8b37ff7..d3f1eb4e9869 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -61,6 +61,8 @@ source_set("ui") { "webui/brave_web_ui_controller_factory.h", "webui/brave_webui_source.cc", "webui/brave_webui_source.h", + "webui/commands_ui.cc", + "webui/commands_ui.h", "webui/webcompat_reporter_ui.cc", "webui/webcompat_reporter_ui.h", ] @@ -411,6 +413,7 @@ source_set("ui") { "//brave/components/brave_vpn/common/buildflags", "//brave/components/brave_vpn/common/mojom", "//brave/components/brave_wayback_machine/buildflags", + "//brave/components/commands/browser", "//brave/components/constants", "//brave/components/cosmetic_filters/resources/data:generated_resources", "//brave/components/l10n/common", @@ -781,6 +784,7 @@ source_set("ui") { "//brave/components/brave_wallet_ui/page:brave_wallet_page_generated", "//brave/components/brave_wallet_ui/panel:brave_wallet_panel_generated", "//brave/components/brave_wallet_ui/trezor:trezor_bridge_generated", + "//brave/components/commands/browser/resources:generated_resources", "//brave/components/resources:strings_grit", "//components/permissions", ] diff --git a/browser/ui/views/frame/brave_browser_view.cc b/browser/ui/views/frame/brave_browser_view.cc index 3e6f7ff2634f..4ad78b132ba8 100644 --- a/browser/ui/views/frame/brave_browser_view.cc +++ b/browser/ui/views/frame/brave_browser_view.cc @@ -5,7 +5,9 @@ #include "brave/browser/ui/views/frame/brave_browser_view.h" +#include #include +#include #include "base/bind.h" #include "brave/browser/brave_rewards/rewards_panel/rewards_panel_coordinator.h" @@ -40,6 +42,7 @@ #include "chrome/common/pref_names.h" #include "extensions/buildflags/buildflags.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/base/accelerators/accelerator.h" #include "ui/events/event_observer.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/event_monitor.h" @@ -81,9 +84,7 @@ class BraveBrowserView::TabCyclingEventHandler : public ui::EventObserver, Start(); } - ~TabCyclingEventHandler() override { - Stop(); - } + ~TabCyclingEventHandler() override { Stop(); } TabCyclingEventHandler(const TabCyclingEventHandler&) = delete; TabCyclingEventHandler& operator=(const TabCyclingEventHandler&) = delete; @@ -110,17 +111,14 @@ class BraveBrowserView::TabCyclingEventHandler : public ui::EventObserver, } // Handle Browser widget closing while tab Cycling is in-progress. - void OnWidgetClosing(views::Widget* widget) override { - Stop(); - } + void OnWidgetClosing(views::Widget* widget) override { Stop(); } void Start() { // Add the event handler auto* widget = browser_view_->GetWidget(); if (widget->GetNativeWindow()) { monitor_ = views::EventMonitor::CreateWindowMonitor( - this, - widget->GetNativeWindow(), + this, widget->GetNativeWindow(), {ui::ET_MOUSE_PRESSED, ui::ET_KEY_RELEASED}); } @@ -405,6 +403,15 @@ views::View* BraveBrowserView::GetWalletButtonAnchorView() { ->GetAsAnchorView(); } +std::map> +BraveBrowserView::GetAcceleratedCommands() { + std::map> result; + for (const auto& [accelerator, command] : accelerator_table_) { + result[command].push_back(accelerator); + } + return result; +} + void BraveBrowserView::CreateWalletBubble() { DCHECK(GetWalletButton()); GetWalletButton()->ShowWalletBubble(); @@ -559,6 +566,6 @@ void BraveBrowserView::StartTabCycling() { void BraveBrowserView::StopTabCycling() { tab_cycling_event_handler_.reset(); - static_cast(browser()->tab_strip_model())-> - StopMRUCycling(); + static_cast(browser()->tab_strip_model()) + ->StopMRUCycling(); } diff --git a/browser/ui/views/frame/brave_browser_view.h b/browser/ui/views/frame/brave_browser_view.h index e825e0ed3beb..df585722c26e 100644 --- a/browser/ui/views/frame/brave_browser_view.h +++ b/browser/ui/views/frame/brave_browser_view.h @@ -6,8 +6,10 @@ #ifndef BRAVE_BROWSER_UI_VIEWS_FRAME_BRAVE_BROWSER_VIEW_H_ #define BRAVE_BROWSER_UI_VIEWS_FRAME_BRAVE_BROWSER_VIEW_H_ +#include #include #include +#include #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" @@ -15,6 +17,7 @@ #include "brave/components/brave_vpn/common/buildflags/buildflags.h" #include "build/build_config.h" #include "chrome/browser/ui/views/frame/browser_view.h" +#include "ui/base/accelerators/accelerator.h" #if BUILDFLAG(ENABLE_BRAVE_VPN) #include "brave/browser/ui/views/toolbar/brave_vpn_panel_controller.h" @@ -61,6 +64,7 @@ class BraveBrowserView : public BrowserView { void CloseWalletBubble(); WalletButton* GetWalletButton(); views::View* GetWalletButtonAnchorView(); + std::map> GetAcceleratedCommands(); // BrowserView overrides: void StartTabCycling() override; diff --git a/browser/ui/webui/brave_web_ui_controller_factory.cc b/browser/ui/webui/brave_web_ui_controller_factory.cc index 01063c428033..12c7ca33cc83 100644 --- a/browser/ui/webui/brave_web_ui_controller_factory.cc +++ b/browser/ui/webui/brave_web_ui_controller_factory.cc @@ -17,10 +17,12 @@ #include "brave/browser/ui/webui/brave_rewards_internals_ui.h" #include "brave/browser/ui/webui/brave_rewards_page_ui.h" #include "brave/browser/ui/webui/brave_tip_ui.h" +#include "brave/browser/ui/webui/commands_ui.h" #include "brave/browser/ui/webui/webcompat_reporter_ui.h" #include "brave/components/brave_federated/features.h" #include "brave/components/brave_rewards/common/rewards_util.h" #include "brave/components/brave_shields/common/features.h" +#include "brave/components/commands/common/features.h" #include "brave/components/constants/pref_names.h" #include "brave/components/constants/webui_url_constants.h" #include "brave/components/ipfs/buildflags/buildflags.h" @@ -96,6 +98,10 @@ WebUIController* NewWebUI(WebUI* web_ui, const GURL& url) { return new IPFSUI(web_ui, url.host()); #endif #if !BUILDFLAG(IS_ANDROID) + } else if (host == kCommandsHost && + base::FeatureList::IsEnabled( + commands::features::kBraveCommandsFeature)) { + return new commands::CommandsUI(web_ui, url.host()); } else if (host == kWalletPageHost && // We don't want to check for supported profile type here because // we want private windows to redirect to the regular profile. @@ -197,6 +203,7 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, const GURL& url) { url.host_piece() == kTipHost || url.host_piece() == kBraveRewardsPanelHost || url.host_piece() == kSpeedreaderPanelHost || + url.host_piece() == kCommandsHost || #endif #if BUILDFLAG(ENABLE_TOR) url.host_piece() == kTorInternalsHost || diff --git a/browser/ui/webui/commands_ui.cc b/browser/ui/webui/commands_ui.cc new file mode 100644 index 000000000000..ceb0594c08bc --- /dev/null +++ b/browser/ui/webui/commands_ui.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2023 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 "brave/browser/ui/webui/commands_ui.h" + +#include +#include + +#include "base/strings/utf_string_conversions.h" +#include "brave/app/command_utils.h" +#include "brave/browser/ui/views/frame/brave_browser_view.h" +#include "brave/browser/ui/webui/brave_webui_source.h" +#include "brave/components/commands/browser/resources/grit/commands_generated_map.h" +#include "brave/components/commands/common/commands.mojom.h" +#include "brave/components/commands/common/key_names.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/browser_finder.h" +#include "components/grit/brave_components_resources.h" +#include "content/public/browser/web_ui_controller.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/events/event_constants.h" + +namespace commands { +CommandsUI::CommandsUI(content::WebUI* web_ui, const std::string& name) + : content::WebUIController(web_ui) { + CreateAndAddWebUIDataSource(web_ui, name, kCommandsGenerated, + kCommandsGeneratedSize, IDR_COMMANDS_HTML); +} + +CommandsUI::~CommandsUI() = default; + +void CommandsUI::BindInterface( + mojo::PendingReceiver pending_receiver) { + if (receiver_.is_bound()) { + receiver_.reset(); + } + + receiver_.Bind(std::move(pending_receiver)); +} + +void CommandsUI::GetCommands(GetCommandsCallback callback) { + auto command_ids = commands::GetCommands(); + auto accelerated_commands = browser_view()->GetAcceleratedCommands(); + + std::vector result; + for (const auto& command_id : command_ids) { + if (!chrome::SupportsCommand(browser(), command_id)) { + continue; + } + + auto command = Command::New(); + command->id = command_id; + command->name = commands::GetCommandName(command_id); + command->enabled = + chrome::IsCommandEnabled(browser_view()->browser(), command_id); + + auto it = accelerated_commands.find(command_id); + if (it != accelerated_commands.end()) { + for (const auto& accel : it->second) { + auto a = Accelerator::New(); + a->keycode = commands::GetKeyName(accel.key_code()); + a->modifiers = commands::GetModifierName(accel.modifiers()); + + if ((!a->modifiers.size() && accel.modifiers() != ui::EF_NONE) || + a->keycode.empty()) { + continue; + } + command->accelerators.push_back(std::move(a)); + } + } + result.push_back(std::move(command)); + } + + std::move(callback).Run(std::move(result)); +} + +void CommandsUI::TryExecuteCommand(uint32_t command_id) { + chrome::ExecuteCommand(browser(), command_id); +} + +Browser* CommandsUI::browser() { + return chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()); +} + +BraveBrowserView* CommandsUI::browser_view() { + return static_cast(browser()->window()); +} + +WEB_UI_CONTROLLER_TYPE_IMPL(CommandsUI) + +} // namespace commands diff --git a/browser/ui/webui/commands_ui.h b/browser/ui/webui/commands_ui.h new file mode 100644 index 000000000000..f0d6a9a38d9b --- /dev/null +++ b/browser/ui/webui/commands_ui.h @@ -0,0 +1,46 @@ +// Copyright (c) 2023 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_UI_WEBUI_COMMANDS_UI_H_ +#define BRAVE_BROWSER_UI_WEBUI_COMMANDS_UI_H_ + +#include + +#include "brave/browser/ui/views/frame/brave_browser_view.h" +#include "brave/components/commands/common/commands.mojom.h" +#include "brave/components/playlist/common/mojom/playlist.mojom.h" +#include "content/public/browser/web_ui_controller.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" + +namespace commands { + +class CommandsUI : public content::WebUIController, public CommandsService { + public: + CommandsUI(content::WebUI* web_ui, const std::string& host); + ~CommandsUI() override; + CommandsUI(const CommandsUI&) = delete; + CommandsUI& operator=(const CommandsUI&) = delete; + + void BindInterface(mojo::PendingReceiver pending_receiver); + + // CommandsService: + void GetCommands(GetCommandsCallback callback) override; + void TryExecuteCommand(uint32_t command_id) override; + + protected: + Browser* browser(); + BraveBrowserView* browser_view(); + + private: + mojo::Receiver receiver_{this}; + + WEB_UI_CONTROLLER_TYPE_DECL(); +}; + +} // namespace commands + +#endif // BRAVE_BROWSER_UI_WEBUI_COMMANDS_UI_H_ diff --git a/components/commands/DEPS b/components/commands/DEPS new file mode 100644 index 000000000000..e5c667693db9 --- /dev/null +++ b/components/commands/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+ui/events" +] diff --git a/components/commands/browser/BUILD.gn b/components/commands/browser/BUILD.gn new file mode 100644 index 000000000000..aacbdf5badc3 --- /dev/null +++ b/components/commands/browser/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright (c) 2023 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/build/config.gni") + +static_library("browser") { + sources = [] + + public_deps = [ "//brave/components/commands/common" ] + + deps = [ + "//base", + "//brave/components/resources:static_resources", + ] +} diff --git a/components/commands/browser/resources/BUILD.gn b/components/commands/browser/resources/BUILD.gn new file mode 100644 index 000000000000..b308468b09d2 --- /dev/null +++ b/components/commands/browser/resources/BUILD.gn @@ -0,0 +1,26 @@ +# Copyright (c) 2023 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/components/common/typescript.gni") + +transpile_web_ui("commands_ui") { + entry_points = [ [ + "commands", + rebase_path("commands.tsx"), + ] ] + + public_deps = [ + "//brave/components/commands/common:mojom_js", + "//mojo/public/mojom/base", + ] + + resource_name = "commands" +} + +pack_web_resources("generated_resources") { + resource_name = "commands" + output_dir = "$root_gen_dir/brave/components/commands/browser/resources" + deps = [ ":commands_ui" ] +} diff --git a/components/commands/browser/resources/commands.html b/components/commands/browser/resources/commands.html new file mode 100644 index 000000000000..968f151837f3 --- /dev/null +++ b/components/commands/browser/resources/commands.html @@ -0,0 +1,21 @@ + + + + + + + Brave Commands + + + + + + +
+ + + diff --git a/components/commands/browser/resources/commands.tsx b/components/commands/browser/resources/commands.tsx new file mode 100644 index 000000000000..d691e6130cad --- /dev/null +++ b/components/commands/browser/resources/commands.tsx @@ -0,0 +1,90 @@ +// Copyright (c) 2023 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 * as CommandsMojo from 'gen/brave/components/commands/common/commands.mojom.m.js' +import * as React from 'react' +import { render } from 'react-dom' +import styled from 'styled-components' +import Command from './components/Command' +import { match } from './utils/match' + +const CommandsContainer = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +` + +const FilterBox = styled.input` + margin: 4px; + padding: 4px; + border: 1px solid lightgray; +` + +const FiltersRow = styled.div` + display: flex; + flex-direction: row; + gap: 8px; +` + +export const api = CommandsMojo.CommandsService.getRemote() + +function usePromise (getPromise: () => Promise, deps: any[]) { + const [result, setResult] = React.useState() + React.useEffect(() => { + getPromise().then(setResult) + }, deps) + + return result +} + +function App () { + const [filter, setFilter] = React.useState('') + const [withAccelerator, setWithAccelerator] = React.useState(false) + const [enabledOnly, setEnabledOnly] = React.useState(true) + + const commands = usePromise( + () => api.getCommands().then((r) => r.commands), + [] + ) + + const filteredCommands = React.useMemo( + () => + commands + ?.filter((c) => match(filter, c)) + .filter((c) => !withAccelerator || c.accelerators.length) + .filter((c) => !enabledOnly || c.enabled), + [filter, withAccelerator, enabledOnly, commands] + ) + return ( + + setFilter(e.target.value)} /> + + + + + {filteredCommands?.map((c) => ( + + ))} + + ) +} + +document.addEventListener('DOMContentLoaded', () => + render(, document.getElementById('root')) +) diff --git a/components/commands/browser/resources/commands_resources.grdp b/components/commands/browser/resources/commands_resources.grdp new file mode 100644 index 000000000000..dee3f380c96c --- /dev/null +++ b/components/commands/browser/resources/commands_resources.grdp @@ -0,0 +1,4 @@ + + + + diff --git a/components/commands/browser/resources/components/Command.tsx b/components/commands/browser/resources/components/Command.tsx new file mode 100644 index 000000000000..b2bf80480c58 --- /dev/null +++ b/components/commands/browser/resources/components/Command.tsx @@ -0,0 +1,79 @@ +// Copyright (c) 2023 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 * as React from 'react' +import * as CommandsMojo from 'gen/brave/components/commands/common/commands.mojom.m.js' +import styled from 'styled-components' +import { api } from '../commands' +import { allKeys } from '../utils/accelerator' + +const Grid = styled.div` + display: grid; + grid-template-columns: 200px min-content auto; + gap: 16px; + align-items: center; + padding: 4px; + min-height: 32px; + + &:hover { + background-color: lightgray; + } +` + +const Column = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +` + +const Kbd = styled.div` + display: inline-block; + border-radius: 4px; + padding: 4px; + background-color: #f6f8fa; + border: 1px solid rgba(174, 184, 193, 0.2); + box-shadow: inset 0 -1px 0 rgba(174, 184, 193, 0.2); +` + +let isSure = false +const ifSure = () => { + if (isSure) return true + return isSure = window.confirm('This is experimental. Executing commands may not behave as expected, or cause your browser to crash. Continue?') +} + +function Accelerator ({ + accelerator +}: { + accelerator: CommandsMojo.Accelerator +}) { + return ( +
+ {allKeys(accelerator).map((k, i) => ( + + {i !== 0 && +} + {k} + + ))} +
+ ) +} + +export default function Command ({ + command +}: { + command: CommandsMojo.Command +}) { + return ( + +
{command.name}
+ + + {command.accelerators.map((a, i) => ( + + ))} + +
+ ) +} diff --git a/components/commands/browser/resources/tsconfig.json b/components/commands/browser/resources/tsconfig.json new file mode 100644 index 000000000000..868d3fa89936 --- /dev/null +++ b/components/commands/browser/resources/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../../tsconfig", + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.d.ts", + "../../definitions/*.d.ts" + ] +} diff --git a/components/commands/browser/resources/utils/accelerator.ts b/components/commands/browser/resources/utils/accelerator.ts new file mode 100644 index 000000000000..db986193bc15 --- /dev/null +++ b/components/commands/browser/resources/utils/accelerator.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2023 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 { Accelerator } from 'gen/brave/components/commands/common/commands.mojom.m' + +export const allKeys = (accelerator: Accelerator) => [...accelerator.modifiers, accelerator.keycode] diff --git a/components/commands/browser/resources/utils/match.ts b/components/commands/browser/resources/utils/match.ts new file mode 100644 index 000000000000..8dfda3040c0b --- /dev/null +++ b/components/commands/browser/resources/utils/match.ts @@ -0,0 +1,19 @@ +// Copyright (c) 2023 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 { Command } from '../../../../../../out/Component/gen/brave/components/commands/common/commands.mojom.m' +import { allKeys } from './accelerator' + +export const match = (query: string, command: Command) => { + if (command.id === parseInt(query)) return true + + const queryUpper = query.toUpperCase() + if (command.name.toUpperCase().includes(queryUpper)) return true + + const keys = queryUpper.split('+').map(k => k.trim()).filter(k => k) + return command.accelerators.some(a => { + const acceleratorKeys = new Set(allKeys(a).map(k => k.toUpperCase())) + return keys.every(k => acceleratorKeys.has(k)) + }) +} diff --git a/components/commands/common/BUILD.gn b/components/commands/common/BUILD.gn new file mode 100644 index 000000000000..22cdc9c681f8 --- /dev/null +++ b/components/commands/common/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright (c) 2023 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("//mojo/public/tools/bindings/mojom.gni") + +static_library("common") { + sources = [ + "features.cc", + "features.h", + "key_names.cc", + "key_names.h", + ] + + deps = [ + "//base", + "//ui/events:event_constants", + "//ui/events:events_base", + ] + + public_deps = [ ":mojom" ] +} + +mojom("mojom") { + sources = [ "commands.mojom" ] + public_deps = [ "//mojo/public/mojom/base" ] +} diff --git a/components/commands/common/commands.mojom b/components/commands/common/commands.mojom new file mode 100644 index 000000000000..fc3e5d768a24 --- /dev/null +++ b/components/commands/common/commands.mojom @@ -0,0 +1,23 @@ +// Copyright (c) 2023 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/. + +struct Command { + string name; + uint32 id; + + bool enabled; + + array accelerators; +}; + +struct Accelerator { + string keycode; + array modifiers; +}; + +interface CommandsService { + GetCommands() => (array commands); + TryExecuteCommand(uint32 command_id); +}; diff --git a/components/commands/common/features.cc b/components/commands/common/features.cc new file mode 100644 index 000000000000..6cefd6951c1b --- /dev/null +++ b/components/commands/common/features.cc @@ -0,0 +1,12 @@ +// Copyright (c) 2023 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 "brave/components/commands/common/features.h" + +namespace commands::features { +BASE_FEATURE(kBraveCommandsFeature, + "BraveCommands", + base::FEATURE_DISABLED_BY_DEFAULT); +} diff --git a/components/commands/common/features.h b/components/commands/common/features.h new file mode 100644 index 000000000000..623729cdac3a --- /dev/null +++ b/components/commands/common/features.h @@ -0,0 +1,15 @@ +// Copyright (c) 2023 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_COMPONENTS_COMMANDS_COMMON_FEATURES_H_ +#define BRAVE_COMPONENTS_COMMANDS_COMMON_FEATURES_H_ + +#include "base/feature_list.h" + +namespace commands::features { +BASE_DECLARE_FEATURE(kBraveCommandsFeature); +} // namespace commands::features + +#endif // BRAVE_COMPONENTS_COMMANDS_COMMON_FEATURES_H_ diff --git a/components/commands/common/key_names.cc b/components/commands/common/key_names.cc new file mode 100644 index 000000000000..ed94717e11bc --- /dev/null +++ b/components/commands/common/key_names.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2023 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 "brave/components/commands/common/key_names.h" + +namespace commands { +std::string GetKeyName(ui::KeyboardCode code) { + switch (code) { + case ui::VKEY_F1: + return "F1"; + case ui::VKEY_F2: + return "F2"; + case ui::VKEY_F3: + return "F3"; + case ui::VKEY_F4: + return "F4"; + case ui::VKEY_F5: + return "F5"; + case ui::VKEY_F6: + return "F6"; + case ui::VKEY_F7: + return "F7"; + case ui::VKEY_F8: + return "F8"; + case ui::VKEY_F9: + return "F9"; + case ui::VKEY_F10: + return "F10"; + case ui::VKEY_F11: + return "F11"; + case ui::VKEY_F12: + return "F12"; + case ui::VKEY_F13: + return "F13"; + case ui::VKEY_F14: + return "F14"; + case ui::VKEY_F15: + return "F15"; + case ui::VKEY_F16: + return "F16"; + case ui::VKEY_F17: + return "F17"; + case ui::VKEY_F18: + return "F18"; + case ui::VKEY_F19: + return "F19"; + case ui::VKEY_F20: + return "F20"; + case ui::VKEY_F21: + return "F21"; + case ui::VKEY_F22: + return "F22"; + case ui::VKEY_F23: + return "F23"; + case ui::VKEY_F24: + return "F24"; + case ui::VKEY_ESCAPE: + return "Esc"; + case ui::VKEY_BROWSER_SEARCH: + return "Search"; + case ui::VKEY_LMENU: + case ui::VKEY_RMENU: + case ui::VKEY_MENU: + return "Menu"; + case ui::VKEY_BROWSER_FORWARD: + return "Forward"; + case ui::VKEY_BROWSER_BACK: + return "Back"; + case ui::VKEY_BROWSER_REFRESH: + return "Refresh"; + case ui::VKEY_BROWSER_HOME: + return "Home"; + case ui::VKEY_BROWSER_STOP: + return "Stop"; + case ui::VKEY_BROWSER_FAVORITES: + return "Favorites"; + case ui::VKEY_NEW: + return "New"; + case ui::VKEY_CLOSE: + return "Close"; + case ui::VKEY_BACK: + return "Back"; + case ui::VKEY_DELETE: + return "Delete"; + case ui::VKEY_MEDIA_PLAY_PAUSE: + return "Play Pause"; + case ui::VKEY_MEDIA_PLAY: + return "Play"; + case ui::VKEY_MEDIA_PAUSE: + return "Pause"; + case ui::VKEY_VOLUME_MUTE: + return "Mute"; + case ui::VKEY_TAB: + return "Tab"; + case ui::VKEY_NEXT: + return "PgDn"; + case ui::VKEY_PRIOR: + return "PgUp"; + case ui::VKEY_RETURN: + return "Enter"; + case ui::VKEY_1: + case ui::VKEY_NUMPAD1: + return "1"; + case ui::VKEY_2: + case ui::VKEY_NUMPAD2: + return "2"; + case ui::VKEY_3: + case ui::VKEY_NUMPAD3: + return "3"; + case ui::VKEY_4: + case ui::VKEY_NUMPAD4: + return "4"; + case ui::VKEY_5: + case ui::VKEY_NUMPAD5: + return "5"; + case ui::VKEY_6: + case ui::VKEY_NUMPAD6: + return "6"; + case ui::VKEY_7: + case ui::VKEY_NUMPAD7: + return "7"; + case ui::VKEY_8: + case ui::VKEY_NUMPAD8: + return "8"; + case ui::VKEY_9: + case ui::VKEY_NUMPAD9: + return "9"; + case ui::VKEY_0: + case ui::VKEY_NUMPAD0: + return "0"; + case ui::VKEY_SUBTRACT: + case ui::VKEY_OEM_MINUS: + return "-"; + case ui::VKEY_ADD: + case ui::VKEY_OEM_PLUS: + return "+"; + case ui::VKEY_SPACE: + return "Space"; + case ui::VKEY_LEFT: + return "Left"; + case ui::VKEY_RIGHT: + return "Right"; + case ui::VKEY_UP: + return "Up"; + case ui::VKEY_DOWN: + return "Down"; + case ui::VKEY_HOME: + return "Home"; + case ui::VKEY_END: + return "End"; + case ui::VKEY_CAPITAL: + return "Caps"; + case ui::VKEY_A: + return "A"; + case ui::VKEY_B: + return "B"; + case ui::VKEY_C: + return "C"; + case ui::VKEY_D: + return "D"; + case ui::VKEY_E: + return "E"; + case ui::VKEY_F: + return "F"; + case ui::VKEY_G: + return "G"; + case ui::VKEY_H: + return "H"; + case ui::VKEY_I: + return "I"; + case ui::VKEY_J: + return "J"; + case ui::VKEY_K: + return "K"; + case ui::VKEY_L: + return "L"; + case ui::VKEY_M: + return "M"; + case ui::VKEY_N: + return "N"; + case ui::VKEY_O: + return "O"; + case ui::VKEY_P: + return "P"; + case ui::VKEY_Q: + return "Q"; + case ui::VKEY_R: + return "R"; + case ui::VKEY_S: + return "S"; + case ui::VKEY_T: + return "T"; + case ui::VKEY_U: + return "U"; + case ui::VKEY_V: + return "V"; + case ui::VKEY_W: + return "W"; + case ui::VKEY_X: + return "X"; + case ui::VKEY_Y: + return "Y"; + case ui::VKEY_Z: + return "Z"; + case ui::VKEY_CANCEL: + return "Cancel"; + case ui::VKEY_BACKTAB: + return "Backtab"; + case ui::VKEY_CLEAR: + return "Clear"; + case ui::VKEY_SHIFT: + return "Shift"; + case ui::VKEY_CONTROL: + return "Ctrk"; + case ui::VKEY_PAUSE: + return "Pause"; + case ui::VKEY_KANA: + return "Kana"; + case ui::VKEY_PASTE: + return "Paste"; + case ui::VKEY_JUNJA: + return "Junja"; + case ui::VKEY_FINAL: + return "Final"; + case ui::VKEY_HANJA: + return "Hanja"; + case ui::VKEY_CONVERT: + return "Convert"; + case ui::VKEY_NONCONVERT: + return "Non Convert"; + case ui::VKEY_ACCEPT: + return ""; + case ui::VKEY_MODECHANGE: + return "Mode"; + case ui::VKEY_SELECT: + return "Select"; + case ui::VKEY_PRINT: + return "Print"; + case ui::VKEY_EXECUTE: + return "Execute"; + case ui::VKEY_SNAPSHOT: + return "PrtScn"; + case ui::VKEY_INSERT: + return "Ins"; + case ui::VKEY_HELP: + return "Help"; + case ui::VKEY_RWIN: + case ui::VKEY_COMMAND: + return "Cmd"; + default: + return "Unknown"; + } +} + +std::vector GetModifierName(ui::KeyEventFlags flags) { + std::vector result; + + if (flags & ui::EF_COMMAND_DOWN) { + result.push_back("Cmd"); + } + + if (flags & ui::EF_CONTROL_DOWN) { + result.push_back("Ctrl"); + } + + if (flags & ui::EF_ALT_DOWN) { + result.push_back("Alt"); + } + + if (flags & ui::EF_SHIFT_DOWN) { + result.push_back("Shift"); + } + + if (flags & ui::EF_FUNCTION_DOWN) { + result.push_back("Fn"); + } + + return result; +} +} // namespace commands diff --git a/components/commands/common/key_names.h b/components/commands/common/key_names.h new file mode 100644 index 000000000000..900ae2215f4c --- /dev/null +++ b/components/commands/common/key_names.h @@ -0,0 +1,20 @@ +// Copyright (c) 2023 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_COMPONENTS_COMMANDS_COMMON_KEY_NAMES_H_ +#define BRAVE_COMPONENTS_COMMANDS_COMMON_KEY_NAMES_H_ + +#include +#include + +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace commands { +std::string GetKeyName(ui::KeyboardCode code); +std::vector GetModifierName(ui::KeyEventFlags flags); +} // namespace commands + +#endif // BRAVE_COMPONENTS_COMMANDS_COMMON_KEY_NAMES_H_ diff --git a/components/constants/webui_url_constants.cc b/components/constants/webui_url_constants.cc index 03faebbc8e17..6e9eb6a4b018 100644 --- a/components/constants/webui_url_constants.cc +++ b/components/constants/webui_url_constants.cc @@ -57,3 +57,4 @@ const char kPlaylistHost[] = "playlist"; const char kPlaylistURL[] = "chrome-untrusted://playlist/"; const char kSpeedreaderPanelURL[] = "chrome://brave-speedreader.top-chrome"; const char kSpeedreaderPanelHost[] = "brave-speedreader.top-chrome"; +const char kCommandsHost[] = "commands"; diff --git a/components/constants/webui_url_constants.h b/components/constants/webui_url_constants.h index a8a3b6112a4c..77301a94cd28 100644 --- a/components/constants/webui_url_constants.h +++ b/components/constants/webui_url_constants.h @@ -58,5 +58,6 @@ extern const char kPlaylistHost[]; extern const char kPlaylistURL[]; extern const char kSpeedreaderPanelURL[]; extern const char kSpeedreaderPanelHost[]; +extern const char kCommandsHost[]; #endif // BRAVE_COMPONENTS_CONSTANTS_WEBUI_URL_CONSTANTS_H_ diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn index ea7d974d6b6d..c1ff29d421a9 100644 --- a/components/resources/BUILD.gn +++ b/components/resources/BUILD.gn @@ -124,6 +124,10 @@ repack("resources") { sources += [ "$root_gen_dir/brave/components/playlist/browser/resources/playlist_generated.pak" ] } + deps += + [ "//brave/components/commands/browser/resources:generated_resources" ] + sources += [ "$root_gen_dir/brave/components/commands/browser/resources/commands_generated.pak" ] + output = "$root_gen_dir/components/brave_components_resources.pak" } diff --git a/components/resources/brave_components_resources.grd b/components/resources/brave_components_resources.grd index 214df046b983..1b70e686e568 100644 --- a/components/resources/brave_components_resources.grd +++ b/components/resources/brave_components_resources.grd @@ -64,6 +64,7 @@ + diff --git a/resources/resource_ids.spec b/resources/resource_ids.spec index a8fa337fbc24..ac1b99750ce4 100644 --- a/resources/resource_ids.spec +++ b/resources/resource_ids.spec @@ -182,6 +182,10 @@ "META": {"sizes": {"includes": [250]}}, "includes": [59270] }, + "<(SHARED_INTERMEDIATE_DIR)/brave/web-ui-commands/commands.grd": { + "META": {"sizes": {"includes": [250]}}, + "includes": [59280] + }, "<(SHARED_INTERMEDIATE_DIR)/brave/web-ui-ledger_bridge/ledger_bridge.grd": { "META": {"sizes": {"includes": [250]}}, "includes": [59520] diff --git a/test/BUILD.gn b/test/BUILD.gn index 6b8b48073dab..b1776679369b 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -91,6 +91,7 @@ test("brave_unit_tests") { ] sources = [ + "//brave/app/command_utils_unittest.cc", "//brave/browser/brave_content_browser_client_unittest.cc", "//brave/browser/brave_resources_util_unittest.cc", "//brave/browser/brave_stats/brave_stats_updater_unittest.cc", @@ -218,6 +219,7 @@ test("brave_unit_tests") { "//brave/components/brave_wallet/common:unit_tests", "//brave/components/brave_wallet/renderer/test:unit_tests", "//brave/components/child_process_monitor:unittests", + "//brave/components/commands/common", "//brave/components/constants", "//brave/components/de_amp/browser/test:unit_tests", "//brave/components/debounce/browser/test:unit_tests", @@ -676,6 +678,7 @@ test("brave_browser_tests") { sources = [ "//brave/app/brave_main_delegate_browsertest.cc", "//brave/app/brave_main_delegate_runtime_flags_browsertest.cc", + "//brave/app/command_utils_browsertest.cc", "//brave/browser/brave_ads/ads_service_browsertest.cc", "//brave/browser/brave_ads/brave_stats_updater_helper_browsertest.cc", "//brave/browser/brave_ads/notification_helper/notification_helper_impl_mock.cc", @@ -860,6 +863,7 @@ test("brave_browser_tests") { "//brave/components/brave_wallet/renderer", "//brave/components/brave_wallet/resources:ethereum_provider_generated_resources", "//brave/components/brave_wayback_machine/buildflags", + "//brave/components/commands/common", "//brave/components/constants", "//brave/components/de_amp/browser/test:browser_tests", "//brave/components/de_amp/common:common",