This source file includes following definitions.
- AdjustCursorPositionIfNecessary
- matches_requested_
- matches_requested_
- RemoveForcedQueryStringIfNecessary
- TypeToString
- Parse
- ParseForEmphasizeComponents
- FormattedStringWithEquivalentMeaning
- NumNonHostComponents
- HasHTTPScheme
- UpdateText
- Clear
#include "chrome/browser/autocomplete/autocomplete_input.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/common/net/url_fixer_upper.h"
#include "content/public/common/url_constants.h"
#include "net/base/net_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/url_canon_ip.h"
#include "url/url_util.h"
namespace {
void AdjustCursorPositionIfNecessary(size_t num_leading_chars_removed,
size_t* cursor_position) {
if (*cursor_position == base::string16::npos)
return;
if (num_leading_chars_removed < *cursor_position)
*cursor_position -= num_leading_chars_removed;
else
*cursor_position = 0;
}
}
AutocompleteInput::AutocompleteInput()
: cursor_position_(base::string16::npos),
current_page_classification_(AutocompleteInput::INVALID_SPEC),
type_(INVALID),
prevent_inline_autocomplete_(false),
prefer_keyword_(false),
allow_exact_keyword_match_(true),
matches_requested_(ALL_MATCHES) {
}
AutocompleteInput::AutocompleteInput(
const base::string16& text,
size_t cursor_position,
const base::string16& desired_tld,
const GURL& current_url,
AutocompleteInput::PageClassification current_page_classification,
bool prevent_inline_autocomplete,
bool prefer_keyword,
bool allow_exact_keyword_match,
MatchesRequested matches_requested)
: cursor_position_(cursor_position),
current_url_(current_url),
current_page_classification_(current_page_classification),
prevent_inline_autocomplete_(prevent_inline_autocomplete),
prefer_keyword_(prefer_keyword),
allow_exact_keyword_match_(allow_exact_keyword_match),
matches_requested_(matches_requested) {
DCHECK(cursor_position <= text.length() ||
cursor_position == base::string16::npos)
<< "Text: '" << text << "', cp: " << cursor_position;
if ((base::TrimWhitespace(text, base::TRIM_LEADING, &text_) &
base::TRIM_LEADING) != 0)
AdjustCursorPositionIfNecessary(text.length() - text_.length(),
&cursor_position_);
GURL canonicalized_url;
type_ = Parse(text_, desired_tld, &parts_, &scheme_, &canonicalized_url);
if (type_ == INVALID)
return;
if (((type_ == UNKNOWN) || (type_ == URL)) &&
canonicalized_url.is_valid() &&
(!canonicalized_url.IsStandard() || canonicalized_url.SchemeIsFile() ||
canonicalized_url.SchemeIsFileSystem() ||
!canonicalized_url.host().empty()))
canonicalized_url_ = canonicalized_url;
size_t chars_removed = RemoveForcedQueryStringIfNecessary(type_, &text_);
AdjustCursorPositionIfNecessary(chars_removed, &cursor_position_);
if (chars_removed) {
base::string16 trimmed_text;
if ((base::TrimWhitespace(text_, base::TRIM_LEADING, &trimmed_text) &
base::TRIM_LEADING) != 0) {
AdjustCursorPositionIfNecessary(text_.length() - trimmed_text.length(),
&cursor_position_);
text_ = trimmed_text;
}
}
}
AutocompleteInput::~AutocompleteInput() {
}
size_t AutocompleteInput::RemoveForcedQueryStringIfNecessary(
Type type,
base::string16* text) {
if (type != FORCED_QUERY || text->empty() || (*text)[0] != L'?')
return 0;
text->erase(0, 1);
return 1;
}
std::string AutocompleteInput::TypeToString(Type type) {
switch (type) {
case INVALID: return "invalid";
case UNKNOWN: return "unknown";
case URL: return "url";
case QUERY: return "query";
case FORCED_QUERY: return "forced-query";
default:
NOTREACHED();
return std::string();
}
}
AutocompleteInput::Type AutocompleteInput::Parse(
const base::string16& text,
const base::string16& desired_tld,
url_parse::Parsed* parts,
base::string16* scheme,
GURL* canonicalized_url) {
size_t first_non_white = text.find_first_not_of(base::kWhitespaceUTF16, 0);
if (first_non_white == base::string16::npos)
return INVALID;
if (text.at(first_non_white) == L'?') {
return FORCED_QUERY;
}
url_parse::Parsed local_parts;
if (!parts)
parts = &local_parts;
const base::string16 parsed_scheme(URLFixerUpper::SegmentURL(text, parts));
if (scheme)
*scheme = parsed_scheme;
GURL placeholder_canonicalized_url;
if (!canonicalized_url)
canonicalized_url = &placeholder_canonicalized_url;
*canonicalized_url = URLFixerUpper::FixupURL(base::UTF16ToUTF8(text),
base::UTF16ToUTF8(desired_tld));
Type return_value_for_non_http_url =
canonicalized_url->is_valid() ? URL : QUERY;
if (LowerCaseEqualsASCII(parsed_scheme, content::kFileScheme)) {
return URL;
}
if (parts->scheme.is_nonempty() &&
!LowerCaseEqualsASCII(parsed_scheme, content::kHttpScheme) &&
!LowerCaseEqualsASCII(parsed_scheme, content::kHttpsScheme)) {
if (ProfileIOData::IsHandledProtocol(base::UTF16ToASCII(parsed_scheme)) ||
LowerCaseEqualsASCII(parsed_scheme, content::kViewSourceScheme) ||
LowerCaseEqualsASCII(parsed_scheme, content::kJavaScriptScheme) ||
LowerCaseEqualsASCII(parsed_scheme, content::kDataScheme))
return return_value_for_non_http_url;
ExternalProtocolHandler::BlockState block_state =
ExternalProtocolHandler::GetBlockState(
base::UTF16ToUTF8(parsed_scheme), true);
switch (block_state) {
case ExternalProtocolHandler::DONT_BLOCK:
return return_value_for_non_http_url;
case ExternalProtocolHandler::BLOCK:
return QUERY;
default: {
const base::string16 http_scheme_prefix =
base::ASCIIToUTF16(std::string(content::kHttpScheme) +
content::kStandardSchemeSeparator);
url_parse::Parsed http_parts;
base::string16 http_scheme;
GURL http_canonicalized_url;
Type http_type = Parse(http_scheme_prefix + text, desired_tld,
&http_parts, &http_scheme,
&http_canonicalized_url);
DCHECK_EQ(std::string(content::kHttpScheme),
base::UTF16ToUTF8(http_scheme));
if ((http_type == URL) && http_parts.username.is_nonempty() &&
http_parts.password.is_nonempty()) {
http_parts.scheme.reset();
url_parse::Component* components[] = {
&http_parts.username,
&http_parts.password,
&http_parts.host,
&http_parts.port,
&http_parts.path,
&http_parts.query,
&http_parts.ref,
};
for (size_t i = 0; i < arraysize(components); ++i) {
URLFixerUpper::OffsetComponent(
-static_cast<int>(http_scheme_prefix.length()), components[i]);
}
*parts = http_parts;
if (scheme)
scheme->clear();
*canonicalized_url = http_canonicalized_url;
return URL;
}
return UNKNOWN;
}
}
}
if (!parts->host.is_nonempty())
return QUERY;
const base::string16 host(text.substr(parts->host.begin, parts->host.len));
const size_t registry_length =
net::registry_controlled_domains::GetRegistryLength(
base::UTF16ToUTF8(host),
net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
if (registry_length == std::string::npos) {
if (!desired_tld.empty()) {
base::string16 host_with_tld(host);
if (host[host.length() - 1] != '.')
host_with_tld += '.';
host_with_tld += desired_tld;
const size_t tld_length =
net::registry_controlled_domains::GetRegistryLength(
base::UTF16ToUTF8(host_with_tld),
net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
if (tld_length != std::string::npos)
return URL;
}
return QUERY;
}
url_canon::CanonHostInfo host_info;
const std::string canonicalized_host(net::CanonicalizeHost(
base::UTF16ToUTF8(host), &host_info));
if ((host_info.family == url_canon::CanonHostInfo::NEUTRAL) &&
!net::IsCanonicalizedHostCompliant(canonicalized_host,
base::UTF16ToUTF8(desired_tld))) {
return (parts->scheme.is_nonempty() ||
((registry_length != 0) &&
(host.find(' ') == base::string16::npos))) ? UNKNOWN : QUERY;
}
if (url_parse::ParsePort(text.c_str(), parts->port) ==
url_parse::PORT_INVALID)
return QUERY;
if (parts->scheme.is_nonempty())
return URL;
if (host_info.family == url_canon::CanonHostInfo::IPV6)
return URL;
if ((host_info.family == url_canon::CanonHostInfo::IPV4) &&
(host_info.num_ipv4_components == 4))
return URL;
if (parts->password.is_nonempty())
return URL;
if (parts->path.is_nonempty()) {
char c = text[parts->path.end() - 1];
if ((c == '\\') || (c == '/'))
return URL;
}
if (NumNonHostComponents(*parts) > 1)
return URL;
if ((host_info.family != url_canon::CanonHostInfo::IPV4) &&
((registry_length != 0) || (host == base::ASCIIToUTF16("localhost") ||
parts->port.is_nonempty())))
return parts->username.is_nonempty() ? UNKNOWN : URL;
if (!desired_tld.empty())
return URL;
return UNKNOWN;
}
void AutocompleteInput::ParseForEmphasizeComponents(
const base::string16& text,
url_parse::Component* scheme,
url_parse::Component* host) {
url_parse::Parsed parts;
base::string16 scheme_str;
Parse(text, base::string16(), &parts, &scheme_str, NULL);
*scheme = parts.scheme;
*host = parts.host;
int after_scheme_and_colon = parts.scheme.end() + 1;
if (LowerCaseEqualsASCII(scheme_str, content::kViewSourceScheme) &&
(static_cast<int>(text.length()) > after_scheme_and_colon)) {
base::string16 real_url(text.substr(after_scheme_and_colon));
url_parse::Parsed real_parts;
AutocompleteInput::Parse(real_url, base::string16(), &real_parts, NULL, NULL);
if (real_parts.scheme.is_nonempty() || real_parts.host.is_nonempty()) {
if (real_parts.scheme.is_nonempty()) {
*scheme = url_parse::Component(
after_scheme_and_colon + real_parts.scheme.begin,
real_parts.scheme.len);
} else {
scheme->reset();
}
if (real_parts.host.is_nonempty()) {
*host = url_parse::Component(
after_scheme_and_colon + real_parts.host.begin,
real_parts.host.len);
} else {
host->reset();
}
}
} else if (LowerCaseEqualsASCII(scheme_str, content::kFileSystemScheme) &&
parts.inner_parsed() && parts.inner_parsed()->scheme.is_valid()) {
*host = parts.inner_parsed()->host;
}
}
base::string16 AutocompleteInput::FormattedStringWithEquivalentMeaning(
const GURL& url,
const base::string16& formatted_url) {
if (!net::CanStripTrailingSlash(url))
return formatted_url;
const base::string16 url_with_path(formatted_url + base::char16('/'));
return (AutocompleteInput::Parse(formatted_url, base::string16(), NULL, NULL,
NULL) ==
AutocompleteInput::Parse(url_with_path, base::string16(), NULL, NULL,
NULL)) ?
formatted_url : url_with_path;
}
int AutocompleteInput::NumNonHostComponents(const url_parse::Parsed& parts) {
int num_nonhost_components = 0;
if (parts.scheme.is_nonempty())
++num_nonhost_components;
if (parts.username.is_nonempty())
++num_nonhost_components;
if (parts.password.is_nonempty())
++num_nonhost_components;
if (parts.port.is_nonempty())
++num_nonhost_components;
if (parts.path.is_nonempty())
++num_nonhost_components;
if (parts.query.is_nonempty())
++num_nonhost_components;
if (parts.ref.is_nonempty())
++num_nonhost_components;
return num_nonhost_components;
}
bool AutocompleteInput::HasHTTPScheme(const base::string16& input) {
std::string utf8_input(base::UTF16ToUTF8(input));
url_parse::Component scheme;
if (url_util::FindAndCompareScheme(utf8_input, content::kViewSourceScheme,
&scheme))
utf8_input.erase(0, scheme.end() + 1);
return url_util::FindAndCompareScheme(utf8_input, content::kHttpScheme, NULL);
}
void AutocompleteInput::UpdateText(const base::string16& text,
size_t cursor_position,
const url_parse::Parsed& parts) {
DCHECK(cursor_position <= text.length() ||
cursor_position == base::string16::npos)
<< "Text: '" << text << "', cp: " << cursor_position;
text_ = text;
cursor_position_ = cursor_position;
parts_ = parts;
}
void AutocompleteInput::Clear() {
text_.clear();
cursor_position_ = base::string16::npos;
current_url_ = GURL();
current_page_classification_ = AutocompleteInput::INVALID_SPEC;
type_ = INVALID;
parts_ = url_parse::Parsed();
scheme_.clear();
canonicalized_url_ = GURL();
prevent_inline_autocomplete_ = false;
prefer_keyword_ = false;
allow_exact_keyword_match_ = false;
matches_requested_ = ALL_MATCHES;
}