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