OLD | NEW |
| (Empty) |
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/autocomplete/autocomplete_input.h" | |
6 | |
7 #include "base/strings/string_util.h" | |
8 #include "base/strings/utf_string_conversions.h" | |
9 #include "components/autocomplete/autocomplete_scheme_classifier.h" | |
10 #include "components/metrics/proto/omnibox_event.pb.h" | |
11 #include "components/url_fixer/url_fixer.h" | |
12 #include "net/base/net_util.h" | |
13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
14 #include "url/url_canon_ip.h" | |
15 #include "url/url_util.h" | |
16 | |
17 namespace { | |
18 | |
19 // Hardcode constant to avoid any dependencies on content/. | |
20 const char kViewSourceScheme[] = "view-source"; | |
21 | |
22 void AdjustCursorPositionIfNecessary(size_t num_leading_chars_removed, | |
23 size_t* cursor_position) { | |
24 if (*cursor_position == base::string16::npos) | |
25 return; | |
26 if (num_leading_chars_removed < *cursor_position) | |
27 *cursor_position -= num_leading_chars_removed; | |
28 else | |
29 *cursor_position = 0; | |
30 } | |
31 | |
32 } // namespace | |
33 | |
34 AutocompleteInput::AutocompleteInput() | |
35 : cursor_position_(base::string16::npos), | |
36 current_page_classification_(metrics::OmniboxEventProto::INVALID_SPEC), | |
37 type_(metrics::OmniboxInputType::INVALID), | |
38 prevent_inline_autocomplete_(false), | |
39 prefer_keyword_(false), | |
40 allow_exact_keyword_match_(true), | |
41 want_asynchronous_matches_(true) { | |
42 } | |
43 | |
44 AutocompleteInput::AutocompleteInput( | |
45 const base::string16& text, | |
46 size_t cursor_position, | |
47 const base::string16& desired_tld, | |
48 const GURL& current_url, | |
49 metrics::OmniboxEventProto::PageClassification current_page_classification, | |
50 bool prevent_inline_autocomplete, | |
51 bool prefer_keyword, | |
52 bool allow_exact_keyword_match, | |
53 bool want_asynchronous_matches, | |
54 const AutocompleteSchemeClassifier& scheme_classifier) | |
55 : cursor_position_(cursor_position), | |
56 current_url_(current_url), | |
57 current_page_classification_(current_page_classification), | |
58 prevent_inline_autocomplete_(prevent_inline_autocomplete), | |
59 prefer_keyword_(prefer_keyword), | |
60 allow_exact_keyword_match_(allow_exact_keyword_match), | |
61 want_asynchronous_matches_(want_asynchronous_matches) { | |
62 DCHECK(cursor_position <= text.length() || | |
63 cursor_position == base::string16::npos) | |
64 << "Text: '" << text << "', cp: " << cursor_position; | |
65 // None of the providers care about leading white space so we always trim it. | |
66 // Providers that care about trailing white space handle trimming themselves. | |
67 if ((base::TrimWhitespace(text, base::TRIM_LEADING, &text_) & | |
68 base::TRIM_LEADING) != 0) | |
69 AdjustCursorPositionIfNecessary(text.length() - text_.length(), | |
70 &cursor_position_); | |
71 | |
72 GURL canonicalized_url; | |
73 type_ = Parse(text_, desired_tld, scheme_classifier, &parts_, &scheme_, | |
74 &canonicalized_url); | |
75 | |
76 if (type_ == metrics::OmniboxInputType::INVALID) | |
77 return; | |
78 | |
79 if (((type_ == metrics::OmniboxInputType::UNKNOWN) || | |
80 (type_ == metrics::OmniboxInputType::URL)) && | |
81 canonicalized_url.is_valid() && | |
82 (!canonicalized_url.IsStandard() || canonicalized_url.SchemeIsFile() || | |
83 canonicalized_url.SchemeIsFileSystem() || | |
84 !canonicalized_url.host().empty())) | |
85 canonicalized_url_ = canonicalized_url; | |
86 | |
87 size_t chars_removed = RemoveForcedQueryStringIfNecessary(type_, &text_); | |
88 AdjustCursorPositionIfNecessary(chars_removed, &cursor_position_); | |
89 if (chars_removed) { | |
90 // Remove spaces between opening question mark and first actual character. | |
91 base::string16 trimmed_text; | |
92 if ((base::TrimWhitespace(text_, base::TRIM_LEADING, &trimmed_text) & | |
93 base::TRIM_LEADING) != 0) { | |
94 AdjustCursorPositionIfNecessary(text_.length() - trimmed_text.length(), | |
95 &cursor_position_); | |
96 text_ = trimmed_text; | |
97 } | |
98 } | |
99 } | |
100 | |
101 AutocompleteInput::~AutocompleteInput() { | |
102 } | |
103 | |
104 // static | |
105 size_t AutocompleteInput::RemoveForcedQueryStringIfNecessary( | |
106 metrics::OmniboxInputType::Type type, | |
107 base::string16* text) { | |
108 if ((type != metrics::OmniboxInputType::FORCED_QUERY) || text->empty() || | |
109 (*text)[0] != L'?') | |
110 return 0; | |
111 // Drop the leading '?'. | |
112 text->erase(0, 1); | |
113 return 1; | |
114 } | |
115 | |
116 // static | |
117 std::string AutocompleteInput::TypeToString( | |
118 metrics::OmniboxInputType::Type type) { | |
119 switch (type) { | |
120 case metrics::OmniboxInputType::INVALID: return "invalid"; | |
121 case metrics::OmniboxInputType::UNKNOWN: return "unknown"; | |
122 case metrics::OmniboxInputType::DEPRECATED_REQUESTED_URL: | |
123 return "deprecated-requested-url"; | |
124 case metrics::OmniboxInputType::URL: return "url"; | |
125 case metrics::OmniboxInputType::QUERY: return "query"; | |
126 case metrics::OmniboxInputType::FORCED_QUERY: return "forced-query"; | |
127 } | |
128 return std::string(); | |
129 } | |
130 | |
131 // static | |
132 metrics::OmniboxInputType::Type AutocompleteInput::Parse( | |
133 const base::string16& text, | |
134 const base::string16& desired_tld, | |
135 const AutocompleteSchemeClassifier& scheme_classifier, | |
136 url::Parsed* parts, | |
137 base::string16* scheme, | |
138 GURL* canonicalized_url) { | |
139 size_t first_non_white = text.find_first_not_of(base::kWhitespaceUTF16, 0); | |
140 if (first_non_white == base::string16::npos) | |
141 return metrics::OmniboxInputType::INVALID; // All whitespace. | |
142 | |
143 if (text[first_non_white] == L'?') { | |
144 // If the first non-whitespace character is a '?', we magically treat this | |
145 // as a query. | |
146 return metrics::OmniboxInputType::FORCED_QUERY; | |
147 } | |
148 | |
149 // Ask our parsing back-end to help us understand what the user typed. We | |
150 // use the URLFixerUpper here because we want to be smart about what we | |
151 // consider a scheme. For example, we shouldn't consider www.google.com:80 | |
152 // to have a scheme. | |
153 url::Parsed local_parts; | |
154 if (!parts) | |
155 parts = &local_parts; | |
156 const base::string16 parsed_scheme(url_fixer::SegmentURL(text, parts)); | |
157 if (scheme) | |
158 *scheme = parsed_scheme; | |
159 const std::string parsed_scheme_utf8(base::UTF16ToUTF8(parsed_scheme)); | |
160 | |
161 // If we can't canonicalize the user's input, the rest of the autocomplete | |
162 // system isn't going to be able to produce a navigable URL match for it. | |
163 // So we just return QUERY immediately in these cases. | |
164 GURL placeholder_canonicalized_url; | |
165 if (!canonicalized_url) | |
166 canonicalized_url = &placeholder_canonicalized_url; | |
167 *canonicalized_url = url_fixer::FixupURL(base::UTF16ToUTF8(text), | |
168 base::UTF16ToUTF8(desired_tld)); | |
169 if (!canonicalized_url->is_valid()) | |
170 return metrics::OmniboxInputType::QUERY; | |
171 | |
172 if (LowerCaseEqualsASCII(parsed_scheme_utf8, url::kFileScheme)) { | |
173 // A user might or might not type a scheme when entering a file URL. In | |
174 // either case, |parsed_scheme_utf8| will tell us that this is a file URL, | |
175 // but |parts->scheme| might be empty, e.g. if the user typed "C:\foo". | |
176 return metrics::OmniboxInputType::URL; | |
177 } | |
178 | |
179 // If the user typed a scheme, and it's HTTP or HTTPS, we know how to parse it | |
180 // well enough that we can fall through to the heuristics below. If it's | |
181 // something else, we can just determine our action based on what we do with | |
182 // any input of this scheme. In theory we could do better with some schemes | |
183 // (e.g. "ftp" or "view-source") but I'll wait to spend the effort on that | |
184 // until I run into some cases that really need it. | |
185 if (parts->scheme.is_nonempty() && | |
186 !LowerCaseEqualsASCII(parsed_scheme_utf8, url::kHttpScheme) && | |
187 !LowerCaseEqualsASCII(parsed_scheme_utf8, url::kHttpsScheme)) { | |
188 metrics::OmniboxInputType::Type type = | |
189 scheme_classifier.GetInputTypeForScheme(parsed_scheme_utf8); | |
190 if (type != metrics::OmniboxInputType::INVALID) | |
191 return type; | |
192 | |
193 // We don't know about this scheme. It might be that the user typed a | |
194 // URL of the form "username:password@foo.com". | |
195 const base::string16 http_scheme_prefix = | |
196 base::ASCIIToUTF16(std::string(url::kHttpScheme) + | |
197 url::kStandardSchemeSeparator); | |
198 url::Parsed http_parts; | |
199 base::string16 http_scheme; | |
200 GURL http_canonicalized_url; | |
201 metrics::OmniboxInputType::Type http_type = | |
202 Parse(http_scheme_prefix + text, desired_tld, scheme_classifier, | |
203 &http_parts, &http_scheme, &http_canonicalized_url); | |
204 DCHECK_EQ(std::string(url::kHttpScheme), | |
205 base::UTF16ToUTF8(http_scheme)); | |
206 | |
207 if ((http_type == metrics::OmniboxInputType::URL) && | |
208 http_parts.username.is_nonempty() && | |
209 http_parts.password.is_nonempty()) { | |
210 // Manually re-jigger the parsed parts to match |text| (without the | |
211 // http scheme added). | |
212 http_parts.scheme.reset(); | |
213 url::Component* components[] = { | |
214 &http_parts.username, | |
215 &http_parts.password, | |
216 &http_parts.host, | |
217 &http_parts.port, | |
218 &http_parts.path, | |
219 &http_parts.query, | |
220 &http_parts.ref, | |
221 }; | |
222 for (size_t i = 0; i < arraysize(components); ++i) { | |
223 url_fixer::OffsetComponent( | |
224 -static_cast<int>(http_scheme_prefix.length()), components[i]); | |
225 } | |
226 | |
227 *parts = http_parts; | |
228 if (scheme) | |
229 scheme->clear(); | |
230 *canonicalized_url = http_canonicalized_url; | |
231 | |
232 return metrics::OmniboxInputType::URL; | |
233 } | |
234 | |
235 // We don't know about this scheme and it doesn't look like the user | |
236 // typed a username and password. It's likely to be a search operator | |
237 // like "site:" or "link:". We classify it as UNKNOWN so the user has | |
238 // the option of treating it as a URL if we're wrong. | |
239 // Note that SegmentURL() is smart so we aren't tricked by "c:\foo" or | |
240 // "www.example.com:81" in this case. | |
241 return metrics::OmniboxInputType::UNKNOWN; | |
242 } | |
243 | |
244 // Either the user didn't type a scheme, in which case we need to distinguish | |
245 // between an HTTP URL and a query, or the scheme is HTTP or HTTPS, in which | |
246 // case we should reject invalid formulations. | |
247 | |
248 // If we have an empty host it can't be a valid HTTP[S] URL. (This should | |
249 // only trigger for input that begins with a colon, which GURL will parse as a | |
250 // valid, non-standard URL; for standard URLs, an empty host would have | |
251 // resulted in an invalid |canonicalized_url| above.) | |
252 if (!parts->host.is_nonempty()) | |
253 return metrics::OmniboxInputType::QUERY; | |
254 | |
255 // Sanity-check: GURL should have failed to canonicalize this URL if it had an | |
256 // invalid port. | |
257 DCHECK_NE(url::PORT_INVALID, url::ParsePort(text.c_str(), parts->port)); | |
258 | |
259 // Likewise, the RCDS can reject certain obviously-invalid hosts. (We also | |
260 // use the registry length later below.) | |
261 const base::string16 host(text.substr(parts->host.begin, parts->host.len)); | |
262 const size_t registry_length = | |
263 net::registry_controlled_domains::GetRegistryLength( | |
264 base::UTF16ToUTF8(host), | |
265 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, | |
266 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); | |
267 if (registry_length == std::string::npos) { | |
268 // Try to append the desired_tld. | |
269 if (!desired_tld.empty()) { | |
270 base::string16 host_with_tld(host); | |
271 if (host[host.length() - 1] != '.') | |
272 host_with_tld += '.'; | |
273 host_with_tld += desired_tld; | |
274 const size_t tld_length = | |
275 net::registry_controlled_domains::GetRegistryLength( | |
276 base::UTF16ToUTF8(host_with_tld), | |
277 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, | |
278 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); | |
279 if (tld_length != std::string::npos) { | |
280 // Something like "99999999999" that looks like a bad IP | |
281 // address, but becomes valid on attaching a TLD. | |
282 return metrics::OmniboxInputType::URL; | |
283 } | |
284 } | |
285 // Could be a broken IP address, etc. | |
286 return metrics::OmniboxInputType::QUERY; | |
287 } | |
288 | |
289 | |
290 // See if the hostname is valid. While IE and GURL allow hostnames to contain | |
291 // many other characters (perhaps for weird intranet machines), it's extremely | |
292 // unlikely that a user would be trying to type those in for anything other | |
293 // than a search query. | |
294 url::CanonHostInfo host_info; | |
295 const std::string canonicalized_host(net::CanonicalizeHost( | |
296 base::UTF16ToUTF8(host), &host_info)); | |
297 if ((host_info.family == url::CanonHostInfo::NEUTRAL) && | |
298 !net::IsCanonicalizedHostCompliant(canonicalized_host, | |
299 base::UTF16ToUTF8(desired_tld))) { | |
300 // Invalid hostname. There are several possible cases: | |
301 // * Our checker is too strict and the user pasted in a real-world URL | |
302 // that's "invalid" but resolves. To catch these, we return UNKNOWN when | |
303 // the user explicitly typed a scheme, so we'll still search by default | |
304 // but we'll show the accidental search infobar if necessary. | |
305 // * The user is typing a multi-word query. If we see a space anywhere in | |
306 // the hostname we assume this is a search and return QUERY. | |
307 // * Our checker is too strict and the user is typing a real-world hostname | |
308 // that's "invalid" but resolves. We return UNKNOWN if the TLD is known. | |
309 // Note that we explicitly excluded hosts with spaces above so that | |
310 // "toys at amazon.com" will be treated as a search. | |
311 // * The user is typing some garbage string. Return QUERY. | |
312 // | |
313 // Thus we fall down in the following cases: | |
314 // * Trying to navigate to a hostname with spaces | |
315 // * Trying to navigate to a hostname with invalid characters and an unknown | |
316 // TLD | |
317 // These are rare, though probably possible in intranets. | |
318 return (parts->scheme.is_nonempty() || | |
319 ((registry_length != 0) && | |
320 (host.find(' ') == base::string16::npos))) ? | |
321 metrics::OmniboxInputType::UNKNOWN : metrics::OmniboxInputType::QUERY; | |
322 } | |
323 | |
324 // Now that we've ruled out all schemes other than http or https and done a | |
325 // little more sanity checking, the presence of a scheme means this is likely | |
326 // a URL. | |
327 if (parts->scheme.is_nonempty()) | |
328 return metrics::OmniboxInputType::URL; | |
329 | |
330 // See if the host is an IP address. | |
331 if (host_info.family == url::CanonHostInfo::IPV6) | |
332 return metrics::OmniboxInputType::URL; | |
333 // If the user originally typed a host that looks like an IP address (a | |
334 // dotted quad), they probably want to open it. If the original input was | |
335 // something else (like a single number), they probably wanted to search for | |
336 // it, unless they explicitly typed a scheme. This is true even if the URL | |
337 // appears to have a path: "1.2/45" is more likely a search (for the answer | |
338 // to a math problem) than a URL. However, if there are more non-host | |
339 // components, then maybe this really was intended to be a navigation. For | |
340 // this reason we only check the dotted-quad case here, and save the "other | |
341 // IP addresses" case for after we check the number of non-host components | |
342 // below. | |
343 if ((host_info.family == url::CanonHostInfo::IPV4) && | |
344 (host_info.num_ipv4_components == 4)) | |
345 return metrics::OmniboxInputType::URL; | |
346 | |
347 // Presence of a password means this is likely a URL. Note that unless the | |
348 // user has typed an explicit "http://" or similar, we'll probably think that | |
349 // the username is some unknown scheme, and bail out in the scheme-handling | |
350 // code above. | |
351 if (parts->password.is_nonempty()) | |
352 return metrics::OmniboxInputType::URL; | |
353 | |
354 // Trailing slashes force the input to be treated as a URL. | |
355 if (parts->path.is_nonempty()) { | |
356 char c = text[parts->path.end() - 1]; | |
357 if ((c == '\\') || (c == '/')) | |
358 return metrics::OmniboxInputType::URL; | |
359 } | |
360 | |
361 // If there is more than one recognized non-host component, this is likely to | |
362 // be a URL, even if the TLD is unknown (in which case this is likely an | |
363 // intranet URL). | |
364 if (NumNonHostComponents(*parts) > 1) | |
365 return metrics::OmniboxInputType::URL; | |
366 | |
367 // If the host has a known TLD or a port, it's probably a URL, with the | |
368 // following exceptions: | |
369 // * Any "IP addresses" that make it here are more likely searches | |
370 // (see above). | |
371 // * If we reach here with a username, our input looks like "user@host[.tld]". | |
372 // Because there is no scheme explicitly specified, we think this is more | |
373 // likely an email address than an HTTP auth attempt. Hence, we search by | |
374 // default and let users correct us on a case-by-case basis. | |
375 // Note that we special-case "localhost" as a known hostname. | |
376 if ((host_info.family != url::CanonHostInfo::IPV4) && | |
377 ((registry_length != 0) || (host == base::ASCIIToUTF16("localhost") || | |
378 parts->port.is_nonempty()))) { | |
379 return parts->username.is_nonempty() ? metrics::OmniboxInputType::UNKNOWN : | |
380 metrics::OmniboxInputType::URL; | |
381 } | |
382 | |
383 // If we reach this point, we know there's no known TLD on the input, so if | |
384 // the user wishes to add a desired_tld, the fixup code will oblige; thus this | |
385 // is a URL. | |
386 if (!desired_tld.empty()) | |
387 return metrics::OmniboxInputType::URL; | |
388 | |
389 // No scheme, password, port, path, and no known TLD on the host. | |
390 // This could be: | |
391 // * An "incomplete IP address"; likely a search (see above). | |
392 // * An email-like input like "user@host", where "host" has no known TLD. | |
393 // It's not clear what the user means here and searching seems reasonable. | |
394 // * A single word "foo"; possibly an intranet site, but more likely a search. | |
395 // This is ideally an UNKNOWN, and we can let the Alternate Nav URL code | |
396 // catch our mistakes. | |
397 // * A URL with a valid TLD we don't know about yet. If e.g. a registrar adds | |
398 // "xxx" as a TLD, then until we add it to our data file, Chrome won't know | |
399 // "foo.xxx" is a real URL. So ideally this is a URL, but we can't really | |
400 // distinguish this case from: | |
401 // * A "URL-like" string that's not really a URL (like | |
402 // "browser.tabs.closeButtons" or "java.awt.event.*"). This is ideally a | |
403 // QUERY. Since this is indistinguishable from the case above, and this | |
404 // case is much more likely, claim these are UNKNOWN, which should default | |
405 // to the right thing and let users correct us on a case-by-case basis. | |
406 return metrics::OmniboxInputType::UNKNOWN; | |
407 } | |
408 | |
409 // static | |
410 void AutocompleteInput::ParseForEmphasizeComponents( | |
411 const base::string16& text, | |
412 const AutocompleteSchemeClassifier& scheme_classifier, | |
413 url::Component* scheme, | |
414 url::Component* host) { | |
415 url::Parsed parts; | |
416 base::string16 scheme_str; | |
417 Parse(text, base::string16(), scheme_classifier, &parts, &scheme_str, NULL); | |
418 | |
419 *scheme = parts.scheme; | |
420 *host = parts.host; | |
421 | |
422 int after_scheme_and_colon = parts.scheme.end() + 1; | |
423 // For the view-source scheme, we should emphasize the scheme and host of the | |
424 // URL qualified by the view-source prefix. | |
425 if (LowerCaseEqualsASCII(scheme_str, kViewSourceScheme) && | |
426 (static_cast<int>(text.length()) > after_scheme_and_colon)) { | |
427 // Obtain the URL prefixed by view-source and parse it. | |
428 base::string16 real_url(text.substr(after_scheme_and_colon)); | |
429 url::Parsed real_parts; | |
430 AutocompleteInput::Parse(real_url, base::string16(), scheme_classifier, | |
431 &real_parts, NULL, NULL); | |
432 if (real_parts.scheme.is_nonempty() || real_parts.host.is_nonempty()) { | |
433 if (real_parts.scheme.is_nonempty()) { | |
434 *scheme = url::Component( | |
435 after_scheme_and_colon + real_parts.scheme.begin, | |
436 real_parts.scheme.len); | |
437 } else { | |
438 scheme->reset(); | |
439 } | |
440 if (real_parts.host.is_nonempty()) { | |
441 *host = url::Component(after_scheme_and_colon + real_parts.host.begin, | |
442 real_parts.host.len); | |
443 } else { | |
444 host->reset(); | |
445 } | |
446 } | |
447 } else if (LowerCaseEqualsASCII(scheme_str, url::kFileSystemScheme) && | |
448 parts.inner_parsed() && parts.inner_parsed()->scheme.is_valid()) { | |
449 *host = parts.inner_parsed()->host; | |
450 } | |
451 } | |
452 | |
453 // static | |
454 base::string16 AutocompleteInput::FormattedStringWithEquivalentMeaning( | |
455 const GURL& url, | |
456 const base::string16& formatted_url, | |
457 const AutocompleteSchemeClassifier& scheme_classifier) { | |
458 if (!net::CanStripTrailingSlash(url)) | |
459 return formatted_url; | |
460 const base::string16 url_with_path(formatted_url + base::char16('/')); | |
461 return (AutocompleteInput::Parse(formatted_url, base::string16(), | |
462 scheme_classifier, NULL, NULL, NULL) == | |
463 AutocompleteInput::Parse(url_with_path, base::string16(), | |
464 scheme_classifier, NULL, NULL, NULL)) ? | |
465 formatted_url : url_with_path; | |
466 } | |
467 | |
468 // static | |
469 int AutocompleteInput::NumNonHostComponents(const url::Parsed& parts) { | |
470 int num_nonhost_components = 0; | |
471 if (parts.scheme.is_nonempty()) | |
472 ++num_nonhost_components; | |
473 if (parts.username.is_nonempty()) | |
474 ++num_nonhost_components; | |
475 if (parts.password.is_nonempty()) | |
476 ++num_nonhost_components; | |
477 if (parts.port.is_nonempty()) | |
478 ++num_nonhost_components; | |
479 if (parts.path.is_nonempty()) | |
480 ++num_nonhost_components; | |
481 if (parts.query.is_nonempty()) | |
482 ++num_nonhost_components; | |
483 if (parts.ref.is_nonempty()) | |
484 ++num_nonhost_components; | |
485 return num_nonhost_components; | |
486 } | |
487 | |
488 // static | |
489 bool AutocompleteInput::HasHTTPScheme(const base::string16& input) { | |
490 std::string utf8_input(base::UTF16ToUTF8(input)); | |
491 url::Component scheme; | |
492 if (url::FindAndCompareScheme(utf8_input, kViewSourceScheme, &scheme)) { | |
493 utf8_input.erase(0, scheme.end() + 1); | |
494 } | |
495 return url::FindAndCompareScheme(utf8_input, url::kHttpScheme, NULL); | |
496 } | |
497 | |
498 void AutocompleteInput::UpdateText(const base::string16& text, | |
499 size_t cursor_position, | |
500 const url::Parsed& parts) { | |
501 DCHECK(cursor_position <= text.length() || | |
502 cursor_position == base::string16::npos) | |
503 << "Text: '" << text << "', cp: " << cursor_position; | |
504 text_ = text; | |
505 cursor_position_ = cursor_position; | |
506 parts_ = parts; | |
507 } | |
508 | |
509 void AutocompleteInput::Clear() { | |
510 text_.clear(); | |
511 cursor_position_ = base::string16::npos; | |
512 current_url_ = GURL(); | |
513 current_page_classification_ = metrics::OmniboxEventProto::INVALID_SPEC; | |
514 type_ = metrics::OmniboxInputType::INVALID; | |
515 parts_ = url::Parsed(); | |
516 scheme_.clear(); | |
517 canonicalized_url_ = GURL(); | |
518 prevent_inline_autocomplete_ = false; | |
519 prefer_keyword_ = false; | |
520 allow_exact_keyword_match_ = false; | |
521 want_asynchronous_matches_ = true; | |
522 } | |
OLD | NEW |