// 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_request_manager.h" #include <stddef.h> #include <memory> #include <optional> #include <string> #include "base/command_line.h" #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/task/sequenced_task_runner.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "components/permissions/features.h" #include "components/permissions/permission_request.h" #include "components/permissions/permission_ui_selector.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" #include "components/permissions/request_type.h" #include "components/permissions/test/mock_permission_prompt_factory.h" #include "components/permissions/test/mock_permission_request.h" #include "components/permissions/test/test_permissions_client.h" #include "content/public/test/test_renderer_host.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/base_event_utils.h" #include "ui/events/event.h" namespace permissions { namespace { QuietUiReason; } class PermissionRequestManagerTest : public content::RenderViewHostTestHarness { … }; //////////////////////////////////////////////////////////////////////////////// // General //////////////////////////////////////////////////////////////////////////////// TEST_F(PermissionRequestManagerTest, NoRequests) { … } TEST_F(PermissionRequestManagerTest, SingleRequest) { … } TEST_F(PermissionRequestManagerTest, SequentialRequests) { … } TEST_F(PermissionRequestManagerTest, ForgetRequestsOnPageNavigation) { … } TEST_F(PermissionRequestManagerTest, RequestsDontNeedUserGesture) { … } TEST_F(PermissionRequestManagerTest, RequestsNotSupported) { … } //////////////////////////////////////////////////////////////////////////////// // Requests grouping //////////////////////////////////////////////////////////////////////////////// // Android is the only platform that does not support the permission chip. #if BUILDFLAG(IS_ANDROID) // Most requests should never be grouped. // Grouping for chip feature is tested in ThreeRequestsStackOrderChip. TEST_F(PermissionRequestManagerTest, TwoRequestsUngrouped) { manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request1_); manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request2_); WaitForBubbleToBeShown(); EXPECT_TRUE(prompt_factory_->is_visible()); ASSERT_EQ(prompt_factory_->request_count(), 1); Accept(); EXPECT_TRUE(request1_.granted()); WaitForBubbleToBeShown(); EXPECT_TRUE(prompt_factory_->is_visible()); ASSERT_EQ(prompt_factory_->request_count(), 1); Accept(); EXPECT_TRUE(request2_.granted()); ASSERT_EQ(prompt_factory_->show_count(), 2); } // Tests for non-Android platforms which support the permission chip. #else // BUILDFLAG(IS_ANDROID) TEST_F(PermissionRequestManagerTest, ThreeRequestsStackOrderChip) { … } // Test new permissions order by adding requests one at a time. TEST_F(PermissionRequestManagerTest, ThreeRequestsOneByOneStackOrderChip) { … } #endif // BUILDFLAG(IS_ANDROID) // Only mic/camera requests from the same origin should be grouped. TEST_F(PermissionRequestManagerTest, MicCameraGrouped) { … } // If mic/camera requests come from different origins, they should not be // grouped. TEST_F(PermissionRequestManagerTest, MicCameraDifferentOrigins) { … } #if !BUILDFLAG(IS_ANDROID) // Only camera/ptz requests from the same origin should be grouped. TEST_F(PermissionRequestManagerTest, CameraPtzGrouped) { … } TEST_F(PermissionRequestManagerTest, CameraPtzDifferentOrigins) { … } // Only mic/camera/ptz requests from the same origin should be grouped. TEST_F(PermissionRequestManagerTest, MicCameraPtzGrouped) { … } // If mic/camera/ptz requests come from different origins, they should not be // grouped. TEST_F(PermissionRequestManagerTest, MicCameraPtzDifferentOrigins) { … } #endif // !BUILDFLAG(IS_ANDROID) // Tests mix of grouped media requests and non-groupable request. TEST_F(PermissionRequestManagerTest, MixOfMediaAndNotMediaRequests) { … } TEST_F(PermissionRequestManagerTest, OpenHelpCenterLink) { … } TEST_F(PermissionRequestManagerTest, OpenHelpCenterLink_RequestNotSupported) { … } //////////////////////////////////////////////////////////////////////////////// // Tab switching //////////////////////////////////////////////////////////////////////////////// #if BUILDFLAG(IS_ANDROID) TEST_F(PermissionRequestManagerTest, TwoRequestsTabSwitch) { manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_mic_); manager_->AddRequest(web_contents()->GetPrimaryMainFrame(), &request_camera_); WaitForBubbleToBeShown(); EXPECT_TRUE(prompt_factory_->is_visible()); ASSERT_EQ(prompt_factory_->request_count(), 2); MockTabSwitchAway(); EXPECT_TRUE(prompt_factory_->is_visible()); MockTabSwitchBack(); WaitForBubbleToBeShown(); EXPECT_TRUE(prompt_factory_->is_visible()); ASSERT_EQ(prompt_factory_->request_count(), 2); Accept(); EXPECT_TRUE(request_mic_.granted()); EXPECT_TRUE(request_camera_.granted()); } #endif // BUILDFLAG(IS_ANDROID) TEST_F(PermissionRequestManagerTest, PermissionRequestWhileTabSwitchedAway) { … } //////////////////////////////////////////////////////////////////////////////// // Duplicated requests //////////////////////////////////////////////////////////////////////////////// TEST_F(PermissionRequestManagerTest, SameRequestRejected) { … } TEST_F(PermissionRequestManagerTest, WeakDuplicateRequests) { … } class QuicklyDeletedRequest : public PermissionRequest { … }; TEST_F(PermissionRequestManagerTest, WeakDuplicateRequestsAccept) { … } TEST_F(PermissionRequestManagerTest, DuplicateRequest) { … } //////////////////////////////////////////////////////////////////////////////// // Requests from iframes //////////////////////////////////////////////////////////////////////////////// TEST_F(PermissionRequestManagerTest, MainFrameNoRequestIFrameRequest) { … } TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestSameDomain) { … } TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestOtherDomain) { … } TEST_F(PermissionRequestManagerTest, IFrameRequestWhenMainRequestVisible) { … } TEST_F(PermissionRequestManagerTest, IFrameRequestOtherDomainWhenMainRequestVisible) { … } //////////////////////////////////////////////////////////////////////////////// // UMA logging //////////////////////////////////////////////////////////////////////////////// // This code path (calling Accept on a non-merged bubble, with no accepted // permission) would never be used in actual Chrome, but its still tested for // completeness. TEST_F(PermissionRequestManagerTest, UMAForSimpleDeniedBubbleAlternatePath) { … } TEST_F(PermissionRequestManagerTest, UMAForTabSwitching) { … } //////////////////////////////////////////////////////////////////////////////// // UI selectors //////////////////////////////////////////////////////////////////////////////// // Simulate a PermissionUiSelector that simply returns a predefined |ui_to_use| // every time. class MockNotificationPermissionUiSelector : public PermissionUiSelector { … }; // Same as the MockNotificationPermissionUiSelector but handling only the // Camera stream request type class MockCameraStreamPermissionUiSelector : public MockNotificationPermissionUiSelector { … }; TEST_F(PermissionRequestManagerTest, UiSelectorNotUsedForPermissionsOtherThanNotification) { … } TEST_F(PermissionRequestManagerTest, UiSelectorUsedForNotifications) { … } TEST_F(PermissionRequestManagerTest, UiSelectionHappensSeparatelyForEachRequest) { … } TEST_F(PermissionRequestManagerTest, SkipNextUiSelector) { … } TEST_F(PermissionRequestManagerTest, MultipleUiSelectors) { … } TEST_F(PermissionRequestManagerTest, SelectorsPredictionLikelihood) { … } TEST_F(PermissionRequestManagerTest, SelectorRequestTypes) { … } //////////////////////////////////////////////////////////////////////////////// // Quiet UI chip. Low priority for Notifications & Geolocation. //////////////////////////////////////////////////////////////////////////////// TEST_F(PermissionRequestManagerTest, NotificationsSingleBubbleAndChipRequest) { … } // Android is the only platform that does not support the permission chip. #if BUILDFLAG(IS_ANDROID) // Quiet UI feature is disabled. Chip is disabled. No low priority requests, the // first request is always shown. // // Permissions requested in order: // 1. Notification (non abusive) // 2. Geolocation // 3. Camera // // Prompt display order: // 1. Notification request shown // 2. Geolocation request shown // 3. Camera request shown TEST_F(PermissionRequestManagerTest, NotificationsGeolocationCameraBubbleRequest) { std::unique_ptr<MockPermissionRequest> request_notifications = CreateAndAddRequest(RequestType::kNotifications, /*should_be_seen=*/true, 1); std::unique_ptr<MockPermissionRequest> request_geolocation = CreateAndAddRequest(RequestType::kGeolocation, /*should_be_seen=*/false, 1); std::unique_ptr<MockPermissionRequest> request_camera = CreateAndAddRequest( RequestType::kCameraStream, /*should_be_seen=*/false, 1); for (auto* kRequest : {request_notifications.get(), request_geolocation.get(), request_camera.get()}) { WaitAndAcceptPromptForRequest(kRequest); } EXPECT_EQ(prompt_factory_->show_count(), 3); } // Tests for non-Android platforms which support the permission chip. #else // BUILDFLAG(IS_ANDROID) // Quiet UI feature is disabled, no low priority requests, the last request is // always shown. // // Permissions requested in order: // 1. Camera // 2. Clipboard // 3. MIDI // // Prompt display order: // 1. Camera request shown but is preempted // 2. Clipboard request shown but is preempted // 3. MIDI request shown // 4. Clipboard request shown again // 5. Camera request shown again TEST_F(PermissionRequestManagerTest, CameraNotificationsGeolocationChipRequest) { … } // Verifies order of simultaneous requests, with quiet chip enabled. // Simultaneous new requests are coming while we are waiting for UI selector // decisions. // // Permissions requested in order: // 1. Geolocation, UI selector takes 2 seconds to decide. // 2. Notification then mic. Notification will preempt geolocation // // Prompt display order: // 1. Mic // 2. Clipboard // 3. Geolocation TEST_F(PermissionRequestManagerTest, NewHighPriorityRequestDuringUIDecision) { … } // Verifies that the quiet UI chip is not ignored if another request came in // less than 8.5 seconds after. // Permissions requested in order: // 1. Notification (abusive) // 2. After less than 8.5 seconds Geolocation // // Prompt display order: // 1. Notifications request shown but is preempted because of quiet UI. // 2. Geolocation request shown // 3. Notifications request shown again TEST_F(PermissionRequestManagerTest, AbusiveNotificationsGeolocationQuietUIChipRequest) { … } // Verifies that the quiet UI chip is ignored if another request came in more // than 8.5 seconds after. // // Permissions requested in order: // 1. Notification (abusive) // 2. After more than 8.5 seconds Geolocation // // Prompt display order: // 1. Notifications request shown but is preempted because of quiet UI. // 2. Geolocation request shown TEST_F(PermissionRequestManagerTest, AbusiveNotificationsShownLongEnough) { … } // Verifies that the quiet UI chip is not ignored if another request came in // more than 8.5 seconds after. Verify different requests priority. Camera // request is shown despite being requested last. // // Permissions requested in order: // 1. Notification (abusive) // 2. After less than 8.5 seconds Geolocation // 3. Camera // // Prompt display order: // 1. Notifications request shown but is preempted because of quiet UI. // 2. Geolocation request shown but is preempted because of low priority. // 3. Camera request shown // 4. Geolocation request shown again // 5. Notifications quiet UI request shown again TEST_F(PermissionRequestManagerTest, AbusiveNotificationsShownLongEnoughCamera) { … } // Verifies that the quiet UI chip is not ignored if another request came in // more than 8.5 seconds after. Verify different requests priority. Camera // request is not preemted. // // Permissions requested in order: // 1. Camera // 2. Notification (abusive) // 3. After less than 8.5 seconds Geolocation // // Prompt display order: // 1. Camera request shown // 2. Geolocation request shown // 3. Camera request shown TEST_F(PermissionRequestManagerTest, CameraAbusiveNotificationsGeolocation) { … } // Verifies that the quiet UI chip is not ignored if another request came in // more than 8.5 seconds after. Verify different requests priority. Camera // request is not preemted. // // Permissions requested in order: // 1. Camera // 2. Notification (abusive) // 3. After less than 8.5 seconds Geolocation // 4. MIDI // // Prompt display order: // 1. Camera request shown // 2. MIDI request shown (or MIDI and then Camera, the order depends on // `PermissionUtil::DoesPlatformSupportChip()`) // 3. Geolocation request shown // 4. Notifications request shown // If Chip is enabled MIDI will replace Camera, hence 5 prompts will be // shown. Otherwise 4. TEST_F(PermissionRequestManagerTest, CameraAbusiveNotificationsGeolocationMIDI) { … } // Verifies that non abusive chip behaves similar to others when Quiet UI Chip // is enabled. // // Permissions requested in order: // 1. Camera // 2. Notification (non abusive) // 3. After less than 8.5 seconds Geolocation // 4. MIDI // // Prompt display order: // 1. Camera request shown // 2. MIDI request shown (or MIDI and then Camera, the order depends on // `PermissionUtil::DoesPlatformSupportChip()`) // 3. Geolocation request shown // 4. Notifications request shown // If Chip is enabled MIDI will replace Camera, hence 5 prompts will be // shown. Otherwise 4. TEST_F(PermissionRequestManagerTest, CameraNonAbusiveNotificationsGeolocationMIDI) { … } // Verifies order of requests with mixed low-high priority requests input, with // both chip and quiet chip enabled. New permissions are added and accepted one // by one. // // Permissions requested in order: // 1. Multiple Download (high) // 2. Geolocation (low) // 3. Mic (high) // // Prompt display order: // 1. Mic // 2. Multiple Download // 3. Geolocation TEST_F(PermissionRequestManagerTest, Mixed1Low2HighPriorityRequests) { … } // Verifies order of requests with mixed low-high priority requests input, with // both chip and quiet chip enabled. New permissions are added and accepted one // by one. // // Permissions requested in order: // 1. Geolocation (low) // 2. Mic (high) // 3. Notification (low) // // Prompt display order: // 1. Mic // 2. Notification // 3. Geolocation TEST_F(PermissionRequestManagerTest, Mixed2Low1HighRequests) { … } // Verifies order of requests with mixed low-high priority requests input, added // simultaneously, with both chip and quiet chip enabled. // // Permissions requested in order: // 1. Geolocation (low) // 2. Mic (high) // 3. Notification (low) // // Prompt display order: // 1. Mic // 2. Notification // 3. Geolocation TEST_F(PermissionRequestManagerTest, MultipleSimultaneous2Low1HighRequests) { … } // Verifies order of requests with mixed low-high priority requests input, // added simultaneously, with both chip and quiet chip enabled. // // Permissions requested in order: // 1. MIDI (high) // 2. Geolocation (low) // 3. Mic (high) // 4. Notification (low) // 5. Multiple Download (high) // // Prompt display order: // 1. Multiple Download // 2. Mic // 3. Midi // 4. Notification // 5. Geolocation TEST_F(PermissionRequestManagerTest, MultipleSimultaneous2Low3HighRequests) { … } // Verifies order of requests with mixed low-high priority requests input, added // simultaneously several times, with both chip and quiet chip enabled. // // Permissions requested in order: // 1. Geolocation(low) then Notification(low) // 2. Mic (high) then multiple downloads (high) // Prompt display order: // 1. Multiple Download // 2. Mic // 3. Notification // 4. Geolocation TEST_F(PermissionRequestManagerTest, MultipleSimultaneous2Low2HighRequests) { … } TEST_F(PermissionRequestManagerTest, PEPCRequestNeverQuiet) { … } #endif // BUILDFLAG(IS_ANDROID) } // namespace permissions