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