@@ -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"
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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" ]
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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" ]
|
||||
}
|
||||
@@ -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":>");
|
||||
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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();
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user