Omnibox Command Palette (#16947)

* Add Commander to Omnibox
This commit is contained in:
Jay Harris
2023-03-31 14:07:36 +13:00
committed by GitHub
parent a40703b406
commit 3c22732081
50 changed files with 1827 additions and 19 deletions
+1
View File
@@ -31,6 +31,7 @@
#include "brave/components/skus/common/features.h"
#include "brave/components/speedreader/common/buildflags/buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/ui/ui_features.h"
#include "components/content_settings/core/common/features.h"
#include "components/flags_ui/feature_entry.h"
#include "components/flags_ui/feature_entry_macros.h"
+1
View File
@@ -232,6 +232,7 @@ brave_chrome_browser_deps = [
if (!is_android && !is_ios) {
brave_chrome_browser_deps += [
"//brave/components/commander/browser",
"//brave/components/commands/browser",
"//brave/components/commands/common",
]
+8
View File
@@ -88,6 +88,10 @@ source_set("ui") {
"browser_commands.cc",
"browser_commands.h",
"browser_dialogs.h",
"commander/commander_service.cc",
"commander/commander_service.h",
"commander/commander_service_factory.cc",
"commander/commander_service_factory.h",
"content_settings/brave_autoplay_blocked_image_model.cc",
"content_settings/brave_autoplay_blocked_image_model.h",
"content_settings/brave_autoplay_content_setting_bubble_model.cc",
@@ -535,6 +539,10 @@ source_set("ui") {
deps += [ "//brave/components/text_recognition/browser" ]
}
if (!is_android && !is_ios) {
deps += [ "//brave/components/commander/browser" ]
}
# This is no longer compiled into Chromium on Android, but we still
# need it
if (is_android) {
+24
View File
@@ -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/.
source_set("browser_tests") {
testonly = true
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
sources = [ "commander_service_browsertest.cc" ]
deps = [
"//base",
"//brave/components/commander/browser",
"//chrome/browser/profiles",
"//chrome/browser/profiles:profile",
"//chrome/browser/ui",
"//chrome/test:test_support_ui",
"//components/omnibox/browser",
"//content/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
+255
View File
@@ -0,0 +1,255 @@
// 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/commander/commander_service.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "brave/components/commander/browser/commander_item_model.h"
#include "brave/components/commander/common/constants.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/commander/bookmark_command_source.h"
#include "chrome/browser/ui/commander/command_source.h"
#include "chrome/browser/ui/commander/commander.h"
#include "chrome/browser/ui/commander/commander_view_model.h"
#include "chrome/browser/ui/commander/open_url_command_source.h"
#include "chrome/browser/ui/commander/simple_command_source.h"
#include "chrome/browser/ui/commander/tab_command_source.h"
#include "chrome/browser/ui/commander/window_command_source.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/omnibox_view.h"
namespace commander {
namespace {
constexpr size_t kMaxResults = 8;
CommandItemModel FromCommand(const std::unique_ptr<CommandItem>& item) {
return CommandItemModel(item->title, item->matched_ranges, item->annotation);
}
} // namespace
CommanderService::CommanderService(Profile* profile) : profile_(profile) {
command_sources_.push_back(std::make_unique<SimpleCommandSource>());
command_sources_.push_back(std::make_unique<OpenURLCommandSource>());
command_sources_.push_back(std::make_unique<BookmarkCommandSource>());
command_sources_.push_back(std::make_unique<WindowCommandSource>());
command_sources_.push_back(std::make_unique<TabCommandSource>());
}
CommanderService::~CommanderService() = default;
void CommanderService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
observer->OnCommanderUpdated();
}
void CommanderService::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void CommanderService::UpdateText(bool force) {
auto* browser = chrome::FindLastActiveWithProfile(profile_);
// The last active browser can have no tabs, if we're in the process of moving
// the last tab from the current window into another one.
if (!browser || browser->tab_strip_model()->empty()) {
return;
}
auto* window = browser->window();
CHECK(window);
auto text = window->GetLocationBar()->GetOmniboxView()->GetText();
if (!base::StartsWith(text, kCommandPrefix)) {
return;
}
std::u16string trimmed_text(base::TrimWhitespace(
text.substr(kCommandPrefix.size()), base::TRIM_LEADING));
// If nothing has changed (and we aren't forcing things), don't update the
// commands.
if (trimmed_text == last_searched_ && browser == last_browser_ && !force) {
return;
}
last_searched_ = trimmed_text;
last_browser_ = browser;
UpdateCommands();
}
void CommanderService::SelectCommand(uint32_t command_index,
uint32_t result_set_id) {
if (command_index >= items_.size() ||
result_set_id != current_result_set_id_) {
return;
}
// Increment the current result set id - we don't want any commands from this
// set to be reused after we've selected a command.
// Note: This needs to happen before beginning a composite command, to ensure
// that the generated model uses right right |result_set_id|.
current_result_set_id_++;
auto* item = items_[command_index].get();
if (item->GetType() == CommandItem::Type::kOneShot) {
std::move(absl::get<base::OnceClosure>(item->command)).Run();
Hide();
} else {
auto composite_command =
absl::get<CommandItem::CompositeCommand>(item->command);
std::tie(prompt_, composite_command_provider_) = composite_command;
Show();
}
}
std::vector<CommandItemModel> CommanderService::GetItems() {
std::vector<CommandItemModel> result;
base::ranges::transform(items_, std::back_inserter(result), FromCommand);
return result;
}
int CommanderService::GetResultSetId() {
return current_result_set_id_;
}
const std::u16string& CommanderService::GetPrompt() {
return prompt_;
}
void CommanderService::Shutdown() {
weak_ptr_factory_.InvalidateWeakPtrs();
}
void CommanderService::Toggle() {
if (IsShowing()) {
Hide();
} else {
Show();
}
}
void CommanderService::Show() {
// Note: This posts a task because we can't change the Omnibox text while
// autocompleting.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&CommanderService::ShowCommander,
weak_ptr_factory_.GetWeakPtr()));
}
void CommanderService::Hide() {
// Note: This posts a task because we can't change the Omnibox text while
// autocompleting.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&CommanderService::HideCommander,
weak_ptr_factory_.GetWeakPtr()));
}
void CommanderService::Reset() {
current_result_set_id_++;
items_.clear();
prompt_.clear();
last_searched_.clear();
last_browser_ = nullptr;
if (composite_command_provider_) {
composite_command_provider_.Reset();
}
NotifyObservers();
}
OmniboxView* CommanderService::GetOmnibox() const {
auto* browser = chrome::FindLastActiveWithProfile(profile_);
if (!browser) {
return nullptr;
}
auto* window = browser->window();
CHECK(window);
return window->GetLocationBar()->GetOmniboxView();
}
bool CommanderService::IsShowing() const {
auto* omnibox = GetOmnibox();
return omnibox && omnibox->GetText().starts_with(kCommandPrefix.data());
}
void CommanderService::UpdateCommands() {
std::vector<std::unique_ptr<CommandItem>> items;
if (composite_command_provider_) {
items = composite_command_provider_.Run(last_searched_);
} else {
for (auto& source : command_sources_) {
auto commands = source->GetCommands(last_searched_, last_browser_);
items.insert(items.end(), std::make_move_iterator(commands.begin()),
std::make_move_iterator(commands.end()));
}
}
// Sort at most |kMaxResults| by score and then alphabetically.
auto max_elements = std::min(items.size(), kMaxResults);
std::partial_sort(
std::begin(items), std::begin(items) + max_elements, std::end(items),
[](const std::unique_ptr<CommandItem>& left,
const std::unique_ptr<CommandItem>& right) {
return (left->score == right->score) ? left->title < right->title
: left->score > right->score;
});
if (items.size() > kMaxResults) {
items.resize(kMaxResults);
}
items_ = std::move(items);
// Increment the current result set id, so we don't confuse these results with
// a prior set before notifying observers.
current_result_set_id_++;
NotifyObservers();
}
void CommanderService::NotifyObservers() {
for (auto& observer : observers_) {
observer.OnCommanderUpdated();
}
}
void CommanderService::ShowCommander() {
if (auto* omnibox = GetOmnibox()) {
omnibox->SetFocus(true);
auto text = base::StrCat({commander::kCommandPrefix, u" "});
omnibox->SetUserText(text);
omnibox->SetCaretPos(text.size());
UpdateText(true);
}
}
void CommanderService::HideCommander() {
Reset();
if (auto* omnibox = GetOmnibox(); omnibox && IsShowing()) {
omnibox->RevertAll();
}
}
} // namespace commander
+80
View File
@@ -0,0 +1,80 @@
// 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_COMMANDER_COMMANDER_SERVICE_H_
#define BRAVE_BROWSER_UI_COMMANDER_COMMANDER_SERVICE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "brave/components/commander/browser/commander_item_model.h"
#include "chrome/browser/ui/commander/command_source.h"
#include "components/keyed_service/core/keyed_service.h"
class OmniboxView;
class Profile;
namespace commander {
class CommanderService : public CommanderFrontendDelegate, public KeyedService {
public:
using CommandSources = std::vector<std::unique_ptr<CommandSource>>;
explicit CommanderService(Profile* profile);
CommanderService(const CommanderService&) = delete;
CommanderService& operator=(const CommanderService&) = delete;
~CommanderService() override;
void Show();
void Reset();
bool IsShowing() const;
// CommanderFrontendDelegate:
void Toggle() override;
void Hide() override;
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
void UpdateText(bool force = false) override;
void SelectCommand(uint32_t command_index, uint32_t result_set_id) override;
std::vector<CommandItemModel> GetItems() override;
int GetResultSetId() override;
const std::u16string& GetPrompt() override;
// KeyedService:
void Shutdown() override;
private:
OmniboxView* GetOmnibox() const;
void UpdateCommands();
void NotifyObservers();
void ShowCommander();
void HideCommander();
CommandSources command_sources_;
std::u16string last_searched_;
std::u16string prompt_;
std::vector<std::unique_ptr<CommandItem>> items_;
uint32_t current_result_set_id_ = 0;
raw_ptr<Browser> last_browser_;
raw_ptr<Profile> profile_;
// Some commands have multiple steps (like move tab to window, pick a
// window). This allows commands to specify a command provider for a
// subsequent step (in the window example, this would be a list of all
// available windows).
CommandItem::CompositeCommandProvider composite_command_provider_;
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<CommanderService> weak_ptr_factory_{this};
};
} // namespace commander
#endif // BRAVE_BROWSER_UI_COMMANDER_COMMANDER_SERVICE_H_
@@ -0,0 +1,189 @@
// 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 <memory>
#include <string>
#include "base/functional/callback_forward.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "brave/browser/ui/commander/commander_service.h"
#include "brave/browser/ui/commander/commander_service_factory.h"
#include "brave/components/commander/common/constants.h"
#include "brave/components/commander/common/features.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
class CommanderServiceBrowserTest : public InProcessBrowserTest {
public:
CommanderServiceBrowserTest() {
features_.InitAndEnableFeature(features::kBraveCommander);
}
~CommanderServiceBrowserTest() override = default;
void SetUp() override { InProcessBrowserTest::SetUp(); }
void TearDownOnMainThread() override {
commander()->Hide();
WaitUntil(base::BindLambdaForTesting(
[this]() { return !commander()->IsShowing(); }));
}
protected:
Profile* profile() { return browser()->profile(); }
commander::CommanderService* commander() {
return commander::CommanderServiceFactory::GetForBrowserContext(profile());
}
OmniboxView* omnibox() {
return browser()->window()->GetLocationBar()->GetOmniboxView();
}
void WaitUntil(base::RepeatingCallback<bool()> condition) {
if (condition.Run()) {
return;
}
base::RepeatingTimer scheduler;
scheduler.Start(FROM_HERE, base::Milliseconds(100),
base::BindLambdaForTesting([this, &condition]() {
if (condition.Run()) {
run_loop_->Quit();
}
}));
Run();
}
private:
void Run() {
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
}
base::test::ScopedFeatureList features_;
std::unique_ptr<base::RunLoop> run_loop_;
};
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest, CanShowCommander) {
EXPECT_FALSE(commander()->IsShowing());
commander()->Show();
WaitUntil(
base::BindLambdaForTesting([&]() { return commander()->IsShowing(); }));
}
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest,
CanShowCommanderViaOmniboxText) {
EXPECT_FALSE(commander()->IsShowing());
omnibox()->SetUserText(
base::StrCat({commander::kCommandPrefix, u" Hello World"}));
WaitUntil(
base::BindLambdaForTesting([&]() { return commander()->IsShowing(); }));
}
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest, CanHideCommander) {
commander()->Show();
WaitUntil(
base::BindLambdaForTesting([&]() { return commander()->IsShowing(); }));
commander()->Hide();
WaitUntil(
base::BindLambdaForTesting([&]() { return !commander()->IsShowing(); }));
}
// NOTE: This test will pass in isolation but they depend on focus
// so they'll fail if run with other tests. It'd be a good candidate for an
// interactive UI test.
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest, MANUAL_HideClearsText) {
commander()->Show();
omnibox()->SetUserText(
base::StrCat({commander::kCommandPrefix, u" Hello World"}));
commander()->Hide();
WaitUntil(
base::BindLambdaForTesting([&]() { return !commander()->IsShowing(); }));
EXPECT_EQ(u"about:blank", omnibox()->GetText());
}
// NOTE: This test will pass in isolation but they depend on focus
// so they'll fail if run with other tests. It'd be a good candidate for an
// interactive UI test.
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest,
MANUAL_CanHideCommanderViaText) {
omnibox()->SetUserText(
base::StrCat({commander::kCommandPrefix, u" Hello World"}));
WaitUntil(
base::BindLambdaForTesting([&]() { return commander()->IsShowing(); }));
omnibox()->SetUserText(u"Hello World");
WaitUntil(
base::BindLambdaForTesting([&]() { return !commander()->IsShowing(); }));
}
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest,
CommandsAreUpdatedViaOmnibox) {
omnibox()->SetUserText(
base::StrCat({commander::kCommandPrefix, u" Create tab"}));
EXPECT_LE(1, commander()->GetResultSetId());
auto items = commander()->GetItems();
ASSERT_EQ(1u, items.size());
EXPECT_EQ(u"Create new tab", items[0].title);
}
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest, CommandsCanBeSelected) {
omnibox()->SetUserText(
base::StrCat({commander::kCommandPrefix, u" Create tab"}));
EXPECT_LE(1, commander()->GetResultSetId());
auto items = commander()->GetItems();
ASSERT_EQ(1u, items.size());
EXPECT_EQ(u"Create new tab", items[0].title);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
commander()->SelectCommand(0, commander()->GetResultSetId());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
}
IN_PROC_BROWSER_TEST_F(CommanderServiceBrowserTest,
CompositeCommandsCanBeSelected) {
omnibox()->SetUserText(
base::StrCat({commander::kCommandPrefix, u" Pin tab"}));
EXPECT_LE(1, commander()->GetResultSetId());
auto items = commander()->GetItems();
ASSERT_EQ(2u, items.size());
EXPECT_EQ(u"Pin tab...", items[0].title);
EXPECT_EQ(u"Close unpinned tabs", items[1].title);
commander()->SelectCommand(0, 1);
EXPECT_LE(2, commander()->GetResultSetId());
// This is retriggered on a different thread normally, but we want to force it
// here because otherwise the tests get a bit flakey with focus.
omnibox()->SetUserText(commander::kCommandPrefix.data());
items = commander()->GetItems();
ASSERT_EQ(1u, items.size());
EXPECT_EQ(u"about:blank", items[0].title);
EXPECT_FALSE(browser()->tab_strip_model()->IsTabPinned(0));
commander()->SelectCommand(0, commander()->GetResultSetId());
EXPECT_TRUE(browser()->tab_strip_model()->IsTabPinned(0));
}
@@ -0,0 +1,39 @@
// 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/commander/commander_service_factory.h"
#include "base/memory/singleton.h"
#include "brave/browser/ui/commander/commander_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/browser_context.h"
namespace commander {
// static
CommanderServiceFactory* CommanderServiceFactory::GetInstance() {
return base::Singleton<CommanderServiceFactory>::get();
}
// static
CommanderService* CommanderServiceFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<CommanderService*>(
GetInstance()->GetServiceForBrowserContext(context, true));
}
CommanderServiceFactory::CommanderServiceFactory()
: ProfileKeyedServiceFactory("CommanderService",
ProfileSelections::BuildForAllProfiles()) {}
CommanderServiceFactory::~CommanderServiceFactory() = default;
KeyedService* CommanderServiceFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new CommanderService(Profile::FromBrowserContext(context));
}
} // namespace commander
@@ -0,0 +1,38 @@
// 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_COMMANDER_COMMANDER_SERVICE_FACTORY_H_
#define BRAVE_BROWSER_UI_COMMANDER_COMMANDER_SERVICE_FACTORY_H_
#include "base/memory/singleton.h"
#include "brave/browser/ui/commander/commander_service.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/browser_context.h"
namespace commander {
class CommanderServiceFactory : public ProfileKeyedServiceFactory {
public:
CommanderServiceFactory(const CommanderServiceFactory&) = delete;
CommanderServiceFactory& operator=(const CommanderServiceFactory&) = delete;
static CommanderServiceFactory* GetInstance();
static CommanderService* GetForBrowserContext(
content::BrowserContext* context);
private:
CommanderServiceFactory();
~CommanderServiceFactory() override;
friend struct base::DefaultSingletonTraits<CommanderServiceFactory>;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
};
} // namespace commander
#endif // BRAVE_BROWSER_UI_COMMANDER_COMMANDER_SERVICE_FACTORY_H_
@@ -14,10 +14,13 @@
#include "brave/browser/profiles/profile_util.h"
#include "brave/browser/themes/brave_theme_service.h"
#include "brave/browser/ui/color/brave_color_id.h"
#include "brave/browser/ui/commander/commander_service_factory.h"
#include "brave/browser/ui/views/brave_actions/brave_actions_container.h"
#include "brave/browser/ui/views/location_bar/brave_news_location_view.h"
#include "brave/browser/ui/views/toolbar/brave_toolbar_view.h"
#include "brave/components/brave_news/common/features.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "brave/components/commander/common/features.h"
#include "brave/components/l10n/common/localization_util.h"
#include "brave/grit/brave_theme_resources.h"
#include "chrome/browser/profiles/profile.h"
@@ -89,8 +92,9 @@ void BraveLocationBarView::Init() {
focus_ring->SetPathGenerator(
std::make_unique<
BraveLocationBarViewFocusRingHighlightPathGenerator>());
if (const auto color_id = GetFocusRingColor(profile()))
if (const auto color_id = GetFocusRingColor(profile())) {
focus_ring->SetColorId(color_id.value());
}
}
if (base::FeatureList::IsEnabled(brave_news::features::kBraveNewsFeature) &&
@@ -119,8 +123,9 @@ void BraveLocationBarView::Init() {
Update(nullptr);
// Stop slide animation for all content settings views icon.
for (auto* content_setting_view : content_setting_views_)
for (auto* content_setting_view : content_setting_views_) {
content_setting_view->disable_animation();
}
}
bool BraveLocationBarView::ShouldShowIPFSLocationView() const {
@@ -128,8 +133,9 @@ bool BraveLocationBarView::ShouldShowIPFSLocationView() const {
const GURL& url = GetLocationBarModel()->GetURL();
if (!ipfs::IpfsServiceFactory::IsIpfsEnabled(profile_) ||
!ipfs::IsIPFSScheme(url) ||
!ipfs::IsLocalGatewayConfigured(profile_->GetPrefs()))
!ipfs::IsLocalGatewayConfigured(profile_->GetPrefs())) {
return false;
}
return true;
#else
@@ -146,12 +152,14 @@ void BraveLocationBarView::Update(content::WebContents* contents) {
auto show_page_actions = !ShouldHidePageActionIcons();
#if BUILDFLAG(ENABLE_TOR)
if (onion_location_view_)
if (onion_location_view_) {
onion_location_view_->Update(contents, show_page_actions);
}
#endif
#if BUILDFLAG(ENABLE_IPFS)
if (ipfs_location_view_)
if (ipfs_location_view_) {
ipfs_location_view_->Update(contents, show_page_actions);
}
#endif
if (brave_news_location_view_) {
@@ -160,12 +168,14 @@ void BraveLocationBarView::Update(content::WebContents* contents) {
LocationBarView::Update(contents);
if (!ShouldShowIPFSLocationView())
if (!ShouldShowIPFSLocationView()) {
return;
}
// Secure display text for a page was set by chromium.
// We do not want to override this.
if (!GetLocationBarModel()->GetSecureDisplayText().empty())
if (!GetLocationBarModel()->GetSecureDisplayText().empty()) {
return;
}
auto badge_text =
brave_l10n::GetLocalizedResourceUTF16String(IDS_IPFS_BADGE_TITLE);
location_icon_view()->SetLabel(badge_text);
@@ -174,30 +184,40 @@ void BraveLocationBarView::Update(content::WebContents* contents) {
ui::ImageModel BraveLocationBarView::GetLocationIcon(
LocationIconView::Delegate::IconFetchedCallback on_icon_fetched) const {
if (!ShouldShowIPFSLocationView() ||
!omnibox_view_->model()->ShouldShowCurrentPageIcon())
!omnibox_view_->model()->ShouldShowCurrentPageIcon()) {
return LocationBarView::GetLocationIcon(std::move(on_icon_fetched));
}
auto& bundle = ui::ResourceBundle::GetSharedInstance();
const auto& ipfs_logo = *bundle.GetImageSkiaNamed(IDR_BRAVE_IPFS_LOGO);
return ui::ImageModel::FromImageSkia(ipfs_logo);
}
void BraveLocationBarView::OnOmniboxBlurred() {
if (commander::CommanderEnabled()) {
commander::CommanderServiceFactory::GetForBrowserContext(profile_)->Hide();
}
LocationBarView::OnOmniboxBlurred();
}
void BraveLocationBarView::OnChanged() {
auto hide_page_actions = ShouldHidePageActionIcons();
if (brave_actions_) {
brave_actions_->SetShouldHide(hide_page_actions);
}
#if BUILDFLAG(ENABLE_TOR)
if (onion_location_view_)
if (onion_location_view_) {
onion_location_view_->Update(
browser_->tab_strip_model()->GetActiveWebContents(),
!hide_page_actions);
}
#endif
#if BUILDFLAG(ENABLE_IPFS)
if (ipfs_location_view_)
if (ipfs_location_view_) {
ipfs_location_view_->Update(
browser_->tab_strip_model()->GetActiveWebContents(),
!hide_page_actions);
}
#endif
if (brave_news_location_view_) {
@@ -210,19 +230,23 @@ void BraveLocationBarView::OnChanged() {
std::vector<views::View*> BraveLocationBarView::GetTrailingViews() {
std::vector<views::View*> views;
if (brave_news_location_view_)
if (brave_news_location_view_) {
views.push_back(brave_news_location_view_);
}
#if BUILDFLAG(ENABLE_TOR)
if (onion_location_view_)
if (onion_location_view_) {
views.push_back(onion_location_view_);
}
#endif
#if BUILDFLAG(ENABLE_IPFS)
if (ipfs_location_view_)
if (ipfs_location_view_) {
views.push_back(ipfs_location_view_);
}
#endif
if (brave_actions_)
if (brave_actions_) {
views.push_back(brave_actions_);
}
return views;
}
@@ -261,8 +285,9 @@ gfx::Size BraveLocationBarView::CalculatePreferredSize() const {
void BraveLocationBarView::OnThemeChanged() {
LocationBarView::OnThemeChanged();
if (!IsInitialized())
if (!IsInitialized()) {
return;
}
Update(nullptr);
RefreshBackground();
@@ -56,6 +56,7 @@ class BraveLocationBarView : public LocationBarView {
ui::ImageModel GetLocationIcon(LocationIconView::Delegate::IconFetchedCallback
on_icon_fetched) const override;
void OnOmniboxBlurred() override;
// views::View:
gfx::Size CalculatePreferredSize() const override;
+1
View File
@@ -18,6 +18,7 @@ include_rules = [
"+brave/components/brave_wallet",
"+brave/components/brave_webtorrent/browser",
"+brave/components/brave_webtorrent/grit",
"+brave/components/commander",
"+brave/components/constants",
"+brave/components/content_settings/core",
"+brave/components/debounce",
+5 -1
View File
@@ -3,7 +3,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/browser/about_flags.cc"
#include "brave/components/commander/common/features.h"
// Note: We replace the kQuickCommands feature with the kBraveCommander feature
// so we can use it from //components without DEPS violations.
#define kQuickCommands kBraveCommander
#include "src/chrome/browser/about_flags.cc"
#undef kQuickCommands
@@ -0,0 +1,16 @@
// 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 "src/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "brave/browser/ui/commander/commander_service_factory.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
commander::CommanderFrontendDelegate*
ChromeAutocompleteProviderClient::GetCommanderDelegate() {
return commander::CommanderServiceFactory::GetForBrowserContext(profile_);
}
#endif
@@ -0,0 +1,22 @@
// 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_CHROMIUM_SRC_CHROME_BROWSER_AUTOCOMPLETE_CHROME_AUTOCOMPLETE_PROVIDER_CLIENT_H_
#define BRAVE_CHROMIUM_SRC_CHROME_BROWSER_AUTOCOMPLETE_CHROME_AUTOCOMPLETE_PROVIDER_CLIENT_H_
#include "components/omnibox/browser/autocomplete_provider_client.h"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#define GetBookmarkModel \
GetBookmarkModel() override; \
commander::CommanderFrontendDelegate* GetCommanderDelegate
#endif
#include "src/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h" // IWYU pragma: export
#undef GetBookmarkModel
#endif // BRAVE_CHROMIUM_SRC_CHROME_BROWSER_AUTOCOMPLETE_CHROME_AUTOCOMPLETE_PROVIDER_CLIENT_H_
@@ -5,14 +5,18 @@
#include "brave/browser/ui/browser_commands.h"
#include "brave/browser/ui/commander/commander_service_factory.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_commands.h"
#define ToggleCommander ToggleCommander_ChromiumImpl
#define ReloadBypassingCache ReloadBypassingCache_ChromiumImpl
#define GetReadingListModel GetReadingListModel_ChromiumImpl
#include "src/chrome/browser/ui/browser_commands.cc"
#undef ReloadBypassingCache
#undef GetReadingListModel
#undef ToggleCommander
namespace chrome {
@@ -20,14 +24,20 @@ void ReloadBypassingCache(Browser* browser, WindowOpenDisposition disposition) {
Profile* profile = browser->profile();
DCHECK(profile);
// NewTorConnectionForSite will do hard reload after obtaining new identity
if (profile->IsTor())
if (profile->IsTor()) {
brave::NewTorConnectionForSite(browser);
else
} else {
ReloadBypassingCache_ChromiumImpl(browser, disposition);
}
}
ReadingListModel* GetReadingListModel(Browser* browser) {
return nullptr;
}
void ToggleCommander(Browser* browser) {
commander::CommanderServiceFactory::GetForBrowserContext(browser->profile())
->Toggle();
}
} // namespace chrome
@@ -0,0 +1,13 @@
// 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/commander/common/features.h"
#include "chrome/browser/ui/ui_features.h"
// We replace Chromium's flag with one of our own, because we want it to be
// accessible from //components (which //chrome/ui/browser is not).
#define kQuickCommands kBraveCommander
#include "src/chrome/browser/ui/commander/commander.cc"
#undef kQuickCommands
@@ -0,0 +1,13 @@
// 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/.
// By default the Fuzzy finder doesn't return any results for the empty string.
// However, this isn't ideal because it's not obvious what the input is for.
// It's much better if we can show a bunch of commands.
#define BRAVE_FUZZY_FINDER_NEEDLE_SIZE_CHECK &&false
#include "src/chrome/browser/ui/commander/fuzzy_finder.cc"
#undef BRAVE_FUZZY_FINDER_NEEDLE_SIZE_CHECK
@@ -13,9 +13,11 @@
public: \
virtual std::vector<views::View*> GetTrailingViews();
#define OnOmniboxBlurred virtual OnOmniboxBlurred
#define GetBorderRadius virtual GetBorderRadius
#include "src/chrome/browser/ui/views/location_bar/location_bar_view.h" // IWYU pragma: export
#undef GetBorderRadius
#undef OnOmniboxBlurred
#undef BRAVE_LOCATION_BAR_VIEW_H_
#endif // BRAVE_CHROMIUM_SRC_CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_LOCATION_BAR_VIEW_H_
@@ -1,5 +1,6 @@
include_rules = [
"+brave/components/brave_search_conversion",
"+brave/components/commander",
"+brave/components/omnibox/browser",
"+content/public/common",
]
@@ -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_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_ACTION_H_
#define BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_ACTION_H_
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#define OpenSharingHub \
OpenSharingHub() = 0; \
virtual commander::CommanderFrontendDelegate* GetCommanderDelegate
#endif
#include "src/components/omnibox/browser/actions/omnibox_action.h" // IWYU pragma: export
#undef OpenSharingHub
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_ACTION_H_
@@ -3,7 +3,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "brave/components/brave_search_conversion/utils.h"
#include "brave/components/commander/common/constants.h"
#include "brave/components/commander/common/features.h"
#include "brave/components/omnibox/browser/brave_bookmark_provider.h"
#include "brave/components/omnibox/browser/brave_history_quick_provider.h"
#include "brave/components/omnibox/browser/brave_history_url_provider.h"
@@ -13,12 +21,61 @@
#include "brave/components/omnibox/browser/promotion_provider.h"
#include "brave/components/omnibox/browser/promotion_utils.h"
#include "brave/components/omnibox/browser/topsites_provider.h"
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/clipboard_provider.h"
#include "components/omnibox/browser/history_cluster_provider.h"
#include "components/omnibox/browser/history_fuzzy_provider.h"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "brave/components/omnibox/browser/commander_provider.h"
#endif
using brave_search_conversion::IsBraveSearchConversionFetureEnabled;
namespace {
// If this input has triggered the commander then only show commander results.
void MaybeShowCommands(AutocompleteResult* result,
const AutocompleteInput& input) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
// If this input isn't a command, return and don't do any work.
if (!commander::CommanderEnabled() ||
!base::StartsWith(input.text(), commander::kCommandPrefix)) {
return;
}
uint32_t seen_commands = 0;
// Move all the commands to the top of the Omnibox suggestions.
for (uint32_t i = 0; i < result->size(); ++i) {
auto* match = result->match_at(i);
if (match->provider->type() != AutocompleteProvider::TYPE_BRAVE_COMMANDER) {
continue;
}
result->ReorderMatch(result->begin() + i, seen_commands++);
}
// Remove all results after the commands.
for (auto it = result->end() - 1; it >= result->begin() + seen_commands;
--it) {
result->RemoveMatch(it);
}
#endif
}
void MaybeAddCommanderProvider(AutocompleteController::Providers& providers,
AutocompleteController* controller) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
if (commander::CommanderEnabled()) {
providers.push_back(base::MakeRefCounted<commander::CommanderProvider>(
controller->autocomplete_provider_client(), controller));
}
#endif
}
} // namespace
#define SearchProvider BraveSearchProvider
#define HistoryQuickProvider BraveHistoryQuickProvider
#define HistoryURLProvider BraveHistoryURLProvider
@@ -26,6 +83,7 @@ using brave_search_conversion::IsBraveSearchConversionFetureEnabled;
#define BookmarkProvider BraveBookmarkProvider
#define ShortcutsProvider BraveShortcutsProvider
#define BRAVE_AUTOCOMPLETE_CONTROLLER_AUTOCOMPLETE_CONTROLLER \
MaybeAddCommanderProvider(providers_, this); \
providers_.push_back(new TopSitesProvider(provider_client_.get())); \
if (IsBraveSearchConversionFetureEnabled() && \
!provider_client_->IsOffTheRecord()) \
@@ -35,7 +93,8 @@ using brave_search_conversion::IsBraveSearchConversionFetureEnabled;
// AutocompleteController::UpdateResult() because result should be updated
// before notifying at the last of UpdateResult().
#define BRAVE_AUTOCOMPLETE_CONTROLLER_UPDATE_RESULT \
SortBraveSearchPromotionMatch(&result_);
SortBraveSearchPromotionMatch(&result_); \
MaybeShowCommands(&result_, input_);
#include "src/components/omnibox/browser/autocomplete_controller.cc"
@@ -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/.
#include "brave/chromium_src/components/omnibox/browser/autocomplete_provider.h"
// This is a bit of a hack to just make COMMANDER use the same metrics/friendly
// name as BOOKMARK. It's easier than patching things and we don't use the
// metrics and the name is mostly useful for debugging.
#define TYPE_BOOKMARK \
TYPE_BRAVE_COMMANDER: \
case TYPE_BOOKMARK
#include "src/components/omnibox/browser/autocomplete_provider.cc"
#undef TYPE_BOOKMARK
@@ -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_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_PROVIDER_H_
#define BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_PROVIDER_H_
// Note: We go negative with the BraveAutoCompleteTypes, so we don't conflict if
// Chromium adds something new.
#define TYPE_BOOKMARK TYPE_BRAVE_COMMANDER = -1 << 0, TYPE_BOOKMARK
#include "src/components/omnibox/browser/autocomplete_provider.h" // IWYU pragma: export
#undef TYPE_BOOKMARK
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_PROVIDER_H_
@@ -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/.
#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_MOCK_AUTOCOMPLETE_PROVIDER_CLIENT_H_
#define BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_MOCK_AUTOCOMPLETE_PROVIDER_CLIENT_H_
#include "components/omnibox/browser/autocomplete_provider_client.h"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#define GetTopSites \
GetTopSites_Unused() { \
return nullptr; \
} \
commander::CommanderFrontendDelegate* GetCommanderDelegate() { \
return commander_delegate_.get(); \
} \
void set_commander_delegate( \
std::unique_ptr<commander::CommanderFrontendDelegate> delegate) { \
commander_delegate_ = std::move(delegate); \
} \
\
private: \
std::unique_ptr<commander::CommanderFrontendDelegate> commander_delegate_; \
\
public: \
scoped_refptr<history::TopSites> GetTopSites
#endif
#include "src/components/omnibox/browser/mock_autocomplete_provider_client.h" // IWYU pragma: export
#undef GetTopSites
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_MOCK_AUTOCOMPLETE_PROVIDER_CLIENT_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_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_EDIT_MODEL_H_
#define BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_EDIT_MODEL_H_
#include "components/omnibox/browser/omnibox_view.h"
#define CanPasteAndGo virtual CanPasteAndGo
#include "src/components/omnibox/browser/omnibox_edit_model.h" // IWYU pragma: export
#undef CanPasteAndGo
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_EDIT_MODEL_H_
@@ -0,0 +1,10 @@
// 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/omnibox/browser/brave_omnibox_edit_model.h"
#define OmniboxEditModel BraveOmniboxEditModel
#include "src/components/omnibox/browser/omnibox_view.cc"
#undef OmniboxEditModel
+25
View File
@@ -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/.
assert(!is_android && !is_ios)
component("browser") {
output_name = "commander_browser"
defines = [ "IS_COMMANDER_BROWSER_IMPL" ]
sources = [
"commander_frontend_delegate.h",
"commander_item_model.cc",
"commander_item_model.h",
]
deps = [
"//base",
"//ui/gfx",
"//url",
]
public_deps = [ "//brave/components/commander/common" ]
}
+8
View File
@@ -0,0 +1,8 @@
# 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_rules = [
"+ui/gfx"
]
@@ -0,0 +1,39 @@
// 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_COMMANDER_BROWSER_COMMANDER_FRONTEND_DELEGATE_H_
#define BRAVE_COMPONENTS_COMMANDER_BROWSER_COMMANDER_FRONTEND_DELEGATE_H_
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/observer_list_types.h"
#include "brave/components/commander/browser/commander_item_model.h"
namespace commander {
class COMPONENT_EXPORT(COMMANDER_BROWSER) CommanderFrontendDelegate {
public:
class Observer : public base::CheckedObserver {
public:
virtual void OnCommanderUpdated() = 0;
};
virtual ~CommanderFrontendDelegate() {}
virtual void Toggle() = 0;
virtual void Hide() = 0;
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) = 0;
virtual void SelectCommand(uint32_t command_index,
uint32_t result_set_id) = 0;
virtual void UpdateText(bool force = false) = 0;
virtual std::vector<CommandItemModel> GetItems() = 0;
virtual int GetResultSetId() = 0;
virtual const std::u16string& GetPrompt() = 0;
};
} // namespace commander
#endif // BRAVE_COMPONENTS_COMMANDER_BROWSER_COMMANDER_FRONTEND_DELEGATE_H_
@@ -0,0 +1,21 @@
// 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/commander/browser/commander_item_model.h"
namespace commander {
CommandItemModel::CommandItemModel(
const std::u16string& title,
const std::vector<gfx::Range>& matched_ranges,
const std::u16string& annotation)
: title(title), matched_ranges(matched_ranges), annotation(annotation) {}
CommandItemModel::CommandItemModel(const CommandItemModel& other) = default;
CommandItemModel::CommandItemModel(CommandItemModel&& other) = default;
CommandItemModel::~CommandItemModel() = default;
} // namespace commander
@@ -0,0 +1,37 @@
// 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_COMMANDER_BROWSER_COMMANDER_ITEM_MODEL_H_
#define BRAVE_COMPONENTS_COMMANDER_BROWSER_COMMANDER_ITEM_MODEL_H_
#include <string>
#include <vector>
#include "base/component_export.h"
#include "ui/gfx/range/range.h"
namespace commander {
// See chrome/browser/ui/commander/commander_view_model.h for details on these
// structs. They exist to get around some deps violations.
struct COMPONENT_EXPORT(COMMANDER_BROWSER) CommandItemModel {
public:
CommandItemModel(const std::u16string& title,
const std::vector<gfx::Range>& matched_ranges,
const std::u16string& annotation);
~CommandItemModel();
CommandItemModel(const CommandItemModel& other);
CommandItemModel(CommandItemModel&& other);
CommandItemModel& operator=(const CommandItemModel& other) = default;
CommandItemModel& operator=(CommandItemModel&& other) = default;
std::u16string title;
std::vector<gfx::Range> matched_ranges;
std::u16string annotation;
};
} // namespace commander
#endif // BRAVE_COMPONENTS_COMMANDER_BROWSER_COMMANDER_ITEM_MODEL_H_
+20
View File
@@ -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/.
assert(!is_android && !is_ios)
component("common") {
output_name = "commander_common"
defines = [ "IS_COMMANDER_COMMON_IMPL" ]
sources = [
"constants.cc",
"constants.h",
"features.cc",
"features.h",
]
deps = [ "//base" ]
}
+14
View File
@@ -0,0 +1,14 @@
// 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/commander/common/constants.h"
#include "base/strings/string_piece.h"
namespace commander {
const base::StringPiece16 kCommandPrefix(u":>");
}
+21
View File
@@ -0,0 +1,21 @@
// 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_COMMANDER_COMMON_CONSTANTS_H_
#define BRAVE_COMPONENTS_COMMANDER_COMMON_CONSTANTS_H_
#include <iterator>
#include "base/component_export.h"
#include "base/strings/string_piece.h"
namespace commander {
COMPONENT_EXPORT(COMMANDER_COMMON)
extern const base::StringPiece16 kCommandPrefix;
} // namespace commander
#endif // BRAVE_COMPONENTS_COMMANDER_COMMON_CONSTANTS_H_
+23
View File
@@ -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/.
#include "brave/components/commander/common/features.h"
#include "base/feature_list.h"
namespace features {
BASE_FEATURE(kBraveCommander,
"BraveCommander",
base::FEATURE_DISABLED_BY_DEFAULT);
}
namespace commander {
bool CommanderEnabled() {
return base::FeatureList::IsEnabled(features::kBraveCommander);
}
} // namespace commander
+26
View File
@@ -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_COMPONENTS_COMMANDER_COMMON_FEATURES_H_
#define BRAVE_COMPONENTS_COMMANDER_COMMON_FEATURES_H_
#include "base/component_export.h"
#include "base/feature_list.h"
namespace features {
// Note: This flag is declared in features rather than commander::features so we
// can replace the upstream flag with it more easily.
COMPONENT_EXPORT(COMMANDER_COMMON) BASE_DECLARE_FEATURE(kBraveCommander);
} // namespace features
namespace commander {
COMPONENT_EXPORT(COMMANDER_COMMON) bool CommanderEnabled();
} // namespace commander
#endif // BRAVE_COMPONENTS_COMMANDER_COMMON_FEATURES_H_
+7
View File
@@ -41,4 +41,11 @@ source_set("unit_tests") {
"//testing/gmock",
"//testing/gtest",
]
if (!is_android && !is_ios) {
sources +=
[ "//brave/components/omnibox/browser/commander_provider_unittest.cc" ]
deps += [ "//brave/components/commander/browser" ]
}
}
+8
View File
@@ -0,0 +1,8 @@
# 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_rules = [
"+ui/gfx"
]
@@ -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/.
#include "brave/components/omnibox/browser/brave_omnibox_edit_model.h"
#include "base/strings/string_util.h"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "brave/components/commander/common/constants.h"
#include "brave/components/commander/common/features.h"
#endif
BraveOmniboxEditModel::~BraveOmniboxEditModel() = default;
bool BraveOmniboxEditModel::CanPasteAndGo(const std::u16string& text) const {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
if (commander::CommanderEnabled() &&
base::StartsWith(text, commander::kCommandPrefix)) {
return false;
}
#endif
return OmniboxEditModel::CanPasteAndGo(text);
}
@@ -0,0 +1,21 @@
// 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_OMNIBOX_BROWSER_BRAVE_OMNIBOX_EDIT_MODEL_H_
#define BRAVE_COMPONENTS_OMNIBOX_BROWSER_BRAVE_OMNIBOX_EDIT_MODEL_H_
#include <string>
#include "components/omnibox/browser/omnibox_edit_model.h"
class BraveOmniboxEditModel : public OmniboxEditModel {
public:
using OmniboxEditModel::OmniboxEditModel;
~BraveOmniboxEditModel() override;
bool CanPasteAndGo(const std::u16string& text) const override;
};
#endif // BRAVE_COMPONENTS_OMNIBOX_BROWSER_BRAVE_OMNIBOX_EDIT_MODEL_H_
@@ -0,0 +1,21 @@
// 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/omnibox/browser/commander_action.h"
namespace commander {
CommanderAction::CommanderAction(uint32_t command_index, uint32_t result_set_id)
: OmniboxAction({}, GURL(), true),
command_index_(command_index),
result_set_id_(result_set_id) {}
CommanderAction::~CommanderAction() = default;
void CommanderAction::Execute(ExecutionContext& context) const {
context.client_->GetCommanderDelegate()->SelectCommand(command_index_,
result_set_id_);
}
} // namespace commander
@@ -0,0 +1,31 @@
// 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_OMNIBOX_BROWSER_COMMANDER_ACTION_H_
#define BRAVE_COMPONENTS_OMNIBOX_BROWSER_COMMANDER_ACTION_H_
#include "components/omnibox/browser/actions/omnibox_action.h"
namespace commander {
class CommanderAction : public OmniboxAction {
public:
CommanderAction(uint32_t command_index, uint32_t result_set_id);
CommanderAction(const CommanderAction&) = delete;
CommanderAction& operator=(const CommanderAction&) = delete;
void Execute(ExecutionContext& context) const override;
protected:
~CommanderAction() override;
private:
uint32_t command_index_;
uint32_t result_set_id_;
};
} // namespace commander
#endif // BRAVE_COMPONENTS_OMNIBOX_BROWSER_COMMANDER_ACTION_H_
@@ -0,0 +1,116 @@
// 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/omnibox/browser/commander_provider.h"
#include <string>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "brave/components/commander/browser/commander_item_model.h"
#include "brave/components/commander/common/constants.h"
#include "brave/components/omnibox/browser/commander_action.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
namespace commander {
CommanderProvider::CommanderProvider(AutocompleteProviderClient* client,
AutocompleteProviderListener* listener)
: AutocompleteProvider(AutocompleteProvider::TYPE_BRAVE_COMMANDER),
client_(client) {
if (listener) {
AddListener(listener);
}
observation_.Observe(client_->GetCommanderDelegate());
}
CommanderProvider::~CommanderProvider() = default;
void CommanderProvider::Start(const AutocompleteInput& input,
bool minimal_changes) {
if (minimal_changes) {
return;
}
matches_.clear();
last_input_ = input.text();
client_->GetCommanderDelegate()->UpdateText();
}
void CommanderProvider::OnCommanderUpdated() {
auto* delegate = client_->GetCommanderDelegate();
matches_.clear();
if (!last_input_.starts_with(commander::kCommandPrefix.data())) {
return;
}
int rank = 10000;
const auto& items = delegate->GetItems();
for (uint32_t i = 0; i < items.size(); ++i) {
const auto& option = items[i];
AutocompleteMatch match(this, rank--, false,
AutocompleteMatchType::BOOKMARK_TITLE);
match.actions.push_back(
base::MakeRefCounted<CommanderAction>(i, delegate->GetResultSetId()));
// This is neat but it would be nice if we could always show it instead of
// only when we have a result selected.
match.contents = option.annotation;
match.additional_text = delegate->GetPrompt();
if (!option.annotation.empty()) {
match.contents_class = {
ACMatchClassification(0, ACMatchClassification::DIM)};
}
match.description =
base::StrCat({commander::kCommandPrefix, u" ", option.title});
match.allowed_to_be_default_match = true;
match.swap_contents_and_description = true;
// We don't want to change the prompt at all while the user is going through
// their options.
match.fill_into_edit = last_input_;
match.description_class = {
ACMatchClassification(0, ACMatchClassification::DIM)};
// All commands have a ":> " prefix added to them, so make sure we take it
// into account when mapping over the matched ranges.
const int offset = commander::kCommandPrefix.size() + 1;
for (size_t j = 0; j < option.matched_ranges.size(); ++j) {
auto range = option.matched_ranges[j];
// If the match has no length (as in the case of the empty string match)
// don't highlight anything - zero length highlights trigger a DCHECK.
if (range.start() == range.end()) {
continue;
}
// Start the match classification at the start of the matching range.
match.description_class.push_back(ACMatchClassification(
range.start() + offset, ACMatchClassification::MATCH));
// If the end of the range isn't the last character in the string, and
// this range doesn't intersect with the next one, change the
// classification back to DIM from the end of this range.
if (range.end() + offset < match.description.size() &&
(j + 1 >= option.matched_ranges.size() ||
option.matched_ranges[j + 1].start() > range.end())) {
match.description_class.push_back(ACMatchClassification(
range.end() + offset, ACMatchClassification::DIM));
}
}
matches_.push_back(match);
}
NotifyListeners(/* updated_matches= */ true);
}
} // namespace commander
@@ -0,0 +1,51 @@
// 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_OMNIBOX_BROWSER_COMMANDER_PROVIDER_H_
#define BRAVE_COMPONENTS_OMNIBOX_BROWSER_COMMANDER_PROVIDER_H_
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "brave/components/commander/browser/commander_item_model.h"
#include "components/omnibox/browser/autocomplete_provider.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
namespace commander {
class CommanderProvider
: public AutocompleteProvider,
public commander::CommanderFrontendDelegate::Observer {
public:
CommanderProvider(AutocompleteProviderClient* client,
AutocompleteProviderListener* listener);
CommanderProvider(const CommanderProvider&) = delete;
CommanderProvider& operator=(const CommanderProvider&) = delete;
// AutocompleteProvider:
void Start(const AutocompleteInput& input, bool minimal_changes) override;
private:
// Destructor for AutocompleteProvider must be private or protected as it
// extends |base::RefCountedThreadSafe|.
~CommanderProvider() override;
// commander::CommanderFrontendDelegate::Observer:
void OnCommanderUpdated() override;
raw_ptr<AutocompleteProviderClient> client_;
base::ScopedObservation<commander::CommanderFrontendDelegate,
commander::CommanderFrontendDelegate::Observer>
observation_{this};
std::u16string last_input_;
base::WeakPtrFactory<CommanderProvider> weak_ptr_factory_{this};
};
} // namespace commander
#endif // BRAVE_COMPONENTS_OMNIBOX_BROWSER_COMMANDER_PROVIDER_H_
@@ -0,0 +1,349 @@
// 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/omnibox/browser/commander_provider.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/strings/strcat.h"
#include "base/test/scoped_feature_list.h"
#include "brave/components/commander/browser/commander_frontend_delegate.h"
#include "brave/components/commander/browser/commander_item_model.h"
#include "brave/components/commander/common/constants.h"
#include "brave/components/commander/common/features.h"
#include "brave/components/omnibox/browser/brave_fake_autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/test_scheme_classifier.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
#include "ui/gfx/range/range.h"
namespace {
const unsigned int kClassificationOffset =
1u + commander::kCommandPrefix.size();
class FakeCommanderDelegate : public commander::CommanderFrontendDelegate {
public:
FakeCommanderDelegate() = default;
~FakeCommanderDelegate() override {}
void AddObserver(Observer* observer) override {
Notify(items_);
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
void Toggle() override { showing_ = !showing_; }
void Hide() override { showing_ = false; }
std::vector<commander::CommandItemModel> GetItems() override {
return items_;
}
int GetResultSetId() override { return notifies_; }
const std::u16string& GetPrompt() override { return prompt_; }
void Notify(const std::vector<commander::CommandItemModel>& items,
std::u16string prompt = std::u16string()) {
items_ = items;
notifies_++;
prompt_ = std::move(prompt);
for (auto& observer : observers_) {
observer.OnCommanderUpdated();
}
}
void SelectCommand(uint32_t command_index, uint32_t result_set_id) override {
NOTIMPLEMENTED();
}
void UpdateText(bool force = false) override { NOTIMPLEMENTED(); }
private:
base::ObserverList<Observer> observers_;
std::u16string prompt_;
std::vector<commander::CommandItemModel> items_;
int notifies_ = 0;
bool showing_ = false;
};
AutocompleteInput CreateInput(std::u16string text) {
return AutocompleteInput(text,
metrics::OmniboxEventProto::PageClassification::
OmniboxEventProto_PageClassification_NTP,
TestSchemeClassifier());
}
} // namespace
class CommanderProviderTest : public testing::Test {
public:
CommanderProviderTest() {
features_.InitAndEnableFeature(features::kBraveCommander);
}
void SetUp() override {
client_.set_commander_delegate(std::make_unique<FakeCommanderDelegate>());
provider_ =
base::MakeRefCounted<commander::CommanderProvider>(&client_, nullptr);
}
FakeCommanderDelegate* delegate() {
return static_cast<FakeCommanderDelegate*>(client_.GetCommanderDelegate());
}
commander::CommanderProvider* provider() { return provider_.get(); }
private:
base::test::ScopedFeatureList features_;
BraveFakeAutocompleteProviderClient client_;
scoped_refptr<commander::CommanderProvider> provider_;
};
TEST_F(CommanderProviderTest, EmptyTextDoesNotTriggerProvider) {
delegate()->Notify({commander::CommandItemModel(u"First", {}, u"Ctrl+F")});
provider()->Start(CreateInput(u""), false);
EXPECT_EQ(0u, provider()->matches().size());
}
TEST_F(CommanderProviderTest, NonPrefixedTextDoesNotTriggerProvider) {
provider()->Start(CreateInput(u"Hello"), false);
EXPECT_EQ(0u, provider()->matches().size());
}
TEST_F(CommanderProviderTest, PrefixTriggersProvider) {
provider()->Start(CreateInput(u""), false);
EXPECT_EQ(0u, provider()->matches().size());
provider()->Start(CreateInput(commander::kCommandPrefix.data()), false);
EXPECT_EQ(0u, provider()->matches().size());
}
TEST_F(CommanderProviderTest, PrefixedCommandTriggersProvider) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u"Hello"})), false);
EXPECT_EQ(0u, provider()->matches().size());
}
TEST_F(CommanderProviderTest, PrefixWhiteSpaceIsStripped) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello"})),
false);
EXPECT_EQ(0u, provider()->matches().size());
}
TEST_F(CommanderProviderTest, ItemsAreConvertedToMatches) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello World"})),
false);
delegate()->Notify({commander::CommandItemModel(u"First", {}, u"Ctrl+F"),
commander::CommandItemModel(u"Second", {}, u"Ctrl+S")});
EXPECT_EQ(2u, provider()->matches().size());
EXPECT_EQ(u":> First", provider()->matches()[0].description);
EXPECT_EQ(u"Ctrl+F", provider()->matches()[0].contents);
EXPECT_EQ(u":> Second", provider()->matches()[1].description);
EXPECT_EQ(u"Ctrl+S", provider()->matches()[1].contents);
for (const auto& match : provider()->matches()) {
// As we haven't specified a prompt, none of the matches should have
// additional_text.
EXPECT_EQ(u"", match.additional_text);
// All matches swap the contents & description, so that the command displays
// on the left and the shortcut (if any) on the right.
EXPECT_TRUE(match.swap_contents_and_description);
// All matches should be allowed to be the default, so when the user presses
// enter, the top command is executed.
EXPECT_TRUE(match.allowed_to_be_default_match);
// fill_into_edit should be the same as whatever the last input was, so
// scrolling through the commands doesn't affect what the user typed.
EXPECT_EQ(u":> Hello World", match.fill_into_edit);
// Check the matches have actions.
EXPECT_NE(0u, match.actions.size());
}
}
TEST_F(CommanderProviderTest, RemovingPrefixClearsMatches) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello World"})),
false);
delegate()->Notify({commander::CommandItemModel(u"First", {}, u"Ctrl+F"),
commander::CommandItemModel(u"Second", {}, u"Ctrl+S")});
EXPECT_EQ(2u, provider()->matches().size());
provider()->Start(CreateInput(u"no prefix!"), false);
EXPECT_EQ(0u, provider()->matches().size());
}
TEST_F(CommanderProviderTest, PromptingForMoreInputSetsAnnotation) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello World"})),
false);
delegate()->Notify({commander::CommandItemModel(u"Foo", {}, u"")},
u"What thing?");
EXPECT_EQ(1u, provider()->matches().size());
EXPECT_EQ(u"What thing?", provider()->matches()[0].additional_text);
EXPECT_EQ(u":> Foo", provider()->matches()[0].description);
}
TEST_F(CommanderProviderTest, NoMatchRangeAllDimStyle) {
provider()->Start(CreateInput(u":> Hello World"), false);
delegate()->Notify({commander::CommandItemModel(u"Foo", {}, u"")},
u"What thing?");
EXPECT_EQ(1u, provider()->matches().size());
const auto& c = provider()->matches()[0].description_class;
ASSERT_EQ(1u, c.size());
EXPECT_EQ(0u, c[0].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[0].style);
}
TEST_F(CommanderProviderTest, ZeroCharMatchIsIgnored) {
provider()->Start(CreateInput(u":> Hello World"), false);
delegate()->Notify({commander::CommandItemModel(u"Foo", {gfx::Range()}, u"")},
u"What thing?");
EXPECT_EQ(1u, provider()->matches().size());
const auto& c = provider()->matches()[0].description_class;
ASSERT_EQ(1u, c.size());
EXPECT_EQ(0u, c[0].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[0].style);
}
TEST_F(CommanderProviderTest, OneCharMatchIsHighlighted) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello World"})),
false);
delegate()->Notify(
{commander::CommandItemModel(u"Foo", {gfx::Range(0, 1)}, u"")},
u"What thing?");
EXPECT_EQ(1u, provider()->matches().size());
const auto& c = provider()->matches()[0].description_class;
ASSERT_EQ(3u, c.size());
// :> is DIM
EXPECT_EQ(0u, c[0].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[0].style);
// F is MATCH
EXPECT_EQ(kClassificationOffset, c[1].offset);
EXPECT_EQ(ACMatchClassification::MATCH, c[1].style);
// oo should be DIM, as it didn't match
EXPECT_EQ(1u + kClassificationOffset, c[2].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[2].style);
}
// Note: The AutocompleteClassifier gets unhappy if the style switches back and
// forth on the same character (i.e one match finishes where another one
// starts).
TEST_F(CommanderProviderTest, AdjacentMatchesDontSwitchBackAndForth) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello World"})),
false);
delegate()->Notify({commander::CommandItemModel(
u"Foo", {gfx::Range(0, 1), gfx::Range(1, 2)}, u"")},
u"What thing?");
const auto& c = provider()->matches()[0].description_class;
EXPECT_EQ(1u, provider()->matches().size());
ASSERT_EQ(4u, c.size());
// :> Foo
// :> is DIM
EXPECT_EQ(0u, c[0].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[0].style);
// F is MATCH, but shouldn't add a closing DIM
EXPECT_EQ(kClassificationOffset, c[1].offset);
EXPECT_EQ(ACMatchClassification::MATCH, c[1].style);
// first o is also MATCH
EXPECT_EQ(1u + kClassificationOffset, c[2].offset);
EXPECT_EQ(ACMatchClassification::MATCH, c[2].style);
// second o should be DIM, as it didn't match
EXPECT_EQ(2u + kClassificationOffset, c[3].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[3].style);
}
TEST_F(CommanderProviderTest, FullLengthMatchIsApplied) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u" Hello World"})),
false);
delegate()->Notify(
{commander::CommandItemModel(u"Foo", {gfx::Range(0, 3)}, u"")},
u"What thing?");
const auto& c = provider()->matches()[0].description_class;
EXPECT_EQ(1u, provider()->matches().size());
ASSERT_EQ(2u, c.size());
// :> Foo
// :> is DIM
EXPECT_EQ(0u, c[0].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[0].style);
// Foo is MATCH
EXPECT_EQ(kClassificationOffset, c[1].offset);
EXPECT_EQ(ACMatchClassification::MATCH, c[1].style);
}
TEST_F(CommanderProviderTest, MatchesCanHaveGaps) {
provider()->Start(
CreateInput(base::StrCat({commander::kCommandPrefix, u"FoBa"})), false);
delegate()->Notify(
{commander::CommandItemModel(u"Foo Bar",
{gfx::Range(0, 2), gfx::Range(4, 6)}, u"")},
u"What thing?");
const auto& c = provider()->matches()[0].description_class;
EXPECT_EQ(1u, provider()->matches().size());
ASSERT_EQ(5u, c.size());
// |:> |Foo Bar is DIM
EXPECT_EQ(0u, c[0].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[0].style);
// :> |Fo|o Bar is MATCH
EXPECT_EQ(kClassificationOffset, c[1].offset);
EXPECT_EQ(ACMatchClassification::MATCH, c[1].style);
EXPECT_EQ(2u + kClassificationOffset, c[2].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[2].style);
// :> Foo |Ba|r is MATCH
EXPECT_EQ(4u + kClassificationOffset, c[3].offset);
EXPECT_EQ(ACMatchClassification::MATCH, c[3].style);
EXPECT_EQ(6u + kClassificationOffset, c[4].offset);
EXPECT_EQ(ACMatchClassification::DIM, c[4].style);
}
+14
View File
@@ -14,6 +14,8 @@ brave_components_omnibox_browser_sources = [
"//brave/components/omnibox/browser/brave_local_history_zero_suggest_provider.h",
"//brave/components/omnibox/browser/brave_omnibox_client.cc",
"//brave/components/omnibox/browser/brave_omnibox_client.h",
"//brave/components/omnibox/browser/brave_omnibox_edit_model.cc",
"//brave/components/omnibox/browser/brave_omnibox_edit_model.h",
"//brave/components/omnibox/browser/brave_omnibox_prefs.cc",
"//brave/components/omnibox/browser/brave_omnibox_prefs.h",
"//brave/components/omnibox/browser/brave_search_provider.cc",
@@ -36,3 +38,15 @@ brave_components_omnibox_browser_deps = [
"//components/prefs",
"//url",
]
if (!is_android && !is_ios) {
brave_components_omnibox_browser_sources += [
"//brave/components/omnibox/browser/commander_action.cc",
"//brave/components/omnibox/browser/commander_action.h",
"//brave/components/omnibox/browser/commander_provider.cc",
"//brave/components/omnibox/browser/commander_provider.h",
]
brave_components_omnibox_browser_deps +=
[ "//brave/components/commander/browser" ]
}
@@ -0,0 +1,13 @@
diff --git a/chrome/browser/ui/commander/fuzzy_finder.cc b/chrome/browser/ui/commander/fuzzy_finder.cc
index 5166bb5d04388..0149fbaea6b93 100644
--- a/chrome/browser/ui/commander/fuzzy_finder.cc
+++ b/chrome/browser/ui/commander/fuzzy_finder.cc
@@ -236,7 +236,7 @@ FuzzyFinder::~FuzzyFinder() = default;
double FuzzyFinder::Find(const std::u16string& haystack,
std::vector<gfx::Range>* matched_ranges) {
matched_ranges->clear();
- if (needle_.size() == 0)
+ if (needle_.size() == 0 BRAVE_FUZZY_FINDER_NEEDLE_SIZE_CHECK)
return 0;
const std::u16string& folded = base::i18n::FoldCase(haystack);
size_t m = needle_.size();
+1
View File
@@ -1085,6 +1085,7 @@ test("brave_browser_tests") {
sources += [ "//brave/browser/ui/views/toolbar/wallet_button_notification_source_browsertest.cc" ]
deps += [
"//brave/browser/sharing_hub:browser_tests",
"//brave/browser/ui/commander:browser_tests",
"//brave/browser/ui/whats_new:browser_test",
"//chrome/browser/apps/app_service:app_service",
"//chrome/browser/apps/app_service:constants",
+4
View File
@@ -634,3 +634,7 @@
# This test crashes because AutocompleteClassifier called when TabStripModel
# was not initialized in test.
-RenderViewContextMenuPrefsTest.ShowAllPasswordsIncognito
# This test fails because we changed the behavior of the fuzzy finder to match
# the empty string.
-CommanderFuzzyFinder.EmptyStringDoesNotMatch