diff options
Diffstat (limited to 'chromium/components/permissions')
25 files changed, 422 insertions, 121 deletions
diff --git a/chromium/components/permissions/BUILD.gn b/chromium/components/permissions/BUILD.gn index 7265a29a466..055fa84b5ee 100644 --- a/chromium/components/permissions/BUILD.gn +++ b/chromium/components/permissions/BUILD.gn @@ -8,6 +8,8 @@ source_set("permissions") { "chooser_context_base.h", "contexts/geolocation_permission_context.cc", "contexts/geolocation_permission_context.h", + "contexts/webxr_permission_context.cc", + "contexts/webxr_permission_context.h", "contexts/window_placement_permission_context.cc", "contexts/window_placement_permission_context.h", "features.cc", @@ -103,6 +105,7 @@ source_set("test_support") { deps = [ ":permissions", "//base", + "//components/content_settings/core/browser", "//components/sync_preferences:test_support", "//components/ukm/content", "//components/vector_icons", @@ -134,6 +137,7 @@ source_set("unit_tests") { "//base", "//base/test:test_support", "//components/content_settings/browser", + "//components/content_settings/browser:test_support", "//components/content_settings/core/browser", "//components/keyed_service/content", "//components/prefs:test_support", diff --git a/chromium/components/permissions/android/BUILD.gn b/chromium/components/permissions/android/BUILD.gn index 6e409eec28d..9c226bae1cb 100644 --- a/chromium/components/permissions/android/BUILD.gn +++ b/chromium/components/permissions/android/BUILD.gn @@ -140,6 +140,9 @@ java_library("components_permissions_junit_tests") { ":java", "//base:base_java_test_support", "//base:base_junit_test_support", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", "//ui/android:ui_java", ] } diff --git a/chromium/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java b/chromium/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java index b1d120e721a..2b05abc83ea 100644 --- a/chromium/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java +++ b/chromium/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java @@ -5,7 +5,6 @@ package org.chromium.components.permissions; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -15,6 +14,7 @@ import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; import org.chromium.base.BuildInfo; +import org.chromium.base.ContextUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.ui.modaldialog.DialogDismissalCause; import org.chromium.ui.modaldialog.ModalDialogManager; @@ -144,12 +144,14 @@ public class PermissionDialogController assert mState == State.NOT_SHOWING; mDialogDelegate = mRequestQueue.remove(0); - Activity activity = mDialogDelegate.getWindow().getActivity().get(); + // Use the context to access resources instead of the activity because the activity may not + // have the correct resources in some cases (e.g. WebLayer). + Context context = mDialogDelegate.getWindow().getContext().get(); // It's possible for the activity to be null if we reach here just after the user // backgrounds the browser and cleanup has happened. In that case, we can't show a prompt, // so act as though the user dismissed it. - if (activity == null) { + if (ContextUtils.activityFromContext(context) == null) { // TODO(timloh): This probably doesn't work, as this happens synchronously when creating // the PermissionPromptAndroid, so the PermissionRequestManager won't be ready yet. mDialogDelegate.onDismiss(); @@ -167,7 +169,7 @@ public class PermissionDialogController mModalDialogManager = mDialogDelegate.getWindow().getModalDialogManager(); mDialogModel = PermissionDialogModel.getModel( - this, mDialogDelegate, () -> showFilteredTouchEventDialog(activity)); + this, mDialogDelegate, () -> showFilteredTouchEventDialog(context)); mModalDialogManager.showDialog(mDialogModel, ModalDialogManager.ModalDialogType.TAB); mState = State.PROMPT_OPEN; } diff --git a/chromium/components/permissions/chooser_context_base.cc b/chromium/components/permissions/chooser_context_base.cc index b3ba00c9bea..5be013f2d77 100644 --- a/chromium/components/permissions/chooser_context_base.cc +++ b/chromium/components/permissions/chooser_context_base.cc @@ -156,7 +156,7 @@ void ChooserContextBase::GrantObjectPermission( void ChooserContextBase::UpdateObjectPermission( const url::Origin& requesting_origin, const url::Origin& embedding_origin, - base::Value& old_object, + const base::Value& old_object, base::Value new_object) { base::Value setting = GetWebsiteSetting(requesting_origin, embedding_origin, /*info=*/nullptr); diff --git a/chromium/components/permissions/chooser_context_base.h b/chromium/components/permissions/chooser_context_base.h index e32535cd80b..e82b8be8fbc 100644 --- a/chromium/components/permissions/chooser_context_base.h +++ b/chromium/components/permissions/chooser_context_base.h @@ -105,14 +105,14 @@ class ChooserContextBase : public KeyedService { // |host_content_settings_map_|. void UpdateObjectPermission(const url::Origin& requesting_origin, const url::Origin& embedding_origin, - base::Value& old_object, + const base::Value& old_object, base::Value new_object); // Revokes |requesting_origin|'s permission to access |object| when embedded // within |embedding_origin|. // // This method may be extended by a subclass to revoke permission to access - // objects returned by GetPreviouslyChosenObjects but not stored in + // objects returned by GetGrantedObjects but not stored in // |host_content_settings_map_|. virtual void RevokeObjectPermission(const url::Origin& requesting_origin, const url::Origin& embedding_origin, diff --git a/chromium/components/permissions/contexts/OWNERS b/chromium/components/permissions/contexts/OWNERS new file mode 100644 index 00000000000..4862047cfa4 --- /dev/null +++ b/chromium/components/permissions/contexts/OWNERS @@ -0,0 +1,6 @@ +file://components/permissions/PERMISSIONS_OWNERS + +per-file *webxr_permission_context*=file://content/browser/xr/OWNERS + +# COMPONENT: Internals>Permissions +# TEAM: permissions-dev@chromium.org diff --git a/chromium/components/permissions/contexts/geolocation_permission_context.cc b/chromium/components/permissions/contexts/geolocation_permission_context.cc index ece50351c21..b62512c32ad 100644 --- a/chromium/components/permissions/contexts/geolocation_permission_context.cc +++ b/chromium/components/permissions/contexts/geolocation_permission_context.cc @@ -43,6 +43,11 @@ void GeolocationPermissionContext::DecidePermission( } } +base::WeakPtr<GeolocationPermissionContext> +GeolocationPermissionContext::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + void GeolocationPermissionContext::UpdateTabContext( const PermissionRequestID& id, const GURL& requesting_frame, diff --git a/chromium/components/permissions/contexts/geolocation_permission_context.h b/chromium/components/permissions/contexts/geolocation_permission_context.h index 6e86b9c215b..bbe8af336ce 100644 --- a/chromium/components/permissions/contexts/geolocation_permission_context.h +++ b/chromium/components/permissions/contexts/geolocation_permission_context.h @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "build/build_config.h" #include "components/content_settings/core/common/content_settings.h" #include "components/permissions/permission_context_base.h" @@ -69,6 +70,8 @@ class GeolocationPermissionContext : public PermissionContextBase { bool user_gesture, BrowserPermissionCallback callback) override; + base::WeakPtr<GeolocationPermissionContext> GetWeakPtr(); + // Make this public for use by the delegate implementation. using PermissionContextBase::NotifyPermissionSet; @@ -85,6 +88,8 @@ class GeolocationPermissionContext : public PermissionContextBase { mojo::Remote<device::mojom::GeolocationControl> geolocation_control_; + base::WeakPtrFactory<GeolocationPermissionContext> weak_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(GeolocationPermissionContext); }; diff --git a/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc b/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc index f1102df0a98..8aec360f714 100644 --- a/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc +++ b/chromium/components/permissions/contexts/geolocation_permission_context_unittest.cc @@ -29,6 +29,7 @@ #include "build/build_config.h" #include "components/content_settings/browser/content_settings_usages_state.h" #include "components/content_settings/browser/tab_specific_content_settings.h" +#include "components/content_settings/browser/test_tab_specific_content_settings_delegate.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/permissions/features.h" #include "components/permissions/permission_context_base.h" @@ -115,55 +116,6 @@ class TestGeolocationPermissionContextDelegate TestingPrefServiceSimple prefs_; base::Optional<url::Origin> dse_origin_; }; - -class TestTabSpecificContentSettingsDelegate - : public content_settings::TabSpecificContentSettings::Delegate { - public: - explicit TestTabSpecificContentSettingsDelegate( - content::BrowserContext* browser_context) - : browser_context_(browser_context) {} - - // content_settings::TabSpecificContentSettings::Delegate: - void UpdateLocationBar() override {} - - void SetContentSettingRules( - content::RenderProcessHost* process, - const RendererContentSettingRules& rules) override {} - - PrefService* GetPrefs() override { return nullptr; } - - HostContentSettingsMap* GetSettingsMap() override { - return PermissionsClient::Get()->GetSettingsMap(browser_context_); - } - - std::vector<storage::FileSystemType> GetAdditionalFileSystemTypes() override { - return {}; - } - - browsing_data::CookieHelper::IsDeletionDisabledCallback - GetIsDeletionDisabledCallback() override { - return base::NullCallback(); - } - - bool IsMicrophoneCameraStateChanged( - content_settings::TabSpecificContentSettings::MicrophoneCameraState - microphone_camera_state, - const std::string& media_stream_selected_audio_device, - const std::string& media_stream_selected_video_device) override { - return false; - } - - content_settings::TabSpecificContentSettings::MicrophoneCameraState - GetMicrophoneCameraState() override { - return content_settings::TabSpecificContentSettings:: - MICROPHONE_CAMERA_NOT_ACCESSED; - } - - void OnContentBlocked(ContentSettingsType type) override {} - - private: - content::BrowserContext* browser_context_; -}; } // namespace // GeolocationPermissionContextTests ------------------------------------------ @@ -317,8 +269,11 @@ void GeolocationPermissionContextTests::SetUp() { RenderViewHostTestHarness::SetUp(); content_settings::TabSpecificContentSettings::CreateForWebContents( - web_contents(), std::make_unique<TestTabSpecificContentSettingsDelegate>( - browser_context())); + web_contents(), + std::make_unique< + content_settings::TestTabSpecificContentSettingsDelegate>( + /*prefs=*/nullptr, + PermissionsClient::Get()->GetSettingsMap(browser_context()))); auto delegate = std::make_unique<TestGeolocationPermissionContextDelegate>( browser_context()); diff --git a/chromium/components/permissions/contexts/webxr_permission_context.cc b/chromium/components/permissions/contexts/webxr_permission_context.cc new file mode 100644 index 00000000000..82227fa9f66 --- /dev/null +++ b/chromium/components/permissions/contexts/webxr_permission_context.cc @@ -0,0 +1,136 @@ +// Copyright 2019 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/permissions/contexts/webxr_permission_context.h" + +#include "base/check.h" +#include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom.h" + +#if defined(OS_ANDROID) +#include "components/permissions/android/android_permission_util.h" +#include "components/permissions/permission_request_id.h" +#include "components/permissions/permissions_client.h" +#include "content/public/browser/web_contents.h" +#endif + +namespace permissions { +WebXrPermissionContext::WebXrPermissionContext( + content::BrowserContext* browser_context, + ContentSettingsType content_settings_type) + : PermissionContextBase(browser_context, + content_settings_type, + blink::mojom::FeaturePolicyFeature::kWebXr), + content_settings_type_(content_settings_type) { + DCHECK(content_settings_type_ == ContentSettingsType::VR || + content_settings_type_ == ContentSettingsType::AR); +} + +WebXrPermissionContext::~WebXrPermissionContext() = default; + +bool WebXrPermissionContext::IsRestrictedToSecureOrigins() const { + return true; +} + +#if defined(OS_ANDROID) +// There are two other permissions that need to check corresponding OS-level +// permissions, and they take two different approaches to this. Geolocation only +// stores the permission ContentSetting if both requests are granted (or if the +// site permission is "Block"). The media permissions follow something more +// similar to this approach, first querying and storing the site-specific +// ContentSetting and then querying for the additional OS permissions as needed. +// However, this is done in MediaStreamDevicesController, not their permission +// context. By persisting and then running additional code as needed, we thus +// mimic that flow, but keep all logic contained into the permission context +// class. +void WebXrPermissionContext::NotifyPermissionSet( + const PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + BrowserPermissionCallback callback, + bool persist, + ContentSetting content_setting) { + // Only AR needs to check for additional permissions, and then only if it was + // actually allowed. + if (!(content_settings_type_ == ContentSettingsType::AR && + content_setting == ContentSetting::CONTENT_SETTING_ALLOW)) { + PermissionContextBase::NotifyPermissionSet( + id, requesting_origin, embedding_origin, std::move(callback), persist, + content_setting); + return; + } + + // Whether or not the user will ultimately accept the OS permissions, we want + // to save the content_setting here if we should. + if (persist) { + PermissionContextBase::UpdateContentSetting( + requesting_origin, embedding_origin, content_setting); + } + + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost( + content::RenderFrameHost::FromID(id.render_process_id(), + id.render_frame_id())); + if (!web_contents) { + // If we can't get the web contents, we don't know the state of the OS + // permission, so assume we don't have it. + OnAndroidPermissionDecided(id, requesting_origin, embedding_origin, + std::move(callback), + false /*permission_granted*/); + return; + } + + // Otherwise, the user granted permission to use AR, so now we need to check + // if we need to prompt for android system permissions. + std::vector<ContentSettingsType> permission_type = {content_settings_type_}; + PermissionRepromptState reprompt_state = + ShouldRepromptUserForPermissions(web_contents, permission_type); + switch (reprompt_state) { + case PermissionRepromptState::kNoNeed: + // We have already returned if permission was denied by the user, and this + // indicates that we have all the OS permissions we need. + OnAndroidPermissionDecided(id, requesting_origin, embedding_origin, + std::move(callback), + true /*permission_granted*/); + return; + + case PermissionRepromptState::kCannotShow: + // If we cannot show the info bar, then we have to assume we don't have + // the permissions we need. + OnAndroidPermissionDecided(id, requesting_origin, embedding_origin, + std::move(callback), + false /*permission_granted*/); + return; + + case PermissionRepromptState::kShow: + // Otherwise, prompt the user that we need additional permissions. + PermissionsClient::Get()->RepromptForAndroidPermissions( + web_contents, permission_type, + base::BindOnce(&WebXrPermissionContext::OnAndroidPermissionDecided, + weak_ptr_factory_.GetWeakPtr(), id, requesting_origin, + embedding_origin, std::move(callback))); + return; + } +} + +void WebXrPermissionContext::OnAndroidPermissionDecided( + const PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + BrowserPermissionCallback callback, + bool permission_granted) { + // If we were supposed to persist the setting we've already done so in the + // initial override of |NotifyPermissionSet|. At this point, if the user + // has denied the OS level permission, we want to notify the requestor that + // the permission has been blocked. + // TODO(https://crbug.com/1060163): Ensure that this is taken into account + // when returning navigator.permissions results. + ContentSetting setting = permission_granted + ? ContentSetting::CONTENT_SETTING_ALLOW + : ContentSetting::CONTENT_SETTING_BLOCK; + PermissionContextBase::NotifyPermissionSet( + id, requesting_origin, embedding_origin, std::move(callback), + false /*persist*/, setting); +} +#endif // defined(OS_ANDROID) +} // namespace permissions diff --git a/chromium/components/permissions/contexts/webxr_permission_context.h b/chromium/components/permissions/contexts/webxr_permission_context.h new file mode 100644 index 00000000000..467a6404413 --- /dev/null +++ b/chromium/components/permissions/contexts/webxr_permission_context.h @@ -0,0 +1,59 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_PERMISSIONS_CONTEXTS_WEBXR_PERMISSION_CONTEXT_H_ +#define COMPONENTS_PERMISSIONS_CONTEXTS_WEBXR_PERMISSION_CONTEXT_H_ + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "build/build_config.h" +#include "components/content_settings/core/common/content_settings_types.h" +#include "components/permissions/permission_context_base.h" + +namespace permissions { +class WebXrPermissionContext : public PermissionContextBase { + public: + WebXrPermissionContext(content::BrowserContext* browser_context, + ContentSettingsType content_settings_type); + ~WebXrPermissionContext() override; + WebXrPermissionContext(const WebXrPermissionContext&) = delete; + WebXrPermissionContext& operator=(const WebXrPermissionContext&) = delete; + + private: + // PermissionContextBase: + bool IsRestrictedToSecureOrigins() const override; + +#if defined(OS_ANDROID) + // On Android we need to do some additional checking for OS level permissions, + // which do not need to happen on Desktop. Note that NotifyPermissionSet is + // only called after a "RequestPermission" call (and not if we are just + // checking the state of the permission), however, the requestSession flow + // requires checking the permission as one of it's steps: (5.6 as of 03/10/20) + // https://immersive-web.github.io/webxr/#dom-xrsystem-requestsession + // When implementing navigator.xr.permission methods, we should ensure that + // GetPermissionStatus is also updated to check these permissions. + void NotifyPermissionSet(const PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + BrowserPermissionCallback callback, + bool persist, + ContentSetting content_setting) override; + + void OnAndroidPermissionDecided(const PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, + BrowserPermissionCallback callback, + bool permission_granted); +#endif + + ContentSettingsType content_settings_type_; + + // Must be the last member, to ensure that it will be + // destroyed first, which will invalidate weak pointers + base::WeakPtrFactory<WebXrPermissionContext> weak_ptr_factory_{this}; +}; + +} // namespace permissions + +#endif // COMPONENTS_PERMISSIONS_CONTEXTS_WEBXR_PERMISSION_CONTEXT_H_ diff --git a/chromium/components/permissions/notification_permission_ui_selector.h b/chromium/components/permissions/notification_permission_ui_selector.h index 4300fc17c17..444dec57b20 100644 --- a/chromium/components/permissions/notification_permission_ui_selector.h +++ b/chromium/components/permissions/notification_permission_ui_selector.h @@ -23,10 +23,12 @@ class NotificationPermissionUiSelector { kEnabledInPrefs, kTriggeredByCrowdDeny, kTriggeredDueToAbusiveRequests, + kTriggeredDueToAbusiveContent, }; enum class WarningReason { kAbusiveRequests, + kAbusiveContent, }; struct Decision { diff --git a/chromium/components/permissions/permission_context_base.cc b/chromium/components/permissions/permission_context_base.cc index 0d55900ad8e..0308b2e3ffc 100644 --- a/chromium/components/permissions/permission_context_base.cc +++ b/chromium/components/permissions/permission_context_base.cc @@ -75,6 +75,10 @@ const char kPermissionBlockedFeaturePolicyMessage[] = "%s permission has been blocked because of a Feature Policy applied to the " "current document. See https://goo.gl/EuHzyv for more details."; +const char kPermissionBlockedPortalsMessage[] = + "%s permission has been blocked because it was requested inside a portal. " + "Portals don't currently support permission requests."; + void LogPermissionBlockedMessage(content::WebContents* web_contents, const char* message, ContentSettingsType type) { @@ -164,6 +168,11 @@ void PermissionContextBase::RequestPermission( kPermissionBlockedFeaturePolicyMessage, content_settings_type_); break; + case PermissionStatusSource::PORTAL: + LogPermissionBlockedMessage(web_contents, + kPermissionBlockedPortalsMessage, + content_settings_type_); + break; case PermissionStatusSource::INSECURE_ORIGIN: case PermissionStatusSource::UNSPECIFIED: case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN: @@ -231,6 +240,12 @@ PermissionResult PermissionContextBase::GetPermissionStatus( content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(render_frame_host); + // Permissions are denied for portals. + if (web_contents && web_contents->IsPortal()) { + return PermissionResult(CONTENT_SETTING_BLOCK, + PermissionStatusSource::PORTAL); + } + // Automatically deny all HTTP or HTTPS requests where the virtual URL and // the loaded URL are for different origins. The loaded URL is the one // actually in the renderer, but the virtual URL is the one diff --git a/chromium/components/permissions/permission_request.cc b/chromium/components/permissions/permission_request.cc index 7ba80986e6f..4032a226ec5 100644 --- a/chromium/components/permissions/permission_request.cc +++ b/chromium/components/permissions/permission_request.cc @@ -18,6 +18,12 @@ ContentSettingsType PermissionRequest::GetContentSettingsType() const { return ContentSettingsType::DEFAULT; } +#if !defined(OS_ANDROID) +base::string16 PermissionRequest::GetChipText() const { + return base::string16(); +} +#endif + base::string16 PermissionRequest::GetMessageTextWarningFragment() const { return base::string16(); } diff --git a/chromium/components/permissions/permission_request.h b/chromium/components/permissions/permission_request.h index 3dcfc3a51dd..f05dc7c82aa 100644 --- a/chromium/components/permissions/permission_request.h +++ b/chromium/components/permissions/permission_request.h @@ -111,6 +111,11 @@ class PermissionRequest { virtual base::string16 GetQuietMessageText() const; #endif +#if !defined(OS_ANDROID) + // Returns the short text for the chip button related to this permission. + virtual base::string16 GetChipText() const; +#endif + // Returns the shortened prompt text for this permission. The permission // bubble may coalesce different requests, and if it does, this text will // be displayed next to an image and indicate the user grants the permission. diff --git a/chromium/components/permissions/permission_request_impl.cc b/chromium/components/permissions/permission_request_impl.cc index 3d1366df87f..e354561025b 100644 --- a/chromium/components/permissions/permission_request_impl.cc +++ b/chromium/components/permissions/permission_request_impl.cc @@ -89,6 +89,7 @@ PermissionRequest::IconId PermissionRequestImpl::GetIconId() const { case ContentSettingsType::MEDIASTREAM_MIC: return vector_icons::kMicIcon; case ContentSettingsType::MEDIASTREAM_CAMERA: + case ContentSettingsType::CAMERA_PAN_TILT_ZOOM: return vector_icons::kVideocamIcon; case ContentSettingsType::ACCESSIBILITY_EVENTS: return vector_icons::kAccessibilityIcon; @@ -99,8 +100,6 @@ PermissionRequest::IconId PermissionRequestImpl::GetIconId() const { return vector_icons::kVrHeadsetIcon; case ContentSettingsType::STORAGE_ACCESS: return vector_icons::kCookieIcon; - case ContentSettingsType::CAMERA_PAN_TILT_ZOOM: - return vector_icons::kCameraPanTiltZoomIcon; case ContentSettingsType::WINDOW_PLACEMENT: return vector_icons::kWindowPlacementIcon; default: @@ -218,8 +217,7 @@ base::string16 PermissionRequestImpl::GetMessageTextFragment() const { message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT; break; case ContentSettingsType::CAMERA_PAN_TILT_ZOOM: - message_id = - IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_ONLY_PERMISSION_FRAGMENT; + message_id = IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_PERMISSION_FRAGMENT; break; case ContentSettingsType::ACCESSIBILITY_EVENTS: message_id = IDS_ACCESSIBILITY_EVENTS_PERMISSION_FRAGMENT; @@ -254,6 +252,42 @@ base::string16 PermissionRequestImpl::GetMessageTextFragment() const { return l10n_util::GetStringUTF16(message_id); } +#if !defined(OS_ANDROID) +base::string16 PermissionRequestImpl::GetChipText() const { + int message_id; + switch (content_settings_type_) { + case ContentSettingsType::GEOLOCATION: + message_id = IDS_GEOLOCATION_PERMISSION_CHIP; + break; + case ContentSettingsType::NOTIFICATIONS: + message_id = IDS_NOTIFICATION_PERMISSIONS_CHIP; + break; + case ContentSettingsType::MIDI_SYSEX: + message_id = IDS_MIDI_SYSEX_PERMISSION_CHIP; + break; + case ContentSettingsType::MEDIASTREAM_MIC: + message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_CHIP; + break; + case ContentSettingsType::MEDIASTREAM_CAMERA: + message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_CHIP; + break; + case ContentSettingsType::CLIPBOARD_READ_WRITE: + message_id = IDS_CLIPBOARD_PERMISSION_CHIP; + break; + case ContentSettingsType::VR: + message_id = IDS_VR_PERMISSION_CHIP; + break; + case ContentSettingsType::AR: + message_id = IDS_AR_PERMISSION_CHIP; + break; + default: + NOTREACHED(); + return base::string16(); + } + return l10n_util::GetStringUTF16(message_id); +} +#endif + base::string16 PermissionRequestImpl::GetMessageTextWarningFragment() const { if (content_settings_type_ == ContentSettingsType::PLUGINS) return l10n_util::GetStringUTF16(IDS_FLASH_PERMISSION_WARNING_FRAGMENT); diff --git a/chromium/components/permissions/permission_request_impl.h b/chromium/components/permissions/permission_request_impl.h index faa231ef608..70631e4033d 100644 --- a/chromium/components/permissions/permission_request_impl.h +++ b/chromium/components/permissions/permission_request_impl.h @@ -41,6 +41,9 @@ class PermissionRequestImpl : public PermissionRequest { base::string16 GetQuietTitleText() const override; base::string16 GetQuietMessageText() const override; #endif +#if !defined(OS_ANDROID) + base::string16 GetChipText() const override; +#endif base::string16 GetMessageTextFragment() const override; base::string16 GetMessageTextWarningFragment() const override; GURL GetEmbeddingOrigin() const override; diff --git a/chromium/components/permissions/permission_request_manager.cc b/chromium/components/permissions/permission_request_manager.cc index b16e1e1a943..60234f3733d 100644 --- a/chromium/components/permissions/permission_request_manager.cc +++ b/chromium/components/permissions/permission_request_manager.cc @@ -15,7 +15,6 @@ #include "base/metrics/user_metrics_action.h" #include "base/stl_util.h" #include "base/strings/string16.h" -#include "base/task/post_task.h" #include "base/threading/sequenced_task_runner_handle.h" #include "build/build_config.h" #include "components/permissions/features.h" @@ -48,6 +47,20 @@ const char kAbusiveNotificationRequestsWarningMessage[] = "fix the issues as soon as possible and submit your site for another " "review. Learn more at https://support.google.com/webtools/answer/9799048."; +constexpr char kAbusiveNotificationContentEnforcementMessage[] = + "Chrome is blocking notification permission requests on this site because " + "the site tends to show notifications with content that mislead or trick " + "users. You should fix the issues as soon as possible and submit your site " + "for another review. Learn more at " + "https://support.google.com/webtools/answer/9799048"; + +constexpr char kAbusiveNotificationContentWarningMessage[] = + "Chrome might start blocking notification permission requests on this site " + "in the future because the site tends to show notifications with content " + "that mislead or trick users. You should fix the issues as soon as " + "possible and submit your site for another review. Learn more at " + "https://support.google.com/webtools/answer/9799048"; + namespace { bool IsMessageTextEqual(PermissionRequest* a, PermissionRequest* b) { @@ -358,8 +371,8 @@ PermissionRequestManager::PermissionRequestManager( void PermissionRequestManager::ScheduleShowBubble() { base::RecordAction(base::UserMetricsAction("PermissionBubbleRequest")); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&PermissionRequestManager::ShowBubble, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PermissionRequestManager::ShowBubble, weak_factory_.GetWeakPtr())); } @@ -421,18 +434,30 @@ void PermissionRequestManager::ShowBubble() { PermissionUmaUtil::PermissionPromptShown(requests_); if (ShouldCurrentRequestUseQuietUI()) { - if (ReasonForUsingQuietUi() == - QuietUiReason::kTriggeredDueToAbusiveRequests) { - LogWarningToConsole(kAbusiveNotificationRequestsEnforcementMessage); + switch (ReasonForUsingQuietUi()) { + case QuietUiReason::kEnabledInPrefs: + case QuietUiReason::kTriggeredByCrowdDeny: + break; + case QuietUiReason::kTriggeredDueToAbusiveRequests: + LogWarningToConsole(kAbusiveNotificationRequestsEnforcementMessage); + break; + case QuietUiReason::kTriggeredDueToAbusiveContent: + LogWarningToConsole(kAbusiveNotificationContentEnforcementMessage); + break; } base::RecordAction(base::UserMetricsAction( "Notifications.Quiet.PermissionRequestShown")); } - if (current_request_ui_to_use_->warning_reason && - *(current_request_ui_to_use_->warning_reason) == - WarningReason::kAbusiveRequests) { - LogWarningToConsole(kAbusiveNotificationRequestsWarningMessage); + if (current_request_ui_to_use_->warning_reason) { + switch (*(current_request_ui_to_use_->warning_reason)) { + case WarningReason::kAbusiveRequests: + LogWarningToConsole(kAbusiveNotificationRequestsWarningMessage); + break; + case WarningReason::kAbusiveContent: + LogWarningToConsole(kAbusiveNotificationContentWarningMessage); + break; + } } } current_request_view_shown_to_user_ = true; diff --git a/chromium/components/permissions/permission_request_manager.h b/chromium/components/permissions/permission_request_manager.h index 839990cd5b5..158214c0d22 100644 --- a/chromium/components/permissions/permission_request_manager.h +++ b/chromium/components/permissions/permission_request_manager.h @@ -36,6 +36,14 @@ extern const char kAbusiveNotificationRequestsEnforcementMessage[]; // the warning list for abusive permission request flows. extern const char kAbusiveNotificationRequestsWarningMessage[]; +// The message to be printed in the Developer Tools console when the site is on +// the blocking list for showing abusive notification content. +extern const char kAbusiveNotificationContentEnforcementMessage[]; + +// The message to be printed in the Developer Tools console when the site is on +// the warning list for showing abusive notification content. +extern const char kAbusiveNotificationContentWarningMessage[]; + // Provides access to permissions bubbles. Allows clients to add a request // callback interface to the existing permission bubble configuration. // Depending on the situation and policy, that may add new UI to an existing diff --git a/chromium/components/permissions/permission_result.h b/chromium/components/permissions/permission_result.h index 37ff25704d9..3275b5722e0 100644 --- a/chromium/components/permissions/permission_result.h +++ b/chromium/components/permissions/permission_result.h @@ -37,6 +37,10 @@ enum class PermissionStatusSource { // seen by the user. This may be very confusing for a user to see in a // permissions request. VIRTUAL_URL_DIFFERENT_ORIGIN, + + // The status is the result of a permissions being requested inside a portal. + // Permissions are currently always denied inside a portal. + PORTAL }; struct PermissionResult { diff --git a/chromium/components/permissions/permission_uma_util.cc b/chromium/components/permissions/permission_uma_util.cc index e58d8ef5f3b..039722c2c8d 100644 --- a/chromium/components/permissions/permission_uma_util.cc +++ b/chromium/components/permissions/permission_uma_util.cc @@ -30,19 +30,6 @@ namespace permissions { -// UMA keys need to be statically initialized so plain function would not -// work. Use macros instead. -#define PERMISSION_ACTION_UMA(secure_origin, permission, permission_secure, \ - permission_insecure, action) \ - base::UmaHistogramEnumeration(permission, action, PermissionAction::NUM); \ - if (secure_origin) { \ - base::UmaHistogramEnumeration(permission_secure, action, \ - PermissionAction::NUM); \ - } else { \ - base::UmaHistogramEnumeration(permission_insecure, action, \ - PermissionAction::NUM); \ - } - #define PERMISSION_BUBBLE_TYPE_UMA(metric_name, permission_bubble_type) \ base::UmaHistogramEnumeration(metric_name, permission_bubble_type, \ PermissionRequestType::NUM) @@ -128,29 +115,41 @@ void RecordEngagementMetric(const std::vector<PermissionRequest*>& requests, base::UmaHistogramPercentage(name, engagement_score); } -void RecordPermissionActionUkm(PermissionAction action, - PermissionRequestGestureType gesture_type, - ContentSettingsType permission, - int dismiss_count, - int ignore_count, - PermissionSourceUI source_ui, - PermissionPromptDisposition ui_disposition, - base::Optional<ukm::SourceId> source_id) { +void RecordPermissionActionUkm( + PermissionAction action, + PermissionRequestGestureType gesture_type, + ContentSettingsType permission, + int dismiss_count, + int ignore_count, + PermissionSourceUI source_ui, + PermissionPromptDisposition ui_disposition, + base::Optional<bool> has_three_consecutive_denies, + base::Optional<ukm::SourceId> source_id) { // Only record the permission change if the origin is in the history. if (!source_id.has_value()) return; size_t num_values = 0; - ukm::builders::Permission(source_id.value()) - .SetAction(static_cast<int64_t>(action)) + + ukm::builders::Permission builder(source_id.value()); + builder.SetAction(static_cast<int64_t>(action)) .SetGesture(static_cast<int64_t>(gesture_type)) .SetPermissionType(static_cast<int64_t>( ContentSettingTypeToHistogramValue(permission, &num_values))) .SetPriorDismissals(std::min(kPriorCountCap, dismiss_count)) .SetPriorIgnores(std::min(kPriorCountCap, ignore_count)) .SetSource(static_cast<int64_t>(source_ui)) - .SetPromptDisposition(static_cast<int64_t>(ui_disposition)) - .Record(ukm::UkmRecorder::Get()); + .SetPromptDisposition(static_cast<int64_t>(ui_disposition)); + + if (has_three_consecutive_denies.has_value()) { + int64_t satisfied_adaptive_triggers = 0; + if (has_three_consecutive_denies.value()) + satisfied_adaptive_triggers |= + static_cast<int64_t>(AdaptiveTriggers::THREE_CONSECUTIVE_DENIES); + builder.SetSatisfiedAdaptiveTriggers(satisfied_adaptive_triggers); + } + + builder.Record(ukm::UkmRecorder::Get()); } std::string GetPromptDispositionString( @@ -265,6 +264,7 @@ void PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource( case PermissionStatusSource::INSECURE_ORIGIN: case PermissionStatusSource::FEATURE_POLICY: case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN: + case PermissionStatusSource::PORTAL: // The permission wasn't under embargo, so don't record anything. We may // embargo it later. break; @@ -489,35 +489,31 @@ void PermissionUmaUtil::RecordPermissionAction( PermissionsClient::Get()->GetUkmSourceId( browser_context, web_contents, requesting_origin, - base::BindOnce(&RecordPermissionActionUkm, action, gesture_type, - permission, dismiss_count, ignore_count, source_ui, - ui_disposition)); - - bool secure_origin = content::IsOriginSecure(requesting_origin); + base::BindOnce( + &RecordPermissionActionUkm, action, gesture_type, permission, + dismiss_count, ignore_count, source_ui, ui_disposition, + permission == ContentSettingsType::NOTIFICATIONS + ? PermissionsClient::Get() + ->HadThreeConsecutiveNotificationPermissionDenies( + browser_context) + : base::nullopt)); switch (permission) { - // Geolocation, MidiSysEx, Push, Media, Clipboard, and AR/VR permissions are - // disabled on insecure origins, so there's no need to record separate - // metrics for secure/insecure. case ContentSettingsType::GEOLOCATION: base::UmaHistogramEnumeration("Permissions.Action.Geolocation", action, PermissionAction::NUM); break; case ContentSettingsType::NOTIFICATIONS: - PERMISSION_ACTION_UMA(secure_origin, "Permissions.Action.Notifications", - "Permissions.Action.SecureOrigin.Notifications", - "Permissions.Action.InsecureOrigin.Notifications", - action); + base::UmaHistogramEnumeration("Permissions.Action.Notifications", action, + PermissionAction::NUM); break; case ContentSettingsType::MIDI_SYSEX: base::UmaHistogramEnumeration("Permissions.Action.MidiSysEx", action, PermissionAction::NUM); break; case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER: - PERMISSION_ACTION_UMA(secure_origin, "Permissions.Action.ProtectedMedia", - "Permissions.Action.SecureOrigin.ProtectedMedia", - "Permissions.Action.InsecureOrigin.ProtectedMedia", - action); + base::UmaHistogramEnumeration("Permissions.Action.ProtectedMedia", action, + PermissionAction::NUM); break; case ContentSettingsType::MEDIASTREAM_MIC: base::UmaHistogramEnumeration("Permissions.Action.AudioCapture", action, @@ -528,9 +524,8 @@ void PermissionUmaUtil::RecordPermissionAction( PermissionAction::NUM); break; case ContentSettingsType::PLUGINS: - PERMISSION_ACTION_UMA(secure_origin, "Permissions.Action.Flash", - "Permissions.Action.SecureOrigin.Flash", - "Permissions.Action.InsecureOrigin.Flash", action); + base::UmaHistogramEnumeration("Permissions.Action.Flash", action, + PermissionAction::NUM); break; case ContentSettingsType::CLIPBOARD_READ_WRITE: base::UmaHistogramEnumeration("Permissions.Action.ClipboardReadWrite", diff --git a/chromium/components/permissions/permission_uma_util.h b/chromium/components/permissions/permission_uma_util.h index a30c52d828f..ac64a8c7381 100644 --- a/chromium/components/permissions/permission_uma_util.h +++ b/chromium/components/permissions/permission_uma_util.h @@ -94,6 +94,15 @@ enum class PermissionPromptDisposition { MINI_INFOBAR = 5, }; +enum class AdaptiveTriggers { + // None of the adaptive triggers were met. Currently this means two or less + // consecutive denies in a row. + NONE = 0, + + // User denied permission prompt 3 or more times. + THREE_CONSECUTIVE_DENIES = 0x01, +}; + // Provides a convenient way of logging UMA for permission related operations. class PermissionUmaUtil { public: diff --git a/chromium/components/permissions/permissions_client.cc b/chromium/components/permissions/permissions_client.cc index c9714ed6c97..02e9d545528 100644 --- a/chromium/components/permissions/permissions_client.cc +++ b/chromium/components/permissions/permissions_client.cc @@ -79,6 +79,12 @@ void PermissionsClient::OnPromptResolved( PermissionRequestType request_type, PermissionAction action) {} +base::Optional<bool> +PermissionsClient::HadThreeConsecutiveNotificationPermissionDenies( + content::BrowserContext* browser_context) { + return base::nullopt; +} + base::Optional<url::Origin> PermissionsClient::GetAutoApprovalOrigin() { return base::nullopt; } diff --git a/chromium/components/permissions/permissions_client.h b/chromium/components/permissions/permissions_client.h index 1becc45542a..96744c3f5a0 100644 --- a/chromium/components/permissions/permissions_client.h +++ b/chromium/components/permissions/permissions_client.h @@ -23,6 +23,10 @@ class BrowserContext; class WebContents; } // namespace content +namespace content_settings { +class CookieSettings; +} + namespace infobars { class InfoBar; class InfoBarManager; @@ -53,6 +57,10 @@ class PermissionsClient { virtual HostContentSettingsMap* GetSettingsMap( content::BrowserContext* browser_context) = 0; + // Retrieves the CookieSettings for this context. + virtual scoped_refptr<content_settings::CookieSettings> GetCookieSettings( + content::BrowserContext* browser_context) = 0; + // Retrieves the PermissionDecisionAutoBlocker for this context. The returned // pointer has the same lifetime as |browser_context|. virtual PermissionDecisionAutoBlocker* GetPermissionDecisionAutoBlocker( @@ -120,6 +128,13 @@ class PermissionsClient { PermissionRequestType request_type, PermissionAction action); + // Returns true if user has 3 consecutive notifications permission denies, + // returns false otherwise. + // Returns base::nullopt if the user is not in the adoptive activation quiet + // ui dry run experiment group. + virtual base::Optional<bool> HadThreeConsecutiveNotificationPermissionDenies( + content::BrowserContext* browser_context); + // If the embedder returns an origin here, any requests matching that origin // will be approved. Requests that do not match the returned origin will // immediately be finished without granting/denying the permission. diff --git a/chromium/components/permissions/quota_permission_context_impl.cc b/chromium/components/permissions/quota_permission_context_impl.cc index fff8d90fc82..a9778e0c829 100644 --- a/chromium/components/permissions/quota_permission_context_impl.cc +++ b/chromium/components/permissions/quota_permission_context_impl.cc @@ -11,7 +11,6 @@ #include "base/bind.h" #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/task/post_task.h" #include "build/build_config.h" #include "components/permissions/permission_request.h" #include "components/permissions/permission_request_manager.h" @@ -171,8 +170,8 @@ void QuotaPermissionContextImpl::RequestQuotaPermission( } if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&QuotaPermissionContextImpl::RequestQuotaPermission, this, params, render_process_id, std::move(callback))); return; @@ -212,8 +211,8 @@ void QuotaPermissionContextImpl::DispatchCallbackOnIOThread( DCHECK(callback); if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) { - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&QuotaPermissionContextImpl::DispatchCallbackOnIOThread, this, std::move(callback), response)); return; |