| // 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_; |
| } |