Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autocomplete/history_url_provider.h" | 5 #include "chrome/browser/autocomplete/history_url_provider.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 42 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 42 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 43 #include "url/gurl.h" | 43 #include "url/gurl.h" |
| 44 #include "url/url_parse.h" | 44 #include "url/url_parse.h" |
| 45 #include "url/url_util.h" | 45 #include "url/url_util.h" |
| 46 | 46 |
| 47 namespace { | 47 namespace { |
| 48 | 48 |
| 49 // Acts like the > operator for URLInfo classes. | 49 // Acts like the > operator for URLInfo classes. |
| 50 bool CompareHistoryMatch(const history::HistoryMatch& a, | 50 bool CompareHistoryMatch(const history::HistoryMatch& a, |
| 51 const history::HistoryMatch& b) { | 51 const history::HistoryMatch& b) { |
| 52 // A promoted match is better than non-promoted. | |
| 53 if (a.promoted != b.promoted) | |
| 54 return a.promoted; | |
| 55 | |
| 56 // A URL that has been typed at all is better than one that has never been | 52 // A URL that has been typed at all is better than one that has never been |
| 57 // typed. (Note "!"s on each side) | 53 // typed. (Note "!"s on each side) |
| 58 if (!a.url_info.typed_count() != !b.url_info.typed_count()) | 54 if (!a.url_info.typed_count() != !b.url_info.typed_count()) |
| 59 return a.url_info.typed_count() > b.url_info.typed_count(); | 55 return a.url_info.typed_count() > b.url_info.typed_count(); |
| 60 | 56 |
| 61 // Innermost matches (matches after any scheme or "www.") are better than | 57 // Innermost matches (matches after any scheme or "www.") are better than |
| 62 // non-innermost matches. | 58 // non-innermost matches. |
| 63 if (a.innermost_match != b.innermost_match) | 59 if (a.innermost_match != b.innermost_match) |
| 64 return a.innermost_match; | 60 return a.innermost_match; |
| 65 | 61 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 143 } | 139 } |
| 144 | 140 |
| 145 return (score_bucket && (undecayed_relevance > score_bucket->second)) ? | 141 return (score_bucket && (undecayed_relevance > score_bucket->second)) ? |
| 146 score_bucket->second : undecayed_relevance; | 142 score_bucket->second : undecayed_relevance; |
| 147 } | 143 } |
| 148 | 144 |
| 149 // Returns a new relevance score for the given |match| based on the | 145 // Returns a new relevance score for the given |match| based on the |
| 150 // |old_relevance| score and |scoring_params|. The new relevance score is | 146 // |old_relevance| score and |scoring_params|. The new relevance score is |
| 151 // guaranteed to be less than or equal to |old_relevance|. In other words, this | 147 // guaranteed to be less than or equal to |old_relevance|. In other words, this |
| 152 // function can only demote a score, never boost it. Returns |old_relevance| if | 148 // function can only demote a score, never boost it. Returns |old_relevance| if |
| 153 // experimental scoring is disabled or if |match.promoted| is true. | 149 // experimental scoring is disabled. |
| 154 int CalculateRelevanceScoreUsingScoringParams( | 150 int CalculateRelevanceScoreUsingScoringParams( |
| 155 const history::HistoryMatch& match, | 151 const history::HistoryMatch& match, |
| 156 int old_relevance, | 152 int old_relevance, |
| 157 const HUPScoringParams& scoring_params) { | 153 const HUPScoringParams& scoring_params) { |
| 158 if (!scoring_params.experimental_scoring_enabled) | 154 if (!scoring_params.experimental_scoring_enabled) |
| 159 return old_relevance; | 155 return old_relevance; |
| 160 | 156 |
| 161 const base::TimeDelta time_since_last_visit = | 157 const base::TimeDelta time_since_last_visit = |
| 162 base::Time::Now() - match.url_info.last_visit(); | 158 base::Time::Now() - match.url_info.last_visit(); |
| 163 | 159 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 225 if (promote) | 221 if (promote) |
| 226 matches->push_front(match); | 222 matches->push_front(match); |
| 227 else | 223 else |
| 228 matches->push_back(match); | 224 matches->push_back(match); |
| 229 | 225 |
| 230 return true; | 226 return true; |
| 231 } | 227 } |
| 232 | 228 |
| 233 // Returns whether |match| is suitable for inline autocompletion. | 229 // Returns whether |match| is suitable for inline autocompletion. |
| 234 bool CanPromoteMatchForInlineAutocomplete(const history::HistoryMatch& match) { | 230 bool CanPromoteMatchForInlineAutocomplete(const history::HistoryMatch& match) { |
| 235 // We can promote this match if it's been marked for promotion or typed at | 231 // We can promote this match if it's been typed at least n times, where n == 1 |
| 236 // least n times, where n == 1 for "simple" (host-only) URLs and n == 2 for | 232 // for "simple" (host-only) URLs and n == 2 for others. We set a higher bar |
| 237 // others. We set a higher bar for these long URLs because it's less likely | 233 // for these long URLs because it's less likely that users will want to visit |
| 238 // that users will want to visit them again. Even though we don't increment | 234 // them again. Even though we don't increment the typed_count for pasted-in |
| 239 // the typed_count for pasted-in URLs, if the user manually edits the URL or | 235 // URLs, if the user manually edits the URL or types some long thing in by |
| 240 // types some long thing in by hand, we wouldn't want to immediately start | 236 // hand, we wouldn't want to immediately start autocompleting it. |
| 241 // autocompleting it. | 237 return match.url_info.typed_count() && |
| 242 return match.promoted || | 238 ((match.url_info.typed_count() > 1) || match.IsHostOnly()); |
| 243 (match.url_info.typed_count() && | |
| 244 ((match.url_info.typed_count() > 1) || match.IsHostOnly())); | |
| 245 } | 239 } |
| 246 | 240 |
| 247 // Given the user's |input| and a |match| created from it, reduce the match's | 241 // Given the user's |input| and a |match| created from it, reduce the match's |
| 248 // URL to just a host. If this host still matches the user input, return it. | 242 // URL to just a host. If this host still matches the user input, return it. |
| 249 // Returns the empty string on failure. | 243 // Returns the empty string on failure. |
| 250 GURL ConvertToHostOnly(const history::HistoryMatch& match, | 244 GURL ConvertToHostOnly(const history::HistoryMatch& match, |
| 251 const base::string16& input) { | 245 const base::string16& input) { |
| 252 // See if we should try to do host-only suggestions for this URL. Nonstandard | 246 // See if we should try to do host-only suggestions for this URL. Nonstandard |
| 253 // schemes means there's no authority section, so suggesting the host name | 247 // schemes means there's no authority section, so suggesting the host name |
| 254 // is useless. File URLs are standard, but host suggestion is not useful for | 248 // is useless. File URLs are standard, but host suggestion is not useful for |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 410 const AutocompleteMatch& what_you_typed_match, | 404 const AutocompleteMatch& what_you_typed_match, |
| 411 const std::string& languages, | 405 const std::string& languages, |
| 412 TemplateURL* default_search_provider, | 406 TemplateURL* default_search_provider, |
| 413 const SearchTermsData& search_terms_data) | 407 const SearchTermsData& search_terms_data) |
| 414 : message_loop(base::MessageLoop::current()), | 408 : message_loop(base::MessageLoop::current()), |
| 415 input(input), | 409 input(input), |
| 416 prevent_inline_autocomplete(input.prevent_inline_autocomplete()), | 410 prevent_inline_autocomplete(input.prevent_inline_autocomplete()), |
| 417 trim_http(trim_http), | 411 trim_http(trim_http), |
| 418 what_you_typed_match(what_you_typed_match), | 412 what_you_typed_match(what_you_typed_match), |
| 419 failed(false), | 413 failed(false), |
| 414 exact_suggestion_is_in_history(false), | |
| 415 promote_type(NEITHER), | |
| 420 languages(languages), | 416 languages(languages), |
| 421 dont_suggest_exact_input(false), | |
| 422 default_search_provider(default_search_provider ? | 417 default_search_provider(default_search_provider ? |
| 423 new TemplateURL(default_search_provider->data()) : NULL), | 418 new TemplateURL(default_search_provider->data()) : NULL), |
| 424 search_terms_data(new SearchTermsDataSnapshot(search_terms_data)) { | 419 search_terms_data(new SearchTermsDataSnapshot(search_terms_data)) { |
| 425 } | 420 } |
| 426 | 421 |
| 427 HistoryURLProviderParams::~HistoryURLProviderParams() { | 422 HistoryURLProviderParams::~HistoryURLProviderParams() { |
| 428 } | 423 } |
| 429 | 424 |
| 430 HistoryURLProvider::HistoryURLProvider(AutocompleteProviderListener* listener, | 425 HistoryURLProvider::HistoryURLProvider(AutocompleteProviderListener* listener, |
| 431 Profile* profile) | 426 Profile* profile) |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 520 history::URLDatabase* url_db = history_service->InMemoryDatabase(); | 515 history::URLDatabase* url_db = history_service->InMemoryDatabase(); |
| 521 // url_db can be NULL if it hasn't finished initializing (or failed to | 516 // url_db can be NULL if it hasn't finished initializing (or failed to |
| 522 // initialize). In this case all we can do is fall back on the second | 517 // initialize). In this case all we can do is fall back on the second |
| 523 // pass. | 518 // pass. |
| 524 // | 519 // |
| 525 // TODO(pkasting): We should just block here until this loads. Any time | 520 // TODO(pkasting): We should just block here until this loads. Any time |
| 526 // someone unloads the history backend, we'll get inconsistent inline | 521 // someone unloads the history backend, we'll get inconsistent inline |
| 527 // autocomplete behavior here. | 522 // autocomplete behavior here. |
| 528 if (url_db) { | 523 if (url_db) { |
| 529 DoAutocomplete(NULL, url_db, params.get()); | 524 DoAutocomplete(NULL, url_db, params.get()); |
| 530 // params->matches now has the matches we should expose to the provider. | |
| 531 // Pass 2 expects a "clean slate" set of matches. | |
| 532 matches_.clear(); | 525 matches_.clear(); |
| 533 matches_.swap(params->matches); | 526 PromoteMatchIfNecessary(*params); |
| 534 UpdateStarredStateOfMatches(); | 527 UpdateStarredStateOfMatches(); |
| 535 // Reset the WYT match in |params| so that both passes get the same input | 528 // NOTE: We don't reset |params| here since at least the |promote_type| |
| 536 // state, since DoAutocomplete() may have modified it. | 529 // field on it will be read by the second pass -- see comments in |
| 537 params->what_you_typed_match = what_you_typed_match; | 530 // DoAutocomplete(). |
| 538 } | 531 } |
| 539 | 532 |
| 540 // Pass 2: Ask the history service to call us back on the history thread, | 533 // Pass 2: Ask the history service to call us back on the history thread, |
| 541 // where we can read the full on-disk DB. | 534 // where we can read the full on-disk DB. |
| 542 if (input.want_asynchronous_matches()) { | 535 if (input.want_asynchronous_matches()) { |
| 543 done_ = false; | 536 done_ = false; |
| 544 params_ = params.release(); // This object will be destroyed in | 537 params_ = params.release(); // This object will be destroyed in |
| 545 // QueryComplete() once we're done with it. | 538 // QueryComplete() once we're done with it. |
| 546 history_service->ScheduleAutocomplete(this, params_); | 539 history_service->ScheduleAutocomplete(this, params_); |
| 547 } | 540 } |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 682 description_matches, offsets, description_word_starts, 0, | 675 description_matches, offsets, description_word_starts, 0, |
| 683 std::string::npos); | 676 std::string::npos); |
| 684 return SpansFromTermMatch( | 677 return SpansFromTermMatch( |
| 685 description_matches, clean_description.length(), false); | 678 description_matches, clean_description.length(), false); |
| 686 } | 679 } |
| 687 | 680 |
| 688 void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, | 681 void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend, |
| 689 history::URLDatabase* db, | 682 history::URLDatabase* db, |
| 690 HistoryURLProviderParams* params) { | 683 HistoryURLProviderParams* params) { |
| 691 // Get the matching URLs from the DB. | 684 // Get the matching URLs from the DB. |
| 685 params->matches.clear(); | |
| 692 history::URLRows url_matches; | 686 history::URLRows url_matches; |
| 693 history::HistoryMatches history_matches; | |
| 694 | |
| 695 const URLPrefixes& prefixes = URLPrefix::GetURLPrefixes(); | 687 const URLPrefixes& prefixes = URLPrefix::GetURLPrefixes(); |
| 696 for (URLPrefixes::const_iterator i(prefixes.begin()); i != prefixes.end(); | 688 for (URLPrefixes::const_iterator i(prefixes.begin()); i != prefixes.end(); |
| 697 ++i) { | 689 ++i) { |
| 698 if (params->cancel_flag.IsSet()) | 690 if (params->cancel_flag.IsSet()) |
| 699 return; // Canceled in the middle of a query, give up. | 691 return; // Canceled in the middle of a query, give up. |
| 700 | 692 |
| 701 // We only need kMaxMatches results in the end, but before we get there we | 693 // We only need kMaxMatches results in the end, but before we get there we |
| 702 // need to promote lower-quality matches that are prefixes of higher-quality | 694 // need to promote lower-quality matches that are prefixes of higher-quality |
| 703 // matches, and remove lower-quality redirects. So we ask for more results | 695 // matches, and remove lower-quality redirects. So we ask for more results |
| 704 // than we need, of every prefix type, in hopes this will give us far more | 696 // than we need, of every prefix type, in hopes this will give us far more |
| 705 // than enough to work with. CullRedirects() will then reduce the list to | 697 // than enough to work with. CullRedirects() will then reduce the list to |
| 706 // the best kMaxMatches results. | 698 // the best kMaxMatches results. |
| 707 db->AutocompleteForPrefix( | 699 db->AutocompleteForPrefix( |
| 708 base::UTF16ToUTF8(i->prefix + params->input.text()), kMaxMatches * 2, | 700 base::UTF16ToUTF8(i->prefix + params->input.text()), kMaxMatches * 2, |
| 709 !backend, &url_matches); | 701 !backend, &url_matches); |
| 710 for (history::URLRows::const_iterator j(url_matches.begin()); | 702 for (history::URLRows::const_iterator j(url_matches.begin()); |
| 711 j != url_matches.end(); ++j) { | 703 j != url_matches.end(); ++j) { |
| 712 const URLPrefix* best_prefix = URLPrefix::BestURLPrefix( | 704 const URLPrefix* best_prefix = URLPrefix::BestURLPrefix( |
| 713 base::UTF8ToUTF16(j->url().spec()), base::string16()); | 705 base::UTF8ToUTF16(j->url().spec()), base::string16()); |
| 714 DCHECK(best_prefix); | 706 DCHECK(best_prefix); |
| 715 history_matches.push_back(history::HistoryMatch( | 707 params->matches.push_back(history::HistoryMatch( |
| 716 *j, i->prefix.length(), !i->num_components, | 708 *j, i->prefix.length(), !i->num_components, |
| 717 i->num_components >= best_prefix->num_components)); | 709 i->num_components >= best_prefix->num_components)); |
| 718 } | 710 } |
| 719 } | 711 } |
| 720 | 712 |
| 721 // Create sorted list of suggestions. | 713 // Create sorted list of suggestions. |
| 722 CullPoorMatches(*params, &history_matches); | 714 CullPoorMatches(params); |
| 723 SortAndDedupMatches(&history_matches); | 715 SortAndDedupMatches(¶ms->matches); |
| 724 | 716 |
| 725 // Try to create a shorter suggestion from the best match. | 717 // Try to create a shorter suggestion from the best match. |
| 726 // We allow the what you typed match to be displayed when there's a reasonable | 718 // We allow the what you typed match to be displayed when there's a reasonable |
| 727 // chance the user actually cares: | 719 // chance the user actually cares: |
| 728 // * Their input can be opened as a URL, and | 720 // * Their input can be opened as a URL, and |
| 729 // * We parsed the input as a URL, or it starts with an explicit "http:" or | 721 // * We parsed the input as a URL, or it starts with an explicit "http:" or |
| 730 // "https:". | 722 // "https:". |
| 731 // Otherwise, this is just low-quality noise. In the cases where we've parsed | 723 // Otherwise, this is just low-quality noise. In the cases where we've parsed |
| 732 // as UNKNOWN, we'll still show an accidental search infobar if need be. | 724 // as UNKNOWN, we'll still show an accidental search infobar if need be. |
| 733 VisitClassifier classifier(this, params->input, db); | 725 VisitClassifier classifier(this, params->input, db); |
| 734 bool have_what_you_typed_match = | 726 bool have_what_you_typed_match = |
| 735 (params->input.type() != metrics::OmniboxInputType::QUERY) && | 727 (params->input.type() != metrics::OmniboxInputType::QUERY) && |
| 736 ((params->input.type() != metrics::OmniboxInputType::UNKNOWN) || | 728 ((params->input.type() != metrics::OmniboxInputType::UNKNOWN) || |
| 737 (classifier.type() == VisitClassifier::UNVISITED_INTRANET) || | 729 (classifier.type() == VisitClassifier::UNVISITED_INTRANET) || |
| 738 !params->trim_http || | 730 !params->trim_http || |
| 739 (AutocompleteInput::NumNonHostComponents(params->input.parts()) > 0)); | 731 (AutocompleteInput::NumNonHostComponents(params->input.parts()) > 0)); |
| 740 PromoteOrCreateShorterSuggestion(db, *params, have_what_you_typed_match, | 732 const bool have_shorter_suggestion_suitable_for_inline_autocomplete = |
| 741 &history_matches); | 733 PromoteOrCreateShorterSuggestion(db, have_what_you_typed_match, params); |
| 742 | 734 |
| 743 // Try to promote a match as an exact/inline autocomplete match. This also | 735 // Check whether what the user typed appears in history. |
| 744 // moves it to the front of |history_matches|, so skip over it when | 736 params->exact_suggestion_is_in_history = |
| 745 // converting the rest of the matches. | 737 // Checking what_you_typed_match.is_history_what_you_typed_match tells us |
| 746 size_t first_match = 1; | 738 // whether SuggestExactInput() succeeded in constructing a valid match. |
| 747 size_t exact_suggestion = 0; | 739 params->what_you_typed_match.is_history_what_you_typed_match && |
| 748 // Checking params->what_you_typed_match.is_history_what_you_typed_match tells | 740 // Additionally, in the case where the user has typed "foo.com" and |
| 749 // us whether SuggestExactInput() succeeded in constructing a valid match. | 741 // visited (but not typed) "foo/", and the input is "foo", the first pass |
| 750 if (params->what_you_typed_match.is_history_what_you_typed_match && | 742 // will fall into the FRONT_HISTORY_MATCH case for "foo.com" but the |
| 751 (!backend || !params->dont_suggest_exact_input) && | 743 // second pass can suggest the exact input as a better URL. Since we need |
| 752 FixupExactSuggestion(db, classifier, params, &history_matches)) { | 744 // both passes to agree, and since during the first pass there's no way to |
| 753 // Got an exact match for the user's input. Treat it as the best match | 745 // know about "foo/", ensure that if the promote type was set to |
| 754 // regardless of the input type. | 746 // FRONT_HISTORY_MATCH during the first pass, the second pass will not |
| 755 exact_suggestion = 1; | 747 // suggest the exact input as a better match. (Note that during the first |
|
Mark P
2014/06/19 21:35:14
nit: At the beginning of this line, consider inse
Peter Kasting
2014/06/19 22:27:12
Done.
| |
| 756 params->matches.push_back(params->what_you_typed_match); | 748 // pass, this conditional will always succeed since |promote_type| is |
| 757 } else if (params->prevent_inline_autocomplete || | 749 // initialized to NEITHER.) |
| 758 history_matches.empty() || | 750 (params->promote_type != HistoryURLProviderParams::FRONT_HISTORY_MATCH) && |
| 759 !PromoteMatchForInlineAutocomplete(history_matches.front(), params)) { | 751 FixupExactSuggestion(db, classifier, params); |
|
Mark P
2014/06/19 21:35:14
nit: I feel like there out to be a trivial comment
Peter Kasting
2014/06/19 22:27:12
I solved this by introducing a temp.
| |
| 760 // Failed to promote any URLs for inline autocompletion. Use the What You | 752 |
| 761 // Typed match, if we have it. | 753 // If the exact suggestion is in history, we should treat it as the best match |
|
Mark P
2014/06/19 21:35:14
optional nit: is in history -> is considered to be
Peter Kasting
2014/06/19 22:27:12
Modified the comment in a somewhat related way.
| |
| 762 first_match = 0; | 754 // regardless of input type. If not, then we check whether there's an inline |
| 763 if (have_what_you_typed_match) | 755 // autocompletion we can create from this input, so we can promote that as the |
| 764 params->matches.push_back(params->what_you_typed_match); | 756 // best match. |
| 757 if (params->exact_suggestion_is_in_history) { | |
| 758 params->promote_type = HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH; | |
| 759 } else if (!params->prevent_inline_autocomplete && !params->matches.empty() && | |
| 760 (have_shorter_suggestion_suitable_for_inline_autocomplete || | |
| 761 CanPromoteMatchForInlineAutocomplete(params->matches[0]))) { | |
| 762 params->promote_type = HistoryURLProviderParams::FRONT_HISTORY_MATCH; | |
| 763 } else { | |
| 764 // Failed to promote any URLs. Use the What You Typed match, if we have it. | |
| 765 params->promote_type = have_what_you_typed_match ? | |
| 766 HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH : | |
| 767 HistoryURLProviderParams::NEITHER; | |
| 765 } | 768 } |
| 766 | 769 |
| 767 // This is the end of the synchronous pass. | 770 const size_t max_results = |
| 768 if (!backend) | 771 kMaxMatches + (params->exact_suggestion_is_in_history ? 1 : 0); |
| 769 return; | 772 if (backend && cull_redirects_) { |
| 770 | |
| 771 // Determine relevancy of highest scoring match, if any. | |
| 772 int relevance = -1; | |
| 773 for (ACMatches::const_iterator it = params->matches.begin(); | |
| 774 it != params->matches.end(); ++it) { | |
| 775 relevance = std::max(relevance, it->relevance); | |
| 776 } | |
| 777 | |
| 778 if (cull_redirects_) { | |
| 779 // Remove redirects and trim list to size. We want to provide up to | 773 // Remove redirects and trim list to size. We want to provide up to |
| 780 // kMaxMatches results plus the What You Typed result, if it was added to | 774 // kMaxMatches results plus the What You Typed result, if it was added to |
| 781 // |history_matches| above. | 775 // params->matches above. |
| 782 CullRedirects(backend, &history_matches, kMaxMatches + exact_suggestion); | 776 CullRedirects(backend, ¶ms->matches, max_results); |
| 783 } else { | 777 } else if (params->matches.size() > max_results) { |
| 784 // Simply trim the list to size. | 778 // Simply trim the list to size. |
| 785 if (history_matches.size() > kMaxMatches + exact_suggestion) | 779 params->matches.resize(max_results); |
| 786 history_matches.resize(kMaxMatches + exact_suggestion); | |
| 787 } | |
| 788 | |
| 789 // Convert the history matches to autocomplete matches. | |
| 790 for (size_t i = first_match; i < history_matches.size(); ++i) { | |
| 791 const history::HistoryMatch& match = history_matches[i]; | |
| 792 DCHECK(!have_what_you_typed_match || | |
| 793 (match.url_info.url() != | |
| 794 GURL(params->matches.front().destination_url))); | |
| 795 // If we've assigned a score already, all later matches score one | |
| 796 // less than the previous match. | |
| 797 relevance = (relevance > 0) ? | |
| 798 (relevance - 1) : | |
| 799 CalculateRelevance(NORMAL, | |
| 800 static_cast<int>(history_matches.size() - 1 - i)); | |
| 801 AutocompleteMatch ac_match = HistoryMatchToACMatch(*params, match, | |
| 802 NORMAL, relevance); | |
| 803 // The experimental scoring must not change the top result's score. | |
| 804 if (!params->matches.empty()) { | |
| 805 relevance = CalculateRelevanceScoreUsingScoringParams(match, relevance, | |
| 806 scoring_params_); | |
| 807 ac_match.relevance = relevance; | |
| 808 } | |
| 809 params->matches.push_back(ac_match); | |
| 810 } | 780 } |
| 811 } | 781 } |
| 812 | 782 |
| 783 void HistoryURLProvider::PromoteMatchIfNecessary( | |
| 784 const HistoryURLProviderParams& params) { | |
| 785 if (params.promote_type == HistoryURLProviderParams::NEITHER) | |
| 786 return; | |
| 787 matches_.push_back( | |
| 788 (params.promote_type == HistoryURLProviderParams::WHAT_YOU_TYPED_MATCH) ? | |
| 789 params.what_you_typed_match : | |
| 790 HistoryMatchToACMatch(params, 0, INLINE_AUTOCOMPLETE, | |
| 791 CalculateRelevance(INLINE_AUTOCOMPLETE, 0))); | |
| 792 } | |
| 793 | |
| 813 void HistoryURLProvider::QueryComplete( | 794 void HistoryURLProvider::QueryComplete( |
| 814 HistoryURLProviderParams* params_gets_deleted) { | 795 HistoryURLProviderParams* params_gets_deleted) { |
| 815 // Ensure |params_gets_deleted| gets deleted on exit. | 796 // Ensure |params_gets_deleted| gets deleted on exit. |
| 816 scoped_ptr<HistoryURLProviderParams> params(params_gets_deleted); | 797 scoped_ptr<HistoryURLProviderParams> params(params_gets_deleted); |
| 817 | 798 |
| 818 // If the user hasn't already started another query, clear our member pointer | 799 // If the user hasn't already started another query, clear our member pointer |
| 819 // so we can't write into deleted memory. | 800 // so we can't write into deleted memory. |
| 820 if (params_ == params_gets_deleted) | 801 if (params_ == params_gets_deleted) |
| 821 params_ = NULL; | 802 params_ = NULL; |
| 822 | 803 |
| 823 // Don't send responses for queries that have been canceled. | 804 // Don't send responses for queries that have been canceled. |
| 824 if (params->cancel_flag.IsSet()) | 805 if (params->cancel_flag.IsSet()) |
| 825 return; // Already set done_ when we canceled, no need to set it again. | 806 return; // Already set done_ when we canceled, no need to set it again. |
| 826 | 807 |
| 827 // Don't modify |matches_| if the query failed, since it might have a default | 808 // Don't modify |matches_| if the query failed, since it might have a default |
| 828 // match in it, whereas |params->matches| will be empty. | 809 // match in it, whereas |params->matches| will be empty. |
| 829 if (!params->failed) { | 810 if (!params->failed) { |
| 830 matches_.swap(params->matches); | 811 matches_.clear(); |
| 812 PromoteMatchIfNecessary(*params); | |
| 813 | |
| 814 // Determine relevance of highest scoring match, if any. | |
| 815 int relevance = matches_.empty() ? | |
| 816 CalculateRelevance(NORMAL, | |
| 817 static_cast<int>(params->matches.size() - 1)) : | |
| 818 matches_[0].relevance; | |
| 819 | |
| 820 // Convert the history matches to autocomplete matches. If we promoted the | |
| 821 // first match, skip over it. | |
| 822 const size_t first_match = | |
| 823 (params->exact_suggestion_is_in_history || | |
| 824 (params->promote_type == | |
| 825 HistoryURLProviderParams::FRONT_HISTORY_MATCH)) ? 1 : 0; | |
| 826 for (size_t i = first_match; i < params->matches.size(); ++i) { | |
| 827 // All matches score one less than the previous match. | |
| 828 --relevance; | |
| 829 // The experimental scoring must not change the top result's score. | |
| 830 if (!matches_.empty()) { | |
| 831 relevance = CalculateRelevanceScoreUsingScoringParams( | |
| 832 params->matches[i], relevance, scoring_params_); | |
| 833 } | |
| 834 matches_.push_back(HistoryMatchToACMatch(*params, i, NORMAL, relevance)); | |
| 835 } | |
| 836 | |
| 831 UpdateStarredStateOfMatches(); | 837 UpdateStarredStateOfMatches(); |
| 832 } | 838 } |
| 833 | 839 |
| 834 done_ = true; | 840 done_ = true; |
| 835 listener_->OnProviderUpdate(true); | 841 listener_->OnProviderUpdate(true); |
| 836 } | 842 } |
| 837 | 843 |
| 838 bool HistoryURLProvider::FixupExactSuggestion( | 844 bool HistoryURLProvider::FixupExactSuggestion( |
| 839 history::URLDatabase* db, | 845 history::URLDatabase* db, |
| 840 const VisitClassifier& classifier, | 846 const VisitClassifier& classifier, |
| 841 HistoryURLProviderParams* params, | 847 HistoryURLProviderParams* params) const { |
| 842 history::HistoryMatches* matches) const { | |
| 843 DCHECK(matches != NULL); | |
| 844 | |
| 845 MatchType type = INLINE_AUTOCOMPLETE; | 848 MatchType type = INLINE_AUTOCOMPLETE; |
| 846 switch (classifier.type()) { | 849 switch (classifier.type()) { |
| 847 case VisitClassifier::INVALID: | 850 case VisitClassifier::INVALID: |
| 848 return false; | 851 return false; |
| 849 case VisitClassifier::UNVISITED_INTRANET: | 852 case VisitClassifier::UNVISITED_INTRANET: |
| 850 type = UNVISITED_INTRANET; | 853 type = UNVISITED_INTRANET; |
| 851 break; | 854 break; |
| 852 default: | 855 default: |
| 853 DCHECK_EQ(VisitClassifier::VISITED, classifier.type()); | 856 DCHECK_EQ(VisitClassifier::VISITED, classifier.type()); |
| 854 // We have data for this match, use it. | 857 // We have data for this match, use it. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 897 return false; | 900 return false; |
| 898 } | 901 } |
| 899 | 902 |
| 900 params->what_you_typed_match.relevance = CalculateRelevance(type, 0); | 903 params->what_you_typed_match.relevance = CalculateRelevance(type, 0); |
| 901 | 904 |
| 902 // If there are any other matches, then don't promote this match here, in | 905 // If there are any other matches, then don't promote this match here, in |
| 903 // hopes the caller will be able to inline autocomplete a better suggestion. | 906 // hopes the caller will be able to inline autocomplete a better suggestion. |
| 904 // DoAutocomplete() will fall back on this match if inline autocompletion | 907 // DoAutocomplete() will fall back on this match if inline autocompletion |
| 905 // fails. This matches how we react to never-visited URL inputs in the non- | 908 // fails. This matches how we react to never-visited URL inputs in the non- |
| 906 // intranet case. | 909 // intranet case. |
| 907 if (type == UNVISITED_INTRANET && !matches->empty()) | 910 if (type == UNVISITED_INTRANET && !params->matches.empty()) |
| 908 return false; | 911 return false; |
| 909 | 912 |
| 910 // Put it on the front of the HistoryMatches for redirect culling. | 913 // Put it on the front of the HistoryMatches for redirect culling. |
| 911 CreateOrPromoteMatch(classifier.url_row(), base::string16::npos, false, | 914 CreateOrPromoteMatch(classifier.url_row(), base::string16::npos, false, |
| 912 matches, true, true); | 915 ¶ms->matches, true, true); |
| 913 return true; | 916 return true; |
| 914 } | 917 } |
| 915 | 918 |
| 916 bool HistoryURLProvider::CanFindIntranetURL( | 919 bool HistoryURLProvider::CanFindIntranetURL( |
| 917 history::URLDatabase* db, | 920 history::URLDatabase* db, |
| 918 const AutocompleteInput& input) const { | 921 const AutocompleteInput& input) const { |
| 919 // Normally passing the first two conditions below ought to guarantee the | 922 // Normally passing the first two conditions below ought to guarantee the |
| 920 // third condition, but because FixupUserInput() can run and modify the | 923 // third condition, but because FixupUserInput() can run and modify the |
| 921 // input's text and parts between Parse() and here, it seems better to be | 924 // input's text and parts between Parse() and here, it seems better to be |
| 922 // paranoid and check. | 925 // paranoid and check. |
| 923 if ((input.type() != metrics::OmniboxInputType::UNKNOWN) || | 926 if ((input.type() != metrics::OmniboxInputType::UNKNOWN) || |
| 924 !LowerCaseEqualsASCII(input.scheme(), url::kHttpScheme) || | 927 !LowerCaseEqualsASCII(input.scheme(), url::kHttpScheme) || |
| 925 !input.parts().host.is_nonempty()) | 928 !input.parts().host.is_nonempty()) |
| 926 return false; | 929 return false; |
| 927 const std::string host(base::UTF16ToUTF8( | 930 const std::string host(base::UTF16ToUTF8( |
| 928 input.text().substr(input.parts().host.begin, input.parts().host.len))); | 931 input.text().substr(input.parts().host.begin, input.parts().host.len))); |
| 929 const size_t registry_length = | 932 const size_t registry_length = |
| 930 net::registry_controlled_domains::GetRegistryLength( | 933 net::registry_controlled_domains::GetRegistryLength( |
| 931 host, | 934 host, |
| 932 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, | 935 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, |
| 933 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); | 936 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); |
| 934 return registry_length == 0 && db->IsTypedHost(host); | 937 return registry_length == 0 && db->IsTypedHost(host); |
| 935 } | 938 } |
| 936 | 939 |
| 937 bool HistoryURLProvider::PromoteMatchForInlineAutocomplete( | 940 bool HistoryURLProvider::PromoteOrCreateShorterSuggestion( |
| 938 const history::HistoryMatch& match, | 941 history::URLDatabase* db, |
| 942 bool have_what_you_typed_match, | |
| 939 HistoryURLProviderParams* params) { | 943 HistoryURLProviderParams* params) { |
| 940 if (!CanPromoteMatchForInlineAutocomplete(match)) | 944 if (params->matches.empty()) |
| 941 return false; | 945 return false; // No matches, nothing to do. |
| 942 | |
| 943 // In the case where the user has typed "foo.com" and visited (but not typed) | |
| 944 // "foo/", and the input is "foo", we can reach here for "foo.com" during the | |
| 945 // first pass but have the second pass suggest the exact input as a better | |
| 946 // URL. Since we need both passes to agree, and since during the first pass | |
| 947 // there's no way to know about "foo/", make reaching this point prevent any | |
| 948 // future pass from suggesting the exact input as a better match. | |
| 949 params->dont_suggest_exact_input = true; | |
| 950 params->matches.push_back(HistoryMatchToACMatch( | |
| 951 *params, match, INLINE_AUTOCOMPLETE, | |
| 952 CalculateRelevance(INLINE_AUTOCOMPLETE, 0))); | |
| 953 return true; | |
| 954 } | |
| 955 | |
| 956 void HistoryURLProvider::PromoteOrCreateShorterSuggestion( | |
| 957 history::URLDatabase* db, | |
| 958 const HistoryURLProviderParams& params, | |
| 959 bool have_what_you_typed_match, | |
| 960 history::HistoryMatches* matches) { | |
| 961 if (matches->empty()) | |
| 962 return; // No matches, nothing to do. | |
| 963 | 946 |
| 964 // Determine the base URL from which to search, and whether that URL could | 947 // Determine the base URL from which to search, and whether that URL could |
| 965 // itself be added as a match. We can add the base iff it's not "effectively | 948 // itself be added as a match. We can add the base iff it's not "effectively |
| 966 // the same" as any "what you typed" match. | 949 // the same" as any "what you typed" match. |
| 967 const history::HistoryMatch& match = matches->front(); | 950 const history::HistoryMatch& match = params->matches[0]; |
| 968 GURL search_base = ConvertToHostOnly(match, params.input.text()); | 951 GURL search_base = ConvertToHostOnly(match, params->input.text()); |
| 969 bool can_add_search_base_to_matches = !have_what_you_typed_match; | 952 bool can_add_search_base_to_matches = !have_what_you_typed_match; |
| 970 if (search_base.is_empty()) { | 953 if (search_base.is_empty()) { |
| 971 // Search from what the user typed when we couldn't reduce the best match | 954 // Search from what the user typed when we couldn't reduce the best match |
| 972 // to a host. Careful: use a substring of |match| here, rather than the | 955 // to a host. Careful: use a substring of |match| here, rather than the |
| 973 // first match in |params|, because they might have different prefixes. If | 956 // first match in |params|, because they might have different prefixes. If |
| 974 // the user typed "google.com", params->what_you_typed_match will hold | 957 // the user typed "google.com", params->what_you_typed_match will hold |
| 975 // "http://google.com/", but |match| might begin with | 958 // "http://google.com/", but |match| might begin with |
| 976 // "http://www.google.com/". | 959 // "http://www.google.com/". |
| 977 // TODO: this should be cleaned up, and is probably incorrect for IDN. | 960 // TODO: this should be cleaned up, and is probably incorrect for IDN. |
| 978 std::string new_match = match.url_info.url().possibly_invalid_spec(). | 961 std::string new_match = match.url_info.url().possibly_invalid_spec(). |
| 979 substr(0, match.input_location + params.input.text().length()); | 962 substr(0, match.input_location + params->input.text().length()); |
| 980 search_base = GURL(new_match); | 963 search_base = GURL(new_match); |
| 981 if (search_base.is_empty()) | 964 if (search_base.is_empty()) |
| 982 return; // Can't construct a valid URL from which to start a search. | 965 return false; // Can't construct a URL from which to start a search. |
| 983 } else if (!can_add_search_base_to_matches) { | 966 } else if (!can_add_search_base_to_matches) { |
| 984 can_add_search_base_to_matches = | 967 can_add_search_base_to_matches = |
| 985 (search_base != params.what_you_typed_match.destination_url); | 968 (search_base != params->what_you_typed_match.destination_url); |
| 986 } | 969 } |
| 987 if (search_base == match.url_info.url()) | 970 if (search_base == match.url_info.url()) |
| 988 return; // Couldn't shorten |match|, so no range of URLs to search over. | 971 return false; // Couldn't shorten |match|, so no URLs to search over. |
| 989 | 972 |
| 990 // Search the DB for short URLs between our base and |match|. | 973 // Search the DB for short URLs between our base and |match|. |
| 991 history::URLRow info(search_base); | 974 history::URLRow info(search_base); |
| 992 bool promote = true; | 975 bool promote = true; |
| 993 // A short URL is only worth suggesting if it's been visited at least a third | 976 // A short URL is only worth suggesting if it's been visited at least a third |
| 994 // as often as the longer URL. | 977 // as often as the longer URL. |
| 995 const int min_visit_count = ((match.url_info.visit_count() - 1) / 3) + 1; | 978 const int min_visit_count = ((match.url_info.visit_count() - 1) / 3) + 1; |
| 996 // For stability between the in-memory and on-disk autocomplete passes, when | 979 // For stability between the in-memory and on-disk autocomplete passes, when |
| 997 // the long URL has been typed before, only suggest shorter URLs that have | 980 // the long URL has been typed before, only suggest shorter URLs that have |
| 998 // also been typed. Otherwise, the on-disk pass could suggest a shorter URL | 981 // also been typed. Otherwise, the on-disk pass could suggest a shorter URL |
| 999 // (which hasn't been typed) that the in-memory pass doesn't know about, | 982 // (which hasn't been typed) that the in-memory pass doesn't know about, |
| 1000 // thereby making the top match, and thus the behavior of inline | 983 // thereby making the top match, and thus the behavior of inline |
| 1001 // autocomplete, unstable. | 984 // autocomplete, unstable. |
| 1002 const int min_typed_count = match.url_info.typed_count() ? 1 : 0; | 985 const int min_typed_count = match.url_info.typed_count() ? 1 : 0; |
| 1003 if (!db->FindShortestURLFromBase(search_base.possibly_invalid_spec(), | 986 if (!db->FindShortestURLFromBase(search_base.possibly_invalid_spec(), |
| 1004 match.url_info.url().possibly_invalid_spec(), min_visit_count, | 987 match.url_info.url().possibly_invalid_spec(), min_visit_count, |
| 1005 min_typed_count, can_add_search_base_to_matches, &info)) { | 988 min_typed_count, can_add_search_base_to_matches, &info)) { |
| 1006 if (!can_add_search_base_to_matches) | 989 if (!can_add_search_base_to_matches) |
| 1007 return; // Couldn't find anything and can't add the search base, bail. | 990 return false; // Couldn't find anything and can't add the search base. |
| 1008 | 991 |
| 1009 // Try to get info on the search base itself. Promote it to the top if the | 992 // Try to get info on the search base itself. Promote it to the top if the |
| 1010 // original best match isn't good enough to autocomplete. | 993 // original best match isn't good enough to autocomplete. |
| 1011 db->GetRowForURL(search_base, &info); | 994 db->GetRowForURL(search_base, &info); |
| 1012 promote = match.url_info.typed_count() <= 1; | 995 promote = match.url_info.typed_count() <= 1; |
| 1013 } | 996 } |
| 1014 | 997 |
| 1015 // Promote or add the desired URL to the list of matches. | 998 // Promote or add the desired URL to the list of matches. |
| 1016 bool ensure_can_inline = | 999 const bool ensure_can_inline = |
| 1017 promote && CanPromoteMatchForInlineAutocomplete(match); | 1000 promote && CanPromoteMatchForInlineAutocomplete(match); |
| 1018 ensure_can_inline &= CreateOrPromoteMatch(info, match.input_location, | 1001 return CreateOrPromoteMatch(info, match.input_location, match.match_in_scheme, |
| 1019 match.match_in_scheme, matches, create_shorter_match_, promote); | 1002 ¶ms->matches, create_shorter_match_, |
| 1020 if (ensure_can_inline) | 1003 promote) && |
| 1021 matches->front().promoted = true; | 1004 ensure_can_inline; |
|
Mark P
2014/06/19 21:35:14
Please leave things in the other order: i.e., ensu
Peter Kasting
2014/06/19 22:27:12
Discussed in person why this is correct.
| |
| 1022 } | 1005 } |
| 1023 | 1006 |
| 1024 void HistoryURLProvider::CullPoorMatches( | 1007 void HistoryURLProvider::CullPoorMatches( |
| 1025 const HistoryURLProviderParams& params, | 1008 HistoryURLProviderParams* params) const { |
| 1026 history::HistoryMatches* matches) const { | |
| 1027 const base::Time& threshold(history::AutocompleteAgeThreshold()); | 1009 const base::Time& threshold(history::AutocompleteAgeThreshold()); |
| 1028 for (history::HistoryMatches::iterator i(matches->begin()); | 1010 for (history::HistoryMatches::iterator i(params->matches.begin()); |
| 1029 i != matches->end(); ) { | 1011 i != params->matches.end(); ) { |
| 1030 if (RowQualifiesAsSignificant(i->url_info, threshold) && | 1012 if (RowQualifiesAsSignificant(i->url_info, threshold) && |
| 1031 !(params.default_search_provider && | 1013 (!params->default_search_provider || |
| 1032 params.default_search_provider->IsSearchURL( | 1014 !params->default_search_provider->IsSearchURL( |
| 1033 i->url_info.url(), *params.search_terms_data.get()))) { | 1015 i->url_info.url(), *params->search_terms_data))) { |
| 1034 ++i; | 1016 ++i; |
| 1035 } else { | 1017 } else { |
| 1036 i = matches->erase(i); | 1018 i = params->matches.erase(i); |
| 1037 } | 1019 } |
| 1038 } | 1020 } |
| 1039 } | 1021 } |
| 1040 | 1022 |
| 1041 void HistoryURLProvider::CullRedirects(history::HistoryBackend* backend, | 1023 void HistoryURLProvider::CullRedirects(history::HistoryBackend* backend, |
| 1042 history::HistoryMatches* matches, | 1024 history::HistoryMatches* matches, |
| 1043 size_t max_results) const { | 1025 size_t max_results) const { |
| 1044 for (size_t source = 0; | 1026 for (size_t source = 0; |
| 1045 (source < matches->size()) && (source < max_results); ) { | 1027 (source < matches->size()) && (source < max_results); ) { |
| 1046 const GURL& url = (*matches)[source].url_info.url(); | 1028 const GURL& url = (*matches)[source].url_info.url(); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1093 // need to shift it to the right and remember that so we can return it. | 1075 // need to shift it to the right and remember that so we can return it. |
| 1094 next = matches->erase(next); | 1076 next = matches->erase(next); |
| 1095 if (static_cast<size_t>(next - matches->begin()) < next_index) | 1077 if (static_cast<size_t>(next - matches->begin()) < next_index) |
| 1096 --next_index; | 1078 --next_index; |
| 1097 } | 1079 } |
| 1098 return next_index; | 1080 return next_index; |
| 1099 } | 1081 } |
| 1100 | 1082 |
| 1101 AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( | 1083 AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( |
| 1102 const HistoryURLProviderParams& params, | 1084 const HistoryURLProviderParams& params, |
| 1103 const history::HistoryMatch& history_match, | 1085 size_t match_number, |
| 1104 MatchType match_type, | 1086 MatchType match_type, |
| 1105 int relevance) { | 1087 int relevance) { |
| 1088 // The FormattedStringWithEquivalentMeaning() call below requires callers to | |
| 1089 // be on the UI thread. | |
| 1090 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) || | |
| 1091 !content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI)); | |
| 1092 | |
| 1093 const history::HistoryMatch& history_match = params.matches[match_number]; | |
| 1106 const history::URLRow& info = history_match.url_info; | 1094 const history::URLRow& info = history_match.url_info; |
| 1107 AutocompleteMatch match(this, relevance, | 1095 AutocompleteMatch match(this, relevance, |
| 1108 !!info.visit_count(), AutocompleteMatchType::HISTORY_URL); | 1096 !!info.visit_count(), AutocompleteMatchType::HISTORY_URL); |
| 1109 match.typed_count = info.typed_count(); | 1097 match.typed_count = info.typed_count(); |
| 1110 match.destination_url = info.url(); | 1098 match.destination_url = info.url(); |
| 1111 DCHECK(match.destination_url.is_valid()); | 1099 DCHECK(match.destination_url.is_valid()); |
| 1112 size_t inline_autocomplete_offset = | 1100 size_t inline_autocomplete_offset = |
| 1113 history_match.input_location + params.input.text().length(); | 1101 history_match.input_location + params.input.text().length(); |
| 1114 std::string languages = (match_type == WHAT_YOU_TYPED) ? | 1102 std::string languages = (match_type == WHAT_YOU_TYPED) ? |
| 1115 std::string() : params.languages; | 1103 std::string() : params.languages; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1147 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, | 1135 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, |
| 1148 match.contents.length(), ACMatchClassification::URL, | 1136 match.contents.length(), ACMatchClassification::URL, |
| 1149 &match.contents_class); | 1137 &match.contents_class); |
| 1150 } | 1138 } |
| 1151 match.description = info.title(); | 1139 match.description = info.title(); |
| 1152 match.description_class = | 1140 match.description_class = |
| 1153 ClassifyDescription(params.input.text(), match.description); | 1141 ClassifyDescription(params.input.text(), match.description); |
| 1154 RecordAdditionalInfoFromUrlRow(info, &match); | 1142 RecordAdditionalInfoFromUrlRow(info, &match); |
| 1155 return match; | 1143 return match; |
| 1156 } | 1144 } |
| OLD | NEW |