[omnibox] [max-suggest] Add dynamic suggestions limit per # of URLs.
Adds a OmniboxDynamicMaxAutocomplete feature with 2 params,
OmniboxDynamicMaxAutocompleteUrlCutoff &
OmniboxDynamicMaxAutocompleteIncreasedLimit.
- When disabled, the omnibox allows UIMaxAutocompleteMatches
suggestions.
- When dynamic max autocompletion is enabled, the omnibox allows
suggestions up to the increased limit if doing so has URL cutoff or less
URL suggestions. It could also allow a partial increase if doing so
avoids showing more than the cutoff.
E.g. a UIMaxAutocompleteMatches of 8, URL cutoff of 2, and increased
limit of 10 translates to "show 10 or 9 suggestions if doing so includes
at most 2 URLs; otherwise show 8 suggestions.
Change-Id: I2f5e589f9e5307a62158223e6f6965476e55c257
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2274466
Commit-Queue: manuk hovanesian <[email protected]>
Reviewed-by: Justin Donnelly <[email protected]>
Cr-Commit-Position: refs/heads/master@{#786541}
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 01cb3839..e39a40e1 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -187,12 +187,12 @@
break;
case OmniboxFieldTrial::EMPHASIZE_WHEN_TITLE_MATCHES:
emphasize = !i.description.empty() &&
- AutocompleteMatch::HasMatchStyle(i.description_class);
+ AutocompleteMatch::HasMatchStyle(i.description_class);
break;
case OmniboxFieldTrial::EMPHASIZE_WHEN_ONLY_TITLE_MATCHES:
emphasize = !i.description.empty() &&
- AutocompleteMatch::HasMatchStyle(i.description_class) &&
- !AutocompleteMatch::HasMatchStyle(i.contents_class);
+ AutocompleteMatch::HasMatchStyle(i.description_class) &&
+ !AutocompleteMatch::HasMatchStyle(i.contents_class);
break;
case OmniboxFieldTrial::EMPHASIZE_NEVER:
break;
@@ -262,8 +262,9 @@
LimitNumberOfURLsShown(GetMaxMatches(input.from_omnibox_focus()),
max_url_count, comparing_object);
- // Limit total matches per OmniboxUIExperimentMaxAutocompleteMatches &
- // OmniboxMaxZeroSuggestMatches.
+ // Limit total matches accounting for suggestions score <= 0, sub matches, and
+ // feature configs such as OmniboxUIExperimentMaxAutocompleteMatches,
+ // OmniboxMaxZeroSuggestMatches, and OmniboxDynamicMaxAutocomplete.
const size_t num_matches = CalculateNumMatches(input.from_omnibox_focus(),
matches_, comparing_object);
matches_.resize(num_matches);
@@ -614,6 +615,15 @@
bool input_from_omnibox_focus,
const ACMatches& matches,
const CompareWithDemoteByType<AutocompleteMatch>& comparing_object) {
+ // Use alternative CalculateNumMatchesPerUrlCount if applicable.
+ // TODO (manukh): CalculateNumMatchesPerUrlCount doesn't account for
+ // submatches. That's ok since we plan to replace submatches with dedicated
+ // row suggestions. Otherwise, CalculateNumMatchesPerUrlCount should account
+ // for them.
+ if (!input_from_omnibox_focus &&
+ base::FeatureList::IsEnabled(omnibox::kNewSearchFeatures) &&
+ base::FeatureList::IsEnabled(omnibox::kDynamicMaxAutocomplete))
+ return CalculateNumMatchesPerUrlCount(matches, comparing_object);
// In the process of trimming, drop all matches with a demoted relevance
// score of 0.
size_t max_matches_by_policy = GetMaxMatches(input_from_omnibox_focus);
@@ -634,6 +644,35 @@
return num_matches;
}
+// static
+size_t AutocompleteResult::CalculateNumMatchesPerUrlCount(
+ const ACMatches& matches,
+ const CompareWithDemoteByType<AutocompleteMatch>& comparing_object) {
+ size_t base_limit = GetMaxMatches();
+ size_t url_cutoff = base::GetFieldTrialParamByFeatureAsInt(
+ omnibox::kDynamicMaxAutocomplete,
+ OmniboxFieldTrial::kDynamicMaxAutocompleteUrlCutoffParam, 0);
+ size_t increased_limit = base::GetFieldTrialParamByFeatureAsInt(
+ omnibox::kDynamicMaxAutocomplete,
+ OmniboxFieldTrial::kDynamicMaxAutocompleteIncreasedLimitParam,
+ base_limit);
+
+ size_t num_matches = 0;
+ size_t num_url_matches = 0;
+ for (auto match : matches) {
+ // Matches scored less than 0 won't be shown anyways, so we can break early.
+ if (comparing_object.GetDemotedRelevance(matches[num_matches]) <= 0)
+ break;
+ if (!AutocompleteMatch::IsSearchType(match.type))
+ num_url_matches++;
+ size_t limit = num_url_matches <= url_cutoff ? increased_limit : base_limit;
+ if (num_matches >= limit)
+ break;
+ num_matches++;
+ }
+ return num_matches;
+}
+
void AutocompleteResult::Reset() {
matches_.clear();
}
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index e6452408..339fbf7 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -124,6 +124,12 @@
bool input_from_omnibox_focus,
const ACMatches& matches,
const CompareWithDemoteByType<AutocompleteMatch>& comparing_object);
+ // Determines how many matches to keep depending on how many URLs would be
+ // shown. CalculateNumMatches defers to CalculateNumMatchesPerUrlCount if the
+ // kDynamicMaxAutocomplete feature is enabled.
+ static size_t CalculateNumMatchesPerUrlCount(
+ const ACMatches& matches,
+ const CompareWithDemoteByType<AutocompleteMatch>& comparing_object);
const SearchSuggestionParser::HeadersMap& headers_map() const {
return headers_map_;
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 0763faa3..ebe4dc09 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -1999,6 +1999,51 @@
}
}
+TEST_F(AutocompleteResultTest, CalculateNumMatchesPerUrlCountTest) {
+ CompareWithDemoteByType<AutocompleteMatch> comparison_object(
+ metrics::OmniboxEventProto::OTHER);
+ enum SuggestionType { search, url };
+
+ auto test = [comparison_object](std::string base_limit,
+ std::string url_cutoff,
+ std::string increased_limit,
+ std::vector<SuggestionType> types,
+ size_t expected_num_matches) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeaturesAndParameters(
+ {{omnibox::kDynamicMaxAutocomplete,
+ {{OmniboxFieldTrial::kDynamicMaxAutocompleteUrlCutoffParam,
+ url_cutoff},
+ {OmniboxFieldTrial::kDynamicMaxAutocompleteIncreasedLimitParam,
+ increased_limit}}},
+ {omnibox::kUIExperimentMaxAutocompleteMatches,
+ {{OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam, base_limit}}},
+ {omnibox::kNewSearchFeatures, {}}},
+ {});
+
+ ACMatches matches;
+ for (auto type : types) {
+ AutocompleteMatch m;
+ m.relevance = 100;
+ if (type)
+ m.type = AutocompleteMatchType::URL_WHAT_YOU_TYPED;
+ matches.push_back(m);
+ }
+ const size_t num_matches = AutocompleteResult::CalculateNumMatches(
+ false, matches, comparison_object);
+ EXPECT_EQ(num_matches, expected_num_matches);
+ };
+
+ test("2", "0", "4", {search}, 1);
+ test("2", "0", "4", {search, search, search, search, search}, 4);
+ test("2", "0", "4", {search, search, search, search, url}, 4);
+ test("2", "0", "4", {search, search, search, url, search}, 3);
+ test("2", "0", "4", {search, search, url, search, search}, 2);
+ test("2", "0", "4", {search, url, search, search, search}, 2);
+ test("2", "1", "4", {search, url, search, search, search}, 4);
+ test("2", "1", "4", {search, url, search, url, search}, 3);
+}
+
TEST_F(AutocompleteResultTest, ClipboardSuggestionOnTopOfSearchSuggestionTest) {
// clang-format off
TestData data[] = {
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index e4d02c3..d069c3ec 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -397,6 +397,28 @@
return default_max_matches_per_provider;
}
+bool OmniboxFieldTrial::IsMaxURLMatchesFeatureEnabled() {
+ // If new search features are disabled, return the default/launched value for
+ // the respective platforms, independent of the state of the Feature.
+ if (!base::FeatureList::IsEnabled(omnibox::kNewSearchFeatures))
+ return omnibox::kOmniboxMaxURLMatchesEnabledByDefault;
+
+ return base::FeatureList::IsEnabled(omnibox::kOmniboxMaxURLMatches);
+}
+
+size_t OmniboxFieldTrial::GetMaxURLMatches() {
+ constexpr size_t kDefaultMaxURLMatches = 7;
+
+ // If new search features are disabled, ignore the parameter and use the
+ // default value.
+ if (!base::FeatureList::IsEnabled(omnibox::kNewSearchFeatures))
+ return kDefaultMaxURLMatches;
+
+ return base::GetFieldTrialParamByFeatureAsInt(
+ omnibox::kOmniboxMaxURLMatches,
+ OmniboxFieldTrial::kOmniboxMaxURLMatchesParam, kDefaultMaxURLMatches);
+}
+
void OmniboxFieldTrial::GetDefaultHUPScoringParams(
HUPScoringParams* scoring_params) {
ScoreBuckets* type_score_buckets = &scoring_params->typed_count_buckets;
@@ -661,19 +683,6 @@
return static_cast<EmphasizeTitlesCondition>(value);
}
-size_t OmniboxFieldTrial::GetMaxURLMatches() {
- constexpr size_t kDefaultMaxURLMatches = 7;
-
- // If new search features are disabled, ignore the parameter and use the
- // default value.
- if (!base::FeatureList::IsEnabled(omnibox::kNewSearchFeatures))
- return kDefaultMaxURLMatches;
-
- return base::GetFieldTrialParamByFeatureAsInt(
- omnibox::kOmniboxMaxURLMatches,
- OmniboxFieldTrial::kOmniboxMaxURLMatchesParam, kDefaultMaxURLMatches);
-}
-
bool OmniboxFieldTrial::IsReverseAnswersEnabled() {
return base::FeatureList::IsEnabled(omnibox::kOmniboxReverseAnswers);
}
@@ -711,15 +720,6 @@
return base::FeatureList::IsEnabled(omnibox::kExperimentalKeywordMode);
}
-bool OmniboxFieldTrial::IsMaxURLMatchesFeatureEnabled() {
- // If new search features are disabled, return the default/launched value for
- // the respective platforms, independent of the state of the Feature.
- if (!base::FeatureList::IsEnabled(omnibox::kNewSearchFeatures))
- return omnibox::kOmniboxMaxURLMatchesEnabledByDefault;
-
- return base::FeatureList::IsEnabled(omnibox::kOmniboxMaxURLMatches);
-}
-
bool OmniboxFieldTrial::IsRichAutocompletionEnabled() {
return base::FeatureList::IsEnabled(omnibox::kRichAutocompletion);
}
@@ -898,6 +898,10 @@
"UIMaxAutocompleteMatchesByProvider";
const char OmniboxFieldTrial::kUIMaxAutocompleteMatchesParam[] =
"UIMaxAutocompleteMatches";
+const char OmniboxFieldTrial::kDynamicMaxAutocompleteUrlCutoffParam[] =
+ "OmniboxDynamicMaxAutocompleteUrlCutoff";
+const char OmniboxFieldTrial::kDynamicMaxAutocompleteIncreasedLimitParam[] =
+ "OmniboxDynamicMaxAutocompleteIncreasedLimit";
const char OmniboxFieldTrial::kOnDeviceHeadModelLocaleConstraint[] =
"ForceModelLocaleConstraint";
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 768c2fa..38723a7e 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -212,13 +212,22 @@
DemotionMultipliers* demotions_by_type);
// ---------------------------------------------------------
-// For the UIMaxAutocompleteMatchesByProvider experiment that's part of the
-// bundled omnibox field trial.
+// For experiments related to the number of suggestions shown.
// If the user is in an experiment group that specifies the max results for a
// particular provider, returns the limit. Otherwise returns the default limit.
size_t GetProviderMaxMatches(AutocompleteProvider::Type provider);
+// Returns whether the feature to limit the number of shown URL matches
+// is enabled.
+bool IsMaxURLMatchesFeatureEnabled();
+
+// 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 the HistoryURL provider new scoring experiment that is part of the
// bundled omnibox field trial.
@@ -368,12 +377,6 @@
EmphasizeTitlesCondition GetEmphasizeTitlesConditionForInput(
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.
@@ -404,10 +407,6 @@
// assortment of keyword mode experiments.
bool IsExperimentalKeywordModeEnabled();
-// Returns whether the feature to limit the number of shown URL matches
-// is enabled.
-bool IsMaxURLMatchesFeatureEnabled();
-
// Rich autocompletion.
bool IsRichAutocompletionEnabled();
bool RichAutocompletionAutocompleteTitles();
@@ -497,6 +496,17 @@
extern const char kOmniboxMaxURLMatchesParam[];
extern const char kUIMaxAutocompleteMatchesByProviderParam[];
extern const char kUIMaxAutocompleteMatchesParam[];
+// The URL cutoff and increased limit for dynamic max autocompletion.
+// - When dynamic max autocompletion is disabled, the omnibox allows
+// UIMaxAutocompleteMatches suggestions.
+// - When dynamic max autocompletion is enabled, the omnibox allows
+// suggestions up to the increased limit if doing so has URL cutoff or less
+// URL suggestions.
+// E.g. a UIMaxAutocompleteMatches of 8, URL cutoff of 2, and increased limit of
+// 10 translates to "show 10 or 9 suggestions if doing so includes at most 2
+// URLs; otherwise show 8 suggestions.
+extern const char kDynamicMaxAutocompleteUrlCutoffParam[];
+extern const char kDynamicMaxAutocompleteIncreasedLimitParam[];
// Parameter names used by on device head provider.
// These four parameters are shared by both non-incognito and incognito.
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index c4c7b56..f12af1d3 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -215,6 +215,12 @@
? base::FEATURE_ENABLED_BY_DEFAULT
: base::FEATURE_DISABLED_BY_DEFAULT};
+// Feature used to cap max suggestions to a dynamic limit based on how many URLs
+// would be shown. E.g., show up to 10 suggestions if doing so would display no
+// URLs; else show up to 8 suggestions if doing so would include 1 or more URLs.
+const base::Feature kDynamicMaxAutocomplete{"OmniboxDynamicMaxAutocomplete",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Feature that configures ZeroSuggestProvider using the "ZeroSuggestVariant"
// per-page-classification parameter.
//
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index c5813af..f5e23308 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -52,6 +52,7 @@
// OmniboxFieldTrial.
extern const bool kOmniboxMaxURLMatchesEnabledByDefault;
extern const base::Feature kOmniboxMaxURLMatches;
+extern const base::Feature kDynamicMaxAutocomplete;
// On-Focus Suggestions a.k.a. ZeroSuggest.
extern const base::Feature kOnFocusSuggestions;