// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/autocomplete/history_quick_provider.h" #include "base/basictypes.h" #include "base/i18n/word_iterator.h" #include "base/string_util.h" #include "base/logging.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete/autocomplete_match.h" #include "chrome/browser/history/history.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/history/in_memory_url_index.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/plugin_service.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "googleurl/src/url_util.h" #include "net/base/escape.h" #include "net/base/net_util.h" using history::InMemoryURLIndex; using history::ScoredHistoryMatch; using history::ScoredHistoryMatches; HistoryQuickProvider::HistoryQuickProvider(ACProviderListener* listener, Profile* profile) : HistoryProvider(listener, profile, "HistoryQuickProvider"), trim_http_(false), languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)) {} HistoryQuickProvider::~HistoryQuickProvider() {} void HistoryQuickProvider::Start(const AutocompleteInput& input, bool minimal_changes) { matches_.clear(); if ((input.type() == AutocompleteInput::INVALID) || (input.type() == AutocompleteInput::FORCED_QUERY)) return; autocomplete_input_ = input; trim_http_ = !HasHTTPScheme(input.text()); // Do some fixup on the user input before matching against it, so we provide // good results for local file paths, input with spaces, etc. // NOTE: This purposefully doesn't take input.desired_tld() into account; if // it did, then holding "ctrl" would change all the results from the // HistoryQuickProvider provider, not just the What You Typed Result. const std::wstring fixed_text(FixupUserInput(input)); if (fixed_text.empty()) { // Conceivably fixup could result in an empty string (although I don't // have cases where this happens offhand). We can't do anything with // empty input, so just bail; otherwise we'd crash later. return; } autocomplete_input_.set_text(fixed_text); // TODO(pkasting): We should just block here until this loads. Any time // someone unloads the history backend, we'll get inconsistent inline // autocomplete behavior here. if (GetIndex()) { DoAutocomplete(); UpdateStarredStateOfMatches(); } } void HistoryQuickProvider::DoAutocomplete() { // Get the matching URLs from the DB. string16 term_string(WideToUTF16(autocomplete_input_.text())); term_string = UnescapeURLComponent(term_string, UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); history::InMemoryURLIndex::String16Vector terms( HistoryQuickProvider::WordVectorFromString16(term_string)); ScoredHistoryMatches matches = GetIndex()->HistoryItemsForTerms(terms); size_t match_num = matches.size() - 1; for (ScoredHistoryMatches::const_iterator match_iter = matches.begin(); match_iter != matches.end(); ++match_iter, --match_num) { const ScoredHistoryMatch& history_match(*match_iter); AutocompleteMatch ac_match = QuickMatchToACMatch(history_match, NORMAL, match_num); matches_.push_back(ac_match); } } AutocompleteMatch HistoryQuickProvider::QuickMatchToACMatch( const ScoredHistoryMatch& history_match, MatchType match_type, size_t match_number) { const history::URLRow& info = history_match.url_info; int score = CalculateRelevance(history_match.raw_score, autocomplete_input_.type(), match_type, match_number); AutocompleteMatch match(this, score, !!info.visit_count(), AutocompleteMatch::HISTORY_URL); match.destination_url = info.url(); DCHECK(match.destination_url.is_valid()); size_t inline_autocomplete_offset = history_match.input_location + autocomplete_input_.text().length(); const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll & ~((trim_http_ && !history_match.match_in_scheme) ? 0 : net::kFormatUrlOmitHTTP); std::string languages = match_type == WHAT_YOU_TYPED ? std::string() : languages_; match.fill_into_edit = AutocompleteInput::FormattedStringWithEquivalentMeaning(info.url(), UTF16ToWide(net::FormatUrl(info.url(), languages, format_types, UnescapeRule::SPACES, NULL, NULL, &inline_autocomplete_offset))); if (!autocomplete_input_.prevent_inline_autocomplete()) match.inline_autocomplete_offset = inline_autocomplete_offset; DCHECK((match.inline_autocomplete_offset == std::wstring::npos) || (match.inline_autocomplete_offset <= match.fill_into_edit.length())); size_t match_start = history_match.input_location; match.contents = UTF16ToWide(net::FormatUrl(info.url(), languages, format_types, UnescapeRule::SPACES, NULL, NULL, &match_start)); if ((match_start != std::wstring::npos) && (inline_autocomplete_offset != std::wstring::npos) && (inline_autocomplete_offset != match_start)) { DCHECK(inline_autocomplete_offset > match_start); AutocompleteMatch::ClassifyLocationInString(match_start, inline_autocomplete_offset - match_start, match.contents.length(), ACMatchClassification::URL, &match.contents_class); } else { AutocompleteMatch::ClassifyLocationInString(std::wstring::npos, 0, match.contents.length(), ACMatchClassification::URL, &match.contents_class); } match.description = UTF16ToWide(info.title()); AutocompleteMatch::ClassifyMatchInString(autocomplete_input_.text(), UTF16ToWide(info.title()), ACMatchClassification::NONE, &match.description_class); return match; } history::InMemoryURLIndex* HistoryQuickProvider::GetIndex() { if (index_for_testing_.get()) return index_for_testing_.get(); HistoryService* const history_service = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); if (!history_service) return NULL; return history_service->InMemoryIndex(); } void HistoryQuickProvider::SetIndexForTesting( history::InMemoryURLIndex* index) { DCHECK(index); index_for_testing_.reset(index); } // Utility Functions history::InMemoryURLIndex::String16Vector HistoryQuickProvider::WordVectorFromString16(const string16& uni_string) { history::InMemoryURLIndex::String16Vector words; WordIterator iter(&uni_string, WordIterator::BREAK_WORD); if (iter.Init()) { while (iter.Advance()) { if (iter.IsWord()) words.push_back(iter.GetWord()); } } return words; } // static int HistoryQuickProvider::CalculateRelevance(int raw_score, AutocompleteInput::Type input_type, MatchType match_type, size_t match_number) { switch (match_type) { case INLINE_AUTOCOMPLETE: return 1400; case WHAT_YOU_TYPED: return 1200; default: return 900 + static_cast(match_number); } }