1 // Copyright (c) 2012 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 #include "chrome/browser/autocomplete/history_url_provider.h" 6 7 #include <algorithm> 8 9 #include "base/basictypes.h" 10 #include "base/bind.h" 11 #include "base/command_line.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/metrics/histogram.h" 14 #include "base/prefs/pref_service.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/time/time.h" 18 #include "chrome/browser/autocomplete/autocomplete_match.h" 19 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 20 #include "chrome/browser/autocomplete/autocomplete_result.h" 21 #include "chrome/browser/history/history_backend.h" 22 #include "chrome/browser/history/history_database.h" 23 #include "chrome/browser/history/history_service.h" 24 #include "chrome/browser/history/history_service_factory.h" 25 #include "chrome/browser/history/history_types.h" 26 #include "chrome/browser/history/in_memory_url_index_types.h" 27 #include "chrome/browser/history/scored_history_match.h" 28 #include "chrome/browser/omnibox/omnibox_field_trial.h" 29 #include "chrome/browser/profiles/profile.h" 30 #include "chrome/browser/search_engines/template_url_service.h" 31 #include "chrome/browser/search_engines/template_url_service_factory.h" 32 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h" 33 #include "chrome/common/chrome_switches.h" 34 #include "chrome/common/pref_names.h" 35 #include "chrome/common/url_constants.h" 36 #include "components/autocomplete/url_prefix.h" 37 #include "components/bookmarks/browser/bookmark_utils.h" 38 #include "components/metrics/proto/omnibox_input_type.pb.h" 39 #include "components/url_fixer/url_fixer.h" 40 #include "content/public/browser/browser_thread.h" 41 #include "net/base/net_util.h" 42 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 43 #include "url/gurl.h" 44 #include "url/url_parse.h" 45 #include "url/url_util.h" 46 47 namespace { 48 49 // Acts like the > operator for URLInfo classes. 50 bool CompareHistoryMatch(const history::HistoryMatch& a, 51 const history::HistoryMatch& b) { 52 // A URL that has been typed at all is better than one that has never been 53 // typed. (Note "!"s on each side) 54 if (!a.url_info.typed_count() != !b.url_info.typed_count()) 55 return a.url_info.typed_count() > b.url_info.typed_count(); 56 57 // Innermost matches (matches after any scheme or "www.") are better than 58 // non-innermost matches. 59 if (a.innermost_match != b.innermost_match) 60 return a.innermost_match; 61 62 // URLs that have been typed more often are better. 63 if (a.url_info.typed_count() != b.url_info.typed_count()) 64 return a.url_info.typed_count() > b.url_info.typed_count(); 65 66 // For URLs that have each been typed once, a host (alone) is better than a 67 // page inside. 68 if ((a.url_info.typed_count() == 1) && (a.IsHostOnly() != b.IsHostOnly())) 69 return a.IsHostOnly(); 70 71 // URLs that have been visited more often are better. 72 if (a.url_info.visit_count() != b.url_info.visit_count()) 73 return a.url_info.visit_count() > b.url_info.visit_count(); 74 75 // URLs that have been visited more recently are better. 76 return a.url_info.last_visit() > b.url_info.last_visit(); 77 } 78 79 // Sorts and dedups the given list of matches. 80 void SortAndDedupMatches(history::HistoryMatches* matches) { 81 // Sort by quality, best first. 82 std::sort(matches->begin(), matches->end(), &CompareHistoryMatch); 83 84 // Remove duplicate matches (caused by the search string appearing in one of 85 // the prefixes as well as after it). Consider the following scenario: 86 // 87 // User has visited "http://http.com" once and "http://htaccess.com" twice. 88 // User types "http". The autocomplete search with prefix "http://" returns 89 // the first host, while the search with prefix "" returns both hosts. Now 90 // we sort them into rank order: 91 // http://http.com (innermost_match) 92 // http://htaccess.com (!innermost_match, url_info.visit_count == 2) 93 // http://http.com (!innermost_match, url_info.visit_count == 1) 94 // 95 // The above scenario tells us we can't use std::unique(), since our 96 // duplicates are not always sequential. It also tells us we should remove 97 // the lower-quality duplicate(s), since otherwise the returned results won't 98 // be ordered correctly. This is easy to do: we just always remove the later 99 // element of a duplicate pair. 100 // Be careful! Because the vector contents may change as we remove elements, 101 // we use an index instead of an iterator in the outer loop, and don't 102 // precalculate the ending position. 103 for (size_t i = 0; i < matches->size(); ++i) { 104 for (history::HistoryMatches::iterator j(matches->begin() + i + 1); 105 j != matches->end(); ) { 106 if ((*matches)[i].url_info.url() == j->url_info.url()) 107 j = matches->erase(j); 108 else 109 ++j; 110 } 111 } 112 } 113 114 // Calculates a new relevance score applying half-life time decaying to |count| 115 // using |time_since_last_visit| and |score_buckets|. This function will never 116 // return a score higher than |undecayed_relevance|; in other words, it can only 117 // demote the old score. 118 double CalculateRelevanceUsingScoreBuckets( 119 const HUPScoringParams::ScoreBuckets& score_buckets, 120 const base::TimeDelta& time_since_last_visit, 121 int undecayed_relevance, 122 int count) { 123 // Back off if above relevance cap. 124 if ((score_buckets.relevance_cap() != -1) && 125 (undecayed_relevance >= score_buckets.relevance_cap())) 126 return undecayed_relevance; 127 128 // Time based decay using half-life time. 129 double decayed_count = count; 130 if (decayed_count > 0) 131 decayed_count *= score_buckets.HalfLifeTimeDecay(time_since_last_visit); 132 133 // Find a threshold where decayed_count >= bucket. 134 const HUPScoringParams::ScoreBuckets::CountMaxRelevance* score_bucket = NULL; 135 for (size_t i = 0; i < score_buckets.buckets().size(); ++i) { 136 score_bucket = &score_buckets.buckets()[i]; 137 if (decayed_count >= score_bucket->first) 138 break; // Buckets are in descending order, so we can ignore the rest. 139 } 140 141 return (score_bucket && (undecayed_relevance > score_bucket->second)) ? 142 score_bucket->second : undecayed_relevance; 143 } 144 145 // Returns a new relevance score for the given |match| based on the 146 // |old_relevance| score and |scoring_params|. The new relevance score is 147 // guaranteed to be less than or equal to |old_relevance|. In other words, this 148 // function can only demote a score, never boost it. Returns |old_relevance| if 149 // experimental scoring is disabled. 150 int CalculateRelevanceScoreUsingScoringParams( 151 const history::HistoryMatch& match, 152 int old_relevance, 153 const HUPScoringParams& scoring_params) { 154 if (!scoring_params.experimental_scoring_enabled) 155 return old_relevance; 156 157 const base::TimeDelta time_since_last_visit = 158 base::Time::Now() - match.url_info.last_visit(); 159 160 int relevance = CalculateRelevanceUsingScoreBuckets( 161 scoring_params.typed_count_buckets, time_since_last_visit, old_relevance, 162 match.url_info.typed_count()); 163 164 // Additional demotion (on top of typed_count demotion) of URLs that were 165 // never typed. 166 if (match.url_info.typed_count() == 0) { 167 relevance = CalculateRelevanceUsingScoreBuckets( 168 scoring_params.visited_count_buckets, time_since_last_visit, relevance, 169 match.url_info.visit_count()); 170 } 171 172 DCHECK_LE(relevance, old_relevance); 173 return relevance; 174 } 175 176 // Extracts typed_count, visit_count, and last_visited time from the URLRow and 177 // puts them in the additional info field of the |match| for display in 178 // about:omnibox. 179 void RecordAdditionalInfoFromUrlRow(const history::URLRow& info, 180 AutocompleteMatch* match) { 181 match->RecordAdditionalInfo("typed count", info.typed_count()); 182 match->RecordAdditionalInfo("visit count", info.visit_count()); 183 match->RecordAdditionalInfo("last visit", info.last_visit()); 184 } 185 186 // If |create_if_necessary| is true, ensures that |matches| contains an entry 187 // for |info|, creating a new such entry if necessary (using |input_location| 188 // and |match_in_scheme|). 189 // 190 // If |promote| is true, this also ensures the entry is the first element in 191 // |matches|, moving or adding it to the front as appropriate. When |promote| 192 // is false, existing matches are left in place, and newly added matches are 193 // placed at the back. 194 // 195 // It's OK to call this function with both |create_if_necessary| and |promote| 196 // false, in which case we'll do nothing. 197 // 198 // Returns whether the match exists regardless if it was promoted/created. 199 bool CreateOrPromoteMatch(const history::URLRow& info, 200 size_t input_location, 201 bool match_in_scheme, 202 history::HistoryMatches* matches, 203 bool create_if_necessary, 204 bool promote) { 205 // |matches| may already have an entry for this. 206 for (history::HistoryMatches::iterator i(matches->begin()); 207 i != matches->end(); ++i) { 208 if (i->url_info.url() == info.url()) { 209 // Rotate it to the front if the caller wishes. 210 if (promote) 211 std::rotate(matches->begin(), i, i + 1); 212 return true; 213 } 214 } 215 216 if (!create_if_necessary) 217 return false; 218 219 // No entry, so create one. 220 history::HistoryMatch match(info, input_location, match_in_scheme, true); 221 if (promote) 222 matches->push_front(match); 223 else 224 matches->push_back(match); 225 226 return true; 227 } 228 229 // Returns whether |match| is suitable for inline autocompletion. 230 bool CanPromoteMatchForInlineAutocomplete(const history::HistoryMatch& match) { 231 // We can promote this match if it's been typed at least n times, where n == 1 232 // for "simple" (host-only) URLs and n == 2 for others. We set a higher bar 233 // for these long URLs because it's less likely that users will want to visit 234 // them again. Even though we don't increment the typed_count for pasted-in 235 // URLs, if the user manually edits the URL or types some long thing in by 236 // hand, we wouldn't want to immediately start autocompleting it. 237 return match.url_info.typed_count() && 238 ((match.url_info.typed_count() > 1) || match.IsHostOnly()); 239 } 240 241 // Given the user's |input| and a |match| created from it, reduce the match's 242 // URL to just a host. If this host still matches the user input, return it. 243 // Returns the empty string on failure. 244 GURL ConvertToHostOnly(const history::HistoryMatch& match, 245 const base::string16& input) { 246 // See if we should try to do host-only suggestions for this URL. Nonstandard 247 // schemes means there's no authority section, so suggesting the host name 248 // is useless. File URLs are standard, but host suggestion is not useful for 249 // them either. 250 const GURL& url = match.url_info.url(); 251 if (!url.is_valid() || !url.IsStandard() || url.SchemeIsFile()) 252 return GURL(); 253 254 // Transform to a host-only match. Bail if the host no longer matches the 255 // user input (e.g. because the user typed more than just a host). 256 GURL host = url.GetWithEmptyPath(); 257 if ((host.spec().length() < (match.input_location + input.length()))) 258 return GURL(); // User typing is longer than this host suggestion. 259 260 const base::string16 spec = base::UTF8ToUTF16(host.spec()); 261 if (spec.compare(match.input_location, input.length(), input)) 262 return GURL(); // User typing is no longer a prefix. 263 264 return host; 265 } 266 267 } // namespace 268 269 // ----------------------------------------------------------------- 270 // SearchTermsDataSnapshot 271 272 // Implementation of SearchTermsData that takes a snapshot of another 273 // SearchTermsData by copying all the responses to the different getters into 274 // member strings, then returning those strings when its own getters are called. 275 // This will typically be constructed on the UI thread from 276 // UIThreadSearchTermsData but is subsequently safe to use on any thread. 277 class SearchTermsDataSnapshot : public SearchTermsData { 278 public: 279 explicit SearchTermsDataSnapshot(const SearchTermsData& search_terms_data); 280 virtual ~SearchTermsDataSnapshot(); 281 282 virtual std::string GoogleBaseURLValue() const OVERRIDE; 283 virtual std::string GetApplicationLocale() const OVERRIDE; 284 virtual base::string16 GetRlzParameterValue( 285 bool from_app_list) const OVERRIDE; 286 virtual std::string GetSearchClient() const OVERRIDE; 287 virtual std::string NTPIsThemedParam() const OVERRIDE; 288 289 private: 290 std::string google_base_url_value_; 291 std::string application_locale_; 292 base::string16 rlz_parameter_value_; 293 std::string search_client_; 294 std::string ntp_is_themed_param_; 295 296 DISALLOW_COPY_AND_ASSIGN(SearchTermsDataSnapshot); 297 }; 298 299 SearchTermsDataSnapshot::SearchTermsDataSnapshot( 300 const SearchTermsData& search_terms_data) 301 : google_base_url_value_(search_terms_data.GoogleBaseURLValue()), 302 application_locale_(search_terms_data.GetApplicationLocale()), 303 rlz_parameter_value_(search_terms_data.GetRlzParameterValue(false)), 304 search_client_(search_terms_data.GetSearchClient()), 305 ntp_is_themed_param_(search_terms_data.NTPIsThemedParam()) {} 306 307 SearchTermsDataSnapshot::~SearchTermsDataSnapshot() { 308 } 309 310 std::string SearchTermsDataSnapshot::GoogleBaseURLValue() const { 311 return google_base_url_value_; 312 } 313 314 std::string SearchTermsDataSnapshot::GetApplicationLocale() const { 315 return application_locale_; 316 } 317 318 base::string16 SearchTermsDataSnapshot::GetRlzParameterValue( 319 bool from_app_list) const { 320 return rlz_parameter_value_; 321 } 322 323 std::string SearchTermsDataSnapshot::GetSearchClient() const { 324 return search_client_; 325 } 326 327 std::string SearchTermsDataSnapshot::NTPIsThemedParam() const { 328 return ntp_is_themed_param_; 329 } 330 331 // ----------------------------------------------------------------- 332 // HistoryURLProvider 333 334 // These ugly magic numbers will go away once we switch all scoring 335 // behavior (including URL-what-you-typed) to HistoryQuick provider. 336 const int HistoryURLProvider::kScoreForBestInlineableResult = 1413; 337 const int HistoryURLProvider::kScoreForUnvisitedIntranetResult = 1403; 338 const int HistoryURLProvider::kScoreForWhatYouTypedResult = 1203; 339 const int HistoryURLProvider::kBaseScoreForNonInlineableResult = 900; 340 341 // VisitClassifier is used to classify the type of visit to a particular url. 342 class HistoryURLProvider::VisitClassifier { 343 public: 344 enum Type { 345 INVALID, // Navigations to the URL are not allowed. 346 UNVISITED_INTRANET, // A navigable URL for which we have no visit data but 347 // which is known to refer to a visited intranet host. 348 VISITED, // The site has been previously visited. 349 }; 350 351 VisitClassifier(HistoryURLProvider* provider, 352 const AutocompleteInput& input, 353 history::URLDatabase* db); 354 355 // Returns the type of visit for the specified input. 356 Type type() const { return type_; } 357 358 // Returns the URLRow for the visit. 359 const history::URLRow& url_row() const { return url_row_; } 360 361 private: 362 HistoryURLProvider* provider_; 363 history::URLDatabase* db_; 364 Type type_; 365 history::URLRow url_row_; 366 367 DISALLOW_COPY_AND_ASSIGN(VisitClassifier); 368 }; 369 370 HistoryURLProvider::VisitClassifier::VisitClassifier( 371 HistoryURLProvider* provider, 372 const AutocompleteInput& input, 373 history::URLDatabase* db) 374 : provider_(provider), 375 db_(db), 376 type_(INVALID) { 377 const GURL& url = input.canonicalized_url(); 378 // Detect email addresses. These cases will look like "http://user@site/", 379 // and because the history backend strips auth creds, we'll get a bogus exact 380 // match below if the user has visited "site". 381 if (!url.is_valid() || 382 ((input.type() == metrics::OmniboxInputType::UNKNOWN) && 383 input.parts().username.is_nonempty() && 384 !input.parts().password.is_nonempty() && 385 !input.parts().path.is_nonempty())) 386 return; 387 388 if (db_->GetRowForURL(url, &url_row_)) { 389 type_ = VISITED; 390 return; 391 } 392 393 if (provider_->CanFindIntranetURL(db_, input)) { 394 // The user typed an intranet hostname that they've visited (albeit with a 395 // different port and/or path) before. 396 url_row_ = history::URLRow(url); 397 type_ = UNVISITED_INTRANET; 398 } 399 } 400 401 HistoryURLProviderParams::HistoryURLProviderParams( 402 const AutocompleteInput& input, 403 bool trim_http, 404 const AutocompleteMatch& what_you_typed_match, 405 const std::string& languages, 406 TemplateURL* default_search_provider, 407 const SearchTermsData& search_terms_data) 408 : message_loop(base::MessageLoop::current()), 409 input(input), 410 prevent_inline_autocomplete(input.prevent_inline_autocomplete()), 411 trim_http(trim_http), 412 what_you_typed_match(what_you_typed_match), 413 failed(false), 414 exact_suggestion_is_in_history(false), 415 promote_type(NEITHER), 416 languages(languages), 417 default_search_provider(default_search_provider ? 418 new TemplateURL(default_search_provider->data()) : NULL), 419 search_terms_data(new SearchTermsDataSnapshot(search_terms_data)) { 420 } 421 422 HistoryURLProviderParams::~HistoryURLProviderParams() { 423 } 424 425 HistoryURLProvider::HistoryURLProvider(AutocompleteProviderListener* listener, 426 Profile* profile) 427 : HistoryProvider(listener, profile, 428 AutocompleteProvider::TYPE_HISTORY_URL), 429 params_(NULL), 430 cull_redirects_( 431 !OmniboxFieldTrial::InHUPCullRedirectsFieldTrial() || 432 !OmniboxFieldTrial::InHUPCullRedirectsFieldTrialExperimentGroup()), 433 create_shorter_match_( 434 !OmniboxFieldTrial::InHUPCreateShorterMatchFieldTrial() || 435 !OmniboxFieldTrial:: 436 InHUPCreateShorterMatchFieldTrialExperimentGroup()) { 437 // Initialize HUP scoring params based on the current experiment. 438 OmniboxFieldTrial::GetExperimentalHUPScoringParams(&scoring_params_); 439 } 440 441 void HistoryURLProvider::Start(const AutocompleteInput& input, 442 bool minimal_changes) { 443 // NOTE: We could try hard to do less work in the |minimal_changes| case 444 // here; some clever caching would let us reuse the raw matches from the 445 // history DB without re-querying. However, we'd still have to go back to 446 // the history thread to mark these up properly, and if pass 2 is currently 447 // running, we'd need to wait for it to return to the main thread before 448 // doing this (we can't just write new data for it to read due to thread 449 // safety issues). At that point it's just as fast, and easier, to simply 450 // re-run the query from scratch and ignore |minimal_changes|. 451 452 // Cancel any in-progress query. 453 Stop(false); 454 455 matches_.clear(); 456 457 if ((input.type() == metrics::OmniboxInputType::INVALID) || 458 (input.type() == metrics::OmniboxInputType::FORCED_QUERY)) 459 return; 460 461 // Do some fixup on the user input before matching against it, so we provide 462 // good results for local file paths, input with spaces, etc. 463 const FixupReturn fixup_return(FixupUserInput(input)); 464 if (!fixup_return.first) 465 return; 466 url::Parsed parts; 467 url_fixer::SegmentURL(fixup_return.second, &parts); 468 AutocompleteInput fixed_up_input(input); 469 fixed_up_input.UpdateText(fixup_return.second, base::string16::npos, parts); 470 471 // Create a match for what the user typed. 472 const bool trim_http = !AutocompleteInput::HasHTTPScheme(input.text()); 473 AutocompleteMatch what_you_typed_match(SuggestExactInput( 474 fixed_up_input.text(), fixed_up_input.canonicalized_url(), trim_http)); 475 what_you_typed_match.relevance = CalculateRelevance(WHAT_YOU_TYPED, 0); 476 477 // Add the WYT match as a fallback in case we can't get the history service or 478 // URL DB; otherwise, we'll replace this match lower down. Don't do this for 479 // queries, though -- while we can sometimes mark up a match for them, it's 480 // not what the user wants, and just adds noise. 481 if (fixed_up_input.type() != metrics::OmniboxInputType::QUERY) 482 matches_.push_back(what_you_typed_match); 483 484 // We'll need the history service to run both passes, so try to obtain it. 485 if (!profile_) 486 return; 487 HistoryService* const history_service = 488 HistoryServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS); 489 if (!history_service) 490 return; 491 492 // Get the default search provider and search terms data now since we have to 493 // retrieve these on the UI thread, and the second pass runs on the history 494 // thread. |template_url_service| can be NULL when testing. 495 TemplateURLService* template_url_service = 496 TemplateURLServiceFactory::GetForProfile(profile_); 497 TemplateURL* default_search_provider = template_url_service ? 498 template_url_service->GetDefaultSearchProvider() : NULL; 499 UIThreadSearchTermsData data(profile_); 500 501 // Create the data structure for the autocomplete passes. We'll save this off 502 // onto the |params_| member for later deletion below if we need to run pass 503 // 2. 504 scoped_ptr<HistoryURLProviderParams> params( 505 new HistoryURLProviderParams( 506 fixed_up_input, trim_http, what_you_typed_match, 507 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), 508 default_search_provider, data)); 509 // Note that we use the non-fixed-up input here, since fixup may strip 510 // trailing whitespace. 511 params->prevent_inline_autocomplete = PreventInlineAutocomplete(input); 512 513 // Pass 1: Get the in-memory URL database, and use it to find and promote 514 // the inline autocomplete match, if any. 515 history::URLDatabase* url_db = history_service->InMemoryDatabase(); 516 // url_db can be NULL if it hasn't finished initializing (or failed to 517 // initialize). In this case all we can do is fall back on the second 518 // pass. 519 // 520 // TODO(pkasting): We should just block here until this loads. Any time 521 // someone unloads the history backend, we'll get inconsistent inline 522 // autocomplete behavior here. 523 if (url_db) { 524 DoAutocomplete(NULL, url_db, params.get()); 525 matches_.clear(); 526 PromoteMatchesIfNecessary(*params); 527 UpdateStarredStateOfMatches(); 528 // NOTE: We don't reset |params| here since at least the |promote_type| 529 // field on it will be read by the second pass -- see comments in 530 // DoAutocomplete(). 531 } 532 533 // Pass 2: Ask the history service to call us back on the history thread, 534 // where we can read the full on-disk DB. 535 if (input.want_asynchronous_matches()) { 536 done_ = false; 537 params_ = params.release(); // This object will be destroyed in 538 // QueryComplete() once we're done with it. 539 history_service->ScheduleAutocomplete(this, params_); 540 } 541 } 542 543 void HistoryURLProvider::Stop(bool clear_cached_results) { 544 done_ = true; 545 546 if (params_) 547 params_->cancel_flag.Set(); 548 } 549 550 AutocompleteMatch HistoryURLProvider::SuggestExactInput( 551 const base::string16& text, 552 const GURL& destination_url, 553 bool trim_http) { 554 // The FormattedStringWithEquivalentMeaning() call below requires callers to 555 // be on the UI thread. 556 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) || 557 !content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI)); 558 559 AutocompleteMatch match(this, 0, false, 560 AutocompleteMatchType::URL_WHAT_YOU_TYPED); 561 562 if (destination_url.is_valid()) { 563 match.destination_url = destination_url; 564 565 // Trim off "http://" if the user didn't type it. 566 // NOTE: We use TrimHttpPrefix() here rather than StringForURLDisplay() to 567 // strip the scheme as we need to know the offset so we can adjust the 568 // |match_location| below. StringForURLDisplay() and TrimHttpPrefix() have 569 // slightly different behavior as well (the latter will strip even without 570 // two slashes after the scheme). 571 DCHECK(!trim_http || !AutocompleteInput::HasHTTPScheme(text)); 572 base::string16 display_string( 573 StringForURLDisplay(destination_url, false, false)); 574 const size_t offset = trim_http ? TrimHttpPrefix(&display_string) : 0; 575 match.fill_into_edit = 576 AutocompleteInput::FormattedStringWithEquivalentMeaning(destination_url, 577 display_string); 578 match.allowed_to_be_default_match = true; 579 // NOTE: Don't set match.inline_autocompletion to something non-empty here; 580 // it's surprising and annoying. 581 582 // Try to highlight "innermost" match location. If we fix up "w" into 583 // "www.w.com", we want to highlight the fifth character, not the first. 584 // This relies on match.destination_url being the non-prefix-trimmed version 585 // of match.contents. 586 match.contents = display_string; 587 const URLPrefix* best_prefix = URLPrefix::BestURLPrefix( 588 base::UTF8ToUTF16(destination_url.spec()), text); 589 // It's possible for match.destination_url to not contain the user's input 590 // at all (so |best_prefix| is NULL), for example if the input is 591 // "view-source:x" and |destination_url| has an inserted "http://" in the 592 // middle. 593 if (best_prefix == NULL) { 594 AutocompleteMatch::ClassifyMatchInString(text, match.contents, 595 ACMatchClassification::URL, 596 &match.contents_class); 597 } else { 598 AutocompleteMatch::ClassifyLocationInString( 599 best_prefix->prefix.length() - offset, text.length(), 600 match.contents.length(), ACMatchClassification::URL, 601 &match.contents_class); 602 } 603 604 match.is_history_what_you_typed_match = true; 605 } 606 607 return match; 608 } 609 610 void HistoryURLProvider::ExecuteWithDB(history::HistoryBackend* backend, 611 history::URLDatabase* db, 612 HistoryURLProviderParams* params) { 613 // We may get called with a NULL database if it couldn't be properly 614 // initialized. 615 if (!db) { 616 params->failed = true; 617 } else if (!params->cancel_flag.IsSet()) { 618 base::TimeTicks beginning_time = base::TimeTicks::Now(); 619 620 DoAutocomplete(backend, db, params); 621 622 UMA_HISTOGRAM_TIMES("Autocomplete.HistoryAsyncQueryTime", 623 base::TimeTicks::Now() - beginning_time); 624 } 625 626 // Return the results (if any) to the main thread. 627 params->message_loop->PostTask(FROM_HERE, base::Bind( 628 &HistoryURLProvider::QueryComplete, this, params)); 629 } 630 631 HistoryURLProvider::~HistoryURLProvider() { 632 // Note: This object can get leaked on shutdown if there are pending 633 // requests on the database (which hold a reference to us). Normally, these 634 // messages get flushed for each thread. We do a round trip from main, to 635 // history, back to main while holding a reference. If the main thread 636 // completes before the history thread, the message to delegate back to the 637 // main thread will not run and the reference will leak. Therefore, don't do 638 // anything on destruction. 639 } 640 641 // static 642 int HistoryURLProvider::CalculateRelevance(MatchType match_type, 643 int match_number) { 644 switch (match_type) { 645 case INLINE_AUTOCOMPLETE: 646 return kScoreForBestInlineableResult; 647 648 case UNVISITED_INTRANET: 649 return kScoreForUnvisitedIntranetResult; 650 651 case WHAT_YOU_TYPED: 652 return kScoreForWhatYouTypedResult; 653 654 default: // NORMAL 655 return kBaseScoreForNonInlineableResult + match_number; 656 } 657 } 658 659 // static 660 ACMatchClassifications HistoryURLProvider::ClassifyDescription( 661 const base::string16& input_text, 662 const base::string16& description) { 663 base::string16 clean_description = bookmark_utils::CleanUpTitleForMatching( 664 description); 665 history::TermMatches description_matches(SortAndDeoverlapMatches( 666 history::MatchTermInString(input_text, clean_description, 0))); 667 history::WordStarts description_word_starts; 668 history::String16VectorFromString16( 669 clean_description, false, &description_word_starts); 670 // If HistoryURL retrieves any matches (and hence we reach this code), we 671 // are guaranteed that the beginning of input_text must be a word break. 672 history::WordStarts offsets(1, 0u); 673 description_matches = 674 history::ScoredHistoryMatch::FilterTermMatchesByWordStarts( 675 description_matches, offsets, description_word_starts, 0, 676 std::string::npos); 677 return SpansFromTermMatch( 678 description_matches, clean_description.length(), false); 679 } 680 681 void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, 682 history::URLDatabase* db, 683 HistoryURLProviderParams* params) { 684 // Get the matching URLs from the DB. 685 params->matches.clear(); 686 history::URLRows url_matches; 687 const URLPrefixes& prefixes = URLPrefix::GetURLPrefixes(); 688 for (URLPrefixes::const_iterator i(prefixes.begin()); i != prefixes.end(); 689 ++i) { 690 if (params->cancel_flag.IsSet()) 691 return; // Canceled in the middle of a query, give up. 692 693 // We only need kMaxMatches results in the end, but before we get there we 694 // need to promote lower-quality matches that are prefixes of higher-quality 695 // matches, and remove lower-quality redirects. So we ask for more results 696 // than we need, of every prefix type, in hopes this will give us far more 697 // than enough to work with. CullRedirects() will then reduce the list to 698 // the best kMaxMatches results. 699 db->AutocompleteForPrefix( 700 base::UTF16ToUTF8(i->prefix + params->input.text()), kMaxMatches * 2, 701 !backend, &url_matches); 702 for (history::URLRows::const_iterator j(url_matches.begin()); 703 j != url_matches.end(); ++j) { 704 const URLPrefix* best_prefix = URLPrefix::BestURLPrefix( 705 base::UTF8ToUTF16(j->url().spec()), base::string16()); 706 DCHECK(best_prefix); 707 params->matches.push_back(history::HistoryMatch( 708 *j, i->prefix.length(), !i->num_components, 709 i->num_components >= best_prefix->num_components)); 710 } 711 } 712 713 // Create sorted list of suggestions. 714 CullPoorMatches(params); 715 SortAndDedupMatches(¶ms->matches); 716 717 // Try to create a shorter suggestion from the best match. 718 // We consider the what you typed match to be eligible to be displayed when 719 // there's a reasonable chance the user actually cares: 720 // * Their input can be opened as a URL, and 721 // * We parsed the input as a URL, or it starts with an explicit "http:" or 722 // "https:". 723 // Otherwise, this is just low-quality noise. In the cases where we've parsed 724 // as UNKNOWN, we'll still show an accidental search infobar if need be. 725 VisitClassifier classifier(this, params->input, db); 726 params->have_what_you_typed_match = 727 (params->input.type() != metrics::OmniboxInputType::QUERY) && 728 ((params->input.type() != metrics::OmniboxInputType::UNKNOWN) || 729 (classifier.type() == VisitClassifier::UNVISITED_INTRANET) || 730 !params->trim_http || 731 (AutocompleteInput::NumNonHostComponents(params->input.parts()) > 0)); 732 const bool have_shorter_suggestion_suitable_for_inline_autocomplete = 733 PromoteOrCreateShorterSuggestion( 734 db, params->have_what_you_typed_match, params); 735 736 // Check whether what the user typed appears in history. 737 const bool can_check_history_for_exact_match = 738 // Checking what_you_typed_match.is_history_what_you_typed_match tells us 739 // whether SuggestExactInput() succeeded in constructing a valid match. 740 params->what_you_typed_match.is_history_what_you_typed_match && 741 // Additionally, in the case where the user has typed "foo.com" and 742 // visited (but not typed) "foo/", and the input is "foo", the first pass 743 // will fall into the FRONT_HISTORY_MATCH case for "foo.com" but the 744 // second pass can suggest the exact input as a better URL. Since we need 745 // both passes to agree, and since during the first pass there's no way to 746 // know about "foo/", ensure that if the promote type was set to 747 // FRONT_HISTORY_MATCH during the first pass, the second pass will not 748 // consider the exact suggestion to be in history and therefore will not 749 // suggest the exact input as a better match. (Note that during the first 750 // pass, this conditional will always succeed since |promote_type| is 751 // initialized to NEITHER.) 752 (params->promote_type != HistoryURLProviderParams::FRONT_HISTORY_MATCH); 753 params->exact_suggestion_is_in_history = can_check_history_for_exact_match && 754 FixupExactSuggestion(db, classifier, params); 755 756 // If we succeeded in fixing up the exact match based on the user's history, 757 // we should treat it as the best match regardless of input type. If not, 758 // then we check whether there's an inline autocompletion we can create from 759 // this input, so we can promote that as the best match. 760 if (params->exact_suggestion_is_in_history) { 761 params->promote_type = HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH; 762 } else if (!params->prevent_inline_autocomplete && !params->matches.empty() && 763 (have_shorter_suggestion_suitable_for_inline_autocomplete || 764 CanPromoteMatchForInlineAutocomplete(params->matches[0]))) { 765 params->promote_type = HistoryURLProviderParams::FRONT_HISTORY_MATCH; 766 } else { 767 // Failed to promote any URLs. Use the What You Typed match, if we have it. 768 params->promote_type = params->have_what_you_typed_match ? 769 HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH : 770 HistoryURLProviderParams::NEITHER; 771 } 772 773 const size_t max_results = 774 kMaxMatches + (params->exact_suggestion_is_in_history ? 1 : 0); 775 if (backend && cull_redirects_) { 776 // Remove redirects and trim list to size. We want to provide up to 777 // kMaxMatches results plus the What You Typed result, if it was added to 778 // params->matches above. 779 CullRedirects(backend, ¶ms->matches, max_results); 780 } else if (params->matches.size() > max_results) { 781 // Simply trim the list to size. 782 params->matches.resize(max_results); 783 } 784 } 785 786 void HistoryURLProvider::PromoteMatchesIfNecessary( 787 const HistoryURLProviderParams& params) { 788 if (params.promote_type == HistoryURLProviderParams::FRONT_HISTORY_MATCH) { 789 matches_.push_back(HistoryMatchToACMatch(params, 0, INLINE_AUTOCOMPLETE, 790 CalculateRelevance(INLINE_AUTOCOMPLETE, 0))); 791 if (OmniboxFieldTrial::AddUWYTMatchEvenIfPromotedURLs() && 792 params.have_what_you_typed_match) { 793 matches_.push_back(params.what_you_typed_match); 794 } 795 } else if (params.promote_type == 796 HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH) { 797 matches_.push_back(params.what_you_typed_match); 798 } 799 } 800 801 void HistoryURLProvider::QueryComplete( 802 HistoryURLProviderParams* params_gets_deleted) { 803 // Ensure |params_gets_deleted| gets deleted on exit. 804 scoped_ptr<HistoryURLProviderParams> params(params_gets_deleted); 805 806 // If the user hasn't already started another query, clear our member pointer 807 // so we can't write into deleted memory. 808 if (params_ == params_gets_deleted) 809 params_ = NULL; 810 811 // Don't send responses for queries that have been canceled. 812 if (params->cancel_flag.IsSet()) 813 return; // Already set done_ when we canceled, no need to set it again. 814 815 // Don't modify |matches_| if the query failed, since it might have a default 816 // match in it, whereas |params->matches| will be empty. 817 if (!params->failed) { 818 matches_.clear(); 819 PromoteMatchesIfNecessary(*params); 820 821 // Determine relevance of highest scoring match, if any. 822 int relevance = matches_.empty() ? 823 CalculateRelevance(NORMAL, 824 static_cast<int>(params->matches.size() - 1)) : 825 matches_[0].relevance; 826 827 // Convert the history matches to autocomplete matches. If we promoted the 828 // first match, skip over it. 829 const size_t first_match = 830 (params->exact_suggestion_is_in_history || 831 (params->promote_type == 832 HistoryURLProviderParams::FRONT_HISTORY_MATCH)) ? 1 : 0; 833 for (size_t i = first_match; i < params->matches.size(); ++i) { 834 // All matches score one less than the previous match. 835 --relevance; 836 // The experimental scoring must not change the top result's score. 837 if (!matches_.empty()) { 838 relevance = CalculateRelevanceScoreUsingScoringParams( 839 params->matches[i], relevance, scoring_params_); 840 } 841 matches_.push_back(HistoryMatchToACMatch(*params, i, NORMAL, relevance)); 842 } 843 844 UpdateStarredStateOfMatches(); 845 } 846 847 done_ = true; 848 listener_->OnProviderUpdate(true); 849 } 850 851 bool HistoryURLProvider::FixupExactSuggestion( 852 history::URLDatabase* db, 853 const VisitClassifier& classifier, 854 HistoryURLProviderParams* params) const { 855 MatchType type = INLINE_AUTOCOMPLETE; 856 switch (classifier.type()) { 857 case VisitClassifier::INVALID: 858 return false; 859 case VisitClassifier::UNVISITED_INTRANET: 860 type = UNVISITED_INTRANET; 861 break; 862 default: 863 DCHECK_EQ(VisitClassifier::VISITED, classifier.type()); 864 // We have data for this match, use it. 865 params->what_you_typed_match.deletable = true; 866 params->what_you_typed_match.description = classifier.url_row().title(); 867 RecordAdditionalInfoFromUrlRow(classifier.url_row(), 868 ¶ms->what_you_typed_match); 869 params->what_you_typed_match.description_class = ClassifyDescription( 870 params->input.text(), params->what_you_typed_match.description); 871 if (!classifier.url_row().typed_count()) { 872 // If we reach here, we must be in the second pass, and we must not have 873 // this row's data available during the first pass. That means we 874 // either scored it as WHAT_YOU_TYPED or UNVISITED_INTRANET, and to 875 // maintain the ordering between passes consistent, we need to score it 876 // the same way here. 877 type = CanFindIntranetURL(db, params->input) ? 878 UNVISITED_INTRANET : WHAT_YOU_TYPED; 879 } 880 break; 881 } 882 883 const GURL& url = params->what_you_typed_match.destination_url; 884 const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec(); 885 // If the what-you-typed result looks like a single word (which can be 886 // interpreted as an intranet address) followed by a pound sign ("#"), 887 // leave the score for the url-what-you-typed result as is. It will be 888 // outscored by a search query from the SearchProvider. This test fixes 889 // cases such as "c#" and "c# foo" where the user has visited an intranet 890 // site "c". We want the search-what-you-typed score to beat the 891 // URL-what-you-typed score in this case. Most of the below test tries to 892 // make sure that this code does not trigger if the user did anything to 893 // indicate the desired match is a URL. For instance, "c/# foo" will not 894 // pass the test because that will be classified as input type URL. The 895 // parsed.CountCharactersBefore() in the test looks for the presence of a 896 // reference fragment in the URL by checking whether the position differs 897 // included the delimiter (pound sign) versus not including the delimiter. 898 // (One cannot simply check url.ref() because it will not distinguish 899 // between the input "c" and the input "c#", both of which will have empty 900 // reference fragments.) 901 if ((type == UNVISITED_INTRANET) && 902 (params->input.type() != metrics::OmniboxInputType::URL) && 903 url.username().empty() && url.password().empty() && url.port().empty() && 904 (url.path() == "/") && url.query().empty() && 905 (parsed.CountCharactersBefore(url::Parsed::REF, true) != 906 parsed.CountCharactersBefore(url::Parsed::REF, false))) { 907 return false; 908 } 909 910 params->what_you_typed_match.relevance = CalculateRelevance(type, 0); 911 912 // If there are any other matches, then don't promote this match here, in 913 // hopes the caller will be able to inline autocomplete a better suggestion. 914 // DoAutocomplete() will fall back on this match if inline autocompletion 915 // fails. This matches how we react to never-visited URL inputs in the non- 916 // intranet case. 917 if (type == UNVISITED_INTRANET && !params->matches.empty()) 918 return false; 919 920 // Put it on the front of the HistoryMatches for redirect culling. 921 CreateOrPromoteMatch(classifier.url_row(), base::string16::npos, false, 922 ¶ms->matches, true, true); 923 return true; 924 } 925 926 bool HistoryURLProvider::CanFindIntranetURL( 927 history::URLDatabase* db, 928 const AutocompleteInput& input) const { 929 // Normally passing the first two conditions below ought to guarantee the 930 // third condition, but because FixupUserInput() can run and modify the 931 // input's text and parts between Parse() and here, it seems better to be 932 // paranoid and check. 933 if ((input.type() != metrics::OmniboxInputType::UNKNOWN) || 934 !LowerCaseEqualsASCII(input.scheme(), url::kHttpScheme) || 935 !input.parts().host.is_nonempty()) 936 return false; 937 const std::string host(base::UTF16ToUTF8( 938 input.text().substr(input.parts().host.begin, input.parts().host.len))); 939 const size_t registry_length = 940 net::registry_controlled_domains::GetRegistryLength( 941 host, 942 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, 943 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 944 return registry_length == 0 && db->IsTypedHost(host); 945 } 946 947 bool HistoryURLProvider::PromoteOrCreateShorterSuggestion( 948 history::URLDatabase* db, 949 bool have_what_you_typed_match, 950 HistoryURLProviderParams* params) { 951 if (params->matches.empty()) 952 return false; // No matches, nothing to do. 953 954 // Determine the base URL from which to search, and whether that URL could 955 // itself be added as a match. We can add the base iff it's not "effectively 956 // the same" as any "what you typed" match. 957 const history::HistoryMatch& match = params->matches[0]; 958 GURL search_base = ConvertToHostOnly(match, params->input.text()); 959 bool can_add_search_base_to_matches = !have_what_you_typed_match; 960 if (search_base.is_empty()) { 961 // Search from what the user typed when we couldn't reduce the best match 962 // to a host. Careful: use a substring of |match| here, rather than the 963 // first match in |params|, because they might have different prefixes. If 964 // the user typed "google.com", params->what_you_typed_match will hold 965 // "http://google.com/", but |match| might begin with 966 // "http://www.google.com/". 967 // TODO: this should be cleaned up, and is probably incorrect for IDN. 968 std::string new_match = match.url_info.url().possibly_invalid_spec(). 969 substr(0, match.input_location + params->input.text().length()); 970 search_base = GURL(new_match); 971 if (search_base.is_empty()) 972 return false; // Can't construct a URL from which to start a search. 973 } else if (!can_add_search_base_to_matches) { 974 can_add_search_base_to_matches = 975 (search_base != params->what_you_typed_match.destination_url); 976 } 977 if (search_base == match.url_info.url()) 978 return false; // Couldn't shorten |match|, so no URLs to search over. 979 980 // Search the DB for short URLs between our base and |match|. 981 history::URLRow info(search_base); 982 bool promote = true; 983 // A short URL is only worth suggesting if it's been visited at least a third 984 // as often as the longer URL. 985 const int min_visit_count = ((match.url_info.visit_count() - 1) / 3) + 1; 986 // For stability between the in-memory and on-disk autocomplete passes, when 987 // the long URL has been typed before, only suggest shorter URLs that have 988 // also been typed. Otherwise, the on-disk pass could suggest a shorter URL 989 // (which hasn't been typed) that the in-memory pass doesn't know about, 990 // thereby making the top match, and thus the behavior of inline 991 // autocomplete, unstable. 992 const int min_typed_count = match.url_info.typed_count() ? 1 : 0; 993 if (!db->FindShortestURLFromBase(search_base.possibly_invalid_spec(), 994 match.url_info.url().possibly_invalid_spec(), min_visit_count, 995 min_typed_count, can_add_search_base_to_matches, &info)) { 996 if (!can_add_search_base_to_matches) 997 return false; // Couldn't find anything and can't add the search base. 998 999 // Try to get info on the search base itself. Promote it to the top if the 1000 // original best match isn't good enough to autocomplete. 1001 db->GetRowForURL(search_base, &info); 1002 promote = match.url_info.typed_count() <= 1; 1003 } 1004 1005 // Promote or add the desired URL to the list of matches. 1006 const bool ensure_can_inline = 1007 promote && CanPromoteMatchForInlineAutocomplete(match); 1008 return CreateOrPromoteMatch(info, match.input_location, match.match_in_scheme, 1009 ¶ms->matches, create_shorter_match_, 1010 promote) && 1011 ensure_can_inline; 1012 } 1013 1014 void HistoryURLProvider::CullPoorMatches( 1015 HistoryURLProviderParams* params) const { 1016 const base::Time& threshold(history::AutocompleteAgeThreshold()); 1017 for (history::HistoryMatches::iterator i(params->matches.begin()); 1018 i != params->matches.end(); ) { 1019 if (RowQualifiesAsSignificant(i->url_info, threshold) && 1020 (!params->default_search_provider || 1021 !params->default_search_provider->IsSearchURL( 1022 i->url_info.url(), *params->search_terms_data))) { 1023 ++i; 1024 } else { 1025 i = params->matches.erase(i); 1026 } 1027 } 1028 } 1029 1030 void HistoryURLProvider::CullRedirects(history::HistoryBackend* backend, 1031 history::HistoryMatches* matches, 1032 size_t max_results) const { 1033 for (size_t source = 0; 1034 (source < matches->size()) && (source < max_results); ) { 1035 const GURL& url = (*matches)[source].url_info.url(); 1036 // TODO(brettw) this should go away when everything uses GURL. 1037 history::RedirectList redirects; 1038 backend->GetMostRecentRedirectsFrom(url, &redirects); 1039 if (!redirects.empty()) { 1040 // Remove all but the first occurrence of any of these redirects in the 1041 // search results. We also must add the URL we queried for, since it may 1042 // not be the first match and we'd want to remove it. 1043 // 1044 // For example, when A redirects to B and our matches are [A, X, B], 1045 // we'll get B as the redirects from, and we want to remove the second 1046 // item of that pair, removing B. If A redirects to B and our matches are 1047 // [B, X, A], we'll want to remove A instead. 1048 redirects.push_back(url); 1049 source = RemoveSubsequentMatchesOf(matches, source, redirects); 1050 } else { 1051 // Advance to next item. 1052 source++; 1053 } 1054 } 1055 1056 if (matches->size() > max_results) 1057 matches->resize(max_results); 1058 } 1059 1060 size_t HistoryURLProvider::RemoveSubsequentMatchesOf( 1061 history::HistoryMatches* matches, 1062 size_t source_index, 1063 const std::vector<GURL>& remove) const { 1064 size_t next_index = source_index + 1; // return value = item after source 1065 1066 // Find the first occurrence of any URL in the redirect chain. We want to 1067 // keep this one since it is rated the highest. 1068 history::HistoryMatches::iterator first(std::find_first_of( 1069 matches->begin(), matches->end(), remove.begin(), remove.end(), 1070 history::HistoryMatch::EqualsGURL)); 1071 DCHECK(first != matches->end()) << "We should have always found at least the " 1072 "original URL."; 1073 1074 // Find any following occurrences of any URL in the redirect chain, these 1075 // should be deleted. 1076 for (history::HistoryMatches::iterator next(std::find_first_of(first + 1, 1077 matches->end(), remove.begin(), remove.end(), 1078 history::HistoryMatch::EqualsGURL)); 1079 next != matches->end(); next = std::find_first_of(next, matches->end(), 1080 remove.begin(), remove.end(), history::HistoryMatch::EqualsGURL)) { 1081 // Remove this item. When we remove an item before the source index, we 1082 // need to shift it to the right and remember that so we can return it. 1083 next = matches->erase(next); 1084 if (static_cast<size_t>(next - matches->begin()) < next_index) 1085 --next_index; 1086 } 1087 return next_index; 1088 } 1089 1090 AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( 1091 const HistoryURLProviderParams& params, 1092 size_t match_number, 1093 MatchType match_type, 1094 int relevance) { 1095 // The FormattedStringWithEquivalentMeaning() call below requires callers to 1096 // be on the UI thread. 1097 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) || 1098 !content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI)); 1099 1100 const history::HistoryMatch& history_match = params.matches[match_number]; 1101 const history::URLRow& info = history_match.url_info; 1102 AutocompleteMatch match(this, relevance, 1103 !!info.visit_count(), AutocompleteMatchType::HISTORY_URL); 1104 match.typed_count = info.typed_count(); 1105 match.destination_url = info.url(); 1106 DCHECK(match.destination_url.is_valid()); 1107 size_t inline_autocomplete_offset = 1108 history_match.input_location + params.input.text().length(); 1109 std::string languages = (match_type == WHAT_YOU_TYPED) ? 1110 std::string() : params.languages; 1111 const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll & 1112 ~((params.trim_http && !history_match.match_in_scheme) ? 1113 0 : net::kFormatUrlOmitHTTP); 1114 match.fill_into_edit = 1115 AutocompleteInput::FormattedStringWithEquivalentMeaning(info.url(), 1116 net::FormatUrl(info.url(), languages, format_types, 1117 net::UnescapeRule::SPACES, NULL, NULL, 1118 &inline_autocomplete_offset)); 1119 if (!params.prevent_inline_autocomplete && 1120 (inline_autocomplete_offset != base::string16::npos)) { 1121 DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length()); 1122 match.inline_autocompletion = 1123 match.fill_into_edit.substr(inline_autocomplete_offset); 1124 } 1125 // The latter part of the test effectively asks "is the inline completion 1126 // empty?" (i.e., is this match effectively the what-you-typed match?). 1127 match.allowed_to_be_default_match = !params.prevent_inline_autocomplete || 1128 ((inline_autocomplete_offset != base::string16::npos) && 1129 (inline_autocomplete_offset >= match.fill_into_edit.length())); 1130 1131 size_t match_start = history_match.input_location; 1132 match.contents = net::FormatUrl(info.url(), languages, 1133 format_types, net::UnescapeRule::SPACES, NULL, NULL, &match_start); 1134 if ((match_start != base::string16::npos) && 1135 (inline_autocomplete_offset != base::string16::npos) && 1136 (inline_autocomplete_offset != match_start)) { 1137 DCHECK(inline_autocomplete_offset > match_start); 1138 AutocompleteMatch::ClassifyLocationInString(match_start, 1139 inline_autocomplete_offset - match_start, match.contents.length(), 1140 ACMatchClassification::URL, &match.contents_class); 1141 } else { 1142 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, 1143 match.contents.length(), ACMatchClassification::URL, 1144 &match.contents_class); 1145 } 1146 match.description = info.title(); 1147 match.description_class = 1148 ClassifyDescription(params.input.text(), match.description); 1149 RecordAdditionalInfoFromUrlRow(info, &match); 1150 return match; 1151 } 1152