| Index: chrome/browser/autocomplete/history_url_provider.cc
|
| diff --git a/chrome/browser/autocomplete/history_url_provider.cc b/chrome/browser/autocomplete/history_url_provider.cc
|
| index 1a61a72a047faaf5812f2613240931694db3aad4..f46eb9686592353677f3a8c7829d49808ad18c82 100644
|
| --- a/chrome/browser/autocomplete/history_url_provider.cc
|
| +++ b/chrome/browser/autocomplete/history_url_provider.cc
|
| @@ -37,6 +37,7 @@
|
| #include "components/bookmarks/browser/bookmark_utils.h"
|
| #include "components/metrics/proto/omnibox_input_type.pb.h"
|
| #include "components/url_fixer/url_fixer.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| #include "net/base/net_util.h"
|
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
| #include "url/gurl.h"
|
| @@ -229,6 +230,20 @@ bool CreateOrPromoteMatch(const history::URLRow& info,
|
| return true;
|
| }
|
|
|
| +// Returns whether |match| is suitable for inline autocompletion.
|
| +bool CanPromoteMatchForInlineAutocomplete(const history::HistoryMatch& match) {
|
| + // We can promote this match if it's been marked for promotion or typed at
|
| + // least n times, where n == 1 for "simple" (host-only) URLs and n == 2 for
|
| + // others. We set a higher bar for these long URLs because it's less likely
|
| + // that users will want to visit them again. Even though we don't increment
|
| + // the typed_count for pasted-in URLs, if the user manually edits the URL or
|
| + // types some long thing in by hand, we wouldn't want to immediately start
|
| + // autocompleting it.
|
| + return match.promoted ||
|
| + (match.url_info.typed_count() &&
|
| + ((match.url_info.typed_count() > 1) || match.IsHostOnly()));
|
| +}
|
| +
|
| // Given the user's |input| and a |match| created from it, reduce the match's
|
| // URL to just a host. If this host still matches the user input, return it.
|
| // Returns the empty string on failure.
|
| @@ -392,6 +407,7 @@ HistoryURLProvider::VisitClassifier::VisitClassifier(
|
| HistoryURLProviderParams::HistoryURLProviderParams(
|
| const AutocompleteInput& input,
|
| bool trim_http,
|
| + const AutocompleteMatch& what_you_typed_match,
|
| const std::string& languages,
|
| TemplateURL* default_search_provider,
|
| const SearchTermsData& search_terms_data)
|
| @@ -399,6 +415,7 @@ HistoryURLProviderParams::HistoryURLProviderParams(
|
| input(input),
|
| prevent_inline_autocomplete(input.prevent_inline_autocomplete()),
|
| trim_http(trim_http),
|
| + what_you_typed_match(what_you_typed_match),
|
| failed(false),
|
| languages(languages),
|
| dont_suggest_exact_input(false),
|
| @@ -414,7 +431,7 @@ HistoryURLProviderParams::~HistoryURLProviderParams() {
|
| HistoryURLProvider::HistoryURLProvider(AutocompleteProviderListener* listener,
|
| Profile* profile)
|
| : HistoryProvider(listener, profile,
|
| - AutocompleteProvider::TYPE_HISTORY_URL),
|
| + AutocompleteProvider::TYPE_HISTORY_URL),
|
| params_(NULL),
|
| cull_redirects_(
|
| !OmniboxFieldTrial::InHUPCullRedirectsFieldTrial() ||
|
| @@ -447,18 +464,28 @@ void HistoryURLProvider::Start(const AutocompleteInput& input,
|
| (input.type() == metrics::OmniboxInputType::FORCED_QUERY))
|
| return;
|
|
|
| - // Create a match for exactly what the user typed. This will only be used as
|
| - // a fallback in case we can't get the history service or URL DB; otherwise,
|
| - // we'll run this again in DoAutocomplete() and use that result instead.
|
| + // Do some fixup on the user input before matching against it, so we provide
|
| + // good results for local file paths, input with spaces, etc.
|
| + const FixupReturn fixup_return(FixupUserInput(input));
|
| + if (!fixup_return.first)
|
| + return;
|
| + url::Parsed parts;
|
| + url_fixer::SegmentURL(fixup_return.second, &parts);
|
| + AutocompleteInput fixed_up_input(input);
|
| + fixed_up_input.UpdateText(fixup_return.second, base::string16::npos, parts);
|
| +
|
| + // Create a match for what the user typed.
|
| const bool trim_http = !AutocompleteInput::HasHTTPScheme(input.text());
|
| - // Don't do this for queries -- while we can sometimes mark up a match for
|
| - // this, it's not what the user wants, and just adds noise.
|
| - if (input.type() != metrics::OmniboxInputType::QUERY) {
|
| - AutocompleteMatch what_you_typed(SuggestExactInput(
|
| - input.text(), input.canonicalized_url(), trim_http));
|
| - what_you_typed.relevance = CalculateRelevance(WHAT_YOU_TYPED, 0);
|
| - matches_.push_back(what_you_typed);
|
| - }
|
| + AutocompleteMatch what_you_typed_match(SuggestExactInput(
|
| + fixed_up_input.text(), fixed_up_input.canonicalized_url(), trim_http));
|
| + what_you_typed_match.relevance = CalculateRelevance(WHAT_YOU_TYPED, 0);
|
| +
|
| + // Add the WYT match as a fallback in case we can't get the history service or
|
| + // URL DB; otherwise, we'll replace this match lower down. Don't do this for
|
| + // queries, though -- while we can sometimes mark up a match for them, it's
|
| + // not what the user wants, and just adds noise.
|
| + if (fixed_up_input.type() != metrics::OmniboxInputType::QUERY)
|
| + matches_.push_back(what_you_typed_match);
|
|
|
| // We'll need the history service to run both passes, so try to obtain it.
|
| if (!profile_)
|
| @@ -477,22 +504,12 @@ void HistoryURLProvider::Start(const AutocompleteInput& input,
|
| template_url_service->GetDefaultSearchProvider() : NULL;
|
| UIThreadSearchTermsData data(profile_);
|
|
|
| - // Do some fixup on the user input before matching against it, so we provide
|
| - // good results for local file paths, input with spaces, etc.
|
| - const FixupReturn fixup_return(FixupUserInput(input));
|
| - if (!fixup_return.first)
|
| - return;
|
| - url::Parsed parts;
|
| - url_fixer::SegmentURL(fixup_return.second, &parts);
|
| - AutocompleteInput fixed_up_input(input);
|
| - fixed_up_input.UpdateText(fixup_return.second, base::string16::npos, parts);
|
| -
|
| // Create the data structure for the autocomplete passes. We'll save this off
|
| // onto the |params_| member for later deletion below if we need to run pass
|
| // 2.
|
| scoped_ptr<HistoryURLProviderParams> params(
|
| new HistoryURLProviderParams(
|
| - fixed_up_input, trim_http,
|
| + fixed_up_input, trim_http, what_you_typed_match,
|
| profile_->GetPrefs()->GetString(prefs::kAcceptLanguages),
|
| default_search_provider, data));
|
| // Note that we use the non-fixed-up input here, since fixup may strip
|
| @@ -516,6 +533,9 @@ void HistoryURLProvider::Start(const AutocompleteInput& input,
|
| matches_.clear();
|
| matches_.swap(params->matches);
|
| UpdateStarredStateOfMatches();
|
| + // Reset the WYT match in |params| so that both passes get the same input
|
| + // state, since DoAutocomplete() may have modified it.
|
| + params->what_you_typed_match = what_you_typed_match;
|
| }
|
|
|
| // Pass 2: Ask the history service to call us back on the history thread,
|
| @@ -539,6 +559,11 @@ AutocompleteMatch HistoryURLProvider::SuggestExactInput(
|
| const base::string16& text,
|
| const GURL& destination_url,
|
| bool trim_http) {
|
| + // The FormattedStringWithEquivalentMeaning() call below requires callers to
|
| + // be on the UI thread.
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) ||
|
| + !content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI));
|
| +
|
| AutocompleteMatch match(this, 0, false,
|
| AutocompleteMatchType::URL_WHAT_YOU_TYPED);
|
|
|
| @@ -664,37 +689,16 @@ ACMatchClassifications HistoryURLProvider::ClassifyDescription(
|
| void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend,
|
| history::URLDatabase* db,
|
| HistoryURLProviderParams* params) {
|
| - VisitClassifier classifier(this, params->input, db);
|
| - // Create a What You Typed match, which we'll need below.
|
| - //
|
| - // We display this to the user when there's a reasonable chance they actually
|
| - // care:
|
| - // * Their input can be opened as a URL, and
|
| - // * We parsed the input as a URL, or it starts with an explicit "http:" or
|
| - // "https:".
|
| - // that is when their input can be opened as a URL.
|
| - // Otherwise, this is just low-quality noise. In the cases where we've parsed
|
| - // as UNKNOWN, we'll still show an accidental search infobar if need be.
|
| - bool have_what_you_typed_match =
|
| - (params->input.type() != metrics::OmniboxInputType::QUERY) &&
|
| - ((params->input.type() != metrics::OmniboxInputType::UNKNOWN) ||
|
| - (classifier.type() == VisitClassifier::UNVISITED_INTRANET) ||
|
| - !params->trim_http ||
|
| - (AutocompleteInput::NumNonHostComponents(params->input.parts()) > 0));
|
| - AutocompleteMatch what_you_typed_match(SuggestExactInput(
|
| - params->input.text(), params->input.canonicalized_url(),
|
| - params->trim_http));
|
| - what_you_typed_match.relevance = CalculateRelevance(WHAT_YOU_TYPED, 0);
|
| -
|
| - // Get the matching URLs from the DB
|
| + // Get the matching URLs from the DB.
|
| history::URLRows url_matches;
|
| history::HistoryMatches history_matches;
|
|
|
| const URLPrefixes& prefixes = URLPrefix::GetURLPrefixes();
|
| for (URLPrefixes::const_iterator i(prefixes.begin()); i != prefixes.end();
|
| - ++i) {
|
| + ++i) {
|
| if (params->cancel_flag.IsSet())
|
| return; // Canceled in the middle of a query, give up.
|
| +
|
| // We only need kMaxMatches results in the end, but before we get there we
|
| // need to promote lower-quality matches that are prefixes of higher-quality
|
| // matches, and remove lower-quality redirects. So we ask for more results
|
| @@ -702,18 +706,15 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend,
|
| // than enough to work with. CullRedirects() will then reduce the list to
|
| // the best kMaxMatches results.
|
| db->AutocompleteForPrefix(
|
| - base::UTF16ToUTF8(i->prefix + params->input.text()),
|
| - kMaxMatches * 2,
|
| - (backend == NULL),
|
| - &url_matches);
|
| + base::UTF16ToUTF8(i->prefix + params->input.text()), kMaxMatches * 2,
|
| + !backend, &url_matches);
|
| for (history::URLRows::const_iterator j(url_matches.begin());
|
| j != url_matches.end(); ++j) {
|
| - const URLPrefix* best_prefix =
|
| - URLPrefix::BestURLPrefix(base::UTF8ToUTF16(j->url().spec()),
|
| - base::string16());
|
| - DCHECK(best_prefix != NULL);
|
| - history_matches.push_back(history::HistoryMatch(*j, i->prefix.length(),
|
| - i->num_components == 0,
|
| + const URLPrefix* best_prefix = URLPrefix::BestURLPrefix(
|
| + base::UTF8ToUTF16(j->url().spec()), base::string16());
|
| + DCHECK(best_prefix);
|
| + history_matches.push_back(history::HistoryMatch(
|
| + *j, i->prefix.length(), !i->num_components,
|
| i->num_components >= best_prefix->num_components));
|
| }
|
| }
|
| @@ -721,24 +722,39 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend,
|
| // Create sorted list of suggestions.
|
| CullPoorMatches(*params, &history_matches);
|
| SortAndDedupMatches(&history_matches);
|
| +
|
| + // Try to create a shorter suggestion from the best match.
|
| + // We allow the what you typed match to be displayed when there's a reasonable
|
| + // chance the user actually cares:
|
| + // * Their input can be opened as a URL, and
|
| + // * We parsed the input as a URL, or it starts with an explicit "http:" or
|
| + // "https:".
|
| + // Otherwise, this is just low-quality noise. In the cases where we've parsed
|
| + // as UNKNOWN, we'll still show an accidental search infobar if need be.
|
| + VisitClassifier classifier(this, params->input, db);
|
| + bool have_what_you_typed_match =
|
| + (params->input.type() != metrics::OmniboxInputType::QUERY) &&
|
| + ((params->input.type() != metrics::OmniboxInputType::UNKNOWN) ||
|
| + (classifier.type() == VisitClassifier::UNVISITED_INTRANET) ||
|
| + !params->trim_http ||
|
| + (AutocompleteInput::NumNonHostComponents(params->input.parts()) > 0));
|
| PromoteOrCreateShorterSuggestion(db, *params, have_what_you_typed_match,
|
| - what_you_typed_match, &history_matches);
|
| + &history_matches);
|
|
|
| // Try to promote a match as an exact/inline autocomplete match. This also
|
| // moves it to the front of |history_matches|, so skip over it when
|
| // converting the rest of the matches.
|
| size_t first_match = 1;
|
| size_t exact_suggestion = 0;
|
| - // Checking |is_history_what_you_typed_match| tells us whether
|
| - // SuggestExactInput() succeeded in constructing a valid match.
|
| - if (what_you_typed_match.is_history_what_you_typed_match &&
|
| + // Checking params->what_you_typed_match.is_history_what_you_typed_match tells
|
| + // us whether SuggestExactInput() succeeded in constructing a valid match.
|
| + if (params->what_you_typed_match.is_history_what_you_typed_match &&
|
| (!backend || !params->dont_suggest_exact_input) &&
|
| - FixupExactSuggestion(db, params->input, classifier, &what_you_typed_match,
|
| - &history_matches)) {
|
| + FixupExactSuggestion(db, classifier, params, &history_matches)) {
|
| // Got an exact match for the user's input. Treat it as the best match
|
| // regardless of the input type.
|
| exact_suggestion = 1;
|
| - params->matches.push_back(what_you_typed_match);
|
| + params->matches.push_back(params->what_you_typed_match);
|
| } else if (params->prevent_inline_autocomplete ||
|
| history_matches.empty() ||
|
| !PromoteMatchForInlineAutocomplete(history_matches.front(), params)) {
|
| @@ -746,7 +762,7 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend,
|
| // Typed match, if we have it.
|
| first_match = 0;
|
| if (have_what_you_typed_match)
|
| - params->matches.push_back(what_you_typed_match);
|
| + params->matches.push_back(params->what_you_typed_match);
|
| }
|
|
|
| // This is the end of the synchronous pass.
|
| @@ -795,7 +811,6 @@ void HistoryURLProvider::DoAutocomplete(history::HistoryBackend* backend,
|
| }
|
| }
|
|
|
| -// Called on the main thread when the query is complete.
|
| void HistoryURLProvider::QueryComplete(
|
| HistoryURLProviderParams* params_gets_deleted) {
|
| // Ensure |params_gets_deleted| gets deleted on exit.
|
| @@ -823,11 +838,9 @@ void HistoryURLProvider::QueryComplete(
|
|
|
| bool HistoryURLProvider::FixupExactSuggestion(
|
| history::URLDatabase* db,
|
| - const AutocompleteInput& input,
|
| const VisitClassifier& classifier,
|
| - AutocompleteMatch* match,
|
| + HistoryURLProviderParams* params,
|
| history::HistoryMatches* matches) const {
|
| - DCHECK(match != NULL);
|
| DCHECK(matches != NULL);
|
|
|
| MatchType type = INLINE_AUTOCOMPLETE;
|
| @@ -840,24 +853,25 @@ bool HistoryURLProvider::FixupExactSuggestion(
|
| default:
|
| DCHECK_EQ(VisitClassifier::VISITED, classifier.type());
|
| // We have data for this match, use it.
|
| - match->deletable = true;
|
| - match->description = classifier.url_row().title();
|
| - RecordAdditionalInfoFromUrlRow(classifier.url_row(), match);
|
| - match->description_class =
|
| - ClassifyDescription(input.text(), match->description);
|
| + params->what_you_typed_match.deletable = true;
|
| + params->what_you_typed_match.description = classifier.url_row().title();
|
| + RecordAdditionalInfoFromUrlRow(classifier.url_row(),
|
| + ¶ms->what_you_typed_match);
|
| + params->what_you_typed_match.description_class = ClassifyDescription(
|
| + params->input.text(), params->what_you_typed_match.description);
|
| if (!classifier.url_row().typed_count()) {
|
| // If we reach here, we must be in the second pass, and we must not have
|
| // this row's data available during the first pass. That means we
|
| // either scored it as WHAT_YOU_TYPED or UNVISITED_INTRANET, and to
|
| // maintain the ordering between passes consistent, we need to score it
|
| // the same way here.
|
| - type = CanFindIntranetURL(db, input) ?
|
| + type = CanFindIntranetURL(db, params->input) ?
|
| UNVISITED_INTRANET : WHAT_YOU_TYPED;
|
| }
|
| break;
|
| }
|
|
|
| - const GURL& url = match->destination_url;
|
| + const GURL& url = params->what_you_typed_match.destination_url;
|
| const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
|
| // If the what-you-typed result looks like a single word (which can be
|
| // interpreted as an intranet address) followed by a pound sign ("#"),
|
| @@ -876,7 +890,7 @@ bool HistoryURLProvider::FixupExactSuggestion(
|
| // between the input "c" and the input "c#", both of which will have empty
|
| // reference fragments.)
|
| if ((type == UNVISITED_INTRANET) &&
|
| - (input.type() != metrics::OmniboxInputType::URL) &&
|
| + (params->input.type() != metrics::OmniboxInputType::URL) &&
|
| url.username().empty() && url.password().empty() && url.port().empty() &&
|
| (url.path() == "/") && url.query().empty() &&
|
| (parsed.CountCharactersBefore(url::Parsed::REF, true) !=
|
| @@ -884,7 +898,7 @@ bool HistoryURLProvider::FixupExactSuggestion(
|
| return false;
|
| }
|
|
|
| - match->relevance = CalculateRelevance(type, 0);
|
| + params->what_you_typed_match.relevance = CalculateRelevance(type, 0);
|
|
|
| // If there are any other matches, then don't promote this match here, in
|
| // hopes the caller will be able to inline autocomplete a better suggestion.
|
| @@ -924,17 +938,7 @@ bool HistoryURLProvider::CanFindIntranetURL(
|
| bool HistoryURLProvider::PromoteMatchForInlineAutocomplete(
|
| const history::HistoryMatch& match,
|
| HistoryURLProviderParams* params) {
|
| - // Promote the first match if it's been marked for promotion or typed at least
|
| - // n times, where n == 1 for "simple" (host-only) URLs and n == 2 for others.
|
| - // We set a higher bar for these long URLs because it's less likely that users
|
| - // will want to visit them again. Even though we don't increment the
|
| - // typed_count for pasted-in URLs, if the user manually edits the URL or types
|
| - // some long thing in by hand, we wouldn't want to immediately start
|
| - // autocompleting it.
|
| - if (!match.promoted &&
|
| - (!match.url_info.typed_count() ||
|
| - ((match.url_info.typed_count() == 1) &&
|
| - !match.IsHostOnly())))
|
| + if (!CanPromoteMatchForInlineAutocomplete(match))
|
| return false;
|
|
|
| // In the case where the user has typed "foo.com" and visited (but not typed)
|
| @@ -943,27 +947,17 @@ bool HistoryURLProvider::PromoteMatchForInlineAutocomplete(
|
| // URL. Since we need both passes to agree, and since during the first pass
|
| // there's no way to know about "foo/", make reaching this point prevent any
|
| // future pass from suggesting the exact input as a better match.
|
| - if (params) {
|
| - params->dont_suggest_exact_input = true;
|
| - AutocompleteMatch ac_match = HistoryMatchToACMatch(
|
| - *params, match, INLINE_AUTOCOMPLETE,
|
| - CalculateRelevance(INLINE_AUTOCOMPLETE, 0));
|
| - params->matches.push_back(ac_match);
|
| - }
|
| + params->dont_suggest_exact_input = true;
|
| + params->matches.push_back(HistoryMatchToACMatch(
|
| + *params, match, INLINE_AUTOCOMPLETE,
|
| + CalculateRelevance(INLINE_AUTOCOMPLETE, 0)));
|
| return true;
|
| }
|
|
|
| -// See if a shorter version of the best match should be created, and if so place
|
| -// it at the front of |matches|. This can suggest history URLs that are
|
| -// prefixes of the best match (if they've been visited enough, compared to the
|
| -// best match), or create host-only suggestions even when they haven't been
|
| -// visited before: if the user visited http://example.com/asdf once, we'll
|
| -// suggest http://example.com/ even if they've never been to it.
|
| void HistoryURLProvider::PromoteOrCreateShorterSuggestion(
|
| history::URLDatabase* db,
|
| const HistoryURLProviderParams& params,
|
| bool have_what_you_typed_match,
|
| - const AutocompleteMatch& what_you_typed_match,
|
| history::HistoryMatches* matches) {
|
| if (matches->empty())
|
| return; // No matches, nothing to do.
|
| @@ -978,21 +972,18 @@ void HistoryURLProvider::PromoteOrCreateShorterSuggestion(
|
| // Search from what the user typed when we couldn't reduce the best match
|
| // to a host. Careful: use a substring of |match| here, rather than the
|
| // first match in |params|, because they might have different prefixes. If
|
| - // the user typed "google.com", |what_you_typed_match| will hold
|
| + // the user typed "google.com", params->what_you_typed_match will hold
|
| // "http://google.com/", but |match| might begin with
|
| // "http://www.google.com/".
|
| // TODO: this should be cleaned up, and is probably incorrect for IDN.
|
| std::string new_match = match.url_info.url().possibly_invalid_spec().
|
| substr(0, match.input_location + params.input.text().length());
|
| search_base = GURL(new_match);
|
| - // TODO(mrossetti): There is a degenerate case where the following may
|
| - // cause a failure: http://www/~someword/fubar.html. Diagnose.
|
| - // See: http://crbug.com/50101
|
| if (search_base.is_empty())
|
| return; // Can't construct a valid URL from which to start a search.
|
| } else if (!can_add_search_base_to_matches) {
|
| can_add_search_base_to_matches =
|
| - (search_base != what_you_typed_match.destination_url);
|
| + (search_base != params.what_you_typed_match.destination_url);
|
| }
|
| if (search_base == match.url_info.url())
|
| return; // Couldn't shorten |match|, so no range of URLs to search over.
|
| @@ -1024,7 +1015,7 @@ void HistoryURLProvider::PromoteOrCreateShorterSuggestion(
|
|
|
| // Promote or add the desired URL to the list of matches.
|
| bool ensure_can_inline =
|
| - promote && PromoteMatchForInlineAutocomplete(match, NULL);
|
| + promote && CanPromoteMatchForInlineAutocomplete(match);
|
| ensure_can_inline &= CreateOrPromoteMatch(info, match.input_location,
|
| match.match_in_scheme, matches, create_shorter_match_, promote);
|
| if (ensure_can_inline)
|
|
|