| // Copyright 2014 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/permissions/permission_uma_util.h" |
| |
| #include <cstdint> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/no_destructor.h" |
| #include "base/notreached.h" |
| #include "base/strings/strcat.h" |
| #include "base/time/clock.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "components/content_settings/core/browser/content_settings_uma_util.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_types.h" |
| #include "components/content_settings/core/common/features.h" |
| #include "components/permissions/constants.h" |
| #include "components/permissions/permission_actions_history.h" |
| #include "components/permissions/permission_decision_auto_blocker.h" |
| #include "components/permissions/permission_request.h" |
| #include "components/permissions/permission_util.h" |
| #include "components/permissions/permissions_client.h" |
| #include "components/permissions/prediction_service/prediction_common.h" |
| #include "components/permissions/prediction_service/prediction_request_features.h" |
| #include "components/permissions/request_type.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "printing/buildflags/buildflags.h" |
| #include "services/metrics/public/cpp/metrics_utils.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| #include "services/network/public/cpp/is_potentially_trustworthy.h" |
| #include "third_party/blink/public/common/permissions/permission_utils.h" |
| #include "third_party/blink/public/common/permissions_policy/permissions_policy.h" |
| #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h" |
| #include "url/gurl.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "base/android/jni_string.h" |
| #endif |
| |
| namespace { |
| bool scoped_revocation_reporter_in_scope = false; |
| } // namespace |
| |
| namespace permissions { |
| |
| #define PERMISSION_BUBBLE_TYPE_UMA(metric_name, request_type_for_uma) \ |
| base::UmaHistogramEnumeration(metric_name, request_type_for_uma, \ |
| RequestTypeForUma::NUM) |
| |
| #define PERMISSION_BUBBLE_GESTURE_TYPE_UMA( \ |
| gesture_metric_name, no_gesture_metric_name, gesture_type, \ |
| permission_bubble_type) \ |
| if (gesture_type == PermissionRequestGestureType::GESTURE) { \ |
| PERMISSION_BUBBLE_TYPE_UMA(gesture_metric_name, permission_bubble_type); \ |
| } else if (gesture_type == PermissionRequestGestureType::NO_GESTURE) { \ |
| PERMISSION_BUBBLE_TYPE_UMA(no_gesture_metric_name, \ |
| permission_bubble_type); \ |
| } |
| |
| using blink::PermissionType; |
| |
| namespace { |
| |
| RequestTypeForUma GetUmaValueForRequestType(RequestType request_type) { |
| switch (request_type) { |
| case RequestType::kAccessibilityEvents: |
| return RequestTypeForUma::PERMISSION_ACCESSIBILITY_EVENTS; |
| case RequestType::kArSession: |
| return RequestTypeForUma::PERMISSION_AR; |
| #if !BUILDFLAG(IS_ANDROID) |
| case RequestType::kCameraPanTiltZoom: |
| return RequestTypeForUma::PERMISSION_CAMERA_PAN_TILT_ZOOM; |
| #endif |
| case RequestType::kCameraStream: |
| return RequestTypeForUma::PERMISSION_MEDIASTREAM_CAMERA; |
| case RequestType::kClipboard: |
| return RequestTypeForUma::PERMISSION_CLIPBOARD_READ_WRITE; |
| case RequestType::kDiskQuota: |
| return RequestTypeForUma::QUOTA; |
| #if !BUILDFLAG(IS_ANDROID) |
| // TODO(crbug.com/40214907): Enable on Android |
| case RequestType::kLocalFonts: |
| return RequestTypeForUma::PERMISSION_LOCAL_FONTS; |
| #endif |
| case RequestType::kGeolocation: |
| return RequestTypeForUma::PERMISSION_GEOLOCATION; |
| case RequestType::kIdleDetection: |
| return RequestTypeForUma::PERMISSION_IDLE_DETECTION; |
| case RequestType::kMicStream: |
| return RequestTypeForUma::PERMISSION_MEDIASTREAM_MIC; |
| case RequestType::kMidiSysex: |
| return RequestTypeForUma::PERMISSION_MIDI_SYSEX; |
| case RequestType::kMultipleDownloads: |
| return RequestTypeForUma::DOWNLOAD; |
| #if BUILDFLAG(IS_ANDROID) |
| case RequestType::kNfcDevice: |
| return RequestTypeForUma::PERMISSION_NFC; |
| #endif |
| case RequestType::kNotifications: |
| return RequestTypeForUma::PERMISSION_NOTIFICATIONS; |
| #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) |
| case RequestType::kProtectedMediaIdentifier: |
| return RequestTypeForUma::PERMISSION_PROTECTED_MEDIA_IDENTIFIER; |
| #endif |
| #if !BUILDFLAG(IS_ANDROID) |
| case RequestType::kRegisterProtocolHandler: |
| return RequestTypeForUma::REGISTER_PROTOCOL_HANDLER; |
| #endif |
| #if BUILDFLAG(IS_CHROMEOS) |
| case RequestType::kSmartCard: |
| return RequestTypeForUma::PERMISSION_SMART_CARD; |
| #endif |
| case RequestType::kStorageAccess: |
| return RequestTypeForUma::PERMISSION_STORAGE_ACCESS; |
| case RequestType::kVrSession: |
| return RequestTypeForUma::PERMISSION_VR; |
| #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_CUPS) |
| case RequestType::kWebPrinting: |
| return RequestTypeForUma::PERMISSION_WEB_PRINTING; |
| #endif |
| #if !BUILDFLAG(IS_ANDROID) |
| case RequestType::kWindowManagement: |
| return RequestTypeForUma::PERMISSION_WINDOW_MANAGEMENT; |
| #endif |
| case RequestType::kTopLevelStorageAccess: |
| return RequestTypeForUma::PERMISSION_TOP_LEVEL_STORAGE_ACCESS; |
| #if !BUILDFLAG(IS_ANDROID) |
| case RequestType::kFileSystemAccess: |
| return RequestTypeForUma::PERMISSION_FILE_SYSTEM_ACCESS; |
| case RequestType::kCapturedSurfaceControl: |
| return RequestTypeForUma::CAPTURED_SURFACE_CONTROL; |
| #endif |
| case RequestType::kIdentityProvider: |
| return RequestTypeForUma::PERMISSION_IDENTITY_PROVIDER; |
| } |
| } |
| |
| const int kPriorCountCap = 10; |
| |
| std::string GetPermissionRequestString(RequestTypeForUma type) { |
| switch (type) { |
| case RequestTypeForUma::MULTIPLE: |
| return "AudioAndVideoCapture"; |
| case RequestTypeForUma::QUOTA: |
| return "Quota"; |
| case RequestTypeForUma::DOWNLOAD: |
| return "MultipleDownload"; |
| case RequestTypeForUma::REGISTER_PROTOCOL_HANDLER: |
| return "RegisterProtocolHandler"; |
| case RequestTypeForUma::PERMISSION_GEOLOCATION: |
| return "Geolocation"; |
| case RequestTypeForUma::PERMISSION_MIDI_SYSEX: |
| return "MidiSysEx"; |
| case RequestTypeForUma::PERMISSION_NOTIFICATIONS: |
| return "Notifications"; |
| case RequestTypeForUma::PERMISSION_PROTECTED_MEDIA_IDENTIFIER: |
| return "ProtectedMedia"; |
| case RequestTypeForUma::PERMISSION_MEDIASTREAM_MIC: |
| return "AudioCapture"; |
| case RequestTypeForUma::PERMISSION_MEDIASTREAM_CAMERA: |
| return "VideoCapture"; |
| case RequestTypeForUma::PERMISSION_PAYMENT_HANDLER: |
| return "PaymentHandler"; |
| case RequestTypeForUma::PERMISSION_NFC: |
| return "Nfc"; |
| case RequestTypeForUma::PERMISSION_CLIPBOARD_READ_WRITE: |
| return "ClipboardReadWrite"; |
| case RequestTypeForUma::PERMISSION_VR: |
| return "VR"; |
| case RequestTypeForUma::PERMISSION_AR: |
| return "AR"; |
| case RequestTypeForUma::PERMISSION_STORAGE_ACCESS: |
| return "StorageAccess"; |
| case RequestTypeForUma::PERMISSION_TOP_LEVEL_STORAGE_ACCESS: |
| return "TopLevelStorageAccess"; |
| case RequestTypeForUma::PERMISSION_CAMERA_PAN_TILT_ZOOM: |
| return "CameraPanTiltZoom"; |
| case RequestTypeForUma::PERMISSION_WINDOW_MANAGEMENT: |
| return "WindowManagement"; |
| case RequestTypeForUma::PERMISSION_LOCAL_FONTS: |
| return "LocalFonts"; |
| case RequestTypeForUma::PERMISSION_IDLE_DETECTION: |
| return "IdleDetection"; |
| case RequestTypeForUma::PERMISSION_ACCESSIBILITY_EVENTS: |
| return "AccessibilityEvents"; |
| case RequestTypeForUma::PERMISSION_FILE_SYSTEM_ACCESS: |
| return "FileSystemAccess"; |
| case RequestTypeForUma::CAPTURED_SURFACE_CONTROL: |
| return "CapturedSurfaceControl"; |
| case RequestTypeForUma::PERMISSION_SMART_CARD: |
| return "SmartCard"; |
| case RequestTypeForUma::PERMISSION_WEB_PRINTING: |
| return "WebPrinting"; |
| case RequestTypeForUma::PERMISSION_IDENTITY_PROVIDER: |
| return "IdentityProvider"; |
| |
| case RequestTypeForUma::UNKNOWN: |
| case RequestTypeForUma::PERMISSION_FLASH: |
| case RequestTypeForUma::PERMISSION_FILE_HANDLING: |
| case RequestTypeForUma::NUM: |
| NOTREACHED(); |
| return ""; |
| } |
| } |
| |
| // Helper to check if the current render frame host is cross-origin with top |
| // level frame. Note: in case of nested frames like A(B(A)), the bottom frame A |
| // will get |IsCrossOriginSubframe| returns false. |
| bool IsCrossOriginSubframe(content::RenderFrameHost* render_frame_host) { |
| DCHECK(render_frame_host); |
| |
| // Permissions are denied for fenced frames and other inner pages. |
| // |GetMainFrame| should be enough to get top level frame. |
| auto current_origin = render_frame_host->GetLastCommittedOrigin(); |
| return !render_frame_host->GetMainFrame() |
| ->GetLastCommittedOrigin() |
| .IsSameOriginWith(current_origin); |
| } |
| |
| // Helper to check if the current render frame host is cross-origin with any of |
| // its parents. |
| bool IsCrossOriginWithAnyParent(content::RenderFrameHost* render_frame_host) { |
| const url::Origin& current_origin = |
| render_frame_host->GetLastCommittedOrigin(); |
| content::RenderFrameHost* parent = render_frame_host->GetParent(); |
| while (parent) { |
| const url::Origin& parent_origin = parent->GetLastCommittedOrigin(); |
| if (!parent_origin.IsSameOriginWith(current_origin)) { |
| return true; |
| } |
| parent = parent->GetParent(); |
| } |
| return false; |
| } |
| |
| // Helper to get permission policy header policy for the top-level frame. |
| // render_frame_host could be the top-level frame or a descendant of top-level |
| // frame. |
| PermissionHeaderPolicyForUMA GetTopLevelPermissionHeaderPolicyForUMA( |
| content::RenderFrameHost* render_frame_host, |
| blink::mojom::PermissionsPolicyFeature feature) { |
| const auto& parsed_permission_policy_header = |
| render_frame_host->GetMainFrame()->GetPermissionsPolicyHeader(); |
| if (parsed_permission_policy_header.empty()) { |
| return PermissionHeaderPolicyForUMA::HEADER_NOT_PRESENT_OR_INVALID; |
| } |
| |
| const auto* permissions_policy = |
| render_frame_host->GetMainFrame()->GetPermissionsPolicy(); |
| const auto& allowlists = permissions_policy->allowlists(); |
| auto allowlist = allowlists.find(feature); |
| if (allowlist == allowlists.end()) { |
| return PermissionHeaderPolicyForUMA::FEATURE_NOT_PRESENT; |
| } |
| |
| if (allowlist->second.MatchesAll()) { |
| return PermissionHeaderPolicyForUMA::FEATURE_ALLOWLIST_IS_WILDCARD; |
| } |
| |
| const auto& origin = render_frame_host->GetLastCommittedOrigin(); |
| return allowlist->second.Contains(origin) |
| ? PermissionHeaderPolicyForUMA:: |
| FEATURE_ALLOWLIST_EXPLICITLY_MATCHES_ORIGIN |
| : PermissionHeaderPolicyForUMA:: |
| FEATURE_ALLOWLIST_DOES_NOT_MATCH_ORIGIN; |
| } |
| |
| void RecordEngagementMetric( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| content::WebContents* web_contents, |
| const std::string& action) { |
| CHECK(!requests.empty()); |
| |
| RequestTypeForUma type = |
| GetUmaValueForRequestType(requests[0]->request_type()); |
| if (requests.size() > 1) |
| type = RequestTypeForUma::MULTIPLE; |
| |
| DCHECK(action == "Accepted" || action == "Denied" || action == "Dismissed" || |
| action == "Ignored" || action == "AcceptedOnce"); |
| std::string name = "Permissions.Engagement." + action + '.' + |
| GetPermissionRequestString(type); |
| |
| double engagement_score = PermissionsClient::Get()->GetSiteEngagementScore( |
| web_contents->GetBrowserContext(), requests[0]->requesting_origin()); |
| base::UmaHistogramPercentageObsoleteDoNotUse(name, engagement_score); |
| } |
| |
| // Records in a UMA histogram whether we should expect to see an event in UKM, |
| // to allow for evaluating if the current constraints on UKM recording work well |
| // in practice. |
| void RecordUmaForWhetherRevocationUkmWasRecorded( |
| ContentSettingsType permission_type, |
| bool has_source_id) { |
| if (permission_type == ContentSettingsType::NOTIFICATIONS) { |
| base::UmaHistogramBoolean( |
| "Permissions.Revocation.Notifications.DidRecordUkm", has_source_id); |
| } |
| } |
| |
| // Records in a UMA histogram whether we should expect to see an event in UKM, |
| // to allow for evaluating if the current constraints on UKM recording work well |
| // in practice. |
| void RecordUmaForWhetherUsageUkmWasRecorded(ContentSettingsType permission_type, |
| bool has_source_id) { |
| if (permission_type == ContentSettingsType::NOTIFICATIONS) { |
| base::UmaHistogramBoolean("Permissions.Usage.Notifications.DidRecordUkm", |
| has_source_id); |
| } |
| } |
| |
| void RecordUmaForRevocationSourceUI(ContentSettingsType permission_type, |
| PermissionSourceUI source_ui) { |
| if (permission_type == ContentSettingsType::NOTIFICATIONS) { |
| base::UmaHistogramEnumeration( |
| "Permissions.Revocation.Notifications.SourceUI", source_ui); |
| } |
| } |
| |
| void RecordPermissionUsageUkm(ContentSettingsType permission_type, |
| std::optional<ukm::SourceId> source_id) { |
| RecordUmaForWhetherUsageUkmWasRecorded(permission_type, |
| source_id.has_value()); |
| if (!source_id.has_value()) |
| return; |
| |
| ukm::builders::PermissionUsage builder(source_id.value()); |
| builder.SetPermissionType(static_cast<int64_t>( |
| content_settings_uma_util::ContentSettingTypeToHistogramValue( |
| permission_type))); |
| builder.Record(ukm::UkmRecorder::Get()); |
| } |
| |
| void RecordPermissionActionUkm( |
| PermissionAction action, |
| PermissionRequestGestureType gesture_type, |
| ContentSettingsType permission, |
| int dismiss_count, |
| int ignore_count, |
| PermissionSourceUI source_ui, |
| base::TimeDelta time_to_decision, |
| PermissionPromptDisposition ui_disposition, |
| std::optional<PermissionPromptDispositionReason> ui_reason, |
| std::optional<std::vector<ElementAnchoredBubbleVariant>> variants, |
| std::optional<bool> has_three_consecutive_denies, |
| std::optional<bool> has_previously_revoked_permission, |
| std::optional<PermissionUmaUtil::PredictionGrantLikelihood> |
| predicted_grant_likelihood, |
| PredictionRequestFeatures::ActionCounts |
| loud_ui_actions_counts_for_request_type, |
| PredictionRequestFeatures::ActionCounts loud_ui_actions_counts, |
| PredictionRequestFeatures::ActionCounts actions_counts_for_request_type, |
| PredictionRequestFeatures::ActionCounts actions_counts, |
| std::optional<bool> prediction_decision_held_back, |
| std::optional<ukm::SourceId> source_id) { |
| if (action == PermissionAction::REVOKED) { |
| RecordUmaForWhetherRevocationUkmWasRecorded(permission, |
| source_id.has_value()); |
| } |
| |
| // Only record the permission change if the origin is in the history. |
| if (!source_id.has_value()) |
| return; |
| |
| const int loud_ui_prompts_count_for_request_type = |
| loud_ui_actions_counts_for_request_type.total(); |
| const int loud_ui_prompts_count = loud_ui_actions_counts.total(); |
| const int prompts_count_for_request_type = |
| actions_counts_for_request_type.total(); |
| const int prompts_count = actions_counts.total(); |
| 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>( |
| content_settings_uma_util::ContentSettingTypeToHistogramValue( |
| permission))) |
| .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)); |
| |
| builder |
| .SetStats_LoudPromptsOfType_DenyRate( |
| GetRoundedRatioForUkm(loud_ui_actions_counts_for_request_type.denies, |
| loud_ui_prompts_count_for_request_type)) |
| .SetStats_LoudPromptsOfType_DismissRate(GetRoundedRatioForUkm( |
| loud_ui_actions_counts_for_request_type.dismissals, |
| loud_ui_prompts_count_for_request_type)) |
| .SetStats_LoudPromptsOfType_GrantRate( |
| GetRoundedRatioForUkm(loud_ui_actions_counts_for_request_type.grants, |
| loud_ui_prompts_count_for_request_type)) |
| .SetStats_LoudPromptsOfType_IgnoreRate( |
| GetRoundedRatioForUkm(loud_ui_actions_counts_for_request_type.ignores, |
| loud_ui_prompts_count_for_request_type)) |
| .SetStats_LoudPromptsOfType_Count( |
| BucketizeValue(loud_ui_prompts_count_for_request_type)); |
| |
| builder |
| .SetStats_LoudPrompts_DenyRate(GetRoundedRatioForUkm( |
| loud_ui_actions_counts.denies, loud_ui_prompts_count)) |
| .SetStats_LoudPrompts_DismissRate(GetRoundedRatioForUkm( |
| loud_ui_actions_counts.dismissals, loud_ui_prompts_count)) |
| .SetStats_LoudPrompts_GrantRate(GetRoundedRatioForUkm( |
| loud_ui_actions_counts.grants, loud_ui_prompts_count)) |
| .SetStats_LoudPrompts_IgnoreRate(GetRoundedRatioForUkm( |
| loud_ui_actions_counts.ignores, loud_ui_prompts_count)) |
| .SetStats_LoudPrompts_Count(BucketizeValue(loud_ui_prompts_count)); |
| |
| builder |
| .SetStats_AllPromptsOfType_DenyRate( |
| GetRoundedRatioForUkm(actions_counts_for_request_type.denies, |
| prompts_count_for_request_type)) |
| .SetStats_AllPromptsOfType_DismissRate( |
| GetRoundedRatioForUkm(actions_counts_for_request_type.dismissals, |
| prompts_count_for_request_type)) |
| .SetStats_AllPromptsOfType_GrantRate( |
| GetRoundedRatioForUkm(actions_counts_for_request_type.grants, |
| prompts_count_for_request_type)) |
| .SetStats_AllPromptsOfType_IgnoreRate( |
| GetRoundedRatioForUkm(actions_counts_for_request_type.ignores, |
| prompts_count_for_request_type)) |
| .SetStats_AllPromptsOfType_Count( |
| BucketizeValue(prompts_count_for_request_type)); |
| |
| builder |
| .SetStats_AllPrompts_DenyRate( |
| GetRoundedRatioForUkm(actions_counts.denies, prompts_count)) |
| .SetStats_AllPrompts_DismissRate( |
| GetRoundedRatioForUkm(actions_counts.dismissals, prompts_count)) |
| .SetStats_AllPrompts_GrantRate( |
| GetRoundedRatioForUkm(actions_counts.grants, prompts_count)) |
| .SetStats_AllPrompts_IgnoreRate( |
| GetRoundedRatioForUkm(actions_counts.ignores, prompts_count)) |
| .SetStats_AllPrompts_Count(BucketizeValue(prompts_count)); |
| |
| if (ui_reason.has_value()) |
| builder.SetPromptDispositionReason(static_cast<int64_t>(ui_reason.value())); |
| |
| if (predicted_grant_likelihood.has_value()) { |
| builder.SetPredictionsApiResponse_GrantLikelihood( |
| static_cast<int64_t>(predicted_grant_likelihood.value())); |
| } |
| |
| if (prediction_decision_held_back.has_value()) { |
| builder.SetPredictionsApiResponse_Heldback( |
| prediction_decision_held_back.value()); |
| } |
| |
| 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); |
| } |
| |
| if (has_previously_revoked_permission.has_value()) { |
| int64_t previously_revoked_permission = 0; |
| if (has_previously_revoked_permission.value()) { |
| previously_revoked_permission = static_cast<int64_t>( |
| PermissionAutoRevocationHistory::PREVIOUSLY_AUTO_REVOKED); |
| } |
| builder.SetPermissionAutoRevocationHistory(previously_revoked_permission); |
| } |
| |
| if (ui_disposition == PermissionPromptDisposition::ELEMENT_ANCHORED_BUBBLE && |
| variants.has_value()) { |
| // Variant can have a maximum of 3 values, one per site level and 2 for OS |
| // level. |
| CHECK_LE(variants->size(), 3U); |
| |
| const std::vector<ElementAnchoredBubbleVariant>& variant_array = |
| variants.value(); |
| |
| for (ElementAnchoredBubbleVariant variant : variant_array) { |
| switch (variant) { |
| case ElementAnchoredBubbleVariant::ADMINISTRATOR_GRANTED: |
| case ElementAnchoredBubbleVariant::PREVIOUSLY_GRANTED: |
| case ElementAnchoredBubbleVariant::ASK: |
| case ElementAnchoredBubbleVariant::PREVIOUSLY_DENIED: |
| case ElementAnchoredBubbleVariant::ADMINISTRATOR_DENIED: |
| builder.SetSiteLevelScreen(static_cast<int64_t>(variant)); |
| break; |
| case ElementAnchoredBubbleVariant::OS_PROMPT: |
| builder.SetOsPromptScreen(static_cast<int64_t>(variant)); |
| break; |
| |
| case ElementAnchoredBubbleVariant::OS_SYSTEM_SETTINGS: |
| builder.SetOsSystemSettingsScreen(static_cast<int64_t>(variant)); |
| break; |
| case ElementAnchoredBubbleVariant::UNINITIALIZED: |
| break; |
| } |
| } |
| } |
| if (!time_to_decision.is_zero()) { |
| builder.SetTimeToDecision(ukm::GetExponentialBucketMinForUserTiming( |
| time_to_decision.InMilliseconds())); |
| } |
| |
| builder.Record(ukm::UkmRecorder::Get()); |
| } |
| |
| // |full_version| represented in the format `YYYY.M.D.m`, where m is the |
| // minute-of-day. Return int represented in the format `YYYYMMDD`. |
| // CrowdDeny versions published before 2020 will be reported as 1. |
| // Returns 0 if no version available. |
| // Returns 1 if a version has invalid format. |
| int ConvertCrowdDenyVersionToInt(const std::optional<base::Version>& version) { |
| if (!version.has_value() || !version.value().IsValid()) |
| return 0; |
| |
| const std::vector<uint32_t>& full_version = version.value().components(); |
| if (full_version.size() != 4) |
| return 1; |
| |
| const int kCrowdDenyMinYearLimit = 2020; |
| const int year = base::checked_cast<int>(full_version.at(0)); |
| if (year < kCrowdDenyMinYearLimit) |
| return 1; |
| |
| const int month = base::checked_cast<int>(full_version.at(1)); |
| const int day = base::checked_cast<int>(full_version.at(2)); |
| |
| int short_version = year; |
| |
| short_version *= 100; |
| short_version += month; |
| short_version *= 100; |
| short_version += day; |
| |
| return short_version; |
| } |
| |
| AutoDSEPermissionRevertTransition GetAutoDSEPermissionRevertedTransition( |
| ContentSetting backed_up_setting, |
| ContentSetting effective_setting, |
| ContentSetting end_state_setting) { |
| if (backed_up_setting == CONTENT_SETTING_ASK && |
| effective_setting == CONTENT_SETTING_ALLOW && |
| end_state_setting == CONTENT_SETTING_ASK) { |
| return AutoDSEPermissionRevertTransition::NO_DECISION_ASK; |
| } else if (backed_up_setting == CONTENT_SETTING_ALLOW && |
| effective_setting == CONTENT_SETTING_ALLOW && |
| end_state_setting == CONTENT_SETTING_ALLOW) { |
| return AutoDSEPermissionRevertTransition::PRESERVE_ALLOW; |
| } else if (backed_up_setting == CONTENT_SETTING_BLOCK && |
| effective_setting == CONTENT_SETTING_ALLOW && |
| end_state_setting == CONTENT_SETTING_ASK) { |
| return AutoDSEPermissionRevertTransition::CONFLICT_ASK; |
| } else if (backed_up_setting == CONTENT_SETTING_ASK && |
| effective_setting == CONTENT_SETTING_BLOCK && |
| end_state_setting == CONTENT_SETTING_BLOCK) { |
| return AutoDSEPermissionRevertTransition::PRESERVE_BLOCK_ASK; |
| } else if (backed_up_setting == CONTENT_SETTING_ALLOW && |
| effective_setting == CONTENT_SETTING_BLOCK && |
| end_state_setting == CONTENT_SETTING_BLOCK) { |
| return AutoDSEPermissionRevertTransition::PRESERVE_BLOCK_ALLOW; |
| } else if (backed_up_setting == CONTENT_SETTING_BLOCK && |
| effective_setting == CONTENT_SETTING_BLOCK && |
| end_state_setting == CONTENT_SETTING_BLOCK) { |
| return AutoDSEPermissionRevertTransition::PRESERVE_BLOCK_BLOCK; |
| } else { |
| return AutoDSEPermissionRevertTransition::INVALID_END_STATE; |
| } |
| } |
| |
| void RecordTopLevelPermissionsHeaderPolicy( |
| ContentSettingsType content_settings_type, |
| const std::string& histogram, |
| content::RenderFrameHost* render_frame_host) { |
| DCHECK(IsCrossOriginSubframe(render_frame_host)); |
| |
| // We only care about about permission types that have a corresponding |
| // permission policy |
| const auto feature = |
| PermissionUtil::GetPermissionsPolicyFeature(content_settings_type); |
| if (!feature.has_value()) { |
| return; |
| } |
| |
| // This function will only be called when we use/prompt a permission requested |
| // from a cross-origin subframe. Being allowed by permission policy is a |
| // necessary condition to use a permission in sub-frame. |
| DCHECK(render_frame_host->IsFeatureEnabled(feature.value())); |
| base::UmaHistogramEnumeration(histogram, |
| GetTopLevelPermissionHeaderPolicyForUMA( |
| render_frame_host, feature.value()), |
| PermissionHeaderPolicyForUMA::NUM); |
| } |
| |
| PermissionChangeInfo GetChangeInfo(bool is_used, |
| bool show_infobar, |
| bool page_reload) { |
| if (show_infobar) { |
| if (page_reload) { |
| if (is_used) { |
| return PermissionChangeInfo::kInfobarShownPageReloadPermissionUsed; |
| } else { |
| return PermissionChangeInfo::kInfobarShownPageReloadPermissionNotUsed; |
| } |
| |
| } else { |
| if (is_used) { |
| return PermissionChangeInfo::kInfobarShownNoPageReloadPermissionUsed; |
| } else { |
| return PermissionChangeInfo::kInfobarShownNoPageReloadPermissionNotUsed; |
| } |
| } |
| } else { |
| if (page_reload) { |
| if (is_used) { |
| return PermissionChangeInfo::kInfobarNotShownPageReloadPermissionUsed; |
| } else { |
| return PermissionChangeInfo:: |
| kInfobarNotShownPageReloadPermissionNotUsed; |
| } |
| |
| } else { |
| if (is_used) { |
| return PermissionChangeInfo::kInfobarNotShownNoPageReloadPermissionUsed; |
| } else { |
| return PermissionChangeInfo:: |
| kInfobarNotShownNoPageReloadPermissionNotUsed; |
| } |
| } |
| } |
| } |
| |
| } // anonymous namespace |
| |
| // PermissionUmaUtil ---------------------------------------------------------- |
| |
| const char PermissionUmaUtil::kPermissionsPromptShown[] = |
| "Permissions.Prompt.Shown"; |
| const char PermissionUmaUtil::kPermissionsPromptShownGesture[] = |
| "Permissions.Prompt.Shown.Gesture"; |
| const char PermissionUmaUtil::kPermissionsPromptShownNoGesture[] = |
| "Permissions.Prompt.Shown.NoGesture"; |
| const char PermissionUmaUtil::kPermissionsPromptAccepted[] = |
| "Permissions.Prompt.Accepted"; |
| const char PermissionUmaUtil::kPermissionsPromptAcceptedGesture[] = |
| "Permissions.Prompt.Accepted.Gesture"; |
| const char PermissionUmaUtil::kPermissionsPromptAcceptedNoGesture[] = |
| "Permissions.Prompt.Accepted.NoGesture"; |
| const char PermissionUmaUtil::kPermissionsPromptAcceptedOnce[] = |
| "Permissions.Prompt.AcceptedOnce"; |
| const char PermissionUmaUtil::kPermissionsPromptAcceptedOnceGesture[] = |
| "Permissions.Prompt.AcceptedOnce.Gesture"; |
| const char PermissionUmaUtil::kPermissionsPromptAcceptedOnceNoGesture[] = |
| "Permissions.Prompt.AcceptedOnce.NoGesture"; |
| const char PermissionUmaUtil::kPermissionsPromptDenied[] = |
| "Permissions.Prompt.Denied"; |
| const char PermissionUmaUtil::kPermissionsPromptDeniedGesture[] = |
| "Permissions.Prompt.Denied.Gesture"; |
| const char PermissionUmaUtil::kPermissionsPromptDeniedNoGesture[] = |
| "Permissions.Prompt.Denied.NoGesture"; |
| const char PermissionUmaUtil::kPermissionsExperimentalUsagePrefix[] = |
| "Permissions.Experimental.Usage."; |
| const char PermissionUmaUtil::kPermissionsActionPrefix[] = |
| "Permissions.Action."; |
| |
| // Make sure you update histograms.xml permission histogram_suffix if you |
| // add new permission |
| void PermissionUmaUtil::PermissionRequested(ContentSettingsType content_type) { |
| PermissionType permission; |
| bool success = PermissionUtil::GetPermissionType(content_type, &permission); |
| DCHECK(success); |
| |
| base::UmaHistogramEnumeration("ContentSettings.PermissionRequested", |
| permission, PermissionType::NUM); |
| } |
| |
| void PermissionUmaUtil::RecordActivityIndicator( |
| std::set<ContentSettingsType> permissions, |
| bool blocked, |
| bool blocked_system_level, |
| bool clicked) { |
| DCHECK(!permissions.empty()); |
| DCHECK(permissions.contains(ContentSettingsType::MEDIASTREAM_CAMERA) || |
| permissions.contains(ContentSettingsType::MEDIASTREAM_MIC)); |
| |
| ActivityIndicatorState state; |
| if (blocked) { |
| if (blocked_system_level) { |
| state = ActivityIndicatorState::kBlockedOnSystemLevel; |
| } else { |
| state = ActivityIndicatorState::kBlockedOnSiteLevel; |
| } |
| } else { |
| state = ActivityIndicatorState::kInUse; |
| } |
| |
| std::string indicators_type; |
| |
| if (permissions.size() > 1) { |
| indicators_type = "AudioAndVideoCapture"; |
| } else if (permissions.contains(ContentSettingsType::MEDIASTREAM_CAMERA)) { |
| indicators_type = "VideoCapture"; |
| } else { |
| indicators_type = "AudioCapture"; |
| } |
| |
| std::string action; |
| if (clicked) { |
| action = "Click"; |
| } else { |
| action = "Show"; |
| } |
| |
| base::UmaHistogramEnumeration( |
| "Permissions.ActivityIndicator.LHS." + indicators_type + "." + action, |
| state); |
| } |
| |
| void PermissionUmaUtil::RecordDismissalType( |
| const std::vector<ContentSettingsType>& content_settings_types, |
| PermissionPromptDisposition ui_disposition, |
| DismissalType dismissalType) { |
| RequestTypeForUma type = GetUmaValueForRequestType( |
| ContentSettingsTypeToRequestType(content_settings_types[0])); |
| |
| if (content_settings_types.size() > 1) { |
| type = RequestTypeForUma::MULTIPLE; |
| } |
| |
| std::string permission_type = GetPermissionRequestString(type); |
| std::string permission_disposition = |
| GetPromptDispositionString(ui_disposition); |
| base::UmaHistogramEnumeration("Permissions.Prompt." + permission_type + "." + |
| permission_disposition + |
| ".Dismissed.Method", |
| dismissalType); |
| } |
| |
| void PermissionUmaUtil::RecordPermissionRequestedFromFrame( |
| ContentSettingsType content_settings_type, |
| content::RenderFrameHost* rfh) { |
| PermissionType permission; |
| bool success = |
| PermissionUtil::GetPermissionType(content_settings_type, &permission); |
| DCHECK(success); |
| |
| if (IsCrossOriginWithAnyParent(rfh)) { |
| base::UmaHistogramEnumeration("Permissions.Request.CrossOrigin", permission, |
| PermissionType::NUM); |
| } else { |
| std::string frame_level = |
| rfh->IsInPrimaryMainFrame() ? "MainFrame" : "SubFrame"; |
| |
| base::UmaHistogramEnumeration( |
| "Permissions.Request.SameOrigin." + frame_level, permission, |
| PermissionType::NUM); |
| } |
| } |
| |
| void PermissionUmaUtil::PermissionRequestPreignored(PermissionType permission) { |
| base::UmaHistogramEnumeration("Permissions.QuietPrompt.Preignore", permission, |
| PermissionType::NUM); |
| } |
| |
| void PermissionUmaUtil::PermissionRevoked( |
| ContentSettingsType permission, |
| PermissionSourceUI source_ui, |
| const GURL& revoked_origin, |
| content::BrowserContext* browser_context) { |
| DCHECK(PermissionUtil::IsPermission(permission)); |
| // An unknown gesture type is passed in since gesture type is only |
| // applicable in prompt UIs where revocations are not possible. |
| RecordPermissionAction(permission, PermissionAction::REVOKED, source_ui, |
| PermissionRequestGestureType::UNKNOWN, |
| /*time_to_decision=*/base::TimeDelta(), |
| PermissionPromptDisposition::NOT_APPLICABLE, |
| /*ui_reason=*/std::nullopt, /*variants=*/std::nullopt, |
| revoked_origin, |
| /*web_contents=*/nullptr, browser_context, |
| /*render_frame_host*/ nullptr, |
| /*predicted_grant_likelihood=*/std::nullopt, |
| /*prediction_decision_held_back=*/std::nullopt); |
| } |
| |
| void PermissionUmaUtil::RecordEmbargoPromptSuppression( |
| PermissionEmbargoStatus embargo_status) { |
| base::UmaHistogramEnumeration( |
| "Permissions.AutoBlocker.EmbargoPromptSuppression", embargo_status, |
| PermissionEmbargoStatus::NUM); |
| } |
| |
| void PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource( |
| content::PermissionStatusSource source) { |
| // Explicitly switch to ensure that any new |
| // PermissionStatusSource values are dealt with appropriately. |
| switch (source) { |
| case content::PermissionStatusSource::MULTIPLE_DISMISSALS: |
| PermissionUmaUtil::RecordEmbargoPromptSuppression( |
| PermissionEmbargoStatus::REPEATED_DISMISSALS); |
| break; |
| case content::PermissionStatusSource::MULTIPLE_IGNORES: |
| PermissionUmaUtil::RecordEmbargoPromptSuppression( |
| PermissionEmbargoStatus::REPEATED_IGNORES); |
| break; |
| case content::PermissionStatusSource::RECENT_DISPLAY: |
| PermissionUmaUtil::RecordEmbargoPromptSuppression( |
| PermissionEmbargoStatus::RECENT_DISPLAY); |
| break; |
| case content::PermissionStatusSource::UNSPECIFIED: |
| case content::PermissionStatusSource::KILL_SWITCH: |
| case content::PermissionStatusSource::INSECURE_ORIGIN: |
| case content::PermissionStatusSource::FEATURE_POLICY: |
| case content::PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN: |
| case content::PermissionStatusSource::FENCED_FRAME: |
| // The permission wasn't under embargo, so don't record anything. We may |
| // embargo it later. |
| break; |
| } |
| } |
| |
| void PermissionUmaUtil::RecordEmbargoStatus( |
| PermissionEmbargoStatus embargo_status) { |
| base::UmaHistogramEnumeration("Permissions.AutoBlocker.EmbargoStatus", |
| embargo_status, PermissionEmbargoStatus::NUM); |
| } |
| |
| void PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType permission, |
| bool is_used, |
| bool show_infobar, |
| bool page_reload) { |
| PermissionChangeInfo change_info = |
| GetChangeInfo(is_used, show_infobar, page_reload); |
| |
| std::string permission_string = GetPermissionRequestString( |
| GetUmaValueForRequestType(ContentSettingsTypeToRequestType(permission))); |
| |
| base::UmaHistogramEnumeration("Permissions.PageInfo.Changed." + |
| permission_string + ".Reallowed.Outcome", |
| change_info); |
| } |
| |
| void PermissionUmaUtil::RecordPermissionPromptAttempt( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| bool IsLocationBarEditingOrEmpty) { |
| DCHECK(!requests.empty()); |
| |
| RequestTypeForUma request_type = RequestTypeForUma::MULTIPLE; |
| PermissionRequestGestureType gesture_type = |
| PermissionRequestGestureType::UNKNOWN; |
| if (requests.size() == 1) { |
| request_type = GetUmaValueForRequestType(requests[0]->request_type()); |
| gesture_type = requests[0]->GetGestureType(); |
| } |
| |
| std::string permission_type = GetPermissionRequestString(request_type); |
| |
| std::string gesture; |
| |
| switch (gesture_type) { |
| case PermissionRequestGestureType::UNKNOWN: { |
| gesture = "Unknown"; |
| break; |
| } |
| case PermissionRequestGestureType::GESTURE: { |
| gesture = "Gesture"; |
| break; |
| } |
| case PermissionRequestGestureType::NO_GESTURE: { |
| gesture = "NoGesture"; |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| |
| std::string histogram_name = |
| "Permissions.Prompt." + permission_type + "." + gesture + ".Attempt"; |
| |
| base::UmaHistogramBoolean(histogram_name, IsLocationBarEditingOrEmpty); |
| } |
| |
| void PermissionUmaUtil::PermissionPromptShown( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& |
| requests) { |
| DCHECK(!requests.empty()); |
| |
| RequestTypeForUma request_type = RequestTypeForUma::MULTIPLE; |
| PermissionRequestGestureType gesture_type = |
| PermissionRequestGestureType::UNKNOWN; |
| if (requests.size() == 1) { |
| request_type = GetUmaValueForRequestType(requests[0]->request_type()); |
| gesture_type = requests[0]->GetGestureType(); |
| } |
| |
| PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptShown, request_type); |
| PERMISSION_BUBBLE_GESTURE_TYPE_UMA(kPermissionsPromptShownGesture, |
| kPermissionsPromptShownNoGesture, |
| gesture_type, request_type); |
| } |
| |
| void PermissionUmaUtil::PermissionPromptResolved( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| content::WebContents* web_contents, |
| PermissionAction permission_action, |
| base::TimeDelta time_to_decision, |
| PermissionPromptDisposition ui_disposition, |
| std::optional<PermissionPromptDispositionReason> ui_reason, |
| std::optional<std::vector<ElementAnchoredBubbleVariant>> variants, |
| std::optional<PredictionGrantLikelihood> predicted_grant_likelihood, |
| std::optional<bool> prediction_decision_held_back, |
| std::optional<permissions::PermissionIgnoredReason> ignored_reason, |
| bool did_show_prompt, |
| bool did_click_managed, |
| bool did_click_learn_more) { |
| switch (permission_action) { |
| case PermissionAction::GRANTED: |
| RecordPromptDecided(requests, /*accepted=*/true, /*is_one_time=*/false); |
| break; |
| case PermissionAction::DENIED: |
| RecordPromptDecided(requests, /*accepted=*/false, /*is_one_time*/ false); |
| break; |
| case PermissionAction::DISMISSED: |
| case PermissionAction::IGNORED: |
| RecordIgnoreReason(requests, ui_disposition, |
| ignored_reason.value_or( |
| permissions::PermissionIgnoredReason::UNKNOWN)); |
| break; |
| case PermissionAction::GRANTED_ONCE: |
| RecordPromptDecided(requests, /*accepted=*/true, /*is_one_time*/ true); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| std::string action_string = GetPermissionActionString(permission_action); |
| RecordEngagementMetric(requests, web_contents, action_string); |
| |
| PermissionDecisionAutoBlocker* autoblocker = |
| PermissionsClient::Get()->GetPermissionDecisionAutoBlocker( |
| web_contents->GetBrowserContext()); |
| |
| for (PermissionRequest* request : requests) { |
| ContentSettingsType permission = request->GetContentSettingsType(); |
| // TODO(timloh): We only record these metrics for permissions which have a |
| // ContentSettingsType, as otherwise they don't support GetGestureType. |
| if (permission == ContentSettingsType::DEFAULT) |
| continue; |
| |
| PermissionRequestGestureType gesture_type = request->GetGestureType(); |
| const GURL& requesting_origin = request->requesting_origin(); |
| |
| RecordPermissionAction( |
| permission, permission_action, PermissionSourceUI::PROMPT, gesture_type, |
| time_to_decision, ui_disposition, ui_reason, variants, |
| requesting_origin, web_contents, web_contents->GetBrowserContext(), |
| content::RenderFrameHost::FromID(request->get_requesting_frame_id()), |
| predicted_grant_likelihood, prediction_decision_held_back); |
| |
| std::string priorDismissPrefix = |
| "Permissions.Prompt." + action_string + ".PriorDismissCount2."; |
| std::string priorIgnorePrefix = |
| "Permissions.Prompt." + action_string + ".PriorIgnoreCount2."; |
| RecordPermissionPromptPriorCount( |
| permission, priorDismissPrefix, |
| autoblocker->GetDismissCount(requesting_origin, permission)); |
| RecordPermissionPromptPriorCount( |
| permission, priorIgnorePrefix, |
| autoblocker->GetIgnoreCount(requesting_origin, permission)); |
| } |
| |
| base::UmaHistogramEnumeration("Permissions.Action.WithDisposition." + |
| GetPromptDispositionString(ui_disposition), |
| permission_action, PermissionAction::NUM); |
| |
| RequestTypeForUma type = |
| GetUmaValueForRequestType(requests[0]->request_type()); |
| if (requests.size() > 1) |
| type = RequestTypeForUma::MULTIPLE; |
| |
| std::string permission_type = GetPermissionRequestString(type); |
| std::string permission_disposition = |
| GetPromptDispositionString(ui_disposition); |
| |
| base::UmaHistogramEnumeration("Permissions.Prompt." + permission_type + "." + |
| permission_disposition + ".Action", |
| permission_action, PermissionAction::NUM); |
| |
| if (!time_to_decision.is_zero()) { |
| base::UmaHistogramLongTimes("Permissions.Prompt." + permission_type + "." + |
| permission_disposition + "." + |
| action_string + ".TimeToAction", |
| time_to_decision); |
| } |
| |
| if (permission_action == PermissionAction::IGNORED && |
| ui_disposition != |
| PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE && |
| ui_disposition != PermissionPromptDisposition::ANCHORED_BUBBLE) { |
| base::UmaHistogramBoolean("Permissions.Prompt." + permission_type + "." + |
| permission_disposition + |
| ".Ignored.DidShowBubble", |
| did_show_prompt); |
| } |
| |
| if (requests[0]->request_type() == RequestType::kGeolocation || |
| requests[0]->request_type() == RequestType::kNotifications) { |
| if (ui_disposition == |
| PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP || |
| ui_disposition == PermissionPromptDisposition::MESSAGE_UI || |
| ui_disposition == PermissionPromptDisposition::MINI_INFOBAR) { |
| base::UmaHistogramBoolean("Permissions.Prompt." + permission_type + "." + |
| permission_disposition + "." + |
| action_string + ".DidClickManage", |
| did_click_managed); |
| } else if (ui_disposition == PermissionPromptDisposition:: |
| LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP || |
| ui_disposition == PermissionPromptDisposition::MESSAGE_UI || |
| ui_disposition == PermissionPromptDisposition::MINI_INFOBAR) { |
| base::UmaHistogramBoolean("Permissions.Prompt." + permission_type + "." + |
| permission_disposition + "." + |
| action_string + ".DidClickLearnMore", |
| did_click_learn_more); |
| } |
| } |
| } // namespace permissions |
| |
| void PermissionUmaUtil::RecordPermissionPromptPriorCount( |
| ContentSettingsType permission, |
| const std::string& prefix, |
| int count) { |
| // The user is not prompted for this permissions, thus there is no prompt |
| // event to record a prior count for. |
| DCHECK_NE(ContentSettingsType::BACKGROUND_SYNC, permission); |
| |
| // Expand UMA_HISTOGRAM_COUNTS_100 so that we can use a dynamically suffixed |
| // histogram name. |
| base::Histogram::FactoryGet( |
| prefix + PermissionUtil::GetPermissionString(permission), 1, 100, 50, |
| base::HistogramBase::kUmaTargetedHistogramFlag) |
| ->Add(count); |
| } |
| |
| void PermissionUmaUtil::RecordInfobarDetailsExpanded(bool expanded) { |
| base::UmaHistogramBoolean("Permissions.Prompt.Infobar.DetailsExpanded", |
| expanded); |
| } |
| |
| void PermissionUmaUtil::RecordCrowdDenyDelayedPushNotification( |
| base::TimeDelta delay) { |
| base::UmaHistogramTimes( |
| "Permissions.CrowdDeny.PreloadData.DelayedPushNotification", delay); |
| } |
| |
| void PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime( |
| const std::optional<base::Version>& version) { |
| base::UmaHistogramSparse( |
| "Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime", |
| ConvertCrowdDenyVersionToInt(version)); |
| } |
| |
| void PermissionUmaUtil::RecordMissingPermissionInfobarShouldShow( |
| bool should_show, |
| const std::vector<ContentSettingsType>& content_settings_types) { |
| for (const auto& content_settings_type : content_settings_types) { |
| base::UmaHistogramBoolean( |
| "Permissions.MissingOSLevelPermission.ShouldShow." + |
| PermissionUtil::GetPermissionString(content_settings_type), |
| should_show); |
| } |
| } |
| |
| void PermissionUmaUtil::RecordMissingPermissionInfobarAction( |
| PermissionAction action, |
| const std::vector<ContentSettingsType>& content_settings_types) { |
| for (const auto& content_settings_type : content_settings_types) { |
| base::UmaHistogramEnumeration( |
| "Permissions.MissingOSLevelPermission.Action." + |
| PermissionUtil::GetPermissionString(content_settings_type), |
| action, PermissionAction::NUM); |
| } |
| } |
| |
| PermissionUmaUtil::ScopedRevocationReporter::ScopedRevocationReporter( |
| content::BrowserContext* browser_context, |
| const GURL& primary_url, |
| const GURL& secondary_url, |
| ContentSettingsType content_type, |
| PermissionSourceUI source_ui) |
| : browser_context_(browser_context), |
| primary_url_(primary_url), |
| secondary_url_(secondary_url), |
| content_type_(content_type), |
| source_ui_(source_ui) { |
| if (!primary_url_.is_valid() || |
| (!secondary_url_.is_valid() && !secondary_url_.is_empty()) || |
| !IsRequestablePermissionType(content_type_) || |
| !PermissionUtil::IsPermission(content_type_)) { |
| is_initially_allowed_ = false; |
| return; |
| } |
| HostContentSettingsMap* settings_map = |
| PermissionsClient::Get()->GetSettingsMap(browser_context_); |
| ContentSetting initial_content_setting = settings_map->GetContentSetting( |
| primary_url_, secondary_url_, content_type_); |
| is_initially_allowed_ = initial_content_setting == CONTENT_SETTING_ALLOW; |
| content_settings::SettingInfo setting_info; |
| settings_map->GetWebsiteSetting(primary_url, secondary_url, content_type_, |
| &setting_info); |
| last_modified_date_ = setting_info.metadata.last_modified(); |
| scoped_revocation_reporter_in_scope = true; |
| } |
| |
| PermissionUmaUtil::ScopedRevocationReporter::ScopedRevocationReporter( |
| content::BrowserContext* browser_context, |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| PermissionSourceUI source_ui) |
| : ScopedRevocationReporter( |
| browser_context, |
| GURL(primary_pattern.ToString()), |
| GURL((secondary_pattern == ContentSettingsPattern::Wildcard()) |
| ? primary_pattern.ToString() |
| : secondary_pattern.ToString()), |
| content_type, |
| source_ui) {} |
| |
| PermissionUmaUtil::ScopedRevocationReporter::~ScopedRevocationReporter() { |
| scoped_revocation_reporter_in_scope = false; |
| if (!is_initially_allowed_) |
| return; |
| if (!IsRequestablePermissionType(content_type_) || |
| !PermissionUtil::IsPermission(content_type_)) { |
| return; |
| } |
| HostContentSettingsMap* settings_map = |
| PermissionsClient::Get()->GetSettingsMap(browser_context_); |
| ContentSetting final_content_setting = settings_map->GetContentSetting( |
| primary_url_, secondary_url_, content_type_); |
| if (final_content_setting != CONTENT_SETTING_ALLOW) { |
| // PermissionUmaUtil takes origins, even though they're typed as GURL. |
| GURL requesting_origin = primary_url_.DeprecatedGetOriginAsURL(); |
| PermissionRevoked(content_type_, source_ui_, requesting_origin, |
| browser_context_); |
| if ((content_type_ == ContentSettingsType::GEOLOCATION || |
| content_type_ == ContentSettingsType::MEDIASTREAM_CAMERA || |
| content_type_ == ContentSettingsType::MEDIASTREAM_MIC) && |
| !last_modified_date_.is_null()) { |
| RecordTimeElapsedBetweenGrantAndRevoke( |
| content_type_, base::Time::Now() - last_modified_date_); |
| } |
| } |
| } |
| |
| bool PermissionUmaUtil::ScopedRevocationReporter::IsInstanceInScope() { |
| return scoped_revocation_reporter_in_scope; |
| } |
| |
| void PermissionUmaUtil::RecordPermissionUsage( |
| ContentSettingsType permission_type, |
| content::BrowserContext* browser_context, |
| content::WebContents* web_contents, |
| const GURL& requesting_origin) { |
| PermissionsClient::Get()->GetUkmSourceId( |
| permission_type, browser_context, web_contents, requesting_origin, |
| base::BindOnce(&RecordPermissionUsageUkm, permission_type)); |
| } |
| |
| void PermissionUmaUtil::RecordPermissionAction( |
| ContentSettingsType permission, |
| PermissionAction action, |
| PermissionSourceUI source_ui, |
| PermissionRequestGestureType gesture_type, |
| base::TimeDelta time_to_decision, |
| PermissionPromptDisposition ui_disposition, |
| std::optional<PermissionPromptDispositionReason> ui_reason, |
| std::optional<std::vector<ElementAnchoredBubbleVariant>> variants, |
| const GURL& requesting_origin, |
| content::WebContents* web_contents, |
| content::BrowserContext* browser_context, |
| content::RenderFrameHost* render_frame_host, |
| std::optional<PredictionGrantLikelihood> predicted_grant_likelihood, |
| std::optional<bool> prediction_decision_held_back) { |
| DCHECK(PermissionUtil::IsPermission(permission)); |
| PermissionDecisionAutoBlocker* autoblocker = |
| PermissionsClient::Get()->GetPermissionDecisionAutoBlocker( |
| browser_context); |
| int dismiss_count = |
| autoblocker->GetDismissCount(requesting_origin, permission); |
| int ignore_count = autoblocker->GetIgnoreCount(requesting_origin, permission); |
| |
| const base::Time cutoff = base::Time::Now() - base::Days(28); |
| PermissionActionsHistory* permission_actions_history = |
| PermissionsClient::Get()->GetPermissionActionsHistory(browser_context); |
| |
| PredictionRequestFeatures::ActionCounts |
| loud_ui_actions_counts_per_request_type; |
| PredictionRequestFeatures::ActionCounts loud_ui_actions_counts; |
| PredictionRequestFeatures::ActionCounts actions_counts_per_request_type; |
| PredictionRequestFeatures::ActionCounts actions_counts; |
| |
| if (permission_actions_history != nullptr) { |
| DCHECK(IsRequestablePermissionType(permission)); |
| auto loud_ui_actions_per_request_type = |
| permission_actions_history->GetHistory( |
| cutoff, ContentSettingsTypeToRequestType(permission), |
| PermissionActionsHistory::EntryFilter::WANT_LOUD_PROMPTS_ONLY); |
| PermissionActionsHistory::FillInActionCounts( |
| &loud_ui_actions_counts_per_request_type, |
| loud_ui_actions_per_request_type); |
| |
| auto loud_ui_actions = permission_actions_history->GetHistory( |
| cutoff, PermissionActionsHistory::EntryFilter::WANT_LOUD_PROMPTS_ONLY); |
| PermissionActionsHistory::FillInActionCounts(&loud_ui_actions_counts, |
| loud_ui_actions); |
| |
| auto actions_per_request_type = permission_actions_history->GetHistory( |
| cutoff, ContentSettingsTypeToRequestType(permission), |
| PermissionActionsHistory::EntryFilter::WANT_ALL_PROMPTS); |
| PermissionActionsHistory::FillInActionCounts( |
| &actions_counts_per_request_type, actions_per_request_type); |
| |
| auto actions = permission_actions_history->GetHistory( |
| cutoff, PermissionActionsHistory::EntryFilter::WANT_ALL_PROMPTS); |
| PermissionActionsHistory::FillInActionCounts(&actions_counts, actions); |
| } |
| |
| if (action == PermissionAction::REVOKED) { |
| RecordUmaForRevocationSourceUI(permission, source_ui); |
| } |
| |
| PermissionsClient::Get()->GetUkmSourceId( |
| permission, browser_context, web_contents, requesting_origin, |
| base::BindOnce( |
| &RecordPermissionActionUkm, action, gesture_type, permission, |
| dismiss_count, ignore_count, source_ui, time_to_decision, |
| ui_disposition, ui_reason, variants, |
| permission == ContentSettingsType::NOTIFICATIONS |
| ? PermissionsClient::Get() |
| ->HadThreeConsecutiveNotificationPermissionDenies( |
| browser_context) |
| : std::nullopt, |
| PermissionsClient::Get()->HasPreviouslyAutoRevokedPermission( |
| browser_context, requesting_origin, permission), |
| predicted_grant_likelihood, loud_ui_actions_counts_per_request_type, |
| loud_ui_actions_counts, actions_counts_per_request_type, |
| actions_counts, prediction_decision_held_back)); |
| |
| if (render_frame_host && IsCrossOriginSubframe(render_frame_host)) { |
| RecordCrossOriginFrameActionAndPolicyConfiguration(permission, action, |
| render_frame_host); |
| } |
| |
| switch (permission) { |
| case ContentSettingsType::GEOLOCATION: |
| base::UmaHistogramEnumeration("Permissions.Action.Geolocation", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::NOTIFICATIONS: |
| 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: |
| base::UmaHistogramEnumeration("Permissions.Action.ProtectedMedia", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::MEDIASTREAM_MIC: |
| base::UmaHistogramEnumeration("Permissions.Action.AudioCapture", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::MEDIASTREAM_CAMERA: |
| base::UmaHistogramEnumeration("Permissions.Action.VideoCapture", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::CLIPBOARD_READ_WRITE: |
| base::UmaHistogramEnumeration("Permissions.Action.ClipboardReadWrite", |
| action, PermissionAction::NUM); |
| break; |
| case ContentSettingsType::PAYMENT_HANDLER: |
| base::UmaHistogramEnumeration("Permissions.Action.PaymentHandler", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::NFC: |
| base::UmaHistogramEnumeration("Permissions.Action.Nfc", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::VR: |
| base::UmaHistogramEnumeration("Permissions.Action.VR", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::AR: |
| base::UmaHistogramEnumeration("Permissions.Action.AR", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::STORAGE_ACCESS: |
| base::UmaHistogramEnumeration("Permissions.Action.StorageAccess", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS: |
| base::UmaHistogramEnumeration("Permissions.Action.TopLevelStorageAccess", |
| action, PermissionAction::NUM); |
| break; |
| case ContentSettingsType::CAMERA_PAN_TILT_ZOOM: |
| base::UmaHistogramEnumeration("Permissions.Action.CameraPanTiltZoom", |
| action, PermissionAction::NUM); |
| break; |
| case ContentSettingsType::WINDOW_MANAGEMENT: |
| base::UmaHistogramEnumeration("Permissions.Action.WindowPlacement", |
| action, PermissionAction::NUM); |
| break; |
| case ContentSettingsType::LOCAL_FONTS: |
| base::UmaHistogramEnumeration("Permissions.Action.LocalFonts", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::IDLE_DETECTION: |
| base::UmaHistogramEnumeration("Permissions.Action.IdleDetection", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::ACCESSIBILITY_EVENTS: |
| base::UmaHistogramEnumeration("Permissions.Action.AccessibilityEvents", |
| action, PermissionAction::NUM); |
| break; |
| case ContentSettingsType::CAPTURED_SURFACE_CONTROL: |
| base::UmaHistogramEnumeration("Permissions.Action.CapturedSurfaceControl", |
| action, PermissionAction::NUM); |
| break; |
| case ContentSettingsType::SMART_CARD_DATA: |
| base::UmaHistogramEnumeration("Permissions.Action.SmartCard", action, |
| PermissionAction::NUM); |
| break; |
| case ContentSettingsType::WEB_PRINTING: |
| base::UmaHistogramEnumeration("Permissions.Action.WebPrinting", action, |
| PermissionAction::NUM); |
| break; |
| // The user is not prompted for these permissions, thus there is no |
| // permission action recorded for them. |
| default: |
| NOTREACHED() << "PERMISSION " |
| << PermissionUtil::GetPermissionString(permission) |
| << " not accounted for"; |
| } |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPromptDecided( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| bool accepted, |
| bool is_one_time) { |
| DCHECK(!requests.empty()); |
| |
| RequestTypeForUma request_type = RequestTypeForUma::MULTIPLE; |
| PermissionRequestGestureType gesture_type = |
| PermissionRequestGestureType::UNKNOWN; |
| if (requests.size() == 1) { |
| request_type = GetUmaValueForRequestType(requests[0]->request_type()); |
| gesture_type = requests[0]->GetGestureType(); |
| } |
| |
| if (accepted) { |
| if (is_one_time) { |
| PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptAcceptedOnce, request_type); |
| PERMISSION_BUBBLE_GESTURE_TYPE_UMA( |
| kPermissionsPromptAcceptedOnceGesture, |
| kPermissionsPromptAcceptedOnceNoGesture, gesture_type, request_type); |
| } else { |
| PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptAccepted, request_type); |
| PERMISSION_BUBBLE_GESTURE_TYPE_UMA(kPermissionsPromptAcceptedGesture, |
| kPermissionsPromptAcceptedNoGesture, |
| gesture_type, request_type); |
| } |
| } else { |
| PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptDenied, request_type); |
| PERMISSION_BUBBLE_GESTURE_TYPE_UMA(kPermissionsPromptDeniedGesture, |
| kPermissionsPromptDeniedNoGesture, |
| gesture_type, request_type); |
| } |
| } |
| |
| void PermissionUmaUtil::RecordTimeElapsedBetweenGrantAndUse( |
| ContentSettingsType type, |
| base::TimeDelta delta, |
| content_settings::SettingSource source) { |
| std::string base_histogram = "Permissions.Usage.ElapsedTimeSinceGrant." + |
| PermissionUtil::GetPermissionString(type); |
| std::string source_suffix; |
| switch (source) { |
| case content_settings::SETTING_SOURCE_NONE: |
| source_suffix = "FromNone"; |
| break; |
| case content_settings::SETTING_SOURCE_POLICY: |
| source_suffix = "FromPolicy"; |
| break; |
| case content_settings::SETTING_SOURCE_EXTENSION: |
| source_suffix = "FromExtension"; |
| break; |
| case content_settings::SETTING_SOURCE_USER: |
| source_suffix = "FromUser"; |
| break; |
| case content_settings::SETTING_SOURCE_ALLOWLIST: |
| source_suffix = "FromAllowlist"; |
| break; |
| case content_settings::SETTING_SOURCE_SUPERVISED: |
| source_suffix = "FromSupervised"; |
| break; |
| case content_settings::SETTING_SOURCE_INSTALLED_WEBAPP: |
| source_suffix = "FromInstalledWebApp"; |
| break; |
| case content_settings::SETTING_SOURCE_TPCD_GRANT: |
| source_suffix = "FromSourceTpcdGrant"; |
| break; |
| } |
| base::UmaHistogramCustomCounts(base_histogram, delta.InSeconds(), 1, |
| base::Days(365).InSeconds(), 100); |
| if (!source_suffix.empty()) { |
| base::UmaHistogramCustomCounts(base_histogram + "." + source_suffix, |
| delta.InSeconds(), 1, |
| base::Days(365).InSeconds(), 100); |
| } |
| } |
| |
| void PermissionUmaUtil::RecordTimeElapsedBetweenGrantAndRevoke( |
| ContentSettingsType type, |
| base::TimeDelta delta) { |
| base::UmaHistogramCustomCounts( |
| "Permissions.Revocation.ElapsedTimeSinceGrant." + |
| PermissionUtil::GetPermissionString(type), |
| delta.InSeconds(), 1, base::Days(365).InSeconds(), 100); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordAutoDSEPermissionReverted( |
| ContentSettingsType permission_type, |
| ContentSetting backed_up_setting, |
| ContentSetting effective_setting, |
| ContentSetting end_state_setting) { |
| std::string permission_string = |
| GetPermissionRequestString(GetUmaValueForRequestType( |
| ContentSettingsTypeToRequestType(permission_type))); |
| auto transition = GetAutoDSEPermissionRevertedTransition( |
| backed_up_setting, effective_setting, end_state_setting); |
| base::UmaHistogramEnumeration( |
| "Permissions.DSE.AutoPermissionRevertTransition." + permission_string, |
| transition); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordDSEEffectiveSetting( |
| ContentSettingsType permission_type, |
| ContentSetting setting) { |
| std::string permission_string = |
| GetPermissionRequestString(GetUmaValueForRequestType( |
| ContentSettingsTypeToRequestType(permission_type))); |
| base::UmaHistogramEnumeration( |
| "Permissions.DSE.EffectiveSetting." + permission_string, setting, |
| CONTENT_SETTING_NUM_SETTINGS); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPermissionPredictionSource( |
| PermissionPredictionSource prediction_source) { |
| base::UmaHistogramEnumeration( |
| "Permissions.PredictionService.PredictionSource", prediction_source); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPermissionPredictionServiceHoldback( |
| RequestType request_type, |
| bool is_on_device, |
| bool is_heldback) { |
| if (is_on_device) { |
| base::UmaHistogramBoolean( |
| "Permissions.OnDevicePredictionService.Response." + |
| GetPermissionRequestString(GetUmaValueForRequestType(request_type)), |
| is_heldback); |
| } else { |
| base::UmaHistogramBoolean( |
| "Permissions.PredictionService.Response." + |
| GetPermissionRequestString(GetUmaValueForRequestType(request_type)), |
| is_heldback); |
| } |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPageInfoDialogAccessType( |
| PageInfoDialogAccessType access_type) { |
| base::UmaHistogramEnumeration( |
| "Permissions.ConfirmationChip.PageInfoDialogAccessType", access_type); |
| } |
| |
| // static |
| std::string PermissionUmaUtil::GetOneTimePermissionEventHistogram( |
| ContentSettingsType type) { |
| // `FILE_SYSTEM_WRITE_GUARD` is not part of `OneTimePermission`, |
| // (i.e. `CanPermissionBeAllowedOnce()`), but it uses its background expiry |
| // flow. As a result, allow logging for this event. |
| DCHECK(permissions::PermissionUtil::CanPermissionBeAllowedOnce(type) || |
| type == ContentSettingsType::FILE_SYSTEM_WRITE_GUARD); |
| |
| std::string permission_type = GetPermissionRequestString( |
| GetUmaValueForRequestType(ContentSettingsTypeToRequestType(type))); |
| return "Permissions.OneTimePermission." + permission_type + ".Event"; |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordOneTimePermissionEvent( |
| ContentSettingsType type, |
| OneTimePermissionEvent event) { |
| base::UmaHistogramEnumeration(GetOneTimePermissionEventHistogram(type), |
| event); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPageInfoPermissionChangeWithin1m( |
| ContentSettingsType type, |
| PermissionAction previous_action, |
| ContentSetting setting_after) { |
| DCHECK(IsRequestablePermissionType(type)); |
| std::string permission_type = GetPermissionRequestString( |
| GetUmaValueForRequestType(ContentSettingsTypeToRequestType(type))); |
| std::string histogram_name = |
| "Permissions.PageInfo.ChangedWithin1m." + permission_type; |
| switch (previous_action) { |
| case PermissionAction::GRANTED: |
| case PermissionAction::GRANTED_ONCE: |
| if (setting_after == ContentSetting::CONTENT_SETTING_BLOCK) { |
| base::UmaHistogramEnumeration(histogram_name, |
| PermissionChangeAction::REVOKED); |
| } else if (setting_after == ContentSetting::CONTENT_SETTING_DEFAULT) { |
| base::UmaHistogramEnumeration( |
| histogram_name, PermissionChangeAction::RESET_FROM_ALLOWED); |
| } |
| break; |
| case PermissionAction::DENIED: |
| if (setting_after == ContentSetting::CONTENT_SETTING_ALLOW) { |
| base::UmaHistogramEnumeration(histogram_name, |
| PermissionChangeAction::REALLOWED); |
| } else if (setting_after == ContentSetting::CONTENT_SETTING_DEFAULT) { |
| base::UmaHistogramEnumeration( |
| histogram_name, PermissionChangeAction::RESET_FROM_DENIED); |
| } |
| break; |
| case PermissionAction::DISMISSED: // dismissing had no effect on content |
| // setting |
| case PermissionAction::IGNORED: // ignoring has no effect on content |
| // settings |
| case PermissionAction::REVOKED: // not a relevant use case for this metric |
| case PermissionAction::NUM: // placeholder |
| break; // NOP |
| } |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPageInfoPermissionChange( |
| ContentSettingsType type, |
| ContentSetting setting_before, |
| ContentSetting setting_after, |
| bool suppress_reload_page_bar) { |
| DCHECK(IsRequestablePermissionType(type)); |
| // Currently only Camera and Mic are supported. |
| DCHECK(type == ContentSettingsType::MEDIASTREAM_MIC || |
| type == ContentSettingsType::MEDIASTREAM_CAMERA); |
| std::string permission_type = GetPermissionRequestString( |
| GetUmaValueForRequestType(ContentSettingsTypeToRequestType(type))); |
| std::string histogram_name = |
| "Permissions.PageInfo.Changed." + permission_type; |
| |
| if (suppress_reload_page_bar) { |
| histogram_name = histogram_name + ".ReloadInfobarNotShown"; |
| } else { |
| histogram_name = histogram_name + ".ReloadInfobarShown"; |
| } |
| |
| if (setting_before == ContentSetting::CONTENT_SETTING_BLOCK) { |
| if (setting_after == ContentSetting::CONTENT_SETTING_ALLOW) { |
| base::UmaHistogramEnumeration(histogram_name, |
| PermissionChangeAction::REALLOWED); |
| } else if (setting_after == ContentSetting::CONTENT_SETTING_ASK || |
| setting_after == ContentSetting::CONTENT_SETTING_DEFAULT) { |
| base::UmaHistogramEnumeration(histogram_name, |
| PermissionChangeAction::RESET_FROM_DENIED); |
| } else { |
| NOTREACHED(); |
| } |
| } else if (setting_before == ContentSetting::CONTENT_SETTING_ALLOW) { |
| if (setting_after == ContentSetting::CONTENT_SETTING_BLOCK) { |
| base::UmaHistogramEnumeration(histogram_name, |
| PermissionChangeAction::REVOKED); |
| } else if (setting_after == ContentSetting::CONTENT_SETTING_ASK || |
| setting_after == ContentSetting::CONTENT_SETTING_DEFAULT) { |
| base::UmaHistogramEnumeration(histogram_name, |
| PermissionChangeAction::RESET_FROM_ALLOWED); |
| } else if (setting_after == ContentSetting::CONTENT_SETTING_ALLOW) { |
| base::UmaHistogramEnumeration( |
| histogram_name, PermissionChangeAction::REMEMBER_CHECKBOX_TOGGLED); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| // static |
| std::string PermissionUmaUtil::GetPermissionActionString( |
| PermissionAction permission_action) { |
| switch (permission_action) { |
| case PermissionAction::GRANTED: |
| return "Accepted"; |
| case PermissionAction::DENIED: |
| return "Denied"; |
| case PermissionAction::DISMISSED: |
| return "Dismissed"; |
| case PermissionAction::IGNORED: |
| return "Ignored"; |
| case PermissionAction::GRANTED_ONCE: |
| return "AcceptedOnce"; |
| default: |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| return std::string(); |
| } |
| |
| // static |
| std::string PermissionUmaUtil::GetPromptDispositionString( |
| PermissionPromptDisposition ui_disposition) { |
| switch (ui_disposition) { |
| case PermissionPromptDisposition::ANCHORED_BUBBLE: |
| return "AnchoredBubble"; |
| case PermissionPromptDisposition::CUSTOM_MODAL_DIALOG: |
| return "CustomModalDialog"; |
| case PermissionPromptDisposition::ELEMENT_ANCHORED_BUBBLE: |
| return "ElementAnchoredBubble"; |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP: |
| return "LocationBarLeftChip"; |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP: |
| return "LocationBarLeftQuietChip"; |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP: |
| return "LocationBarLeftQuietAbusiveChip"; |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE: |
| return "LocationBarLeftChipAutoBubble"; |
| case PermissionPromptDisposition::LOCATION_BAR_RIGHT_ANIMATED_ICON: |
| return "LocationBarRightAnimatedIcon"; |
| case PermissionPromptDisposition::LOCATION_BAR_RIGHT_STATIC_ICON: |
| return "LocationBarRightStaticIcon"; |
| case PermissionPromptDisposition::MINI_INFOBAR: |
| return "MiniInfobar"; |
| case PermissionPromptDisposition::MESSAGE_UI: |
| return "MessageUI"; |
| case PermissionPromptDisposition::MODAL_DIALOG: |
| return "ModalDialog"; |
| case PermissionPromptDisposition::NONE_VISIBLE: |
| return "NoneVisible"; |
| case PermissionPromptDisposition::NOT_APPLICABLE: |
| return "NotApplicable"; |
| case PermissionPromptDisposition::MAC_OS_PROMPT: |
| return "MacOsPrompt"; |
| } |
| |
| NOTREACHED(); |
| return std::string(); |
| } |
| |
| // static |
| std::string PermissionUmaUtil::GetPromptDispositionReasonString( |
| PermissionPromptDispositionReason ui_disposition_reason) { |
| switch (ui_disposition_reason) { |
| case PermissionPromptDispositionReason::DEFAULT_FALLBACK: |
| return "DefaultFallback"; |
| case PermissionPromptDispositionReason::ON_DEVICE_PREDICTION_MODEL: |
| return "OnDevicePredictionModel"; |
| case PermissionPromptDispositionReason::PREDICTION_SERVICE: |
| return "PredictionService"; |
| case PermissionPromptDispositionReason::SAFE_BROWSING_VERDICT: |
| return "SafeBrowsingVerdict"; |
| case PermissionPromptDispositionReason::USER_PREFERENCE_IN_SETTINGS: |
| return "UserPreferenceInSettings"; |
| } |
| |
| NOTREACHED(); |
| return std::string(); |
| } |
| |
| // static |
| std::string PermissionUmaUtil::GetRequestTypeString(RequestType request_type) { |
| return GetPermissionRequestString(GetUmaValueForRequestType(request_type)); |
| } |
| |
| // static |
| bool PermissionUmaUtil::IsPromptDispositionQuiet( |
| PermissionPromptDisposition prompt_disposition) { |
| switch (prompt_disposition) { |
| case PermissionPromptDisposition::LOCATION_BAR_RIGHT_STATIC_ICON: |
| case PermissionPromptDisposition::LOCATION_BAR_RIGHT_ANIMATED_ICON: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP: |
| case PermissionPromptDisposition::MINI_INFOBAR: |
| case PermissionPromptDisposition::MESSAGE_UI: |
| case PermissionPromptDisposition::MAC_OS_PROMPT: |
| return true; |
| case PermissionPromptDisposition::ANCHORED_BUBBLE: |
| case PermissionPromptDisposition::ELEMENT_ANCHORED_BUBBLE: |
| case PermissionPromptDisposition::MODAL_DIALOG: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE: |
| case PermissionPromptDisposition::NONE_VISIBLE: |
| case PermissionPromptDisposition::CUSTOM_MODAL_DIALOG: |
| case PermissionPromptDisposition::NOT_APPLICABLE: |
| return false; |
| } |
| } |
| |
| // static |
| bool PermissionUmaUtil::IsPromptDispositionLoud( |
| PermissionPromptDisposition prompt_disposition) { |
| switch (prompt_disposition) { |
| case PermissionPromptDisposition::ANCHORED_BUBBLE: |
| case PermissionPromptDisposition::ELEMENT_ANCHORED_BUBBLE: |
| case PermissionPromptDisposition::MODAL_DIALOG: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_CHIP_AUTO_BUBBLE: |
| return true; |
| case PermissionPromptDisposition::LOCATION_BAR_RIGHT_STATIC_ICON: |
| case PermissionPromptDisposition::LOCATION_BAR_RIGHT_ANIMATED_ICON: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_CHIP: |
| case PermissionPromptDisposition::LOCATION_BAR_LEFT_QUIET_ABUSIVE_CHIP: |
| case PermissionPromptDisposition::MINI_INFOBAR: |
| case PermissionPromptDisposition::MESSAGE_UI: |
| case PermissionPromptDisposition::NONE_VISIBLE: |
| case PermissionPromptDisposition::CUSTOM_MODAL_DIALOG: |
| case PermissionPromptDisposition::NOT_APPLICABLE: |
| case PermissionPromptDisposition::MAC_OS_PROMPT: |
| return false; |
| } |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordIgnoreReason( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| PermissionPromptDisposition prompt_disposition, |
| PermissionIgnoredReason reason) { |
| RequestTypeForUma request_type = RequestTypeForUma::MULTIPLE; |
| if (requests.size() == 1) { |
| request_type = GetUmaValueForRequestType(requests[0]->request_type()); |
| } |
| |
| std::string histogram_name = |
| "Permissions.Prompt." + GetPermissionRequestString(request_type) + "." + |
| GetPromptDispositionString(prompt_disposition) + ".IgnoredReason"; |
| base::UmaHistogramEnumeration(histogram_name, reason, |
| PermissionIgnoredReason::NUM); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordPermissionsUsageSourceAndPolicyConfiguration( |
| ContentSettingsType content_settings_type, |
| content::RenderFrameHost* render_frame_host) { |
| const bool is_cross_origin_subframe = |
| IsCrossOriginSubframe(render_frame_host); |
| const auto usage_histogram = base::StrCat( |
| {kPermissionsExperimentalUsagePrefix, |
| PermissionUtil::GetPermissionString(content_settings_type)}); |
| base::UmaHistogramBoolean( |
| base::StrCat({usage_histogram, ".IsCrossOriginFrame"}), |
| is_cross_origin_subframe); |
| if (is_cross_origin_subframe) { |
| RecordTopLevelPermissionsHeaderPolicy( |
| content_settings_type, |
| base::StrCat( |
| {usage_histogram, ".CrossOriginFrame.TopLevelHeaderPolicy"}), |
| render_frame_host); |
| } |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordElementAnchoredBubbleDismiss( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| DismissedReason reason) { |
| CHECK(!requests.empty()); |
| |
| RequestTypeForUma type = |
| GetUmaValueForRequestType(requests[0]->request_type()); |
| if (requests.size() > 1) { |
| type = RequestTypeForUma::MULTIPLE; |
| } |
| |
| base::UmaHistogramEnumeration("Permissions.Prompt." + |
| GetPermissionRequestString(type) + |
| ".ElementAnchoredBubble.DismissedReason", |
| reason); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordElementAnchoredBubbleOsScreenAction( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| OsScreen screen, |
| OsScreenAction action) { |
| CHECK(!requests.empty()); |
| |
| RequestTypeForUma type = |
| GetUmaValueForRequestType(requests[0]->request_type()); |
| if (requests.size() > 1) { |
| type = RequestTypeForUma::MULTIPLE; |
| } |
| |
| std::string screen_type; |
| switch (screen) { |
| case OsScreen::OS_PROMPT: |
| screen_type = "OS_PROMPT"; |
| break; |
| case OsScreen::OS_SYSTEM_SETTINGS: |
| screen_type = "OS_SYSTEM_SETTINGS"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| base::UmaHistogramEnumeration( |
| "Permissions.Prompt." + GetPermissionRequestString(type) + |
| ".ElementAnchoredBubble" + screen_type + ".OsScreenAction", |
| action); |
| } |
| |
| void PermissionUmaUtil::RecordElementAnchoredBubbleVariantUMA( |
| const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests, |
| ElementAnchoredBubbleVariant variant) { |
| CHECK(!requests.empty()); |
| |
| RequestTypeForUma type = |
| GetUmaValueForRequestType(requests[0]->request_type()); |
| if (requests.size() > 1) { |
| type = RequestTypeForUma::MULTIPLE; |
| } |
| |
| base::UmaHistogramEnumeration("Permissions.Prompt." + |
| GetPermissionRequestString(type) + |
| ".ElementAnchoredBubble.Variant", |
| variant); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordCrossOriginFrameActionAndPolicyConfiguration( |
| ContentSettingsType content_settings_type, |
| PermissionAction action, |
| content::RenderFrameHost* render_frame_host) { |
| DCHECK(IsCrossOriginSubframe(render_frame_host)); |
| |
| const auto histogram = |
| base::StrCat({kPermissionsActionPrefix, |
| PermissionUtil::GetPermissionString(content_settings_type), |
| ".CrossOriginFrame"}); |
| base::UmaHistogramEnumeration(histogram, action, PermissionAction::NUM); |
| RecordTopLevelPermissionsHeaderPolicy( |
| content_settings_type, base::StrCat({histogram, ".TopLevelHeaderPolicy"}), |
| render_frame_host); |
| } |
| |
| // static |
| void PermissionUmaUtil::RecordTopLevelPermissionsHeaderPolicyOnNavigation( |
| content::RenderFrameHost* render_frame_host) { |
| DCHECK(render_frame_host); |
| static constexpr ContentSettingsType kContentSettingsTypesForMetrics[] = { |
| ContentSettingsType::GEOLOCATION, ContentSettingsType::MEDIASTREAM_CAMERA, |
| ContentSettingsType::MEDIASTREAM_MIC}; |
| |
| for (const auto content_settings_type : kContentSettingsTypesForMetrics) { |
| const auto feature = |
| PermissionUtil::GetPermissionsPolicyFeature(content_settings_type); |
| DCHECK(feature.has_value()); |
| base::UmaHistogramEnumeration( |
| base::StrCat( |
| {"Permissions.Experimental.PrimaryMainNavigationFinished.", |
| PermissionUtil::GetPermissionString(content_settings_type), |
| ".TopLevelHeaderPolicy"}), |
| GetTopLevelPermissionHeaderPolicyForUMA(render_frame_host, |
| feature.value()), |
| PermissionHeaderPolicyForUMA::NUM); |
| } |
| } |
| |
| void PermissionUmaUtil::RecordPermissionRegrantForUnusedSites( |
| const GURL& origin, |
| ContentSettingsType content_settings_type, |
| PermissionSourceUI source_ui, |
| content::BrowserContext* browser_context, |
| base::Time current_time) { |
| auto* hcsm = PermissionsClient::Get()->GetSettingsMap(browser_context); |
| std::optional<uint32_t> days_since_revocation = |
| GetDaysSinceUnusedSitePermissionRevocation(origin, content_settings_type, |
| current_time, hcsm); |
| if (!days_since_revocation.has_value()) { |
| return; |
| } |
| std::string source_ui_string; |
| // We are only interested in permission updates through the UI that go from |
| // Ask to Allow. This can only be done through the permission prompt and the |
| // site settings page. |
| switch (source_ui) { |
| case PermissionSourceUI::PROMPT: |
| source_ui_string = "Prompt"; |
| break; |
| case PermissionSourceUI::SITE_SETTINGS: |
| source_ui_string = "Settings"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| base::UmaHistogramExactLinear( |
| "Settings.SafetyCheck.UnusedSitePermissionsRegrantDays" + |
| source_ui_string + "." + |
| PermissionUtil::GetPermissionString(content_settings_type), |
| days_since_revocation.value(), 31); |
| base::UmaHistogramExactLinear( |
| "Settings.SafetyCheck.UnusedSitePermissionsRegrantDays" + |
| source_ui_string + ".All", |
| days_since_revocation.value(), 31); |
| } |
| |
| // static |
| std::optional<uint32_t> |
| PermissionUmaUtil::GetDaysSinceUnusedSitePermissionRevocation( |
| const GURL& origin, |
| ContentSettingsType content_settings_type, |
| base::Time current_time, |
| HostContentSettingsMap* hcsm) { |
| content_settings::SettingInfo info; |
| base::Value stored_value(hcsm->GetWebsiteSetting( |
| origin, origin, ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, |
| &info)); |
| if (!stored_value.is_dict()) { |
| return std::nullopt; |
| } |
| base::Value::List* permission_type_list = |
| stored_value.GetDict().FindList(permissions::kRevokedKey); |
| if (!permission_type_list) { |
| return std::nullopt; |
| } |
| base::Time revoked_time = |
| info.metadata.expiration() - |
| content_settings::features:: |
| kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold.Get(); |
| uint32_t days_since_revoked = (current_time - revoked_time).InDays(); |
| |
| for (auto& permission_type : *permission_type_list) { |
| auto type_int = permission_type.GetIfInt(); |
| if (!type_int.has_value()) { |
| continue; |
| } |
| if (content_settings_type == |
| static_cast<ContentSettingsType>(type_int.value())) { |
| return days_since_revoked; |
| } |
| } |
| |
| return std::nullopt; |
| } |
| |
| } // namespace permissions |