1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_OMNIBOX_SEARCH_SUGGESTION_PARSER_H_ 6 #define COMPONENTS_OMNIBOX_SEARCH_SUGGESTION_PARSER_H_ 7 8 #include <string> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/gtest_prod_util.h" 13 #include "base/strings/string16.h" 14 #include "components/omnibox/autocomplete_match.h" 15 #include "components/omnibox/autocomplete_match_type.h" 16 #include "url/gurl.h" 17 18 class AutocompleteInput; 19 class AutocompleteSchemeClassifier; 20 21 namespace base { 22 class DictionaryValue; 23 class Value; 24 } 25 26 namespace net { 27 class URLFetcher; 28 } 29 30 class SearchSuggestionParser { 31 public: 32 // The Result classes are intermediate representations of AutocompleteMatches, 33 // simply containing relevance-ranked search and navigation suggestions. 34 // They may be cached to provide some synchronous matches while requests for 35 // new suggestions from updated input are in flight. 36 // TODO(msw) Extend these classes to generate their corresponding matches and 37 // other requisite data, in order to consolidate and simplify the 38 // highly fragmented SearchProvider logic for each Result type. 39 class Result { 40 public: 41 Result(bool from_keyword_provider, 42 int relevance, 43 bool relevance_from_server, 44 AutocompleteMatchType::Type type, 45 const std::string& deletion_url); 46 virtual ~Result(); 47 from_keyword_provider()48 bool from_keyword_provider() const { return from_keyword_provider_; } 49 match_contents()50 const base::string16& match_contents() const { return match_contents_; } match_contents_class()51 const ACMatchClassifications& match_contents_class() const { 52 return match_contents_class_; 53 } 54 type()55 AutocompleteMatchType::Type type() const { return type_; } relevance()56 int relevance() const { return relevance_; } set_relevance(int relevance)57 void set_relevance(int relevance) { relevance_ = relevance; } received_after_last_keystroke()58 bool received_after_last_keystroke() const { 59 return received_after_last_keystroke_; 60 } set_received_after_last_keystroke(bool received_after_last_keystroke)61 void set_received_after_last_keystroke( 62 bool received_after_last_keystroke) { 63 received_after_last_keystroke_ = received_after_last_keystroke; 64 } 65 relevance_from_server()66 bool relevance_from_server() const { return relevance_from_server_; } set_relevance_from_server(bool relevance_from_server)67 void set_relevance_from_server(bool relevance_from_server) { 68 relevance_from_server_ = relevance_from_server; 69 } 70 deletion_url()71 const std::string& deletion_url() const { return deletion_url_; } 72 73 // Returns the default relevance value for this result (which may 74 // be left over from a previous omnibox input) given the current 75 // input and whether the current input caused a keyword provider 76 // to be active. 77 virtual int CalculateRelevance(const AutocompleteInput& input, 78 bool keyword_provider_requested) const = 0; 79 80 protected: 81 // The contents to be displayed and its style info. 82 base::string16 match_contents_; 83 ACMatchClassifications match_contents_class_; 84 85 // True if the result came from the keyword provider. 86 bool from_keyword_provider_; 87 88 AutocompleteMatchType::Type type_; 89 90 // The relevance score. 91 int relevance_; 92 93 private: 94 // Whether this result's relevance score was fully or partly calculated 95 // based on server information, and thus is assumed to be more accurate. 96 // This is ultimately used in 97 // SearchProvider::ConvertResultsToAutocompleteMatches(), see comments 98 // there. 99 bool relevance_from_server_; 100 101 // Whether this result was received asynchronously after the last 102 // keystroke, otherwise it must have come from prior cached results 103 // or from a synchronous provider. 104 bool received_after_last_keystroke_; 105 106 // Optional deletion URL provided with suggestions. Fetching this URL 107 // should result in some reasonable deletion behaviour on the server, 108 // e.g. deleting this term out of a user's server-side search history. 109 std::string deletion_url_; 110 }; 111 112 class SuggestResult : public Result { 113 public: 114 SuggestResult(const base::string16& suggestion, 115 AutocompleteMatchType::Type type, 116 const base::string16& match_contents, 117 const base::string16& match_contents_prefix, 118 const base::string16& annotation, 119 const base::string16& answer_contents, 120 const base::string16& answer_type, 121 const std::string& suggest_query_params, 122 const std::string& deletion_url, 123 bool from_keyword_provider, 124 int relevance, 125 bool relevance_from_server, 126 bool should_prefetch, 127 const base::string16& input_text); 128 virtual ~SuggestResult(); 129 suggestion()130 const base::string16& suggestion() const { return suggestion_; } match_contents_prefix()131 const base::string16& match_contents_prefix() const { 132 return match_contents_prefix_; 133 } annotation()134 const base::string16& annotation() const { return annotation_; } suggest_query_params()135 const std::string& suggest_query_params() const { 136 return suggest_query_params_; 137 } 138 answer_contents()139 const base::string16& answer_contents() const { return answer_contents_; } answer_type()140 const base::string16& answer_type() const { return answer_type_; } 141 should_prefetch()142 bool should_prefetch() const { return should_prefetch_; } 143 144 // Fills in |match_contents_class_| to reflect how |match_contents_| should 145 // be displayed and bolded against the current |input_text|. If 146 // |allow_bolding_all| is false and |match_contents_class_| would have all 147 // of |match_contents_| bolded, do nothing. 148 void ClassifyMatchContents(const bool allow_bolding_all, 149 const base::string16& input_text); 150 151 // Result: 152 virtual int CalculateRelevance( 153 const AutocompleteInput& input, 154 bool keyword_provider_requested) const OVERRIDE; 155 156 private: 157 // The search terms to be used for this suggestion. 158 base::string16 suggestion_; 159 160 // The contents to be displayed as prefix of match contents. 161 // Used for postfix suggestions to display a leading ellipsis (or some 162 // equivalent character) to indicate omitted text. 163 // Only used to pass this information to about:omnibox's "Additional Info". 164 base::string16 match_contents_prefix_; 165 166 // Optional annotation for the |match_contents_| for disambiguation. 167 // This may be displayed in the autocomplete match contents, but is defined 168 // separately to facilitate different formatting. 169 base::string16 annotation_; 170 171 // Optional additional parameters to be added to the search URL. 172 std::string suggest_query_params_; 173 174 // Optional formatted Answers result. 175 base::string16 answer_contents_; 176 177 // Type of optional formatted Answers result. 178 base::string16 answer_type_; 179 180 // Should this result be prefetched? 181 bool should_prefetch_; 182 }; 183 184 class NavigationResult : public Result { 185 public: 186 NavigationResult(const AutocompleteSchemeClassifier& scheme_classifier, 187 const GURL& url, 188 AutocompleteMatchType::Type type, 189 const base::string16& description, 190 const std::string& deletion_url, 191 bool from_keyword_provider, 192 int relevance, 193 bool relevance_from_server, 194 const base::string16& input_text, 195 const std::string& languages); 196 virtual ~NavigationResult(); 197 url()198 const GURL& url() const { return url_; } description()199 const base::string16& description() const { return description_; } formatted_url()200 const base::string16& formatted_url() const { return formatted_url_; } 201 202 // Fills in |match_contents_| and |match_contents_class_| to reflect how 203 // the URL should be displayed and bolded against the current |input_text| 204 // and user |languages|. If |allow_bolding_nothing| is false and 205 // |match_contents_class_| would result in an entirely unbolded 206 // |match_contents_|, do nothing. 207 void CalculateAndClassifyMatchContents(const bool allow_bolding_nothing, 208 const base::string16& input_text, 209 const std::string& languages); 210 211 // Result: 212 virtual int CalculateRelevance( 213 const AutocompleteInput& input, 214 bool keyword_provider_requested) const OVERRIDE; 215 216 private: 217 // The suggested url for navigation. 218 GURL url_; 219 220 // The properly formatted ("fixed up") URL string with equivalent meaning 221 // to the one in |url_|. 222 base::string16 formatted_url_; 223 224 // The suggested navigational result description; generally the site name. 225 base::string16 description_; 226 }; 227 228 typedef std::vector<SuggestResult> SuggestResults; 229 typedef std::vector<NavigationResult> NavigationResults; 230 231 // A simple structure bundling most of the information (including 232 // both SuggestResults and NavigationResults) returned by a call to 233 // the suggest server. 234 // 235 // This has to be declared after the typedefs since it relies on some of them. 236 struct Results { 237 Results(); 238 ~Results(); 239 240 // Clears |suggest_results| and |navigation_results| and resets 241 // |verbatim_relevance| to -1 (implies unset). 242 void Clear(); 243 244 // Returns whether any of the results (including verbatim) have 245 // server-provided scores. 246 bool HasServerProvidedScores() const; 247 248 // Query suggestions sorted by relevance score. 249 SuggestResults suggest_results; 250 251 // Navigational suggestions sorted by relevance score. 252 NavigationResults navigation_results; 253 254 // The server supplied verbatim relevance scores. Negative values 255 // indicate that there is no suggested score; a value of 0 256 // suppresses the verbatim result. 257 int verbatim_relevance; 258 259 // The JSON metadata associated with this server response. 260 std::string metadata; 261 262 // If the active suggest field trial (if any) has triggered. 263 bool field_trial_triggered; 264 265 // If the relevance values of the results are from the server. 266 bool relevances_from_server; 267 268 // URLs of any images in Answers results. 269 std::vector<GURL> answers_image_urls; 270 271 private: 272 DISALLOW_COPY_AND_ASSIGN(Results); 273 }; 274 275 // Extracts JSON data fetched by |source| and converts it to UTF-8. 276 static std::string ExtractJsonData(const net::URLFetcher* source); 277 278 // Parses JSON response received from the provider, stripping XSSI 279 // protection if needed. Returns the parsed data if successful, NULL 280 // otherwise. 281 static scoped_ptr<base::Value> DeserializeJsonData(std::string json_data); 282 283 // Parses results from the suggest server and updates the appropriate suggest 284 // and navigation result lists in |results|. |is_keyword_result| indicates 285 // whether the response was received from the keyword provider. 286 // Returns whether the appropriate result list members were updated. 287 static bool ParseSuggestResults( 288 const base::Value& root_val, 289 const AutocompleteInput& input, 290 const AutocompleteSchemeClassifier& scheme_classifier, 291 int default_result_relevance, 292 const std::string& languages, 293 bool is_keyword_result, 294 Results* results); 295 296 private: 297 FRIEND_TEST_ALL_PREFIXES(SearchSuggestionParser, 298 GetAnswersImageURLsWithoutImagelines); 299 FRIEND_TEST_ALL_PREFIXES(SearchSuggestionParser, 300 GetAnswersImageURLsWithValidImage); 301 302 // Gets URLs of any images in Answers results. 303 static void GetAnswersImageURLs(const base::DictionaryValue* answer_json, 304 std::vector<GURL>* urls); 305 306 DISALLOW_COPY_AND_ASSIGN(SearchSuggestionParser); 307 }; 308 309 #endif // COMPONENTS_OMNIBOX_SEARCH_SUGGESTION_PARSER_H_ 310