// Copyright 2014 The Chromium Authors. All rights reserved. // 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 #include #include #include "base/memory/scoped_ptr.h" #include "components/omnibox/browser/autocomplete_input.h" #include "components/omnibox/browser/autocomplete_match_type.h" #include "components/search_engines/template_url.h" #include "ui/base/page_transition_types.h" #include "url/gurl.h" class AutocompleteProvider; class SuggestionAnswer; class TemplateURL; class TemplateURLService; namespace base { class Time; } // namespace base const char kACMatchPropertyInputText[] = "input text"; const char kACMatchPropertyContentsPrefix[] = "match contents prefix"; const char kACMatchPropertyContentsStartIndex[] = "match contents start index"; // 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 { // 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 // bitfield. This also means we use "int" instead of this enum type when // passing the values around, so the compiler doesn't complain. 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" }; ACMatchClassification(size_t offset, int style) : offset(offset), style(style) { } // Offset within the string that this classification starts size_t offset; int style; }; typedef std::vector ACMatchClassifications; // Type used by providers to attach additional, optional information to // an AutocompleteMatch. typedef std::map 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 base::char16 kInvalidChars[]; AutocompleteMatch(); AutocompleteMatch(AutocompleteProvider* provider, int relevance, bool deletable, Type type); AutocompleteMatch(const AutocompleteMatch& match); ~AutocompleteMatch(); // Converts |type| to a string representation. Used in logging and debugging. AutocompleteMatch& operator=(const AutocompleteMatch& match); // Converts |type| to a resource identifier for the appropriate icon for this // type to show in the completion popup. static int TypeToIcon(Type type); // Comparison function for determining when one match is better than another. static bool MoreRelevant(const AutocompleteMatch& elem1, const AutocompleteMatch& elem2); // Comparison function for removing matches with duplicate destinations. // Destinations are compared using |stripped_destination_url|. Pairs of // matches with empty destinations are treated as differing, since empty // destinations are expected for non-navigable matches. static bool DestinationsEqual(const AutocompleteMatch& elem1, const AutocompleteMatch& elem2); // Helper functions for classes creating matches: // Fills in the classifications for |text|, using |style| as the base style // and marking the first instance of |find_text| as a match. (This match // will also not be dimmed, if |style| has DIM set.) static void ClassifyMatchInString(const base::string16& find_text, const base::string16& text, int style, ACMatchClassifications* classifications); // Similar to ClassifyMatchInString(), but for cases where the range to mark // as matching is already known (avoids calling find()). This can be helpful // when find() would be misleading (e.g. you want to mark the second match in // a string instead of the first). static void ClassifyLocationInString(size_t match_location, size_t match_length, size_t overall_length, int style, ACMatchClassifications* classifications); // 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); // Returns true if at least one style in |classifications| is of type MATCH. static bool HasMatchStyle(const ACMatchClassifications& classifications); // Removes invalid characters from |text|. Should be called on strings coming // from external sources (such as extensions) before assigning to |contents| // or |description|. static base::string16 SanitizeString(const base::string16& text); // 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); // 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 base::string16& keyword, const std::string& host); // Returns |url| altered by stripping off "www.", converting https protocol // to http, and stripping excess query parameters. These conversions are // merely to allow comparisons to remove likely duplicates; these URLs are // not used as actual destination URLs. If |template_url_service| is not // NULL, it is used to get a template URL corresponding to this match. If // the match's keyword is known, it can be passed in. Otherwise, it can be // left empty and the template URL (if any) is determined from the // destination's hostname. The template URL is used to strip off query args // other than the search terms themselves that would otherwise prevent doing // proper deduping. |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. |languages| is used to format punycoded // domain names to UTF-8 for the aforementioned duplicate detection. static GURL GURLToStrippedGURL(const GURL& url, const AutocompleteInput& input, const std::string& languages, TemplateURLService* template_url_service, const base::string16& keyword); // Computes the stripped destination URL (via GURLToStrippedGURL()) and // stores the result in |stripped_destination_url|. |input| and |languages| // are used for the same purpose as in GURLToStrippedGURL(). void ComputeStrippedDestinationURL( const AutocompleteInput& input, const std::string& languages, TemplateURLService* template_url_service); // Sets |allowed_to_be_default_match| to true if this match is effectively // the URL-what-you-typed match (i.e., would be dupped against the UWYT // match when AutocompleteResult merges matches). |languages| is used // for the same purpose as in GURLToStrippedGURL(). void EnsureUWYTIsAllowedToBeDefault( const AutocompleteInput& input, const std::string& languages, TemplateURLService* template_url_service); // 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| 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| 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| 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(). void GetKeywordUIState(TemplateURLService* template_url_service, base::string16* keyword, 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". base::string16 GetSubstitutingExplicitlyInvokedKeyword( TemplateURLService* template_url_service) const; // 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; // Adds optional information to the |additional_info| dictionary. void RecordAdditionalInfo(const std::string& property, const std::string& value); void RecordAdditionalInfo(const std::string& property, int value); void RecordAdditionalInfo(const std::string& property, const base::Time& value); // Returns the value recorded for |property| in the |additional_info| // dictionary. Returns the empty string if no such value exists. std::string GetAdditionalInfo(const std::string& property) 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 he or she presses enter in those cases if the match // is not shown. bool IsVerbatimType() 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; // Swaps the contents and description fields, and their associated // classifications, if this is a match for which we should emphasize the // title (stored in the description field) over the URL (in the contents // field). Intended to only be used at the UI level before displaying, lest // other omnibox systems get confused about which is which. See the code // that sets |swap_contents_and_description| for conditions under which // it is true. void PossiblySwapContentsAndDescriptionForDisplay(); // 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). AutocompleteProvider* provider; // The relevance of this match. See table in autocomplete.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. // // TODO(pkasting): http://b/1111299 This should be calculated algorithmically, // rather than being a fairly fixed value defined by the table above. int relevance; // 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; // True if the user should be able to delete this match. bool deletable; // 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. base::string16 fill_into_edit; // The inline autocompletion to display after the user's typing in the // omnibox, if this match becomes the default match. It may be empty. base::string16 inline_autocompletion; // 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; // 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 with "www." stripped off for better dupe finding. GURL stripped_destination_url; // The main text displayed in the address bar dropdown. base::string16 contents; ACMatchClassifications contents_class; // Additional helper text for each entry, such as a title or description. base::string16 description; ACMatchClassifications description_class; // If true, UI-level code should swap the contents and description fields // before displaying. bool swap_contents_and_description; // TODO(jdonnelly): Remove the first two properties once the downstream // clients are using the SuggestionAnswer. // A rich-format version of the display for the dropdown. base::string16 answer_contents; base::string16 answer_type; scoped_ptr answer; // 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; // Type of this match. Type type; // 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(). scoped_ptr 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! base::string16 keyword; // True if this match is from a previous result. bool from_previous; // Optional search terms args. If present, // AutocompleteController::UpdateAssistedQueryStats() 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. scoped_ptr search_terms_args; // 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 list of matches culled during de-duplication process, retained to // ensure if a match is deleted, the duplicates are deleted as well. std::vector duplicate_matches; #ifndef NDEBUG // Does a data integrity check on this match. void Validate() const; // Checks one text/classifications pair for valid values. void ValidateClassifications( const base::string16& text, const ACMatchClassifications& classifications) const; #endif }; typedef AutocompleteMatch::ACMatchClassification ACMatchClassification; typedef std::vector ACMatchClassifications; typedef std::vector ACMatches; #endif // COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_MATCH_H_