Commit b3e0873d authored by Kevin Bailey's avatar Kevin Bailey Committed by Commit Bot

[omnibox] Experiment to limit the number of URL suggestions shown

This CL adds a feature and companion parameter that respectively
enable and select the preferred maximum number of URL suggestions to
be shown in the Omnibox. The limit can be exceeded if there are no
other types to replace them with.

Bug: 966462
Change-Id: Id4a5e9cf5b5ef778f79de7f085e8bd5c7bfa779b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1628640Reviewed-by: default avatarJustin Donnelly <jdonnelly@chromium.org>
Reviewed-by: default avatarmanuk hovanesian <manukh@chromium.org>
Commit-Queue: Kevin Bailey <krb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664115}
parent d231545e
...@@ -179,6 +179,12 @@ void AutocompleteResult::SortAndCull( ...@@ -179,6 +179,12 @@ void AutocompleteResult::SortAndCull(
auto it = FindTopMatch(input.current_page_classification(), &matches_); auto it = FindTopMatch(input.current_page_classification(), &matches_);
if (it != matches_.end()) if (it != matches_.end())
std::rotate(matches_.begin(), it, it + 1); std::rotate(matches_.begin(), it, it + 1);
size_t max_url_count = 0;
if (OmniboxFieldTrial::IsCapURLMatchesFeatureEnabled() &&
(max_url_count = OmniboxFieldTrial::GetMaxURLMatches()) != 0)
LimitNumberOfURLsShown(max_url_count, comparing_object);
// In the process of trimming, drop all matches with a demoted relevance // In the process of trimming, drop all matches with a demoted relevance
// score of 0. // score of 0.
const size_t max_num_matches = std::min(GetMaxMatches(), matches_.size()); const size_t max_num_matches = std::min(GetMaxMatches(), matches_.size());
...@@ -197,9 +203,8 @@ void AutocompleteResult::SortAndCull( ...@@ -197,9 +203,8 @@ void AutocompleteResult::SortAndCull(
// There is no default match for chromeOS launcher zero prefix query // There is no default match for chromeOS launcher zero prefix query
// suggestions. // suggestions.
if ((input.text().empty() && if (input.text().empty() && (input.current_page_classification() ==
input.current_page_classification() == metrics::OmniboxEventProto::CHROMEOS_APP_LIST)) {
metrics::OmniboxEventProto::CHROMEOS_APP_LIST)) {
default_match_ = end(); default_match_ = end();
alternate_nav_url_ = GURL(); alternate_nav_url_ = GURL();
return; return;
...@@ -737,3 +742,31 @@ std::pair<GURL, bool> AutocompleteResult::GetMatchComparisonFields( ...@@ -737,3 +742,31 @@ std::pair<GURL, bool> AutocompleteResult::GetMatchComparisonFields(
return std::make_pair(match.stripped_destination_url, return std::make_pair(match.stripped_destination_url,
match.type == ACMatchType::CALCULATOR); match.type == ACMatchType::CALCULATOR);
} }
void AutocompleteResult::LimitNumberOfURLsShown(
size_t max_url_count,
const CompareWithDemoteByType<AutocompleteMatch>& comparing_object) {
size_t search_count = std::count_if(
matches_.begin(), matches_.end(), [&](const AutocompleteMatch& m) {
return AutocompleteMatch::IsSearchType(m.type) &&
// Don't count if would be removed.
comparing_object.GetDemotedRelevance(m) > 0;
});
// Display more than GetMaxURLMatches() if there are no non-URL suggestions
// to replace them. Avoid signed math.
if (GetMaxMatches() > search_count &&
GetMaxMatches() - search_count > max_url_count)
max_url_count = GetMaxMatches() - search_count;
size_t url_count = 0, total_count = 0;
// Erase URL suggestions past the count of allowed ones, or anything past
// maximum.
matches_.erase(
std::remove_if(matches_.begin(), matches_.end(),
[&url_count, max_url_count,
&total_count](const AutocompleteMatch& m) {
return (!AutocompleteMatch::IsSearchType(m.type) &&
++url_count > max_url_count) ||
++total_count > GetMaxMatches();
}),
matches_.end());
}
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/omnibox/browser/autocomplete_match.h" #include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/match_compare.h"
#include "third_party/metrics_proto/omnibox_event.pb.h" #include "third_party/metrics_proto/omnibox_event.pb.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -190,6 +191,13 @@ class AutocompleteResult { ...@@ -190,6 +191,13 @@ class AutocompleteResult {
static std::pair<GURL, bool> GetMatchComparisonFields( static std::pair<GURL, bool> GetMatchComparisonFields(
const AutocompleteMatch& match); const AutocompleteMatch& match);
// This method reduces the number of navigation suggestions to that of
// |max_url_matches| but will allow more if there are no other types to
// replace them.
void LimitNumberOfURLsShown(
size_t max_url_count,
const CompareWithDemoteByType<AutocompleteMatch>& comparing_object);
ACMatches matches_; ACMatches matches_;
const_iterator default_match_; const_iterator default_match_;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
...@@ -1048,6 +1049,88 @@ TEST_F(AutocompleteResultTest, SortAndCullGroupSuggestionsByType) { ...@@ -1048,6 +1049,88 @@ TEST_F(AutocompleteResultTest, SortAndCullGroupSuggestionsByType) {
AssertResultMatches(result, expected_data, base::size(expected_data)); AssertResultMatches(result, expected_data, base::size(expected_data));
} }
TEST_F(AutocompleteResultTest, SortAndCullCapURLMatches) {
base::test::ScopedFeatureList feature_list;
std::map<std::string, std::string> parameters = {
{OmniboxFieldTrial::kOmniboxMaxURLMatchesParam, "3"}};
feature_list.InitAndEnableFeatureWithParameters(
omnibox::kOmniboxCapURLMatches, parameters);
EXPECT_TRUE(OmniboxFieldTrial::IsCapURLMatchesFeatureEnabled());
EXPECT_EQ(OmniboxFieldTrial::GetMaxURLMatches(), 3u);
// Case 1: Eject URL match for a search.
{
ACMatches matches;
const AutocompleteMatchTestData data[] = {
{"http://search-what-you-typed/",
AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
{"http://search-history/", AutocompleteMatchType::SEARCH_HISTORY},
{"http://history-url/", AutocompleteMatchType::HISTORY_URL},
{"http://history-title/", AutocompleteMatchType::HISTORY_TITLE},
{"http://url-what-you-typed/",
AutocompleteMatchType::URL_WHAT_YOU_TYPED},
{"http://clipboard-url/", AutocompleteMatchType::CLIPBOARD_URL},
{"http://search-suggest/", AutocompleteMatchType::SEARCH_SUGGEST},
};
PopulateAutocompleteMatchesFromTestData(data, base::size(data), &matches);
AutocompleteInput input(base::ASCIIToUTF16("a"),
metrics::OmniboxEventProto::OTHER,
TestSchemeClassifier());
AutocompleteResult result;
result.AppendMatches(input, matches);
result.SortAndCull(input, template_url_service_.get());
EXPECT_EQ(result.size(), 6u);
AutocompleteMatchType::Type expected_types[] = {
AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
AutocompleteMatchType::SEARCH_HISTORY,
AutocompleteMatchType::HISTORY_URL,
AutocompleteMatchType::HISTORY_TITLE,
AutocompleteMatchType::URL_WHAT_YOU_TYPED,
AutocompleteMatchType::SEARCH_SUGGEST,
};
for (size_t i = 0; i < result.size(); ++i)
EXPECT_EQ(result.match_at(i)->type, expected_types[i]);
}
// Case 2: Do not eject URL match because there's no replacement.
{
ACMatches matches;
const AutocompleteMatchTestData data[] = {
{"http://search-what-you-typed/",
AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
{"http://search-history/", AutocompleteMatchType::SEARCH_HISTORY},
{"http://history-url/", AutocompleteMatchType::HISTORY_URL},
{"http://history-title/", AutocompleteMatchType::HISTORY_TITLE},
{"http://url-what-you-typed/",
AutocompleteMatchType::URL_WHAT_YOU_TYPED},
{"http://clipboard-url/", AutocompleteMatchType::CLIPBOARD_URL},
{"http://bookmark-title/", AutocompleteMatchType::BOOKMARK_TITLE},
};
PopulateAutocompleteMatchesFromTestData(data, base::size(data), &matches);
AutocompleteInput input(base::ASCIIToUTF16("a"),
metrics::OmniboxEventProto::OTHER,
TestSchemeClassifier());
AutocompleteResult result;
result.AppendMatches(input, matches);
result.SortAndCull(input, template_url_service_.get());
EXPECT_EQ(result.size(), 6u);
AutocompleteMatchType::Type expected_types[] = {
AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
AutocompleteMatchType::SEARCH_HISTORY,
AutocompleteMatchType::HISTORY_URL,
AutocompleteMatchType::HISTORY_TITLE,
AutocompleteMatchType::URL_WHAT_YOU_TYPED,
AutocompleteMatchType::CLIPBOARD_URL,
};
for (size_t i = 0; i < result.size(); ++i)
EXPECT_EQ(result.match_at(i)->type, expected_types[i]);
}
}
TEST_F(AutocompleteResultTest, TopMatchIsStandaloneVerbatimMatch) { TEST_F(AutocompleteResultTest, TopMatchIsStandaloneVerbatimMatch) {
ACMatches matches; ACMatches matches;
AutocompleteResult result; AutocompleteResult result;
......
...@@ -647,6 +647,13 @@ OmniboxFieldTrial::GetEmphasizeTitlesConditionForInput( ...@@ -647,6 +647,13 @@ OmniboxFieldTrial::GetEmphasizeTitlesConditionForInput(
return static_cast<EmphasizeTitlesCondition>(value); return static_cast<EmphasizeTitlesCondition>(value);
} }
size_t OmniboxFieldTrial::GetMaxURLMatches() {
return base::GetFieldTrialParamByFeatureAsInt(
omnibox::kOmniboxCapURLMatches,
OmniboxFieldTrial::kOmniboxMaxURLMatchesParam,
0); // default
}
bool OmniboxFieldTrial::IsPreserveDefaultMatchScoreEnabled() { bool OmniboxFieldTrial::IsPreserveDefaultMatchScoreEnabled() {
return base::FeatureList::IsEnabled( return base::FeatureList::IsEnabled(
omnibox::kOmniboxPreserveDefaultMatchScore); omnibox::kOmniboxPreserveDefaultMatchScore);
...@@ -709,6 +716,10 @@ bool OmniboxFieldTrial::IsGroupSuggestionsBySearchVsUrlFeatureEnabled() { ...@@ -709,6 +716,10 @@ bool OmniboxFieldTrial::IsGroupSuggestionsBySearchVsUrlFeatureEnabled() {
omnibox::kOmniboxGroupSuggestionsBySearchVsUrl); omnibox::kOmniboxGroupSuggestionsBySearchVsUrl);
} }
bool OmniboxFieldTrial::IsCapURLMatchesFeatureEnabled() {
return base::FeatureList::IsEnabled(omnibox::kOmniboxCapURLMatches);
}
const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] = const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] =
"OmniboxBundledExperimentV1"; "OmniboxBundledExperimentV1";
const char OmniboxFieldTrial::kDisableProvidersRule[] = "DisableProviders"; const char OmniboxFieldTrial::kDisableProvidersRule[] = "DisableProviders";
...@@ -745,6 +756,9 @@ const char OmniboxFieldTrial::kKeywordScoreForSufficientlyCompleteMatchRule[] = ...@@ -745,6 +756,9 @@ const char OmniboxFieldTrial::kKeywordScoreForSufficientlyCompleteMatchRule[] =
"KeywordScoreForSufficientlyCompleteMatch"; "KeywordScoreForSufficientlyCompleteMatch";
const char OmniboxFieldTrial::kEmphasizeTitlesRule[] = "EmphasizeTitles"; const char OmniboxFieldTrial::kEmphasizeTitlesRule[] = "EmphasizeTitles";
const char OmniboxFieldTrial::kOmniboxMaxURLMatchesParam[] =
"OmniboxMaxURLMatches";
const char OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam[] = const char OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam[] =
"TypedCountRelevanceCap"; "TypedCountRelevanceCap";
const char OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam[] = const char OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam[] =
......
...@@ -390,6 +390,12 @@ int KeywordScoreForSufficientlyCompleteMatch(); ...@@ -390,6 +390,12 @@ int KeywordScoreForSufficientlyCompleteMatch();
EmphasizeTitlesCondition GetEmphasizeTitlesConditionForInput( EmphasizeTitlesCondition GetEmphasizeTitlesConditionForInput(
const AutocompleteInput& input); const AutocompleteInput& input);
// Returns the maximum number of URL matches that should be allowed within
// the Omnibox if there are search-type matches available to replace them.
// If the capping feature is not enabled, or the parameter cannot be
// parsed, it returns 0.
size_t GetMaxURLMatches();
// --------------------------------------------------------- // ---------------------------------------------------------
// For UI experiments. // For UI experiments.
...@@ -431,6 +437,10 @@ bool IsExperimentalKeywordModeEnabled(); ...@@ -431,6 +437,10 @@ bool IsExperimentalKeywordModeEnabled();
// which "bunches" search suggestions (except for the default match). // which "bunches" search suggestions (except for the default match).
bool IsGroupSuggestionsBySearchVsUrlFeatureEnabled(); bool IsGroupSuggestionsBySearchVsUrlFeatureEnabled();
// Returns whether the feature to limit the number of shown URL matches
// is enabled.
bool IsCapURLMatchesFeatureEnabled();
// --------------------------------------------------------- // ---------------------------------------------------------
// Clipboard URL suggestions: // Clipboard URL suggestions:
...@@ -468,6 +478,9 @@ extern const char kKeywordScoreForSufficientlyCompleteMatchRule[]; ...@@ -468,6 +478,9 @@ extern const char kKeywordScoreForSufficientlyCompleteMatchRule[];
extern const char kHQPAllowDupMatchesForScoringRule[]; extern const char kHQPAllowDupMatchesForScoringRule[];
extern const char kEmphasizeTitlesRule[]; extern const char kEmphasizeTitlesRule[];
// Parameter name used by the Omnibox match capping experiment.
extern const char kOmniboxMaxURLMatchesParam[];
// Parameter names used by the HUP new scoring experiments. // Parameter names used by the HUP new scoring experiments.
extern const char kHUPNewScoringTypedCountRelevanceCapParam[]; extern const char kHUPNewScoringTypedCountRelevanceCapParam[];
extern const char kHUPNewScoringTypedCountHalfLifeTimeParam[]; extern const char kHUPNewScoringTypedCountHalfLifeTimeParam[];
......
...@@ -80,6 +80,14 @@ const base::Feature kOmniboxGroupSuggestionsBySearchVsUrl{ ...@@ -80,6 +80,14 @@ const base::Feature kOmniboxGroupSuggestionsBySearchVsUrl{
const base::Feature kOmniboxLocalEntitySuggestions{ const base::Feature kOmniboxLocalEntitySuggestions{
"OmniboxLocalEntitySuggestions", base::FEATURE_DISABLED_BY_DEFAULT}; "OmniboxLocalEntitySuggestions", base::FEATURE_DISABLED_BY_DEFAULT};
// Feature used to cap the number of URL-type matches shown within the
// Omnibox. If enabled, the number of URL-type matches is limited (unless
// there are no more non-URL matches available.) If enabled, there is a
// companion parameter - OmniboxMaxURLMatches - which specifies the maximum
// desired number of URL-type matches.
const base::Feature kOmniboxCapURLMatches{"OmniboxCapURLMatches",
base::FEATURE_DISABLED_BY_DEFAULT};
// Feature used to enable entity suggestion images and enhanced presentation // Feature used to enable entity suggestion images and enhanced presentation
// showing more context and descriptive text about the entity. // showing more context and descriptive text about the entity.
const base::Feature kOmniboxRichEntitySuggestions{ const base::Feature kOmniboxRichEntitySuggestions{
......
...@@ -18,6 +18,7 @@ extern const base::Feature kOneClickUnelide; ...@@ -18,6 +18,7 @@ extern const base::Feature kOneClickUnelide;
extern const base::Feature kSimplifyHttpsIndicator; extern const base::Feature kSimplifyHttpsIndicator;
extern const base::Feature kOmniboxGroupSuggestionsBySearchVsUrl; extern const base::Feature kOmniboxGroupSuggestionsBySearchVsUrl;
extern const base::Feature kOmniboxLocalEntitySuggestions; extern const base::Feature kOmniboxLocalEntitySuggestions;
extern const base::Feature kOmniboxCapURLMatches;
extern const base::Feature kOmniboxRichEntitySuggestions; extern const base::Feature kOmniboxRichEntitySuggestions;
extern const base::Feature kOmniboxNewAnswerLayout; extern const base::Feature kOmniboxNewAnswerLayout;
extern const base::Feature kOmniboxReverseAnswers; extern const base::Feature kOmniboxReverseAnswers;
......
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