blob: 32660116c72eadd0294b470948850b0f58fff760 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/omnibox/browser/location_bar_model_impl.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/omnibox/browser/autocomplete_classifier.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/buildflags.h"
#include "components/omnibox/browser/location_bar_model_delegate.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url_service.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/text_elider.h"
#include "ui/gfx/vector_icon_types.h"
#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
#include "components/omnibox/browser/vector_icons.h" // nogncheck
#include "components/vector_icons/vector_icons.h" // nogncheck
#endif
LocationBarModelImpl::LocationBarModelImpl(LocationBarModelDelegate* delegate,
size_t max_url_display_chars)
: delegate_(delegate), max_url_display_chars_(max_url_display_chars) {
DCHECK(delegate_);
}
LocationBarModelImpl::~LocationBarModelImpl() {}
// LocationBarModelImpl Implementation.
base::string16 LocationBarModelImpl::GetFormattedFullURL() const {
return GetFormattedURL(url_formatter::kFormatUrlOmitDefaults);
}
base::string16 LocationBarModelImpl::GetURLForDisplay() const {
url_formatter::FormatUrlTypes format_types =
url_formatter::kFormatUrlOmitDefaults;
// Early exit to prevent elision of URLs when relevant extension is enabled.
if (delegate_->ShouldPreventElision()) {
return GetFormattedURL(format_types);
}
#if defined(OS_IOS)
format_types |= url_formatter::kFormatUrlTrimAfterHost;
#endif
if (OmniboxFieldTrial::IsHideSteadyStateUrlSchemeEnabled())
format_types |= url_formatter::kFormatUrlOmitHTTPS;
if (OmniboxFieldTrial::IsHideSteadyStateUrlTrivialSubdomainsEnabled())
format_types |= url_formatter::kFormatUrlOmitTrivialSubdomains;
if (base::FeatureList::IsEnabled(omnibox::kHideFileUrlScheme))
format_types |= url_formatter::kFormatUrlOmitFileScheme;
return GetFormattedURL(format_types);
}
base::string16 LocationBarModelImpl::GetFormattedURL(
url_formatter::FormatUrlTypes format_types) const {
GURL url(GetURL());
// Note that we can't unescape spaces here, because if the user copies this
// and pastes it into another program, that program may think the URL ends at
// the space.
const base::string16 formatted_text =
delegate_->FormattedStringWithEquivalentMeaning(
url,
url_formatter::FormatUrl(url, format_types, net::UnescapeRule::NORMAL,
nullptr, nullptr, nullptr));
// Truncating the URL breaks editing and then pressing enter, but hopefully
// people won't try to do much with such enormous URLs anyway. If this becomes
// a real problem, we could perhaps try to keep some sort of different "elided
// visible URL" where editing affects and reloads the "real underlying URL",
// but this seems very tricky for little gain.
return gfx::TruncateString(formatted_text, max_url_display_chars_,
gfx::CHARACTER_BREAK);
}
GURL LocationBarModelImpl::GetURL() const {
GURL url;
return delegate_->GetURL(&url) ? url : GURL(url::kAboutBlankURL);
}
security_state::SecurityLevel LocationBarModelImpl::GetSecurityLevel() const {
// When empty, assume no security style.
if (!ShouldDisplayURL())
return security_state::NONE;
return delegate_->GetSecurityLevel();
}
bool LocationBarModelImpl::GetDisplaySearchTerms(base::string16* search_terms) {
if (!base::FeatureList::IsEnabled(omnibox::kQueryInOmnibox) ||
delegate_->ShouldPreventElision())
return false;
// Only show the search terms if the site is secure. However, make an
// exception before the security state is initialized to prevent a UI flicker.
std::unique_ptr<security_state::VisibleSecurityState> visible_security_state =
delegate_->GetVisibleSecurityState();
security_state::SecurityLevel security_level = delegate_->GetSecurityLevel();
if (visible_security_state->connection_info_initialized &&
security_level != security_state::SecurityLevel::SECURE &&
security_level != security_state::SecurityLevel::EV_SECURE) {
return false;
}
base::string16 extracted_search_terms = ExtractSearchTermsInternal(GetURL());
if (extracted_search_terms.empty())
return false;
if (search_terms)
*search_terms = extracted_search_terms;
return true;
}
const gfx::VectorIcon& LocationBarModelImpl::GetVectorIcon() const {
#if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
auto* const icon_override = delegate_->GetVectorIconOverride();
if (icon_override)
return *icon_override;
if (IsOfflinePage())
return omnibox::kOfflinePinIcon;
switch (GetSecurityLevel()) {
case security_state::NONE:
case security_state::HTTP_SHOW_WARNING:
return omnibox::kHttpIcon;
case security_state::EV_SECURE:
case security_state::SECURE:
return omnibox::kHttpsValidIcon;
case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
return vector_icons::kBusinessIcon;
case security_state::DANGEROUS:
return omnibox::kHttpsInvalidIcon;
case security_state::SECURITY_LEVEL_COUNT:
NOTREACHED();
return omnibox::kHttpIcon;
}
NOTREACHED();
return omnibox::kHttpIcon;
#else
NOTREACHED();
static const gfx::VectorIcon dummy = {};
return dummy;
#endif
}
LocationBarModelImpl::SecureChipText LocationBarModelImpl::GetSecureChipText()
const {
// Note that displayed text (the first output) will be implicitly used as the
// accessibility text unless no display text has been specified.
// Security UI study (https://crbug.com/803501): Change EV/Secure text.
const std::string securityUIStudyParam =
base::FeatureList::IsEnabled(omnibox::kSimplifyHttpsIndicator)
? base::GetFieldTrialParamValueByFeature(
omnibox::kSimplifyHttpsIndicator,
OmniboxFieldTrial::kSimplifyHttpsIndicatorParameterName)
: std::string();
if (IsOfflinePage())
return SecureChipText(l10n_util::GetStringUTF16(IDS_OFFLINE_VERBOSE_STATE));
switch (GetSecurityLevel()) {
case security_state::HTTP_SHOW_WARNING:
return SecureChipText(
l10n_util::GetStringUTF16(IDS_NOT_SECURE_VERBOSE_STATE));
case security_state::EV_SECURE: {
if (securityUIStudyParam ==
OmniboxFieldTrial::kSimplifyHttpsIndicatorParameterEvToSecure)
return SecureChipText(
l10n_util::GetStringUTF16(IDS_SECURE_VERBOSE_STATE));
if (securityUIStudyParam ==
OmniboxFieldTrial::kSimplifyHttpsIndicatorParameterBothToLock)
return SecureChipText(base::string16(), l10n_util::GetStringUTF16(
IDS_SECURE_VERBOSE_STATE));
// Note: Cert is guaranteed non-NULL or the security level would be NONE.
scoped_refptr<net::X509Certificate> cert = delegate_->GetCertificate();
DCHECK(cert);
// EV are required to have an organization name and country.
DCHECK(!cert->subject().organization_names.empty());
DCHECK(!cert->subject().country_name.empty());
return SecureChipText(l10n_util::GetStringFUTF16(
IDS_SECURE_CONNECTION_EV,
base::UTF8ToUTF16(cert->subject().organization_names[0]),
base::UTF8ToUTF16(cert->subject().country_name)));
}
case security_state::SECURE:
if (securityUIStudyParam !=
OmniboxFieldTrial::kSimplifyHttpsIndicatorParameterKeepSecureChip)
return SecureChipText(base::string16(), l10n_util::GetStringUTF16(
IDS_SECURE_VERBOSE_STATE));
return SecureChipText(
l10n_util::GetStringUTF16(IDS_SECURE_VERBOSE_STATE));
case security_state::DANGEROUS: {
std::unique_ptr<security_state::VisibleSecurityState>
visible_security_state = delegate_->GetVisibleSecurityState();
// Don't show any text in the security indicator for sites on the billing
// interstitial list.
if (visible_security_state->malicious_content_status ==
security_state::MALICIOUS_CONTENT_STATUS_BILLING) {
#if defined(OS_IOS)
// On iOS, we never expect this status, because there are no billing
// interstitials.
NOTREACHED();
#endif
return SecureChipText(base::string16());
}
bool fails_malware_check =
visible_security_state->malicious_content_status !=
security_state::MALICIOUS_CONTENT_STATUS_NONE;
return SecureChipText(l10n_util::GetStringUTF16(
fails_malware_check ? IDS_DANGEROUS_VERBOSE_STATE
: IDS_NOT_SECURE_VERBOSE_STATE));
}
default:
return SecureChipText(base::string16());
}
}
base::string16 LocationBarModelImpl::GetSecureDisplayText() const {
return GetSecureChipText().display_text_;
}
base::string16 LocationBarModelImpl::GetSecureAccessibilityText() const {
auto labels = GetSecureChipText();
return labels.display_text_.empty() ? labels.accessibility_label_
: labels.display_text_;
}
bool LocationBarModelImpl::ShouldDisplayURL() const {
return delegate_->ShouldDisplayURL();
}
bool LocationBarModelImpl::IsOfflinePage() const {
return delegate_->IsOfflinePage();
}
base::string16 LocationBarModelImpl::ExtractSearchTermsInternal(
const GURL& url) {
AutocompleteClassifier* autocomplete_classifier =
delegate_->GetAutocompleteClassifier();
TemplateURLService* template_url_service = delegate_->GetTemplateURLService();
if (!autocomplete_classifier || !template_url_service)
return base::string16();
if (url.is_empty())
return base::string16();
// Because we cache keyed by URL, if the user changes the default search
// provider, we will continue to extract the search terms from the cached URL
// (even if it's no longer from the default search provider) until the user
// changes tabs or navigates the tab. That is intentional, as it would be
// weird otherwise if the omnibox text changed without any user gesture.
if (url != cached_url_) {
cached_url_ = url;
cached_search_terms_.clear();
const TemplateURL* default_provider =
template_url_service->GetDefaultSearchProvider();
if (default_provider) {
// If |url| doesn't match the default search provider,
// |cached_search_terms_| will remain empty.
default_provider->ExtractSearchTermsFromURL(
url, template_url_service->search_terms_data(),
&cached_search_terms_);
// Clear out the search terms if it looks like a URL.
AutocompleteMatch match;
autocomplete_classifier->Classify(
cached_search_terms_, false, false,
metrics::OmniboxEventProto::INVALID_SPEC, &match, nullptr);
if (!AutocompleteMatch::IsSearchType(match.type))
cached_search_terms_.clear();
}
}
return cached_search_terms_;
}