blob: b6d5343927a882c9e4fd0e7f136dc3bcec933545 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/titled_url_match_utils.h"
#include <memory>
#include <string_view>
#include "base/memory/scoped_refptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "components/bookmarks/browser/titled_url_match.h"
#include "components/bookmarks/browser/titled_url_node.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_provider.h"
#include "components/omnibox/browser/fake_autocomplete_provider.h"
#include "components/omnibox/browser/test_scheme_classifier.h"
#include "components/omnibox/common/omnibox_features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "url/gurl.h"
using bookmarks::TitledUrlMatchToAutocompleteMatch;
namespace {
class MockTitledUrlNode : public bookmarks::TitledUrlNode {
public:
MockTitledUrlNode(const std::u16string& title,
const GURL& url,
std::vector<std::u16string> ancestors = {})
: title_(title), url_(url), ancestors_(ancestors) {}
// TitledUrlNode
const std::u16string& GetTitledUrlNodeTitle() const override {
return title_;
}
const GURL& GetTitledUrlNodeUrl() const override { return url_; }
std::vector<std::u16string_view> GetTitledUrlNodeAncestorTitles()
const override {
std::vector<std::u16string_view> ancestors;
base::ranges::transform(
ancestors_, std::back_inserter(ancestors),
[](auto& ancestor) { return std::u16string_view(ancestor); });
return ancestors;
}
private:
std::u16string title_;
GURL url_;
std::vector<std::u16string> ancestors_;
};
std::string ACMatchClassificationsAsString(
const ACMatchClassifications& classifications) {
std::string position_string("{");
for (auto classification : classifications) {
position_string +=
"{offset " + base::NumberToString(classification.offset) + ", style " +
base::NumberToString(classification.style) + "}, ";
}
position_string += "}\n";
return position_string;
}
// Use a test fixture to ensure that any scoped settings that are set during the
// test are cleared after the test is terminated.
class TitledUrlMatchUtilsTest : public testing::Test {
protected:
void TearDown() override {
RichAutocompletionParams::ClearParamsForTesting();
}
};
} // namespace
TEST_F(TitledUrlMatchUtilsTest, TitledUrlMatchToAutocompleteMatch) {
std::u16string input_text(u"goo");
std::u16string match_title(u"Google Search");
GURL match_url("https://www.google.com/");
AutocompleteMatchType::Type type = AutocompleteMatchType::BOOKMARK_TITLE;
int relevance = 123;
int bookmark_count = 3;
MockTitledUrlNode node(match_title, match_url);
bookmarks::TitledUrlMatch titled_url_match;
titled_url_match.node = &node;
titled_url_match.title_match_positions = {{0, 3}};
titled_url_match.url_match_positions = {{12, 15}};
scoped_refptr<FakeAutocompleteProvider> provider =
new FakeAutocompleteProvider(AutocompleteProvider::Type::TYPE_BOOKMARK);
TestSchemeClassifier classifier;
AutocompleteInput input(input_text, metrics::OmniboxEventProto::NTP,
classifier);
const std::u16string fixed_up_input(input_text);
AutocompleteMatch autocomplete_match = TitledUrlMatchToAutocompleteMatch(
titled_url_match, type, relevance, bookmark_count, provider.get(),
classifier, input, fixed_up_input);
ACMatchClassifications expected_contents_class = {
{0, ACMatchClassification::URL | ACMatchClassification::MATCH},
{3, ACMatchClassification::URL},
};
ACMatchClassifications expected_description_class = {
{0, ACMatchClassification::MATCH},
{3, ACMatchClassification::NONE},
};
std::u16string expected_inline_autocompletion(u"gle.com");
EXPECT_EQ(provider.get(), autocomplete_match.provider);
EXPECT_EQ(type, autocomplete_match.type);
EXPECT_EQ(relevance, autocomplete_match.relevance);
EXPECT_EQ(match_url, autocomplete_match.destination_url);
EXPECT_EQ(u"google.com", autocomplete_match.contents);
EXPECT_TRUE(base::ranges::equal(expected_contents_class,
autocomplete_match.contents_class))
<< "EXPECTED: " << ACMatchClassificationsAsString(expected_contents_class)
<< "ACTUAL: "
<< ACMatchClassificationsAsString(autocomplete_match.contents_class);
EXPECT_EQ(match_title, autocomplete_match.description);
EXPECT_TRUE(base::ranges::equal(expected_description_class,
autocomplete_match.description_class));
EXPECT_EQ(u"https://www.google.com", autocomplete_match.fill_into_edit);
EXPECT_TRUE(autocomplete_match.allowed_to_be_default_match);
EXPECT_EQ(expected_inline_autocompletion,
autocomplete_match.inline_autocompletion);
}
AutocompleteMatch BuildTestAutocompleteMatch(
scoped_refptr<FakeAutocompleteProvider> provider,
const std::string& input_text_s,
const GURL& match_url,
const bookmarks::TitledUrlMatch::MatchPositions& match_positions) {
std::u16string input_text(base::ASCIIToUTF16(input_text_s));
std::u16string match_title(u"The Facebook");
AutocompleteMatchType::Type type = AutocompleteMatchType::BOOKMARK_TITLE;
int relevance = 123;
int bookmark_count = 3;
MockTitledUrlNode node(match_title, match_url);
bookmarks::TitledUrlMatch titled_url_match;
titled_url_match.node = &node;
titled_url_match.title_match_positions = {{0, 3}};
// Don't capture the scheme, so that it doesn't match.
titled_url_match.url_match_positions = match_positions;
TestSchemeClassifier classifier;
AutocompleteInput input(input_text, metrics::OmniboxEventProto::NTP,
classifier);
const std::u16string fixed_up_input(input_text);
return TitledUrlMatchToAutocompleteMatch(titled_url_match, type, relevance,
bookmark_count, provider.get(),
classifier, input, fixed_up_input);
}
TEST_F(TitledUrlMatchUtilsTest, DoTrimHttpScheme) {
scoped_refptr<FakeAutocompleteProvider> provider =
base::MakeRefCounted<FakeAutocompleteProvider>(
AutocompleteProvider::Type::TYPE_BOOKMARK);
GURL match_url("http://www.facebook.com/");
AutocompleteMatch autocomplete_match =
BuildTestAutocompleteMatch(provider, "face", match_url, {{11, 15}});
ACMatchClassifications expected_contents_class = {
{0, ACMatchClassification::URL | ACMatchClassification::MATCH},
{4, ACMatchClassification::URL},
};
std::u16string expected_contents(u"facebook.com");
EXPECT_EQ(match_url, autocomplete_match.destination_url);
EXPECT_EQ(expected_contents, autocomplete_match.contents);
EXPECT_TRUE(base::ranges::equal(expected_contents_class,
autocomplete_match.contents_class))
<< "EXPECTED: " << ACMatchClassificationsAsString(expected_contents_class)
<< "ACTUAL: "
<< ACMatchClassificationsAsString(autocomplete_match.contents_class);
EXPECT_TRUE(autocomplete_match.allowed_to_be_default_match);
}
TEST_F(TitledUrlMatchUtilsTest, DontTrimHttpSchemeIfInputHasScheme) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature({omnibox::kRichAutocompletion});
RichAutocompletionParams::ClearParamsForTesting();
scoped_refptr<FakeAutocompleteProvider> provider =
base::MakeRefCounted<FakeAutocompleteProvider>(
AutocompleteProvider::Type::TYPE_BOOKMARK);
GURL match_url("http://www.facebook.com/");
AutocompleteMatch autocomplete_match = BuildTestAutocompleteMatch(
provider, "http://face", match_url, {{11, 15}});
ACMatchClassifications expected_contents_class = {
{0, ACMatchClassification::URL | ACMatchClassification::MATCH},
{11, ACMatchClassification::URL},
};
std::u16string expected_contents(u"http://facebook.com");
EXPECT_EQ(match_url, autocomplete_match.destination_url);
EXPECT_EQ(expected_contents, autocomplete_match.contents);
EXPECT_TRUE(base::ranges::equal(expected_contents_class,
autocomplete_match.contents_class))
<< "EXPECTED: " << ACMatchClassificationsAsString(expected_contents_class)
<< "ACTUAL: "
<< ACMatchClassificationsAsString(autocomplete_match.contents_class);
EXPECT_FALSE(autocomplete_match.allowed_to_be_default_match);
}
TEST_F(TitledUrlMatchUtilsTest, DoTrimHttpsScheme) {
scoped_refptr<FakeAutocompleteProvider> provider =
base::MakeRefCounted<FakeAutocompleteProvider>(
AutocompleteProvider::Type::TYPE_BOOKMARK);
GURL match_url("https://www.facebook.com/");
AutocompleteMatch autocomplete_match =
BuildTestAutocompleteMatch(provider, "face", match_url, {{12, 16}});
ACMatchClassifications expected_contents_class = {
{0, ACMatchClassification::URL | ACMatchClassification::MATCH},
{4, ACMatchClassification::URL},
};
std::u16string expected_contents(u"facebook.com");
EXPECT_EQ(match_url, autocomplete_match.destination_url);
EXPECT_EQ(expected_contents, autocomplete_match.contents);
EXPECT_TRUE(base::ranges::equal(expected_contents_class,
autocomplete_match.contents_class))
<< "EXPECTED: " << ACMatchClassificationsAsString(expected_contents_class)
<< "ACTUAL: "
<< ACMatchClassificationsAsString(autocomplete_match.contents_class);
EXPECT_TRUE(autocomplete_match.allowed_to_be_default_match);
}
TEST_F(TitledUrlMatchUtilsTest, DontTrimHttpsSchemeIfInputHasScheme) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature({omnibox::kRichAutocompletion});
RichAutocompletionParams::ClearParamsForTesting();
scoped_refptr<FakeAutocompleteProvider> provider =
base::MakeRefCounted<FakeAutocompleteProvider>(
AutocompleteProvider::Type::TYPE_BOOKMARK);
GURL match_url("https://www.facebook.com/");
AutocompleteMatch autocomplete_match = BuildTestAutocompleteMatch(
provider, "https://face", match_url, {{12, 16}});
ACMatchClassifications expected_contents_class = {
{0, ACMatchClassification::URL | ACMatchClassification::MATCH},
{12, ACMatchClassification::URL},
};
std::u16string expected_contents(u"https://facebook.com");
EXPECT_EQ(match_url, autocomplete_match.destination_url);
EXPECT_EQ(expected_contents, autocomplete_match.contents);
EXPECT_TRUE(base::ranges::equal(expected_contents_class,
autocomplete_match.contents_class))
<< "EXPECTED: " << ACMatchClassificationsAsString(expected_contents_class)
<< "ACTUAL: "
<< ACMatchClassificationsAsString(autocomplete_match.contents_class);
EXPECT_FALSE(autocomplete_match.allowed_to_be_default_match);
}
TEST_F(TitledUrlMatchUtilsTest, EmptyInlineAutocompletion) {
// Since there is no URL prefix match, the inline autocompletion string will
// be empty.
std::u16string input_text(u"goo");
std::u16string match_title(u"Email by Google");
GURL match_url("http://www.gmail.com/google");
AutocompleteMatchType::Type type = AutocompleteMatchType::BOOKMARK_TITLE;
int relevance = 123;
int bookmark_count = 3;
MockTitledUrlNode node(match_title, match_url);
bookmarks::TitledUrlMatch titled_url_match;
titled_url_match.node = &node;
titled_url_match.title_match_positions = {{9, 12}};
titled_url_match.url_match_positions = {{21, 24}};
titled_url_match.has_ancestor_match = false;
scoped_refptr<FakeAutocompleteProvider> provider =
new FakeAutocompleteProvider(AutocompleteProvider::Type::TYPE_BOOKMARK);
TestSchemeClassifier classifier;
AutocompleteInput input(input_text, metrics::OmniboxEventProto::NTP,
classifier);
const std::u16string fixed_up_input(input_text);
AutocompleteMatch autocomplete_match = TitledUrlMatchToAutocompleteMatch(
titled_url_match, type, relevance, bookmark_count, provider.get(),
classifier, input, fixed_up_input);
// 'goo' in 'gmail.com/google'
ACMatchClassifications expected_contents_class = {
{0, ACMatchClassification::URL},
{10, ACMatchClassification::URL | ACMatchClassification::MATCH},
{13, ACMatchClassification::URL},
};
// 'goo' in 'Email by Google'
ACMatchClassifications expected_description_class = {
{0, ACMatchClassification::NONE},
{9, ACMatchClassification::MATCH},
{12, ACMatchClassification::NONE},
};
EXPECT_EQ(provider.get(), autocomplete_match.provider);
EXPECT_EQ(type, autocomplete_match.type);
EXPECT_EQ(relevance, autocomplete_match.relevance);
EXPECT_EQ(match_url, autocomplete_match.destination_url);
EXPECT_EQ(u"gmail.com/google", autocomplete_match.contents);
EXPECT_TRUE(base::ranges::equal(expected_contents_class,
autocomplete_match.contents_class))
<< "EXPECTED: " << ACMatchClassificationsAsString(expected_contents_class)
<< "ACTUAL: "
<< ACMatchClassificationsAsString(autocomplete_match.contents_class);
EXPECT_EQ(match_title, autocomplete_match.description);
EXPECT_TRUE(base::ranges::equal(expected_description_class,
autocomplete_match.description_class));
EXPECT_EQ(u"www.gmail.com/google", autocomplete_match.fill_into_edit);
EXPECT_FALSE(autocomplete_match.allowed_to_be_default_match);
EXPECT_TRUE(autocomplete_match.inline_autocompletion.empty());
}
TEST_F(TitledUrlMatchUtilsTest, PathsInContentsAndDescription) {
scoped_refptr<FakeAutocompleteProvider> provider =
new FakeAutocompleteProvider(AutocompleteProvider::Type::TYPE_BOOKMARK);
TestSchemeClassifier classifier;
std::vector<std::u16string> ancestors = {u"parent", u"grandparent"};
// Verifies contents and description of the AutocompleteMatch returned from
// |bookmarks::TitledUrlMatchToAutocompleteMatch()|.
auto test = [&](std::string title, std::string url, bool has_url_match,
bool has_ancestor_match, std::string expected_contents,
std::string expected_description) {
SCOPED_TRACE("title [" + title + "], url [" + url + "], has_url_match [" +
std::string(has_url_match ? "true" : "false") +
"], has_ancestor_match [" +
std::string(has_ancestor_match ? "true" : "false") + "].");
MockTitledUrlNode node(base::UTF8ToUTF16(title), GURL(url), ancestors);
bookmarks::TitledUrlMatch titled_url_match;
titled_url_match.node = &node;
if (has_url_match)
titled_url_match.url_match_positions.push_back(
{8, 8}); // 8 in order to be after 'https://'
titled_url_match.has_ancestor_match = has_ancestor_match;
AutocompleteInput input(std::u16string(), metrics::OmniboxEventProto::NTP,
classifier);
AutocompleteMatch autocomplete_match = TitledUrlMatchToAutocompleteMatch(
titled_url_match, AutocompleteMatchType::BOOKMARK_TITLE, 1,
/*bookmark_count=*/3, provider.get(), classifier, input,
std::u16string());
EXPECT_EQ(base::UTF16ToUTF8(autocomplete_match.contents),
expected_contents);
EXPECT_EQ(base::UTF16ToUTF8(autocomplete_match.description),
expected_description);
};
test("title", "https://url.com", false, false, "grandparent/parent", "title");
test("title", "https://url.com", true, false, "url.com", "title");
test("title", "https://url.com", false, true, "grandparent/parent", "title");
test("title", "https://url.com", true, true, "grandparent/parent", "title");
}