| // Copyright 2016 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 <memory> |
| |
| #include "base/feature_list.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/time/clock.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "components/content_settings/core/browser/content_settings_uma_util.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/content_settings_pattern.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_decision.h" |
| #include "components/permissions/permission_manager.h" |
| #include "components/permissions/permission_request.h" |
| #include "components/permissions/permission_request_data.h" |
| #include "components/permissions/permission_request_manager.h" |
| #include "components/permissions/permission_util.h" |
| #include "components/permissions/request_type.h" |
| #include "components/permissions/resolvers/content_setting_permission_resolver.h" |
| #include "components/permissions/test/mock_permission_prompt_factory.h" |
| #include "components/permissions/test/mock_permission_request.h" |
| #include "components/permissions/test/permission_request_observer.h" |
| #include "components/permissions/test/test_permissions_client.h" |
| #include "components/ukm/content/source_url_recorder.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/test/test_render_frame_host.h" |
| #include "services/network/public/cpp/permissions_policy/origin_with_possible_wildcards.h" |
| #include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h" |
| #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace permissions { |
| |
| namespace { |
| |
| constexpr const char* kTopLevelUrl = "https://google.com"; |
| constexpr const char* kSameOriginFrameUrl = "https://google.com/a/same.html"; |
| constexpr const char* kCrossOriginFrameUrl = "https://embedded.google.com"; |
| constexpr const char* kCrossOriginFrameUrl2 = "https://embedded2.google.com"; |
| |
| constexpr const char* kGeolocationUsageHistogramName = |
| "Permissions.Experimental.Usage.Geolocation.IsCrossOriginFrame"; |
| constexpr const char* kGeolocationPermissionsPolicyUsageHistogramName = |
| "Permissions.Experimental.Usage.Geolocation.CrossOriginFrame." |
| "TopLevelHeaderPolicy"; |
| constexpr const char* kGeolocationPermissionsPolicyActionHistogramName = |
| "Permissions.Action.Geolocation.CrossOriginFrame." |
| "TopLevelHeaderPolicy"; |
| |
| network::ParsedPermissionsPolicy CreatePermissionsPolicy( |
| network::mojom::PermissionsPolicyFeature feature, |
| const std::vector<std::string>& origins, |
| bool matches_all_origins = false) { |
| std::vector<network::OriginWithPossibleWildcards> allow_origins; |
| for (const auto& origin : origins) { |
| allow_origins.emplace_back( |
| *network::OriginWithPossibleWildcards::FromOrigin( |
| url::Origin::Create(GURL(origin)))); |
| } |
| return {{feature, allow_origins, /*self_if_matches=*/std::nullopt, |
| matches_all_origins, |
| /*matches_opaque_src*/ false}}; |
| } |
| |
| struct PermissionsDelegationTestConfig { |
| ContentSettingsType type; |
| PermissionAction action; |
| std::optional<network::mojom::PermissionsPolicyFeature> feature_overriden; |
| |
| bool matches_all_origins; |
| std::vector<std::string> origins; |
| |
| // Expected resulting permissions policy configuration. |
| std::optional<PermissionHeaderPolicyForUMA> expected_configuration; |
| }; |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| ContentSettingsForOneType GetRevokedUnusedPermissions( |
| HostContentSettingsMap* hcsm) { |
| return hcsm->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS); |
| } |
| #endif |
| |
| std::unique_ptr<permissions::PermissionRequest> CreateRequest( |
| permissions::RequestType type, |
| const char* url) { |
| return std::make_unique<permissions::PermissionRequest>( |
| std::make_unique<PermissionRequestData>( |
| std::make_unique<ContentSettingPermissionResolver>( |
| RequestTypeToContentSettingsType(type).value()), |
| /*user_gesture=*/true, GURL(url)), |
| base::BindRepeating( |
| [](PermissionDecision, bool, const PermissionRequestData&) {})); |
| } |
| |
| } // namespace |
| |
| class PermissionsDelegationUmaUtilTest |
| : public content::RenderViewHostTestHarness, |
| public testing::WithParamInterface<PermissionsDelegationTestConfig> { |
| protected: |
| void SetUp() override { |
| RenderViewHostTestHarness::SetUp(); |
| |
| auto* main_frame = web_contents()->GetPrimaryMainFrame(); |
| content::RenderFrameHostTester::For(main_frame) |
| ->InitializeRenderFrameIfNeeded(); |
| |
| SimulateNavigation(&main_frame, GURL(kTopLevelUrl)); |
| |
| PermissionRequestManager::CreateForWebContents(web_contents()); |
| manager_ = PermissionRequestManager::FromWebContents(web_contents()); |
| prompt_factory_ = std::make_unique<MockPermissionPromptFactory>(manager_); |
| } |
| |
| void TearDown() override { |
| prompt_factory_ = nullptr; |
| manager_ = nullptr; |
| content::RenderViewHostTestHarness::TearDown(); |
| } |
| |
| content::RenderFrameHost* AddChildFrameWithPermissionsPolicy( |
| content::RenderFrameHost* parent, |
| const char* origin, |
| network::ParsedPermissionsPolicy policy) { |
| content::RenderFrameHost* result = |
| content::RenderFrameHostTester::For(parent)->AppendChildWithPolicy( |
| "", policy); |
| content::RenderFrameHostTester::For(result) |
| ->InitializeRenderFrameIfNeeded(); |
| SimulateNavigation(&result, GURL(origin)); |
| return result; |
| } |
| |
| // The permissions policy is invariant and required the page to be |
| // refreshed |
| void RefreshAndSetPermissionsPolicy(content::RenderFrameHost** rfh, |
| network::ParsedPermissionsPolicy policy) { |
| content::RenderFrameHost* current = *rfh; |
| auto navigation = content::NavigationSimulator::CreateRendererInitiated( |
| current->GetLastCommittedURL(), current); |
| navigation->SetPermissionsPolicyHeader(policy); |
| navigation->Commit(); |
| *rfh = navigation->GetFinalRenderFrameHost(); |
| } |
| |
| // Simulates navigation and returns the final RenderFrameHost. |
| void SimulateNavigation(content::RenderFrameHost** rfh, const GURL& url) { |
| auto navigation_simulator = |
| content::NavigationSimulator::CreateRendererInitiated(url, *rfh); |
| navigation_simulator->Commit(); |
| *rfh = navigation_simulator->GetFinalRenderFrameHost(); |
| } |
| |
| void AddRequest(content::RenderFrameHost* rfh, |
| std::unique_ptr<PermissionRequest> request) { |
| permissions::PermissionRequestObserver observer(web_contents()); |
| manager_->AddRequest(rfh, std::move(request)); |
| observer.Wait(); |
| } |
| |
| content::RenderFrameHost* primary_main_frame() { |
| return web_contents()->GetPrimaryMainFrame(); |
| } |
| |
| protected: |
| raw_ptr<PermissionRequestManager> manager_; |
| |
| private: |
| TestPermissionsClient permissions_client_; |
| std::unique_ptr<MockPermissionPromptFactory> prompt_factory_; |
| }; |
| |
| class PermissionUmaUtilTest : public testing::Test { |
| private: |
| content::BrowserTaskEnvironment task_environment_; |
| TestPermissionsClient permissions_client_; |
| }; |
| |
| TEST_F(PermissionUmaUtilTest, ScopedRevocationReporter) { |
| content::TestBrowserContext browser_context; |
| |
| // TODO(tsergeant): Add more comprehensive tests of PermissionUmaUtil. |
| base::HistogramTester histograms; |
| auto* map = PermissionsClient::Get()->GetSettingsMap(&browser_context); |
| GURL host("https://example.com"); |
| ContentSettingsPattern host_pattern = |
| ContentSettingsPattern::FromURLNoWildcard(host); |
| ContentSettingsPattern host_containing_wildcards_pattern = |
| ContentSettingsPattern::FromString("https://[*.]example.com/"); |
| ContentSettingsType type = |
| base::FeatureList::IsEnabled( |
| content_settings::features::kApproximateGeolocationPermission) |
| ? ContentSettingsType::GEOLOCATION_WITH_OPTIONS |
| : ContentSettingsType::GEOLOCATION; |
| PermissionSourceUI source_ui = PermissionSourceUI::SITE_SETTINGS; |
| |
| // Allow->Block triggers a revocation. |
| map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); |
| { |
| PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( |
| &browser_context, host, host, type, source_ui); |
| map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_BLOCK); |
| } |
| histograms.ExpectBucketCount("Permissions.Action.Geolocation", |
| static_cast<int>(PermissionAction::REVOKED), 1); |
| |
| // Block->Allow does not trigger a revocation. |
| { |
| PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( |
| &browser_context, host, host, type, source_ui); |
| map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); |
| } |
| histograms.ExpectBucketCount("Permissions.Action.Geolocation", |
| static_cast<int>(PermissionAction::REVOKED), 1); |
| |
| // Allow->Default triggers a revocation when default is 'ask'. |
| map->SetDefaultContentSetting(type, CONTENT_SETTING_ASK); |
| { |
| PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( |
| &browser_context, host, host, type, source_ui); |
| map->SetContentSettingDefaultScope(host, host, type, |
| CONTENT_SETTING_DEFAULT); |
| } |
| histograms.ExpectBucketCount("Permissions.Action.Geolocation", |
| static_cast<int>(PermissionAction::REVOKED), 2); |
| |
| // Allow->Default does not trigger a revocation when default is 'allow'. |
| map->SetDefaultContentSetting(type, CONTENT_SETTING_ALLOW); |
| { |
| PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( |
| &browser_context, host, host, type, source_ui); |
| map->SetContentSettingDefaultScope(host, host, type, |
| CONTENT_SETTING_DEFAULT); |
| } |
| histograms.ExpectBucketCount("Permissions.Action.Geolocation", |
| static_cast<int>(PermissionAction::REVOKED), 2); |
| |
| // Allow->Block with url pattern string triggers a revocation. |
| map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); |
| { |
| PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( |
| &browser_context, host_pattern, host_pattern, type, source_ui); |
| map->SetContentSettingCustomScope(host_pattern, |
| ContentSettingsPattern::Wildcard(), type, |
| CONTENT_SETTING_BLOCK); |
| } |
| histograms.ExpectBucketCount("Permissions.Action.Geolocation", |
| static_cast<int>(PermissionAction::REVOKED), 3); |
| |
| // Allow->Block with non url pattern string does not trigger a revocation. |
| map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); |
| { |
| PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( |
| &browser_context, host_containing_wildcards_pattern, host_pattern, type, |
| source_ui); |
| map->SetContentSettingCustomScope(host_containing_wildcards_pattern, |
| ContentSettingsPattern::Wildcard(), type, |
| CONTENT_SETTING_BLOCK); |
| } |
| histograms.ExpectBucketCount("Permissions.Action.Geolocation", |
| static_cast<int>(PermissionAction::REVOKED), 3); |
| } |
| |
| TEST_F(PermissionUmaUtilTest, CrowdDenyVersionTest) { |
| base::HistogramTester histograms; |
| |
| const std::optional<base::Version> empty_version; |
| PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime(empty_version); |
| histograms.ExpectBucketCount( |
| "Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime", 0, 1); |
| |
| const std::optional<base::Version> valid_version = |
| base::Version({2020, 10, 11, 1234}); |
| PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime(valid_version); |
| histograms.ExpectBucketCount( |
| "Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime", 20201011, 1); |
| |
| const std::optional<base::Version> valid_old_version = |
| base::Version({2019, 10, 10, 1234}); |
| PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime(valid_old_version); |
| histograms.ExpectBucketCount( |
| "Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime", 1, 1); |
| |
| const std::optional<base::Version> valid_future_version = |
| base::Version({2021, 1, 1, 1234}); |
| PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime( |
| valid_future_version); |
| histograms.ExpectBucketCount( |
| "Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime", 20210101, 1); |
| |
| const std::optional<base::Version> invalid_version = |
| base::Version({2020, 10, 11}); |
| PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime(valid_version); |
| histograms.ExpectBucketCount( |
| "Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime", 1, 1); |
| } |
| |
| // Test that the appropriate UMA metrics have been recorded when the DSE is |
| // disabled. |
| TEST_F(PermissionUmaUtilTest, MetricsAreRecordedWhenAutoDSEPermissionReverted) { |
| const std::string kTransitionHistogramPrefix = |
| "Permissions.DSE.AutoPermissionRevertTransition."; |
| |
| constexpr struct { |
| ContentSetting backed_up_setting; |
| ContentSetting effective_setting; |
| ContentSetting end_state_setting; |
| permissions::AutoDSEPermissionRevertTransition expected_transition; |
| } kTests[] = { |
| // Expected valid combinations. |
| {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, |
| permissions::AutoDSEPermissionRevertTransition::NO_DECISION_ASK}, |
| {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, |
| permissions::AutoDSEPermissionRevertTransition::PRESERVE_ALLOW}, |
| {CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, |
| permissions::AutoDSEPermissionRevertTransition::CONFLICT_ASK}, |
| {CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, |
| permissions::AutoDSEPermissionRevertTransition::PRESERVE_BLOCK_ASK}, |
| {CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, |
| permissions::AutoDSEPermissionRevertTransition::PRESERVE_BLOCK_ALLOW}, |
| {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, |
| permissions::AutoDSEPermissionRevertTransition::PRESERVE_BLOCK_BLOCK}, |
| }; |
| |
| // We test every combination of test case for notifications and geolocation to |
| // basically test the entire possible transition space. |
| for (const auto& test : kTests) { |
| for (const auto type : {ContentSettingsType::NOTIFICATIONS, |
| ContentSettingsType::GEOLOCATION}) { |
| const std::string type_string = type == ContentSettingsType::NOTIFICATIONS |
| ? "Notifications" |
| : "Geolocation"; |
| base::HistogramTester histograms; |
| PermissionUmaUtil::RecordAutoDSEPermissionReverted( |
| type, test.backed_up_setting, test.effective_setting, |
| test.end_state_setting); |
| |
| // Test that the expected samples are recorded in histograms. |
| histograms.ExpectBucketCount(kTransitionHistogramPrefix + type_string, |
| test.expected_transition, 1); |
| histograms.ExpectTotalCount(kTransitionHistogramPrefix + type_string, 1); |
| } |
| } |
| } |
| |
| TEST_F(PermissionsDelegationUmaUtilTest, UsageAndPromptInTopLevelFrame) { |
| base::HistogramTester histograms; |
| auto* main_frame = primary_main_frame(); |
| histograms.ExpectTotalCount(kGeolocationUsageHistogramName, 0); |
| |
| AddRequest(main_frame, |
| CreateRequest(RequestType::kGeolocation, kTopLevelUrl)); |
| |
| PermissionUmaUtil::RecordPermissionsUsageSourceAndPolicyConfiguration( |
| ContentSettingsType::GEOLOCATION, main_frame); |
| EXPECT_THAT(histograms.GetAllSamples(kGeolocationUsageHistogramName), |
| testing::ElementsAre(base::Bucket(0, 1))); |
| |
| PermissionUmaUtil::PermissionPromptResolved( |
| manager_->Requests(), web_contents(), PermissionAction::GRANTED, |
| /*time_to_decision*/ base::TimeDelta(), |
| PermissionPromptDisposition::NOT_APPLICABLE, |
| /* ui_reason*/ std::nullopt, |
| /*variants*/ {}, |
| /*predicted_grant_likelihood*/ std::nullopt, |
| /*permission_request_relevance*/ std::nullopt, |
| /*prediction_decision_held_back*/ std::nullopt, |
| /*ignored_reason*/ std::nullopt, /*did_show_prompt*/ false, |
| /*did_click_managed*/ false, |
| /*did_click_learn_more*/ false); |
| histograms.ExpectTotalCount(kGeolocationPermissionsPolicyActionHistogramName, |
| 0); |
| } |
| |
| TEST_F(PermissionUmaUtilTest, LhsIndicatorsShowTest) { |
| base::HistogramTester histograms; |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/false, |
| /*blocked_system_level=*/false, |
| /*click=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.VideoCapture.Show", |
| ActivityIndicatorState::kInUse, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/true, |
| /*blocked_system_level=*/false, |
| /*click=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.VideoCapture.Show", |
| ActivityIndicatorState::kBlockedOnSiteLevel, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/true, |
| /*blocked_system_level=*/true, |
| /*click=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.VideoCapture.Show", |
| ActivityIndicatorState::kBlockedOnSystemLevel, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_MIC}, |
| /*blocked=*/false, |
| /*blocked_system_level=*/false, |
| /*click=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.AudioCapture.Show", |
| ActivityIndicatorState::kInUse, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_MIC, |
| ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/false, |
| /*blocked_system_level=*/false, |
| /*click=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.AudioAndVideoCapture.Show", |
| ActivityIndicatorState::kInUse, 1); |
| } |
| |
| TEST_F(PermissionUmaUtilTest, LhsIndicatorsClickTest) { |
| base::HistogramTester histograms; |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/false, |
| /*blocked_system_level=*/false, |
| /*click=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.VideoCapture.Click", |
| ActivityIndicatorState::kInUse, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/true, |
| /*blocked_system_level=*/false, |
| /*click=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.VideoCapture.Click", |
| ActivityIndicatorState::kBlockedOnSiteLevel, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/true, |
| /*blocked_system_level=*/true, |
| /*click=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.VideoCapture.Click", |
| ActivityIndicatorState::kBlockedOnSystemLevel, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_MIC}, |
| /*blocked=*/false, |
| /*blocked_system_level=*/false, |
| /*click=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.AudioCapture.Click", |
| ActivityIndicatorState::kInUse, 1); |
| |
| PermissionUmaUtil::RecordActivityIndicator( |
| {ContentSettingsType::MEDIASTREAM_MIC, |
| ContentSettingsType::MEDIASTREAM_CAMERA}, |
| /*blocked=*/false, |
| /*blocked_system_level=*/false, |
| /*click=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.ActivityIndicator.LHS.AudioAndVideoCapture.Click", |
| ActivityIndicatorState::kInUse, 1); |
| } |
| |
| TEST_F(PermissionUmaUtilTest, PageInfoPermissionReallowedTest) { |
| base::HistogramTester histograms; |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, |
| /*show_infobar=*/true, /*page_reload=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo::kInfobarShownPageReloadPermissionUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, |
| /*show_infobar=*/true, /*page_reload=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarShownPageReloadPermissionNotUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, |
| /*show_infobar=*/true, /*page_reload=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarShownNoPageReloadPermissionUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, |
| /*show_infobar=*/true, /*page_reload=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarShownNoPageReloadPermissionNotUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, |
| /*show_infobar=*/false, /*page_reload=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarNotShownPageReloadPermissionUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, |
| /*show_infobar=*/false, /*page_reload=*/true); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarNotShownPageReloadPermissionNotUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, |
| /*show_infobar=*/false, /*page_reload=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarNotShownNoPageReloadPermissionUsed, |
| 1); |
| |
| PermissionUmaUtil::RecordPermissionRecoverySuccessRate( |
| ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, |
| /*show_infobar=*/false, /*page_reload=*/false); |
| histograms.ExpectBucketCount( |
| "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", |
| permissions::PermissionChangeInfo:: |
| kInfobarNotShownNoPageReloadPermissionNotUsed, |
| 1); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| TEST_F(PermissionUmaUtilTest, RecordPermissionRegrantForUnusedSites) { |
| const GURL origin = GURL("https://example1.com:443"); |
| content::TestBrowserContext browser_context; |
| base::HistogramTester histograms; |
| ContentSettingsType content_type = ContentSettingsType::GEOLOCATION; |
| std::string permission_string = |
| PermissionUtil::GetPermissionString(content_type); |
| base::SimpleTestClock clock; |
| base::Time now(base::Time::Now()); |
| clock.SetNow(now); |
| HostContentSettingsMap* hcsm = |
| PermissionsClient::Get()->GetSettingsMap(&browser_context); |
| hcsm->SetClockForTesting(&clock); |
| |
| std::string prefix = "Settings.SafetyCheck.UnusedSitePermissionsRegrantDays"; |
| |
| // Record regrant before permission has been revoked. |
| PermissionUmaUtil::RecordPermissionRegrantForUnusedSites( |
| origin, content_type, PermissionSourceUI::PROMPT, &browser_context, now); |
| histograms.ExpectTotalCount(prefix + "Prompt." + permission_string, 0); |
| histograms.ExpectTotalCount(prefix + "Prompt.All", 0); |
| |
| // Create a revoked permission. |
| auto dict = base::Value::Dict().Set( |
| permissions::kRevokedKey, |
| base::Value::List().Append(static_cast<int32_t>(content_type))); |
| // Set expiration to five days before the clean-up threshold to mimic that the |
| // permission was revoked five days ago. |
| base::Time past(now - base::Days(5)); |
| content_settings::ContentSettingConstraints constraint(past); |
| constraint.set_lifetime( |
| content_settings::features:: |
| kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold.Get()); |
| hcsm->SetWebsiteSettingDefaultScope( |
| origin, origin, ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, |
| base::Value(dict.Clone()), constraint); |
| |
| // Regrant another permission through the prompt. |
| PermissionUmaUtil::RecordPermissionRegrantForUnusedSites( |
| origin, ContentSettingsType::NOTIFICATIONS, PermissionSourceUI::PROMPT, |
| &browser_context, now); |
| histograms.ExpectTotalCount(prefix + "Prompt." + |
| PermissionUtil::GetPermissionString( |
| ContentSettingsType::NOTIFICATIONS), |
| 0); |
| histograms.ExpectTotalCount(prefix + "Prompt.All", 0); |
| |
| // Regrant the geolocation permission through the prompt. |
| PermissionUmaUtil::RecordPermissionRegrantForUnusedSites( |
| origin, content_type, PermissionSourceUI::PROMPT, &browser_context, now); |
| histograms.ExpectBucketCount(prefix + "Prompt." + permission_string, 5, 1); |
| histograms.ExpectBucketCount(prefix + "Prompt.All", 5, 1); |
| |
| // Regrant the geolocation permission through site settings. |
| PermissionUmaUtil::RecordPermissionRegrantForUnusedSites( |
| origin, content_type, PermissionSourceUI::SITE_SETTINGS, &browser_context, |
| now); |
| histograms.ExpectBucketCount(prefix + "Settings." + permission_string, 5, 1); |
| histograms.ExpectBucketCount(prefix + "Settings.All", 5, 1); |
| } |
| |
| TEST_F(PermissionUmaUtilTest, GetDaysSinceUnusedSitePermissionRevocation) { |
| base::test::ScopedFeatureList scoped_feature; |
| scoped_feature.InitAndEnableFeature( |
| content_settings::features::kSafetyCheckUnusedSitePermissions); |
| |
| content::TestBrowserContext browser_context; |
| base::SimpleTestClock clock; |
| base::Time now(base::Time::Now()); |
| clock.SetNow(now); |
| HostContentSettingsMap* hcsm = |
| PermissionsClient::Get()->GetSettingsMap(&browser_context); |
| |
| const GURL url = GURL("https://example1.com:443"); |
| const ContentSettingsType type = ContentSettingsType::GEOLOCATION; |
| content_settings::ContentSettingConstraints constraint(clock.Now()); |
| constraint.set_track_last_visit_for_autoexpiration(true); |
| |
| std::optional<uint32_t> days_since_revocation; |
| |
| // Permission has not yet been revoked, so shouldn't return a number of days |
| // since revocation. |
| days_since_revocation = |
| PermissionUmaUtil::GetDaysSinceUnusedSitePermissionRevocation( |
| url, ContentSettingsType::GEOLOCATION, now, hcsm); |
| ASSERT_FALSE(days_since_revocation.has_value()); |
| |
| hcsm->SetContentSettingDefaultScope( |
| url, url, type, ContentSetting::CONTENT_SETTING_ALLOW, constraint); |
| EXPECT_EQ(GetRevokedUnusedPermissions(hcsm).size(), 0u); |
| |
| // Travel 70 days through time such that the granted permission would be |
| // revoked. |
| clock.Advance(base::Days(70)); |
| // Revoke permission. |
| content_settings::ContentSettingConstraints expiration_constraint( |
| clock.Now()); |
| expiration_constraint.set_lifetime(base::Days(30)); |
| auto dict = base::Value::Dict().Set( |
| permissions::kRevokedKey, base::Value::List().Append(static_cast<int32_t>( |
| ContentSettingsType::GEOLOCATION))); |
| hcsm->SetWebsiteSettingCustomScope( |
| ContentSettingsPattern::FromURLNoWildcard(url), |
| ContentSettingsPattern::Wildcard(), |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, |
| base::Value(std::move(dict)), expiration_constraint); |
| EXPECT_EQ(GetRevokedUnusedPermissions(hcsm).size(), 1u); |
| |
| days_since_revocation = |
| PermissionUmaUtil::GetDaysSinceUnusedSitePermissionRevocation( |
| url, ContentSettingsType::GEOLOCATION, clock.Now(), hcsm); |
| ASSERT_TRUE(days_since_revocation.has_value()); |
| EXPECT_EQ(days_since_revocation.value(), 0u); |
| |
| // Forward the clock for five days, which would be the number of days since |
| // revocation. |
| clock.Advance(base::Days(5)); |
| |
| days_since_revocation = |
| PermissionUmaUtil::GetDaysSinceUnusedSitePermissionRevocation( |
| url, ContentSettingsType::GEOLOCATION, clock.Now(), hcsm); |
| ASSERT_TRUE(days_since_revocation.has_value()); |
| EXPECT_EQ(days_since_revocation.value(), 5u); |
| } |
| #endif |
| |
| // Inside your PermissionRecorderTest test fixture from earlier |
| TEST_F(PermissionsDelegationUmaUtilTest, SiteLevelAndOSPromptVariantsTest) { |
| std::vector<ElementAnchoredBubbleVariant> variant_vector = { |
| ElementAnchoredBubbleVariant::kAsk}; |
| |
| #if BUILDFLAG(IS_MAC) |
| variant_vector.push_back(ElementAnchoredBubbleVariant::kOsPrompt); |
| variant_vector.push_back(ElementAnchoredBubbleVariant::kOsSystemSettings); |
| #endif |
| |
| std::optional<std::vector<ElementAnchoredBubbleVariant>> variants = |
| variant_vector; |
| |
| ukm::InitializeSourceUrlRecorderForWebContents(web_contents()); |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| |
| auto* main_frame = primary_main_frame(); |
| |
| AddRequest(main_frame, |
| CreateRequest(RequestType::kCameraStream, kTopLevelUrl)); |
| |
| PermissionUmaUtil::PermissionPromptResolved( |
| {manager_->Requests()}, web_contents(), PermissionAction::GRANTED, |
| /*time_to_decision*/ base::TimeDelta(), |
| PermissionPromptDisposition::ELEMENT_ANCHORED_BUBBLE, |
| /* ui_reason*/ std::nullopt, variants, |
| /*predicted_grant_likelihood*/ std::nullopt, |
| /*permission_request_relevance*/ std::nullopt, |
| /*prediction_decision_held_back*/ std::nullopt, |
| /*ignored_reason*/ std::nullopt, /*did_show_prompt*/ true, |
| /*did_click_managed*/ false, |
| /*did_click_learn_more*/ false); |
| |
| const auto entries = ukm_recorder.GetEntriesByName("Permission"); |
| ASSERT_EQ(1u, entries.size()); |
| const auto* entry = entries.back().get(); |
| EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "SiteLevelScreen"), |
| static_cast<int64_t>(ElementAnchoredBubbleVariant::kAsk)); |
| #if BUILDFLAG(IS_MAC) |
| EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "OsPromptScreen"), |
| static_cast<int64_t>(ElementAnchoredBubbleVariant::kOsPrompt)); |
| EXPECT_EQ( |
| *ukm_recorder.GetEntryMetric(entry, "OsSystemSettingsScreen"), |
| static_cast<int64_t>(ElementAnchoredBubbleVariant::kOsSystemSettings)); |
| #endif |
| } |
| |
| TEST_F(PermissionsDelegationUmaUtilTest, SameOriginFrame) { |
| base::HistogramTester histograms; |
| auto* main_frame = primary_main_frame(); |
| auto* child_frame = AddChildFrameWithPermissionsPolicy( |
| main_frame, kSameOriginFrameUrl, |
| CreatePermissionsPolicy( |
| network::mojom::PermissionsPolicyFeature::kGeolocation, |
| {std::string(kTopLevelUrl), std::string(kSameOriginFrameUrl)}, |
| /*matches_all_origins*/ true)); |
| histograms.ExpectTotalCount(kGeolocationUsageHistogramName, 0); |
| |
| AddRequest(child_frame, |
| CreateRequest(RequestType::kGeolocation, kSameOriginFrameUrl)); |
| |
| PermissionUmaUtil::RecordPermissionsUsageSourceAndPolicyConfiguration( |
| ContentSettingsType::GEOLOCATION, child_frame); |
| EXPECT_THAT(histograms.GetAllSamples(kGeolocationUsageHistogramName), |
| testing::ElementsAre(base::Bucket(0, 1))); |
| histograms.ExpectTotalCount(kGeolocationPermissionsPolicyUsageHistogramName, |
| 0); |
| PermissionUmaUtil::PermissionPromptResolved( |
| manager_->Requests(), web_contents(), PermissionAction::GRANTED, |
| /*time_to_decision*/ base::TimeDelta(), |
| PermissionPromptDisposition::NOT_APPLICABLE, |
| /* ui_reason*/ std::nullopt, |
| /*variants*/ {}, |
| /*predicted_grant_likelihood*/ std::nullopt, |
| /*permission_request_relevance*/ std::nullopt, |
| /*prediction_decision_held_back*/ std::nullopt, |
| /*ignored_reason*/ std::nullopt, /*did_show_prompt*/ false, |
| /*did_click_managed*/ false, |
| /*did_click_learn_more*/ false); |
| histograms.ExpectTotalCount(kGeolocationPermissionsPolicyActionHistogramName, |
| 0); |
| } |
| |
| TEST_P(PermissionsDelegationUmaUtilTest, TopLevelFrame) { |
| auto type = GetParam().type; |
| std::string permission_string = PermissionUtil::GetPermissionString(type); |
| // The histogram values should match with the ones defined in |
| // |permission_uma_util.cc| |
| std::string kPermissionsPolicyHeaderHistogramName = |
| base::StrCat({"Permissions.Experimental.PrimaryMainNavigationFinished.", |
| permission_string, ".TopLevelHeaderPolicy"}); |
| |
| base::HistogramTester histograms; |
| auto* main_frame = primary_main_frame(); |
| auto feature = PermissionUtil::GetPermissionsPolicyFeature(type); |
| network::ParsedPermissionsPolicy top_policy; |
| if (feature.has_value() && |
| (GetParam().matches_all_origins || !GetParam().origins.empty())) { |
| top_policy = CreatePermissionsPolicy( |
| GetParam().feature_overriden.has_value() |
| ? GetParam().feature_overriden.value() |
| : feature.value(), |
| GetParam().origins, GetParam().matches_all_origins); |
| } |
| |
| RefreshAndSetPermissionsPolicy(&main_frame, top_policy); |
| EXPECT_THAT( |
| histograms.GetAllSamples(kPermissionsPolicyHeaderHistogramName), |
| testing::ElementsAre(base::Bucket( |
| static_cast<int>(GetParam().expected_configuration.value()), 1))); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| TopLevelFrame, |
| PermissionsDelegationUmaUtilTest, |
| testing::Values( |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, PermissionAction::GRANTED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ true, |
| /*origins*/ {}, |
| PermissionHeaderPolicyForUMA::FEATURE_ALLOWLIST_IS_WILDCARD}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, |
| PermissionAction::GRANTED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ false, |
| {std::string(kTopLevelUrl)}, |
| PermissionHeaderPolicyForUMA:: |
| FEATURE_ALLOWLIST_EXPLICITLY_MATCHES_ORIGIN}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, PermissionAction::GRANTED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ false, |
| /*origins*/ {}, |
| PermissionHeaderPolicyForUMA::HEADER_NOT_PRESENT_OR_INVALID}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, |
| PermissionAction::GRANTED, |
| std::make_optional<network::mojom::PermissionsPolicyFeature>( |
| network::mojom::PermissionsPolicyFeature::kCamera), |
| /*matches_all_origins*/ false, |
| {std::string(kTopLevelUrl)}, |
| PermissionHeaderPolicyForUMA::FEATURE_NOT_PRESENT}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, |
| PermissionAction::GRANTED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ false, |
| {std::string(kCrossOriginFrameUrl)}, |
| PermissionHeaderPolicyForUMA:: |
| FEATURE_ALLOWLIST_DOES_NOT_MATCH_ORIGIN})); |
| |
| class CrossFramePermissionsDelegationUmaUtilTest |
| : public PermissionsDelegationUmaUtilTest { |
| public: |
| CrossFramePermissionsDelegationUmaUtilTest() = default; |
| }; |
| |
| TEST_P(CrossFramePermissionsDelegationUmaUtilTest, CrossOriginFrame) { |
| auto type = GetParam().type; |
| std::string permission_string = PermissionUtil::GetPermissionString(type); |
| // The histogram values should match with the ones defined in |
| // |permission_uma_util.cc| |
| std::string kUsageHistogramName = |
| base::StrCat({PermissionUmaUtil::kPermissionsExperimentalUsagePrefix, |
| permission_string, ".IsCrossOriginFrame"}); |
| std::string kCrossOriginFrameActionHistogramName = |
| base::StrCat({PermissionUmaUtil::kPermissionsActionPrefix, |
| permission_string, ".CrossOriginFrame"}); |
| std::string kPermissionsPolicyUsageHistogramName = base::StrCat( |
| {PermissionUmaUtil::kPermissionsExperimentalUsagePrefix, |
| permission_string, ".CrossOriginFrame.TopLevelHeaderPolicy"}); |
| std::string kPermissionsPolicyActionHistogramName = base::StrCat( |
| {PermissionUmaUtil::kPermissionsActionPrefix, permission_string, |
| ".CrossOriginFrame.TopLevelHeaderPolicy"}); |
| |
| base::HistogramTester histograms; |
| auto* main_frame = primary_main_frame(); |
| auto feature = PermissionUtil::GetPermissionsPolicyFeature(type); |
| network::ParsedPermissionsPolicy top_policy; |
| if (feature.has_value() && |
| (GetParam().matches_all_origins || !GetParam().origins.empty())) { |
| top_policy = CreatePermissionsPolicy( |
| GetParam().feature_overriden.has_value() |
| ? GetParam().feature_overriden.value() |
| : feature.value(), |
| GetParam().origins, GetParam().matches_all_origins); |
| } |
| |
| if (!top_policy.empty()) { |
| RefreshAndSetPermissionsPolicy(&main_frame, top_policy); |
| } |
| |
| // Add nested subframes A(B(C)) |
| network::ParsedPermissionsPolicy empty_policy; |
| auto* child_frame = AddChildFrameWithPermissionsPolicy( |
| main_frame, kCrossOriginFrameUrl, |
| feature.has_value() |
| ? CreatePermissionsPolicy(feature.value(), |
| {std::string(kCrossOriginFrameUrl)}, |
| /*matches_all_origins*/ false) |
| : empty_policy); |
| child_frame = AddChildFrameWithPermissionsPolicy( |
| child_frame, kCrossOriginFrameUrl2, |
| feature.has_value() |
| ? CreatePermissionsPolicy(feature.value(), |
| {std::string(kCrossOriginFrameUrl2)}, |
| /*matches_all_origins*/ false) |
| : empty_policy); |
| histograms.ExpectTotalCount(kUsageHistogramName, 0); |
| histograms.ExpectTotalCount(kPermissionsPolicyUsageHistogramName, 0); |
| histograms.ExpectTotalCount(kPermissionsPolicyActionHistogramName, 0); |
| |
| PermissionUmaUtil::RecordPermissionsUsageSourceAndPolicyConfiguration( |
| type, child_frame); |
| EXPECT_THAT(histograms.GetAllSamples(kUsageHistogramName), |
| testing::ElementsAre(base::Bucket(1, 1))); |
| if (feature.has_value()) { |
| EXPECT_THAT( |
| histograms.GetAllSamples(kPermissionsPolicyUsageHistogramName), |
| testing::ElementsAre(base::Bucket( |
| static_cast<int>(GetParam().expected_configuration.value()), 1))); |
| } else { |
| histograms.ExpectTotalCount(kPermissionsPolicyUsageHistogramName, 0); |
| } |
| |
| AddRequest(child_frame, |
| CreateRequest(permissions::ContentSettingsTypeToRequestType(type), |
| kCrossOriginFrameUrl2)); |
| |
| PermissionUmaUtil::PermissionPromptResolved( |
| manager_->Requests(), web_contents(), GetParam().action, |
| /*time_to_decision*/ base::TimeDelta(), |
| PermissionPromptDisposition::NOT_APPLICABLE, |
| /* ui_reason*/ std::nullopt, |
| /*variants*/ {}, |
| /*predicted_grant_likelihood*/ std::nullopt, |
| /*permission_request_relevance*/ std::nullopt, |
| /*prediction_decision_held_back*/ std::nullopt, |
| /*ignored_reason*/ std::nullopt, /*did_show_prompt*/ false, |
| /*did_click_managed*/ false, |
| /*did_click_learn_more*/ false); |
| if (feature.has_value()) { |
| EXPECT_THAT( |
| histograms.GetAllSamples(kPermissionsPolicyActionHistogramName), |
| testing::ElementsAre(base::Bucket( |
| static_cast<int>(GetParam().expected_configuration.value()), 1))); |
| } else { |
| histograms.ExpectTotalCount(kPermissionsPolicyActionHistogramName, 0); |
| } |
| |
| EXPECT_THAT(histograms.GetAllSamples(kCrossOriginFrameActionHistogramName), |
| testing::ElementsAre( |
| base::Bucket(static_cast<int>(GetParam().action), 1))); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| CrossOriginFrame, |
| CrossFramePermissionsDelegationUmaUtilTest, |
| testing::Values( |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, PermissionAction::GRANTED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ true, |
| /*origins*/ {}, |
| PermissionHeaderPolicyForUMA::FEATURE_ALLOWLIST_IS_WILDCARD}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, |
| PermissionAction::DENIED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ false, |
| {std::string(kTopLevelUrl), std::string(kCrossOriginFrameUrl), |
| std::string(kCrossOriginFrameUrl2)}, |
| PermissionHeaderPolicyForUMA:: |
| FEATURE_ALLOWLIST_EXPLICITLY_MATCHES_ORIGIN}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, PermissionAction::GRANTED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ false, |
| /*origins*/ {}, |
| PermissionHeaderPolicyForUMA::HEADER_NOT_PRESENT_OR_INVALID}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, |
| PermissionAction::GRANTED, |
| std::make_optional<network::mojom::PermissionsPolicyFeature>( |
| network::mojom::PermissionsPolicyFeature::kCamera), |
| /*matches_all_origins*/ false, |
| {std::string(kTopLevelUrl), std::string(kCrossOriginFrameUrl)}, |
| PermissionHeaderPolicyForUMA::FEATURE_NOT_PRESENT}, |
| |
| PermissionsDelegationTestConfig{ |
| ContentSettingsType::GEOLOCATION, |
| PermissionAction::DENIED, |
| /*feature_overriden*/ std::nullopt, |
| /*matches_all_origins*/ false, |
| {std::string(kTopLevelUrl), std::string(kCrossOriginFrameUrl)}, |
| PermissionHeaderPolicyForUMA:: |
| FEATURE_ALLOWLIST_DOES_NOT_MATCH_ORIGIN})); |
| |
| class UkmRecorderPermissionUmaUtilTest |
| : public content::RenderViewHostTestHarness { |
| public: |
| void SetUp() override { content::RenderViewHostTestHarness::SetUp(); } |
| |
| class UkmRecorderTestPermissionsClient : public TestPermissionsClient { |
| public: |
| UkmRecorderTestPermissionsClient() = default; |
| |
| void SetSimulatedHasSourceId(bool source_id) { |
| simulated_has_source_id_ = source_id; |
| } |
| |
| void GetUkmSourceId(ContentSettingsType permission_type, |
| content::BrowserContext* browser_context, |
| content::WebContents* web_contents, |
| const GURL& requesting_origin, |
| GetUkmSourceIdCallback callback) override { |
| // Short circuit and return a null SourceId. |
| if (!simulated_has_source_id_) { |
| std::move(callback).Run(std::nullopt); |
| } else { |
| ukm::SourceId fake_source_id = |
| ukm::ConvertToSourceId(1, ukm::SourceIdType::NOTIFICATION_ID); |
| std::move(callback).Run(fake_source_id); |
| } |
| } |
| |
| private: |
| bool simulated_has_source_id_ = false; |
| }; |
| |
| UkmRecorderTestPermissionsClient permissions_client_; |
| }; |
| |
| TEST_F(UkmRecorderPermissionUmaUtilTest, |
| NotificationRevocationHistogramDidRecordUkmTest) { |
| base::HistogramTester histograms; |
| content::TestBrowserContext browser_context; |
| ukm::InitializeSourceUrlRecorderForWebContents(web_contents()); |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| |
| permissions_client_.SetSimulatedHasSourceId(true); |
| const GURL origin(kTopLevelUrl); |
| PermissionUmaUtil::PermissionRevoked( |
| ContentSettingsType::NOTIFICATIONS, |
| permissions::PermissionSourceUI::ANDROID_SETTINGS, origin, |
| &browser_context); |
| |
| histograms.ExpectBucketCount("Permissions.Action.Notifications", |
| static_cast<int64_t>(PermissionAction::REVOKED), |
| 1); |
| histograms.ExpectBucketCount( |
| "Permissions.Revocation.Notifications.DidRecordUkm", 1, 1); |
| const auto entries = ukm_recorder.GetEntriesByName("Permission"); |
| ASSERT_EQ(1u, entries.size()); |
| const auto* entry = entries.back().get(); |
| EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Action"), |
| static_cast<int64_t>(PermissionAction::REVOKED)); |
| } |
| |
| TEST_F(UkmRecorderPermissionUmaUtilTest, |
| NotificationRevocationHistogramDroppedUkmTest) { |
| base::HistogramTester histograms; |
| content::TestBrowserContext browser_context; |
| ukm::InitializeSourceUrlRecorderForWebContents(web_contents()); |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| |
| permissions_client_.SetSimulatedHasSourceId(false); |
| const GURL origin(kTopLevelUrl); |
| PermissionUmaUtil::PermissionRevoked( |
| ContentSettingsType::NOTIFICATIONS, |
| permissions::PermissionSourceUI::ANDROID_SETTINGS, origin, |
| &browser_context); |
| |
| histograms.ExpectBucketCount("Permissions.Action.Notifications", |
| static_cast<int64_t>(PermissionAction::REVOKED), |
| 1); |
| |
| histograms.ExpectBucketCount( |
| "Permissions.Revocation.Notifications.DidRecordUkm", 0, 1); |
| const auto entries = ukm_recorder.GetEntriesByName("Permission"); |
| EXPECT_EQ(0u, entries.size()); |
| } |
| |
| TEST_F(UkmRecorderPermissionUmaUtilTest, |
| NotificationUsageHistogramDidRecordUkmTest) { |
| base::HistogramTester histograms; |
| content::TestBrowserContext browser_context; |
| ukm::InitializeSourceUrlRecorderForWebContents(web_contents()); |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| |
| permissions_client_.SetSimulatedHasSourceId(true); |
| PermissionUmaUtil::RecordPermissionUsage(ContentSettingsType::NOTIFICATIONS, |
| &browser_context, web_contents(), |
| GURL(kTopLevelUrl)); |
| |
| histograms.ExpectBucketCount("Permissions.Usage.Notifications.DidRecordUkm", |
| 1, 1); |
| const auto entries = ukm_recorder.GetEntriesByName("PermissionUsage"); |
| ASSERT_EQ(1u, entries.size()); |
| const auto* entry = entries.back().get(); |
| EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "PermissionType"), |
| content_settings_uma_util::ContentSettingTypeToHistogramValue( |
| ContentSettingsType::NOTIFICATIONS)); |
| } |
| |
| TEST_F(UkmRecorderPermissionUmaUtilTest, |
| NotificationUsageHistogramDroppedUkmTest) { |
| base::HistogramTester histograms; |
| content::TestBrowserContext browser_context; |
| |
| ukm::InitializeSourceUrlRecorderForWebContents(web_contents()); |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| |
| permissions_client_.SetSimulatedHasSourceId(false); |
| PermissionUmaUtil::RecordPermissionUsage(ContentSettingsType::NOTIFICATIONS, |
| &browser_context, web_contents(), |
| GURL(kTopLevelUrl)); |
| |
| histograms.ExpectBucketCount("Permissions.Usage.Notifications.DidRecordUkm", |
| 0, 1); |
| const auto entries = ukm_recorder.GetEntriesByName("PermissionUsage"); |
| ASSERT_EQ(0u, entries.size()); |
| } |
| } // namespace permissions |