From 48af20fdc09de7f2ac293392776caab55489eb82 Mon Sep 17 00:00:00 2001 From: Jay Harris Date: Tue, 21 Feb 2023 12:35:33 +1300 Subject: [PATCH] Make it possible to view all commands in Brave, and their shortcuts (#16845) Add a WebUI page where it's possible to view all commands and their accelerators --- app/BUILD.gn | 79 +++++ app/command_utils.cc | 246 +++++++++++++++ app/command_utils.h | 26 ++ app/command_utils_browsertest.cc | 76 +++++ app/command_utils_unittest.cc | 28 ++ browser/about_flags.cc | 18 ++ browser/brave_content_browser_client.cc | 9 + browser/sources.gni | 4 + browser/ui/BUILD.gn | 10 + browser/ui/brave_browser_window.cc | 9 + browser/ui/brave_browser_window.h | 9 + browser/ui/views/frame/brave_browser_view.cc | 74 +++-- browser/ui/views/frame/brave_browser_view.h | 4 + .../webui/brave_web_ui_controller_factory.cc | 6 + browser/ui/webui/commands_ui.cc | 99 ++++++ browser/ui/webui/commands_ui.h | 48 +++ components/commands/DEPS | 3 + .../commands/browser/resources/BUILD.gn | 26 ++ .../commands/browser/resources/commands.html | 28 ++ .../commands/browser/resources/commands.tsx | 89 ++++++ .../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 | 34 +++ components/commands/common/commands.mojom | 23 ++ components/commands/common/features.cc | 17 ++ components/commands/common/features.h | 18 ++ components/commands/common/key_names.cc | 288 ++++++++++++++++++ components/commands/common/key_names.h | 27 ++ components/constants/webui_url_constants.cc | 1 + components/constants/webui_url_constants.h | 1 + components/resources/BUILD.gn | 6 + .../resources/brave_components_resources.grd | 1 + resources/resource_ids.spec | 6 +- test/BUILD.gn | 4 +- 37 files changed, 1407 insertions(+), 28 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/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..291b6f133f8a 100644 --- a/app/BUILD.gn +++ b/app/BUILD.gn @@ -13,6 +13,20 @@ import("//build/config/locales.gni") source_set("command_ids") { sources = [ "brave_command_ids.h" ] + + deps = [ + "//base", + "//chrome/app:command_ids", + ] + + if (!is_android && !is_ios) { + sources += [ + "command_utils.cc", + "command_utils.h", + ] + + deps += [ "//brave/components/commands/common" ] + } } brave_grit("brave_generated_resources_grit") { @@ -36,5 +50,70 @@ brave_grit("brave_generated_resources_grit") { } } +source_set("browser_tests") { + testonly = true + + if (!is_android && !is_ios) { + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + + sources = [ + "brave_main_delegate_browsertest.cc", + "brave_main_delegate_runtime_flags_browsertest.cc", + "command_utils_browsertest.cc", + ] + + deps = [ + ":command_ids", + "//base", + "//brave/components/commands/common", + "//chrome/app:command_ids", + "//chrome/browser", + "//chrome/browser/enterprise/connectors/analysis:features", + "//chrome/browser/ui", + "//chrome/common:chrome_features", + "//chrome/common:non_code_constants", + "//chrome/test", + "//chrome/test:test_support", + "//chrome/test:test_support_ui", + "//components/autofill/core/common", + "//components/commerce/core:feature_list", + "//components/component_updater", + "//components/embedder_support", + "//components/history_clusters/core", + "//components/language/core/common", + "//components/lens", + "//components/network_time", + "//components/omnibox/browser", + "//components/optimization_guide/core", + "//components/permissions", + "//components/privacy_sandbox", + "//components/reading_list/features:flags", + "//components/segmentation_platform/public", + "//components/send_tab_to_self", + "//components/subresource_filter/core/common", + "//components/translate/core/common", + "//extensions/common", + "//services/device/public/cpp:device_features", + "//testing/gtest", + ] + } +} + +source_set("unit_tests") { + testonly = true + + if (!is_android && !is_ios) { + sources = [ "command_utils_unittest.cc" ] + deps = [ + ":command_ids", + "//base", + "//base/test:test_support", + "//brave/components/commands/common", + "//chrome/browser/ui/views", + "//testing/gtest", + ] + } +} + group("app") { } diff --git a/app/command_utils.cc b/app/command_utils.cc new file mode 100644 index 000000000000..b0d203bbacec --- /dev/null +++ b/app/command_utils.cc @@ -0,0 +1,246 @@ +// 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 + +#include "base/feature_list.h" +#include "base/no_destructor.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "brave/app/brave_command_ids.h" +#include "brave/components/commands/common/features.h" +#include "chrome/app/chrome_command_ids.h" + +#define ADD_UNTRANSLATED_COMMAND(name) \ + { \ + IDC_##name, { \ + GetName(#name) \ + } \ + } + +namespace commands { +namespace { + +std::string GetName(const 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, " "); +} + +const base::flat_map& GetCommandInfo() { + static const base::NoDestructor> + 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), + +#if BUILDFLAG(IS_LINUX) + ADD_UNTRANSLATED_COMMAND(USE_SYSTEM_TITLE_BAR), + ADD_UNTRANSLATED_COMMAND(RESTORE_WINDOW), +#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; +} + +} // namespace + +const std::vector& GetCommands() { + DCHECK(base::FeatureList::IsEnabled(features::kBraveCommands)) + << "This should only be used when |kBraveCommands| is enabled."; + static base::NoDestructor> result([]() { + std::vector result; + base::ranges::transform( + GetCommandInfo(), std::back_inserter(result), + /*id projection*/ + &base::flat_map::value_type::first); + return result; + }()); + + return *result; +} + +const std::string& GetCommandName(int command_id) { + DCHECK(base::FeatureList::IsEnabled(features::kBraveCommands)) + << "This should only be used when |kBraveCommands| is enabled."; + const auto& info = GetCommandInfo(); + auto it = info.find(command_id); + CHECK(it != info.end()) << "Unknown command " << command_id + << ". This function should only be used for known " + "commands (i.e. commands in |GetCommandInfo|). " + "This command should probably be added."; + return it->second; +} + +} // namespace commands diff --git a/app/command_utils.h b/app/command_utils.h new file mode 100644 index 000000000000..e5dc274ac491 --- /dev/null +++ b/app/command_utils.h @@ -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/. + +#ifndef BRAVE_APP_COMMAND_UTILS_H_ +#define BRAVE_APP_COMMAND_UTILS_H_ + +#include +#include + +namespace commands { + +// Gets the command ids of all commands which don't require parameters and can +// be executed in the main browser window. This is used for listing the +// shortcuts available to users and will eventually be used to allow configuring +// shortcuts. +const std::vector& GetCommands(); + +// Gets a string representing a command. In future this will be translated, but +// while we're prototyping the feature it will always returns English strings. +const 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..889ccb1b946a --- /dev/null +++ b/app/command_utils_browsertest.cc @@ -0,0 +1,76 @@ +// 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/containers/contains.h" +#include "base/test/scoped_feature_list.h" +#include "brave/components/commands/common/features.h" +#include "build/buildflag.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_window.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" +#include "testing/gtest/include/gtest/gtest.h" + +class CommandUtilsBrowserTest : public InProcessBrowserTest { + public: + CommandUtilsBrowserTest() = default; + ~CommandUtilsBrowserTest() override = default; + + void SetUp() override { + features_.InitAndEnableFeature(commands::features::kBraveCommands); + InProcessBrowserTest::SetUp(); + } + + private: + base::test::ScopedFeatureList features_; +}; + +// This test is a sanity check - if commands fail here but work when testing +// things manually there's probably a conflict with some of the other commands, +// in which case we can just add it to the ignored commands list. +IN_PROC_BROWSER_TEST_F(CommandUtilsBrowserTest, + AllCommandsShouldBeExecutableWithoutCrash) { + // Some commands, particularly those that create dialogs introduce some test + // flakes, so we disable them. + constexpr int kKnownGoodCommandsThatSometimesBreakTest[] = { + IDC_PRINT, + IDC_BASIC_PRINT, + IDC_OPEN_FILE, + IDC_SAVE_PAGE, +#if BUILDFLAG(IS_MAC) + IDC_FOCUS_THIS_TAB, + IDC_FOCUS_TOOLBAR, + IDC_FOCUS_LOCATION, + IDC_FOCUS_SEARCH, + IDC_FOCUS_MENU_BAR, + IDC_FOCUS_NEXT_PANE, + IDC_FOCUS_PREVIOUS_PANE, + IDC_FOCUS_BOOKMARKS, + IDC_FOCUS_INACTIVE_POPUP_FOR_ACCESSIBILITY, + IDC_FOCUS_WEB_CONTENTS_PANE, + IDC_TOGGLE_FULLSCREEN_TOOLBAR, + IDC_CONTENT_CONTEXT_EXIT_FULLSCREEN, + IDC_FULLSCREEN, +#endif + + IDC_EXIT + }; + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"))); + const auto& commands = commands::GetCommands(); + for (const auto& command : commands) { + if (base::Contains(kKnownGoodCommandsThatSometimesBreakTest, command)) { + continue; + } + + SCOPED_TRACE(testing::Message() << commands::GetCommandName(command)); + chrome::ExecuteCommand(browser(), command); + } +} diff --git a/app/command_utils_unittest.cc b/app/command_utils_unittest.cc new file mode 100644 index 000000000000..caf0a0c361f4 --- /dev/null +++ b/app/command_utils_unittest.cc @@ -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/. + +#include "brave/app/command_utils.h" + +#include "base/containers/contains.h" +#include "base/test/scoped_feature_list.h" +#include "brave/components/commands/common/features.h" +#include "chrome/browser/ui/views/accelerator_table.h" +#include "testing/gtest/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) { + base::test::ScopedFeatureList features; + features.InitAndEnableFeature(commands::features::kBraveCommands); + + auto accelerators = GetAcceleratorList(); + const auto& commands = commands::GetCommands(); + + for (const auto& accelerator : accelerators) { + EXPECT_TRUE(base::Contains(commands, accelerator.command_id)) + << "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 3ec13a6f46d9..84f130a78e24 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -56,6 +56,8 @@ #if BUILDFLAG(IS_ANDROID) #include "brave/browser/android/preferences/features.h" #include "brave/browser/android/safe_browsing/features.h" +#else +#include "brave/components/commands/common/features.h" #endif using brave_shields::features::kBraveAdblockCnameUncloaking; @@ -205,6 +207,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[] = @@ -627,6 +633,17 @@ constexpr char kRestrictEventSourcePoolDescription[] = #define PLAYLIST_FEATURE_ENTRIES #endif +#if !BUILDFLAG(IS_ANDROID) +#define BRAVE_COMMANDS_FEATURE_ENTRIES \ + {"brave-commands", \ + flag_descriptions::kBraveCommandsName, \ + flag_descriptions::kBraveCommandsDescription, \ + kOsWin | kOsMac | kOsLinux, \ + FEATURE_VALUE_TYPE(commands::features::kBraveCommands)}, +#else +#define BRAVE_COMMANDS_FEATURE_ENTRIES +#endif + #if defined(TOOLKIT_VIEWS) #define BRAVE_VERTICAL_TABS_FEATURE_ENTRY \ {"brave-vertical-tabs", \ @@ -866,6 +883,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 d80abd37a83e..c6a3b2afe6ce 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -188,6 +188,7 @@ using extensions::ChromeContentBrowserClientExtensionsPart; #include "brave/browser/ui/webui/brave_shields/shields_panel_ui.h" #include "brave/browser/ui/webui/brave_wallet/wallet_page_ui.h" #include "brave/browser/ui/webui/brave_wallet/wallet_panel_ui.h" +#include "brave/browser/ui/webui/commands_ui.h" #include "brave/browser/ui/webui/new_tab_page/brave_new_tab_ui.h" #include "brave/browser/ui/webui/private_new_tab_page/brave_private_new_tab_ui.h" #include "brave/components/brave_new_tab_ui/brave_new_tab_page.mojom.h" @@ -198,6 +199,8 @@ using extensions::ChromeContentBrowserClientExtensionsPart; #include "brave/components/brave_shields/common/cookie_list_opt_in.mojom.h" #include "brave/components/brave_today/common/brave_news.mojom.h" #include "brave/components/brave_today/common/features.h" +#include "brave/components/commands/common/commands.mojom.h" +#include "brave/components/commands/common/features.h" #endif #if BUILDFLAG(ENABLE_PLAYLIST) @@ -481,6 +484,12 @@ void BraveContentBrowserClient::RegisterWebUIInterfaceBrokers( .Add(); } #endif + +#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) + if (base::FeatureList::IsEnabled(commands::features::kBraveCommands)) { + registry.ForWebUI().Add(); + } +#endif } bool BraveContentBrowserClient::AllowWorkerFingerprinting( diff --git a/browser/sources.gni b/browser/sources.gni index 64ef2ebee543..11c990d67d10 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -233,6 +233,10 @@ brave_chrome_browser_deps = [ "//url", ] +if (!is_android && !is_ios) { + brave_chrome_browser_deps += [ "//brave/components/commands/common" ] +} + if (is_mac) { brave_chrome_browser_sources += [ "//brave/browser/brave_app_controller_mac.h", diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index c3c88b286298..de6dca9e5137 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -65,6 +65,14 @@ source_set("ui") { "webui/webcompat_reporter_ui.h", ] + # It doesn't make sense to view accelerators on iOS & Android. + if (!is_android && !is_ios) { + sources += [ + "webui/commands_ui.cc", + "webui/commands_ui.h", + ] + } + if (enable_ipfs) { sources += [ "webui/ipfs_ui.cc", @@ -586,6 +594,7 @@ source_set("ui") { "//brave/components/brave_wallet/common:common_utils", "//brave/components/brave_welcome/common", "//brave/components/brave_welcome_ui:generated_resources", + "//brave/components/commands/common", "//brave/components/speedreader/common", "//brave/components/speedreader/common:mojom", "//brave/components/speedreader/resources/panel:generated_resources", @@ -781,6 +790,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/brave_browser_window.cc b/browser/ui/brave_browser_window.cc index bf967aa325f0..f0f4053fc953 100644 --- a/browser/ui/brave_browser_window.cc +++ b/browser/ui/brave_browser_window.cc @@ -5,6 +5,10 @@ #include "brave/browser/ui/brave_browser_window.h" +#include + +#include "ui/base/accelerators/accelerator.h" + // Provide a base implementation (important for `TestBrowserWindow ` in tests) // For real implementation, see `BraveBrowserView`. @@ -18,6 +22,11 @@ gfx::Rect BraveBrowserWindow::GetShieldsBubbleRect() { return gfx::Rect(); } +std::map> +BraveBrowserWindow::GetAcceleratedCommands() { + return std::map>(); +} + // static BraveBrowserWindow* BraveBrowserWindow::From(BrowserWindow* window) { return static_cast(window); diff --git a/browser/ui/brave_browser_window.h b/browser/ui/brave_browser_window.h index 34b68dc22c15..266a47e87498 100644 --- a/browser/ui/brave_browser_window.h +++ b/browser/ui/brave_browser_window.h @@ -6,6 +6,9 @@ #ifndef BRAVE_BROWSER_UI_BRAVE_BROWSER_WINDOW_H_ #define BRAVE_BROWSER_UI_BRAVE_BROWSER_WINDOW_H_ +#include +#include + #include "brave/components/speedreader/common/buildflags/buildflags.h" #include "chrome/browser/ui/browser_window.h" @@ -24,6 +27,10 @@ class SpeedreaderBubbleView; class SpeedreaderTabHelper; } // namespace speedreader +namespace ui { +class Accelerator; +} + class BraveBrowserWindow : public BrowserWindow { public: ~BraveBrowserWindow() override {} @@ -37,6 +44,8 @@ class BraveBrowserWindow : public BrowserWindow { // the overall screen's height virtual gfx::Rect GetShieldsBubbleRect(); + virtual std::map> GetAcceleratedCommands(); + #if BUILDFLAG(ENABLE_SPEEDREADER) virtual speedreader::SpeedreaderBubbleView* ShowSpeedreaderBubble( speedreader::SpeedreaderTabHelper* tab_helper, diff --git a/browser/ui/views/frame/brave_browser_view.cc b/browser/ui/views/frame/brave_browser_view.cc index 27d1090b05f2..260b4a5a5e6b 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" @@ -41,6 +43,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" @@ -82,9 +85,7 @@ class BraveBrowserView::TabCyclingEventHandler : public ui::EventObserver, Start(); } - ~TabCyclingEventHandler() override { - Stop(); - } + ~TabCyclingEventHandler() override { Stop(); } TabCyclingEventHandler(const TabCyclingEventHandler&) = delete; TabCyclingEventHandler& operator=(const TabCyclingEventHandler&) = delete; @@ -99,29 +100,28 @@ class BraveBrowserView::TabCyclingEventHandler : public ui::EventObserver, return; } - if (event.type() == ui::ET_MOUSE_PRESSED) + if (event.type() == ui::ET_MOUSE_PRESSED) { Stop(); + } } // views::WidgetObserver overrides: void OnWidgetActivationChanged(views::Widget* widget, bool active) override { // We should stop cycling if other application gets active state. - if (!active) + if (!active) { Stop(); + } } // 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}); } @@ -129,9 +129,10 @@ class BraveBrowserView::TabCyclingEventHandler : public ui::EventObserver, } void Stop() { - if (!monitor_.get()) + if (!monitor_.get()) { // We already stopped return; + } // Remove event handler auto* widget = browser_view_->GetWidget(); @@ -202,8 +203,9 @@ BraveBrowserView::BraveBrowserView(std::unique_ptr browser) base::Unretained(this))); } - if (!supports_vertical_tabs && !can_have_sidebar) + if (!supports_vertical_tabs && !can_have_sidebar) { return; + } // Make sure |find_bar_host_view_| is the last child of BrowserView by // re-ordering. FindBarHost widgets uses this view as a kHostViewKey. @@ -276,8 +278,9 @@ views::View* BraveBrowserView::GetAnchorViewForBraveVPNPanel() { #if BUILDFLAG(ENABLE_BRAVE_VPN) auto* vpn_button = static_cast(toolbar())->brave_vpn_button(); - if (vpn_button->GetVisible()) + if (vpn_button->GetVisible()) { return vpn_button; + } return toolbar()->app_menu_button(); #else return nullptr; @@ -287,25 +290,29 @@ views::View* BraveBrowserView::GetAnchorViewForBraveVPNPanel() { gfx::Rect BraveBrowserView::GetShieldsBubbleRect() { auto* brave_location_bar_view = static_cast(GetLocationBarView()); - if (!brave_location_bar_view) + if (!brave_location_bar_view) { return gfx::Rect(); + } auto* shields_action_view = brave_location_bar_view->brave_actions_contatiner_view() ->GetShieldsActionView(); - if (!shields_action_view) + if (!shields_action_view) { return gfx::Rect(); + } auto* bubble_widget = shields_action_view->GetBubbleWidget(); - if (!bubble_widget) + if (!bubble_widget) { return gfx::Rect(); + } return bubble_widget->GetClientAreaBoundsInScreen(); } bool BraveBrowserView::GetTabStripVisible() const { - if (!base::FeatureList::IsEnabled(tabs::features::kBraveVerticalTabs)) + if (!base::FeatureList::IsEnabled(tabs::features::kBraveVerticalTabs)) { return BrowserView::GetTabStripVisible(); + } if (tabs::utils::ShouldShowVerticalTabs(browser())) { return false; @@ -316,8 +323,9 @@ bool BraveBrowserView::GetTabStripVisible() const { #if BUILDFLAG(IS_WIN) bool BraveBrowserView::GetSupportsTitle() const { - if (!base::FeatureList::IsEnabled(tabs::features::kBraveVerticalTabs)) + if (!base::FeatureList::IsEnabled(tabs::features::kBraveVerticalTabs)) { return BrowserView::GetSupportsTitle(); + } if (tabs::utils::SupportsVerticalTabs(browser())) { return true; @@ -330,8 +338,9 @@ bool BraveBrowserView::GetSupportsTitle() const { void BraveBrowserView::SetStarredState(bool is_starred) { BookmarkButton* button = static_cast(toolbar())->bookmark_button(); - if (button) + if (button) { button->SetToggled(is_starred); + } } void BraveBrowserView::ShowSpeedreaderWebUIBubble(Browser* browser) { @@ -420,6 +429,17 @@ views::View* BraveBrowserView::GetWalletButtonAnchorView() { ->GetAsAnchorView(); } +std::map> +BraveBrowserView::GetAcceleratedCommands() { + // In future, it will be possible to customize this map so users can configure + // their own keyboard shortcuts. + std::map> result; + for (const auto& [accelerator, command] : accelerator_table_) { + result[command].push_back(accelerator); + } + return result; +} + void BraveBrowserView::CreateWalletBubble() { DCHECK(GetWalletButton()); GetWalletButton()->ShowWalletBubble(); @@ -431,8 +451,9 @@ void BraveBrowserView::CreateApproveWalletBubble() { } void BraveBrowserView::CloseWalletBubble() { - if (GetWalletButton()) + if (GetWalletButton()) { GetWalletButton()->CloseWalletBubble(); + } } void BraveBrowserView::AddedToWidget() { @@ -534,16 +555,19 @@ void BraveBrowserView::OnWidgetActivationChanged(views::Widget* widget, // from other windows. So, simply trying to update when window activation // state is changed. With this, active window could have correct sidebar item // state. - if (sidebar_container_view_) + if (sidebar_container_view_) { sidebar_container_view_->UpdateSidebar(); + } } bool BraveBrowserView::ShouldShowWindowTitle() const { - if (BrowserView::ShouldShowWindowTitle()) + if (BrowserView::ShouldShowWindowTitle()) { return true; + } - if (!base::FeatureList::IsEnabled(tabs::features::kBraveVerticalTabs)) + if (!base::FeatureList::IsEnabled(tabs::features::kBraveVerticalTabs)) { return false; + } if (tabs::utils::ShouldShowWindowTitleForVerticalTabs(browser())) { return true; @@ -575,6 +599,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 636741b4af8d..ae669bc55ffc 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() override; // 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..ae1a22d20acb 100644 --- a/browser/ui/webui/brave_web_ui_controller_factory.cc +++ b/browser/ui/webui/brave_web_ui_controller_factory.cc @@ -42,12 +42,14 @@ #include "brave/browser/ui/webui/brave_wallet/wallet_page_ui.h" #include "brave/browser/ui/webui/brave_wallet/wallet_panel_ui.h" #include "brave/browser/ui/webui/brave_welcome_ui.h" +#include "brave/browser/ui/webui/commands_ui.h" #include "brave/browser/ui/webui/new_tab_page/brave_new_tab_ui.h" #include "brave/browser/ui/webui/private_new_tab_page/brave_private_new_tab_ui.h" #include "brave/browser/ui/webui/speedreader/speedreader_panel_ui.h" #include "brave/components/brave_wallet/browser/brave_wallet_utils.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" #include "brave/components/brave_wallet/common/common_util.h" +#include "brave/components/commands/common/features.h" #endif #include "brave/browser/brave_vpn/vpn_utils.h" @@ -96,6 +98,9 @@ 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::kBraveCommands)) { + 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 +202,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..0e35f4001371 --- /dev/null +++ b/browser/ui/webui/commands_ui.cc @@ -0,0 +1,99 @@ +// 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/feature_list.h" +#include "base/strings/utf_string_conversions.h" +#include "brave/app/command_utils.h" +#include "brave/browser/ui/brave_browser_window.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/features.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) { + DCHECK(base::FeatureList::IsEnabled(features::kBraveCommands)); + 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) { + const auto& command_ids = commands::GetCommands(); + auto accelerated_commands = GetWindow()->GetAcceleratedCommands(); + + std::vector result; + for (const auto& command_id : command_ids) { + if (!chrome::SupportsCommand(GetBrowser(), command_id)) { + continue; + } + + auto command = Command::New(); + command->id = command_id; + command->name = commands::GetCommandName(command_id); + command->enabled = chrome::IsCommandEnabled(GetBrowser(), 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()); + + DCHECK(!a->keycode.empty()) + << "Found an accelerator which didn't have any keys assigned (" + << command->name << ")"; + DCHECK(a->modifiers.size() == 0 || accel.modifiers() != ui::EF_NONE) + << "Found an accelerator which we didn't understand the modifiers " + "for (" + << command->name << ")"; + 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(GetBrowser(), command_id); +} + +Browser* CommandsUI::GetBrowser() { + return chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()); +} + +BraveBrowserWindow* CommandsUI::GetWindow() { + return static_cast(GetBrowser()->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..9f7170640ac4 --- /dev/null +++ b/browser/ui/webui/commands_ui.h @@ -0,0 +1,48 @@ +// 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/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" + +class Browser; +class BraveBrowserWindow; + +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* GetBrowser(); + BraveBrowserWindow* GetWindow(); + + 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/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..cf5570bab55d --- /dev/null +++ b/components/commands/browser/resources/commands.html @@ -0,0 +1,28 @@ + + + + + + + + + Brave Commands + + + + + + +
+ + + diff --git a/components/commands/browser/resources/commands.tsx b/components/commands/browser/resources/commands.tsx new file mode 100644 index 000000000000..eed59386d902 --- /dev/null +++ b/components/commands/browser/resources/commands.tsx @@ -0,0 +1,89 @@ +// 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..35c00809b727 --- /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 type * 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..06781bc7fa78 --- /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 { type 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..121caa00df15 --- /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 { type Command } from '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..5b34e0c42fa1 --- /dev/null +++ b/components/commands/common/BUILD.gn @@ -0,0 +1,34 @@ +# 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") + +component("common") { + output_name = "commands_common" + defines = [ "IS_COMMANDS_COMMON_IMPL" ] + + 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_component("mojom") { + output_prefix = "brave_commands_mojom" + macro_prefix = "BRAVE_COMMANDS_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..5b38acc8d151 --- /dev/null +++ b/components/commands/common/features.cc @@ -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/. + +#include "brave/components/commands/common/features.h" + +namespace commands::features { + +// This feature enables a page a brave://commands which lists all available +// commands in Brave, and the shortcut for executing them. In future, this will +// allow configuring the shortcuts for various commands. +BASE_FEATURE(kBraveCommands, + "BraveCommands", + base::FEATURE_DISABLED_BY_DEFAULT); + +} // namespace commands::features diff --git a/components/commands/common/features.h b/components/commands/common/features.h new file mode 100644 index 000000000000..99e8299fbb3d --- /dev/null +++ b/components/commands/common/features.h @@ -0,0 +1,18 @@ +// 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/component_export.h" +#include "base/feature_list.h" + +namespace commands::features { + +COMPONENT_EXPORT(COMMANDS_COMMON) BASE_DECLARE_FEATURE(kBraveCommands); + +} // 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..b8189f803ffe --- /dev/null +++ b/components/commands/common/key_names.cc @@ -0,0 +1,288 @@ +// 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" + +#include "build/build_config.h" + +namespace commands { + +std::string GetKeyName(ui::KeyboardCode code) { + switch (code) { +#if !BUILDFLAG(IS_WIN) + case ui::VKEY_BACKTAB: + return "Backtab"; + case ui::VKEY_NEW: + return "New"; + case ui::VKEY_CLOSE: + return "Close"; + case ui::VKEY_MEDIA_PLAY: + return "Play"; + case ui::VKEY_MEDIA_PAUSE: + return "Pause"; +#endif + 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 "Alt"; + 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_BACK: + return "Back"; + case ui::VKEY_DELETE: + return "Delete"; + case ui::VKEY_MEDIA_PLAY_PAUSE: + return "Play 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_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..c05edacec1f5 --- /dev/null +++ b/components/commands/common/key_names.h @@ -0,0 +1,27 @@ +// 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 "base/component_export.h" +#include "ui/events/event_constants.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace commands { + +// Gets a string representing a keyboard key. +COMPONENT_EXPORT(COMMANDS_COMMON) std::string GetKeyName(ui::KeyboardCode code); + +// Gets a string representing an accelerator modifier. +COMPONENT_EXPORT(COMMANDS_COMMON) +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 93fb6952e200..366c771bfae1 100644 --- a/components/resources/BUILD.gn +++ b/components/resources/BUILD.gn @@ -124,6 +124,12 @@ repack("resources") { sources += [ "$root_gen_dir/brave/components/playlist/browser/resources/playlist_generated.pak" ] } + if (!is_android && !is_ios) { + 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..e46e71c03cc2 100644 --- a/components/resources/brave_components_resources.grd +++ b/components/resources/brave_components_resources.grd @@ -63,6 +63,7 @@ + diff --git a/resources/resource_ids.spec b/resources/resource_ids.spec index 0878f016a243..a7771c546796 100644 --- a/resources/resource_ids.spec +++ b/resources/resource_ids.spec @@ -205,5 +205,9 @@ "<(SHARED_INTERMEDIATE_DIR)/brave/web-ui-brave_adblock_internals/brave_adblock_internals.grd": { "META": {"sizes": {"includes": [50]}}, "includes": [59840], - } + }, + "<(SHARED_INTERMEDIATE_DIR)/brave/web-ui-commands/commands.grd": { + "META": {"sizes": {"includes": [250]}}, + "includes": [60090] + }, } diff --git a/test/BUILD.gn b/test/BUILD.gn index 7164c1d0de57..324a5097eef8 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -163,6 +163,7 @@ test("brave_unit_tests") { ":crypto_unittests", ":test_support", "//brave/app:command_ids", + "//brave/app:unit_tests", "//brave/app/theme:brave_theme_resources_grit", "//brave/base:base_unittests", "//brave/browser", @@ -678,8 +679,6 @@ test("brave_browser_tests") { ] sources = [ - "//brave/app/brave_main_delegate_browsertest.cc", - "//brave/app/brave_main_delegate_runtime_flags_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", @@ -801,6 +800,7 @@ test("brave_browser_tests") { deps = [ "//brave/app:brave_generated_resources_grit", + "//brave/app:browser_tests", "//brave/app:command_ids", "//brave/app/theme:brave_theme_resources_grit", "//brave/app/theme:brave_unscaled_resources_grit",