910e62b5创建于 1月15日历史提交
// 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