[email protected] | eac4499 | 2012-02-14 21:39:35 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 371dab1 | 2012-06-01 03:23:55 | [diff] [blame] | 5 | #include "chrome/browser/autocomplete/autocomplete_match.h" |
| 6 | |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 7 | #include "base/i18n/time_formatting.h" |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 8 | #include "base/logging.h" |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 9 | #include "base/string16.h" |
[email protected] | 3ea1b18 | 2013-02-08 22:38:41 | [diff] [blame^] | 10 | #include "base/string_util.h" |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 11 | #include "base/stringprintf.h" |
[email protected] | 3ea1b18 | 2013-02-08 22:38:41 | [diff] [blame^] | 12 | #include "base/strings/string_number_conversions.h" |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 13 | #include "base/time.h" |
| 14 | #include "base/utf_string_conversions.h" |
[email protected] | 30f5bc9 | 2012-06-26 04:14:55 | [diff] [blame] | 15 | #include "chrome/browser/autocomplete/autocomplete_provider.h" |
[email protected] | 033f342 | 2012-03-13 21:24:18 | [diff] [blame] | 16 | #include "chrome/browser/search_engines/template_url.h" |
[email protected] | 85b8d6f | 2012-05-08 20:53:47 | [diff] [blame] | 17 | #include "chrome/browser/search_engines/template_url_service.h" |
| 18 | #include "chrome/browser/search_engines/template_url_service_factory.h" |
[email protected] | 345e8fda | 2012-09-06 01:07:47 | [diff] [blame] | 19 | #include "content/public/common/url_constants.h" |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 20 | #include "grit/theme_resources.h" |
| 21 | |
[email protected] | 3b81314d | 2012-09-11 02:48:41 | [diff] [blame] | 22 | namespace { |
| 23 | |
| 24 | bool IsTrivialClassification(const ACMatchClassifications& classifications) { |
| 25 | return classifications.empty() || |
| 26 | ((classifications.size() == 1) && |
| 27 | (classifications.back().style == ACMatchClassification::NONE)); |
| 28 | } |
| 29 | |
| 30 | } // namespace |
| 31 | |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 32 | // AutocompleteMatch ---------------------------------------------------------- |
| 33 | |
[email protected] | 531e034 | 2011-11-10 15:08:41 | [diff] [blame] | 34 | // static |
| 35 | const char16 AutocompleteMatch::kInvalidChars[] = { |
| 36 | '\n', '\r', '\t', |
| 37 | 0x2028, // Line separator |
| 38 | 0x2029, // Paragraph separator |
| 39 | 0 |
| 40 | }; |
| 41 | |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 42 | AutocompleteMatch::AutocompleteMatch() |
| 43 | : provider(NULL), |
| 44 | relevance(0), |
[email protected] | cf6256f | 2012-06-12 23:36:01 | [diff] [blame] | 45 | typed_count(-1), |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 46 | deletable(false), |
[email protected] | a2fedb1e | 2011-01-25 15:23:36 | [diff] [blame] | 47 | inline_autocomplete_offset(string16::npos), |
[email protected] | 2905f74 | 2011-10-13 03:51:58 | [diff] [blame] | 48 | transition(content::PAGE_TRANSITION_GENERATED), |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 49 | is_history_what_you_typed_match(false), |
| 50 | type(SEARCH_WHAT_YOU_TYPED), |
[email protected] | 7abfb346 | 2011-01-31 16:43:06 | [diff] [blame] | 51 | starred(false), |
| 52 | from_previous(false) { |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | AutocompleteMatch::AutocompleteMatch(AutocompleteProvider* provider, |
| 56 | int relevance, |
| 57 | bool deletable, |
| 58 | Type type) |
| 59 | : provider(provider), |
| 60 | relevance(relevance), |
[email protected] | cf6256f | 2012-06-12 23:36:01 | [diff] [blame] | 61 | typed_count(-1), |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 62 | deletable(deletable), |
[email protected] | a2fedb1e | 2011-01-25 15:23:36 | [diff] [blame] | 63 | inline_autocomplete_offset(string16::npos), |
[email protected] | 2905f74 | 2011-10-13 03:51:58 | [diff] [blame] | 64 | transition(content::PAGE_TRANSITION_TYPED), |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 65 | is_history_what_you_typed_match(false), |
| 66 | type(type), |
[email protected] | 7abfb346 | 2011-01-31 16:43:06 | [diff] [blame] | 67 | starred(false), |
| 68 | from_previous(false) { |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 69 | } |
| 70 | |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 71 | AutocompleteMatch::AutocompleteMatch(const AutocompleteMatch& match) |
| 72 | : provider(match.provider), |
| 73 | relevance(match.relevance), |
[email protected] | cf6256f | 2012-06-12 23:36:01 | [diff] [blame] | 74 | typed_count(match.typed_count), |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 75 | deletable(match.deletable), |
| 76 | fill_into_edit(match.fill_into_edit), |
| 77 | inline_autocomplete_offset(match.inline_autocomplete_offset), |
| 78 | destination_url(match.destination_url), |
| 79 | stripped_destination_url(match.stripped_destination_url), |
| 80 | contents(match.contents), |
| 81 | contents_class(match.contents_class), |
| 82 | description(match.description), |
| 83 | description_class(match.description_class), |
| 84 | transition(match.transition), |
| 85 | is_history_what_you_typed_match(match.is_history_what_you_typed_match), |
| 86 | type(match.type), |
[email protected] | bca359b | 2012-06-24 07:53:04 | [diff] [blame] | 87 | associated_keyword(match.associated_keyword.get() ? |
| 88 | new AutocompleteMatch(*match.associated_keyword) : NULL), |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 89 | keyword(match.keyword), |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 90 | starred(match.starred), |
[email protected] | bca359b | 2012-06-24 07:53:04 | [diff] [blame] | 91 | from_previous(match.from_previous), |
| 92 | search_terms_args(match.search_terms_args.get() ? |
| 93 | new TemplateURLRef::SearchTermsArgs(*match.search_terms_args) : |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 94 | NULL), |
| 95 | additional_info(match.additional_info) { |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 96 | } |
| 97 | |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 98 | AutocompleteMatch::~AutocompleteMatch() { |
| 99 | } |
| 100 | |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 101 | AutocompleteMatch& AutocompleteMatch::operator=( |
| 102 | const AutocompleteMatch& match) { |
| 103 | if (this == &match) |
| 104 | return *this; |
| 105 | |
| 106 | provider = match.provider; |
| 107 | relevance = match.relevance; |
[email protected] | cf6256f | 2012-06-12 23:36:01 | [diff] [blame] | 108 | typed_count = match.typed_count; |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 109 | deletable = match.deletable; |
| 110 | fill_into_edit = match.fill_into_edit; |
| 111 | inline_autocomplete_offset = match.inline_autocomplete_offset; |
| 112 | destination_url = match.destination_url; |
| 113 | stripped_destination_url = match.stripped_destination_url; |
| 114 | contents = match.contents; |
| 115 | contents_class = match.contents_class; |
| 116 | description = match.description; |
| 117 | description_class = match.description_class; |
| 118 | transition = match.transition; |
| 119 | is_history_what_you_typed_match = match.is_history_what_you_typed_match; |
| 120 | type = match.type; |
| 121 | associated_keyword.reset(match.associated_keyword.get() ? |
| 122 | new AutocompleteMatch(*match.associated_keyword) : NULL); |
| 123 | keyword = match.keyword; |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 124 | starred = match.starred; |
| 125 | from_previous = match.from_previous; |
[email protected] | bca359b | 2012-06-24 07:53:04 | [diff] [blame] | 126 | search_terms_args.reset(match.search_terms_args.get() ? |
| 127 | new TemplateURLRef::SearchTermsArgs(*match.search_terms_args) : NULL); |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 128 | additional_info = match.additional_info; |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 129 | return *this; |
| 130 | } |
| 131 | |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 132 | // static |
| 133 | std::string AutocompleteMatch::TypeToString(Type type) { |
[email protected] | fc65f27 | 2011-06-28 22:21:30 | [diff] [blame] | 134 | const char* strings[] = { |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 135 | "url-what-you-typed", |
| 136 | "history-url", |
| 137 | "history-title", |
| 138 | "history-body", |
| 139 | "history-keyword", |
| 140 | "navsuggest", |
| 141 | "search-what-you-typed", |
| 142 | "search-history", |
| 143 | "search-suggest", |
| 144 | "search-other-engine", |
[email protected] | 8f740548 | 2011-04-13 11:08:52 | [diff] [blame] | 145 | "extension-app", |
[email protected] | dbacefb | 2012-09-12 03:32:06 | [diff] [blame] | 146 | "contact", |
[email protected] | 2532060 | 2012-10-18 22:05:56 | [diff] [blame] | 147 | "bookmark-title", |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 148 | }; |
[email protected] | fc65f27 | 2011-06-28 22:21:30 | [diff] [blame] | 149 | COMPILE_ASSERT(arraysize(strings) == NUM_TYPES, |
| 150 | strings_array_must_match_type_enum); |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 151 | return strings[type]; |
| 152 | } |
| 153 | |
| 154 | // static |
| 155 | int AutocompleteMatch::TypeToIcon(Type type) { |
[email protected] | fc65f27 | 2011-06-28 22:21:30 | [diff] [blame] | 156 | int icons[] = { |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 157 | IDR_OMNIBOX_HTTP, |
| 158 | IDR_OMNIBOX_HTTP, |
[email protected] | c37ad9e | 2012-06-12 04:57:13 | [diff] [blame] | 159 | IDR_OMNIBOX_HTTP, |
| 160 | IDR_OMNIBOX_HTTP, |
| 161 | IDR_OMNIBOX_HTTP, |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 162 | IDR_OMNIBOX_HTTP, |
| 163 | IDR_OMNIBOX_SEARCH, |
| 164 | IDR_OMNIBOX_SEARCH, |
| 165 | IDR_OMNIBOX_SEARCH, |
| 166 | IDR_OMNIBOX_SEARCH, |
[email protected] | 8f740548 | 2011-04-13 11:08:52 | [diff] [blame] | 167 | IDR_OMNIBOX_EXTENSION_APP, |
[email protected] | dbacefb | 2012-09-12 03:32:06 | [diff] [blame] | 168 | // ContactProvider isn't used by the omnibox, so this icon is never |
| 169 | // displayed. |
| 170 | IDR_OMNIBOX_SEARCH, |
[email protected] | 2532060 | 2012-10-18 22:05:56 | [diff] [blame] | 171 | IDR_OMNIBOX_HTTP, |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 172 | }; |
[email protected] | fc65f27 | 2011-06-28 22:21:30 | [diff] [blame] | 173 | COMPILE_ASSERT(arraysize(icons) == NUM_TYPES, |
| 174 | icons_array_must_match_type_enum); |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 175 | return icons[type]; |
| 176 | } |
| 177 | |
| 178 | // static |
[email protected] | dd2f9e3 | 2012-09-19 14:23:40 | [diff] [blame] | 179 | int AutocompleteMatch::TypeToLocationBarIcon(Type type) { |
| 180 | int id = TypeToIcon(type); |
| 181 | if (id == IDR_OMNIBOX_HTTP) |
| 182 | return IDR_LOCATION_BAR_HTTP; |
| 183 | return id; |
| 184 | } |
| 185 | |
| 186 | // static |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 187 | bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& elem1, |
| 188 | const AutocompleteMatch& elem2) { |
| 189 | // For equal-relevance matches, we sort alphabetically, so that providers |
| 190 | // who return multiple elements at the same priority get a "stable" sort |
| 191 | // across multiple updates. |
[email protected] | 033f342 | 2012-03-13 21:24:18 | [diff] [blame] | 192 | return (elem1.relevance == elem2.relevance) ? |
| 193 | (elem1.contents < elem2.contents) : (elem1.relevance > elem2.relevance); |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | // static |
| 197 | bool AutocompleteMatch::DestinationSortFunc(const AutocompleteMatch& elem1, |
| 198 | const AutocompleteMatch& elem2) { |
| 199 | // Sort identical destination_urls together. Place the most relevant matches |
| 200 | // first, so that when we call std::unique(), these are the ones that get |
| 201 | // preserved. |
[email protected] | 00193bf | 2012-09-15 14:52:50 | [diff] [blame] | 202 | if (DestinationsEqual(elem1, elem2) || |
| 203 | (elem1.stripped_destination_url.is_empty() && |
| 204 | elem2.stripped_destination_url.is_empty())) |
| 205 | return MoreRelevant(elem1, elem2); |
| 206 | return elem1.stripped_destination_url < elem2.stripped_destination_url; |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | // static |
| 210 | bool AutocompleteMatch::DestinationsEqual(const AutocompleteMatch& elem1, |
| 211 | const AutocompleteMatch& elem2) { |
[email protected] | 00193bf | 2012-09-15 14:52:50 | [diff] [blame] | 212 | if (elem1.stripped_destination_url.is_empty() && |
| 213 | elem2.stripped_destination_url.is_empty()) |
| 214 | return false; |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 215 | return elem1.stripped_destination_url == elem2.stripped_destination_url; |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | // static |
| 219 | void AutocompleteMatch::ClassifyMatchInString( |
[email protected] | a2fedb1e | 2011-01-25 15:23:36 | [diff] [blame] | 220 | const string16& find_text, |
| 221 | const string16& text, |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 222 | int style, |
| 223 | ACMatchClassifications* classification) { |
| 224 | ClassifyLocationInString(text.find(find_text), find_text.length(), |
| 225 | text.length(), style, classification); |
| 226 | } |
| 227 | |
[email protected] | 24d692aa | 2011-05-25 23:07:58 | [diff] [blame] | 228 | // static |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 229 | void AutocompleteMatch::ClassifyLocationInString( |
| 230 | size_t match_location, |
| 231 | size_t match_length, |
| 232 | size_t overall_length, |
| 233 | int style, |
| 234 | ACMatchClassifications* classification) { |
| 235 | classification->clear(); |
| 236 | |
| 237 | // Don't classify anything about an empty string |
| 238 | // (AutocompleteMatch::Validate() checks this). |
| 239 | if (overall_length == 0) |
| 240 | return; |
| 241 | |
| 242 | // Mark pre-match portion of string (if any). |
| 243 | if (match_location != 0) { |
| 244 | classification->push_back(ACMatchClassification(0, style)); |
| 245 | } |
| 246 | |
| 247 | // Mark matching portion of string. |
[email protected] | a2fedb1e | 2011-01-25 15:23:36 | [diff] [blame] | 248 | if (match_location == string16::npos) { |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 249 | // No match, above classification will suffice for whole string. |
| 250 | return; |
| 251 | } |
| 252 | // Classifying an empty match makes no sense and will lead to validation |
| 253 | // errors later. |
[email protected] | eac4499 | 2012-02-14 21:39:35 | [diff] [blame] | 254 | DCHECK_GT(match_length, 0U); |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 255 | classification->push_back(ACMatchClassification(match_location, |
| 256 | (style | ACMatchClassification::MATCH) & ~ACMatchClassification::DIM)); |
| 257 | |
| 258 | // Mark post-match portion of string (if any). |
| 259 | const size_t after_match(match_location + match_length); |
| 260 | if (after_match < overall_length) { |
| 261 | classification->push_back(ACMatchClassification(after_match, style)); |
| 262 | } |
| 263 | } |
| 264 | |
[email protected] | 5595f40d | 2011-10-28 17:29:18 | [diff] [blame] | 265 | // static |
[email protected] | 3b81314d | 2012-09-11 02:48:41 | [diff] [blame] | 266 | AutocompleteMatch::ACMatchClassifications |
| 267 | AutocompleteMatch::MergeClassifications( |
| 268 | const ACMatchClassifications& classifications1, |
| 269 | const ACMatchClassifications& classifications2) { |
| 270 | // We must return the empty vector only if both inputs are truly empty. |
| 271 | // The result of merging an empty vector with a single (0, NONE) |
| 272 | // classification is the latter one-entry vector. |
| 273 | if (IsTrivialClassification(classifications1)) |
| 274 | return classifications2.empty() ? classifications1 : classifications2; |
| 275 | if (IsTrivialClassification(classifications2)) |
| 276 | return classifications1; |
| 277 | |
| 278 | ACMatchClassifications output; |
| 279 | for (ACMatchClassifications::const_iterator i = classifications1.begin(), |
| 280 | j = classifications2.begin(); i != classifications1.end();) { |
| 281 | AutocompleteMatch::AddLastClassificationIfNecessary(&output, |
| 282 | std::max(i->offset, j->offset), i->style | j->style); |
| 283 | const size_t next_i_offset = (i + 1) == classifications1.end() ? |
| 284 | static_cast<size_t>(-1) : (i + 1)->offset; |
| 285 | const size_t next_j_offset = (j + 1) == classifications2.end() ? |
| 286 | static_cast<size_t>(-1) : (j + 1)->offset; |
| 287 | if (next_i_offset >= next_j_offset) |
| 288 | ++j; |
| 289 | if (next_j_offset >= next_i_offset) |
| 290 | ++i; |
| 291 | } |
| 292 | |
| 293 | return output; |
| 294 | } |
| 295 | |
| 296 | // static |
[email protected] | 9d2b5f3b | 2012-03-14 21:34:32 | [diff] [blame] | 297 | std::string AutocompleteMatch::ClassificationsToString( |
| 298 | const ACMatchClassifications& classifications) { |
| 299 | std::string serialized_classifications; |
| 300 | for (size_t i = 0; i < classifications.size(); ++i) { |
| 301 | if (i) |
| 302 | serialized_classifications += ','; |
| 303 | serialized_classifications += base::IntToString(classifications[i].offset) + |
| 304 | ',' + base::IntToString(classifications[i].style); |
| 305 | } |
| 306 | return serialized_classifications; |
| 307 | } |
| 308 | |
| 309 | // static |
| 310 | ACMatchClassifications AutocompleteMatch::ClassificationsFromString( |
| 311 | const std::string& serialized_classifications) { |
| 312 | ACMatchClassifications classifications; |
| 313 | std::vector<std::string> tokens; |
| 314 | Tokenize(serialized_classifications, ",", &tokens); |
| 315 | DCHECK(!(tokens.size() & 1)); // The number of tokens should be even. |
| 316 | for (size_t i = 0; i < tokens.size(); i += 2) { |
| 317 | int classification_offset = 0; |
| 318 | int classification_style = ACMatchClassification::NONE; |
| 319 | if (!base::StringToInt(tokens[i], &classification_offset) || |
| 320 | !base::StringToInt(tokens[i + 1], &classification_style)) { |
| 321 | NOTREACHED(); |
| 322 | return classifications; |
| 323 | } |
| 324 | classifications.push_back(ACMatchClassification(classification_offset, |
| 325 | classification_style)); |
| 326 | } |
| 327 | return classifications; |
| 328 | } |
| 329 | |
| 330 | // static |
| 331 | void AutocompleteMatch::AddLastClassificationIfNecessary( |
| 332 | ACMatchClassifications* classifications, |
| 333 | size_t offset, |
| 334 | int style) { |
| 335 | DCHECK(classifications); |
| 336 | if (classifications->empty() || classifications->back().style != style) { |
| 337 | DCHECK(classifications->empty() || |
| 338 | (offset > classifications->back().offset)); |
| 339 | classifications->push_back(ACMatchClassification(offset, style)); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | // static |
[email protected] | 5595f40d | 2011-10-28 17:29:18 | [diff] [blame] | 344 | string16 AutocompleteMatch::SanitizeString(const string16& text) { |
| 345 | // NOTE: This logic is mirrored by |sanitizeString()| in |
[email protected] | 83820d4 | 2011-11-12 22:03:11 | [diff] [blame] | 346 | // schema_generated_bindings.js. |
[email protected] | 5595f40d | 2011-10-28 17:29:18 | [diff] [blame] | 347 | string16 result; |
| 348 | TrimWhitespace(text, TRIM_LEADING, &result); |
[email protected] | 531e034 | 2011-11-10 15:08:41 | [diff] [blame] | 349 | RemoveChars(result, kInvalidChars, &result); |
[email protected] | 5595f40d | 2011-10-28 17:29:18 | [diff] [blame] | 350 | return result; |
| 351 | } |
| 352 | |
[email protected] | 749e7ae0 | 2012-09-05 18:47:46 | [diff] [blame] | 353 | // static |
| 354 | bool AutocompleteMatch::IsSearchType(Type type) { |
| 355 | return type == SEARCH_WHAT_YOU_TYPED || |
| 356 | type == SEARCH_HISTORY || |
| 357 | type == SEARCH_SUGGEST || |
| 358 | type == SEARCH_OTHER_ENGINE; |
| 359 | } |
| 360 | |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 361 | void AutocompleteMatch::ComputeStrippedDestinationURL(Profile* profile) { |
[email protected] | 345e8fda | 2012-09-06 01:07:47 | [diff] [blame] | 362 | stripped_destination_url = destination_url; |
| 363 | if (!stripped_destination_url.is_valid()) |
| 364 | return; |
| 365 | |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 366 | // If the destination URL looks like it was generated from a TemplateURL, |
| 367 | // remove all substitutions other than the search terms. This allows us |
| 368 | // to eliminate cases like past search URLs from history that differ only |
| 369 | // by some obscure query param from each other or from the search/keyword |
| 370 | // provider matches. |
| 371 | TemplateURL* template_url = GetTemplateURL(profile, true); |
| 372 | if (template_url != NULL && template_url->SupportsReplacement()) { |
| 373 | string16 search_terms; |
| 374 | if (template_url->ExtractSearchTermsFromURL(stripped_destination_url, |
| 375 | &search_terms)) { |
| 376 | stripped_destination_url = |
| 377 | GURL(template_url->url_ref().ReplaceSearchTerms( |
| 378 | TemplateURLRef::SearchTermsArgs(search_terms))); |
| 379 | } |
| 380 | } |
| 381 | |
[email protected] | 345e8fda | 2012-09-06 01:07:47 | [diff] [blame] | 382 | // |replacements| keeps all the substitions we're going to make to |
| 383 | // from {destination_url} to {stripped_destination_url}. |need_replacement| |
| 384 | // is a helper variable that helps us keep track of whether we need |
| 385 | // to apply the replacement. |
| 386 | bool needs_replacement = false; |
| 387 | GURL::Replacements replacements; |
| 388 | |
| 389 | // Remove the www. prefix from the host. |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 390 | static const char prefix[] = "www."; |
| 391 | static const size_t prefix_len = arraysize(prefix) - 1; |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 392 | std::string host = stripped_destination_url.host(); |
[email protected] | 345e8fda | 2012-09-06 01:07:47 | [diff] [blame] | 393 | if (host.compare(0, prefix_len, prefix) == 0) { |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 394 | host = host.substr(prefix_len); |
[email protected] | 345e8fda | 2012-09-06 01:07:47 | [diff] [blame] | 395 | replacements.SetHostStr(host); |
| 396 | needs_replacement = true; |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 397 | } |
[email protected] | 345e8fda | 2012-09-06 01:07:47 | [diff] [blame] | 398 | |
| 399 | // Replace https protocol with http protocol. |
| 400 | if (stripped_destination_url.SchemeIs(chrome::kHttpsScheme)) { |
| 401 | replacements.SetScheme( |
| 402 | chrome::kHttpScheme, |
| 403 | url_parse::Component(0, strlen(chrome::kHttpScheme))); |
| 404 | needs_replacement = true; |
| 405 | } |
| 406 | |
| 407 | if (needs_replacement) |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 408 | stripped_destination_url = stripped_destination_url.ReplaceComponents( |
| 409 | replacements); |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 410 | } |
| 411 | |
[email protected] | 85b8d6f | 2012-05-08 20:53:47 | [diff] [blame] | 412 | void AutocompleteMatch::GetKeywordUIState(Profile* profile, |
| 413 | string16* keyword, |
[email protected] | 033f342 | 2012-03-13 21:24:18 | [diff] [blame] | 414 | bool* is_keyword_hint) const { |
| 415 | *is_keyword_hint = associated_keyword.get() != NULL; |
| 416 | keyword->assign(*is_keyword_hint ? associated_keyword->keyword : |
[email protected] | 85b8d6f | 2012-05-08 20:53:47 | [diff] [blame] | 417 | GetSubstitutingExplicitlyInvokedKeyword(profile)); |
[email protected] | 033f342 | 2012-03-13 21:24:18 | [diff] [blame] | 418 | } |
| 419 | |
[email protected] | 85b8d6f | 2012-05-08 20:53:47 | [diff] [blame] | 420 | string16 AutocompleteMatch::GetSubstitutingExplicitlyInvokedKeyword( |
| 421 | Profile* profile) const { |
[email protected] | 9b74ab5 | 2012-03-30 16:08:07 | [diff] [blame] | 422 | if (transition != content::PAGE_TRANSITION_KEYWORD) |
| 423 | return string16(); |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 424 | const TemplateURL* t_url = GetTemplateURL(profile, false); |
[email protected] | 9b74ab5 | 2012-03-30 16:08:07 | [diff] [blame] | 425 | return (t_url && t_url->SupportsReplacement()) ? keyword : string16(); |
[email protected] | 033f342 | 2012-03-13 21:24:18 | [diff] [blame] | 426 | } |
| 427 | |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 428 | TemplateURL* AutocompleteMatch::GetTemplateURL( |
| 429 | Profile* profile, bool allow_fallback_to_destination_host) const { |
[email protected] | 85b8d6f | 2012-05-08 20:53:47 | [diff] [blame] | 430 | DCHECK(profile); |
[email protected] | dbff44658 | 2012-10-30 00:20:26 | [diff] [blame] | 431 | TemplateURLService* template_url_service = |
| 432 | TemplateURLServiceFactory::GetForProfile(profile); |
| 433 | if (template_url_service == NULL) |
| 434 | return NULL; |
| 435 | TemplateURL* template_url = keyword.empty() ? NULL : |
| 436 | template_url_service->GetTemplateURLForKeyword(keyword); |
| 437 | if (template_url == NULL && allow_fallback_to_destination_host) { |
| 438 | template_url = template_url_service->GetTemplateURLForHost( |
| 439 | destination_url.host()); |
| 440 | } |
| 441 | return template_url; |
[email protected] | 3cb0f8d9 | 2012-02-29 05:43:34 | [diff] [blame] | 442 | } |
| 443 | |
[email protected] | 5281d42 | 2012-07-28 21:37:10 | [diff] [blame] | 444 | void AutocompleteMatch::RecordAdditionalInfo(const std::string& property, |
| 445 | const std::string& value) { |
| 446 | DCHECK(property.size()); |
| 447 | DCHECK(value.size()); |
| 448 | additional_info[property] = value; |
| 449 | } |
| 450 | |
| 451 | void AutocompleteMatch::RecordAdditionalInfo(const std::string& property, |
| 452 | int value) { |
| 453 | RecordAdditionalInfo(property, StringPrintf("%d", value)); |
| 454 | } |
| 455 | |
| 456 | void AutocompleteMatch::RecordAdditionalInfo(const std::string& property, |
| 457 | const base::Time& value) { |
| 458 | RecordAdditionalInfo(property, |
| 459 | UTF16ToUTF8(base::TimeFormatShortDateAndTime(value))); |
| 460 | } |
| 461 | |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 462 | #ifndef NDEBUG |
| 463 | void AutocompleteMatch::Validate() const { |
| 464 | ValidateClassifications(contents, contents_class); |
| 465 | ValidateClassifications(description, description_class); |
| 466 | } |
| 467 | |
| 468 | void AutocompleteMatch::ValidateClassifications( |
[email protected] | a2fedb1e | 2011-01-25 15:23:36 | [diff] [blame] | 469 | const string16& text, |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 470 | const ACMatchClassifications& classifications) const { |
| 471 | if (text.empty()) { |
[email protected] | 37b9573 | 2011-05-26 23:11:09 | [diff] [blame] | 472 | DCHECK(classifications.empty()); |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 473 | return; |
| 474 | } |
| 475 | |
| 476 | // The classifications should always cover the whole string. |
[email protected] | eac4499 | 2012-02-14 21:39:35 | [diff] [blame] | 477 | DCHECK(!classifications.empty()) << "No classification for \"" << text << '"'; |
| 478 | DCHECK_EQ(0U, classifications[0].offset) |
| 479 | << "Classification misses beginning for \"" << text << '"'; |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 480 | if (classifications.size() == 1) |
| 481 | return; |
| 482 | |
| 483 | // The classifications should always be sorted. |
| 484 | size_t last_offset = classifications[0].offset; |
| 485 | for (ACMatchClassifications::const_iterator i(classifications.begin() + 1); |
| 486 | i != classifications.end(); ++i) { |
[email protected] | 35f1f4f0 | 2012-09-11 13:17:00 | [diff] [blame] | 487 | const char* provider_name = provider ? provider->GetName() : "None"; |
[email protected] | eac4499 | 2012-02-14 21:39:35 | [diff] [blame] | 488 | DCHECK_GT(i->offset, last_offset) |
[email protected] | 6d478b1 | 2012-06-06 12:19:34 | [diff] [blame] | 489 | << " Classification for \"" << text << "\" with offset of " << i->offset |
| 490 | << " is unsorted in relation to last offset of " << last_offset |
[email protected] | 35f1f4f0 | 2012-09-11 13:17:00 | [diff] [blame] | 491 | << ". Provider: " << provider_name << "."; |
[email protected] | eac4499 | 2012-02-14 21:39:35 | [diff] [blame] | 492 | DCHECK_LT(i->offset, text.length()) |
[email protected] | 6d478b1 | 2012-06-06 12:19:34 | [diff] [blame] | 493 | << " Classification of [" << i->offset << "," << text.length() |
| 494 | << "] is out of bounds for \"" << text << "\". Provider: " |
[email protected] | 35f1f4f0 | 2012-09-11 13:17:00 | [diff] [blame] | 495 | << provider_name << "."; |
[email protected] | 9ac4009 | 2010-10-27 23:05:26 | [diff] [blame] | 496 | last_offset = i->offset; |
| 497 | } |
| 498 | } |
| 499 | #endif |