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/autocomplete_provider.h" 6 7 #include "base/logging.h" 8 #include "base/prefs/pref_service.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "chrome/browser/autocomplete/autocomplete_match.h" 11 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 12 #include "chrome/browser/bookmarks/bookmark_model.h" 13 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/common/net/url_fixer_upper.h" 16 #include "chrome/common/pref_names.h" 17 #include "content/public/common/url_constants.h" 18 #include "net/base/net_util.h" 19 #include "url/gurl.h" 20 21 // static 22 const size_t AutocompleteProvider::kMaxMatches = 3; 23 24 AutocompleteProvider::AutocompleteProvider( 25 AutocompleteProviderListener* listener, 26 Profile* profile, 27 Type type) 28 : profile_(profile), 29 listener_(listener), 30 done_(true), 31 type_(type) { 32 } 33 34 // static 35 const char* AutocompleteProvider::TypeToString(Type type) { 36 switch (type) { 37 case TYPE_BOOKMARK: 38 return "Bookmark"; 39 case TYPE_BUILTIN: 40 return "Builtin"; 41 case TYPE_CONTACT: 42 return "Contact"; 43 case TYPE_EXTENSION_APP: 44 return "ExtensionApp"; 45 case TYPE_HISTORY_QUICK: 46 return "HistoryQuick"; 47 case TYPE_HISTORY_URL: 48 return "HistoryURL"; 49 case TYPE_KEYWORD: 50 return "Keyword"; 51 case TYPE_SEARCH: 52 return "Search"; 53 case TYPE_SHORTCUTS: 54 return "Shortcuts"; 55 case TYPE_ZERO_SUGGEST: 56 return "ZeroSuggest"; 57 default: 58 NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type; 59 return "Unknown"; 60 } 61 } 62 63 void AutocompleteProvider::Stop(bool clear_cached_results) { 64 done_ = true; 65 } 66 67 const char* AutocompleteProvider::GetName() const { 68 return TypeToString(type_); 69 } 70 71 metrics::OmniboxEventProto_ProviderType AutocompleteProvider:: 72 AsOmniboxEventProviderType() const { 73 switch (type_) { 74 case TYPE_BOOKMARK: 75 return metrics::OmniboxEventProto::BOOKMARK; 76 case TYPE_BUILTIN: 77 return metrics::OmniboxEventProto::BUILTIN; 78 case TYPE_CONTACT: 79 return metrics::OmniboxEventProto::CONTACT; 80 case TYPE_EXTENSION_APP: 81 return metrics::OmniboxEventProto::EXTENSION_APPS; 82 case TYPE_HISTORY_QUICK: 83 return metrics::OmniboxEventProto::HISTORY_QUICK; 84 case TYPE_HISTORY_URL: 85 return metrics::OmniboxEventProto::HISTORY_URL; 86 case TYPE_KEYWORD: 87 return metrics::OmniboxEventProto::KEYWORD; 88 case TYPE_SEARCH: 89 return metrics::OmniboxEventProto::SEARCH; 90 case TYPE_SHORTCUTS: 91 return metrics::OmniboxEventProto::SHORTCUTS; 92 case TYPE_ZERO_SUGGEST: 93 return metrics::OmniboxEventProto::ZERO_SUGGEST; 94 default: 95 NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type_; 96 return metrics::OmniboxEventProto::UNKNOWN_PROVIDER; 97 } 98 } 99 100 void AutocompleteProvider::DeleteMatch(const AutocompleteMatch& match) { 101 DLOG(WARNING) << "The AutocompleteProvider '" << GetName() 102 << "' has not implemented DeleteMatch."; 103 } 104 105 void AutocompleteProvider::AddProviderInfo(ProvidersInfo* provider_info) const { 106 } 107 108 void AutocompleteProvider::ResetSession() { 109 } 110 111 base::string16 AutocompleteProvider::StringForURLDisplay(const GURL& url, 112 bool check_accept_lang, 113 bool trim_http) const { 114 std::string languages = (check_accept_lang && profile_) ? 115 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages) : std::string(); 116 return net::FormatUrl(url, languages, 117 net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP), 118 net::UnescapeRule::SPACES, NULL, NULL, NULL); 119 } 120 121 AutocompleteProvider::~AutocompleteProvider() { 122 Stop(false); 123 } 124 125 void AutocompleteProvider::UpdateStarredStateOfMatches() { 126 if (matches_.empty()) 127 return; 128 129 if (!profile_) 130 return; 131 132 BookmarkModel* bookmark_model = BookmarkModelFactory::GetForProfile(profile_); 133 if (!bookmark_model || !bookmark_model->loaded()) 134 return; 135 136 for (ACMatches::iterator i(matches_.begin()); i != matches_.end(); ++i) 137 i->starred = bookmark_model->IsBookmarked(i->destination_url); 138 } 139 140 // static 141 bool AutocompleteProvider::FixupUserInput(AutocompleteInput* input) { 142 const base::string16& input_text = input->text(); 143 // Fixup and canonicalize user input. 144 const GURL canonical_gurl(URLFixerUpper::FixupURL(UTF16ToUTF8(input_text), 145 std::string())); 146 std::string canonical_gurl_str(canonical_gurl.possibly_invalid_spec()); 147 if (canonical_gurl_str.empty()) { 148 // This probably won't happen, but there are no guarantees. 149 return false; 150 } 151 152 // If the user types a number, GURL will convert it to a dotted quad. 153 // However, if the parser did not mark this as a URL, then the user probably 154 // didn't intend this interpretation. Since this can break history matching 155 // for hostname beginning with numbers (e.g. input of "17173" will be matched 156 // against "0.0.67.21" instead of the original "17173", failing to find 157 // "17173.com"), swap the original hostname in for the fixed-up one. 158 if ((input->type() != AutocompleteInput::URL) && 159 canonical_gurl.HostIsIPAddress()) { 160 std::string original_hostname = 161 UTF16ToUTF8(input_text.substr(input->parts().host.begin, 162 input->parts().host.len)); 163 const url_parse::Parsed& parts = 164 canonical_gurl.parsed_for_possibly_invalid_spec(); 165 // parts.host must not be empty when HostIsIPAddress() is true. 166 DCHECK(parts.host.is_nonempty()); 167 canonical_gurl_str.replace(parts.host.begin, parts.host.len, 168 original_hostname); 169 } 170 base::string16 output = UTF8ToUTF16(canonical_gurl_str); 171 // Don't prepend a scheme when the user didn't have one. Since the fixer 172 // upper only prepends the "http" scheme, that's all we need to check for. 173 if (!AutocompleteInput::HasHTTPScheme(input_text)) 174 TrimHttpPrefix(&output); 175 176 // Make the number of trailing slashes on the output exactly match the input. 177 // Examples of why not doing this would matter: 178 // * The user types "a" and has this fixed up to "a/". Now no other sites 179 // beginning with "a" will match. 180 // * The user types "file:" and has this fixed up to "file://". Now inline 181 // autocomplete will append too few slashes, resulting in e.g. "file:/b..." 182 // instead of "file:///b..." 183 // * The user types "http:/" and has this fixed up to "http:". Now inline 184 // autocomplete will append too many slashes, resulting in e.g. 185 // "http:///c..." instead of "http://c...". 186 // NOTE: We do this after calling TrimHttpPrefix() since that can strip 187 // trailing slashes (if the scheme is the only thing in the input). It's not 188 // clear that the result of fixup really matters in this case, but there's no 189 // harm in making sure. 190 const size_t last_input_nonslash = 191 input_text.find_last_not_of(ASCIIToUTF16("/\\")); 192 const size_t num_input_slashes = 193 (last_input_nonslash == base::string16::npos) ? 194 input_text.length() : (input_text.length() - 1 - last_input_nonslash); 195 const size_t last_output_nonslash = 196 output.find_last_not_of(ASCIIToUTF16("/\\")); 197 const size_t num_output_slashes = 198 (last_output_nonslash == base::string16::npos) ? 199 output.length() : (output.length() - 1 - last_output_nonslash); 200 if (num_output_slashes < num_input_slashes) 201 output.append(num_input_slashes - num_output_slashes, '/'); 202 else if (num_output_slashes > num_input_slashes) 203 output.erase(output.length() - num_output_slashes + num_input_slashes); 204 205 url_parse::Parsed parts; 206 URLFixerUpper::SegmentURL(output, &parts); 207 input->UpdateText(output, base::string16::npos, parts); 208 return !output.empty(); 209 } 210 211 // static 212 size_t AutocompleteProvider::TrimHttpPrefix(base::string16* url) { 213 // Find any "http:". 214 if (!AutocompleteInput::HasHTTPScheme(*url)) 215 return 0; 216 size_t scheme_pos = 217 url->find(ASCIIToUTF16(content::kHttpScheme) + char16(':')); 218 DCHECK_NE(base::string16::npos, scheme_pos); 219 220 // Erase scheme plus up to two slashes. 221 size_t prefix_end = scheme_pos + strlen(content::kHttpScheme) + 1; 222 const size_t after_slashes = std::min(url->length(), prefix_end + 2); 223 while ((prefix_end < after_slashes) && ((*url)[prefix_end] == '/')) 224 ++prefix_end; 225 url->erase(scheme_pos, prefix_end - scheme_pos); 226 return (scheme_pos == 0) ? prefix_end : 0; 227 } 228 229