blob: 8b736ec5e7c3fc0648466b1c9a91b3da594bb100 [file] [log] [blame] [edit]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/verbatim_match.h"
#include "base/containers/fixed_flat_set.h"
#include "components/omnibox/browser/autocomplete_classifier.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match_classification.h"
#include "components/omnibox/browser/autocomplete_provider.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/in_memory_url_index_types.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/common/url_constants.h"
#include "extensions/buildflags/buildflags.h"
#include "third_party/metrics_proto/omnibox_scoring_signals.pb.h"
#include "url/gurl.h"
#include "url/url_constants.h"
#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
#include "extensions/common/constants.h"
#endif
namespace {
#if BUILDFLAG(IS_ANDROID)
// Note: On Android, restrict the verbatim URLs allowed to be default. We
// explicitly exclude schemes that may be used to execute Javascript code
// snippet in the context of the current page on mobile devices.
constexpr auto kAndroidNavigableSchemes =
base::MakeFixedFlatSet<std::string_view>({
url::kHttpScheme,
url::kHttpsScheme,
url::kAboutScheme,
content::kChromeUIScheme,
#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
// On desktop, extensions are always enabled, `kAndroidNavigableSchemes`
// is not used, and verbatim extension URLs are always allowed to be
// default. On mobile android, extensions are disabled,
// `ENABLE_EXTENSIONS_CORE` is false, and verbatim extension URLs are
// not allowed to be default. On desktop android, extensions are enabled
// and verbatim extension URLs are allowed to be default depending on
// `ENABLE_EXTENSIONS_CORE`.
extensions::kExtensionScheme,
#endif
});
#endif // BUILDFLAG(IS_ANDROID)
} // namespace
AutocompleteMatch VerbatimMatchForURL(
AutocompleteProvider* provider,
AutocompleteProviderClient* client,
const AutocompleteInput& input,
const GURL& destination_url,
const std::u16string& destination_description,
int verbatim_relevance) {
AutocompleteMatch match;
// If the caller is a provider and already knows where the verbatim match
// should go, construct the match directly, don't call Classify() on the
// input. Classify() runs all providers' synchronous passes. Some providers
// such as HistoryQuick can have a slow synchronous pass on some inputs.
if (provider != nullptr && destination_url.is_valid()) {
match =
VerbatimMatchForInput(provider, client, input, destination_url,
!AutocompleteInput::HasHTTPScheme(input.text()));
match.description = destination_description;
if (!match.description.empty())
match.description_class.push_back({0, ACMatchClassification::NONE});
} else {
client->Classify(input.text(), false, true,
input.current_page_classification(), &match, nullptr);
}
match.allowed_to_be_default_match = true;
// The default relevance to use for relevance match. Should be greater than
// all relevance matches returned by the ZeroSuggest server.
const int kDefaultVerbatimRelevance = 1300;
match.relevance =
verbatim_relevance >= 0 ? verbatim_relevance : kDefaultVerbatimRelevance;
return match;
}
AutocompleteMatch VerbatimMatchForInput(AutocompleteProvider* provider,
AutocompleteProviderClient* client,
const AutocompleteInput& input,
const GURL& destination_url,
bool trim_default_scheme) {
AutocompleteMatch match(provider, 0, false,
AutocompleteMatchType::URL_WHAT_YOU_TYPED);
if (destination_url.is_valid()) {
match.destination_url = destination_url;
// If the input explicitly contains "http://" or "https://", callers must
// set |trim_default_scheme| to false. Otherwise, |trim_default_scheme| may
// be either true or false.
if (input.added_default_scheme_to_typed_url()) {
DCHECK(!(trim_default_scheme &&
AutocompleteInput::HasHTTPSScheme(input.text())));
} else {
DCHECK(!(trim_default_scheme &&
AutocompleteInput::HasHTTPScheme(input.text())));
}
const url_formatter::FormatUrlType format_type =
input.added_default_scheme_to_typed_url()
? url_formatter::kFormatUrlOmitHTTPS
: url_formatter::kFormatUrlOmitHTTP;
std::u16string display_string(url_formatter::FormatUrl(
destination_url, url_formatter::kFormatUrlOmitDefaults & ~format_type,
base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
if (trim_default_scheme) {
AutocompleteProvider::TrimSchemePrefix(
&display_string, input.added_default_scheme_to_typed_url());
}
match.fill_into_edit =
AutocompleteInput::FormattedStringWithEquivalentMeaning(
destination_url, display_string, client->GetSchemeClassifier(),
nullptr);
// The what-you-typed match is generally only allowed to be default for
// URL inputs or when there is no default search provider. (It's also
// allowed to be default for UNKNOWN inputs where the destination is a known
// intranet site. In this case, |allowed_to_be_default_match| is revised in
// FixupExactSuggestion().)
const bool has_default_search_provider =
client->GetTemplateURLService() &&
client->GetTemplateURLService()->GetDefaultSearchProvider();
match.allowed_to_be_default_match =
(input.type() == metrics::OmniboxInputType::URL) ||
!has_default_search_provider;
#if BUILDFLAG(IS_ANDROID)
// Disallow non-navigable schemes to be default. This prevents javascript:
// snippets from being accidentally executed upon paste, refine, edit, etc.
match.allowed_to_be_default_match &=
kAndroidNavigableSchemes.contains(destination_url.scheme());
#endif
// NOTE: Don't set match.inline_autocompletion to something non-empty here;
// it's surprising and annoying.
// Try to highlight "innermost" match location. If we fix up "w" into
// "www.w.com", we want to highlight the fifth character, not the first.
// This relies on match.destination_url being the non-prefix-trimmed version
// of match.contents.
match.contents = display_string;
TermMatches termMatches = {{0, 0, input.text().length()}};
match.contents_class = ClassifyTermMatches(
termMatches, match.contents.size(),
ACMatchClassification::MATCH | ACMatchClassification::URL,
ACMatchClassification::URL);
// Only set scoring signals for eligible matches.
if (match.IsMlSignalLoggingEligible()) {
if (!match.scoring_signals) {
match.scoring_signals =
std::make_optional<::metrics::OmniboxScoringSignals>();
}
match.scoring_signals->set_is_verbatim(true);
}
}
return match;
}