blob: 3dd5adb8217243a808e191b145e2de35fd73cd0f [file] [log] [blame]
// 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 <utility>
#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_request_data.h"
#include "components/permissions/permission_request_enums.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/prediction_service/permission_ui_selector.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/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"
#include "url/gurl.h"
namespace permissions {
namespace {
using QuietUiReason = PermissionUiSelector::QuietUiReason;
}
class PermissionRequestManagerTest : public content::RenderViewHostTestHarness {
public:
PermissionRequestManagerTest()
: RenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME),
request1_(RequestType::kGeolocation,
PermissionRequestGestureType::GESTURE),
request2_(RequestType::kMultipleDownloads,
PermissionRequestGestureType::NO_GESTURE),
request_mic_(RequestType::kMicStream,
PermissionRequestGestureType::NO_GESTURE),
request_camera_(RequestType::kCameraStream,
PermissionRequestGestureType::NO_GESTURE),
#if !BUILDFLAG(IS_ANDROID)
request_ptz_(RequestType::kCameraPanTiltZoom,
PermissionRequestGestureType::NO_GESTURE),
#endif
iframe_request_same_domain_(GURL("https://www.google.com/some/url"),
RequestType::kMidiSysex),
iframe_request_other_domain_(GURL("https://www.youtube.com"),
RequestType::kClipboard),
iframe_request_camera_other_domain_(GURL("https://www.youtube.com"),
RequestType::kStorageAccess),
iframe_request_mic_other_domain_(GURL("https://www.youtube.com"),
RequestType::kMicStream) {
}
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
SetContents(CreateTestWebContents());
NavigateAndCommit(GURL(MockPermissionRequest::kDefaultOrigin));
PermissionRequestManager::CreateForWebContents(web_contents());
manager_ = PermissionRequestManager::FromWebContents(web_contents());
manager_->set_enabled_app_level_notification_permission_for_testing(true);
prompt_factory_ = std::make_unique<MockPermissionPromptFactory>(manager_);
}
void TearDown() override {
prompt_factory_ = nullptr;
manager_ = nullptr;
content::RenderViewHostTestHarness::TearDown();
}
void Accept() {
manager_->Accept();
task_environment()->RunUntilIdle();
}
void AcceptThisTime() {
manager_->AcceptThisTime();
task_environment()->RunUntilIdle();
}
void Deny() {
manager_->Deny();
task_environment()->RunUntilIdle();
}
void Closing() {
manager_->Dismiss();
task_environment()->RunUntilIdle();
}
void OpenHelpCenterLink() {
#if !BUILDFLAG(IS_ANDROID)
const ui::MouseEvent event(ui::EventType::kMousePressed, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(), 0, 0);
#else // BUILDFLAG(IS_ANDROID)
const ui::TouchEvent event(
ui::EventType::kTouchMoved, gfx::PointF(), gfx::PointF(),
ui::EventTimeForNow(),
ui::PointerDetails(ui::EventPointerType::kTouch, 1));
#endif
manager_->OpenHelpCenterLink(event);
task_environment()->RunUntilIdle();
}
void WaitForFrameLoad() {
// PermissionRequestManager ignores all parameters. Yay?
manager_->DOMContentLoaded(nullptr);
task_environment()->RunUntilIdle();
}
void WaitForBubbleToBeShown() {
manager_->DocumentOnLoadCompletedInPrimaryMainFrame();
task_environment()->RunUntilIdle();
}
void MockTabSwitchAway() {
manager_->OnVisibilityChanged(content::Visibility::HIDDEN);
}
void MockTabSwitchBack() {
manager_->OnVisibilityChanged(content::Visibility::VISIBLE);
}
virtual void NavigationEntryCommitted(
const content::LoadCommittedDetails& details) {
manager_->NavigationEntryCommitted(details);
}
std::unique_ptr<MockPermissionRequest::MockPermissionRequestState>
CreateAndAddRequest(RequestType type,
bool should_be_seen,
int expected_request_count) {
auto request_state =
std::make_unique<MockPermissionRequest::MockPermissionRequestState>();
std::unique_ptr<MockPermissionRequest> request =
std::make_unique<MockPermissionRequest>(
type, PermissionRequestGestureType::GESTURE,
request_state->GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
if (should_be_seen) {
EXPECT_TRUE(prompt_factory_->RequestTypeSeen(type));
} else {
EXPECT_FALSE(prompt_factory_->RequestTypeSeen(type));
}
EXPECT_EQ(prompt_factory_->TotalRequestCount(), expected_request_count);
return request_state;
}
void WaitAndAcceptPromptForRequest(
MockPermissionRequest::MockPermissionRequestState* request_state) {
WaitForBubbleToBeShown();
EXPECT_FALSE(request_state->finished);
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_state->granted);
}
protected:
std::unique_ptr<MockPermissionRequest> CreateRequest(
std::pair<RequestType, PermissionRequestGestureType> request_params,
base::WeakPtr<MockPermissionRequest::MockPermissionRequestState> state =
nullptr) {
return std::make_unique<permissions::MockPermissionRequest>(
request_params.first, request_params.second, state);
}
std::unique_ptr<permissions::MockPermissionRequest> CreateRequest(
std::pair<GURL, RequestType> request_params,
base::WeakPtr<MockPermissionRequest::MockPermissionRequestState> state =
nullptr) {
return std::make_unique<permissions::MockPermissionRequest>(
request_params.first, request_params.second, state);
}
std::pair<RequestType, PermissionRequestGestureType> request1_;
std::pair<RequestType, PermissionRequestGestureType> request2_;
std::pair<RequestType, PermissionRequestGestureType> request_mic_;
std::pair<RequestType, PermissionRequestGestureType> request_camera_;
#if !BUILDFLAG(IS_ANDROID)
std::pair<RequestType, PermissionRequestGestureType> request_ptz_;
#endif
std::pair<GURL, RequestType> iframe_request_same_domain_;
std::pair<GURL, RequestType> iframe_request_other_domain_;
std::pair<GURL, RequestType> iframe_request_camera_other_domain_;
std::pair<GURL, RequestType> iframe_request_mic_other_domain_;
raw_ptr<PermissionRequestManager> manager_;
std::unique_ptr<MockPermissionPromptFactory> prompt_factory_;
TestPermissionsClient client_;
base::test::ScopedFeatureList feature_list_;
};
////////////////////////////////////////////////////////////////////////////////
// General
////////////////////////////////////////////////////////////////////////////////
TEST_F(PermissionRequestManagerTest, NoRequests) {
WaitForBubbleToBeShown();
EXPECT_FALSE(prompt_factory_->is_visible());
}
TEST_F(PermissionRequestManagerTest, SingleRequest) {
MockPermissionRequest::MockPermissionRequestState request1_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
TEST_F(PermissionRequestManagerTest, SequentialRequests) {
MockPermissionRequest::MockPermissionRequestState request1_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
Accept();
EXPECT_TRUE(request1_state.granted);
EXPECT_FALSE(prompt_factory_->is_visible());
MockPermissionRequest::MockPermissionRequestState request2_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
Accept();
EXPECT_FALSE(prompt_factory_->is_visible());
EXPECT_TRUE(request2_state.granted);
}
TEST_F(PermissionRequestManagerTest, ForgetRequestsOnPageNavigation) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState
iframe_request_other_domain_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_other_domain_,
iframe_request_other_domain_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
NavigateAndCommit(GURL("http://www2.google.com/"));
WaitForBubbleToBeShown();
EXPECT_FALSE(prompt_factory_->is_visible());
EXPECT_TRUE(request1_state.finished);
EXPECT_TRUE(request2_state.finished);
EXPECT_TRUE(iframe_request_other_domain_state.finished);
}
TEST_F(PermissionRequestManagerTest, RequestsDontNeedUserGesture) {
WaitForFrameLoad();
WaitForBubbleToBeShown();
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_other_domain_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_));
task_environment()->RunUntilIdle();
EXPECT_TRUE(prompt_factory_->is_visible());
}
TEST_F(PermissionRequestManagerTest, RequestsNotSupported) {
MockPermissionRequest::MockPermissionRequestState request1_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
Accept();
EXPECT_TRUE(request1_state.granted);
manager_->set_web_contents_supports_permission_requests(false);
MockPermissionRequest::MockPermissionRequestState request2_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
EXPECT_TRUE(request2_state.cancelled);
}
////////////////////////////////////////////////////////////////////////////////
// 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) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request2_state.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, requests shouldn't be grouped.
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request2_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request2_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
// Test new permissions order by adding requests one at a time.
TEST_F(PermissionRequestManagerTest, ThreeRequestsOneByOneStackOrderChip) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
WaitForBubbleToBeShown();
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request2_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request2_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
#endif // BUILDFLAG(IS_ANDROID)
// Only mic/camera requests from the same origin should be grouped.
TEST_F(PermissionRequestManagerTest, MicCameraGrouped) {
MockPermissionRequest::MockPermissionRequestState request_mic_state;
MockPermissionRequest::MockPermissionRequestState request_camera_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_, request_camera_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 2);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_TRUE(request_camera_state.granted);
}
// If mic/camera requests come from different origins, they should not be
// grouped.
TEST_F(PermissionRequestManagerTest, MicCameraDifferentOrigins) {
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_mic_other_domain_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
}
#if !BUILDFLAG(IS_ANDROID)
// Only camera/ptz requests from the same origin should be grouped.
TEST_F(PermissionRequestManagerTest, CameraPtzGrouped) {
MockPermissionRequest::MockPermissionRequestState request_camera_state;
MockPermissionRequest::MockPermissionRequestState request_ptz_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_, request_camera_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_ptz_, request_ptz_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 2);
Accept();
EXPECT_TRUE(request_camera_state.granted);
EXPECT_TRUE(request_ptz_state.granted);
}
TEST_F(PermissionRequestManagerTest, CameraPtzDifferentOrigins) {
// If camera/ptz requests come from different origins, they should not be
// grouped.
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_camera_other_domain_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_ptz_));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
}
// Only mic/camera/ptz requests from the same origin should be grouped.
TEST_F(PermissionRequestManagerTest, MicCameraPtzGrouped) {
MockPermissionRequest::MockPermissionRequestState request_mic_state;
MockPermissionRequest::MockPermissionRequestState request_camera_state;
MockPermissionRequest::MockPermissionRequestState request_ptz_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_, request_camera_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_ptz_, request_ptz_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 3);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_TRUE(request_camera_state.granted);
EXPECT_TRUE(request_ptz_state.granted);
}
// If mic/camera/ptz requests come from different origins, they should not be
// grouped.
TEST_F(PermissionRequestManagerTest, MicCameraPtzDifferentOrigins) {
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_mic_other_domain_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_ptz_));
WaitForBubbleToBeShown();
// Requests should be split into two groups and each one will contain less
// than 3 requests (1 request + 2 request for current logic and 2 requests + 1
// request for chip).
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_LT(prompt_factory_->request_count(), 3);
Accept();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_LT(prompt_factory_->request_count(), 3);
}
#endif // !BUILDFLAG(IS_ANDROID)
// Tests mix of grouped media requests and non-groupable request.
TEST_F(PermissionRequestManagerTest, MixOfMediaAndNotMediaRequests) {
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_));
WaitForBubbleToBeShown();
// Requests should be split into two groups and each one will contain less
// than 3 requests (1 request + 2 request for current logic and 2 requests + 1
// request for chip).
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_LT(prompt_factory_->request_count(), 3);
Accept();
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_LT(prompt_factory_->request_count(), 3);
Accept();
}
TEST_F(PermissionRequestManagerTest, OpenHelpCenterLink) {
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_camera_other_domain_));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
OpenHelpCenterLink();
SUCCEED();
}
TEST_F(PermissionRequestManagerTest, OpenHelpCenterLink_RequestNotSupported) {
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_DEATH_IF_SUPPORTED(OpenHelpCenterLink(), "");
}
////////////////////////////////////////////////////////////////////////////////
// Tab switching
////////////////////////////////////////////////////////////////////////////////
#if BUILDFLAG(IS_ANDROID)
TEST_F(PermissionRequestManagerTest, TwoRequestsTabSwitch) {
MockPermissionRequest::MockPermissionRequestState request_mic_state;
MockPermissionRequest::MockPermissionRequestState request_camera_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_, request_camera_state.GetWeakPtr()));
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_state.granted);
EXPECT_TRUE(request_camera_state.granted);
}
#endif // BUILDFLAG(IS_ANDROID)
TEST_F(PermissionRequestManagerTest, PermissionRequestWhileTabSwitchedAway) {
MockTabSwitchAway();
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_));
WaitForBubbleToBeShown();
EXPECT_FALSE(prompt_factory_->is_visible());
MockTabSwitchBack();
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
}
////////////////////////////////////////////////////////////////////////////////
// Duplicated requests
////////////////////////////////////////////////////////////////////////////////
TEST_F(PermissionRequestManagerTest, SameRequestRejected) {
auto request1_state = CreateAndAddRequest(request1_.first, true, 1);
auto dupe_request1_state = CreateAndAddRequest(request1_.first, true, 1);
EXPECT_FALSE(request1_state->finished);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Accept();
task_environment()->RunUntilIdle();
EXPECT_TRUE(request1_state->granted);
EXPECT_FALSE(prompt_factory_->is_visible());
}
class QuicklyDeletedRequest : public PermissionRequest {
public:
QuicklyDeletedRequest(const GURL& requesting_origin,
RequestType request_type,
PermissionRequestGestureType gesture_type)
: PermissionRequest(
std::make_unique<PermissionRequestData>(
std::make_unique<ContentSettingPermissionResolver>(
request_type),
/*user_gesture=*/gesture_type ==
PermissionRequestGestureType::GESTURE,
requesting_origin),
base::BindLambdaForTesting(
[](PermissionDecision decision,
bool is_final_decision,
const PermissionRequestData&) { NOTREACHED(); })) {}
static std::unique_ptr<QuicklyDeletedRequest> CreateRequest(
MockPermissionRequest* request) {
return std::make_unique<QuicklyDeletedRequest>(request->requesting_origin(),
request->request_type(),
request->GetGestureType());
}
};
TEST_F(PermissionRequestManagerTest, DuplicateRequest) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request1_dupe_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState request2_dupe_state;
auto request1 = std::make_unique<MockPermissionRequest>(
request1_.first, PermissionRequestGestureType::GESTURE,
request1_state.GetWeakPtr());
auto request1_dupe =
request1->CreateDuplicateRequest(request1_dupe_state.GetWeakPtr());
auto request2 = std::make_unique<MockPermissionRequest>(
request2_.first, PermissionRequestGestureType::GESTURE,
request2_state.GetWeakPtr());
auto request2_dupe =
request2->CreateDuplicateRequest(request2_dupe_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request2));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1_dupe));
EXPECT_FALSE(request1_dupe_state.finished);
EXPECT_FALSE(request1_state.finished);
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request2_dupe));
EXPECT_FALSE(request2_dupe_state.finished);
EXPECT_FALSE(request2_state.finished);
WaitForBubbleToBeShown();
Accept();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(request2_dupe_state.finished);
EXPECT_TRUE(request2_state.finished);
} else {
EXPECT_TRUE(request1_dupe_state.finished);
EXPECT_TRUE(request1_state.finished);
}
WaitForBubbleToBeShown();
Accept();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(request1_dupe_state.finished);
EXPECT_TRUE(request1_state.finished);
} else {
EXPECT_TRUE(request2_dupe_state.finished);
EXPECT_TRUE(request2_state.finished);
}
}
////////////////////////////////////////////////////////////////////////////////
// Requests from iframes
////////////////////////////////////////////////////////////////////////////////
TEST_F(PermissionRequestManagerTest, MainFrameNoRequestIFrameRequest) {
MockPermissionRequest::MockPermissionRequestState
iframe_request_same_domain_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_same_domain_,
iframe_request_same_domain_state.GetWeakPtr()));
WaitForBubbleToBeShown();
WaitForFrameLoad();
EXPECT_TRUE(prompt_factory_->is_visible());
Closing();
EXPECT_TRUE(iframe_request_same_domain_state.finished);
}
TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestSameDomain) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState
iframe_request_same_domain_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_same_domain_,
iframe_request_same_domain_state.GetWeakPtr()));
WaitForFrameLoad();
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(1, prompt_factory_->request_count());
Closing();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(iframe_request_same_domain_state.finished);
EXPECT_FALSE(request1_state.finished);
} else {
EXPECT_TRUE(request1_state.finished);
EXPECT_FALSE(iframe_request_same_domain_state.finished);
}
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(1, prompt_factory_->request_count());
Closing();
EXPECT_FALSE(prompt_factory_->is_visible());
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(request1_state.finished);
} else {
EXPECT_TRUE(iframe_request_same_domain_state.finished);
}
}
TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestOtherDomain) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState
iframe_request_other_domain_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_other_domain_,
iframe_request_other_domain_state.GetWeakPtr()));
WaitForFrameLoad();
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
Closing();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(iframe_request_other_domain_state.finished);
EXPECT_FALSE(request1_state.finished);
} else {
EXPECT_TRUE(request1_state.finished);
EXPECT_FALSE(iframe_request_other_domain_state.finished);
}
EXPECT_TRUE(prompt_factory_->is_visible());
Closing();
EXPECT_TRUE(iframe_request_other_domain_state.finished);
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(request1_state.finished);
} else {
EXPECT_TRUE(iframe_request_other_domain_state.finished);
}
}
TEST_F(PermissionRequestManagerTest, IFrameRequestWhenMainRequestVisible) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState
iframe_request_other_domain_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_same_domain_,
iframe_request_other_domain_state.GetWeakPtr()));
WaitForFrameLoad();
ASSERT_EQ(prompt_factory_->request_count(), 1);
Closing();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(iframe_request_other_domain_state.finished);
EXPECT_FALSE(request1_state.finished);
} else {
EXPECT_TRUE(request1_state.finished);
EXPECT_FALSE(iframe_request_other_domain_state.finished);
}
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Closing();
EXPECT_TRUE(iframe_request_other_domain_state.finished);
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(request1_state.finished);
} else {
EXPECT_TRUE(iframe_request_other_domain_state.finished);
}
}
TEST_F(PermissionRequestManagerTest,
IFrameRequestOtherDomainWhenMainRequestVisible) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState
iframe_request_other_domain_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(iframe_request_other_domain_,
iframe_request_other_domain_state.GetWeakPtr()));
WaitForFrameLoad();
Closing();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(iframe_request_other_domain_state.finished);
EXPECT_FALSE(request1_state.finished);
} else {
EXPECT_TRUE(request1_state.finished);
EXPECT_FALSE(iframe_request_other_domain_state.finished);
}
EXPECT_TRUE(prompt_factory_->is_visible());
Closing();
if (PermissionUtil::DoesPlatformSupportChip()) {
EXPECT_TRUE(request1_state.finished);
} else {
EXPECT_TRUE(iframe_request_other_domain_state.finished);
}
}
////////////////////////////////////////////////////////////////////////////////
// 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) {
base::HistogramTester histograms;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_));
WaitForBubbleToBeShown();
// No need to test UMA for showing prompts again, they were tested in
// UMAForSimpleAcceptedBubble.
Deny();
histograms.ExpectUniqueSample(PermissionUmaUtil::kPermissionsPromptDenied,
static_cast<base::HistogramBase::Sample32>(
RequestTypeForUma::PERMISSION_GEOLOCATION),
1);
}
TEST_F(PermissionRequestManagerTest, UMAForTabSwitching) {
base::HistogramTester histograms;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_));
WaitForBubbleToBeShown();
histograms.ExpectUniqueSample(PermissionUmaUtil::kPermissionsPromptShown,
static_cast<base::HistogramBase::Sample32>(
RequestTypeForUma::PERMISSION_GEOLOCATION),
1);
MockTabSwitchAway();
MockTabSwitchBack();
histograms.ExpectUniqueSample(PermissionUmaUtil::kPermissionsPromptShown,
static_cast<base::HistogramBase::Sample32>(
RequestTypeForUma::PERMISSION_GEOLOCATION),
1);
}
////////////////////////////////////////////////////////////////////////////////
// UI selectors
////////////////////////////////////////////////////////////////////////////////
// Simulate a PermissionUiSelector that simply returns a predefined |ui_to_use|
// every time.
class MockNotificationPermissionUiSelector : public PermissionUiSelector {
public:
explicit MockNotificationPermissionUiSelector(
std::optional<QuietUiReason> quiet_ui_reason,
std::optional<PermissionUiSelector::PredictionGrantLikelihood>
prediction_likelihood,
std::optional<base::TimeDelta> async_delay)
: quiet_ui_reason_(quiet_ui_reason),
prediction_likelihood_(prediction_likelihood),
async_delay_(async_delay) {}
void SelectUiToUse(content::WebContents* web_contents,
PermissionRequest* request,
DecisionMadeCallback callback) override {
selected_ui_to_use_ = true;
Decision decision(quiet_ui_reason_, Decision::ShowNoWarning());
if (async_delay_) {
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, base::BindOnce(std::move(callback), decision),
async_delay_.value());
} else {
std::move(callback).Run(decision);
}
}
bool IsPermissionRequestSupported(RequestType request_type) override {
return request_type == RequestType::kNotifications ||
request_type == RequestType::kGeolocation;
}
std::optional<PermissionUiSelector::PredictionGrantLikelihood>
PredictedGrantLikelihoodForUKM() override {
return prediction_likelihood_;
}
static void CreateForManager(
PermissionRequestManager* manager,
std::optional<QuietUiReason> quiet_ui_reason,
std::optional<base::TimeDelta> async_delay,
std::optional<PermissionUiSelector::PredictionGrantLikelihood>
prediction_likelihood = std::nullopt) {
manager->add_permission_ui_selector_for_testing(
std::make_unique<MockNotificationPermissionUiSelector>(
quiet_ui_reason, prediction_likelihood, async_delay));
}
bool selected_ui_to_use() const { return selected_ui_to_use_; }
private:
std::optional<QuietUiReason> quiet_ui_reason_;
std::optional<PermissionUiSelector::PredictionGrantLikelihood>
prediction_likelihood_;
std::optional<base::TimeDelta> async_delay_;
bool selected_ui_to_use_ = false;
};
// Same as the MockNotificationPermissionUiSelector but handling only the
// Camera stream request type
class MockCameraStreamPermissionUiSelector
: public MockNotificationPermissionUiSelector {
public:
explicit MockCameraStreamPermissionUiSelector(
std::optional<QuietUiReason> quiet_ui_reason,
std::optional<PermissionUiSelector::PredictionGrantLikelihood>
prediction_likelihood,
std::optional<base::TimeDelta> async_delay)
: MockNotificationPermissionUiSelector(quiet_ui_reason,
prediction_likelihood,
async_delay) {}
bool IsPermissionRequestSupported(RequestType request_type) override {
return request_type == RequestType::kCameraStream;
}
static void CreateForManager(
PermissionRequestManager* manager,
std::optional<QuietUiReason> quiet_ui_reason,
std::optional<base::TimeDelta> async_delay,
std::optional<PermissionUiSelector::PredictionGrantLikelihood>
prediction_likelihood = std::nullopt) {
manager->add_permission_ui_selector_for_testing(
std::make_unique<MockCameraStreamPermissionUiSelector>(
quiet_ui_reason, prediction_likelihood, async_delay));
}
};
TEST_F(PermissionRequestManagerTest,
UiSelectorNotUsedForPermissionsOtherThanNotification) {
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, PermissionUiSelector::QuietUiReason::kEnabledInPrefs,
std::nullopt /* async_delay */);
MockPermissionRequest::MockPermissionRequestState request_camera_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_camera_, request_camera_state.GetWeakPtr()));
WaitForBubbleToBeShown();
ASSERT_TRUE(prompt_factory_->is_visible());
ASSERT_TRUE(
prompt_factory_->RequestTypeSeen(request_camera_state.request_type));
EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
EXPECT_TRUE(request_camera_state.granted);
}
TEST_F(PermissionRequestManagerTest, UiSelectorUsedForNotifications) {
const struct {
std::optional<PermissionUiSelector::QuietUiReason> quiet_ui_reason;
std::optional<base::TimeDelta> async_delay;
} kTests[] = {
{QuietUiReason::kEnabledInPrefs, std::make_optional<base::TimeDelta>()},
{PermissionUiSelector::Decision::UseNormalUi(),
std::make_optional<base::TimeDelta>()},
{QuietUiReason::kEnabledInPrefs, std::nullopt},
{PermissionUiSelector::Decision::UseNormalUi(), std::nullopt},
};
for (const auto& test : kTests) {
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, test.quiet_ui_reason, test.async_delay);
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_TRUE(prompt_factory_->RequestTypeSeen(request_state.request_type));
EXPECT_EQ(!!test.quiet_ui_reason,
manager_->ShouldCurrentRequestUseQuietUI());
Accept();
EXPECT_TRUE(request_state.granted);
}
}
TEST_F(PermissionRequestManagerTest,
UiSelectionHappensSeparatelyForEachRequest) {
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, QuietUiReason::kEnabledInPrefs,
std::make_optional<base::TimeDelta>());
auto request1 = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE);
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
auto request2 = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE);
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, PermissionUiSelector::Decision::UseNormalUi(),
std::make_optional<base::TimeDelta>());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request2));
WaitForBubbleToBeShown();
EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
}
TEST_F(PermissionRequestManagerTest, SkipNextUiSelector) {
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, QuietUiReason::kEnabledInPrefs,
/* async_delay */ std::nullopt);
MockNotificationPermissionUiSelector::CreateForManager(
manager_, PermissionUiSelector::Decision::UseNormalUi(),
/* async_delay */ std::nullopt);
auto request1 = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE);
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
auto* next_selector =
manager_->get_permission_ui_selectors_for_testing().back().get();
EXPECT_FALSE(static_cast<MockNotificationPermissionUiSelector*>(next_selector)
->selected_ui_to_use());
EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
}
TEST_F(PermissionRequestManagerTest, MultipleUiSelectors) {
const struct {
std::vector<std::optional<QuietUiReason>> quiet_ui_reasons;
std::vector<bool> simulate_delayed_decision;
std::optional<QuietUiReason> expected_reason;
} kTests[] = {
// Simple sync selectors, first one should take priority.
{{QuietUiReason::kTriggeredByCrowdDeny, QuietUiReason::kEnabledInPrefs},
{false, false},
QuietUiReason::kTriggeredByCrowdDeny},
{{QuietUiReason::kTriggeredDueToDisruptiveBehavior,
QuietUiReason::kEnabledInPrefs},
{false, false},
QuietUiReason::kTriggeredDueToDisruptiveBehavior},
{{QuietUiReason::kTriggeredDueToDisruptiveBehavior,
QuietUiReason::kServicePredictedVeryUnlikelyGrant},
{false, false},
QuietUiReason::kTriggeredDueToDisruptiveBehavior},
{{QuietUiReason::kTriggeredDueToDisruptiveBehavior,
QuietUiReason::kTriggeredByCrowdDeny},
{false, false},
QuietUiReason::kTriggeredDueToDisruptiveBehavior},
// First selector is async but should still take priority even if it
// returns later.
{{QuietUiReason::kTriggeredByCrowdDeny, QuietUiReason::kEnabledInPrefs},
{true, false},
QuietUiReason::kTriggeredByCrowdDeny},
{{QuietUiReason::kTriggeredDueToDisruptiveBehavior,
QuietUiReason::kEnabledInPrefs},
{true, false},
QuietUiReason::kTriggeredDueToDisruptiveBehavior},
// The first selector that has a quiet ui decision should be used.
{{std::nullopt, std::nullopt,
QuietUiReason::kTriggeredDueToAbusiveContent,
QuietUiReason::kEnabledInPrefs},
{false, true, true, false},
QuietUiReason::kTriggeredDueToAbusiveContent},
// If all selectors return a normal ui, it should use a normal ui.
{{std::nullopt, std::nullopt}, {false, true}, std::nullopt},
// Use a bunch of selectors both async and sync.
{{std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
QuietUiReason::kTriggeredDueToAbusiveRequests, std::nullopt,
QuietUiReason::kEnabledInPrefs},
{false, true, false, true, true, true, false, false},
QuietUiReason::kTriggeredDueToAbusiveRequests},
// Use a bunch of selectors all sync.
{{std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
QuietUiReason::kTriggeredDueToAbusiveRequests, std::nullopt,
QuietUiReason::kEnabledInPrefs},
{false, false, false, false, false, false, false, false},
QuietUiReason::kTriggeredDueToAbusiveRequests},
// Use a bunch of selectors all async.
{{std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
QuietUiReason::kTriggeredDueToAbusiveRequests, std::nullopt,
QuietUiReason::kEnabledInPrefs},
{true, true, true, true, true, true, true, true},
QuietUiReason::kTriggeredDueToAbusiveRequests},
// Use a bunch of selectors both async and sync.
{{std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
QuietUiReason::kTriggeredDueToDisruptiveBehavior, std::nullopt,
QuietUiReason::kEnabledInPrefs},
{true, false, false, true, true, true, false, false},
QuietUiReason::kTriggeredDueToDisruptiveBehavior},
};
for (const auto& test : kTests) {
manager_->clear_permission_ui_selector_for_testing();
for (size_t i = 0; i < test.quiet_ui_reasons.size(); ++i) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_, test.quiet_ui_reasons[i],
test.simulate_delayed_decision[i]
? std::make_optional<base::TimeDelta>()
: std::nullopt);
}
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_TRUE(prompt_factory_->RequestTypeSeen(request_state.request_type));
if (test.expected_reason.has_value()) {
EXPECT_EQ(test.expected_reason, manager_->ReasonForUsingQuietUi());
} else {
EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
}
Accept();
EXPECT_TRUE(request_state.granted);
}
}
TEST_F(PermissionRequestManagerTest, SelectorsPredictionLikelihood) {
using PredictionLikelihood = PermissionUiSelector::PredictionGrantLikelihood;
const auto VeryLikely = PredictionLikelihood::
PermissionPrediction_Likelihood_DiscretizedLikelihood_VERY_LIKELY;
const auto Neutral = PredictionLikelihood::
PermissionPrediction_Likelihood_DiscretizedLikelihood_NEUTRAL;
const struct {
std::vector<bool> enable_quiet_uis;
std::vector<std::optional<PredictionLikelihood>> prediction_likelihoods;
std::optional<PredictionLikelihood> expected_prediction_likelihood;
} kTests[] = {
// Sanity check: prediction likelihood is populated correctly.
{{true}, {VeryLikely}, VeryLikely},
{{false}, {Neutral}, Neutral},
// Prediction likelihood is populated only if the selector was considered.
{{true, true}, {std::nullopt, VeryLikely}, std::nullopt},
{{false, true}, {std::nullopt, VeryLikely}, VeryLikely},
{{false, false}, {std::nullopt, VeryLikely}, VeryLikely},
// First considered selector is preserved.
{{true, true}, {Neutral, VeryLikely}, Neutral},
{{false, true}, {Neutral, VeryLikely}, Neutral},
{{false, false}, {Neutral, VeryLikely}, Neutral},
};
for (const auto& test : kTests) {
manager_->clear_permission_ui_selector_for_testing();
for (size_t i = 0; i < test.enable_quiet_uis.size(); ++i) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_,
test.enable_quiet_uis[i]
? std::optional<QuietUiReason>(QuietUiReason::kEnabledInPrefs)
: std::nullopt,
std::nullopt /* async_delay */, test.prediction_likelihoods[i]);
}
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_TRUE(prompt_factory_->RequestTypeSeen(request_state.request_type));
EXPECT_EQ(test.expected_prediction_likelihood,
manager_->prediction_grant_likelihood_for_testing());
Accept();
EXPECT_TRUE(request_state.granted);
}
}
TEST_F(PermissionRequestManagerTest, SelectorRequestTypes) {
const struct {
RequestType request_type;
bool should_request_use_quiet_ui;
} kTests[] = {
{RequestType::kNotifications, true},
{RequestType::kGeolocation, true},
{RequestType::kCameraStream, false},
};
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, QuietUiReason::kEnabledInPrefs,
std::make_optional<base::TimeDelta>());
for (const auto& test : kTests) {
auto request = std::make_unique<MockPermissionRequest>(
test.request_type, PermissionRequestGestureType::GESTURE);
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_EQ(test.should_request_use_quiet_ui,
manager_->ShouldCurrentRequestUseQuietUI());
Accept();
}
// Adding a mock PermissionUiSelector that handles Camera stream.
MockCameraStreamPermissionUiSelector::CreateForManager(
manager_, QuietUiReason::kEnabledInPrefs,
std::make_optional<base::TimeDelta>());
// Now the RequestType::kCameraStream should show a quiet UI as well
auto request2 = std::make_unique<MockPermissionRequest>(
RequestType::kCameraStream, PermissionRequestGestureType::GESTURE);
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request2));
WaitForBubbleToBeShown();
EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
}
////////////////////////////////////////////////////////////////////////////////
// Quiet UI chip. Low priority for Notifications & Geolocation.
////////////////////////////////////////////////////////////////////////////////
TEST_F(PermissionRequestManagerTest, NotificationsSingleBubbleAndChipRequest) {
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
ASSERT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_state.granted);
EXPECT_EQ(prompt_factory_->show_count(), 1);
}
// 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) {
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/true, 1);
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/false, 1);
auto request_camera = CreateAndAddRequest(RequestType::kCameraStream,
/*should_be_seen=*/false, 1);
WaitAndAcceptPromptForRequest(request_notifications.get());
WaitAndAcceptPromptForRequest(request_geolocation.get());
WaitAndAcceptPromptForRequest(request_camera.get());
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) {
auto request_camera = CreateAndAddRequest(RequestType::kCameraStream,
/*should_be_seen=*/true, 1);
auto request_clipboard =
CreateAndAddRequest(RequestType::kClipboard, /*should_be_seen=*/true, 2);
auto request_midi =
CreateAndAddRequest(RequestType::kMidiSysex, /*should_be_seen=*/true, 3);
WaitAndAcceptPromptForRequest(request_midi.get());
WaitAndAcceptPromptForRequest(request_clipboard.get());
WaitAndAcceptPromptForRequest(request_camera.get());
EXPECT_EQ(prompt_factory_->show_count(), 5);
}
// 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) {
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, QuietUiReason::kTriggeredDueToAbusiveRequests,
std::make_optional<base::TimeDelta>(base::Seconds(2)));
MockPermissionRequest::MockPermissionRequestState request1_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
task_environment()->FastForwardBy(base::Seconds(1));
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kClipboard, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
WaitForBubbleToBeShown();
manager_->clear_permission_ui_selector_for_testing();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
// 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) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_,
PermissionUiSelector::QuietUiReason::kTriggeredDueToAbusiveRequests,
std::nullopt /* async_delay */);
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/true, 1);
// Less then 8.5 seconds.
manager_->set_current_request_first_display_time_for_testing(
base::Time::Now() - base::Milliseconds(5000));
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/true, 2);
WaitAndAcceptPromptForRequest(request_geolocation.get());
WaitAndAcceptPromptForRequest(request_notifications.get());
EXPECT_EQ(prompt_factory_->show_count(), 3);
}
// 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) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_,
PermissionUiSelector::QuietUiReason::kTriggeredDueToAbusiveRequests,
std::nullopt /* async_delay */);
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/true, 1);
// More then 8.5 seconds.
manager_->set_current_request_first_display_time_for_testing(
base::Time::Now() - base::Milliseconds(9000));
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/true, 2);
// The second permission was requested after 8.5 second window, the quiet UI
// Notifiations request for an abusive origin is automatically ignored.
EXPECT_FALSE(request_notifications->granted);
EXPECT_TRUE(request_notifications->finished);
WaitAndAcceptPromptForRequest(request_geolocation.get());
EXPECT_EQ(prompt_factory_->show_count(), 2);
}
// 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) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_,
PermissionUiSelector::QuietUiReason::kTriggeredDueToAbusiveRequests,
std::nullopt /* async_delay */);
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/true, 1);
// Less then 8.5 seconds.
manager_->set_current_request_first_display_time_for_testing(
base::Time::Now() - base::Milliseconds(5000));
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/true, 2);
auto request_camera = CreateAndAddRequest(RequestType::kCameraStream,
/*should_be_seen=*/true, 3);
// The second permission was requested in 8.5 second window, the quiet UI
// Notifiations request for an abusive origin is not automatically ignored.
EXPECT_FALSE(request_notifications->granted);
EXPECT_FALSE(request_notifications->finished);
WaitAndAcceptPromptForRequest(request_camera.get());
WaitAndAcceptPromptForRequest(request_geolocation.get());
WaitAndAcceptPromptForRequest(request_notifications.get());
EXPECT_EQ(prompt_factory_->show_count(), 5);
}
// 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) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_,
PermissionUiSelector::QuietUiReason::kTriggeredDueToAbusiveRequests,
std::nullopt /* async_delay */);
auto request_camera = CreateAndAddRequest(RequestType::kCameraStream,
/*should_be_seen=*/true, 1);
// Quiet UI is not shown because Camera has higher priority.
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/false, 1);
// Less then 8.5 seconds.
manager_->set_current_request_first_display_time_for_testing(
base::Time::Now() - base::Milliseconds(5000));
// Geolocation is not shown because Camera has higher priority.
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/false, 1);
// The second permission after quiet UI was requested in 8.5 second window,
// the quiet UI Notifiations request for an abusive origin is not
// automatically ignored.
EXPECT_FALSE(request_notifications->granted);
EXPECT_FALSE(request_notifications->finished);
WaitAndAcceptPromptForRequest(request_camera.get());
WaitAndAcceptPromptForRequest(request_geolocation.get());
WaitAndAcceptPromptForRequest(request_notifications.get());
EXPECT_EQ(prompt_factory_->show_count(), 3);
}
// 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) {
MockNotificationPermissionUiSelector::CreateForManager(
manager_,
PermissionUiSelector::QuietUiReason::kTriggeredDueToAbusiveRequests,
std::nullopt /* async_delay */);
auto request_camera = CreateAndAddRequest(RequestType::kCameraStream,
/*should_be_seen=*/true, 1);
// Quiet UI is not shown because Camera has higher priority.
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/false, 1);
// Less then 8.5 seconds.
manager_->set_current_request_first_display_time_for_testing(
base::Time::Now() - base::Milliseconds(5000));
// Geolocation is not shown because Camera has higher priority.
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/false, 1);
// Since the chip is enabled, MIDI should be shown.
auto request_midi = CreateAndAddRequest(RequestType::kMidiSysex,
/*should_be_seen=*/true, 2);
// The second permission after quiet UI was requested in 8.5 second window,
// the quiet UI Notifiations request for an abusive origin is not
// automatically ignored.
EXPECT_FALSE(request_notifications->granted);
EXPECT_FALSE(request_notifications->finished);
WaitAndAcceptPromptForRequest(request_midi.get());
WaitAndAcceptPromptForRequest(request_camera.get());
WaitAndAcceptPromptForRequest(request_geolocation.get());
WaitAndAcceptPromptForRequest(request_notifications.get());
EXPECT_EQ(prompt_factory_->show_count(), 5);
}
// 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) {
auto request_camera = CreateAndAddRequest(RequestType::kCameraStream,
/*should_be_seen=*/true, 1);
// Quiet UI is not shown because Camera has higher priority.
auto request_notifications = CreateAndAddRequest(RequestType::kNotifications,
/*should_be_seen=*/false, 1);
// Less then 8.5 seconds.
manager_->set_current_request_first_display_time_for_testing(
base::Time::Now() - base::Milliseconds(5000));
// Geolocation is not shown because Camera has higher priority.
auto request_geolocation = CreateAndAddRequest(RequestType::kGeolocation,
/*should_be_seen=*/false, 1);
// If Chip is enabled, MIDI should be shown, otherwise MIDI should not be
// shown.
auto request_midi = CreateAndAddRequest(RequestType::kMidiSysex,
/*should_be_seen=*/true, 2);
// The second permission after quiet UI was requested in 8.5 second window,
// the quiet UI Notifiations request for an abusive origin is not
// automatically ignored.
EXPECT_FALSE(request_notifications->granted);
EXPECT_FALSE(request_notifications->finished);
WaitAndAcceptPromptForRequest(request_midi.get());
WaitAndAcceptPromptForRequest(request_camera.get());
WaitAndAcceptPromptForRequest(request_geolocation.get());
WaitAndAcceptPromptForRequest(request_notifications.get());
EXPECT_EQ(prompt_factory_->show_count(), 5);
}
// Verifies that a high-priority request cannot preempt a low-priority request
// if the high-priority request comes in as the result of a permission prompt
// being accepted.
// Permissions requested in order:
// 1. Gelocation (low)
// 2. Mic (high)
TEST_F(PermissionRequestManagerTest, ReentrantPermissionRequestAccept) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
auto request1 = CreateRequest(request1_, request1_state.GetWeakPtr());
request1->RegisterOnPermissionDecidedCallback(
base::BindLambdaForTesting([&]() {
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
}));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
EXPECT_FALSE(request_mic_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
}
// Verifies that a high-priority request cannot preempt a low-priority request
// if the high-priority request comes in as the result of a permission prompt
// being accepted once.
// Permissions requested in order:
// 1. Gelocation (low)
// 2. Mic (high)
TEST_F(PermissionRequestManagerTest, ReentrantPermissionRequestAcceptOnce) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
auto request1 = CreateRequest(request1_, request1_state.GetWeakPtr());
request1->RegisterOnPermissionDecidedCallback(
base::BindLambdaForTesting([&]() {
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
}));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
AcceptThisTime();
EXPECT_TRUE(request1_state.granted);
EXPECT_FALSE(request_mic_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
}
// Verifies that a high-priority request cannot preempt a low-priority request
// if the high-priority request comes in as the result of a permission prompt
// being denied.
// Permissions requested in order:
// 1. Gelocation (low)
// 2. Mic (high)
TEST_F(PermissionRequestManagerTest, ReentrantPermissionRequestDeny) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
auto request1 = CreateRequest(request1_, request1_state.GetWeakPtr());
request1->RegisterOnPermissionDecidedCallback(
base::BindLambdaForTesting([&]() {
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
}));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Deny();
EXPECT_FALSE(request1_state.granted);
EXPECT_FALSE(request_mic_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_FALSE(request1_state.granted);
EXPECT_TRUE(request_mic_state.granted);
}
// Verifies that a high-priority request cannot preempt a low-priority request
// if the high-priority request comes in as the result of a permission prompt
// being dismissed.
// Permissions requested in order:
// 1. Gelocation (low)
// 2. Mic (high)
TEST_F(PermissionRequestManagerTest, ReentrantPermissionRequestCancelled) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
auto request1 = CreateRequest(request1_, request1_state.GetWeakPtr());
request1->RegisterOnPermissionDecidedCallback(
base::BindLambdaForTesting([&]() {
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
}));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request1));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Closing();
EXPECT_TRUE(request1_state.cancelled);
EXPECT_FALSE(request_mic_state.cancelled);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_FALSE(request1_state.granted);
EXPECT_TRUE(request_mic_state.granted);
}
// 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) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
WaitForBubbleToBeShown();
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request2_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request2_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
// 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) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
WaitForBubbleToBeShown();
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
WaitForBubbleToBeShown();
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
// 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) {
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
// 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) {
MockPermissionRequest::MockPermissionRequestState request_midi_state;
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
MockPermissionRequest::MockPermissionRequestState request_notification_state;
auto request_midi = std::make_unique<MockPermissionRequest>(
RequestType::kMidiSysex, PermissionRequestGestureType::GESTURE,
request_midi_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request_midi));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
auto request_notification = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_notification_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request_notification));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_FALSE(request_mic_state.granted);
EXPECT_TRUE(request2_state.granted);
EXPECT_FALSE(request_notification_state.granted);
EXPECT_FALSE(request1_state.granted);
EXPECT_FALSE(request_midi_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request_notification_state.granted);
EXPECT_FALSE(request1_state.granted);
EXPECT_FALSE(request_midi_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_FALSE(request_notification_state.granted);
EXPECT_FALSE(request1_state.granted);
EXPECT_TRUE(request_midi_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_notification_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
// 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) {
MockPermissionRequest::MockPermissionRequestState request_state;
MockPermissionRequest::MockPermissionRequestState request1_state;
MockPermissionRequest::MockPermissionRequestState request2_state;
MockPermissionRequest::MockPermissionRequestState request_mic_state;
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request1_, request1_state.GetWeakPtr()));
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
manager_->AddRequest(
web_contents()->GetPrimaryMainFrame(),
CreateRequest(request_mic_, request_mic_state.GetWeakPtr()));
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
CreateRequest(request2_, request2_state.GetWeakPtr()));
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request2_state.granted);
EXPECT_FALSE(request_mic_state.granted);
EXPECT_FALSE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_mic_state.granted);
EXPECT_FALSE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request_state.granted);
EXPECT_FALSE(request1_state.granted);
WaitForBubbleToBeShown();
EXPECT_TRUE(prompt_factory_->is_visible());
EXPECT_EQ(prompt_factory_->request_count(), 1);
Accept();
EXPECT_TRUE(request1_state.granted);
}
TEST_F(PermissionRequestManagerTest, PEPCRequestNeverQuiet) {
manager_->clear_permission_ui_selector_for_testing();
MockNotificationPermissionUiSelector::CreateForManager(
manager_, PermissionUiSelector::QuietUiReason::kEnabledInPrefs,
std::nullopt /* async_delay */);
// PEPC request is not quieted by selector.
MockPermissionRequest::MockPermissionRequestState pepc_request_state;
auto pepc_request = std::make_unique<MockPermissionRequest>(
GURL(MockPermissionRequest::kDefaultOrigin), RequestType::kNotifications,
/*embedded_permission_element_initiated=*/true,
pepc_request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(pepc_request));
WaitForBubbleToBeShown();
ASSERT_TRUE(prompt_factory_->is_visible());
ASSERT_TRUE(
prompt_factory_->RequestTypeSeen(pepc_request_state.request_type));
EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
// Regular request is quieted by selector.
MockPermissionRequest::MockPermissionRequestState request_state;
auto request = std::make_unique<MockPermissionRequest>(
RequestType::kNotifications, PermissionRequestGestureType::GESTURE,
request_state.GetWeakPtr());
manager_->AddRequest(web_contents()->GetPrimaryMainFrame(),
std::move(request));
WaitForBubbleToBeShown();
ASSERT_TRUE(prompt_factory_->is_visible());
ASSERT_TRUE(prompt_factory_->RequestTypeSeen(request_state.request_type));
EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
Accept();
}
#endif // BUILDFLAG(IS_ANDROID)
} // namespace permissions