910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/network/cookie_settings.h"

#include <optional>
#include <tuple>
#include <utility>

#include "base/functional/callback_helpers.h"
#include "base/rand_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_metadata.h"
#include "components/content_settings/core/common/features.h"
#include "net/base/features.h"
#include "net/base/network_delegate.h"
#include "net/base/schemeful_site.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/canonical_cookie_test_helpers.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_setting_override.h"
#include "net/cookies/cookie_util.h"
#include "net/cookies/site_for_cookies.h"
#include "net/cookies/static_cookie_policy.h"
#include "net/first_party_sets/first_party_set_metadata.h"
#include "services/network/public/cpp/features.h"
#include "services/network/tpcd/metadata/manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
#include "url/url_util.h"

namespace network {
namespace {

using testing::_;
using testing::AllOf;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Not;
using testing::UnorderedElementsAre;

constexpr char kAllowedRequestsHistogram[] =
    "API.StorageAccess.AllowedRequests4.Subsampled";
constexpr char kAllowedByStorageAccessTypeHistogram[] =
    "API.EffectiveStorageAccess.AllowedByStorageAccessType.Subsampled";

constexpr char kDomainURL[] = "http://example.com";
constexpr char kURL[] = "http://foo.com";
constexpr char kSecureSchemedURL[] = "https://foo.com";
constexpr char kOtherURL[] = "http://other.com";
constexpr char kSubDomainURL[] = "http://www.corp.example.com";
constexpr char kDomain[] = "example.com";
constexpr char kDotDomain[] = ".example.com";
constexpr char kSubDomain[] = "www.corp.example.com";
constexpr char kOtherDomain[] = "not-example.com";
constexpr char kDomainWildcardPattern[] = "[*.]example.com";
constexpr char kRwsOwnerURL[] = "https://rws-owner.test";
constexpr char kRwsMemberURL[] = "https://rws-member.test";
constexpr char kUnrelatedURL[] = "http://unrelated.com";
constexpr char kChromeScheme[] = "chrome";
constexpr char kChromeSchemedURL[] = "chrome://new-tab-page/";
constexpr char kUmaCookieName[] = "umaCookieName";
constexpr char kCookieName[] = "name";

std::unique_ptr<net::CanonicalCookie> MakeCanonicalCookie(
    const std::string& name,
    const std::string& domain,
    std::optional<net::CookiePartitionKey> cookie_partition_key =
        std::nullopt) {
  return net::CanonicalCookie::CreateUnsafeCookieForTesting(
      name, "1", domain, /*path=*/"/", /*creation=*/base::Time(),
      /*expiration=*/base::Time(), /*last_access=*/base::Time(),
      /*last_update=*/base::Time(),
      /*secure=*/true, /*httponly=*/false, net::CookieSameSite::UNSPECIFIED,
      net::CookiePriority::COOKIE_PRIORITY_DEFAULT, cookie_partition_key);
}

std::unique_ptr<net::CanonicalCookie> MakeCanonicalSameSiteNoneCookie(
    const std::string& name,
    const std::string& domain,
    std::optional<net::CookiePartitionKey> cookie_partition_key =
        std::nullopt) {
  return net::CanonicalCookie::CreateUnsafeCookieForTesting(
      name, "1", domain, /*path=*/"/", /*creation=*/base::Time(),
      /*expiration=*/base::Time(), /*last_access=*/base::Time(),
      /*last_update=*/base::Time(),
      /*secure=*/true, /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
      net::CookiePriority::COOKIE_PRIORITY_DEFAULT, cookie_partition_key);
}

// NOTE: Consider modifying
// /components/content_settings/core/browser/cookie_settings_unittest.cc if
// applicable.

// To avoid an explosion of test cases, please don't just add a boolean to
// the test features. Consider whether features can interact with each other and
// whether you really need all combinations.

// Controls features that can unblock 3p cookies.
enum GrantSource {
  // Not eligible for additional grants.
  kNoneGranted,
  // Eligible for StorageAccess grants via API usage.
  kStorageAccessGrantsEligibleViaAPI,
  // Can use Storage Access permission grants via an HTTP response header.
  kStorageAccessGrantsEligibleViaHeader,
  // Eligible for TopLevelStorageAccess grants.
  kTopLevelStorageAccessGrantEligible,

  kGrantSourceCount
};

// Features that can block 3p cookies.
enum BlockSource {
  // 3PCD is not enabled. But the test case can still block 3pc.
  kNoneBlocked,
  // Tracking protection enabled by default.
  kTrackingProtectionEnabledFor3pcd,
  // Third-party cookie blocking is enabled through a flag.
  kForceThirdPartyCookieBlockingFlagEnabled,

  kBlockSourceCount

};

enum TestVariables {
  kGrantSource,
  kBlockSource,
};

class CookieSettingsTestBase : public testing::Test {
 public:
  CookieSettingsTestBase()
      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

  ContentSettingPatternSource CreateSetting(
      const std::string& primary_pattern,
      const std::string& secondary_pattern,
      ContentSetting setting,
      base::Time expiration = base::Time(),
      content_settings::ProviderType source =
          content_settings::ProviderType::kNone,
      bool off_the_record = false) {
    content_settings::RuleMetaData metadata;
    metadata.SetExpirationAndLifetime(
        expiration, expiration.is_null() ? base::TimeDelta()
                                         : expiration - base::Time::Now());
    return ContentSettingPatternSource(
        ContentSettingsPattern::FromString(primary_pattern),
        ContentSettingsPattern::FromString(secondary_pattern),
        base::Value(setting), source, off_the_record, std::move(metadata));
  }

  void FastForwardTime(base::TimeDelta delta) {
    task_environment_.FastForwardBy(delta);
  }

 protected:
  base::test::ScopedFeatureList feature_list_;

 private:
  base::test::TaskEnvironment task_environment_;
  base::MetricsSubSampler::ScopedAlwaysSampleForTesting always_sample_;
};

// Default test class to be used by most tests. If you want to add a new
// parameter, consider whether all test cases actually require this parameter
// or whether it is sufficient to add a new subclass of CookieSettingsTestBase.
class CookieSettingsTest : public CookieSettingsTestBase {};

// Parameterized class that tests combinations of StorageAccess grants and 3pcd
// grants. Tests that don't need the whole range of combinations should create
// their own parameterized subclasses.
class CookieSettingsTestP : public CookieSettingsTestBase,
                            public testing::WithParamInterface<
                                std::tuple</*kGrantSource*/ GrantSource,
                                           /*kBlockSource*/ BlockSource>> {
 public:
  CookieSettingsTestP() {
    std::vector<base::test::FeatureRef> enabled_features;
    if (IsForceThirdPartyCookieBlockingFlagEnabled()) {
      enabled_features.push_back(net::features::kForceThirdPartyCookieBlocking);
      enabled_features.push_back(net::features::kThirdPartyStoragePartitioning);
    }

    feature_list_.InitWithFeatures(enabled_features, {});
  }

  // Indicates whether the setting comes from the testing flag if the
  // test case has 3pc blocked.
  bool IsForceThirdPartyCookieBlockingFlagEnabled() const {
    return std::get<TestVariables::kBlockSource>(GetParam()) ==
           BlockSource::kForceThirdPartyCookieBlockingFlagEnabled;
  }

  bool IsTrackingProtectionEnabledFor3pcd() const {
    return std::get<TestVariables::kBlockSource>(GetParam()) ==
           BlockSource::kTrackingProtectionEnabledFor3pcd;
  }

  bool IsTPCDEnabled() const {
    return IsForceThirdPartyCookieBlockingFlagEnabled() ||
           IsTrackingProtectionEnabledFor3pcd();
  }

  bool IsStorageAccessGrantEligibleViaAPI() const {
    return std::get<TestVariables::kGrantSource>(GetParam()) ==
           GrantSource::kStorageAccessGrantsEligibleViaAPI;
  }

  bool IsStorageAccessGrantEligibleViaHeader() const {
    return std::get<TestVariables::kGrantSource>(GetParam()) ==
           GrantSource::kStorageAccessGrantsEligibleViaHeader;
  }

  bool IsTopLevelStorageAccessGrantEligible() const {
    return std::get<TestVariables::kGrantSource>(GetParam()) ==
           GrantSource::kTopLevelStorageAccessGrantEligible;
  }

  net::CookieSettingOverrides GetCookieSettingOverrides() const {
    net::CookieSettingOverrides overrides;
    if (IsStorageAccessGrantEligibleViaAPI()) {
      overrides.Put(net::CookieSettingOverride::kStorageAccessGrantEligible);
    }
    if (IsStorageAccessGrantEligibleViaHeader()) {
      overrides.Put(
          net::CookieSettingOverride::kStorageAccessGrantEligibleViaHeader);
    }
    if (IsTopLevelStorageAccessGrantEligible()) {
      overrides.Put(
          net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible);
    }
    return overrides;
  }

  // Assumes that cookie access would be blocked if not for a Storage
  // Access API grant. The `allow` parameter indicates the setting to
  // be returned if cookie access is expected to be allowed.
  ContentSetting SettingWithSaaOverride(ContentSetting allow) const {
    DCHECK(allow == CONTENT_SETTING_ALLOW ||
           allow == CONTENT_SETTING_SESSION_ONLY);
    return IsStorageAccessGrantEligibleViaAPI() ||
                   IsStorageAccessGrantEligibleViaHeader()
               ? allow
               : CONTENT_SETTING_BLOCK;
  }

  // Assumes that cookie access would be blocked if not for some usage of the
  // Storage Access API. Note that this is not the same thing as having a SAA
  // permission grant.
  ContentSetting SettingWithSaaViaAPI(ContentSetting allow) const {
    DCHECK(allow == CONTENT_SETTING_ALLOW ||
           allow == CONTENT_SETTING_SESSION_ONLY);
    return IsStorageAccessGrantEligibleViaAPI() ? allow : CONTENT_SETTING_BLOCK;
  }

  // A version of above that considers Top-Level Storage Access API
  // grant instead of Storage Access API grant.
  ContentSetting SettingWithTopLevelSaaOverride() const {
    // TODO(crbug.com/40246640): Check TopLevelStorageAccessAPI
    // instead after separating the feature flag.
    return IsTopLevelStorageAccessGrantEligible() ? CONTENT_SETTING_ALLOW
                                                  : CONTENT_SETTING_BLOCK;
  }

  // The cookie access result would be blocked if not for a Storage
  // Access API grant.
  net::cookie_util::StorageAccessResult
  BlockedStorageAccessResultWithSaaOverride() const {
    if (IsStorageAccessGrantEligibleViaAPI() ||
        IsStorageAccessGrantEligibleViaHeader()) {
      return net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_STORAGE_ACCESS_GRANT;
    }
    return net::cookie_util::StorageAccessResult::ACCESS_BLOCKED;
  }

  // The cookie access result would be blocked if not for some Storage Access
  // API usage. Note that this is not the same thing as presence of a permission
  // grant.
  net::cookie_util::StorageAccessResult
  BlockedStorageAccessResultWithSaaViaAPI() const {
    if (IsStorageAccessGrantEligibleViaAPI()) {
      return net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_STORAGE_ACCESS_GRANT;
    }
    return net::cookie_util::StorageAccessResult::ACCESS_BLOCKED;
  }

  // A version of above that considers Top-Level Storage Access API
  // grant instead of Storage Access API grant.
  net::cookie_util::StorageAccessResult
  BlockedStorageAccessResultWithTopLevelSaaOverride() const {
    // TODO(crbug.com/40246640): Check TopLevelStorageAccessAPI
    // instead after separating the feature flag.
    if (IsTopLevelStorageAccessGrantEligible()) {
      return net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_TOP_LEVEL_STORAGE_ACCESS_GRANT;
    }
    return net::cookie_util::StorageAccessResult::ACCESS_BLOCKED;
  }
};

TEST_F(CookieSettingsTest, GetCookieSettingDefault) {
  CookieSettings settings;
  EXPECT_EQ(
      settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(), GURL(kURL),
                                net::CookieSettingOverrides(), nullptr),
      CONTENT_SETTING_ALLOW);
}

TEST_F(CookieSettingsTest, GetCookieSetting) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, kURL, CONTENT_SETTING_BLOCK)});
  EXPECT_EQ(
      settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(), GURL(kURL),
                                net::CookieSettingOverrides(), nullptr),
      CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, GetCookieSettingMultipleProviders) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, kURL, CONTENT_SETTING_SESSION_ONLY, base::Time(),
                     content_settings::ProviderType::kPolicyProvider),
       CreateSetting("*", "*", CONTENT_SETTING_BLOCK, base::Time(),
                     content_settings::ProviderType::kPolicyProvider),
       CreateSetting(kOtherURL, kOtherURL, CONTENT_SETTING_ALLOW, base::Time(),
                     content_settings::ProviderType::kPrefProvider),
       CreateSetting("*", "*", CONTENT_SETTING_ALLOW, base::Time(),
                     content_settings::ProviderType::kDefaultProvider)});
  EXPECT_EQ(
      settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(), GURL(kURL),
                                net::CookieSettingOverrides(), nullptr),
      CONTENT_SETTING_SESSION_ONLY);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kOtherURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      net::CookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, GetCookieSettingOtrProviders) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, kURL, CONTENT_SETTING_SESSION_ONLY, base::Time(),
                     content_settings::ProviderType::kPrefProvider, true),
       CreateSetting("*", "*", CONTENT_SETTING_BLOCK, base::Time(),
                     content_settings::ProviderType::kPrefProvider, true),
       CreateSetting(kOtherURL, kOtherURL, CONTENT_SETTING_ALLOW, base::Time(),
                     content_settings::ProviderType::kPrefProvider, false),
       CreateSetting("*", "*", CONTENT_SETTING_ALLOW, base::Time(),
                     content_settings::ProviderType::kDefaultProvider, false)});
  EXPECT_EQ(
      settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(), GURL(kURL),
                                net::CookieSettingOverrides(), nullptr),
      CONTENT_SETTING_SESSION_ONLY);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kOtherURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      net::CookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

TEST_P(CookieSettingsTestP, GetCookieSettingMustMatchBothPatterns) {
  CookieSettings settings;
  // This setting needs kOtherURL as the secondary pattern.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_BLOCK)});
  EXPECT_EQ(settings.GetCookieSetting(
                GURL(kURL), net::SiteForCookies::FromUrl(GURL(kURL)),
                GURL(kURL), GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_ALLOW);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kOtherURL), net::SiteForCookies(),
                                      GURL(kURL), GetCookieSettingOverrides(),
                                      nullptr),
            IsForceThirdPartyCookieBlockingFlagEnabled()
                ? CONTENT_SETTING_BLOCK
                : CONTENT_SETTING_ALLOW);

  // This is blocked because both patterns match.
  EXPECT_EQ(settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, GetCookieSettingGetsFirstSetting) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, kURL, CONTENT_SETTING_BLOCK),
       CreateSetting("*", kURL, CONTENT_SETTING_SESSION_ONLY)});
  EXPECT_EQ(
      settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(), GURL(kURL),
                                net::CookieSettingOverrides(), nullptr),
      CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, GetCookieSettingDontBlockThirdParty) {
  base::HistogramTester histogram_tester;
  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(false);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      net::CookieSettingOverrides(), nullptr),
            CONTENT_SETTING_ALLOW);
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_ALLOWED, 1);
}

TEST_F(CookieSettingsTest, GetCookieSettingBlockThirdParty) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      net::CookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, ForceEnableThirdPartyCookieMitigations) {
  const GURL top_level_url(kDomainURL);
  const GURL url(kUnrelatedURL);
  CookieSettings settings;

  // Override to force block third party cookies and force enable the
  // mitigations
  const net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations});

  {
    base::HistogramTester histogram_tester;

    // Verify third-party cookie access is blocked when adding the
    // kForceDisableThirdPartyCookies override and before adding metadata grant
    ASSERT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(),
                                        top_level_url, overrides, nullptr),
              CONTENT_SETTING_BLOCK);
    histogram_tester.ExpectUniqueSample(
        kAllowedRequestsHistogram,
        net::cookie_util::StorageAccessResult::ACCESS_BLOCKED,
        /*expected_bucket_count=*/1);
  }

  base::HistogramTester histogram_tester;
  // Add a mitigation setting (e.g., 3PCD metadata grant) to unblock third-party
  // cookies.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(url.GetHost(), top_level_url.GetHost(),
                                   CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  // Verify the mitigation unblocks cookies.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      overrides, nullptr),
            CONTENT_SETTING_ALLOW);
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_ALLOWED_3PCD_METADATA_GRANT,
      /*expected_bucket_count=*/1);
}

TEST_F(CookieSettingsTest,
       IsCookieAccessible_CookieSettingOverrideBlockSameSiteNoneCookie) {
  CookieSettings settings;

  const std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kURL);
  const url::Origin top_level_origin = url::Origin::Create(GURL(kOtherURL));

  settings.set_block_third_party_cookies(false);

  auto check_third_party_accessibility =
      [&](net::CookieSettingOverrides overrides,
          std::optional<net::CookieInclusionStatus::ExclusionReason>
              expected_exclusion_reason,
          std::optional<net::CookieInclusionStatus::WarningReason>
              expected_warning_reason = std::nullopt,
          const base::Location& location = FROM_HERE) -> bool {
    net::CookieInclusionStatus status;
    bool allowed = settings.IsCookieAccessible(
        *cookie, GURL(kURL), net::SiteForCookies(), top_level_origin,
        net::FirstPartySetMetadata(), overrides, &status);
    EXPECT_NE(allowed, expected_exclusion_reason.has_value())
        << location.ToString();
    EXPECT_EQ(allowed, status.IsInclude()) << location.ToString();

    if (expected_exclusion_reason) {
      EXPECT_TRUE(status.HasExclusionReason(expected_exclusion_reason.value()))
          << location.ToString();
    }
    if (expected_warning_reason) {
      EXPECT_TRUE(status.HasWarningReason(expected_warning_reason.value()))
          << location.ToString();
    }
    return allowed;
  };
  // Third-party cookie is accessible with no source blocking it.
  ASSERT_TRUE(check_third_party_accessibility(
      net::CookieSettingOverrides(), /*expected_exclusion_reason=*/std::nullopt,
      /*expected_warning_reason=*/
      net::CookieInclusionStatus::WarningReason::WARN_THIRD_PARTY_PHASEOUT));

  // Third-party cookies are blocked by overrides.
  EXPECT_FALSE(check_third_party_accessibility(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies},
      /*expected_exclusion_reason=*/net::CookieInclusionStatus::
          ExclusionReason::EXCLUDE_USER_PREFERENCES));

  // Force-disable override takes precedence over force-enable override.
  EXPECT_FALSE(check_third_party_accessibility(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookies},
      /*expected_exclusion_reason=*/net::CookieInclusionStatus::
          ExclusionReason::EXCLUDE_USER_PREFERENCES));

  // Verify that cookie has the phaseout exclusion reason with both required
  // overrides present.
  EXPECT_FALSE(check_third_party_accessibility(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations},
      /*expected_exclusion_reason=*/net::CookieInclusionStatus::
          ExclusionReason::EXCLUDE_THIRD_PARTY_PHASEOUT));

  // Force-enable cookie override continues to be suppressed by the
  // force-disable override.
  EXPECT_FALSE(check_third_party_accessibility(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations,
       net::CookieSettingOverride::kForceEnableThirdPartyCookies},
      /*expected_exclusion_reason=*/net::CookieInclusionStatus::
          ExclusionReason::EXCLUDE_THIRD_PARTY_PHASEOUT));

  net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations});
  // No override can overrule a site-specific setting.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(check_third_party_accessibility(
      overrides, /*expected_exclusion_reason=*/net::CookieInclusionStatus::
          ExclusionReason::EXCLUDE_USER_PREFERENCES));

  // No override can overrule a global setting.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(check_third_party_accessibility(
      overrides, /*expected_exclusion_reason=*/net::CookieInclusionStatus::
          ExclusionReason::EXCLUDE_USER_PREFERENCES));
}

TEST_F(CookieSettingsTest, ShouldAlwaysAllowCookies) {
  CookieSettings settings;
  settings.set_secure_origin_cookies_allowed_schemes({kChromeScheme});
  settings.set_block_third_party_cookies(false);
  ASSERT_FALSE(settings.ShouldAlwaysAllowCookiesForTesting(
      GURL(kURL), GURL(kChromeSchemedURL)));
  ASSERT_TRUE(settings.ShouldAlwaysAllowCookiesForTesting(
      GURL(kSecureSchemedURL), GURL(kChromeSchemedURL)));

  settings.set_block_third_party_cookies(true);
  EXPECT_FALSE(settings.ShouldAlwaysAllowCookiesForTesting(
      GURL(kURL), GURL(kChromeSchemedURL)));
  EXPECT_TRUE(settings.ShouldAlwaysAllowCookiesForTesting(
      GURL(kSecureSchemedURL), GURL(kChromeSchemedURL)));
}

TEST_F(CookieSettingsTest, IsCookieAccessible_AlwaysAllowCookieNotAffected) {
  url::ScopedSchemeRegistryForTests scoped_registry;
  url::AddStandardScheme(kChromeScheme, url::SCHEME_WITH_HOST);

  CookieSettings settings;
  settings.set_block_third_party_cookies(false);
  settings.set_secure_origin_cookies_allowed_schemes({kChromeScheme});
  net::CookieInclusionStatus status;

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kSecureSchemedURL);

  EXPECT_TRUE(settings.IsCookieAccessible(
      *cookie, GURL(kSecureSchemedURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kChromeSchemedURL)),
      net::FirstPartySetMetadata(), net::CookieSettingOverrides(), &status));
  EXPECT_FALSE(status.HasWarningReason(
      net::CookieInclusionStatus::WarningReason::WARN_THIRD_PARTY_PHASEOUT));
}

TEST_F(CookieSettingsTest, ExplicitSettingNoopWhenCookiesAreAllowed) {
  GURL top_level_url = GURL(kURL);
  GURL url = GURL(kOtherURL);

  CookieSettings settings;
  net::CookieInclusionStatus status;
  settings.set_block_third_party_cookies(false);

  // Explicit setting.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(url.GetHost(), top_level_url.GetHost(),
                     CONTENT_SETTING_ALLOW)});

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);

  EXPECT_TRUE(settings.IsCookieAccessible(
      *cookie, GURL(kOtherURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
      net::CookieSettingOverrides(), &status));

  // When cookies are explicitly allowed, no exemption or warning reason should
  // be attached.
  EXPECT_EQ(status.exemption_reason(),
            net::CookieInclusionStatus::ExemptionReason::kNone);

  EXPECT_TRUE(status.warning_reasons().empty());
}

TEST_P(CookieSettingsTestP,
       GetCookieSettingOverridePreservesSessionOnlySetting) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_SESSION_ONLY)});

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithSaaOverride(CONTENT_SETTING_SESSION_ONLY));
}

TEST_F(CookieSettingsTest, GetCookieSettingDontBlockThirdPartyWithException) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);
  EXPECT_EQ(settings.GetCookieSetting(GURL(kURL), net::SiteForCookies(),
                                      GURL(kOtherURL),
                                      net::CookieSettingOverrides(), nullptr),
            CONTENT_SETTING_ALLOW);
}

// The Storage Access API should unblock storage access that would otherwise be
// blocked.
TEST_P(CookieSettingsTestP, GetCookieSettingSAAUnblocks) {
  GURL top_level_url = GURL(kURL);
  GURL url = GURL(kOtherURL);
  GURL third_url = GURL(kDomainURL);

  base::HistogramTester histogram_tester;
  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(url.GetHost(), top_level_url.GetHost(),
                     CONTENT_SETTING_ALLOW)});

  // When requesting our setting for the embedder/top-level combination our
  // grant is for access should be allowed. For any other domain pairs access
  // should still be blocked.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithSaaOverride(CONTENT_SETTING_ALLOW));
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram, BlockedStorageAccessResultWithSaaOverride(),
      1);

  // Invalid pair the |top_level_url| granting access to |url| is now
  // being loaded under |url| as the top level url.
  EXPECT_EQ(settings.GetCookieSetting(top_level_url, net::SiteForCookies(), url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);

  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_STORAGE_ACCESS_GRANT,
      IsStorageAccessGrantEligibleViaAPI() ||
              IsStorageAccessGrantEligibleViaHeader()
          ? 1
          : 0);
  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram, BlockedStorageAccessResultWithSaaOverride(),
      IsStorageAccessGrantEligibleViaAPI() ||
              IsStorageAccessGrantEligibleViaHeader()
          ? 1
          : 2);

  // Invalid pairs where a |third_url| is used.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), third_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
  EXPECT_EQ(
      settings.GetCookieSetting(third_url, net::SiteForCookies(), top_level_url,
                                GetCookieSettingOverrides(), nullptr),
      CONTENT_SETTING_BLOCK);

  // If third-party cookies are blocked, SAA grant takes precedence over
  // possible override to allow 3PCs.
  {
    settings.set_block_third_party_cookies(true);
    base::HistogramTester histogram_tester_2;
    EXPECT_EQ(
        settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                  GetCookieSettingOverrides(), nullptr),
        SettingWithSaaOverride(CONTENT_SETTING_ALLOW));
    histogram_tester_2.ExpectUniqueSample(
        kAllowedRequestsHistogram, BlockedStorageAccessResultWithSaaOverride(),
        1);
  }

  // If cookies are globally blocked, SAA grants and 3PC override
  // should both be ignored.
  {
    settings.set_content_settings(
        ContentSettingsType::COOKIES,
        {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
    settings.set_block_third_party_cookies(true);
    base::HistogramTester histogram_tester_2;
    EXPECT_EQ(
        settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                  GetCookieSettingOverrides(), nullptr),
        CONTENT_SETTING_BLOCK);
    histogram_tester_2.ExpectUniqueSample(
        kAllowedRequestsHistogram,
        net::cookie_util::StorageAccessResult::ACCESS_BLOCKED, 1);
  }
}

TEST_P(CookieSettingsTestP, GetCookieSettingSAAUnblocksViaFedCM) {
  GURL top_level_url = GURL(kURL);
  GURL url = GURL(kOtherURL);
  GURL third_url = GURL(kDomainURL);

  base::HistogramTester histogram_tester;

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);

  settings.set_content_settings(
      ContentSettingsType::FEDERATED_IDENTITY_SHARING,
      {CreateSetting(net::SchemefulSite(url).Serialize(),
                     net::SchemefulSite(top_level_url).Serialize(),
                     CONTENT_SETTING_ALLOW)});

  // When requesting our setting for the embedder/top-level combination our
  // grant is for access should be allowed. For any other domain pairs access
  // should still be blocked.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithSaaViaAPI(CONTENT_SETTING_ALLOW));
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram, BlockedStorageAccessResultWithSaaViaAPI(), 1);

  // Grants are not bidirectional.
  EXPECT_EQ(settings.GetCookieSetting(top_level_url, net::SiteForCookies(), url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);

  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_STORAGE_ACCESS_GRANT,
      IsStorageAccessGrantEligibleViaAPI() ? 1 : 0);
  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram, BlockedStorageAccessResultWithSaaViaAPI(),
      IsStorageAccessGrantEligibleViaAPI() ? 1 : 2);

  // Unrelated contexts do not get access.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), third_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
  EXPECT_EQ(
      settings.GetCookieSetting(third_url, net::SiteForCookies(), top_level_url,
                                GetCookieSettingOverrides(), nullptr),
      CONTENT_SETTING_BLOCK);

  // If cookies are globally blocked, SAA grants and 3PC override
  // should both be ignored.
  {
    settings.set_content_settings(
        ContentSettingsType::COOKIES,
        {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
    settings.set_block_third_party_cookies(true);
    base::HistogramTester histogram_tester_2;
    EXPECT_EQ(
        settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                  GetCookieSettingOverrides(), nullptr),
        CONTENT_SETTING_BLOCK);
    histogram_tester_2.ExpectUniqueSample(
        kAllowedRequestsHistogram,
        net::cookie_util::StorageAccessResult::ACCESS_BLOCKED, 1);
  }
}

// The Top-Level Storage Access API should unblock storage access that would
// otherwise be blocked.
TEST_P(CookieSettingsTestP, GetCookieSettingTopLevelStorageAccessUnblocks) {
  const GURL top_level_url(kURL);
  const GURL url(kOtherURL);
  const GURL third_url(kDomainURL);

  base::HistogramTester histogram_tester;
  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);

  // Only set the storage access granted by Top-Level Storage Access API.
  settings.set_content_settings(
      ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
      {CreateSetting(url.GetHost(), top_level_url.GetHost(),
                     CONTENT_SETTING_ALLOW)});

  // When requesting our setting for the embedder/top-level combination our
  // grant is for access should be allowed. For any other domain pairs access
  // should still be blocked.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithTopLevelSaaOverride());
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      BlockedStorageAccessResultWithTopLevelSaaOverride(), 1);

  // Check the cookie setting that does not match the top-level storage access
  // grant--the |top_level_url| granting access to |url| is now being loaded
  // under |url| as the top level url.
  EXPECT_EQ(settings.GetCookieSetting(top_level_url, net::SiteForCookies(), url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 2);
  // TODO(crbug.com/40246640): Separate metrics between StorageAccessAPI
  // and the page-level variant.
  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_TOP_LEVEL_STORAGE_ACCESS_GRANT,
      IsTopLevelStorageAccessGrantEligible() ? 1 : 0);
  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      BlockedStorageAccessResultWithTopLevelSaaOverride(),
      IsTopLevelStorageAccessGrantEligible() ? 1 : 2);

  // Check the cookie setting that does not match the top-level storage access
  // grant where a |third_url| is used.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), third_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
  EXPECT_EQ(
      settings.GetCookieSetting(third_url, net::SiteForCookies(), top_level_url,
                                GetCookieSettingOverrides(), nullptr),
      CONTENT_SETTING_BLOCK);

  // If third-party cookies are blocked, Top-Level Storage Access grant takes
  // precedence over possible override to allow third-party cookies.
  {
    base::HistogramTester histogram_tester_2;
    EXPECT_EQ(
        settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                  GetCookieSettingOverrides(), nullptr),
        SettingWithTopLevelSaaOverride());
    histogram_tester_2.ExpectUniqueSample(
        kAllowedRequestsHistogram,
        BlockedStorageAccessResultWithTopLevelSaaOverride(), 1);
  }

  // If cookies are globally blocked, Top-Level Storage Access grants and 3PC
  // override should both be ignored.
  {
    settings.set_content_settings(
        ContentSettingsType::COOKIES,
        {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
    base::HistogramTester histogram_tester_2;
    EXPECT_EQ(
        settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                  GetCookieSettingOverrides(), nullptr),
        CONTENT_SETTING_BLOCK);
    histogram_tester_2.ExpectUniqueSample(
        kAllowedRequestsHistogram,
        net::cookie_util::StorageAccessResult::ACCESS_BLOCKED, 1);
  }
}

// Subdomains of the granted embedding url should not gain access if a valid
// grant exists.
TEST_P(CookieSettingsTestP, GetCookieSettingSAAResourceWildcards) {
  GURL top_level_url = GURL(kURL);
  GURL url = GURL(kDomainURL);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(kDomain, top_level_url.GetHost(), CONTENT_SETTING_ALLOW)});

  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithSaaOverride(CONTENT_SETTING_ALLOW));

  EXPECT_EQ(settings.GetCookieSetting(GURL(kSubDomainURL),
                                      net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

// Subdomains of the granted top level url should not grant access if a valid
// grant exists.
TEST_P(CookieSettingsTestP, GetCookieSettingSAATopLevelWildcards) {
  GURL top_level_url = GURL(kDomainURL);
  GURL url(kURL);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(url.GetHost(), kDomain, CONTENT_SETTING_ALLOW)});

  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithSaaOverride(CONTENT_SETTING_ALLOW));

  EXPECT_EQ(
      settings.GetCookieSetting(url, net::SiteForCookies(), GURL(kSubDomainURL),
                                GetCookieSettingOverrides(), nullptr),
      CONTENT_SETTING_BLOCK);
}

// Any Storage Access API grant should not override an explicit setting to block
// cookie access.
TEST_P(CookieSettingsTestP, GetCookieSettingSAARespectsSettings) {
  GURL top_level_url = GURL(kURL);
  GURL url = GURL(kOtherURL);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(url.GetHost(), top_level_url.GetHost(),
                     CONTENT_SETTING_ALLOW)});

  base::HistogramTester histogram_tester;

  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

// Once a grant expires access should no longer be given.
TEST_P(CookieSettingsTestP, GetCookieSettingSAAExpiredGrant) {
  GURL top_level_url = GURL(kURL);
  GURL url = GURL(kOtherURL);

  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  settings.set_block_third_party_cookies(true);

  base::Time expiration_time = base::Time::Now() + base::Seconds(100);
  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(url.GetHost(), top_level_url.GetHost(),
                     CONTENT_SETTING_ALLOW, expiration_time)});

  base::HistogramTester histogram_tester;
  // When requesting our setting for the embedder/top-level combination our
  // grant is for access should be allowed. For any other domain pairs access
  // should still be blocked.
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWithSaaOverride(CONTENT_SETTING_ALLOW));

  // If we fastforward past the expiration of our grant the result should be
  // CONTENT_SETTING_BLOCK now.
  FastForwardTime(base::Seconds(101));
  EXPECT_EQ(settings.GetCookieSetting(url, net::SiteForCookies(), top_level_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, CreateDeleteCookieOnExitPredicateNoSettings) {
  CookieSettings settings;
  EXPECT_FALSE(settings.CreateDeleteCookieOnExitPredicate());
}

TEST_F(CookieSettingsTest, CreateDeleteCookieOnExitPredicateNoSessionOnly) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});
  EXPECT_FALSE(settings.CreateDeleteCookieOnExitPredicate());
}

TEST_F(CookieSettingsTest, CreateDeleteCookieOnExitPredicateSessionOnly) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_SESSION_ONLY)});
  EXPECT_TRUE(settings.CreateDeleteCookieOnExitPredicate().Run(
      "foo.com", net::CookieSourceScheme::kNonSecure));
}

TEST_F(CookieSettingsTest, CreateDeleteCookieOnExitPredicateExceptionOnly) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("https://foo.com", "*", CONTENT_SETTING_SESSION_ONLY)});

  EXPECT_TRUE(settings.CreateDeleteCookieOnExitPredicate().Run(
      "foo.com", net::CookieSourceScheme::kSecure));
  EXPECT_FALSE(settings.CreateDeleteCookieOnExitPredicate().Run(
      "other.com", net::CookieSourceScheme::kSecure));
}

TEST_F(CookieSettingsTest, CreateDeleteCookieOnExitPredicateAllow) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_ALLOW),
       CreateSetting("*", "*", CONTENT_SETTING_SESSION_ONLY)});
  EXPECT_FALSE(settings.CreateDeleteCookieOnExitPredicate().Run(
      "foo.com", net::CookieSourceScheme::kNonSecure));
}

TEST_F(CookieSettingsTest, GetCookieSettingSecureOriginCookiesAllowed) {
  CookieSettings settings;
  settings.set_secure_origin_cookies_allowed_schemes({"chrome"});
  settings.set_block_third_party_cookies(true);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("https://foo.com") /* url */, net::SiteForCookies(),
                GURL("chrome://foo") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_ALLOW);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("chrome://foo") /* url */, net::SiteForCookies(),
                GURL("https://foo.com") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_BLOCK);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("http://foo.com") /* url */, net::SiteForCookies(),
                GURL("chrome://foo") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, GetCookieSettingWithThirdPartyCookiesAllowedScheme) {
  CookieSettings settings;
  settings.set_third_party_cookies_allowed_schemes({"chrome-extension"});
  settings.set_block_third_party_cookies(true);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("http://foo.com") /* url */, net::SiteForCookies(),
                GURL("chrome-extension://foo") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_ALLOW);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("http://foo.com") /* url */, net::SiteForCookies(),
                GURL("other-scheme://foo") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_BLOCK);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("chrome-extension://foo") /* url */, net::SiteForCookies(),
                GURL("http://foo.com") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, GetCookieSettingMatchingSchemeCookiesAllowed) {
  CookieSettings settings;
  settings.set_matching_scheme_cookies_allowed_schemes({"chrome-extension"});
  settings.set_block_third_party_cookies(true);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("chrome-extension://bar") /* url */, net::SiteForCookies(),
                GURL("chrome-extension://foo") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_ALLOW);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("http://foo.com") /* url */, net::SiteForCookies(),
                GURL("chrome-extension://foo") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_BLOCK);

  EXPECT_EQ(settings.GetCookieSetting(
                GURL("chrome-extension://foo") /* url */, net::SiteForCookies(),
                GURL("http://foo.com") /* first_party_url */,
                net::CookieSettingOverrides()),
            CONTENT_SETTING_BLOCK);
}

TEST_F(CookieSettingsTest, LegacyCookieAccessDefault) {
  CookieSettings settings;

  EXPECT_EQ(settings.GetSettingForLegacyCookieAccess(kDomain),
            CONTENT_SETTING_BLOCK);
  EXPECT_EQ(net::CookieAccessSemantics::NONLEGACY,
            settings.GetCookieAccessSemanticsForDomain(kDomain));
}

TEST_F(CookieSettingsTest, CookieAccessSemanticsForDomain) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::LEGACY_COOKIE_ACCESS,
      {CreateSetting(kDomain, "*", CONTENT_SETTING_ALLOW)});
  const struct {
    net::CookieAccessSemantics status;
    std::string cookie_domain;
  } kTestCases[] = {
      // These two test cases are LEGACY because they match the setting.
      {net::CookieAccessSemantics::LEGACY, kDomain},
      {net::CookieAccessSemantics::LEGACY, kDotDomain},
      // These two test cases default into NONLEGACY.
      // Subdomain does not match pattern.
      {net::CookieAccessSemantics::NONLEGACY, kSubDomain},
      {net::CookieAccessSemantics::NONLEGACY, kOtherDomain}};
  for (const auto& test : kTestCases) {
    EXPECT_EQ(test.status,
              settings.GetCookieAccessSemanticsForDomain(test.cookie_domain));
  }
}

TEST_F(CookieSettingsTest, CookieAccessSemanticsForDomainWithWildcard) {
  CookieSettings settings;
  settings.set_content_settings(
      ContentSettingsType::LEGACY_COOKIE_ACCESS,
      {CreateSetting(kDomainWildcardPattern, "*", CONTENT_SETTING_ALLOW)});
  const struct {
    net::CookieAccessSemantics status;
    std::string cookie_domain;
  } kTestCases[] = {
      // These three test cases are LEGACY because they match the setting.
      {net::CookieAccessSemantics::LEGACY, kDomain},
      {net::CookieAccessSemantics::LEGACY, kDotDomain},
      // Subdomain also matches pattern.
      {net::CookieAccessSemantics::LEGACY, kSubDomain},
      // This test case defaults into NONLEGACY.
      {net::CookieAccessSemantics::NONLEGACY, kOtherDomain}};
  for (const auto& test : kTestCases) {
    EXPECT_EQ(test.status,
              settings.GetCookieAccessSemanticsForDomain(test.cookie_domain));
  }
}

struct AllowedByStorageAccessTypeTestData {
  content_settings::CookieSettingsBase::AllowedByStorageAccessType
      expected_type;
  net::CookieSettingOverrides overrides;
  std::vector<ContentSettingsType> settings_types;
  bool expected_api_result = true;
};
class AllowedByStorageAccessTypeTest
    : public CookieSettingsTestBase,
      public testing::WithParamInterface<AllowedByStorageAccessTypeTestData> {
 public:
  AllowedByStorageAccessTypeTest() {
    for (const auto& val : GetParam().settings_types) {
      settings_.set_content_settings(
          val, {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});
    }
  }

  content_settings::CookieSettingsBase::AllowedByStorageAccessType
  ExpectedType() const {
    return GetParam().expected_type;
  }

  bool ExpectedApiResult() const { return GetParam().expected_api_result; }

  net::CookieSettingOverrides Overrides() const { return GetParam().overrides; }

  std::vector<ContentSettingsType> SettingTypes() const {
    return GetParam().settings_types;
  }

  CookieSettings settings_;
};

INSTANTIATE_TEST_SUITE_P(
    /* no label */,
    AllowedByStorageAccessTypeTest,
    testing::ValuesIn({
        /*No overrides or settings*/
        AllowedByStorageAccessTypeTestData{
            content_settings::CookieSettingsBase::AllowedByStorageAccessType::
                kNone,
            {},
            {},
            /*expected_api_result=*/false,
        },
        /*Override but not settings*/
        AllowedByStorageAccessTypeTestData{
            content_settings::CookieSettingsBase::AllowedByStorageAccessType::
                kNone,
            {net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible},
            {},
            /*expected_api_result=*/false,
        },
        /*Settings but not override*/
        AllowedByStorageAccessTypeTestData{
            content_settings::CookieSettingsBase::AllowedByStorageAccessType::
                kNone,
            {},
            {ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS},
            /*expected_api_result=*/false,
        },
        AllowedByStorageAccessTypeTestData{
            content_settings::CookieSettingsBase::AllowedByStorageAccessType::
                kTopLevelOnly,
            {net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible},
            {ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS},
        },
        AllowedByStorageAccessTypeTestData{
            content_settings::CookieSettingsBase::AllowedByStorageAccessType::
                kStorageAccessOnly,
            {net::CookieSettingOverride::kStorageAccessGrantEligible},
            {ContentSettingsType::STORAGE_ACCESS},
        },
        AllowedByStorageAccessTypeTestData{
            content_settings::CookieSettingsBase::AllowedByStorageAccessType::
                kTopLevelAndStorageAccess,
            {net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible,
             net::CookieSettingOverride::kStorageAccessGrantEligible},
            {ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
             ContentSettingsType::STORAGE_ACCESS},
        },
    }));

TEST_P(AllowedByStorageAccessTypeTest, IsCookieAccessible) {
  base::HistogramTester histogram_tester;
  net::CookieInclusionStatus status;
  settings_.set_block_third_party_cookies(true);

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalCookie(kUmaCookieName, kURL);

  ASSERT_EQ(settings_.IsCookieAccessible(
                *cookie, GURL(kURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kOtherURL)),
                net::FirstPartySetMetadata(), Overrides(), &status),
            ExpectedApiResult());

  histogram_tester.ExpectUniqueSample(kAllowedByStorageAccessTypeHistogram,
                                      ExpectedType(), 1);
}

TEST_P(AllowedByStorageAccessTypeTest, AnnotateAndMoveUserBlockedCookies) {
  base::HistogramTester histogram_tester;
  settings_.set_block_third_party_cookies(true);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalCookie(kUmaCookieName, kURL), {}}};
  net::CookieAccessResultList excluded_cookies = {};

  ASSERT_EQ(
      settings_.AnnotateAndMoveUserBlockedCookies(
          GURL(kURL), net::SiteForCookies(),
          url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
          Overrides(), maybe_included_cookies, excluded_cookies),
      ExpectedApiResult());

  histogram_tester.ExpectUniqueSample(kAllowedByStorageAccessTypeHistogram,
                                      ExpectedType(), 1);
}

TEST_P(CookieSettingsTestP, IsPrivacyModeEnabled) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);

  const net::NetworkDelegate::PrivacySetting
      third_party_partitioned_or_all_allowed =
          net::NetworkDelegate::PrivacySetting::kPartitionedStateAllowedOnly;

  // Third-party requests should only have accessed to partitioned state.
  EXPECT_EQ(third_party_partitioned_or_all_allowed,
            settings.IsPrivacyModeEnabled(GURL(kURL), net::SiteForCookies(),
                                          url::Origin::Create(GURL(kOtherURL)),
                                          GetCookieSettingOverrides()));

  // Same for requests with a null site_for_cookies, even if the
  // top_frame_origin matches.
  const net::NetworkDelegate::PrivacySetting
      first_party_partitioned_or_all_allowed =
          IsStorageAccessGrantEligibleViaAPI() ||
                  IsStorageAccessGrantEligibleViaHeader()
              ? net::NetworkDelegate::PrivacySetting::kStateAllowed
              : net::NetworkDelegate::PrivacySetting::
                    kPartitionedStateAllowedOnly;
  EXPECT_EQ(first_party_partitioned_or_all_allowed,
            settings.IsPrivacyModeEnabled(GURL(kURL), net::SiteForCookies(),
                                          url::Origin::Create(GURL(kURL)),
                                          GetCookieSettingOverrides()));

  // The first party is able to send any type of state.
  EXPECT_EQ(net::NetworkDelegate::PrivacySetting::kStateAllowed,
            settings.IsPrivacyModeEnabled(
                GURL(kURL), net::SiteForCookies::FromUrl(GURL(kURL)),
                url::Origin::Create(GURL(kURL)), GetCookieSettingOverrides()));

  // Setting a site-specific rule for the top-level frame origin when it is
  // embedded on an unrelated site should not affect if partitioned state is
  // allowed.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kOtherURL, kUnrelatedURL, CONTENT_SETTING_BLOCK)});
  EXPECT_EQ(third_party_partitioned_or_all_allowed,
            settings.IsPrivacyModeEnabled(GURL(kURL), net::SiteForCookies(),
                                          url::Origin::Create(GURL(kOtherURL)),
                                          GetCookieSettingOverrides()));

  // No state is allowed if there's a site-specific rule that blocks access,
  // regardless of the kind of request.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  // Third-party requests:
  EXPECT_EQ(net::NetworkDelegate::PrivacySetting::kStateDisallowed,
            settings.IsPrivacyModeEnabled(GURL(kURL), net::SiteForCookies(),
                                          url::Origin::Create(GURL(kOtherURL)),
                                          GetCookieSettingOverrides()));

  // Requests with a null site_for_cookies, but matching top_frame_origin.
  EXPECT_EQ(net::NetworkDelegate::PrivacySetting::kStateDisallowed,
            settings.IsPrivacyModeEnabled(GURL(kURL), net::SiteForCookies(),
                                          url::Origin::Create(GURL(kURL)),
                                          GetCookieSettingOverrides()));
  // First-party requests.
  EXPECT_EQ(net::NetworkDelegate::PrivacySetting::kStateDisallowed,
            settings.IsPrivacyModeEnabled(
                GURL(kURL), net::SiteForCookies::FromUrl(GURL(kURL)),
                url::Origin::Create(GURL(kURL)), GetCookieSettingOverrides()));
}

TEST_P(CookieSettingsTestP, IsCookieAccessible_SameSiteNoneCookie) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kURL);

  // Third-party cookies are blocked, so the cookie should not be accessible by
  // default in a third-party context.
  net::CookieInclusionStatus status;
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  EXPECT_FALSE(status.HasWarningReason(
      net::CookieInclusionStatus::WarningReason::WARN_THIRD_PARTY_PHASEOUT));
  EXPECT_TRUE(status.HasExclusionReason(
      IsTPCDEnabled() ? net::CookieInclusionStatus::ExclusionReason::
                            EXCLUDE_THIRD_PARTY_PHASEOUT
                      : net::CookieInclusionStatus::ExclusionReason::
                            EXCLUDE_USER_PREFERENCES));

  // Note that the SiteForCookies matches nothing, so this is a third-party
  // context even though the `url` matches the `top_frame_origin`.
  status.ResetForTesting();
  EXPECT_EQ(settings.IsCookieAccessible(
                *cookie, GURL(kURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
                GetCookieSettingOverrides(), &status),
            IsStorageAccessGrantEligibleViaAPI() ||
                IsStorageAccessGrantEligibleViaHeader());
  EXPECT_THAT(
      status.HasExclusionReason(
          IsTPCDEnabled() ? net::CookieInclusionStatus::ExclusionReason::
                                EXCLUDE_THIRD_PARTY_PHASEOUT
                          : net::CookieInclusionStatus::ExclusionReason::
                                EXCLUDE_USER_PREFERENCES),
      Not(IsStorageAccessGrantEligibleViaAPI() ||
          IsStorageAccessGrantEligibleViaHeader()));
  EXPECT_THAT(status.exemption_reason() ==
                  net::CookieInclusionStatus::ExemptionReason::kStorageAccess,
              IsStorageAccessGrantEligibleViaAPI() ||
                  IsStorageAccessGrantEligibleViaHeader());

  // No override can overrule a site-specific setting.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  status.ResetForTesting();
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  // Cookies blocked by a site-specific setting should still use
  // `EXCLUDE_USER_PREFERENCES` reason.
  EXPECT_TRUE(status.HasExclusionReason(
      net::CookieInclusionStatus::ExclusionReason::EXCLUDE_USER_PREFERENCES));

  // No override can overrule a global setting.
  status.ResetForTesting();
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  EXPECT_TRUE(status.HasOnlyExclusionReason(
      net::CookieInclusionStatus::ExclusionReason::EXCLUDE_USER_PREFERENCES));
}

TEST_P(CookieSettingsTestP, IsCookieAccessible_SameSiteLaxCookie) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalCookie("name", kURL);

  net::CookieInclusionStatus status;
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  if (IsTPCDEnabled()) {
    // This cookie is blocked due to SameSite=Lax, thus the following reasons
    // are irrelevant.
    EXPECT_FALSE(status.HasWarningReason(
        net::CookieInclusionStatus::WarningReason::WARN_THIRD_PARTY_PHASEOUT));
    EXPECT_FALSE(
        status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
                                      EXCLUDE_THIRD_PARTY_PHASEOUT));
    EXPECT_FALSE(status.HasExclusionReason(
        net::CookieInclusionStatus::ExclusionReason::EXCLUDE_USER_PREFERENCES));
  } else {
    EXPECT_TRUE(status.HasExclusionReason(
        net::CookieInclusionStatus::ExclusionReason::EXCLUDE_USER_PREFERENCES));
  }
  // Note that the SiteForCookies matches nothing, so this is a third-party
  // context even though the `url` matches the `top_frame_origin`.
  status.ResetForTesting();
  EXPECT_EQ(settings.IsCookieAccessible(
                *cookie, GURL(kURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
                GetCookieSettingOverrides(), &status),
            IsStorageAccessGrantEligibleViaAPI() ||
                IsStorageAccessGrantEligibleViaHeader());
  // This cookie is not SameSite=None cookie, thus exemption reason is
  // irrelevant.
  EXPECT_EQ(status.exemption_reason(),
            net::CookieInclusionStatus::ExemptionReason::kNone);

  // No override can overrule a site-specific setting.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  status.ResetForTesting();
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  // Cookies blocked by a site-specific setting should still use
  // `EXCLUDE_USER_PREFERENCES` reason.
  EXPECT_TRUE(status.HasExclusionReason(
      net::CookieInclusionStatus::ExclusionReason::EXCLUDE_USER_PREFERENCES));

  // No override can overrule a global setting.
  status.ResetForTesting();
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  EXPECT_TRUE(status.HasExclusionReason(
      net::CookieInclusionStatus::ExclusionReason::EXCLUDE_USER_PREFERENCES));
}

TEST_P(CookieSettingsTestP, IsCookieAccessible_PartitionedCookies) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  std::unique_ptr<net::CanonicalCookie> partitioned_cookie =
      MakeCanonicalCookie(
          "__Host-partitioned", kURL,
          net::CookiePartitionKey::FromURLForTesting(GURL(kOtherURL)));

  EXPECT_TRUE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  // If third-party cookie blocking is disabled, the partitioned cookie should
  // still be available
  settings.set_block_third_party_cookies(false);
  EXPECT_TRUE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  // If there is a site-specific content setting blocking cookies, then
  // partitioned cookies should not be available.
  settings.set_block_third_party_cookies(false);
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  // If third-party cookie blocking is enabled and there is a site-specific
  // content setting blocking the top-frame origin's cookies, then the
  // partitioned cookie should be blocked as well.
  settings.set_block_third_party_cookies(true);
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", kOtherURL, CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  // If third-party cookie blocking is enabled and there is a site-specific
  // setting for the top-frame origin that only applies on an unrelated site,
  // then the partitioned cookie should still be allowed.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kUnrelatedURL, kOtherURL, CONTENT_SETTING_BLOCK)});
  EXPECT_TRUE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  // If third-party cookie blocking is enabled and there is a matching Storage
  // Access setting whose value is BLOCK, then the partitioned cookie should
  // still be allowed.
  settings.set_block_third_party_cookies(true);
  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_BLOCK)});
  EXPECT_TRUE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));

  // Partitioned cookies are not affected by 3pc phaseout, so the
  // *_THIRD_PARTY_PHASEOUT warning/exclusion reason is irrelevant.
  EXPECT_FALSE(status.HasWarningReason(
      net::CookieInclusionStatus::WarningReason::WARN_THIRD_PARTY_PHASEOUT));
  EXPECT_FALSE(
      status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
                                    EXCLUDE_THIRD_PARTY_PHASEOUT));
}

TEST_P(CookieSettingsTestP, IsCookieAccessible_NoneExemptionReason) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  std::unique_ptr<net::CanonicalCookie> partitioned_cookie =
      MakeCanonicalSameSiteNoneCookie(
          "__Host-partitioned", kURL,
          net::CookiePartitionKey::FromURLForTesting(GURL(kOtherURL)));
  std::unique_ptr<net::CanonicalCookie> samesitelax_cookie =
      MakeCanonicalCookie("samesite_lax", kURL);

  // Precautionary - partitioned cookies should be allowed with no exemption
  // reason.
  EXPECT_TRUE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  EXPECT_TRUE(status.exemption_reason() ==
              net::CookieInclusionStatus::ExemptionReason::kNone);

  // Sets exemptions
  if (IsStorageAccessGrantEligibleViaAPI() ||
      IsStorageAccessGrantEligibleViaHeader()) {
    // Sets the storage access granted by Storage Access API.
    settings.set_content_settings(
        ContentSettingsType::STORAGE_ACCESS,
        {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});
  } else if (IsTopLevelStorageAccessGrantEligible()) {
    // Sets the storage access granted by Top-Level Storage Access API.
    settings.set_content_settings(
        ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
        {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});
  } else {
    // Sets a site-specific setting
    settings.set_content_settings(
        ContentSettingsType::COOKIES,
        {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});
  }
  // Partitioned cookies should be allowed with no exemption reason even with
  // the exemptions.
  status.ResetForTesting();
  EXPECT_TRUE(settings.IsCookieAccessible(
      *partitioned_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  EXPECT_TRUE(status.exemption_reason() ==
              net::CookieInclusionStatus::ExemptionReason::kNone);

  // non-SameSite=None cookies should have no exemption reason.
  status.ResetForTesting();
  EXPECT_TRUE(settings.IsCookieAccessible(
      *samesitelax_cookie, GURL(kURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kOtherURL)), net::FirstPartySetMetadata(),
      GetCookieSettingOverrides(), &status));
  EXPECT_TRUE(status.exemption_reason() ==
              net::CookieInclusionStatus::ExemptionReason::kNone);
}

TEST_P(CookieSettingsTestP, IsCookieAccessible_SitesInFirstPartySets) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  url::Origin top_level_origin = url::Origin::Create(GURL(kRwsOwnerURL));

  net::SchemefulSite primary((GURL(kRwsOwnerURL)));

  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kRwsMemberURL);

  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kRwsMemberURL), net::SiteForCookies(), top_level_origin,
      net::FirstPartySetMetadata(), GetCookieSettingOverrides(), &status));
  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
      {IsTPCDEnabled() ? net::CookieInclusionStatus::ExclusionReason::
                             EXCLUDE_THIRD_PARTY_PHASEOUT
                       : net::CookieInclusionStatus::ExclusionReason::
                             EXCLUDE_USER_PREFERENCES}));

  status.ResetForTesting();
  // EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET should be added with the
  // FirstPartySetMetadata indicating the cookie url and the top-level url are
  // in the same RWS.
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kRwsMemberURL), net::SiteForCookies(), top_level_origin,
      net::FirstPartySetMetadata(
          net::FirstPartySetEntry(primary, net::SiteType::kAssociated),
          net::FirstPartySetEntry(primary, net::SiteType::kPrimary)),
      GetCookieSettingOverrides(), &status));
  if (IsTPCDEnabled()) {
    EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
        {net::CookieInclusionStatus::ExclusionReason::
             EXCLUDE_THIRD_PARTY_PHASEOUT,
         net::CookieInclusionStatus::ExclusionReason::
             EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET}));
  } else {
    EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
        {net::CookieInclusionStatus::ExclusionReason::
             EXCLUDE_USER_PREFERENCES}));
  }
}

TEST_P(CookieSettingsTestP, AnnotateAndMoveUserBlockedCookies_CrossSiteEmbed) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}},
      {*MakeCanonicalCookie("cookie", kURL), {}}};
  net::CookieAccessResultList excluded_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kURL),
       // The ExclusionReason below is irrelevant, as long as there is
       // one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))},
      {*MakeCanonicalCookie("excluded_samesitelax", kURL),
       // The ExclusionReason below is irrelevant, as long as there is
       // one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))},
  };
  url::Origin origin = url::Origin::Create(GURL(kOtherURL));

  // Note that `url` does not match the `top_frame_origin`.
  EXPECT_EQ(settings.AnnotateAndMoveUserBlockedCookies(
                GURL(kURL), net::SiteForCookies(), &origin,
                net::FirstPartySetMetadata(), GetCookieSettingOverrides(),
                maybe_included_cookies, excluded_cookies),
            IsTPCDEnabled());

  if (IsTPCDEnabled()) {
    EXPECT_THAT(
        maybe_included_cookies,
        UnorderedElementsAre(MatchesCookieWithAccessResult(
            net::MatchesCookieWithName("cookie"),
            // This cookie really ought to be excluded based on SameSite rules,
            // but since those are not enforced by CookieSettings, and
            // CookieSettings doesn't add 3PCD-related exclusion reasons to
            // non-SameSite=None cookies, we don't expect an exclusion reason
            // for this cookie.
            MatchesCookieAccessResult(
                AllOf(net::IsInclude(),
                      Not(net::HasWarningReason(
                          net::CookieInclusionStatus::WarningReason::
                              WARN_THIRD_PARTY_PHASEOUT)),
                      net::HasExactlyExemptionReason(
                          net::CookieInclusionStatus::ExemptionReason::kNone)),
                _, _, _))));

    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_other"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("third_party"),
                        MatchesCookieAccessResult(
                            net::HasExclusionReason(
                                net::CookieInclusionStatus::ExclusionReason::
                                    EXCLUDE_THIRD_PARTY_PHASEOUT),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_samesitelax"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY}),
                            _, _, _))));
  } else {
    // The 3pcs are blocked by user.
    EXPECT_THAT(maybe_included_cookies, IsEmpty());
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("cookie"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_other"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY,
                                 net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_samesitelax"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting({
                                net::CookieInclusionStatus::ExclusionReason::
                                    EXCLUDE_SECURE_ONLY,
                                net::CookieInclusionStatus::ExclusionReason::
                                    EXCLUDE_USER_PREFERENCES,
                            }),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("third_party"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _))));
  }
}

TEST_F(CookieSettingsTest,
       AnnotateAndMoveUserBlockedCookies_CrossSiteEmbed_3PCAllowed) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(false);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalCookie("lax_by_default", kURL), {}},
      {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}}};
  net::CookieAccessResultList excluded_cookies = {};
  url::Origin origin = url::Origin::Create(GURL(kOtherURL));

  // Note that `url` does not match the `top_frame_origin`.
  EXPECT_TRUE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kURL), net::SiteForCookies(), &origin, net::FirstPartySetMetadata(),
      net::CookieSettingOverrides(), maybe_included_cookies, excluded_cookies));

  // Verify that the allowed cookie has the expected warning reason.
  EXPECT_THAT(maybe_included_cookies,
              UnorderedElementsAre(
                  MatchesCookieWithAccessResult(
                      net::MatchesCookieWithName("lax_by_default"),
                      MatchesCookieAccessResult(
                          AllOf(net::IsInclude(),
                                Not(net::HasWarningReason(
                                    net::CookieInclusionStatus::WarningReason::
                                        WARN_THIRD_PARTY_PHASEOUT))),
                          _, _, _)),
                  MatchesCookieWithAccessResult(
                      net::MatchesCookieWithName("third_party"),
                      MatchesCookieAccessResult(
                          AllOf(net::IsInclude(),
                                net::HasExactlyWarningReasonsForTesting(
                                    {net::CookieInclusionStatus::WarningReason::
                                         WARN_THIRD_PARTY_PHASEOUT})),
                          _, _, _))));
}

TEST_F(
    CookieSettingsTest,
    AnnotateAndMoveUserBlockedCookies_CrossSiteEmbed_CookieSettingOverrideBlock3PC) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(false);

  const url::Origin origin = url::Origin::Create(GURL(kOtherURL));

  net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies});
  {
    net::CookieAccessResultList maybe_included_cookies = {
        {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}}};
    net::CookieAccessResultList excluded_cookies = {};

    EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
        GURL(kURL), net::SiteForCookies(), &origin,
        net::FirstPartySetMetadata(), overrides, maybe_included_cookies,
        excluded_cookies));

    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("third_party"),
                    MatchesCookieAccessResult(
                        net::HasExactlyExclusionReasonsForTesting(
                            {net::CookieInclusionStatus::ExclusionReason::
                                 EXCLUDE_USER_PREFERENCES}),
                        _, _, _))));
  }
  // Both overrides should be present to yield the phaseout exclusion reason.
  overrides.Put(
      net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations);
  {
    net::CookieAccessResultList maybe_included_cookies = {
        {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}}};
    net::CookieAccessResultList excluded_cookies = {};
    EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
        GURL(kURL), net::SiteForCookies(), &origin,
        net::FirstPartySetMetadata(), overrides, maybe_included_cookies,
        excluded_cookies));

    // Verify that the excluded cookie has the expected reason.
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("third_party"),
                    MatchesCookieAccessResult(
                        net::HasExactlyExclusionReasonsForTesting(
                            {net::CookieInclusionStatus::ExclusionReason::
                                 EXCLUDE_THIRD_PARTY_PHASEOUT}),
                        _, _, _))));
  }
}

TEST_F(CookieSettingsTest,
       AnnotateAndMoveUserBlockedCookies_SameSiteEmbed_3PCAllowed) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(false);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("cookie", kDomainURL), {}}};
  net::CookieAccessResultList excluded_cookies = {};
  url::Origin origin = url::Origin::Create(GURL(kDomainURL));

  // This is a first-party context.
  EXPECT_TRUE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kSubDomainURL), net::SiteForCookies(net::SchemefulSite(origin)),
      &origin, net::FirstPartySetMetadata(), net::CookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));

  // Verify that the allowed cookie does not have the 3PC warning reason.
  EXPECT_THAT(
      maybe_included_cookies,
      ElementsAre(MatchesCookieWithAccessResult(
          net::MatchesCookieWithName("cookie"),
          MatchesCookieAccessResult(
              AllOf(net::IsInclude(),
                    Not(net::HasWarningReason(
                        net::CookieInclusionStatus::WarningReason::
                            WARN_THIRD_PARTY_PHASEOUT)),
                    net::HasExactlyExemptionReason(
                        net::CookieInclusionStatus::ExemptionReason::kNone)),
              _, _, _))));

  // This is a third-party context, even though the request URL and the
  // top-frame URL are same-site with each other.
  maybe_included_cookies[0].access_result.status = net::CookieInclusionStatus();
  EXPECT_TRUE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kSubDomainURL), net::SiteForCookies(), &origin,
      net::FirstPartySetMetadata(), net::CookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));

  // Verify that the allowed cookie has the 3PC warning reason.
  EXPECT_THAT(maybe_included_cookies,
              ElementsAre(MatchesCookieWithAccessResult(
                  net::MatchesCookieWithName("cookie"),
                  MatchesCookieAccessResult(
                      AllOf(net::IsInclude(),
                            net::HasWarningReason(
                                net::CookieInclusionStatus::WarningReason::
                                    WARN_THIRD_PARTY_PHASEOUT)),
                      _, _, _))));
}

TEST_P(CookieSettingsTestP,
       AnnotateAndMoveUserBlockedCookies_SameSiteEmbed_FirstPartyContext) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}},
      {*MakeCanonicalCookie("cookie", kURL), {}}};
  net::CookieAccessResultList excluded_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kURL),
       // The ExclusionReason below is irrelevant, as long as there is
       // one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))},
      {*MakeCanonicalCookie("excluded_samesitelax", kURL),
       // The ExclusionReason below is irrelevant, as long as there is
       // one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))},
  };
  url::Origin origin = url::Origin::Create(GURL(kURL));

  // Note that `url` does not match the `top_frame_origin`.
  EXPECT_TRUE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kURL), net::SiteForCookies::FromUrl(GURL(kURL)), &origin,
      net::FirstPartySetMetadata(), GetCookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));

  EXPECT_THAT(
      maybe_included_cookies,
      UnorderedElementsAre(
          MatchesCookieWithAccessResult(
              net::MatchesCookieWithName("cookie"),
              // This cookie really ought to be excluded based on SameSite
              // rules, but those are not enforced by CookieSettings.
              MatchesCookieAccessResult(
                  AllOf(
                      net::IsInclude(),
                      Not(net::HasWarningReason(
                          net::CookieInclusionStatus::WarningReason::
                              WARN_THIRD_PARTY_PHASEOUT)),
                      net::HasExactlyExemptionReason(
                          net::CookieInclusionStatus::ExemptionReason::kNone)),
                  _, _, _)),
          MatchesCookieWithAccessResult(
              net::MatchesCookieWithName("third_party"),
              MatchesCookieAccessResult(
                  AllOf(
                      net::IsInclude(),
                      Not(net::HasWarningReason(
                          net::CookieInclusionStatus::WarningReason::
                              WARN_THIRD_PARTY_PHASEOUT)),
                      net::HasExactlyExemptionReason(
                          net::CookieInclusionStatus::ExemptionReason::kNone)),
                  _, _, _))));

  EXPECT_THAT(excluded_cookies,
              UnorderedElementsAre(
                  MatchesCookieWithAccessResult(
                      net::MatchesCookieWithName("excluded_other"),
                      MatchesCookieAccessResult(
                          net::HasExactlyExclusionReasonsForTesting(
                              {net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}),
                          _, _, _)),
                  MatchesCookieWithAccessResult(
                      net::MatchesCookieWithName("excluded_samesitelax"),
                      MatchesCookieAccessResult(
                          net::HasExactlyExclusionReasonsForTesting(
                              {net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}),
                          _, _, _))));
}

TEST_P(CookieSettingsTestP,
       AnnotateAndMoveUserBlockedCookies_SameSiteEmbed_ThirdPartyContext) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("cookie", kDomainURL), {}},
      {*MakeCanonicalCookie("samesite_lax", kDomainURL), {}},
  };
  net::CookieAccessResultList excluded_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kDomainURL),
       // The ExclusionReason below is irrelevant, as long as there is one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))}};
  url::Origin origin = url::Origin::Create(GURL(kDomainURL));

  const bool expected_any_allowed = IsStorageAccessGrantEligibleViaAPI() ||
                                    IsStorageAccessGrantEligibleViaHeader() ||
                                    IsTPCDEnabled();

  // Note that the site of `url` matches the site of `top_frame_origin`. This is
  // a third-party context for the purposes of third-party-cookie-blocking, even
  // though the request URL and the top-frame URL are same-site with each other.
  EXPECT_EQ(settings.AnnotateAndMoveUserBlockedCookies(
                GURL(kSubDomainURL), net::SiteForCookies(), &origin,
                net::FirstPartySetMetadata(), GetCookieSettingOverrides(),
                maybe_included_cookies, excluded_cookies),
            expected_any_allowed);

  if (IsStorageAccessGrantEligibleViaAPI() ||
      IsStorageAccessGrantEligibleViaHeader()) {
    EXPECT_THAT(
        maybe_included_cookies,
        ElementsAre(
            MatchesCookieWithAccessResult(
                net::MatchesCookieWithName("cookie"),
                MatchesCookieAccessResult(
                    AllOf(net::IsInclude(),
                          Not(net::HasWarningReason(
                              net::CookieInclusionStatus::WarningReason::
                                  WARN_THIRD_PARTY_PHASEOUT)),
                          net::HasExactlyExemptionReason(
                              net::CookieInclusionStatus::ExemptionReason::
                                  kStorageAccess)),
                    _, _, _)),
            MatchesCookieWithAccessResult(
                net::MatchesCookieWithName("samesite_lax"),
                MatchesCookieAccessResult(
                    AllOf(net::IsInclude(),
                          Not(net::HasWarningReason(
                              net::CookieInclusionStatus::WarningReason::
                                  WARN_THIRD_PARTY_PHASEOUT)),
                          net::HasExactlyExemptionReason(
                              net::CookieInclusionStatus::ExemptionReason::
                                  kNone)),
                    _, _, _))));
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("excluded_other"),
                    MatchesCookieAccessResult(
                        net::HasExactlyExclusionReasonsForTesting(
                            {net::CookieInclusionStatus::ExclusionReason::
                                 EXCLUDE_SECURE_ONLY}),
                        _, _, _))));
  } else if (IsTPCDEnabled()) {
    EXPECT_THAT(
        maybe_included_cookies,
        UnorderedElementsAre(MatchesCookieWithAccessResult(
            net::MatchesCookieWithName("samesite_lax"),
            // This cookie really ought to be excluded based on SameSite
            // rules, but since those are not enforced by CookieSettings, and
            // CookieSettings doesn't add 3PCD-related exclusion reasons to
            // non-SameSite=None cookies, we don't expect an exclusion reason
            // for this cookie.
            MatchesCookieAccessResult(
                AllOf(net::IsInclude(),
                      Not(net::HasWarningReason(
                          net::CookieInclusionStatus::WarningReason::
                              WARN_THIRD_PARTY_PHASEOUT)),
                      net::HasExactlyExemptionReason(
                          net::CookieInclusionStatus::ExemptionReason::kNone)),
                _, _, _))));
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("cookie"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_THIRD_PARTY_PHASEOUT}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_other"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY}),
                            _, _, _))));
  } else {
    // 3pc is blocked by user.
    EXPECT_THAT(maybe_included_cookies, IsEmpty());
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("cookie"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("samesite_lax"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_other"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY,
                                 net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _))));
  }
}

TEST_P(CookieSettingsTestP,
       AnnotateAndMoveUserBlockedCookies_SitesInFirstPartySet) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("third_party_but_member",
                                        kRwsMemberURL),
       {}}};
  net::CookieAccessResultList excluded_cookies = {};

  url::Origin origin = url::Origin::Create(GURL(kRwsOwnerURL));
  net::SchemefulSite primary((GURL(kRwsOwnerURL)));

  EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kRwsMemberURL), net::SiteForCookies(), &origin,
      net::FirstPartySetMetadata(
          net::FirstPartySetEntry(primary, net::SiteType::kAssociated),
          net::FirstPartySetEntry(primary, net::SiteType::kPrimary)),
      GetCookieSettingOverrides(), maybe_included_cookies, excluded_cookies));

  EXPECT_EQ(0u, maybe_included_cookies.size());
  EXPECT_THAT(
      excluded_cookies,
      ElementsAre(MatchesCookieWithAccessResult(
          net::MatchesCookieWithName("third_party_but_member"),
          MatchesCookieAccessResult(
              net::HasExactlyExclusionReasonsForTesting(
                  IsForceThirdPartyCookieBlockingFlagEnabled() ||
                          IsTrackingProtectionEnabledFor3pcd()
                      ? net::CookieInclusionStatus::ExclusionReasonBitset{
                                    net::CookieInclusionStatus::
                                        ExclusionReason::EXCLUDE_THIRD_PARTY_PHASEOUT,
                                    net::CookieInclusionStatus::
                                        ExclusionReason::EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET,
                                }
                      :
                            net::CookieInclusionStatus::
                                ExclusionReasonBitset{net::CookieInclusionStatus::
                                                     ExclusionReason::EXCLUDE_USER_PREFERENCES}),
              _, _, _))));
}

TEST_P(
    CookieSettingsTestP,
    AnnotateAndMoveUserBlockedCookies_SitesInFirstPartySet_FirstPartyURLBlocked) {
  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  if (IsTrackingProtectionEnabledFor3pcd()) {
    settings.set_tracking_protection_enabled_for_3pcd(true);
  }
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kRwsOwnerURL, kRwsOwnerURL, CONTENT_SETTING_BLOCK)});

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("third_party_but_member", kRwsMemberURL);

  url::Origin top_frame_origin = url::Origin::Create(GURL(kRwsOwnerURL));

  net::SchemefulSite primary((GURL(kRwsOwnerURL)));
  net::FirstPartySetEntry frame_entry(primary, net::SiteType::kAssociated);
  net::FirstPartySetEntry top_frame_entry(primary, net::SiteType::kPrimary);

  net::CookieAccessResultList maybe_included_cookies = {{*cookie, {}}};
  net::CookieAccessResultList excluded_cookies = {};

  EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kRwsMemberURL), net::SiteForCookies(), &top_frame_origin,
      net::FirstPartySetMetadata(frame_entry, top_frame_entry),
      GetCookieSettingOverrides(), maybe_included_cookies, excluded_cookies));

  EXPECT_EQ(0u, maybe_included_cookies.size());

  EXPECT_THAT(
      excluded_cookies,
      ElementsAre(MatchesCookieWithAccessResult(
          net::MatchesCookieWithName("third_party_but_member"),
          MatchesCookieAccessResult(
              net::HasExactlyExclusionReasonsForTesting(
                  IsForceThirdPartyCookieBlockingFlagEnabled() ||
                          IsTrackingProtectionEnabledFor3pcd()
                      ?
                            net::CookieInclusionStatus::
                                ExclusionReasonBitset{
                                    net::CookieInclusionStatus::
                                                ExclusionReason::EXCLUDE_THIRD_PARTY_PHASEOUT,
                                    net::CookieInclusionStatus::
                                                ExclusionReason::EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET,
                                }
                      :
                            net::CookieInclusionStatus::
                                ExclusionReasonBitset{net::CookieInclusionStatus::
                                                     ExclusionReason::EXCLUDE_USER_PREFERENCES}),
              _, _, _))));
}

namespace {

net::CookieAccessResultList MakePartitionedCookie() {
  return {
      {*MakeCanonicalCookie(
           "__Host-partitioned", kURL,
           net::CookiePartitionKey::FromURLForTesting(GURL(kOtherURL))),
       {}},
  };
}

}  // namespace

TEST_F(CookieSettingsTest,
       AnnotateAndMoveUserBlockedCookies_PartitionedCookies) {
  CookieSettings settings;

  net::CookieAccessResultList maybe_included_cookies = MakePartitionedCookie();
  net::CookieAccessResultList excluded_cookies = {};

  url::Origin top_level_origin = url::Origin::Create(GURL(kOtherURL));

  // If 3PC blocking is enabled and there are no site-specific content settings
  // then partitioned cookies should be allowed.
  settings.set_block_third_party_cookies(true);
  EXPECT_TRUE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kURL), net::SiteForCookies(), &top_level_origin,
      net::FirstPartySetMetadata(), net::CookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));
  EXPECT_THAT(maybe_included_cookies,
              ElementsAre(MatchesCookieWithAccessResult(
                  net::MatchesCookieWithName("__Host-partitioned"),
                  MatchesCookieAccessResult(net::IsInclude(), _, _, _))));
  EXPECT_THAT(excluded_cookies, IsEmpty());

  // If there is a site-specific content setting blocking cookies, then
  // partitioned cookies should not be allowed.
  maybe_included_cookies = MakePartitionedCookie();
  excluded_cookies = {};
  settings.set_block_third_party_cookies(false);
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kURL), net::SiteForCookies(), &top_level_origin,
      net::FirstPartySetMetadata(), net::CookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));
  EXPECT_THAT(maybe_included_cookies, IsEmpty());
  EXPECT_THAT(excluded_cookies,
              UnorderedElementsAre(MatchesCookieWithAccessResult(
                  net::MatchesCookieWithName("__Host-partitioned"),
                  MatchesCookieAccessResult(
                      net::HasExclusionReason(
                          net::CookieInclusionStatus::ExclusionReason::
                              EXCLUDE_USER_PREFERENCES),
                      _, _, _))));

  // If there is a site-specific content setting blocking cookies on the
  // current origin, then partitioned cookies should not be allowed.
  maybe_included_cookies = MakePartitionedCookie();
  excluded_cookies = {};
  settings.set_block_third_party_cookies(true);
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
  EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kURL), net::SiteForCookies(), &top_level_origin,
      net::FirstPartySetMetadata(), net::CookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));

  {
    EXPECT_THAT(maybe_included_cookies, IsEmpty());
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("__Host-partitioned"),
                    MatchesCookieAccessResult(
                        net::HasExclusionReason(
                            net::CookieInclusionStatus::ExclusionReason::
                                EXCLUDE_USER_PREFERENCES),
                        _, _, _))));
  }

  // If there is a site-specific content setting blocking cookies on the
  // current top-level origin but only when it is embedded on an unrelated site,
  // then partitioned cookies should still be allowed.
  maybe_included_cookies = MakePartitionedCookie();
  excluded_cookies = {};
  settings.set_block_third_party_cookies(true);
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting(kOtherURL, kUnrelatedURL, CONTENT_SETTING_BLOCK)});
  EXPECT_TRUE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kURL), net::SiteForCookies(), &top_level_origin,
      net::FirstPartySetMetadata(), net::CookieSettingOverrides(),
      maybe_included_cookies, excluded_cookies));
  EXPECT_THAT(maybe_included_cookies,
              ElementsAre(MatchesCookieWithAccessResult(
                  net::MatchesCookieWithName("__Host-partitioned"),
                  MatchesCookieAccessResult(net::IsInclude(), _, _, _))));
  EXPECT_THAT(excluded_cookies, IsEmpty());
}

TEST_F(CookieSettingsTest, GetStorageAccessStatus) {
  // TODO(crbug.com/382291442): Remove `scoped_feature_list` once PP launched.
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatures(
      /*enabled_features=*/
      {network::features::kPopulatePermissionsPolicyOnRequest,
       network::features::kStorageAccessHeadersRespectPermissionsPolicy},
      /*disabled_features=*/{});

  CookieSettings settings;
  GURL url(kURL);
  url::Origin top_frame_origin = url::Origin::Create(GURL(kOtherURL));
  settings.set_block_third_party_cookies(true);

  url::Origin origin = url::Origin::Create(url);
  const std::unique_ptr<network::PermissionsPolicy>
      allowing_permissions_policy =
          network::PermissionsPolicy::CreateFromParsedPolicy(
              {{{network::mojom::PermissionsPolicyFeature::kStorageAccessAPI,
                 /*allowed_origins=*/
                 {*network::OriginWithPossibleWildcards::FromOrigin(origin)},
                 /*self_if_matches=*/std::nullopt,
                 /*matches_all_origins=*/false,
                 /*matches_opaque_src=*/false}}},
              std::nullopt, origin);

  EXPECT_EQ(
      settings.GetStorageAccessStatus(url, net::SiteForCookies::FromUrl(url),
                                      origin, net::CookieSettingOverrides(),
                                      /*cookie_partition_key=*/std::nullopt,
                                      *allowing_permissions_policy),
      std::nullopt);

  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies::FromUrl(url), origin,
          net::CookieSettingOverrides(
              {net::CookieSettingOverride::kStorageAccessGrantEligible}),
          /*cookie_partition_key=*/std::nullopt, *allowing_permissions_policy),
      std::nullopt);

  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt,
          *allowing_permissions_policy),
      net::cookie_util::StorageAccessStatus::kNone);

  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(
              {net::CookieSettingOverride::kStorageAccessGrantEligible}),
          /*cookie_partition_key=*/std::nullopt, *allowing_permissions_policy),
      net::cookie_util::StorageAccessStatus::kNone);

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});

  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt,
          *allowing_permissions_policy),
      net::cookie_util::StorageAccessStatus::kInactive);

  const std::unique_ptr<network::PermissionsPolicy>
      blocking_permissions_policy =
          network::PermissionsPolicy::CreateFromParsedPolicy(
              {{{network::mojom::PermissionsPolicyFeature::kStorageAccessAPI,
                 /*allowed_origins=*/{},
                 /*self_if_matches=*/std::nullopt,
                 /*matches_all_origins=*/false,
                 /*matches_opaque_src=*/false}}},
              std::nullopt, origin);
  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt,
          *blocking_permissions_policy),
      net::cookie_util::StorageAccessStatus::kNone);

  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(
              {net::CookieSettingOverride::kStorageAccessGrantEligible}),
          /*cookie_partition_key=*/std::nullopt, *allowing_permissions_policy),
      net::cookie_util::StorageAccessStatus::kActive);

  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(
              {net::CookieSettingOverride::
                   kStorageAccessGrantEligibleViaHeader}),
          /*cookie_partition_key=*/std::nullopt, *allowing_permissions_policy),
      net::cookie_util::StorageAccessStatus::kActive);

  net::CookiePartitionKey cookie_partition_key =
      net::CookiePartitionKey::FromURLForTesting(
          GURL(kOtherURL),
          net::CookiePartitionKey::AncestorChainBit::kCrossSite,
          base::UnguessableToken::Create());

  EXPECT_EQ(settings.GetStorageAccessStatus(
                url, net::SiteForCookies(), top_frame_origin,
                net::CookieSettingOverrides(), cookie_partition_key,
                *allowing_permissions_policy),
            net::cookie_util::StorageAccessStatus::kNone);

  EXPECT_EQ(settings.GetStorageAccessStatus(
                url, net::SiteForCookies(), top_frame_origin,
                net::CookieSettingOverrides(
                    {net::CookieSettingOverride::kStorageAccessGrantEligible}),
                cookie_partition_key, *allowing_permissions_policy),
            net::cookie_util::StorageAccessStatus::kNone);

  EXPECT_EQ(settings.GetStorageAccessStatus(
                url, net::SiteForCookies(), top_frame_origin,
                net::CookieSettingOverrides(
                    {net::CookieSettingOverride::
                         kStorageAccessGrantEligibleViaHeader}),
                cookie_partition_key, *allowing_permissions_policy),
            net::cookie_util::StorageAccessStatus::kNone);
}

// TODO(crbug.com/382291442): Remove test once feature is launched.
TEST_F(CookieSettingsTest,
       GetStorageAccessStatus_RespectPermissionsPolicyDisabled) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatures(
      /*enabled_features=*/
      {network::features::kPopulatePermissionsPolicyOnRequest},
      /*disabled_features=*/{
          network::features::kStorageAccessHeadersRespectPermissionsPolicy});

  CookieSettings settings;
  GURL url(kURL);
  url::Origin top_frame_origin = url::Origin::Create(GURL(kOtherURL));
  settings.set_block_third_party_cookies(true);

  settings.set_content_settings(
      ContentSettingsType::STORAGE_ACCESS,
      {CreateSetting(kURL, kOtherURL, CONTENT_SETTING_ALLOW)});

  const std::unique_ptr<network::PermissionsPolicy>
      blocking_permissions_policy =
          network::PermissionsPolicy::CreateFromParsedPolicy(
              {{{network::mojom::PermissionsPolicyFeature::kStorageAccessAPI,
                 /*allowed_origins=*/{},
                 /*self_if_matches=*/std::nullopt,
                 /*matches_all_origins=*/false,
                 /*matches_opaque_src=*/false}}},
              std::nullopt, url::Origin::Create(url));
  EXPECT_EQ(
      settings.GetStorageAccessStatus(
          url, net::SiteForCookies(), top_frame_origin,
          net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt,
          *blocking_permissions_policy),
      net::cookie_util::StorageAccessStatus::kInactive);
}

TEST_F(CookieSettingsTest,
       IsCookieAccessible_SandboxValueAllowsSameSiteNoneCookiesWhenSet) {
  CookieSettings settings;
  GURL url(kURL);
  url::Origin origin = url::Origin::Create(url);
  settings.set_block_third_party_cookies(true);
  base::HistogramTester histogram_tester;

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", url.spec());

  // Cookie should be blocked by 3PC Blocking
  ASSERT_FALSE(settings.IsCookieAccessible(
      *cookie, url, net::SiteForCookies(), origin, net::FirstPartySetMetadata(),
      net::CookieSettingOverrides(), nullptr));
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_BLOCKED, 1);

  // Set overrides and enable feature.
  const net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox});
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      net::features::kAllowSameSiteNoneCookiesInSandbox);

  // Override should allow cookie access despite null SiteForCookies due to the
  // sandboxed context.
  net::CookieInclusionStatus status;
  EXPECT_TRUE(settings.IsCookieAccessible(*cookie, url, net::SiteForCookies(),
                                          origin, net::FirstPartySetMetadata(),
                                          overrides, &status));
  EXPECT_EQ(status.exemption_reason(),
            net::CookieInclusionStatus::ExemptionReason::
                kSameSiteNoneCookiesInSandbox);
  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_ALLOWED_SANDBOX_VALUE, 1);
}

TEST_F(CookieSettingsTest,
       IsCookieAccessible_SandboxValueDoesNotAllowCrossSiteCookiesWhenSet) {
  CookieSettings settings;
  GURL cross_site_url(kOtherURL);
  url::Origin origin = url::Origin::Create(GURL(kURL));
  settings.set_block_third_party_cookies(true);
  base::HistogramTester histogram_tester;

  // Create a cross-site cookie
  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", cross_site_url.spec());

  // Set overrides and enable feature.
  const net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox});
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      net::features::kAllowSameSiteNoneCookiesInSandbox);

  // Override should not allow cookie that is cross-site with the top-level to
  // be included in cross-site request
  net::CookieInclusionStatus status;
  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, cross_site_url, net::SiteForCookies(), origin,
      net::FirstPartySetMetadata(), overrides, &status));
  EXPECT_EQ(status.exemption_reason(),
            net::CookieInclusionStatus::ExemptionReason::kNone);
  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_BLOCKED, 1);
}

// NOTE: These tests will fail if their FINAL name is of length greater than 256
// characters. Thus, try to avoid (unnecessary) generalized parameterization
// when possible.
std::string CustomTestName(
    const testing::TestParamInfo<CookieSettingsTestP::ParamType>& info) {
  std::stringstream custom_test_name;
  // clang-format off
  custom_test_name
      << "GrantSource_"
      << std::get<TestVariables::kGrantSource>(info.param)
      << "_BlockSource_"
      << std::get<TestVariables::kBlockSource>(info.param);
  // clang-format on
  return custom_test_name.str();
}

INSTANTIATE_TEST_SUITE_P(
    /* no prefix */,
    CookieSettingsTestP,
    testing::Combine(testing::Range(GrantSource::kNoneGranted,
                                    GrantSource::kGrantSourceCount),
                     testing::Range(BlockSource::kNoneBlocked,
                                    BlockSource::kBlockSourceCount)),
    CustomTestName);

class CookieSettingsTpcdMetadataGrantsTest
    : public CookieSettingsTestBase,
      public testing::WithParamInterface</* net::features::kTpcdMetadataGrants:
                                          */
                                         bool> {
 public:
  CookieSettingsTpcdMetadataGrantsTest() {
    std::vector<base::test::FeatureRef> enabled_features;
    std::vector<base::test::FeatureRef> disabled_features;

    if (IsTpcdMetadataGrantEligible()) {
      enabled_features.push_back(net::features::kTpcdMetadataGrants);
    } else {
      disabled_features.push_back(net::features::kTpcdMetadataGrants);
    }

    feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

  bool IsTpcdMetadataGrantEligible() const { return GetParam(); }

  net::CookieSettingOverrides GetCookieSettingOverrides() const {
    net::CookieSettingOverrides overrides;
    return overrides;
  }

  ContentSetting SettingWith3pcdMetadataGrantEligibleOverride() const {
    return IsTpcdMetadataGrantEligible() ? CONTENT_SETTING_ALLOW
                                         : CONTENT_SETTING_BLOCK;
  }

  // The storage access result would be blocked if not for a
  // `net::features::kTpcdMetadataGrants` enablement.
  net::cookie_util::StorageAccessResult
  BlockedStorageAccessResultWith3pcdMetadataGrantOverride() const {
    if (IsTpcdMetadataGrantEligible()) {
      return net::cookie_util::StorageAccessResult::
          ACCESS_ALLOWED_3PCD_METADATA_GRANT;
    }
    return net::cookie_util::StorageAccessResult::ACCESS_BLOCKED;
  }
};

TEST_P(CookieSettingsTpcdMetadataGrantsTest, Grants) {
  GURL first_party_url = GURL(kURL);
  GURL third_party_url_1 = GURL(kOtherURL);
  GURL third_party_url_2 = GURL(kDomainURL);

  base::HistogramTester histogram_tester;

  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  settings.set_mitigations_enabled_for_3pcd(true);

  // Precautionary - ensures that a default cookie setting is specified.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants(
      {CreateSetting(third_party_url_1.GetHost(), first_party_url.GetHost(),
                     CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);

  EXPECT_EQ(settings.GetCookieSetting(third_party_url_1, net::SiteForCookies(),
                                      first_party_url,
                                      GetCookieSettingOverrides(), nullptr),
            SettingWith3pcdMetadataGrantEligibleOverride());

  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      BlockedStorageAccessResultWith3pcdMetadataGrantOverride(), 1);

  EXPECT_EQ(settings.GetCookieSetting(first_party_url, net::SiteForCookies(),
                                      third_party_url_1,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);

  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_ALLOWED_3PCD_METADATA_GRANT,
      IsTpcdMetadataGrantEligible() ? 1 : 0);
  histogram_tester.ExpectBucketCount(
      kAllowedRequestsHistogram,
      BlockedStorageAccessResultWith3pcdMetadataGrantOverride(),
      IsTpcdMetadataGrantEligible() ? 1 : 2);

  EXPECT_EQ(settings.GetCookieSetting(third_party_url_2, net::SiteForCookies(),
                                      first_party_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest, IsCookieAccessible) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  settings.set_block_third_party_cookies(true);
  settings.set_mitigations_enabled_for_3pcd(true);

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);

  EXPECT_EQ(settings.IsCookieAccessible(
                *cookie, GURL(kOtherURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
                GetCookieSettingOverrides(), &status),
            IsTpcdMetadataGrantEligible());
  EXPECT_EQ(status.exemption_reason() ==
                net::CookieInclusionStatus::ExemptionReason::k3PCDMetadata,
            IsTpcdMetadataGrantEligible());
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest,
       IsCookieAccessible_OverridesLimitTPC) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations});

  // Third-party cookie is not blocked by user or browser config.
  settings.set_block_third_party_cookies(false);

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);

  EXPECT_EQ(settings.IsCookieAccessible(
                *cookie, GURL(kOtherURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
                overrides, &status),
            IsTpcdMetadataGrantEligible());
  EXPECT_EQ(status.exemption_reason() ==
                net::CookieInclusionStatus::ExemptionReason::k3PCDMetadata,
            IsTpcdMetadataGrantEligible());
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest,
       IsCookieAccessible_OverridesDisableMetadata) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations,
       net::CookieSettingOverride::kSkipTPCDMetadataGrant});

  // Third-party cookie is not blocked by user or browser config.
  settings.set_block_third_party_cookies(false);

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie("name", kOtherURL);

  EXPECT_FALSE(settings.IsCookieAccessible(
      *cookie, GURL(kOtherURL), net::SiteForCookies(),
      url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(), overrides,
      &status));

  EXPECT_TRUE(
      status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
                                    EXCLUDE_THIRD_PARTY_PHASEOUT));
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest,
       AnnotateAndMoveUserBlockedCookies) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  settings.set_block_third_party_cookies(true);
  settings.set_mitigations_enabled_for_3pcd(true);

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("third_party", kOtherURL), {}}};
  net::CookieAccessResultList excluded_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kOtherURL),
       // The ExclusionReason below is irrelevant, as long as there is one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))}};

  url::Origin origin = url::Origin::Create(GURL(kURL));

  // Note that `url` does not match the `top_frame_origin`.
  EXPECT_EQ(settings.AnnotateAndMoveUserBlockedCookies(
                GURL(kOtherURL), net::SiteForCookies(), &origin,
                net::FirstPartySetMetadata(), GetCookieSettingOverrides(),
                maybe_included_cookies, excluded_cookies),
            IsTpcdMetadataGrantEligible());

  if (IsTpcdMetadataGrantEligible()) {
    EXPECT_THAT(maybe_included_cookies,
                ElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("third_party"),
                    MatchesCookieAccessResult(
                        AllOf(net::IsInclude(),
                              net::HasExactlyExemptionReason(
                                  net::CookieInclusionStatus::ExemptionReason::
                                      k3PCDMetadata)),
                        _, _, _))));
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("excluded_other"),
                    MatchesCookieAccessResult(
                        net::HasExactlyExclusionReasonsForTesting(
                            {net::CookieInclusionStatus::ExclusionReason::
                                 EXCLUDE_SECURE_ONLY}),
                        _, _, _))));
  } else {
    EXPECT_THAT(maybe_included_cookies, IsEmpty());
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_other"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY,
                                 net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("third_party"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_USER_PREFERENCES}),
                            _, _, _))));
  }
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest,
       AnnotateAndMoveUserBlockedCookies_OverridesLimitTPC) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations});

  // Third-party cookie is not blocked by user or browser config.
  settings.set_block_third_party_cookies(false);

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("third_party", kOtherURL), {}}};
  net::CookieAccessResultList excluded_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kOtherURL),
       // The ExclusionReason below is irrelevant, as long as there is one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))}};

  url::Origin origin = url::Origin::Create(GURL(kURL));

  // Note that `url` does not match the `top_frame_origin`.
  EXPECT_EQ(settings.AnnotateAndMoveUserBlockedCookies(
                GURL(kOtherURL), net::SiteForCookies(), &origin,
                net::FirstPartySetMetadata(), overrides, maybe_included_cookies,
                excluded_cookies),
            IsTpcdMetadataGrantEligible());

  if (IsTpcdMetadataGrantEligible()) {
    EXPECT_THAT(maybe_included_cookies,
                ElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("third_party"),
                    MatchesCookieAccessResult(
                        AllOf(net::IsInclude(),
                              net::HasExactlyExemptionReason(
                                  net::CookieInclusionStatus::ExemptionReason::
                                      k3PCDMetadata)),
                        _, _, _))));
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName("excluded_other"),
                    MatchesCookieAccessResult(
                        net::HasExactlyExclusionReasonsForTesting(
                            {net::CookieInclusionStatus::ExclusionReason::
                                 EXCLUDE_SECURE_ONLY}),
                        _, _, _))));
  } else {
    EXPECT_THAT(maybe_included_cookies, IsEmpty());
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("excluded_other"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_SECURE_ONLY}),
                            _, _, _)),
                    MatchesCookieWithAccessResult(
                        net::MatchesCookieWithName("third_party"),
                        MatchesCookieAccessResult(
                            net::HasExactlyExclusionReasonsForTesting(
                                {net::CookieInclusionStatus::ExclusionReason::
                                     EXCLUDE_THIRD_PARTY_PHASEOUT}),
                            _, _, _))));
  }
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest,
       AnnotateAndMoveUserBlockedCookies_OverridesDisableMetadata) {
  CookieSettings settings;
  net::CookieInclusionStatus status;
  net::CookieSettingOverrides overrides(
      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations,
       net::CookieSettingOverride::kSkipTPCDMetadataGrant});

  // Third-party cookie is not blocked by user or browser config.
  settings.set_block_third_party_cookies(false);

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants({CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("third_party", kOtherURL), {}}};
  net::CookieAccessResultList excluded_cookies = {
      {*MakeCanonicalSameSiteNoneCookie("excluded_other", kOtherURL),
       // The ExclusionReason below is irrelevant, as long as there is one.
       net::CookieAccessResult(
           net::CookieInclusionStatus::MakeFromReasonsForTesting(
               /*exclusions=*/{net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}))}};

  url::Origin origin = url::Origin::Create(GURL(kURL));

  // Note that `url` does not match the `top_frame_origin`.
  EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
      GURL(kOtherURL), net::SiteForCookies(), &origin,
      net::FirstPartySetMetadata(), overrides, maybe_included_cookies,
      excluded_cookies));

  EXPECT_THAT(maybe_included_cookies, IsEmpty());
  EXPECT_THAT(excluded_cookies,
              UnorderedElementsAre(
                  MatchesCookieWithAccessResult(
                      net::MatchesCookieWithName("excluded_other"),
                      MatchesCookieAccessResult(
                          net::HasExactlyExclusionReasonsForTesting(
                              {net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_SECURE_ONLY}),
                          _, _, _)),
                  MatchesCookieWithAccessResult(
                      net::MatchesCookieWithName("third_party"),
                      MatchesCookieAccessResult(
                          net::HasExactlyExclusionReasonsForTesting(
                              {net::CookieInclusionStatus::ExclusionReason::
                                   EXCLUDE_THIRD_PARTY_PHASEOUT}),
                          _, _, _))));
}

TEST_P(CookieSettingsTpcdMetadataGrantsTest, ExplicitSettingPreserved) {
  GURL first_party_url = GURL(kURL);
  GURL third_party_url = GURL(kOtherURL);
  base::HistogramTester histogram_tester;

  CookieSettings settings;
  settings.set_block_third_party_cookies(true);
  settings.set_mitigations_enabled_for_3pcd(true);

  // Precautionary - ensures that a default cookie setting is specified.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", "*", CONTENT_SETTING_ALLOW)});

  // Explicit setting.
  settings.set_content_settings(
      ContentSettingsType::COOKIES,
      {CreateSetting("*", first_party_url.GetHost(), CONTENT_SETTING_BLOCK)});

  // Allowlisting.
  network::tpcd::metadata::Manager manager;
  manager.SetGrants(
      {CreateSetting(third_party_url.GetHost(), first_party_url.GetHost(),
                     CONTENT_SETTING_ALLOW)});
  settings.set_tpcd_metadata_manager(&manager);

  histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);

  EXPECT_EQ(settings.GetCookieSetting(third_party_url, net::SiteForCookies(),
                                      first_party_url,
                                      GetCookieSettingOverrides(), nullptr),
            CONTENT_SETTING_BLOCK);

  histogram_tester.ExpectUniqueSample(
      kAllowedRequestsHistogram,
      net::cookie_util::StorageAccessResult::ACCESS_BLOCKED, 1);
}

INSTANTIATE_TEST_SUITE_P(/* no prefix */,
                         CookieSettingsTpcdMetadataGrantsTest,
                         testing::Bool());

struct CookieSettingsForceEnableOverrideTestData {
  std::vector<ContentSettingsType> settings_types;
  net::CookieSettingOverrides overrides;
  bool cookie_access_allowed;
  bool warning_reason_shown;
};
class CookieSettingsForceEnableOverrideTest
    : public CookieSettingsTestBase,
      public testing::WithParamInterface<
          CookieSettingsForceEnableOverrideTestData> {
 public:
  CookieSettingsForceEnableOverrideTest() {
    for (const auto& val : GetParam().settings_types) {
      settings_.set_content_settings(
          val, {CreateSetting(kOtherURL, kURL, CONTENT_SETTING_ALLOW)});
    }
  }

  net::CookieSettingOverrides overrides() const { return GetParam().overrides; }

  bool cookie_access_allowed() const {
    return GetParam().cookie_access_allowed;
  }
  bool warning_reason_shown() const { return GetParam().warning_reason_shown; }

  std::vector<ContentSettingsType> settings_types() const {
    return GetParam().settings_types;
  }

  CookieSettings settings_;
};

INSTANTIATE_TEST_SUITE_P(
    /* no label */,
    CookieSettingsForceEnableOverrideTest,
    testing::ValuesIn(
        {/*No overrides or settings*/
         CookieSettingsForceEnableOverrideTestData{
             .settings_types = {},
             .overrides = {},
             .cookie_access_allowed = false,
             .warning_reason_shown = false,
         },
         /*Override applied. Cookie should be allowed with warning reason*/
         CookieSettingsForceEnableOverrideTestData{
             .settings_types = {},
             .overrides =
                 {net::CookieSettingOverride::kForceEnableThirdPartyCookies},
             .cookie_access_allowed = true,
             .warning_reason_shown = true,
         },
         /*Override and setting applied. Explicit setting should mean no
            warning*/
         CookieSettingsForceEnableOverrideTestData{
             .settings_types = {ContentSettingsType::COOKIES},
             .overrides =
                 {net::CookieSettingOverride::kForceEnableThirdPartyCookies},
             .cookie_access_allowed = true,
             .warning_reason_shown = false,
         }}));

TEST_P(CookieSettingsForceEnableOverrideTest, IsCookieAccessible) {
  settings_.set_tracking_protection_enabled_for_3pcd(true);
  std::unique_ptr<net::CanonicalCookie> cookie =
      MakeCanonicalSameSiteNoneCookie(kCookieName, kOtherURL);

  net::CookieInclusionStatus status;
  EXPECT_EQ(settings_.IsCookieAccessible(
                *cookie, GURL(kOtherURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
                overrides(), &status),
            cookie_access_allowed());
  EXPECT_EQ(
      status.HasWarningReason(
          net::CookieInclusionStatus::WarningReason::WARN_THIRD_PARTY_PHASEOUT),
      warning_reason_shown());
}

TEST_P(CookieSettingsForceEnableOverrideTest,
       AnnotateAndMoveUserBlockedCookies) {
  settings_.set_tracking_protection_enabled_for_3pcd(true);

  net::CookieAccessResultList maybe_included_cookies = {
      {*MakeCanonicalSameSiteNoneCookie(kCookieName, kOtherURL)}};
  net::CookieAccessResultList excluded_cookies = {};

  ASSERT_EQ(settings_.AnnotateAndMoveUserBlockedCookies(
                GURL(kOtherURL), net::SiteForCookies(),
                url::Origin::Create(GURL(kURL)), net::FirstPartySetMetadata(),
                overrides(), maybe_included_cookies, excluded_cookies),
            cookie_access_allowed());

  if (cookie_access_allowed()) {
    EXPECT_THAT(
        maybe_included_cookies,
        UnorderedElementsAre(MatchesCookieWithAccessResult(
            net::MatchesCookieWithName(kCookieName),
            MatchesCookieAccessResult(
                AllOf(
                    net::IsInclude(),
                    net::HasExactlyWarningReasonsForTesting(
                        warning_reason_shown()
                            ? net::CookieInclusionStatus::
                                  WarningReasonBitset{net::CookieInclusionStatus::
                                                          WarningReason::
                                                              WARN_THIRD_PARTY_PHASEOUT}
                            : net::CookieInclusionStatus::
                                  WarningReasonBitset{}),
                    net::HasExactlyExemptionReason(
                        net::CookieInclusionStatus::ExemptionReason::kNone)),
                _, _, _))));
    EXPECT_THAT(excluded_cookies, IsEmpty());
  } else {
    EXPECT_THAT(maybe_included_cookies, IsEmpty());
    EXPECT_THAT(excluded_cookies,
                UnorderedElementsAre(MatchesCookieWithAccessResult(
                    net::MatchesCookieWithName(kCookieName), _)));
  }
}

}  // namespace
}  // namespace network