blob: 7f2c06c5722e107c3e943a9ad2fb6986fdb55121 [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/feature_list.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/with_feature_override.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/permissions/permission_request_manager_test_api.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/request_type.h"
#include "components/permissions/resolvers/permission_prompt_options.h"
#include "components/permissions/test/permission_request_observer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "media/base/media_switches.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
class OneTimePermissionsBrowserTestBase : public InProcessBrowserTest {
public:
OneTimePermissionsBrowserTestBase() = default;
~OneTimePermissionsBrowserTestBase() override = default;
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
https_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_server_->Start());
manager_ = permissions::PermissionRequestManager::FromWebContents(
browser()->tab_strip_model()->GetActiveWebContents());
test_api_ =
std::make_unique<test::PermissionRequestManagerTestApi>(manager_);
}
void TearDownOnMainThread() override {
manager_ = nullptr;
InProcessBrowserTest::TearDownOnMainThread();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
// This is needed to auto-accept the media stream permission requests.
command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
command_line->AppendSwitch("use-fake-ui-for-media-stream");
}
void RequestPermissionAnAcceptThisTime(
permissions::RequestType request_type) {
permissions::PermissionRequestObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
test_api_->AddSimpleRequest(browser()
->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame(),
request_type);
observer.Wait();
PromptOptions prompt_options =
(request_type == permissions::RequestType::kGeolocation &&
base::FeatureList::IsEnabled(
content_settings::features::kApproximateGeolocationPermission))
? PromptOptions(GeolocationPromptOptions{
.selected_accuracy = GeolocationAccuracy::kPrecise})
: std::monostate();
manager_->AcceptThisTime(prompt_options);
}
GURL GetTestURL() {
return https_server_->GetURL("a.test", "/permissions/requests.html");
}
private:
std::unique_ptr<net::EmbeddedTestServer> https_server_;
std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_;
raw_ptr<permissions::PermissionRequestManager> manager_;
};
class OneTimePermissionsBrowserTest : public base::test::WithFeatureOverride,
public OneTimePermissionsBrowserTestBase {
public:
OneTimePermissionsBrowserTest()
: base::test::WithFeatureOverride(
content_settings::features::kApproximateGeolocationPermission) {}
};
INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(OneTimePermissionsBrowserTest);
IN_PROC_BROWSER_TEST_P(OneTimePermissionsBrowserTest, RecordOneTimeGrant) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestURL()));
base::HistogramTester histogram_tester;
// Geolocation
RequestPermissionAnAcceptThisTime(permissions::RequestType::kGeolocation);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.OneTimeGrant", 1, 1);
RequestPermissionAnAcceptThisTime(permissions::RequestType::kGeolocation);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.Geolocation.OneTimeGrant", 2, 1);
// Mic
RequestPermissionAnAcceptThisTime(permissions::RequestType::kMicStream);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.AudioCapture.OneTimeGrant", 1, 1);
RequestPermissionAnAcceptThisTime(permissions::RequestType::kMicStream);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.AudioCapture.OneTimeGrant", 2, 1);
// Camera
RequestPermissionAnAcceptThisTime(permissions::RequestType::kCameraStream);
histogram_tester.ExpectBucketCount(
"Permissions.OneTimePermission.VideoCapture.OneTimeGrant", 1, 1);
}
struct OneTimePermissionActionTestParams {
permissions::PermissionAction action;
std::string histogram_suffix;
permissions::RequestType request_type;
std::string permission_type_string;
bool approximate_geolocation_enabled = false;
};
class OneTimePermissionActionBrowserTest
: public OneTimePermissionsBrowserTestBase,
public testing::WithParamInterface<OneTimePermissionActionTestParams> {
public:
OneTimePermissionActionBrowserTest() {
scoped_feature_list_.InitWithFeatureState(
content_settings::features::kApproximateGeolocationPermission,
GetParam().approximate_geolocation_enabled);
}
std::string GetOneTimePermissionActionHistogramName() {
return "Permissions.OneTimePermission." +
GetParam().permission_type_string + "." +
GetParam().histogram_suffix;
}
std::string GetOneTimeGrantHistogramName() {
return "Permissions.OneTimePermission." +
GetParam().permission_type_string + ".OneTimeGrant";
}
void ExecuteAction(permissions::PermissionRequestManager* manager) {
PromptOptions prompt_options =
(GetParam().request_type == permissions::RequestType::kGeolocation &&
base::FeatureList::IsEnabled(
content_settings::features::kApproximateGeolocationPermission))
? PromptOptions(GeolocationPromptOptions{
.selected_accuracy = GeolocationAccuracy::kPrecise})
: std::monostate();
switch (GetParam().action) {
case permissions::PermissionAction::GRANTED:
manager->Accept(prompt_options);
break;
case permissions::PermissionAction::DENIED:
manager->Deny(prompt_options);
break;
case permissions::PermissionAction::DISMISSED:
manager->Dismiss(prompt_options);
break;
case permissions::PermissionAction::IGNORED:
manager->Ignore(prompt_options);
break;
default:
NOTREACHED();
}
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(OneTimePermissionActionBrowserTest, RecordOTPCount) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestURL()));
base::HistogramTester histogram_tester;
auto* manager = permissions::PermissionRequestManager::FromWebContents(
browser()->tab_strip_model()->GetActiveWebContents());
test::PermissionRequestManagerTestApi test_api(manager);
RequestPermissionAnAcceptThisTime(GetParam().request_type);
RequestPermissionAnAcceptThisTime(GetParam().request_type);
// I.e. Permissions.OneTimePermission.Geolocation.OneTimeGrant
histogram_tester.ExpectBucketCount(GetOneTimeGrantHistogramName(), 1, 1);
histogram_tester.ExpectBucketCount(GetOneTimeGrantHistogramName(), 2, 1);
// Trigger the permanent action (Grant/Deny/Dismiss/Ignore)
permissions::PermissionRequestObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
test_api.AddSimpleRequest(browser()
->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame(),
GetParam().request_type);
observer.Wait();
ExecuteAction(manager);
// Expect correct histogram count, i.e. for
// Permissions.OneTimePermission.Geolocation.GrantOTPCount
histogram_tester.ExpectBucketCount(GetOneTimePermissionActionHistogramName(),
2, 1);
}
INSTANTIATE_TEST_SUITE_P(
All,
OneTimePermissionActionBrowserTest,
testing::Values(
// Geolocation
OneTimePermissionActionTestParams{
permissions::PermissionAction::GRANTED, "GrantOTPCount",
permissions::RequestType::kGeolocation, "Geolocation"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DENIED, "DenyOTPCount",
permissions::RequestType::kGeolocation, "Geolocation"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DISMISSED, "DismissOTPCount",
permissions::RequestType::kGeolocation, "Geolocation"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::IGNORED, "IgnoreOTPCount",
permissions::RequestType::kGeolocation, "Geolocation"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::GRANTED, "GrantOTPCount",
permissions::RequestType::kGeolocation, "Geolocation", true},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DENIED, "DenyOTPCount",
permissions::RequestType::kGeolocation, "Geolocation", true},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DISMISSED, "DismissOTPCount",
permissions::RequestType::kGeolocation, "Geolocation", true},
OneTimePermissionActionTestParams{
permissions::PermissionAction::IGNORED, "IgnoreOTPCount",
permissions::RequestType::kGeolocation, "Geolocation", true},
// Mic
OneTimePermissionActionTestParams{
permissions::PermissionAction::GRANTED, "GrantOTPCount",
permissions::RequestType::kMicStream, "AudioCapture"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DENIED, "DenyOTPCount",
permissions::RequestType::kMicStream, "AudioCapture"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DISMISSED, "DismissOTPCount",
permissions::RequestType::kMicStream, "AudioCapture"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::IGNORED, "IgnoreOTPCount",
permissions::RequestType::kMicStream, "AudioCapture"},
// Camera
OneTimePermissionActionTestParams{
permissions::PermissionAction::GRANTED, "GrantOTPCount",
permissions::RequestType::kCameraStream, "VideoCapture"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DENIED, "DenyOTPCount",
permissions::RequestType::kCameraStream, "VideoCapture"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::DISMISSED, "DismissOTPCount",
permissions::RequestType::kCameraStream, "VideoCapture"},
OneTimePermissionActionTestParams{
permissions::PermissionAction::IGNORED, "IgnoreOTPCount",
permissions::RequestType::kCameraStream, "VideoCapture"}),
[](const testing::TestParamInfo<OneTimePermissionActionTestParams>& info) {
std::string action_str;
switch (info.param.action) {
case permissions::PermissionAction::GRANTED:
action_str = "Granted";
break;
case permissions::PermissionAction::DENIED:
action_str = "Denied";
break;
case permissions::PermissionAction::DISMISSED:
action_str = "Dismissed";
break;
case permissions::PermissionAction::IGNORED:
action_str = "Ignored";
break;
default:
action_str = "Unknown";
}
return info.param.permission_type_string + "_" + action_str +
(info.param.approximate_geolocation_enabled
? "_withApproximateLocation"
: "");
});