blob: 38193575b75b74e8839fb299b5749d0f78f90d27 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/permissions/chrome_permissions_client.h"
#include <vector>
#include "base/feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/bluetooth/bluetooth_chooser_context.h"
#include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/engagement/important_sites_util.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "chrome/browser/metrics/ukm_background_recorder_service.h"
#include "chrome/browser/permissions/abusive_origin_permission_revocation_request.h"
#include "chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.h"
#include "chrome/browser/permissions/contextual_notification_permission_ui_selector.h"
#include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/permissions/prediction_based_permission_ui_selector.h"
#include "chrome/browser/permissions/pref_notification_permission_ui_selector.h"
#include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
#include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
#include "chrome/browser/usb/usb_chooser_context.h"
#include "chrome/browser/usb/usb_chooser_context_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/google/core/common/google_util.h"
#include "components/permissions/features.h"
#include "components/prefs/pref_service.h"
#include "components/subresource_filter/content/browser/subresource_filter_content_settings_manager.h"
#include "components/subresource_filter/content/browser/subresource_filter_profile_context.h"
#include "components/ukm/content/source_url_recorder.h"
#include "extensions/common/constants.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/origin.h"
#if defined(OS_ANDROID)
#include "chrome/browser/android/resource_mapper.h"
#include "chrome/browser/android/search_permissions/search_permissions_service.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/permissions/grouped_permission_infobar_delegate_android.h"
#include "chrome/browser/permissions/permission_update_infobar_delegate_android.h"
#else
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
#endif
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h"
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#endif
// static
ChromePermissionsClient* ChromePermissionsClient::GetInstance() {
static base::NoDestructor<ChromePermissionsClient> instance;
return instance.get();
}
HostContentSettingsMap* ChromePermissionsClient::GetSettingsMap(
content::BrowserContext* browser_context) {
return HostContentSettingsMapFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
}
scoped_refptr<content_settings::CookieSettings>
ChromePermissionsClient::GetCookieSettings(
content::BrowserContext* browser_context) {
return CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
}
bool ChromePermissionsClient::IsSubresourceFilterActivated(
content::BrowserContext* browser_context,
const GURL& url) {
return SubresourceFilterProfileContextFactory::GetForProfile(
Profile::FromBrowserContext(browser_context))
->settings_manager()
->GetSiteActivationFromMetadata(url);
}
permissions::ChooserContextBase* ChromePermissionsClient::GetChooserContext(
content::BrowserContext* browser_context,
ContentSettingsType type) {
switch (type) {
case ContentSettingsType::USB_CHOOSER_DATA:
return UsbChooserContextFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
case ContentSettingsType::BLUETOOTH_CHOOSER_DATA:
return BluetoothChooserContextFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
default:
NOTREACHED();
return nullptr;
}
}
permissions::PermissionDecisionAutoBlocker*
ChromePermissionsClient::GetPermissionDecisionAutoBlocker(
content::BrowserContext* browser_context) {
return PermissionDecisionAutoBlockerFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
}
permissions::PermissionManager* ChromePermissionsClient::GetPermissionManager(
content::BrowserContext* browser_context) {
return PermissionManagerFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
}
double ChromePermissionsClient::GetSiteEngagementScore(
content::BrowserContext* browser_context,
const GURL& origin) {
return SiteEngagementService::Get(
Profile::FromBrowserContext(browser_context))
->GetScore(origin);
}
void ChromePermissionsClient::AreSitesImportant(
content::BrowserContext* browser_context,
std::vector<std::pair<url::Origin, bool>>* origins) {
// We need to limit our size due to the algorithm in ImportantSiteUtil,
// but we want to be more on the liberal side here as we're not exposing
// these sites to the user, we're just using them for our 'clear
// unimportant' feature in ManageSpaceActivity.java.
const int kMaxImportantSites = 10;
std::vector<ImportantSitesUtil::ImportantDomainInfo> important_domains =
ImportantSitesUtil::GetImportantRegisterableDomains(
Profile::FromBrowserContext(browser_context), kMaxImportantSites);
for (auto& entry : *origins) {
const url::Origin& origin = entry.first;
const std::string& host = origin.host();
std::string registerable_domain =
net::registry_controlled_domains::GetDomainAndRegistry(
origin,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (registerable_domain.empty())
registerable_domain = host; // IP address or internal hostname.
auto important_domain_search =
[&registerable_domain](
const ImportantSitesUtil::ImportantDomainInfo& item) {
return item.registerable_domain == registerable_domain;
};
entry.second =
std::find_if(important_domains.begin(), important_domains.end(),
important_domain_search) != important_domains.end();
}
}
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
// Some Google-affiliated domains are not allowed to delete cookies for
// supervised accounts.
bool ChromePermissionsClient::IsCookieDeletionDisabled(
content::BrowserContext* browser_context,
const GURL& origin) {
if (!Profile::FromBrowserContext(browser_context)->IsChild())
return false;
return google_util::IsYoutubeDomainUrl(origin, google_util::ALLOW_SUBDOMAIN,
google_util::ALLOW_NON_STANDARD_PORTS);
}
#endif
void ChromePermissionsClient::GetUkmSourceId(
content::BrowserContext* browser_context,
const content::WebContents* web_contents,
const GURL& requesting_origin,
GetUkmSourceIdCallback callback) {
if (web_contents) {
ukm::SourceId source_id =
ukm::GetSourceIdForWebContentsDocument(web_contents);
std::move(callback).Run(source_id);
} else {
// We only record a permission change if the origin is in the user's
// history.
ukm::UkmBackgroundRecorderFactory::GetForProfile(
Profile::FromBrowserContext(browser_context))
->GetBackgroundSourceIdIfAllowed(url::Origin::Create(requesting_origin),
std::move(callback));
}
}
permissions::PermissionRequest::IconId
ChromePermissionsClient::GetOverrideIconId(ContentSettingsType type) {
#if defined(OS_CHROMEOS)
// TODO(xhwang): fix this icon, see crbug.com/446263.
if (type == ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER)
return kProductIcon;
#endif
return PermissionsClient::GetOverrideIconId(type);
}
std::vector<std::unique_ptr<permissions::NotificationPermissionUiSelector>>
ChromePermissionsClient::CreateNotificationPermissionUiSelectors(
content::BrowserContext* browser_context) {
std::vector<std::unique_ptr<permissions::NotificationPermissionUiSelector>>
selectors;
selectors.emplace_back(
std::make_unique<ContextualNotificationPermissionUiSelector>());
selectors.emplace_back(std::make_unique<PrefNotificationPermissionUiSelector>(
Profile::FromBrowserContext(browser_context)));
selectors.emplace_back(std::make_unique<PredictionBasedPermissionUiSelector>(
Profile::FromBrowserContext(browser_context)));
return selectors;
}
void ChromePermissionsClient::OnPromptResolved(
content::BrowserContext* browser_context,
permissions::PermissionRequestType request_type,
permissions::PermissionAction action,
const GURL& origin,
base::Optional<QuietUiReason> quiet_ui_reason) {
if (request_type ==
permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS) {
Profile* profile = Profile::FromBrowserContext(browser_context);
AdaptiveQuietNotificationPermissionUiEnabler::GetForProfile(profile)
->RecordPermissionPromptOutcome(action);
if (action == permissions::PermissionAction::GRANTED &&
quiet_ui_reason.has_value() &&
(quiet_ui_reason.value() ==
QuietUiReason::kTriggeredDueToAbusiveRequests ||
quiet_ui_reason.value() ==
QuietUiReason::kTriggeredDueToAbusiveContent)) {
AbusiveOriginPermissionRevocationRequest::
ExemptOriginFromFutureRevocations(profile, origin);
}
}
}
base::Optional<bool>
ChromePermissionsClient::HadThreeConsecutiveNotificationPermissionDenies(
content::BrowserContext* browser_context) {
if (!QuietNotificationPermissionUiConfig::IsAdaptiveActivationDryRunEnabled())
return base::nullopt;
return Profile::FromBrowserContext(browser_context)
->GetPrefs()
->GetBoolean(prefs::kHadThreeConsecutiveNotificationPermissionDenies);
}
base::Optional<bool>
ChromePermissionsClient::HasPreviouslyAutoRevokedPermission(
content::BrowserContext* browser_context,
const GURL& origin,
ContentSettingsType permission) {
if (permission != ContentSettingsType::NOTIFICATIONS) {
return base::nullopt;
}
Profile* profile = Profile::FromBrowserContext(browser_context);
return AbusiveOriginPermissionRevocationRequest::
HasPreviouslyRevokedPermission(profile, origin);
}
base::Optional<url::Origin> ChromePermissionsClient::GetAutoApprovalOrigin() {
#if defined(OS_CHROMEOS)
// In web kiosk mode, all permission requests are auto-approved for the origin
// of the main app.
if (user_manager::UserManager::IsInitialized() &&
user_manager::UserManager::Get()->IsLoggedInAsWebKioskApp()) {
const AccountId& account_id =
user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
DCHECK(chromeos::WebKioskAppManager::IsInitialized());
const chromeos::WebKioskAppData* app_data =
chromeos::WebKioskAppManager::Get()->GetAppByAccountId(account_id);
DCHECK(app_data);
return url::Origin::Create(app_data->install_url());
}
#endif
return base::nullopt;
}
bool ChromePermissionsClient::CanBypassEmbeddingOriginCheck(
const GURL& requesting_origin,
const GURL& embedding_origin) {
// The New Tab Page is excluded from origin checks as its effective requesting
// origin may be the Default Search Engine origin. Extensions are also
// excluded as currently they can request permission from iframes when
// embedded in non-secure contexts (https://crbug.com/530507).
return embedding_origin == GURL(chrome::kChromeUINewTabURL).GetOrigin() ||
requesting_origin.SchemeIs(extensions::kExtensionScheme);
}
base::Optional<GURL> ChromePermissionsClient::OverrideCanonicalOrigin(
const GURL& requesting_origin,
const GURL& embedding_origin) {
if (embedding_origin.GetOrigin() ==
GURL(chrome::kChromeUINewTabURL).GetOrigin()) {
if (requesting_origin.GetOrigin() ==
GURL(chrome::kChromeSearchLocalNtpUrl).GetOrigin() ||
requesting_origin.GetOrigin() ==
GURL(chrome::kChromeUINewTabPageURL).GetOrigin()) {
return GURL(UIThreadSearchTermsData().GoogleBaseURLValue()).GetOrigin();
}
return requesting_origin;
}
// Note that currently chrome extensions are allowed to use permissions even
// when in embedded in non-secure contexts. This is unfortunate and we
// should remove this at some point, but for now always use the requesting
// origin for embedded extensions. https://crbug.com/530507.
if (requesting_origin.SchemeIs(extensions::kExtensionScheme)) {
return requesting_origin;
}
return base::nullopt;
}
#if defined(OS_ANDROID)
bool ChromePermissionsClient::IsPermissionControlledByDse(
content::BrowserContext* browser_context,
ContentSettingsType type,
const url::Origin& origin) {
SearchPermissionsService* search_helper =
SearchPermissionsService::Factory::GetForBrowserContext(browser_context);
return search_helper &&
search_helper->IsPermissionControlledByDSE(type, origin);
}
bool ChromePermissionsClient::ResetPermissionIfControlledByDse(
content::BrowserContext* browser_context,
ContentSettingsType type,
const url::Origin& origin) {
SearchPermissionsService* search_helper =
SearchPermissionsService::Factory::GetForBrowserContext(browser_context);
if (search_helper &&
search_helper->IsPermissionControlledByDSE(type, origin)) {
search_helper->ResetDSEPermission(type);
return true;
}
return false;
}
infobars::InfoBarManager* ChromePermissionsClient::GetInfoBarManager(
content::WebContents* web_contents) {
return InfoBarService::FromWebContents(web_contents);
}
infobars::InfoBar* ChromePermissionsClient::MaybeCreateInfoBar(
content::WebContents* web_contents,
ContentSettingsType type,
base::WeakPtr<permissions::PermissionPromptAndroid> prompt) {
InfoBarService* infobar_service =
InfoBarService::FromWebContents(web_contents);
if (infobar_service &&
GroupedPermissionInfoBarDelegate::ShouldShowMiniInfobar(web_contents,
type)) {
return GroupedPermissionInfoBarDelegate::Create(std::move(prompt),
infobar_service);
}
return nullptr;
}
void ChromePermissionsClient::RepromptForAndroidPermissions(
content::WebContents* web_contents,
const std::vector<ContentSettingsType>& content_settings_types,
PermissionsUpdatedCallback callback) {
PermissionUpdateInfoBarDelegate::Create(web_contents, content_settings_types,
std::move(callback));
}
int ChromePermissionsClient::MapToJavaDrawableId(int resource_id) {
return ResourceMapper::MapToJavaDrawableId(resource_id);
}
#else
std::unique_ptr<permissions::PermissionPrompt>
ChromePermissionsClient::CreatePrompt(
content::WebContents* web_contents,
permissions::PermissionPrompt::Delegate* delegate) {
return CreatePermissionPrompt(web_contents, delegate);
}
#endif