Commit 588fb27f authored by Leonard Grey's avatar Leonard Grey Committed by Commit Bot

Commander: introduce SimpleCommandSource

This is a first draft of a command source that handles basic
Chromium commands independent of actual content (like tabs, windows,
bookmarks, etc.)

The purpose for now is just to provide something that can be interacted
with when the view layer is added. Accordingly, commands have  been
chosen on the basis of: it's something we would like to implement
and it has a cross-platform translation that works for this context.

(NB: I didn't try particularly hard to find all of these)

There's also a stubbed out fuzzy finder which is not fuzzy in the least
at the moment.

Bug: 1014639
Change-Id: I08282aed82ba9fb1c62e592088e774a02479142e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2327820
Commit-Queue: Leonard Grey <lgrey@chromium.org>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#793635}
parent af60fc9d
...@@ -925,6 +925,10 @@ static_library("ui") { ...@@ -925,6 +925,10 @@ static_library("ui") {
"commander/commander_controller.h", "commander/commander_controller.h",
"commander/commander_view_model.cc", "commander/commander_view_model.cc",
"commander/commander_view_model.h", "commander/commander_view_model.h",
"commander/fuzzy_finder.cc",
"commander/fuzzy_finder.h",
"commander/simple_command_source.cc",
"commander/simple_command_source.h",
"confirm_bubble_model.cc", "confirm_bubble_model.cc",
"confirm_bubble_model.h", "confirm_bubble_model.h",
"content_settings/content_setting_bubble_model.cc", "content_settings/content_setting_bubble_model.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/commander/fuzzy_finder.h"
#include "base/i18n/case_conversion.h"
namespace commander {
double FuzzyFind(const base::string16& needle,
const base::string16& haystack,
std::vector<gfx::Range>* matched_ranges) {
DCHECK(needle == base::i18n::FoldCase(needle));
matched_ranges->clear();
const base::string16& folded = base::i18n::FoldCase(haystack);
if (folded == needle) {
matched_ranges->emplace_back(0, needle.length());
return 1.0;
}
size_t substring_position = folded.find(needle);
if (substring_position == std::string::npos)
return 0;
matched_ranges->emplace_back(substring_position, needle.length());
if (substring_position == 0)
return .99;
return std::min(1 - static_cast<double>(substring_position) / folded.length(),
0.01);
}
} // namespace commander
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COMMANDER_FUZZY_FINDER_H_
#define CHROME_BROWSER_UI_COMMANDER_FUZZY_FINDER_H_
#include "base/strings/string16.h"
#include "ui/gfx/range/range.h"
namespace commander {
// TODO(lgrey): Make this actually fuzzy find.
// Returns a score from 0 to 1 based on how well |needle| matches |haystack|.
// 0 means no match. |matched_ranges| will be filled with the ranges of
// |haystack| that match |needle| so they can be highlighted in the UI; see
// comment on commander::CommandItem |matched_ranges| for a worked example.
// *** TEMPORARY ***
// Temporarily, a non-zero match means that |needle| is a substring of
// |haystack|, with a penalty applied based on how far into |haystack|
// |needle| begins. Exact matches are 1.0 (vs. a max of .99 for non-exact
// prefix).
// This will be replaced with a more sophisticated implementation in the
// near future.
// *** END TEMPORARY ***
// |needle| is expected to already be case folded (this is DCHECKED) to save
// redundant processing, as the needle will be checked with many haystacks.
double FuzzyFind(const base::string16& needle,
const base::string16& haystack,
std::vector<gfx::Range>* matched_ranges);
} // namespace commander
#endif // CHROME_BROWSER_UI_COMMANDER_FUZZY_FINDER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/commander/fuzzy_finder.h"
#include "base/i18n/case_conversion.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace commander {
TEST(CommanderFuzzyFinder, NonmatchIsZero) {
std::vector<gfx::Range> ranges;
EXPECT_EQ(0, FuzzyFind(base::ASCIIToUTF16("orange"),
base::ASCIIToUTF16("orangutan"), &ranges));
EXPECT_TRUE(ranges.empty());
EXPECT_EQ(0, FuzzyFind(base::ASCIIToUTF16("elephant"),
base::ASCIIToUTF16("orangutan"), &ranges));
EXPECT_TRUE(ranges.empty());
}
TEST(CommanderFuzzyFinder, ExactMatchIsOne) {
std::vector<gfx::Range> ranges;
EXPECT_EQ(1, FuzzyFind(base::ASCIIToUTF16("orange"),
base::ASCIIToUTF16("orange"), &ranges));
EXPECT_EQ(ranges, std::vector<gfx::Range>({{0, 6}}));
}
TEST(CommanderFuzzyFinder, CaseInsensitive) {
std::vector<gfx::Range> ranges;
EXPECT_EQ(1, FuzzyFind(base::ASCIIToUTF16("orange"),
base::ASCIIToUTF16("Orange"), &ranges));
EXPECT_EQ(ranges, std::vector<gfx::Range>({{0, 6}}));
}
TEST(CommanderFuzzyFinder, PrefixRanksHigherThanInternal) {
std::vector<gfx::Range> ranges;
double prefix_rank = FuzzyFind(base::ASCIIToUTF16("orange"),
base::ASCIIToUTF16("Orange juice"), &ranges);
EXPECT_EQ(ranges, std::vector<gfx::Range>({{0, 6}}));
double non_prefix_rank =
FuzzyFind(base::ASCIIToUTF16("orange"),
base::ASCIIToUTF16("William of Orange"), &ranges);
EXPECT_EQ(ranges, std::vector<gfx::Range>({{11, 6}}));
EXPECT_GT(prefix_rank, 0);
EXPECT_GT(non_prefix_rank, 0);
EXPECT_LT(prefix_rank, 1);
EXPECT_LT(non_prefix_rank, 1);
EXPECT_GT(prefix_rank, non_prefix_rank);
}
} // namespace commander
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/commander/simple_command_source.h"
#include "base/bind.h"
#include "base/i18n/case_conversion.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/commander/fuzzy_finder.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
namespace commander {
SimpleCommandSource::SimpleCommandSource() {
weak_this_ = weak_ptr_factory_.GetWeakPtr();
}
SimpleCommandSource::~SimpleCommandSource() = default;
CommandSource::CommandResults SimpleCommandSource::GetCommands(
const base::string16& input,
Browser* browser) const {
static constexpr struct {
int command_id;
int string_constant;
} command_map[] = {
{IDC_SHOW_HISTORY, IDS_HISTORY_SHOWFULLHISTORY_LINK},
{IDC_FIND, IDS_FIND},
{IDC_RELOAD, IDS_TOOLTIP_RELOAD},
{IDC_SAVE_PAGE, IDS_SAVE_PAGE},
{IDC_PRINT, IDS_PRINT},
};
CommandSource::CommandResults results;
const base::string16& folded_input = base::i18n::FoldCase(input);
std::vector<gfx::Range> ranges;
for (const auto& command_spec : command_map) {
if (!chrome::IsCommandEnabled(browser, command_spec.command_id))
continue;
const base::string16 title =
l10n_util::GetStringUTF16(command_spec.string_constant);
double score = FuzzyFind(folded_input, title, &ranges);
if (score == 0)
continue;
auto item = std::make_unique<CommandItem>();
item->title = title;
item->score = score;
item->matched_ranges = ranges;
// TODO(lgrey): For base::Unretained to be safe here, we need to ensure
// that if |browser| is destroyed, the palette is reset. It's likely
// that this will be the case anyway, but leaving this comment so:
// - it doesn't get dropped/forgotten
// - as a reminder to replace the comment with the actual explanation
// when we have it
item->command =
base::BindOnce(&SimpleCommandSource::ExecuteCommand, weak_this_,
base::Unretained(browser), command_spec.command_id);
results.push_back(std::move(item));
}
return results;
}
// Why this is necessary:
// chrome::ExecuteCommand has a third default argument |time_stamp| which
// makes it difficult to use with BindOnce. Pre-binding it at command creation
// is wrong since it defaults to base::TimeTicks::Now(); that means if pre-bound
// it would get the timestamp when the command was generated, rather than when
// it was invoked.
void SimpleCommandSource::ExecuteCommand(Browser* browser, int command_id) {
chrome::ExecuteCommand(browser, command_id);
}
} // namespace commander
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COMMANDER_SIMPLE_COMMAND_SOURCE_H_
#define CHROME_BROWSER_UI_COMMANDER_SIMPLE_COMMAND_SOURCE_H_
#include "chrome/browser/ui/commander/command_source.h"
#include "base/memory/weak_ptr.h"
namespace commander {
// A command source which hosts simple one-shot browser commands, most of which
// are accessible by hotkey. This is an alternative surface to provide for
// hotkey discovery.
class SimpleCommandSource : public CommandSource {
public:
SimpleCommandSource();
~SimpleCommandSource() override;
// Disallow copy and assign
SimpleCommandSource(const SimpleCommandSource& other) = delete;
SimpleCommandSource& operator=(const SimpleCommandSource& other) = delete;
// Command source overrides
CommandSource::CommandResults GetCommands(const base::string16& input,
Browser* browser) const override;
private:
base::WeakPtr<SimpleCommandSource> weak_this_;
// Wrapper around chrome::ExecuteCommand. See implementation comment
// for details.
void ExecuteCommand(Browser* browser, int command_id);
base::WeakPtrFactory<SimpleCommandSource> weak_ptr_factory_{this};
};
} // namespace commander
#endif // CHROME_BROWSER_UI_COMMANDER_SIMPLE_COMMAND_SOURCE_H_
...@@ -4240,6 +4240,7 @@ test("unit_tests") { ...@@ -4240,6 +4240,7 @@ test("unit_tests") {
"../browser/ui/browser_unittest.cc", "../browser/ui/browser_unittest.cc",
"../browser/ui/browser_window_state_unittest.cc", "../browser/ui/browser_window_state_unittest.cc",
"../browser/ui/commander/commander_controller_unittest.cc", "../browser/ui/commander/commander_controller_unittest.cc",
"../browser/ui/commander/fuzzy_finder_unittest.cc",
"../browser/ui/content_settings/content_setting_bubble_model_unittest.cc", "../browser/ui/content_settings/content_setting_bubble_model_unittest.cc",
"../browser/ui/content_settings/content_setting_image_model_unittest.cc", "../browser/ui/content_settings/content_setting_image_model_unittest.cc",
"../browser/ui/content_settings/content_setting_media_image_model_unittest.mm", "../browser/ui/content_settings/content_setting_media_image_model_unittest.mm",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment