| // Copyright 2022 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/autocomplete_grouper_sections.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <optional> |
| |
| #include "base/containers/contains.h" |
| #include "base/dcheck_is_on.h" |
| #include "base/debug/crash_logging.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/notreached.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "components/omnibox/browser/autocomplete_grouper_groups.h" |
| #include "components/omnibox/browser/autocomplete_match.h" |
| #include "components/omnibox/browser/autocomplete_match_type.h" |
| #include "components/omnibox/browser/omnibox_field_trial.h" |
| #include "components/omnibox/common/omnibox_feature_configs.h" |
| #include "third_party/omnibox_proto/groups.pb.h" |
| |
| namespace { |
| constexpr size_t kMobileMostVisitedTilesLimit = 10; |
| constexpr bool is_android = !!BUILDFLAG(IS_ANDROID); |
| constexpr size_t kMaxSuggestionsPerUnscopedExtension = 4; |
| constexpr size_t kMaxExtensions = 2; |
| } |
| |
| Section::Section(size_t limit, |
| Groups groups, |
| omnibox::GroupConfigMap& group_configs, |
| omnibox::GroupConfig_SideType side_type) |
| : limit_(limit), |
| groups_(std::move(groups)), |
| group_configs_(group_configs), |
| side_type_(side_type) { |
| #if DCHECK_IS_ON() |
| // Make sure all the `GroupId`s in the `Group`s have the same `RenderType`. |
| for (const auto& group : groups_) { |
| std::optional<omnibox::GroupConfig_RenderType> last_render_type; |
| for (const auto& [group_id, _] : group.group_id_limits_and_counts()) { |
| const auto& render_type = group_configs[group_id].render_type(); |
| DCHECK_EQ(last_render_type.value_or(render_type), render_type) |
| << "GroupId " << group_id |
| << " has different RenderType than the previous one."; |
| last_render_type = render_type; |
| } |
| } |
| #endif // DCHECK_IS_ON() |
| } |
| |
| Section::~Section() = default; |
| |
| // static |
| ACMatches Section::GroupMatches(PSections sections, ACMatches& matches) { |
| for (auto& section : sections) { |
| section->InitFromMatches(matches); |
| } |
| |
| for (const auto& match : matches) { |
| DCHECK(match.suggestion_group_id.has_value()); |
| for (const auto& section : sections) { |
| if (section->Add(match)) |
| break; |
| } |
| } |
| |
| ACMatches grouped_matches = {}; |
| for (auto& section : sections) { |
| for (auto& group : section->groups_) { |
| group.GroupMatches(); |
| for (AutocompleteMatch* match : group.matches()) { |
| grouped_matches.push_back(std::move(*match)); |
| } |
| } |
| } |
| |
| return grouped_matches; |
| } |
| |
| Groups::iterator Section::FindGroup(const AutocompleteMatch& match) { |
| // Check if match is allowed in this `Section` by its GroupId `SideType`. |
| const auto& group_id = match.suggestion_group_id.value(); |
| if (group_configs_[group_id].side_type() != side_type_) { |
| return groups_.end(); |
| } |
| return std::ranges::find_if( |
| groups_, [&](const auto& group) { return group.CanAdd(match); }); |
| } |
| |
| bool Section::Add(const AutocompleteMatch& match) { |
| if (count_ >= limit_) { |
| return false; |
| } |
| auto group_itr = FindGroup(match); |
| if (group_itr == groups_.end()) { |
| return false; |
| } |
| group_itr->Add(match); |
| count_++; |
| return true; |
| } |
| |
| ZpsSection::ZpsSection(size_t limit, |
| Groups groups, |
| omnibox::GroupConfigMap& group_configs, |
| omnibox::GroupConfig_SideType side_type) |
| : Section(limit, std::move(groups), group_configs, side_type) {} |
| |
| void ZpsSection::InitFromMatches(ACMatches& matches) { |
| // Ensure matches are sorted in the order of their potential containing |
| // groups. E.g., if `groups_ = {group 1, group 2}, matches that can be added |
| // to group 1 must appear before those that can only be added to group 2. |
| size_t last_group_index = 0; |
| for (const auto& match : matches) { |
| auto group_itr = FindGroup(match); |
| if (group_itr == groups_.end()) { |
| continue; |
| } |
| size_t current_group_index = std::distance(groups_.begin(), group_itr); |
| if (current_group_index < last_group_index) { |
| const std::string match_type = |
| AutocompleteMatchType::ToString(match.type); |
| const std::string match_group_id = |
| omnibox::GroupId_Name(match.suggestion_group_id.value()); |
| const std::string match_relevance = base::NumberToString(match.relevance); |
| const std::string group_description = base::JoinString( |
| [&]() { |
| std::vector<std::string> transformed; |
| std::ranges::transform( |
| group_itr->group_id_limits_and_counts(), |
| std::back_inserter(transformed), [](const auto& pair) { |
| return omnibox::GroupId_Name(pair.first) + " (" + |
| base::NumberToString( |
| static_cast<int>(pair.second.limit)) + |
| ")"; |
| }); |
| return transformed; |
| }(), |
| ", "); |
| SCOPED_CRASH_KEY_STRING32("ZpsSection", "match-type", match_type); |
| SCOPED_CRASH_KEY_STRING32("ZpsSection", "match-group-id", match_group_id); |
| SCOPED_CRASH_KEY_STRING32("ZpsSection", "match-relevance", |
| match_relevance); |
| SCOPED_CRASH_KEY_STRING32("ZpsSection", "group-description", |
| group_description); |
| base::debug::DumpWithoutCrashing(); |
| #if DCHECK_IS_ON() |
| NOTREACHED() << "Match with type " << match_type << " and group id " |
| << match_group_id << " and relevance " << match_relevance |
| << " is not sorted correctly while being added to Group " |
| << group_description; |
| #endif // DCHECK_IS_ON() |
| } |
| last_group_index = current_group_index; |
| } |
| } |
| |
| ZpsSectionWithLocalHistory::ZpsSectionWithLocalHistory( |
| size_t limit, |
| Groups groups, |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(limit, std::move(groups), group_configs) {} |
| |
| void ZpsSectionWithLocalHistory::InitFromMatches(ACMatches& matches) { |
| // Sort matches in the order of their potential containing groups. E.g., if |
| // `groups_ = {group 1, group 2}, this sorts all matches that can be added to |
| // group 1 before those that can only be added to group 2. |
| std::ranges::stable_sort(matches, std::less<int>{}, [&](const auto& match) { |
| // Don't have to handle `FindGroup()` returning `groups_.end()` since |
| // those matches won't be added to the section anyways. |
| return std::distance(groups_.begin(), FindGroup(match)); |
| }); |
| ZpsSection::InitFromMatches(matches); |
| } |
| |
| // Number of matches that fit in the visible section of the screen. |
| // This number includes the Default match, shown in the top section. |
| // The default match needs to be kept separate, because it should not be |
| // moved when we group suggestions by Search vs URL. |
| // TODO(b/328617350): plumb the value via AutocompleteInput. |
| /* static */ size_t AndroidNonZPSSection::num_visible_matches_{6}; |
| |
| AndroidNonZPSSection::AndroidNonZPSSection( |
| bool show_only_search_suggestions, |
| omnibox::GroupConfigMap& group_configs) |
| : Section( |
| 15, |
| { |
| // Default match Group, not part of the Grouping. |
| Group(1, |
| { |
| {omnibox::GROUP_SEARCH, 1}, |
| {omnibox::GROUP_OTHER_NAVS, 1}, |
| {omnibox::GROUP_MOBILE_RICH_ANSWER, |
| OmniboxFieldTrial::kAnswerActionsShowRichCard.Get() && |
| !OmniboxFieldTrial:: |
| kAnswerActionsShowAboveKeyboard.Get() |
| ? 1 |
| : 0}, |
| }, |
| /*is_zps=*/false), |
| // Top Group / above the keyboard. |
| Group(num_visible_matches_ - 1, |
| { |
| {omnibox::GROUP_SEARCH, 14}, |
| {omnibox::GROUP_OTHER_NAVS, |
| show_only_search_suggestions ? 0 : 14}, |
| }, |
| /*is_zps=*/false), |
| // Dedicated Group for rich answer card just above the fold. |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_RICH_ANSWER, |
| OmniboxFieldTrial::kAnswerActionsShowRichCard.Get() && |
| OmniboxFieldTrial:: |
| kAnswerActionsShowAboveKeyboard.Get() |
| ? 1 |
| : 0}, |
| }, |
| /*is_zps=*/false), |
| // Bottom Group, up to the Section limit. |
| Group(14, |
| { |
| {omnibox::GROUP_SEARCH, 14}, |
| {omnibox::GROUP_OTHER_NAVS, |
| show_only_search_suggestions ? 0 : 14}, |
| }, |
| /*is_zps=*/false), |
| }, |
| group_configs) {} |
| |
| void AndroidNonZPSSection::InitFromMatches(ACMatches& matches) { |
| auto rich_answer_match = std::ranges::find_if( |
| matches, |
| [&](const auto& match) { return match.answer_template.has_value(); }); |
| bool has_rich_answer = rich_answer_match != matches.end(); |
| if (!has_rich_answer) { |
| return; |
| } |
| |
| bool has_url = std::ranges::any_of(matches, [](const auto& match) { |
| return !AutocompleteMatch::IsSearchType(match.type); |
| }); |
| bool hide_if_urls_present = |
| !OmniboxFieldTrial::kAnswerActionsShowIfUrlsPresent.Get(); |
| if (has_url && hide_if_urls_present) { |
| rich_answer_match->suggestion_group_id = omnibox::GROUP_SEARCH; |
| } |
| |
| if (!OmniboxFieldTrial::kAnswerActionsShowRichCard.Get() || |
| !OmniboxFieldTrial::kAnswerActionsShowAboveKeyboard.Get()) { |
| return; |
| } |
| |
| auto& above_keyboard_group = groups_[1]; |
| above_keyboard_group.set_limit(above_keyboard_group.limit() - 1); |
| } |
| |
| AndroidHubZPSSection::AndroidHubZPSSection( |
| omnibox::GroupConfigMap& group_configs) |
| : Section(5, |
| { |
| Group(5, |
| { |
| {omnibox::GROUP_MOBILE_OPEN_TABS, 5}, |
| }), |
| }, |
| group_configs) {} |
| |
| AndroidHubNonZPSSection::AndroidHubNonZPSSection( |
| omnibox::GroupConfigMap& group_configs) |
| : Section( |
| 35, |
| { |
| // Reserve most of the spots for open tabs. |
| Group(20, |
| { |
| {omnibox::GROUP_MOBILE_OPEN_TABS, 20}, |
| }, |
| /*is_zps=*/false), |
| Group(5, |
| { |
| {omnibox::GROUP_MOBILE_BOOKMARKS, 5}, |
| }, |
| /*is_zps=*/false), |
| // LINT.IfChange(HubHistorySectionSlots) |
| Group(5, |
| { |
| {omnibox::GROUP_MOBILE_HISTORY, 5}, |
| }, |
| /*is_zps=*/false), |
| // LINT.ThenChange(//components/omnibox/browser/history_quick_provider.cc:HubHistoryMaxMatches) |
| // Fallback to search suggestions at the bottom of the results. |
| Group(5, |
| { |
| {omnibox::GROUP_SEARCH, 5}, |
| }, |
| /*is_zps=*/false), |
| }, |
| group_configs) {} |
| |
| void AndroidNTPZpsSection::InitFromMatches(ACMatches& matches) { |
| bool mia_suggestions_detected = |
| std::ranges::any_of(matches, [&](const auto& match) { |
| return match.suggestion_group_id.value_or(omnibox::GROUP_INVALID) == |
| omnibox::GROUP_MIA_RECOMMENDATIONS; |
| }); |
| |
| if (omnibox_feature_configs::MiaZPS::Get() |
| .suppress_psuggest_backfill_with_mia && |
| mia_suggestions_detected) { |
| // Hacky and delicate, but follows a pattern found in other sections of this |
| // file. |
| const_cast<Group::LimitAndCount&>( |
| groups_[1].group_id_limits_and_counts().at( |
| omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST)) |
| .limit = 0; |
| } |
| |
| ZpsSectionWithLocalHistory::InitFromMatches(matches); |
| } |
| |
| AndroidNTPZpsSection::AndroidNTPZpsSection( |
| omnibox::GroupConfigMap& group_configs, |
| bool mia_enabled) |
| : ZpsSectionWithLocalHistory( |
| 30, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_CLIPBOARD, 1}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumNtpZpsRecentSearches.Get(), |
| { |
| { |
| omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST_WITH_MIA, |
| mia_enabled |
| ? OmniboxFieldTrial:: |
| kOmniboxNumNtpZpsRecentSearches.Get() |
| : 0, |
| }, |
| { |
| omnibox::GROUP_MIA_RECOMMENDATIONS, |
| mia_enabled |
| ? OmniboxFieldTrial:: |
| kOmniboxNumNtpZpsRecentSearches.Get() |
| : 0, |
| }, |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, |
| OmniboxFieldTrial::kOmniboxNumNtpZpsRecentSearches |
| .Get()}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumNtpZpsTrendingSearches.Get(), |
| { |
| {omnibox::GROUP_TRENDS, |
| OmniboxFieldTrial::kOmniboxNumNtpZpsTrendingSearches |
| .Get()}, |
| }), |
| }, |
| group_configs) {} |
| |
| AndroidSRPZpsSection::AndroidSRPZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection( |
| 15, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX, 1}, |
| }), |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_CLIPBOARD, 1}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumSrpZpsRelatedSearches.Get(), |
| { |
| {omnibox::GROUP_PREVIOUS_SEARCH_RELATED, |
| OmniboxFieldTrial::kOmniboxNumSrpZpsRelatedSearches |
| .Get()}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumSrpZpsRecentSearches.Get(), |
| { |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, |
| OmniboxFieldTrial::kOmniboxNumSrpZpsRecentSearches |
| .Get()}, |
| }), |
| }, |
| group_configs) {} |
| |
| AndroidWebZpsSection::AndroidWebZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSectionWithMVTiles( |
| 15, // Excludes MV tile count (calculated at runtime). |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX, 1}, |
| }), |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_CLIPBOARD, 1}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumWebZpsMostVisitedUrls.Get(), |
| { |
| {omnibox::GROUP_MOBILE_MOST_VISITED, |
| OmniboxFieldTrial::kOmniboxNumWebZpsMostVisitedUrls |
| .Get()}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumWebZpsRelatedSearches.Get(), |
| { |
| {omnibox::GROUP_VISITED_DOC_RELATED, |
| OmniboxFieldTrial::kOmniboxNumWebZpsRelatedSearches |
| .Get()}, |
| }), |
| Group(OmniboxFieldTrial::kOmniboxNumWebZpsRecentSearches.Get(), |
| { |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, |
| OmniboxFieldTrial::kOmniboxNumWebZpsRecentSearches |
| .Get()}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopNTPZpsSection::DesktopNTPZpsSection( |
| omnibox::GroupConfigMap& group_configs, |
| size_t limit, |
| bool mia_enabled) |
| : ZpsSectionWithLocalHistory( |
| limit, |
| { |
| Group( |
| 8, |
| { |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST_WITH_MIA, |
| mia_enabled ? 8 : 0}, |
| {omnibox::GROUP_MIA_RECOMMENDATIONS, mia_enabled ? 8 : 0}, |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 8}, |
| }), |
| Group(8, |
| { |
| {omnibox::GROUP_TRENDS, 8}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopNTPZpsIPHSection::DesktopNTPZpsIPHSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(1, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP, 1}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopZpsUnscopedExtensionSection::DesktopZpsUnscopedExtensionSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(kMaxSuggestionsPerUnscopedExtension * kMaxExtensions, |
| { |
| Group(kMaxSuggestionsPerUnscopedExtension, |
| { |
| {omnibox::GROUP_UNSCOPED_EXTENSION_1, |
| kMaxSuggestionsPerUnscopedExtension}, |
| }), |
| Group(kMaxSuggestionsPerUnscopedExtension, |
| { |
| {omnibox::GROUP_UNSCOPED_EXTENSION_2, |
| kMaxSuggestionsPerUnscopedExtension}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopSecondaryNTPZpsSection::DesktopSecondaryNTPZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection( |
| (omnibox_feature_configs::RealboxContextualAndTrendingSuggestions:: |
| Get() |
| .enabled) |
| ? omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .total_limit |
| : 3, |
| { |
| Group( |
| 3, |
| { |
| {omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS, 3}, |
| }), |
| Group( |
| (omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .enabled) |
| ? omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .contextual_suggestions_limit |
| : 0, |
| { |
| {omnibox::GROUP_PREVIOUS_SEARCH_RELATED, |
| (omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .enabled) |
| ? omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .contextual_suggestions_limit |
| : 0}, |
| }), |
| Group( |
| (omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .enabled) |
| ? omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .trending_suggestions_limit |
| : 0, |
| { |
| {omnibox::GROUP_TRENDS, |
| (omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .enabled) |
| ? omnibox_feature_configs:: |
| RealboxContextualAndTrendingSuggestions::Get() |
| .trending_suggestions_limit |
| : 0}, |
| }), |
| }, |
| group_configs, |
| omnibox::GroupConfig_SideType_SECONDARY) {} |
| |
| DesktopSRPZpsSection::DesktopSRPZpsSection( |
| omnibox::GroupConfigMap& group_configs, |
| size_t max_suggestions, |
| size_t search_limit, |
| size_t url_limit, |
| size_t contextual_action_limit) |
| : ZpsSection( |
| max_suggestions, |
| { |
| Group( |
| search_limit, |
| { |
| {omnibox::GROUP_PREVIOUS_SEARCH_RELATED, search_limit}, |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, search_limit}, |
| }), |
| Group(url_limit, |
| { |
| {omnibox::GROUP_MOST_VISITED, url_limit}, |
| }), |
| #if 1 |
| Group(contextual_action_limit, |
| { |
| {omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION, |
| contextual_action_limit}, |
| }), |
| #endif |
| }, |
| group_configs) { |
| } |
| |
| DesktopWebURLZpsSection::DesktopWebURLZpsSection( |
| omnibox::GroupConfigMap& group_configs, |
| size_t limit) |
| : ZpsSection(limit, |
| { |
| Group(limit, |
| { |
| {omnibox::GROUP_MOST_VISITED, limit}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopWebSearchZpsSection::DesktopWebSearchZpsSection( |
| omnibox::GroupConfigMap& group_configs, |
| size_t limit, |
| size_t contextual_action_limit, |
| size_t contextual_search_limit) |
| : Section(limit, |
| { |
| Group(limit, |
| { |
| {omnibox::GROUP_VISITED_DOC_RELATED, limit}, |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, limit}, |
| }), |
| Group(contextual_action_limit, |
| { |
| {omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION, |
| contextual_action_limit}, |
| }), |
| Group(contextual_search_limit, |
| { |
| {omnibox::GROUP_CONTEXTUAL_SEARCH, |
| contextual_search_limit}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopWebSearchZpsContextualOnlySection:: |
| DesktopWebSearchZpsContextualOnlySection( |
| omnibox::GroupConfigMap& group_configs, |
| size_t contextual_action_limit, |
| size_t contextual_search_limit) |
| : Section(contextual_action_limit + contextual_search_limit, |
| { |
| Group(contextual_action_limit, |
| { |
| {omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION, |
| contextual_action_limit}, |
| }), |
| Group(contextual_search_limit, |
| { |
| {omnibox::GROUP_CONTEXTUAL_SEARCH, |
| contextual_search_limit}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopLensContextualZpsSection::DesktopLensContextualZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(5, |
| { |
| Group(5, |
| { |
| {omnibox::GROUP_CONTEXTUAL_SEARCH, 5}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopLensMultimodalZpsSection::DesktopLensMultimodalZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(8, |
| { |
| Group(8, |
| { |
| {omnibox::GROUP_MULTIMODAL, 8}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopComposeboxZpsSection::DesktopComposeboxZpsSection( |
| omnibox::GroupConfigMap& group_configs, |
| size_t max_suggestions, |
| size_t max_aim_suggestions, |
| size_t max_contextual_suggestions) |
| : ZpsSection( |
| max_suggestions, |
| { |
| Group(max_suggestions, |
| {{omnibox::GROUP_MIA_RECOMMENDATIONS, max_aim_suggestions}, |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST_WITH_MIA, |
| max_aim_suggestions}, |
| {omnibox::GROUP_CONTEXTUAL_SEARCH, |
| max_contextual_suggestions}}), |
| }, |
| group_configs) {} |
| |
| ToolbeltSection::ToolbeltSection(omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(1, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_SEARCH_TOOLBELT, 1}, |
| }), |
| }, |
| group_configs) {} |
| |
| DesktopNonZpsSection::DesktopNonZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : Section(10, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_STARTER_PACK, 1}, |
| {omnibox::GROUP_SEARCH, 1}, |
| {omnibox::GROUP_OTHER_NAVS, 1}, |
| }, |
| /*is_zps=*/false, |
| /*is_default=*/true), |
| Group(9, |
| { |
| {omnibox::GROUP_STARTER_PACK, 9}, |
| }, |
| /*is_zps=*/false), |
| Group(9, |
| { |
| {omnibox::GROUP_SEARCH, 9}, |
| {omnibox::GROUP_HISTORY_CLUSTER, 1}, |
| }, |
| /*is_zps=*/false), |
| Group(7, |
| { |
| {omnibox::GROUP_OTHER_NAVS, 7}, |
| }, |
| /*is_zps=*/false), |
| }, |
| group_configs) {} |
| |
| void DesktopNonZpsSection::InitFromMatches(ACMatches& matches) { |
| auto& default_group = groups_[0]; |
| auto& search_group = groups_[2]; |
| auto& nav_group = groups_[3]; |
| |
| // Determine if `matches` contains any searches. |
| bool has_search = std::ranges::any_of( |
| matches, [&](const auto& match) { return search_group.CanAdd(match); }); |
| |
| // Determine if the default match will be a search. |
| auto default_match = std::ranges::find_if( |
| matches, [&](const auto& match) { return default_group.CanAdd(match); }); |
| bool default_is_search = |
| default_match != matches.end() && search_group.CanAdd(*default_match); |
| |
| // Find the 1st nav's index. |
| size_t first_nav_index = std::distance( |
| matches.begin(), std::ranges::find_if(matches, [&](const auto& match) { |
| return nav_group.CanAdd(match); |
| })); |
| |
| // Show at most 8 suggestions if doing so includes navs; otherwise show 9 or |
| // 10, if doing so doesn't include navs. |
| limit_ = std::clamp<size_t>(first_nav_index, 8, 10); |
| |
| // Show at least 1 search, either in the default group or the search group. |
| if (has_search && !default_is_search) { |
| DCHECK_GE(limit_, 2U); |
| nav_group.set_limit(limit_ - 2); |
| } |
| } |
| |
| ZpsSectionWithMVTiles::ZpsSectionWithMVTiles( |
| size_t limit, |
| Groups groups, |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(limit, std::move(groups), group_configs) {} |
| |
| void ZpsSectionWithMVTiles::InitFromMatches(ACMatches& matches) { |
| size_t tile_count = std::count_if( |
| matches.begin(), matches.end(), [](const AutocompleteMatch& m) { |
| return m.suggestion_group_id.value_or(omnibox::GROUP_INVALID) == |
| omnibox::GROUP_MOBILE_MOST_VISITED; |
| }); |
| const size_t max_most_visited_tiles = |
| is_android ? OmniboxFieldTrial::kOmniboxNumWebZpsMostVisitedUrls.Get() |
| : kMobileMostVisitedTilesLimit; |
| // In the event we find more MV tiles than we can accommodate, trim the limit. |
| limit_ += std::min(tile_count, max_most_visited_tiles); |
| // Note that the horizontal render group takes a single slot in vertical list: |
| // we therefore count it as an individual item, meaning this list: |
| // [ URL_WHAT_YOU_TYPED ] |
| // [ [MV] [MV] [MV] [MV] ] |
| // [ SEARCH_SUGGEST ] |
| // has 3 elements built from 6 AutocompleteMatch objects. |
| limit_ -= (tile_count ? 1 : 0); |
| ZpsSection::InitFromMatches(matches); |
| } |
| |
| void IOSNTPZpsSection::InitFromMatches(ACMatches& matches) { |
| bool mia_suggestions_detected = |
| std::ranges::any_of(matches, [&](const auto& match) { |
| return match.suggestion_group_id.value_or(omnibox::GROUP_INVALID) == |
| omnibox::GROUP_MIA_RECOMMENDATIONS; |
| }); |
| |
| if (omnibox_feature_configs::MiaZPS::Get() |
| .suppress_psuggest_backfill_with_mia && |
| mia_suggestions_detected) { |
| // Hacky and delicate, but follows a pattern found in other sections of this |
| // file. |
| const_cast<Group::LimitAndCount&>( |
| groups_[1].group_id_limits_and_counts().at( |
| omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST)) |
| .limit = 0; |
| } |
| |
| ZpsSectionWithLocalHistory::InitFromMatches(matches); |
| } |
| |
| IOSNTPZpsSection::IOSNTPZpsSection(omnibox::GroupConfigMap& group_configs, |
| bool mia_enabled) |
| : ZpsSectionWithLocalHistory( |
| 26, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_CLIPBOARD, 1}, |
| }), |
| Group(20, |
| { |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST_WITH_MIA, |
| mia_enabled ? 20 : 0}, |
| {omnibox::GROUP_MIA_RECOMMENDATIONS, |
| mia_enabled ? 20 : 0}, |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 20}, |
| }), |
| Group(5, |
| { |
| {omnibox::GROUP_TRENDS, 5}, |
| }), |
| }, |
| group_configs) {} |
| |
| IOSSRPZpsSection::IOSSRPZpsSection(omnibox::GroupConfigMap& group_configs) |
| : ZpsSectionWithMVTiles( |
| 20, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX, 1}, |
| }), |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_CLIPBOARD, 1}, |
| }), |
| Group(kMobileMostVisitedTilesLimit, |
| { |
| {omnibox::GROUP_MOBILE_MOST_VISITED, |
| kMobileMostVisitedTilesLimit}, |
| }), |
| Group(8, |
| { |
| {omnibox::GROUP_PREVIOUS_SEARCH_RELATED, 8}, |
| }), |
| Group(20, |
| { |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 20}, |
| }), |
| }, |
| group_configs) {} |
| |
| IOSWebZpsSection::IOSWebZpsSection(omnibox::GroupConfigMap& group_configs) |
| : ZpsSectionWithMVTiles( |
| 20, |
| { |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX, 1}, |
| }), |
| Group(1, |
| { |
| {omnibox::GROUP_MOBILE_CLIPBOARD, 1}, |
| }), |
| Group(kMobileMostVisitedTilesLimit, |
| { |
| {omnibox::GROUP_MOBILE_MOST_VISITED, |
| kMobileMostVisitedTilesLimit}, |
| }), |
| Group(8, |
| { |
| {omnibox::GROUP_VISITED_DOC_RELATED, 8}, |
| }), |
| Group(20, |
| { |
| {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 20}, |
| }), |
| }, |
| group_configs) {} |
| |
| IOSLensMultimodalZpsSection::IOSLensMultimodalZpsSection( |
| omnibox::GroupConfigMap& group_configs) |
| : ZpsSection(10, |
| { |
| Group(10, |
| { |
| {omnibox::GROUP_MULTIMODAL, 10}, |
| }), |
| }, |
| group_configs) {} |