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.
5 #include "components/omnibox/browser/autocomplete_input.h"
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/metrics/proto/omnibox_event.pb.h"
11 #include "components/omnibox/browser/autocomplete_scheme_classifier.h"
12 #include "components/omnibox/browser/omnibox_field_trial.h"
13 #include "components/url_formatter/url_fixer.h"
14 #include "components/url_formatter/url_formatter.h"
15 #include "net/base/net_util.h"
16 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
17 #include "url/url_canon_ip.h"
18 #include "url/url_util.h"
22 // Hardcode constant to avoid any dependencies on content/.
23 const char kViewSourceScheme
[] = "view-source";
25 void AdjustCursorPositionIfNecessary(size_t num_leading_chars_removed
,
26 size_t* cursor_position
) {
27 if (*cursor_position
== base::string16::npos
)
29 if (num_leading_chars_removed
< *cursor_position
)
30 *cursor_position
-= num_leading_chars_removed
;
35 // Finds all terms in |text| that start with http:// or https:// plus at least
36 // one more character and puts the text after the prefix in
37 // |terms_prefixed_by_http_or_https|.
38 void PopulateTermsPrefixedByHttpOrHttps(
39 const base::string16
& text
,
40 std::vector
<base::string16
>* terms_prefixed_by_http_or_https
) {
41 // Split on whitespace rather than use ICU's word iterator because, for
42 // example, ICU's iterator may break on punctuation (such as ://) or decide
43 // to split a single term in a hostname (if it seems to think that the
44 // hostname is multiple words). Neither of these behaviors is desirable.
45 const std::string
separator(url::kStandardSchemeSeparator
);
46 for (const auto& term
:
47 base::SplitString(text
, base::ASCIIToUTF16(" "),
48 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
)) {
49 const std::string
term_utf8(base::UTF16ToUTF8(term
));
50 static const char* kSchemes
[2] = { url::kHttpScheme
, url::kHttpsScheme
};
51 for (const char* scheme
: kSchemes
) {
52 const std::string
prefix(scheme
+ separator
);
53 // Doing an ASCII comparison is okay because prefix is ASCII.
54 if (base::StartsWith(term_utf8
, prefix
,
55 base::CompareCase::INSENSITIVE_ASCII
) &&
56 (term_utf8
.length() > prefix
.length())) {
57 terms_prefixed_by_http_or_https
->push_back(
58 term
.substr(prefix
.length()));
66 AutocompleteInput::AutocompleteInput()
67 : cursor_position_(base::string16::npos
),
68 current_page_classification_(metrics::OmniboxEventProto::INVALID_SPEC
),
69 type_(metrics::OmniboxInputType::INVALID
),
70 prevent_inline_autocomplete_(false),
71 prefer_keyword_(false),
72 allow_exact_keyword_match_(true),
73 want_asynchronous_matches_(true),
74 from_omnibox_focus_(false) {
77 AutocompleteInput::AutocompleteInput(
78 const base::string16
& text
,
79 size_t cursor_position
,
80 const std::string
& desired_tld
,
81 const GURL
& current_url
,
82 metrics::OmniboxEventProto::PageClassification current_page_classification
,
83 bool prevent_inline_autocomplete
,
85 bool allow_exact_keyword_match
,
86 bool want_asynchronous_matches
,
87 bool from_omnibox_focus
,
88 const AutocompleteSchemeClassifier
& scheme_classifier
)
89 : cursor_position_(cursor_position
),
90 current_url_(current_url
),
91 current_page_classification_(current_page_classification
),
92 prevent_inline_autocomplete_(prevent_inline_autocomplete
),
93 prefer_keyword_(prefer_keyword
),
94 allow_exact_keyword_match_(allow_exact_keyword_match
),
95 want_asynchronous_matches_(want_asynchronous_matches
),
96 from_omnibox_focus_(from_omnibox_focus
) {
97 DCHECK(cursor_position
<= text
.length() ||
98 cursor_position
== base::string16::npos
)
99 << "Text: '" << text
<< "', cp: " << cursor_position
;
100 // None of the providers care about leading white space so we always trim it.
101 // Providers that care about trailing white space handle trimming themselves.
102 if ((base::TrimWhitespace(text
, base::TRIM_LEADING
, &text_
) &
103 base::TRIM_LEADING
) != 0)
104 AdjustCursorPositionIfNecessary(text
.length() - text_
.length(),
107 GURL canonicalized_url
;
108 type_
= Parse(text_
, desired_tld
, scheme_classifier
, &parts_
, &scheme_
,
110 PopulateTermsPrefixedByHttpOrHttps(text_
, &terms_prefixed_by_http_or_https_
);
112 if (type_
== metrics::OmniboxInputType::INVALID
)
115 if (((type_
== metrics::OmniboxInputType::UNKNOWN
) ||
116 (type_
== metrics::OmniboxInputType::URL
)) &&
117 canonicalized_url
.is_valid() &&
118 (!canonicalized_url
.IsStandard() || canonicalized_url
.SchemeIsFile() ||
119 canonicalized_url
.SchemeIsFileSystem() ||
120 !canonicalized_url
.host().empty()))
121 canonicalized_url_
= canonicalized_url
;
123 size_t chars_removed
= RemoveForcedQueryStringIfNecessary(type_
, &text_
);
124 AdjustCursorPositionIfNecessary(chars_removed
, &cursor_position_
);
126 // Remove spaces between opening question mark and first actual character.
127 base::string16 trimmed_text
;
128 if ((base::TrimWhitespace(text_
, base::TRIM_LEADING
, &trimmed_text
) &
129 base::TRIM_LEADING
) != 0) {
130 AdjustCursorPositionIfNecessary(text_
.length() - trimmed_text
.length(),
132 text_
= trimmed_text
;
137 AutocompleteInput::~AutocompleteInput() {
141 size_t AutocompleteInput::RemoveForcedQueryStringIfNecessary(
142 metrics::OmniboxInputType::Type type
,
143 base::string16
* text
) {
144 if ((type
!= metrics::OmniboxInputType::FORCED_QUERY
) || text
->empty() ||
147 // Drop the leading '?'.
153 std::string
AutocompleteInput::TypeToString(
154 metrics::OmniboxInputType::Type type
) {
156 case metrics::OmniboxInputType::INVALID
: return "invalid";
157 case metrics::OmniboxInputType::UNKNOWN
: return "unknown";
158 case metrics::OmniboxInputType::DEPRECATED_REQUESTED_URL
:
159 return "deprecated-requested-url";
160 case metrics::OmniboxInputType::URL
: return "url";
161 case metrics::OmniboxInputType::QUERY
: return "query";
162 case metrics::OmniboxInputType::FORCED_QUERY
: return "forced-query";
164 return std::string();
168 metrics::OmniboxInputType::Type
AutocompleteInput::Parse(
169 const base::string16
& text
,
170 const std::string
& desired_tld
,
171 const AutocompleteSchemeClassifier
& scheme_classifier
,
173 base::string16
* scheme
,
174 GURL
* canonicalized_url
) {
175 size_t first_non_white
= text
.find_first_not_of(base::kWhitespaceUTF16
, 0);
176 if (first_non_white
== base::string16::npos
)
177 return metrics::OmniboxInputType::INVALID
; // All whitespace.
179 if (text
[first_non_white
] == L
'?') {
180 // If the first non-whitespace character is a '?', we magically treat this
182 return metrics::OmniboxInputType::FORCED_QUERY
;
185 // Ask our parsing back-end to help us understand what the user typed. We
186 // use the URLFixerUpper here because we want to be smart about what we
187 // consider a scheme. For example, we shouldn't consider www.google.com:80
189 url::Parsed local_parts
;
191 parts
= &local_parts
;
192 const base::string16
parsed_scheme(url_formatter::SegmentURL(text
, parts
));
194 *scheme
= parsed_scheme
;
195 const std::string
parsed_scheme_utf8(base::UTF16ToUTF8(parsed_scheme
));
197 // If we can't canonicalize the user's input, the rest of the autocomplete
198 // system isn't going to be able to produce a navigable URL match for it.
199 // So we just return QUERY immediately in these cases.
200 GURL placeholder_canonicalized_url
;
201 if (!canonicalized_url
)
202 canonicalized_url
= &placeholder_canonicalized_url
;
204 url_formatter::FixupURL(base::UTF16ToUTF8(text
), desired_tld
);
205 if (!canonicalized_url
->is_valid())
206 return metrics::OmniboxInputType::QUERY
;
208 if (base::LowerCaseEqualsASCII(parsed_scheme_utf8
, url::kFileScheme
)) {
209 // A user might or might not type a scheme when entering a file URL. In
210 // either case, |parsed_scheme_utf8| will tell us that this is a file URL,
211 // but |parts->scheme| might be empty, e.g. if the user typed "C:\foo".
212 return metrics::OmniboxInputType::URL
;
215 // If the user typed a scheme, and it's HTTP or HTTPS, we know how to parse it
216 // well enough that we can fall through to the heuristics below. If it's
217 // something else, we can just determine our action based on what we do with
218 // any input of this scheme. In theory we could do better with some schemes
219 // (e.g. "ftp" or "view-source") but I'll wait to spend the effort on that
220 // until I run into some cases that really need it.
221 if (parts
->scheme
.is_nonempty() &&
222 !base::LowerCaseEqualsASCII(parsed_scheme_utf8
, url::kHttpScheme
) &&
223 !base::LowerCaseEqualsASCII(parsed_scheme_utf8
, url::kHttpsScheme
)) {
224 metrics::OmniboxInputType::Type type
=
225 scheme_classifier
.GetInputTypeForScheme(parsed_scheme_utf8
);
226 if (type
!= metrics::OmniboxInputType::INVALID
)
229 // We don't know about this scheme. It might be that the user typed a
230 // URL of the form "username:password@foo.com".
231 const base::string16 http_scheme_prefix
=
232 base::ASCIIToUTF16(std::string(url::kHttpScheme
) +
233 url::kStandardSchemeSeparator
);
234 url::Parsed http_parts
;
235 base::string16 http_scheme
;
236 GURL http_canonicalized_url
;
237 metrics::OmniboxInputType::Type http_type
=
238 Parse(http_scheme_prefix
+ text
, desired_tld
, scheme_classifier
,
239 &http_parts
, &http_scheme
, &http_canonicalized_url
);
240 DCHECK_EQ(std::string(url::kHttpScheme
),
241 base::UTF16ToUTF8(http_scheme
));
243 if ((http_type
== metrics::OmniboxInputType::URL
) &&
244 http_parts
.username
.is_nonempty() &&
245 http_parts
.password
.is_nonempty()) {
246 // Manually re-jigger the parsed parts to match |text| (without the
247 // http scheme added).
248 http_parts
.scheme
.reset();
249 url::Component
* components
[] = {
250 &http_parts
.username
,
251 &http_parts
.password
,
258 for (size_t i
= 0; i
< arraysize(components
); ++i
) {
259 url_formatter::OffsetComponent(
260 -static_cast<int>(http_scheme_prefix
.length()), components
[i
]);
266 *canonicalized_url
= http_canonicalized_url
;
268 return metrics::OmniboxInputType::URL
;
271 // We don't know about this scheme and it doesn't look like the user
272 // typed a username and password. It's likely to be a search operator
273 // like "site:" or "link:". We classify it as UNKNOWN so the user has
274 // the option of treating it as a URL if we're wrong.
275 // Note that SegmentURL() is smart so we aren't tricked by "c:\foo" or
276 // "www.example.com:81" in this case.
277 return metrics::OmniboxInputType::UNKNOWN
;
280 // Either the user didn't type a scheme, in which case we need to distinguish
281 // between an HTTP URL and a query, or the scheme is HTTP or HTTPS, in which
282 // case we should reject invalid formulations.
284 // If we have an empty host it can't be a valid HTTP[S] URL. (This should
285 // only trigger for input that begins with a colon, which GURL will parse as a
286 // valid, non-standard URL; for standard URLs, an empty host would have
287 // resulted in an invalid |canonicalized_url| above.)
288 if (!canonicalized_url
->has_host())
289 return metrics::OmniboxInputType::QUERY
;
291 // Determine the host family. We get this information by (re-)canonicalizing
292 // the already-canonicalized host rather than using the user's original input,
293 // in case fixup affected the result here (e.g. an input that looks like an
294 // IPv4 address but with a non-empty desired TLD would return IPV4 before
295 // fixup and NEUTRAL afterwards, and we want to treat it as NEUTRAL).
296 url::CanonHostInfo host_info
;
297 net::CanonicalizeHost(canonicalized_url
->host(), &host_info
);
299 // Check if the canonicalized host has a known TLD, which we'll want to know
301 const size_t registry_length
=
302 net::registry_controlled_domains::GetRegistryLength(
303 canonicalized_url
->host(),
304 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES
,
305 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
);
306 DCHECK_NE(std::string::npos
, registry_length
);
307 const bool has_known_tld
= registry_length
!= 0;
309 // See if the hostname is valid. While IE and GURL allow hostnames to contain
310 // many other characters (perhaps for weird intranet machines), it's extremely
311 // unlikely that a user would be trying to type those in for anything other
312 // than a search query.
313 const base::string16
original_host(
314 text
.substr(parts
->host
.begin
, parts
->host
.len
));
315 if ((host_info
.family
== url::CanonHostInfo::NEUTRAL
) &&
316 !net::IsCanonicalizedHostCompliant(canonicalized_url
->host())) {
317 // Invalid hostname. There are several possible cases:
318 // * The user is typing a multi-word query. If we see a space anywhere in
319 // the input host we assume this is a search and return QUERY. (We check
320 // the input string instead of canonicalized_url->host() in case fixup
321 // escaped the space.)
322 // * The user is typing some garbage string. Return QUERY.
323 // * Our checker is too strict and the user is typing a real-world URL
324 // that's "invalid" but resolves. To catch these, we return UNKNOWN when
325 // the user explicitly typed a scheme or when the hostname has a known
326 // TLD, so we'll still search by default but we'll show the accidental
327 // search infobar if necessary.
329 // This means we would block the following kinds of navigation attempts:
330 // * Navigations to a hostname with spaces
331 // * Navigations to a hostname with invalid characters and an unknown TLD
332 // These might be possible in intranets, but we're not going to support them
333 // without concrete evidence that doing so is necessary.
334 return (parts
->scheme
.is_nonempty() ||
335 (has_known_tld
&& (original_host
.find(' ') == base::string16::npos
))) ?
336 metrics::OmniboxInputType::UNKNOWN
: metrics::OmniboxInputType::QUERY
;
339 // For hostnames that look like IP addresses, distinguish between IPv6
340 // addresses, which are basically guaranteed to be navigations, and IPv4
341 // addresses, which are much fuzzier.
342 if (host_info
.family
== url::CanonHostInfo::IPV6
)
343 return metrics::OmniboxInputType::URL
;
344 if (host_info
.family
== url::CanonHostInfo::IPV4
) {
345 // The host may be a real IP address, or something that looks a bit like it
346 // (e.g. "1.2" or "3232235521"). We check whether it was convertible to an
347 // IP with a non-zero first octet; IPs with first octet zero are "source
348 // IPs" and are almost never navigable as destination addresses.
350 // The one exception to this is 0.0.0.0; on many systems, attempting to
351 // navigate to this IP actually navigates to localhost. To support this
352 // case, when the converted IP is 0.0.0.0, we go ahead and run the "did the
353 // user actually type four components" test in the conditional below, so
354 // that we'll allow explicit attempts to navigate to "0.0.0.0". If the
355 // input was anything else (e.g. "0"), we'll fall through to returning QUERY
357 if ((host_info
.address
[0] != 0) ||
358 ((host_info
.address
[1] == 0) && (host_info
.address
[2] == 0) &&
359 (host_info
.address
[3] == 0))) {
360 // This is theoretically a navigable IP. We have four cases. The first
362 // * If the user typed four distinct components, this is an IP for sure.
363 // * If the user typed two or three components, this is almost certainly a
364 // query, especially for two components (as in "13.5/7.25"), but we'll
365 // allow navigation for an explicit scheme or trailing slash below.
366 // * If the user typed one component, this is likely a query, but could be
367 // a non-dotted-quad version of an IP address.
368 // Unfortunately, since we called CanonicalizeHost() on the
369 // already-canonicalized host, all of these cases will have been changed
370 // to have four components (e.g. 13.2 -> 13.0.0.2), so we have to call
371 // CanonicalizeHost() again, this time on the original input, so that we
372 // can get the correct number of IP components.
374 // The fourth case is that the user typed something ambiguous like ".1.2"
375 // that fixup converted to an IP address ("1.0.0.2"). In this case the
376 // call to CanonicalizeHost() will return NEUTRAL here. Since it's not
377 // clear what the user intended, we fall back to our other heuristics.
378 net::CanonicalizeHost(base::UTF16ToUTF8(original_host
), &host_info
);
379 if ((host_info
.family
== url::CanonHostInfo::IPV4
) &&
380 (host_info
.num_ipv4_components
== 4))
381 return metrics::OmniboxInputType::URL
;
384 // By this point, if we have an "IP" with first octet zero, we know it
385 // wasn't "0.0.0.0", so mark it as non-navigable.
386 if (host_info
.address
[0] == 0)
387 return metrics::OmniboxInputType::QUERY
;
390 // Now that we've ruled out all schemes other than http or https and done a
391 // little more sanity checking, the presence of a scheme means this is likely
393 if (parts
->scheme
.is_nonempty())
394 return metrics::OmniboxInputType::URL
;
396 // Trailing slashes force the input to be treated as a URL.
397 if (parts
->path
.is_nonempty()) {
398 base::char16 c
= text
[parts
->path
.end() - 1];
399 if ((c
== '\\') || (c
== '/'))
400 return metrics::OmniboxInputType::URL
;
403 // Handle the cases we detected in the IPv4 code above as "almost certainly a
404 // query" now that we know the user hasn't tried to force navigation via a
405 // scheme/trailing slash.
406 if ((host_info
.family
== url::CanonHostInfo::IPV4
) &&
407 (host_info
.num_ipv4_components
> 1))
408 return metrics::OmniboxInputType::QUERY
;
410 // If there is more than one recognized non-host component, this is likely to
411 // be a URL, even if the TLD is unknown (in which case this is likely an
413 if (NumNonHostComponents(*parts
) > 1)
414 return metrics::OmniboxInputType::URL
;
416 // If we reach here with a username, our input looks something like
417 // "user@host". Unless there is a desired TLD, we think this is more likely
418 // an email address than an HTTP auth attempt, so we search by default. (When
419 // there _is_ a desired TLD, the user hit ctrl-enter, and we assume that
420 // implies an attempted navigation.)
421 if (canonicalized_url
->has_username() && desired_tld
.empty())
422 return metrics::OmniboxInputType::UNKNOWN
;
424 // If the host has a known TLD or a port, it's probably a URL. Note that we
425 // special-case "localhost" as a known hostname.
426 if (has_known_tld
|| (canonicalized_url
->host() == "localhost") ||
427 canonicalized_url
->has_port())
428 return metrics::OmniboxInputType::URL
;
430 // If the input looks like a word followed by a pound sign and possibly more
431 // characters ("c#" or "c# foo"), this is almost certainly an attempt to
432 // search. We try to be conservative here by not firing on cases like "c/#"
433 // or "c?#" that might actually indicate some cryptic attempt to access an
434 // intranet host, and by placing this check late enough that other tests
435 // (e.g., for a non-empty TLD or a non-empty scheme) will have already
437 if (!OmniboxFieldTrial::PreventUWYTDefaultForNonURLInputs() &&
438 !parts
->path
.is_valid() && !canonicalized_url
->has_query() &&
439 canonicalized_url
->has_ref())
440 return metrics::OmniboxInputType::QUERY
;
442 // No scheme, username, port, and no known TLD on the host.
444 // * A single word "foo"; possibly an intranet site, but more likely a search.
445 // This is ideally an UNKNOWN, and we can let the Alternate Nav URL code
446 // catch our mistakes.
447 // * A URL with a valid TLD we don't know about yet. If e.g. a registrar adds
448 // "xxx" as a TLD, then until we add it to our data file, Chrome won't know
449 // "foo.xxx" is a real URL. So ideally this is a URL, but we can't really
450 // distinguish this case from:
451 // * A "URL-like" string that's not really a URL (like
452 // "browser.tabs.closeButtons" or "java.awt.event.*"). This is ideally a
453 // QUERY. Since this is indistinguishable from the case above, and this
454 // case is much more likely, claim these are UNKNOWN, which should default
455 // to the right thing and let users correct us on a case-by-case basis.
456 return metrics::OmniboxInputType::UNKNOWN
;
460 void AutocompleteInput::ParseForEmphasizeComponents(
461 const base::string16
& text
,
462 const AutocompleteSchemeClassifier
& scheme_classifier
,
463 url::Component
* scheme
,
464 url::Component
* host
) {
466 base::string16 scheme_str
;
467 Parse(text
, std::string(), scheme_classifier
, &parts
, &scheme_str
, NULL
);
469 *scheme
= parts
.scheme
;
472 int after_scheme_and_colon
= parts
.scheme
.end() + 1;
473 // For the view-source scheme, we should emphasize the scheme and host of the
474 // URL qualified by the view-source prefix.
475 if (base::LowerCaseEqualsASCII(scheme_str
, kViewSourceScheme
) &&
476 (static_cast<int>(text
.length()) > after_scheme_and_colon
)) {
477 // Obtain the URL prefixed by view-source and parse it.
478 base::string16
real_url(text
.substr(after_scheme_and_colon
));
479 url::Parsed real_parts
;
480 AutocompleteInput::Parse(real_url
, std::string(), scheme_classifier
,
481 &real_parts
, NULL
, NULL
);
482 if (real_parts
.scheme
.is_nonempty() || real_parts
.host
.is_nonempty()) {
483 if (real_parts
.scheme
.is_nonempty()) {
484 *scheme
= url::Component(
485 after_scheme_and_colon
+ real_parts
.scheme
.begin
,
486 real_parts
.scheme
.len
);
490 if (real_parts
.host
.is_nonempty()) {
491 *host
= url::Component(after_scheme_and_colon
+ real_parts
.host
.begin
,
492 real_parts
.host
.len
);
497 } else if (base::LowerCaseEqualsASCII(scheme_str
, url::kFileSystemScheme
) &&
498 parts
.inner_parsed() && parts
.inner_parsed()->scheme
.is_valid()) {
499 *host
= parts
.inner_parsed()->host
;
504 base::string16
AutocompleteInput::FormattedStringWithEquivalentMeaning(
506 const base::string16
& formatted_url
,
507 const AutocompleteSchemeClassifier
& scheme_classifier
) {
508 if (!url_formatter::CanStripTrailingSlash(url
))
509 return formatted_url
;
510 const base::string16
url_with_path(formatted_url
+ base::char16('/'));
511 return (AutocompleteInput::Parse(formatted_url
, std::string(),
512 scheme_classifier
, NULL
, NULL
, NULL
) ==
513 AutocompleteInput::Parse(url_with_path
, std::string(),
514 scheme_classifier
, NULL
, NULL
, NULL
)) ?
515 formatted_url
: url_with_path
;
519 int AutocompleteInput::NumNonHostComponents(const url::Parsed
& parts
) {
520 int num_nonhost_components
= 0;
521 if (parts
.scheme
.is_nonempty())
522 ++num_nonhost_components
;
523 if (parts
.username
.is_nonempty())
524 ++num_nonhost_components
;
525 if (parts
.password
.is_nonempty())
526 ++num_nonhost_components
;
527 if (parts
.port
.is_nonempty())
528 ++num_nonhost_components
;
529 if (parts
.path
.is_nonempty())
530 ++num_nonhost_components
;
531 if (parts
.query
.is_nonempty())
532 ++num_nonhost_components
;
533 if (parts
.ref
.is_nonempty())
534 ++num_nonhost_components
;
535 return num_nonhost_components
;
539 bool AutocompleteInput::HasHTTPScheme(const base::string16
& input
) {
540 std::string
utf8_input(base::UTF16ToUTF8(input
));
541 url::Component scheme
;
542 if (url::FindAndCompareScheme(utf8_input
, kViewSourceScheme
, &scheme
)) {
543 utf8_input
.erase(0, scheme
.end() + 1);
545 return url::FindAndCompareScheme(utf8_input
, url::kHttpScheme
, NULL
);
548 void AutocompleteInput::UpdateText(const base::string16
& text
,
549 size_t cursor_position
,
550 const url::Parsed
& parts
) {
551 DCHECK(cursor_position
<= text
.length() ||
552 cursor_position
== base::string16::npos
)
553 << "Text: '" << text
<< "', cp: " << cursor_position
;
555 cursor_position_
= cursor_position
;
559 void AutocompleteInput::Clear() {
561 cursor_position_
= base::string16::npos
;
562 current_url_
= GURL();
563 current_page_classification_
= metrics::OmniboxEventProto::INVALID_SPEC
;
564 type_
= metrics::OmniboxInputType::INVALID
;
565 parts_
= url::Parsed();
567 canonicalized_url_
= GURL();
568 prevent_inline_autocomplete_
= false;
569 prefer_keyword_
= false;
570 allow_exact_keyword_match_
= false;
571 want_asynchronous_matches_
= true;
572 from_omnibox_focus_
= false;
573 terms_prefixed_by_http_or_https_
.clear();