blob: 3c46ff67343bbebdc7ebd64970f71df256a88ba2 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/content_settings/one_time_permission_provider.h"
#include <memory>
#include <optional>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/permissions/one_time_permissions_tracker.h"
#include "chrome/browser/permissions/one_time_permissions_tracker_observer.h"
#include "components/content_settings/core/browser/content_settings_mock_observer.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/permission_settings_registry.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_constraints.h"
#include "components/content_settings/core/common/features.h"
#include "components/content_settings/core/test/content_settings_test_utils.h"
#include "components/permissions/content_setting_permission_context_base.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using ::testing::_;
namespace content_settings {
class OneTimePermissionProviderTest : public testing::Test {
public:
OneTimePermissionProviderTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
// Ensure all content settings are initialized.
ContentSettingsRegistry::GetInstance();
}
void SetUp() override {
tracker_ = std::make_unique<OneTimePermissionsTracker>();
one_time_permission_provider_ =
std::make_unique<OneTimePermissionProvider>(tracker_.get());
}
void TearDown() override {
one_time_permission_provider_->ShutdownOnUIThread();
one_time_permission_provider_
.reset(); // required because destructor may destroy tracker_ first
}
void FastForwardTime(base::TimeDelta delta) {
task_environment_.FastForwardBy(delta);
}
protected:
content_settings::ContentSettingConstraints one_time_constraints() {
content_settings::ContentSettingConstraints constraints;
constraints.set_session_model(
content_settings::mojom::SessionModel::ONE_TIME);
return constraints;
}
GURL primary_url = GURL("http://example.com/");
ContentSettingsPattern primary_pattern =
ContentSettingsPattern::FromURLNoWildcard(primary_url);
GURL other_url = GURL("http://other.com");
ContentSettingsPattern other_pattern =
ContentSettingsPattern::FromURLNoWildcard(other_url);
GURL secondary_url = GURL("*");
std::unique_ptr<OneTimePermissionProvider> one_time_permission_provider_;
private:
content::BrowserTaskEnvironment task_environment_;
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<OneTimePermissionsTracker> tracker_;
};
TEST_F(OneTimePermissionProviderTest, SetAndGetContentSetting) {
base::HistogramTester histograms;
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
EXPECT_EQ(CONTENT_SETTING_ALLOW,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
histograms.ExpectUniqueSample(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
}
TEST_F(OneTimePermissionProviderTest, SetAndGetGeolocationSetting) {
base::HistogramTester histograms;
auto* info = PermissionSettingsRegistry::GetInstance()->Get(
mojom::ContentSettingsType::GEOLOCATION_WITH_OPTIONS);
EXPECT_EQ(std::nullopt,
TestUtils::GetPermissionSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
GeolocationSetting allow_setting{PermissionOption::kAllowed,
PermissionOption::kAllowed};
// Set setting.
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION_WITH_OPTIONS,
info->delegate().ToValue(allow_setting), one_time_constraints());
auto setting = TestUtils::GetPermissionSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION_WITH_OPTIONS, false);
ASSERT_TRUE(setting.has_value());
EXPECT_EQ(PermissionSetting(allow_setting), *setting);
histograms.ExpectUniqueSample(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION_WITH_OPTIONS),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
// Reset setting.
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION_WITH_OPTIONS, base::Value(),
one_time_constraints());
EXPECT_EQ(std::nullopt,
TestUtils::GetPermissionSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
}
TEST_F(OneTimePermissionProviderTest,
SetAndGetContentSettingWithoutOneTimeCapabilityDoesNotAllow) {
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::NOTIFICATIONS, false));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::NOTIFICATIONS, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::NOTIFICATIONS, false));
}
TEST_F(OneTimePermissionProviderTest,
SetAndGetContentSettingWithoutOneTimeConstraintsDoeNotAllow) {
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(CONTENT_SETTING_ALLOW), {});
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
}
TEST_F(OneTimePermissionProviderTest,
AllTabsInBackgroundExpiryRevokesGeolocation) {
base::HistogramTester histograms;
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
one_time_permission_provider_->SetWebsiteSetting(
other_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
one_time_permission_provider_->OnAllTabsInBackgroundTimerExpired(
url::Origin::Create(primary_url),
OneTimePermissionsTrackerObserver::BackgroundExpiryType::kTimeout);
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), other_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
// We granted to two distinct origins
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
2);
// Only one origin was in the background and should have been expired
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::EXPIRED_IN_BACKGROUND),
1);
}
TEST_F(OneTimePermissionProviderTest, CaptureExpiryRevokesPermissions) {
base::HistogramTester histograms;
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::MEDIASTREAM_CAMERA,
base::Value(CONTENT_SETTING_ALLOW), one_time_constraints());
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::MEDIASTREAM_MIC, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
one_time_permission_provider_->OnCapturingVideoExpired(
url::Origin::Create(primary_url));
one_time_permission_provider_->OnCapturingAudioExpired(
url::Origin::Create(primary_url));
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::MEDIASTREAM_CAMERA, false));
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), other_url, secondary_url,
ContentSettingsType::MEDIASTREAM_MIC, false));
histograms.ExpectTotalCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_CAMERA),
2);
histograms.ExpectTotalCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_MIC),
2);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_CAMERA),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_CAMERA),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::EXPIRED_IN_BACKGROUND),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_MIC),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_MIC),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::EXPIRED_IN_BACKGROUND),
1);
}
TEST_F(OneTimePermissionProviderTest,
AllTabsInBackgroundExpiryDoesNotRevokeCamMic) {
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::MEDIASTREAM_CAMERA, false));
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::MEDIASTREAM_MIC, false));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::MEDIASTREAM_CAMERA,
base::Value(CONTENT_SETTING_ALLOW), one_time_constraints());
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::MEDIASTREAM_MIC, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
one_time_permission_provider_->OnAllTabsInBackgroundTimerExpired(
url::Origin::Create(primary_url),
OneTimePermissionsTrackerObserver::BackgroundExpiryType::kTimeout);
EXPECT_EQ(CONTENT_SETTING_ALLOW,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::MEDIASTREAM_CAMERA, false));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::MEDIASTREAM_MIC, false));
}
TEST_F(OneTimePermissionProviderTest, ManualRevocationUmaTest) {
base::HistogramTester histograms;
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::GEOLOCATION, false));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(), one_time_constraints());
histograms.ExpectTotalCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION),
2);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::GEOLOCATION),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::REVOKED_MANUALLY),
1);
}
TEST_F(OneTimePermissionProviderTest, VerifyPermissionObserversNotified) {
base::HistogramTester histograms;
content_settings::MockObserver mock_observer;
one_time_permission_provider_->AddObserver(&mock_observer);
EXPECT_CALL(mock_observer,
OnContentSettingChanged(_, _, ContentSettingsType::GEOLOCATION));
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::GEOLOCATION, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
}
class OneTimePermissionProviderExpiryTest
: public OneTimePermissionProviderTest,
public ::testing::WithParamInterface<bool> {
public:
OneTimePermissionProviderExpiryTest() {
if (GetParam()) {
feature_list_.InitWithFeatures(
{content_settings::features::kActiveContentSettingExpiry}, {});
} else {
feature_list_.InitWithFeatures(
{}, {content_settings::features::kActiveContentSettingExpiry});
}
}
OneTimePermissionProviderExpiryTest(
const OneTimePermissionProviderExpiryTest&) = delete;
OneTimePermissionProviderExpiryTest& operator=(
const OneTimePermissionProviderExpiryTest&) = delete;
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(OneTimePermissionProviderTest, SuspendExpiresAllGrants) {
base::HistogramTester histograms;
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::MEDIASTREAM_CAMERA,
base::Value(CONTENT_SETTING_ALLOW), one_time_constraints());
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::MEDIASTREAM_MIC, base::Value(CONTENT_SETTING_ALLOW),
one_time_constraints());
one_time_permission_provider_->OnSuspend();
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, secondary_url,
ContentSettingsType::MEDIASTREAM_CAMERA, false));
EXPECT_EQ(CONTENT_SETTING_DEFAULT,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), other_url, secondary_url,
ContentSettingsType::MEDIASTREAM_MIC, false));
histograms.ExpectTotalCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_CAMERA),
2);
histograms.ExpectTotalCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_MIC),
2);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_CAMERA),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_CAMERA),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::EXPIRED_ON_SUSPEND),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_MIC),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::GRANTED_ONE_TIME),
1);
histograms.ExpectBucketCount(
permissions::PermissionUmaUtil::GetOneTimePermissionEventHistogram(
ContentSettingsType::MEDIASTREAM_MIC),
static_cast<base::HistogramBase::Sample32>(
permissions::OneTimePermissionEvent::EXPIRED_ON_SUSPEND),
1);
}
INSTANTIATE_TEST_SUITE_P(All,
OneTimePermissionProviderExpiryTest,
testing::Bool());
TEST_P(OneTimePermissionProviderExpiryTest, RenewContentSetting_Noop) {
GURL primary_url("https://example.com/");
ContentSettingsPattern primary_pattern =
ContentSettingsPattern::FromString("https://[*.]example.com");
ContentSettingConstraints constraints = one_time_constraints();
if (GetParam()) {
constraints.set_lifetime(permissions::kOneTimePermissionMaximumLifetime);
} else {
constraints.set_lifetime(base::Days(2));
}
one_time_permission_provider_->SetWebsiteSetting(
primary_pattern, primary_pattern, ContentSettingsType::GEOLOCATION,
base::Value(CONTENT_SETTING_ALLOW), constraints);
RuleMetaData metadata;
EXPECT_EQ(CONTENT_SETTING_ALLOW,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, primary_url,
ContentSettingsType::GEOLOCATION,
/*include_incognito=*/false, &metadata));
if (GetParam()) {
EXPECT_EQ(metadata.lifetime(),
permissions::kOneTimePermissionMaximumLifetime);
EXPECT_NE(metadata.expiration(), base::Time());
}
// The lifetime given by `constraints` is ignored.
base::Time original_expiration = metadata.expiration();
EXPECT_FALSE(one_time_permission_provider_->RenewContentSetting(
primary_url, primary_url, ContentSettingsType::GEOLOCATION,
std::nullopt));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
TestUtils::GetContentSetting(
one_time_permission_provider_.get(), primary_url, primary_url,
ContentSettingsType::GEOLOCATION,
/*include_incognito=*/false, &metadata));
if (GetParam()) {
EXPECT_EQ(metadata.lifetime(),
permissions::kOneTimePermissionMaximumLifetime);
EXPECT_EQ(original_expiration, metadata.expiration());
}
}
} // namespace content_settings