blob: dd6e14724bdb807d4c688f0971a7a5cc0f88ca32 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_MATCH_H_
#define COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_MATCH_H_
#include <stddef.h>
#include <array>
#include <map>
#include <memory>
#include <optional>
#include <ranges>
#include <string>
#include <utility>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_offset_string_conversions.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "components/omnibox/browser/actions/omnibox_action_concepts.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/buildflags.h"
#include "components/omnibox/browser/suggestion_answer.h"
#include "components/saved_tab_groups/public/types.h"
#include "components/search_engines/template_url.h"
#include "components/url_formatter/url_formatter.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "third_party/metrics_proto/omnibox_scoring_signals.pb.h"
#include "third_party/omnibox_proto/answer_type.pb.h"
#include "third_party/omnibox_proto/groups.pb.h"
#include "third_party/omnibox_proto/navigational_intent.pb.h"
#include "third_party/omnibox_proto/rich_answer_template.pb.h"
#include "third_party/omnibox_proto/suggest_template_info.pb.h"
#include "third_party/omnibox_proto/types.pb.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
#include "ui/base/page_transition_types.h"
#include "ui/gfx/range/range.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#endif
class AutocompleteProvider;
class OmniboxAction;
class TemplateURL;
class TemplateURLService;
namespace base {
class Time;
} // namespace base
namespace gfx {
struct VectorIcon;
} // namespace gfx
const char kACMatchPropertySuggestionText[] = "match suggestion text";
const char kACMatchPropertyContentsPrefix[] = "match contents prefix";
const char kACMatchPropertyContentsStartIndex[] = "match contents start index";
// A match attribute when a default match's score has been boosted with a higher
// scoring non-default match.
const char kACMatchPropertyScoreBoostedFrom[] = "score_boosted_from";
// Util structs/enums ----------------------------------------------------------
// `RichAutocompletionParams` is a cache for the params used by
// `TryRichAutocompletion()`. `TryRichAutocompletion()` is called about 80 times
// per keystroke; fetching all 16 params each time causes measurable timing
// regressions. Using `static const` local variables instead wouldn't be
// testable.
struct RichAutocompletionParams {
RichAutocompletionParams();
static RichAutocompletionParams& GetParams();
static void ClearParamsForTesting();
bool enabled;
size_t autocomplete_titles_min_char;
size_t autocomplete_shortcut_text_min_char;
};
struct SessionData {
SessionData();
SessionData(const SessionData& session_data);
~SessionData();
SessionData& operator=(const SessionData& match);
// Whether zero-prefix suggestions could have been shown in the session.
bool zero_prefix_enabled = false;
// The number of zero-prefix suggestions shown in the session.
size_t num_zero_prefix_suggestions_shown = 0u;
// Whether at least one zero-prefix suggestion was shown in the
// session.
bool zero_prefix_suggestions_shown_in_session = false;
// Whether at least one typed suggestion was shown in the session.
bool typed_suggestions_shown_in_session = false;
// List of GWS event ID hashes accumulated during the course of the session.
std::vector<int64_t> gws_event_id_hashes;
// Whether at least one zero-prefix Search/URL suggestion was
// shown in the session. This is used in order to ensure that the relevant
// client-side metrics logging code emits the proper values.
bool zero_prefix_search_suggestions_shown_in_session = false;
bool zero_prefix_url_suggestions_shown_in_session = false;
// Whether at least one typed Search/URL suggestion was shown in
// the session. This is used in order to ensure that the relevant client-side
// metrics logging code emits the proper values.
bool typed_search_suggestions_shown_in_session = false;
bool typed_url_suggestions_shown_in_session = false;
// Whether at least one contextual search suggestion was shown in the
// session.
bool contextual_search_suggestions_shown_in_session = false;
// Whether the "Ask Google Lens about this page" action was shown at least
// once in the session.
bool lens_action_shown_in_session = false;
};
enum class IphType {
kNone,
// '@gemini' promo; shown in zero state.
kGemini,
// Enterprise search aggregator promo; shown in zero state.
kEnterpriseSearchAggregator,
// Featured enterprise site search promo; shown in zero state.
kFeaturedEnterpriseSiteSearch,
// Embeddings' setting promo when embeddings are disabled; shown in '@history'
// scope.
kHistoryEmbeddingsSettingsPromo,
// Disclaimer when embeddings are enabled; shown in '@history' scope.
kHistoryEmbeddingsDisclaimer,
// '@history' promo when embeddings are disabled; shown in zero state.
kHistoryScopePromo,
// '@history' promo when embeddings are enabled; shown in zero state.
kHistoryEmbeddingsScopePromo,
};
enum class FeedbackType {
kNone,
kThumbsUp,
kThumbsDown,
};
// Used with `stripped_destination_url` to dedupe matches. Matches with the same
// URL but different types won't be deduped. This'll allow showing e.g. both a
// "1+1" normal query and a "1+1 = 2" calculator suggestion simultaneously.
enum class AutocompleteMatchDedupeType {
kNormal,
kCalculator, // E.g. "1+1 = 2" matches.
kVerbatimProvider, // Matches that come from the verbatim provider, which
// does not include the verbatim SWYT match.
kHistoryEmbeddingAnswer, // Matches with type `HISTORY_EMBEDDINGS_ANSWER`.
kAiMode, // Matches that activate the DSE's AI Mode. AIM suggestions' URLs
// are discerned by a query param `udm=50`. But deduping doesn't
// consider extra query params; `google.com/?q=query&udm=50` and
// `google.com/?q=query` would usually be deduped. `kAiMode` allows
// matches with `udm=50` in their suggest template to not be deduped
// with matches without it. But this does not apply to `udm=50` in
// the actual match URL; nor to udm values other than 50.
};
// AutocompleteMatch ----------------------------------------------------------
// A single result line with classified spans. The autocomplete popup displays
// the 'contents' and the 'description' (the description is optional) in the
// autocomplete dropdown, and fills in 'fill_into_edit' into the textbox when
// that line is selected. fill_into_edit may be the same as 'description' for
// things like URLs, but may be different for searches or other providers. For
// example, a search result may say "Search for asdf" as the description, but
// "asdf" should appear in the box.
struct AutocompleteMatch {
using ScoringSignals = ::metrics::OmniboxScoringSignals;
// Autocomplete matches contain strings that are classified according to a
// separate vector of styles. This vector associates flags with particular
// string segments, and must be in sorted order. All text must be associated
// with some kind of classification. Even if a match has no distinct
// segments, its vector should contain an entry at offset 0 with no flags.
//
// Example: The user typed "goog"
// http://www.google.com/ Google
// ^ ^ ^ ^ ^
// 0, | 15, | 4,
// 11,match 0,match
//
// This structure holds the classification information for each span.
struct ACMatchClassification {
// The values in here are not mutually exclusive -- use them like a
// bit field. This also means we use "int" instead of this enum type when
// passing the values around, so the compiler doesn't complain.
//
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.omnibox
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: MatchClassificationStyle
// clang-format off
enum Style {
NONE = 0,
URL = 1 << 0, // A URL
MATCH = 1 << 1, // A match for the user's search term
DIM = 1 << 2, // "Helper text"
TOOLBELT = 1 << 3, // Toolbelt label
};
// clang-format on
ACMatchClassification() = default;
ACMatchClassification(size_t offset, int style)
: offset(offset), style(style) {}
friend bool operator==(const ACMatchClassification&,
const ACMatchClassification&) = default;
// Offset within the string that this classification starts
size_t offset = 0;
// Contains a bitmask of flags defined in enum Style.
int style = 0;
};
// SuggestTiles are used specifically with TILE_NAVSUGGEST matches.
// This structure should describe only the specific details for individual
// tiles; all other properties are considered as shared and should be
// extracted from the encompassing AutocompleteMatch object.
struct SuggestTile {
GURL url{};
std::u16string title{};
bool is_search{};
};
typedef std::vector<ACMatchClassification> ACMatchClassifications;
// Type used by providers to attach additional, optional information to
// an AutocompleteMatch.
typedef std::map<std::string, std::string> AdditionalInfo;
// The type of this match.
typedef AutocompleteMatchType::Type Type;
// Null-terminated array of characters that are not valid within |contents|
// and |description| strings.
static const char16_t kInvalidChars[];
// Document subtype, for AutocompleteMatchType::DOCUMENT.
// Update kDocumentTypeStrings when updating DocumentType.
enum class DocumentType {
NONE = 0,
DRIVE_DOCS,
DRIVE_FORMS,
DRIVE_SHEETS,
DRIVE_SLIDES,
DRIVE_IMAGE,
DRIVE_PDF,
DRIVE_VIDEO,
DRIVE_FOLDER,
DRIVE_OTHER,
DOCUMENT_TYPE_SIZE
};
// Enterprise search aggregator subtype, for suggestions from the
// EnterpriseSearchAggregatorProvider provider.
enum class EnterpriseSearchAggregatorType {
NONE = 0,
QUERY,
PEOPLE,
CONTENT,
};
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class RichAutocompletionType {
kNone = 0,
// kUrlNonPrefix = 1, // deprecated
kTitlePrefix = 2,
// kTitleNonPrefix = 3, // deprecated
kShortcutTextPrefix = 4,
kMaxValue = kShortcutTextPrefix,
};
static constexpr auto kDocumentTypeStrings = std::to_array<const char*>(
{"none", "drive_docs", "drive_forms", "drive_sheets", "drive_slides",
"drive_image", "drive_pdf", "drive_video", "drive_folder",
"drive_other"});
static_assert(kDocumentTypeStrings.size() ==
static_cast<int>(DocumentType::DOCUMENT_TYPE_SIZE),
"Sizes of AutocompleteMatch::kDocumentTypeStrings and "
"AutocompleteMatch::DocumentType don't match.");
// Return a string version of the core type values. Only used for
// `RecordAdditionalInfo()`.
static const char* DocumentTypeString(DocumentType type);
// Use this function to convert integers to DocumentType enum values.
// If you're sure it will be valid, you can call CHECK on the return value.
// Returns true if |value| was successfully converted to a valid enum value.
// The valid enum value will be written into |result|.
static bool DocumentTypeFromInteger(int value, DocumentType* result);
AutocompleteMatch();
AutocompleteMatch(AutocompleteProvider* provider,
int relevance,
bool deletable,
Type type);
AutocompleteMatch(const AutocompleteMatch& match);
AutocompleteMatch(AutocompleteMatch&& match) noexcept;
~AutocompleteMatch();
AutocompleteMatch& operator=(const AutocompleteMatch& match);
AutocompleteMatch& operator=(AutocompleteMatch&& match) noexcept;
#if BUILDFLAG(IS_ANDROID)
// Returns a corresponding Java object, creating it if necessary.
// NOTE: Android specific methods are defined in autocomplete_match_android.cc
base::android::ScopedJavaLocalRef<jobject> GetOrCreateJavaObject(
JNIEnv* env) const;
// Update the bond with- or drop the Java AutocompleteMatch instance.
// This should be called whenever the native AutocompleteMatch object is
// updated for an existing Java object.
void UpdateJavaObjectNativeRef();
// Notify the Java object that its native counterpart is about to be
// destroyed.
void DestroyJavaObject();
// Returns a corresponding Java Class object.
static jclass GetClazz(JNIEnv* env);
// Update the clipboard match with the current clipboard data.
void UpdateWithClipboardContent(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_callback);
// Called when the match is updated with the clipboard content.
void OnClipboardSuggestionContentUpdated(
const base::android::JavaRef<jobject>& j_callback);
// Update the Java object with clipboard content.
void UpdateClipboardContent(JNIEnv* env);
// Update the Java object with new destination URL.
void UpdateJavaNavigationDetails();
// Update the Java object with new Answer-in-Suggest.
void UpdateJavaAnswer();
// Update the Java object description.
void UpdateJavaDescription();
// Update the pointer to corresponding Java tab object.
void UpdateMatchingJavaTab(const JavaObjectWeakGlobalRef& tab);
// Get the matching Java Tab object.
JavaObjectWeakGlobalRef GetMatchingJavaTab() const;
#endif
#if (!BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !BUILDFLAG(IS_IOS)
// Converts omnibox::AnswerType to an answer vector icon.
static const gfx::VectorIcon& AnswerTypeToAnswerIcon(
omnibox::AnswerType type);
// Gets the vector icon identifier for the icon to be shown for this match. If
// `is_bookmark` is true, returns a bookmark icon rather than what the type
// would normally determine. Note that in addition to `type`, the icon chosen
// may depend on match contents (e.g. Drive `document_type` or `actions`).
// The reason `is_bookmark` is passed as a parameter and is not baked into the
// AutocompleteMatch is likely that 1) this info is not used elsewhere in the
// Autocomplete machinery except before displaying the match and 2) obtaining
// this info is trivially done by calling BookmarkModel::IsBookmarked().
// `turl` is used to identify the proper vector icon associated with a given
// starter pack suggestion (e.g. @tabs, @history, @bookmarks, etc.).
const gfx::VectorIcon& GetVectorIcon(bool is_bookmark,
const TemplateURL* turl = nullptr) const;
#endif
// Comparison function for determining whether the first match is better than
// the second.
static bool MoreRelevant(const AutocompleteMatch& match1,
const AutocompleteMatch& match2);
// Comparison functions for determining whether the first match is preferred
// over the second when choosing between candidate duplicates.
static bool BetterDuplicate(const AutocompleteMatch& match1,
const AutocompleteMatch& match2);
static bool BetterDuplicateByIterator(
const std::vector<AutocompleteMatch>::const_iterator it1,
const std::vector<AutocompleteMatch>::const_iterator it2);
// Returns a new vector of classifications containing the merged contents of
// |classifications1| and |classifications2|.
static ACMatchClassifications MergeClassifications(
const ACMatchClassifications& classifications1,
const ACMatchClassifications& classifications2);
// Converts classifications to and from a serialized string representation
// (using comma-separated integers to sequentially list positions and styles).
static std::string ClassificationsToString(
const ACMatchClassifications& classifications);
static ACMatchClassifications ClassificationsFromString(
const std::string& serialized_classifications);
// Adds a classification to the end of |classifications| iff its style is
// different from the last existing classification. |offset| must be larger
// than the offset of the last classification in |classifications|.
static void AddLastClassificationIfNecessary(
ACMatchClassifications* classifications,
size_t offset,
int style);
// Removes invalid characters from |text|. Should be called on strings coming
// from external sources (such as extensions) before assigning to |contents|
// or |description|.
static std::u16string SanitizeString(const std::u16string& text);
// Convenience function to check if `type` is featured Enterprise search.
static bool IsFeaturedEnterpriseSearchType(Type type);
// Convenience function to check if `type` is featured search type, e.g.
// starter pack and featured site search engines created by policy.
static bool IsFeaturedSearchType(Type type);
// Convenience function to check if `type` is preconnectable.
// Preconnecting allows connecting to an origin before requesting any
// resources from that origin, effectively "warming up" the connection. When a
// resource from that origin is requested, we can immediately use the
// established connection, saving valuable round-trips. This differs from
// preloading and prefetching in that it does not actually fetch any resources
// from the origin, it just establishes the connection to the origin earlier.
static bool IsPreconnectableType(Type type);
// Convenience function to check if |type| is a search (as opposed to a URL or
// an extension).
static bool IsSearchType(Type type);
// Convenience function to check if |type| is a special search suggest type -
// like entity, personalized, profile or postfix.
static bool IsSpecializedSearchType(Type type);
// Convenience function to check if |type| is a search history type -
// usually this surfaces a clock icon to the user.
static bool IsSearchHistoryType(Type type);
// Returns whether this match is a starter pack suggestion provided by the
// built-in provider. This is the suggestion that the starter pack keyword
// mode chips attach to.
static bool IsStarterPackType(Type type);
// Returns whether this match is a Clipboard suggestion.
static bool IsClipboardType(Type type);
// Convenience function to check if |type| is one of the suggest types we
// need to skip for search vs url partitions - url, text or image in the
// clipboard or query tile.
static bool ShouldBeSkippedForGroupBySearchVsUrl(Type type);
// Return a group ID based on type. Should only be used as a fill in for
// matches that don't already have a group ID set by providers.
static omnibox::GroupId GetDefaultGroupId(Type type);
// A static version GetTemplateURL() that takes the match's keyword and
// match's hostname as parameters. In short, returns the TemplateURL
// associated with |keyword| if it exists; otherwise returns the TemplateURL
// associated with |host| if it exists.
static TemplateURL* GetTemplateURLWithKeyword(
TemplateURLService* template_url_service,
const std::u16string& keyword,
const std::string& host);
static const TemplateURL* GetTemplateURLWithKeyword(
const TemplateURLService* template_url_service,
const std::u16string& keyword,
const std::string& host);
// Returns `url` altered by stripping off "www.", converting https protocol
// to http, and stripping query params other than the search terms. If
// `keep_search_intent_params` is true, also keep search intent params which
// disambiguate the search terms and determine the fulfillment.
// These conversions are meant to allow URL comparisons to find likely
// duplicates; and these URLs are not used as actual destination URLs.
// In most use cases `keep_search_intent_params` need not be true, e.g., when
// computing `stripped_destination_url` for matches. Otherwise, keeping the
// search intent params would create an unnecessary level of granularity which
// prevents proper deduping of matches from various local or remote providers.
// If a provider wishes to prevent its matches (or a subset of them) from
// being deduped with other matches with the same search terms, it must
// precompute `stripped_destination_url` while maintaining the search intent
// params. A notable example is when multiple entity suggestions with the same
// search terms are offered by SearchProvider or ZeroSuggestProvider. In that
// case, the entity matches (with the exception of the first one) keep their
// search intent params in `stripped_destination_url` to avoid being deduped.
// - `input` is used to decide if the scheme is allowed to be altered during
// stripping. If this URL, minus the scheme and separator, starts with any
// the terms in input.terms_prefixed_by_http_or_https(), we avoid converting
// an HTTPS scheme to HTTP. This means URLs that differ only by these
// schemes won't be marked as dupes, since the distinction seems to matter
// to the user.
// - If `template_url_service` is not NULL, it is used to get a template URL
// corresponding to this match, which is used to strip off query params
// other than the search terms (and optionally search intent params) that
// would otherwise prevent proper comparison/deduping.
// - If the match's keyword is known, it can be provided in `keyword`.
// Otherwise, it can be left empty and the template URL (if any) is
// determined from the destination's hostname.
static GURL GURLToStrippedGURL(const GURL& url,
const AutocompleteInput& input,
const TemplateURLService* template_url_service,
const std::u16string& keyword,
const bool keep_search_intent_params);
// Sets the |match_in_scheme| and |match_in_subdomain| flags based on the
// provided |url| and list of substring |match_positions|. |match_positions|
// is the [begin, end) positions of a match within the unstripped URL spec.
using MatchPosition = std::pair<size_t, size_t>;
static void GetMatchComponents(
const GURL& url,
const std::vector<MatchPosition>& match_positions,
bool* match_in_scheme,
bool* match_in_subdomain);
// Gets the formatting flags used for display of suggestions. This method
// encapsulates the return of experimental flags too, so any URLs displayed
// as an Omnibox suggestion should use this method.
//
// This function returns flags that may destructively format the URL, and
// therefore should never be used for the |fill_into_edit| field.
//
// |preserve_scheme| and |preserve_subdomain| indicate that these URL
// components are important (part of the match), and should not be trimmed.
static url_formatter::FormatUrlTypes GetFormatTypes(bool preserve_scheme,
bool preserve_subdomain);
// Logs the search engine used to navigate to a search page or auto complete
// suggestion. For direct URL navigations, nothing is logged.
static void LogSearchEngineUsed(const AutocompleteMatch& match,
TemplateURLService* template_url_service);
// Computes the stripped destination URL (via GURLToStrippedGURL()) and
// stores the result in |stripped_destination_url|. |input| is used for the
// same purpose as in GURLToStrippedGURL().
void ComputeStrippedDestinationURL(const AutocompleteInput& input,
TemplateURLService* template_url_service);
// Returns whether `destination_url` looks like a doc URL. If so, will also
// set `stripped_destination_url` to avoid repeating the computation later.
bool IsDocumentSuggestion();
// Checks if this match is a trend suggestion based on the match subtypes.
bool IsTrendSuggestion() const;
// Checks if this match is an informational IPH suggestion based on the match
// and provider type.
bool IsIphSuggestion() const;
// Checks if this match has an attached action with the given `action_id`.
bool HasAction(OmniboxActionId action_id) const;
// Checks if this match is a contextual search suggestion to be fulfilled
// by lens in the side panel.
bool IsContextualSearchSuggestion() const;
// Checks if this match is a specialized toolbelt match with actions on
// a button row.
bool IsToolbelt() const;
// Checks if this match is a AI mode suggestion.
bool IsSearchAimSuggestion() const;
// Checks if this match has a Lens search action.
bool HasLensSearchAction() const;
// Returns true if this match may attach one or more `actions`.
// This method is used to keep actions off of matches with types that don't
// mix well with Pedals or other actions (e.g. entities).
bool IsActionCompatible() const;
// Returns true if this match has a keyword that puts the omnibox instantly
// into keyword mode when the match is focused via keyboard, instead of
// the usual waiting for activation of a visible keyword button.
bool HasInstantKeyword(TemplateURLService* template_url_service) const;
// Gets data relevant to whether there should be any special keyword-related
// UI shown for this match. If this match represents a selected keyword, i.e.
// the UI should be "in keyword mode", `keyword_out` will be set to the
// keyword and `is_keyword_hint` will be set to false. If this match has a
// non-null `associated_keyword`, i.e. we should show a "Press [tab] to search
// ___" hint and allow the user to toggle into keyword mode, `keyword_out`
// will be set to the associated keyword and `is_keyword_hint` will be set to
// true. Note that only one of these states can be in effect at once. In all
// other cases, `keyword_out` will be cleared, even when our member variable
// `keyword` is non-empty -- such as with non-substituting keywords or matches
// that represent searches using the default search engine. See also
// `GetSubstitutingExplicitlyInvokedKeyword()`. `keyword_placeholder_out` will
// be set to any placeholder text the keyword wants to display. Set for both
// hint and non-hint keyword modes. `is_history_embeddings_enabled` will
// affect the placeholder text for the @history keyword.
void GetKeywordUIState(TemplateURLService* template_url_service,
bool is_history_embeddings_enabled,
std::u16string* keyword_out,
std::u16string* keyword_placeholder_out,
bool* is_keyword_hint) const;
// Returns |keyword|, but only if it represents a substituting keyword that
// the user has explicitly invoked. If for example this match represents a
// search with the default search engine (and the user didn't explicitly
// invoke its keyword), this returns the empty string. The result is that
// this function returns a non-empty string in the same cases as when the UI
// should show up as being "in keyword mode".
std::u16string GetSubstitutingExplicitlyInvokedKeyword(
TemplateURLService* template_url_service) const;
// Returns the placeholder text to display for the given starter pack keyword
// TemplateURL, returned for both hint and non-hint keyword modes.
// The `template_url` may be nullptr and this method often defaults to
// returning the empty string.
static std::u16string GetKeywordPlaceholder(
const TemplateURL* template_url,
bool is_history_embeddings_enabled);
// Returns the TemplateURL associated with this match. This may be NULL if
// the match has no keyword OR if the keyword no longer corresponds to a valid
// TemplateURL. See comments on |keyword| below.
// If |allow_fallback_to_destination_host| is true and the keyword does
// not map to a valid TemplateURL, we'll then check for a TemplateURL that
// corresponds to the destination_url's hostname.
TemplateURL* GetTemplateURL(TemplateURLService* template_url_service,
bool allow_fallback_to_destination_host) const;
// Gets the URL for the match image (whether it be an answer or entity). If
// there isn't an image URL, returns an empty GURL (test with is_empty()).
GURL ImageUrl() const;
// Adds optional information to the |additional_info| dictionary.
void RecordAdditionalInfo(const std::string& property,
const std::string& value);
void RecordAdditionalInfo(const std::string& property,
const std::u16string& value);
void RecordAdditionalInfo(const std::string& property, int value);
void RecordAdditionalInfo(const std::string& property, double value);
void RecordAdditionalInfo(const std::string& property, base::Time value);
// Returns the value recorded for |property| in the |additional_info|
// dictionary. Returns the empty string if no such value exists. This is for
// debugging in chrome://omnibox only. It should only be called by
// `OmniboxPageHandler` and tests. For match info that's used for
// non-debugging, use class fields. Unfortunately, There are existing
// non-debug callsites; those should be cleaned up, not added to.
std::string GetAdditionalInfoForDebugging(const std::string& property) const;
// Returns the provider type selected from this match, which is by default
// taken from the match `provider` type but may be a (pseudo-)provider
// associated with one of the match's action types if one of the match's
// actions are chosen with `action_index`.
metrics::OmniboxEventProto::ProviderType GetOmniboxEventProviderType(
int action_index = -1) const;
// Returns the result type selected from this match, which is by default
// equivalent to the match type but may be one of the match's action
// types if one of the match's actions are chosen with `action_index`.
metrics::OmniboxEventProto::Suggestion::ResultType GetOmniboxEventResultType(
int action_index = -1) const;
// Returns whether this match is a "verbatim" match: a URL navigation directly
// to the user's input, a search for the user's input with the default search
// engine, or a "keyword mode" search for the query portion of the user's
// input. Note that rare or unusual types that could be considered verbatim,
// such as keyword engine matches or extension-provided matches, aren't
// detected by this IsVerbatimType, as the user will not be able to infer
// what will happen when they press enter in those cases if the match is not
// shown.
bool IsVerbatimType() const;
// Returns whether this match is a "verbatim URL" suggestion.
bool IsVerbatimUrlSuggestion() const;
// Returns whether this match is a search suggestion provided by search
// provider.
bool IsSearchProviderSearchSuggestion() const;
// Returns whether this match is a search suggestion provided by on device
// providers.
bool IsOnDeviceSearchSuggestion() const;
// Returns the top-level sorting order of the suggestion.
// Suggestions should be sorted by this value first, and by Relevance score
// next.
int GetSortingOrder() const;
// Whether this autocomplete match supports custom descriptions.
bool HasCustomDescription() const;
// Returns true if the match is eligible for ML scoring signal logging.
bool IsMlSignalLoggingEligible() const;
// Returns true if the match is eligible to be re-scored by ML scoring.
bool IsMlScoringEligible() const;
// Filter OmniboxActions based on the supplied qualifiers.
// The order of the supplied qualifiers determines the preference.
void FilterOmniboxActions(
const std::vector<OmniboxActionId>& allowed_action_ids);
// Rearranges and truncates ActionsInSuggest objects to match the desired
// order and presence of actions.
// Unlike FilterOmniboxActions(), this method specifically targets
// ActionsInSuggest.
void FilterAndSortActionsInSuggest();
// Remove all Answer Actions.
void RemoveAnswerActions();
// Returns whether the autocompletion is trivial enough that we consider it
// an autocompletion for which the omnibox autocompletion code did not add
// any value.
bool IsTrivialAutocompletion() const;
// Returns whether this match or any duplicate of this match can be deleted.
// This is used to decide whether we should call DeleteMatch().
bool SupportsDeletion() const;
// Returns a copy of this match with the contents and description fields, and
// their associated classifications, possibly swapped. We swap these if this
// is a match for which we should emphasize the title (stored in the
// description field) over the URL (in the contents field).
//
// We specifically return a copy to prevent the UI code from accidentally
// mucking with the matches stored in the model, lest other omnibox systems
// get confused about which is which. See the code that sets
// |swap_contents_and_description| for conditions they are swapped.
//
// TODO(crbug.com/40179316): Clean up the handling of contents and description
// so that this copy is no longer required.
AutocompleteMatch GetMatchWithContentsAndDescriptionPossiblySwapped() const;
// Determines whether this match is allowed to be the default match by
// comparing |input.text| and |inline_autocompletion|. Therefore,
// |inline_autocompletion| should be set prior to invoking this method. Also
// considers trailing whitespace in the input, so the input should not be
// fixed up. May trim trailing whitespaces from |inline_autocompletion|.
//
// Input "x" will allow default matches "x", "xy", and "x y".
// Input "x " will allow default matches "x" and "x y".
// Input "x " will allow default match "x".
// Input "x y" will allow default match "x y".
// Input "x" with prevent_inline_autocomplete will allow default match "x".
void SetAllowedToBeDefault(const AutocompleteInput& input);
// Estimates dynamic memory usage.
// See base/trace_event/memory_usage_estimator.h for more info.
size_t EstimateMemoryUsage() const;
// Upgrades this match by absorbing the best properties from
// |duplicate_match|. For instance: if |duplicate_match| has a higher
// relevance score, this match's own relevance score will be upgraded.
void UpgradeMatchWithPropertiesFrom(AutocompleteMatch& duplicate_match);
// Merges scoring signals from the other match for ML model scoring and
// training .
void MergeScoringSignals(const AutocompleteMatch& other);
// Tries, in order, to:
// - Prefix autocomplete |primary_text|
// - Prefix autocomplete |secondary_text|
// - Non-prefix autocomplete |primary_text|
// - Non-prefix autocomplete |secondary_text|
// - Split autocomplete |primary_text|
// - Split autocomplete |secondary_text|
// Returns false if none of the autocompletions were appropriate (or the
// features were disabled).
bool TryRichAutocompletion(const AutocompleteInput& input,
const std::u16string& primary_text,
const std::u16string& secondary_text,
const std::u16string& shortcut_text = u"");
// Serialise this object into a trace.
void WriteIntoTrace(perfetto::TracedValue context) const;
// Returns the action at `index`, or nullptr if `index` is out of bounds.
OmniboxAction* GetActionAt(size_t index) const;
// Returns if `predicate` returns true for the match or one of its duplicates.
template <typename UnaryPredicate>
bool MatchOrDuplicateMeets(UnaryPredicate predicate) const {
return predicate(*this) ||
std::ranges::any_of(duplicate_matches, std::move(predicate));
}
// Finds first action where `predicate` returns true. This is a special use
// utility method for situations where actions with certain constraints
// need to be selected. If no such action is found, returns nullptr.
template <typename UnaryPredicate>
OmniboxAction* GetActionWhere(UnaryPredicate predicate) const {
auto it = std::ranges::find_if(actions, std::move(predicate));
return it != actions.end() ? it->get() : nullptr;
}
// Returns true if this match has a `takeover_action` with given `id`.
bool HasTakeoverAction(OmniboxActionId id) const;
// Create a new match from scratch based on this match and its action at
// given `action_index`. The content and takeover match on the returned
// match will be set up to execute the action, and only a minimum of
// data is shared from this source match.
AutocompleteMatch CreateActionMatch(size_t action_index) const;
// The provider of this match, used to remember which provider the user had
// selected when the input changes. This may be NULL, in which case there is
// no provider (or memory of the user's selection).
raw_ptr<AutocompleteProvider> provider = nullptr;
// The relevance of this match. See table in autocomplete_provider.h for
// scores returned by various providers. This is used to rank matches among
// all responding providers, so different providers must be carefully tuned to
// supply matches with appropriate relevance.
int relevance = 0;
// The "navigational intent" of this match. In other words, the likelihood
// that the user intends to navigate to a specific place by making use of
// this match.
omnibox::NavigationalIntent navigational_intent{omnibox::NAV_INTENT_NONE};
// How many times this result was typed in / selected from the omnibox.
// Only set for some providers and result_types. If it is not set,
// its value is -1. At the time of writing this comment, it is only
// set for matches from HistoryURL and HistoryQuickProvider.
int typed_count = -1;
// True if the user should be able to delete this match.
bool deletable = false;
// This string is loaded into the location bar when the item is selected
// by pressing the arrow keys. This may be different than a URL, for example,
// for search suggestions, this would just be the search terms.
std::u16string fill_into_edit;
// This string is displayed adjacent to the omnibox if this match is the
// default. Will usually be URL when autocompleting a title, and empty
// otherwise.
std::u16string additional_text;
// The inline autocompletion to display after the user's input in the
// omnibox, if this match becomes the default match. It may be empty.
std::u16string inline_autocompletion;
// Whether rich autocompletion triggered; i.e. this suggestion *is or could
// have been* rich autocompleted.
// TODO(manukh): remove `rich_autocompletion_triggered` when counterfactual
// experiments end.
RichAutocompletionType rich_autocompletion_triggered =
RichAutocompletionType::kNone;
// If false, the omnibox should prevent this match from being the
// default match. Providers should set this to true only if the
// user's input, plus any inline autocompletion on this match, would
// lead the user to expect a navigation to this match's destination.
// For example, with input "foo", a search for "bar" or navigation
// to "bar.com" should not set this flag; a navigation to "foo.com"
// should only set this flag if ".com" will be inline autocompleted;
// and a navigation to "foo/" (an intranet host) or search for "foo"
// should set this flag.
bool allowed_to_be_default_match = false;
// The URL to actually load when the autocomplete item is selected. This URL
// should be canonical so we can compare URLs with strcmp to avoid dupes.
// It may be empty if there is no possible navigation.
GURL destination_url;
// The destination URL modified for better dupe finding. The result may not
// be navigable or even valid; it's only meant to be used for detecting
// duplicates. Providers are not expected to set this field,
// `AutocompleteResult` will set it using `ComputeStrippedDestinationURL()`.
// Providers may manually set it to avoid the default
// `ComputeStrippedDestinationURL()` computation.
GURL stripped_destination_url;
// Extra headers to add to the navigation. Keys of the map represent the
// header name, and values represent header value, e.g.
// extra_headers["Content-Type"] = "application/json";
std::map<std::string, std::string> extra_headers;
// Optional image information. Used for some types of suggestions, such as
// entity suggestions, that want to display an associated image, which will be
// rendered larger than a regular suggestion icon.
// The dominant color can be used to paint an image placeholder while fetching
// the image. The value is a hex string (for example, "#424242").
std::string image_dominant_color;
GURL image_url;
// Optional icon URL. Providers may set this to override the default icon for
// the match.
GURL icon_url;
// Optional entity id for entity suggestions. Empty string means no entity ID.
// This is not meant for display, but internal use only. The actual UI display
// is controlled by the `type` and `image_url`.
std::string entity_id;
// Optional website URI for entity suggestions. Empty string means no website
// URI.
std::string website_uri;
// Used for document suggestions to show the mime-corresponding icons.
DocumentType document_type = DocumentType::NONE;
// Used for enterprise search aggregator suggestions for grouping.
EnterpriseSearchAggregatorType enterprise_search_aggregator_type =
EnterpriseSearchAggregatorType::NONE;
// Holds the common part of tail suggestion. Used to indent the contents.
// Can't simply store the character length of the string, as different
// characters may have different rendered widths.
std::u16string tail_suggest_common_prefix;
// The main text displayed in the address bar dropdown.
std::u16string contents;
ACMatchClassifications contents_class;
// Additional helper text for each entry, such as a title or description.
std::u16string description;
ACMatchClassifications description_class;
// In the case of the document provider, `description` includes a last
// updated date that may become stale. Likewise for the bookmark provider,
// `contents` may be the path which may become stale when the bookmark is
// moved. To avoid showing stale text, when `description_for_shortcut`
// is not empty, it will be stored instead of `description` (or `contents` if
// `swap_contents_and_description` is true) in the shortcuts provider.
// TODO(manukh) This is a temporary misnomer (since it can represent both
// `description` and `contents`) until `swap_contents_and_description` is
// removed.
std::u16string description_for_shortcuts;
ACMatchClassifications description_class_for_shortcuts;
// The optional suggestion group ID used to look up the suggestion group info
// for the group this suggestion belongs to from the AutocompleteResult.
//
// Use omnibox::GROUP_INVALID in place of a missing value when converting
// this to a primitive type.
// TODO(manukh): Seems redundant to prefix a suggestion field with
// 'suggestion_'. Check if it makes sense to rename to 'group_id', and
// likewise for the associated methods and local variables.
std::optional<omnibox::GroupId> suggestion_group_id;
// If true, UI-level code should swap the contents and description fields
// before displaying.
bool swap_contents_and_description = false;
std::optional<omnibox::RichAnswerTemplate> answer_template;
std::optional<omnibox::SuggestTemplateInfo> suggest_template;
// AnswerType for answer verticals, including rich answers.
omnibox::AnswerType answer_type{omnibox::ANSWER_TYPE_UNSPECIFIED};
// The transition type to use when the user opens this match. By default,
// this is TYPED. Providers whose matches do not look like URLs should set
// it to GENERATED.
ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
// Type of this match.
Type type = AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED;
// The type of this suggestion as reported from and back to the suggest server
// via the server response and the ChromeSearchboxStats (reported in the match
// destination URL) respectively.
// The default value indicates a native Chrome suggestion which must include a
// SUBTYPE_OMNIBOX_* in `subtypes`.
//
// The value is always present in omnibox::SuggestType enum. Although the list
// of types in omnibox::SuggestType enum may not be exhaustive, the known type
// names found in the server response are mapped to the equivalent enum values
// and the unknown types fall back to omnibox::TYPE_QUERY.
omnibox::SuggestType suggest_type{omnibox::TYPE_NATIVE_CHROME};
// Used to identify the specific source / type for suggestions by the
// suggest server. See SuggestSubtype in types.proto for more details.
// Uses flat_set to deduplicate subtypes (e.g., as a result of Chrome adding
// additional subtypes). The order of elements reported back via
// ChromeSearchboxStats is irrelevant. flat_set uses std::vector as a
// container, reducing memory overhead of keeping a handful of integers, while
// offering similar functionality as std::set.
//
// This set may contain int values not present in omnibox::SuggestSubtype
// enum. This is because the list of subtypes in omnibox::SuggestSubtype enum
// is not exhaustive. However, casting int values into omnibox::SuggestSubtype
// enum without testing membership is expected to be safe as
// omnibox::SuggestSubtype enum has a fixed int underlying type.
base::flat_set<omnibox::SuggestSubtype> subtypes;
// True if we saw a tab that matched this suggestion.
// Unset if it has not been computed yet.
std::optional<bool> has_tab_match;
// Set with a keyword provider match if this match can show a keyword hint.
// For example, if this is a SearchProvider match for "www.amazon.com",
// |associated_keyword| could be a KeywordProvider match for "amazon.com".
//
// When this is set, the popup will show a ">" symbol at the right edge of the
// line for this match, and tab/shift-tab will toggle in and out of keyword
// mode without disturbing the rest of the popup. See also
// OmniboxPopupModel::SetSelectedLineState().
std::unique_ptr<AutocompleteMatch> associated_keyword;
// The keyword of the TemplateURL the match originated from. This is nonempty
// for both explicit "keyword mode" matches as well as matches for the default
// search provider (so, any match for which we're doing substitution); it
// doesn't imply (alone) that the UI is going to show a keyword hint or
// keyword mode. For that, see GetKeywordUIState() or
// GetSubstitutingExplicitlyInvokedKeyword().
//
// CAUTION: The TemplateURL associated with this keyword may be deleted or
// modified while the AutocompleteMatch is alive. This means anyone who
// accesses it must perform any necessary sanity checks before blindly using
// it!
std::u16string keyword;
// Set in matches originating in keyword mode. `from_keyword` can be true even
// if `keyword` is empty and vice versa. `from_keyword` basically means the
// user input is in keyword mode. `!keyword.empty()` basically means the match
// was generated using a template URL.
//
// CAUTION: Not consistently set by all providers. That's fine-ish since this
// field isn't used much. But code relying on this feature to be correctly set
// should take care.
bool from_keyword = false;
// The visible actions relevant to this match.
std::vector<scoped_refptr<OmniboxAction>> actions;
// An optional invisible action that takes over the match navigation. That is:
// if provided, when the user selects the match, the navigation is ignored and
// this action is executed instead.
scoped_refptr<OmniboxAction> takeover_action;
// True if this match is from a previous result.
bool from_previous = false;
// Session-based metrics struct that tracks various bits of info during the
// course of a single Omnibox session (e.g. number of ZPS shown, etc.).
std::optional<SessionData> session;
// Optional search terms args. If present,
// AutocompleteController::UpdateSearchboxStats() will incorporate this data
// with additional data it calculates and pass the completed struct to
// TemplateURLRef::ReplaceSearchTerms() to reset the match's |destination_url|
// after the complete set of matches in the AutocompleteResult has been chosen
// and sorted. Most providers will leave this as NULL, which will cause the
// AutocompleteController to do no additional transformations.
std::unique_ptr<TemplateURLRef::SearchTermsArgs> search_terms_args;
// Optional post content. If this is set, the request to load the destination
// url will pass this post content as well.
std::unique_ptr<TemplateURLRef::PostContent> post_content;
// Information dictionary into which each provider can optionally record a
// property and associated value and which is presented in chrome://omnibox.
AdditionalInfo additional_info;
// A vector of matches culled during de-duplication process, sorted from
// second-best to worst according to the de-duplication preference criteria.
// This vector is retained so that if the user deletes a match, all the
// duplicates are deleted as well. This is also used for re-duping Search
// Entity vs. plain Search suggestions.
std::vector<AutocompleteMatch> duplicate_matches;
// A list of navsuggest tiles to be shown as part of this match.
// This object is only populated for TILE_NAVSUGGEST AutocompleteMatches.
std::vector<SuggestTile> suggest_tiles;
// Signals for ML scoring.
std::optional<ScoringSignals> scoring_signals;
// A flag to mark whether this would've been excluded from the "original" list
// of matches. Traditionally, providers limit the number of suggestions they
// provide to the top N most relevant matches. When ML scoring is enabled,
// however, providers pass ALL suggestion candidates to the controller. When
// this flag is true, this match is an "extra" suggestion that would've
// originally been culled by the provider.
// TODO(yoangela|manukh): Currently unused except in tests. Remove if not
// needed. Might be needed when increasing the max provider limit?
bool culled_by_provider = false;
// True for shortcut suggestions that were boosted. Used for grouping logic.
// TODO(manukh): Remove this field and use `suggestion_group_id` once grouping
// launches. In the meantime, shortcut grouping won't work for users in the
// grouping experiments.
bool shortcut_boosted = false;
// E.g. the gemini IPH match shown at the bottom of the popup.
IphType iph_type = IphType::kNone;
// IPH matches aren't clickable like other matches, but may have a next-action
// or learn-more type of link. This link is always appended to the end of
// their contents/description text.
std::u16string iph_link_text;
GURL iph_link_url;
// The text to show above the match contents & description for
// `HISTORY_EMBEDDINGS_ANSWER` matches.
std::u16string history_embeddings_answer_header_text;
// Whether the answer is still loading and should therefore show a throbber.
bool history_embeddings_answer_header_loading = false;
// The user feedback on the match.
FeedbackType feedback_type = FeedbackType::kNone;
// Stores the matching tab group uuid for this suggestion.
std::optional<base::Uuid> matching_tab_group_uuid = std::nullopt;
// So users of AutocompleteMatch can use the same ellipsis that it uses.
static const char16_t kEllipsis[];
#if DCHECK_IS_ON()
// Does a data integrity check on this match.
void Validate() const;
#endif // DCHECK_IS_ON()
// Checks one text/classifications pair for valid values.
static void ValidateClassifications(
const std::u16string& text,
const ACMatchClassifications& classifications,
const std::string& provider_name = "");
private:
#if BUILDFLAG(IS_ANDROID)
// Corresponding Java object.
// This element should not be copied with the rest of the AutocompleteMatch
// object to ensure consistent 1:1 relationship between the objects.
// This object should never be accessed directly. To acquire a reference to
// java object, call the GetOrCreateJavaObject().
// Note that this object is lazily constructed to avoid creating Java matches
// for throw away AutocompleteMatch objects, eg. during Classify() or
// QualifyPartialUrlQuery() calls.
// See AutocompleteControllerAndroid for more details.
mutable std::unique_ptr<base::android::ScopedJavaGlobalRef<jobject>>
java_match_;
// When set, holds a weak reference to Java Tab object.
JavaObjectWeakGlobalRef matching_java_tab_{};
base::WeakPtrFactory<AutocompleteMatch> weak_ptr_factory_{this};
#endif
};
typedef AutocompleteMatch::ACMatchClassification ACMatchClassification;
typedef std::vector<ACMatchClassification> ACMatchClassifications;
typedef std::vector<AutocompleteMatch> ACMatches;
// Can be used as the key for grouping AutocompleteMatches in a map based on a
// std::tuple of fields.
// The accompanying hash function makes the key usable in an std::unordered_map.
template <typename... Args>
using ACMatchKey = std::tuple<Args...>;
template <typename... Args>
struct ACMatchKeyHash {
size_t operator()(const ACMatchKey<Args...>& key) const;
};
#endif // COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_MATCH_H_