// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ssl/https_first_mode_settings_tracker.h"

#include "base/json/values_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
#include "chrome/browser/ssl/https_upgrades_util.h"
#include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_test_utils.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
#include "components/security_interstitials/core/https_only_mode_metrics.h"
#include "components/site_engagement/content/site_engagement_score.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/site_engagement/core/pref_names.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

using security_interstitials::https_only_mode::
    kSiteEngagementHeuristicAccumulatedHostCountHistogram;
using security_interstitials::https_only_mode::
    kSiteEngagementHeuristicEnforcementDurationHistogram;
using security_interstitials::https_only_mode::
    kSiteEngagementHeuristicHostCountHistogram;
using security_interstitials::https_only_mode::
    kSiteEngagementHeuristicStateHistogram;
using security_interstitials::https_only_mode::SiteEngagementHeuristicState;

// Tests for HTTPS First Mode settings, such as enabling HFM through Site
// Engagement scores.
//
// Most of these tests don't need to enable the feature flags for HFM heuristics
// because they call the feature-flag gated functions directly (
// MaybeEnableHttpsFirstModeForEngagedSitesAndWait() and
// CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstMode()).
class HttpsFirstModeSettingsTrackerTest : public testing::Test {
 protected:
  void SetUp() override {
    TestingProfile::Builder builder;
    builder.AddTestingFactory(
        StatefulSSLHostStateDelegateFactory::GetInstance(),
        StatefulSSLHostStateDelegateFactory::GetDefaultFactoryForTesting());
    builder.AddTestingFactory(
        safe_browsing::AdvancedProtectionStatusManagerFactory::GetInstance(),
        safe_browsing::AdvancedProtectionStatusManagerFactory::
            GetDefaultFactoryForTesting());
    builder.AddTestingFactory(
        HttpsFirstModeServiceFactory::GetInstance(),
        HttpsFirstModeServiceFactory::GetDefaultFactoryForTesting());
    profile_ = IdentityTestEnvironmentProfileAdaptor::
        CreateProfileForIdentityTestEnvironment(builder);

    ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(false);
  }

  void TearDown() override {
    HttpsFirstModeServiceFactory::SetClockForTesting(nullptr);
  }

  void SetSiteEngagementScoreForTypicallySecureUserHeuristic() {
    // Typically Secure User heuristic requires a minimum total site engagement
    // score.
    site_engagement::SiteEngagementService* engagement_service =
        site_engagement::SiteEngagementService::Get(profile());
    ASSERT_TRUE(engagement_service);
    engagement_service->ResetBaseScoreForURL(GURL("https://google.com"), 90);
  }

  TestingProfile* profile() { return profile_.get(); }
  base::test::ScopedFeatureList* feature_list() { return &feature_list_; }

  content::BrowserTaskEnvironment task_environment_;

 private:
  std::unique_ptr<TestingProfile> profile_;
  base::test::ScopedFeatureList feature_list_;
};

class HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest
    : public HttpsFirstModeSettingsTrackerTest {
 public:
  HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest() {
    feature_list()->InitWithFeatures(
        /*enabled_features=*/{features::kHttpsFirstModeV2ForEngagedSites,
                              features::kHttpsFirstBalancedMode},
        /*disabled_features=*/{});
  }
};

void MaybeEnableHttpsFirstModeForEngagedSitesAndWait(
    HttpsFirstModeService* hfm_service) {
  base::RunLoop run_loop;
  hfm_service->MaybeEnableHttpsFirstModeForEngagedSites(run_loop.QuitClosure());
  run_loop.Run();
}

// Check that changing the HFM pref clears Site Engagement heuristic's HTTPS
// enforcelist and effectively disables the heuristic.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       ShouldNotEnableHeuristicIfPrefIsSet) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  auto clock = std::make_unique<base::SimpleTestClock>();
  auto* clock_ptr = clock.get();
  state->SetClockForTesting(std::move(clock));
  clock_ptr->SetNow(base::Time::NowFromSystemTime());

  // Site Engagement heuristic should enforce HTTPS on hosts with high
  // engagement score if Balanced Mode is available.
  GURL https_url("https://example.com");
  engagement_service->ResetBaseScoreForURL(https_url, 90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);

  EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
      GURL("https://example.com"), profile()->GetDefaultStoragePartition()));

  // Disable HFM. This should clear the enforcelist.
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, false);
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("https://example.com"), profile()->GetDefaultStoragePartition()));

  // Check again. Should remain unenforced.
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("https://example.com"), profile()->GetDefaultStoragePartition()));
}

// Check that high site engagement scores of HTTPS URLs with non-default ports
// do not auto-enable HTTPS-First Balanced Mode.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       ShouldIgnoreEngagementScoreOfUrlWithNonDefaultPort) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;
  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  // HFM should initially be disabled on this site by default.
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);

  // Increase the score. Since the URL has a non-default port, HFM should remain
  // disabled.
  engagement_service->ResetBaseScoreForURL(GURL("https://example.com:8443"),
                                           90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
}

// Check that Site Engagement heuristic is never enabled on enterprise
// managed devices.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       EnterpriseManaged_ShouldNotEnable) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;
  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(true);

  // Set the https score high. HTTPS shouldn't be enforced because the device
  // is managed.
  engagement_service->ResetBaseScoreForURL(GURL("https://example.com"), 90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);

  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
}

TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       BalancedModeExceptions) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;
  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  auto clock = std::make_unique<base::SimpleTestClock>();
  auto* clock_ptr = clock.get();
  state->SetClockForTesting(std::move(clock));
  clock_ptr->SetNow(base::Time::NowFromSystemTime());

  // HTTPS-First Balanced Mode should ignore non-unique hostnames.
  GURL https_url("https://site.test");
  GURL http_url("http://site.test");

  // HFM should initially be disabled on this site by default.
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      http_url, profile()->GetDefaultStoragePartition()));
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);

  // Increase the score. This should still not enable HFM on site.test.
  engagement_service->ResetBaseScoreForURL(https_url, 90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);

  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      http_url, profile()->GetDefaultStoragePartition()));
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);
}

TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       ShouldEnforceHttps) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;
  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  auto clock = std::make_unique<base::SimpleTestClock>();
  auto* clock_ptr = clock.get();
  state->SetClockForTesting(std::move(clock));
  clock_ptr->SetNow(base::Time::NowFromSystemTime());

  GURL https_url("https://example.com");
  GURL http_url("http://example.com");

  // Step 1: HFM should initially be disabled on this site by default.
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_FALSE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 0);
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 0);
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);

  // Step 2: Increase the score, should now enable HFM.
  engagement_service->ResetBaseScoreForURL(https_url, 90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_TRUE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_TRUE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  // Check events.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kEnabled, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kDisabled, 0);
  // Check host counts.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/1, /*expected_count=*/1);
  // Check accumulated host counts.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 1);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/1, /*expected_count=*/1);
  // Check enforcement durations. Enforcement duration is only recorded when a
  // host is removed from the list.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);

  // Subdomains shouldn't be affected.
  EXPECT_FALSE(
      state->IsHttpsEnforcedForUrl(GURL("http://test.example.com"),
                                   profile()->GetDefaultStoragePartition()));

  // Step 3: Decrease the score, but only slightly. This shouldn't result in HFM
  // being disabled.
  engagement_service->ResetBaseScoreForURL(https_url, 85);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_TRUE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_TRUE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  // Check events.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kEnabled, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kDisabled, 0);
  // Check host counts.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/1, /*expected_count=*/1);
  // Check accumulated host counts.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 1);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/1, /*expected_count=*/1);
  // Check enforcement durations. Enforcement duration is only recorded when a
  // host is removed from the list.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 0);

  // Step 4: Decrease the score further. This should result in HFM being
  // disabled. Also move the time forward.
  clock_ptr->Advance(base::Hours(1));
  engagement_service->ResetBaseScoreForURL(https_url, 25);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_FALSE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  // Check events.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kEnabled, 1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kDisabled, 1);
  // Check host counts.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/0, /*expected_count=*/1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/1, /*expected_count=*/1);
  // Check accumulated host counts.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 2);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/0, /*expected_count=*/0);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/1, /*expected_count=*/2);
  // Check enforcement durations.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 1);
  histograms.ExpectTimeBucketCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);

  // Step 5: Increase the score again and re-enable HFM.
  clock_ptr->Advance(base::Hours(2));
  engagement_service->ResetBaseScoreForURL(https_url, 90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_TRUE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_TRUE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  // Check state.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 3);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kEnabled, 2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kDisabled, 1);
  // Check host counts.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 3);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/0, /*expected_count=*/1);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/1, /*expected_count=*/2);
  // Check accumulated host counts.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 3);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/0, /*expected_count=*/0);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/1, /*expected_count=*/3);
  // Check enforcement durations.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 1);
  histograms.ExpectTimeBucketCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);

  // Step 6: Also increase the HTTP score. This should disable HFM even though
  // the HTTPS score is still high.
  engagement_service->ResetBaseScoreForURL(http_url, 20);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_FALSE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  // Check state.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 4);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kEnabled, 2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kDisabled, 2);
  // Check host count.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 4);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/0, /*expected_count=*/2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/1, /*expected_count=*/2);
  // Check accumulated host counts.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 4);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/0, /*expected_count=*/0);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/1, /*expected_count=*/4);
  // Check enforcement durations.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 2);
  histograms.ExpectTimeBucketCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);
  histograms.ExpectTimeBucketCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(2), 1);

  // Step 7: Set HTTP score to max and set HTTPS score to zero. This simulates
  // the user spending their time on the HTTP URL and the HTTPS score decaying
  // over time.
  engagement_service->ResetBaseScoreForURL(https_url, 0);
  engagement_service->ResetBaseScoreForURL(http_url, 100);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  ASSERT_FALSE(state->IsHttpsEnforcedForUrl(
      GURL("http://example.com"), profile()->GetDefaultStoragePartition()));
  ASSERT_FALSE(
      state->GetHttpsEnforcedHosts(profile()->GetDefaultStoragePartition())
          .contains(GURL("https://example.com/")));
  // Check state.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicStateHistogram, 4);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kEnabled, 2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicStateHistogram,
                               SiteEngagementHeuristicState::kDisabled, 2);
  // Check host counts.
  histograms.ExpectTotalCount(kSiteEngagementHeuristicHostCountHistogram, 4);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/0, /*expected_count=*/2);
  histograms.ExpectBucketCount(kSiteEngagementHeuristicHostCountHistogram,
                               /*sample=*/1, /*expected_count=*/2);
  // Check accumulated host counts.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram, 4);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/0, /*expected_count=*/0);
  histograms.ExpectBucketCount(
      kSiteEngagementHeuristicAccumulatedHostCountHistogram,
      /*sample=*/1, /*expected_count=*/4);
  // Check enforcement durations.
  histograms.ExpectTotalCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, 2);
  histograms.ExpectTimeBucketCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(1), 1);
  histograms.ExpectTimeBucketCount(
      kSiteEngagementHeuristicEnforcementDurationHistogram, base::Hours(2), 1);
}

// If a site that was previously HTTPS-enforced is no longer in the site
// engagement list, it should no longer be HTTPS-enforced anymore.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       NoEngagementScoreShouldUnenforceHttps) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;
  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  GURL https_url("https://example.com");
  GURL http_url("http://example.com");

  // Increase the HTTPS engagement score. This will enable HFM Balanced Mode on
  // this host if available.
  engagement_service->ResetBaseScoreForURL(https_url, 90);
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  EXPECT_TRUE(state->IsHttpsEnforcedForUrl(
      http_url, profile()->GetDefaultStoragePartition()));

  // Clear up the engagement scores. This should remove the enforcement.
  HostContentSettingsMap* settings_map =
      HostContentSettingsMapFactory::GetForProfile(profile());
  settings_map->SetWebsiteSettingDefaultScope(
      https_url, GURL(), ContentSettingsType::SITE_ENGAGEMENT, base::Value());
  settings_map->SetWebsiteSettingDefaultScope(
      http_url, GURL(), ContentSettingsType::SITE_ENGAGEMENT, base::Value());
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);

  // Sites that fall outside the site engagement list should no longer have
  // HTTPS enforced.
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      http_url, profile()->GetDefaultStoragePartition()));
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      https_url, profile()->GetDefaultStoragePartition()));
}

// If a non-unique hostname was HTTPS-enforced by a previous version of Chrome,
// it should be removed from the enforcement list when possible.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       PreviouslyEnforcedNonUniqueHostnameShouldBeRemoved) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;
  site_engagement::SiteEngagementService* engagement_service =
      site_engagement::SiteEngagementService::Get(profile());
  ASSERT_TRUE(engagement_service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  GURL http_url("http://site.test");
  state->SetHttpsEnforcementForHost(http_url.GetHost(), /*enforce=*/true,
                                    profile()->GetDefaultStoragePartition());

  // The heuristic should clean up the enforcement list and remove non-unique
  // hostnames.
  MaybeEnableHttpsFirstModeForEngagedSitesAndWait(service);
  EXPECT_FALSE(state->IsHttpsEnforcedForUrl(
      http_url, profile()->GetDefaultStoragePartition()));
}

// Tests the Typically Secure User heuristic to ensure that it respects the
// finch flag. See TypicallySecureUserPref for more details.
// Regression test for crbug.com/40070502.
TEST_F(HttpsFirstModeSettingsTrackerSiteEngagementHeuristicTest,
       TypicallySecureUser_DisabledByDefault) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      features::kHttpsFirstModeV2ForTypicallySecureUsers);

  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  SetSiteEngagementScoreForTypicallySecureUserHeuristic();

  base::SimpleTestClock clock;
  base::Time now = base::Time::NowFromSystemTime();
  clock.SetNow(now);
  service->SetClockForTesting(&clock);
  // Move the clock so that the profile is old enough.
  clock.SetNow(now + base::Days(10));

  // This situation would normally qualify for enabling HFM, but it should stay
  // disabled since the feature is disabled.
  service->RecordHttpsUpgradeFallbackEvent();
  EXPECT_FALSE(service->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
}

// Creates the HFM service and waits for it to write the initial values into
// the typically secure fallbacks pref.
HttpsFirstModeService*
CreateHttpsFirstModeServiceAndWaitForTypicallySecureUserPrefInitialized(
    Profile* profile,
    base::SimpleTestClock* clock) {
  HttpsFirstModeServiceFactory::SetClockForTesting(clock);
  base::Time now = clock->Now();
  HttpsFirstModeService* hfm_service =
      HttpsFirstModeServiceFactory::GetForProfile(profile);
  base::DictValue expected_pref =
      base::DictValue()
          .Set("heuristic_start_timestamp", base::TimeToValue(now))
          .Set("fallback_events", base::ListValue());
  WaitForPrefValue(profile->GetPrefs(), prefs::kHttpsUpgradeFallbacks,
                   base::Value(std::move(expected_pref)));
  return hfm_service;
}

// A new profile shouldn't write any prefs related to Typically Secure User
// heuristic. TypicallySecureUserTest's SetUp() sets an old profile creation
// time and then create HttpsFirstModeService, so this can't be a
// TypicallySecureUserTest.
TEST_F(HttpsFirstModeSettingsTrackerTest, TypicallySecureUser_NewProfile) {
  base::SimpleTestClock clock;
  base::Time now = base::Time::NowFromSystemTime();
  clock.SetNow(now);

  HttpsFirstModeService* hfm_service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  profile()->SetCreationTimeForTesting(now);
  // A new profile will not initialize the heuristic on startup, so no need to
  // wait for kHttpsUpgradeFallbacks pref.

  SetSiteEngagementScoreForTypicallySecureUserHeuristic();
  hfm_service->SetClockForTesting(&clock);

  // kHttpsUpgradeFallbacks is normally written on startup, but not for new
  // profiles. Other prefs shouldn't change.
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  // A fallback event should be a no-op for a new profile.
  hfm_service->RecordHttpsUpgradeFallbackEvent();
  hfm_service->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();

  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
}

// Typically Secure User fallback entries pref written by an older version of
// Chrome should be handled properly by new versions.
// This can't be a TypicallySecureUserTest because we need to write the prefs
// before creating the service.
TEST_F(HttpsFirstModeSettingsTrackerTest,
       DISABLED_TypicallySecureUser_OldVersion) {
  // Write an empty pref without the "start_heuristic_timestamp" key.
  base::DictValue new_base_pref;
  profile()->GetPrefs()->SetDict(prefs::kHttpsUpgradeFallbacks,
                                 std::move(new_base_pref));
  base::SimpleTestClock clock;
  base::Time now = base::Time::NowFromSystemTime();
  clock.SetNow(now);
  profile()->SetCreationTimeForTesting(now - base::Days(20));

  // Enable the feature flags explicitly for this test.
  feature_list()->InitWithFeatures(
      /*enabled_features=*/{features::kHttpsFirstModeV2ForTypicallySecureUsers,
                            features::kHttpsFirstBalancedMode},
      /*disabled_features=*/{});

  HttpsFirstModeService* hfm_service =
      CreateHttpsFirstModeServiceAndWaitForTypicallySecureUserPrefInitialized(
          profile(), &clock);
  EXPECT_TRUE(hfm_service);

  EXPECT_TRUE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
  EXPECT_EQ(0u, hfm_service->GetFallbackEntryCountForTesting());

  // A fallback event should be a no-op as the last fallback event is too
  // recent.
  hfm_service->RecordHttpsUpgradeFallbackEvent();
  hfm_service->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();

  EXPECT_TRUE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
  EXPECT_EQ(1u, hfm_service->GetFallbackEntryCountForTesting());
}

class HttpsFirstModeSettingsTrackerTypicallySecureUserTest
    : public HttpsFirstModeSettingsTrackerTest {
 public:
  HttpsFirstModeSettingsTrackerTypicallySecureUserTest() {
    base::FieldTrialParams feature_params;
    feature_params["min-recent-navigations"] = "5";
    feature_list()->InitWithFeaturesAndParameters(
        /*enabled_features=*/{{features::
                                   kHttpsFirstModeV2ForTypicallySecureUsers,
                               feature_params},
                              {features::kHttpsFirstBalancedMode, {}}},
        /*disabled_features=*/{});
  }

 protected:
  void SetUp() override {
    HttpsFirstModeSettingsTrackerTest::SetUp();

    base::Time now = base::Time::NowFromSystemTime();
    profile()->SetCreationTimeForTesting(now - base::Days(20));
    clock_.SetNow(now);

    SetSiteEngagementScoreForTypicallySecureUserHeuristic();

    hfm_service_ =
        CreateHttpsFirstModeServiceAndWaitForTypicallySecureUserPrefInitialized(
            profile(), &clock_);
    ASSERT_FALSE(
        profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));

    clock_.SetNow(now);
  }

  void RecordFallbackEventAndMaybeEnableHttpsFirstMode() {
    hfm_service()->RecordHttpsUpgradeFallbackEvent();
    hfm_service()
        ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  }
  void IncrementRecentNavigationCount(size_t count) {
    for (size_t i = 0; i < count; i++) {
      hfm_service()->IncrementRecentNavigationCount();
    }
  }

  HttpsFirstModeService* hfm_service() { return hfm_service_; }

  base::SimpleTestClock* clock() { return &clock_; }

 private:
  raw_ptr<HttpsFirstModeService> hfm_service_;
  base::SimpleTestClock clock_;
};

// An old profile should initialize the prefs related to Typically Secure User
// heuristic.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
       DISABLED_ProfileOldEnough) {
  SetSiteEngagementScoreForTypicallySecureUserHeuristic();

  EXPECT_TRUE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
  EXPECT_EQ(0u, hfm_service()->GetFallbackEntryCountForTesting());

  // A fallback event should be a no-op as the last fallback event is too
  // recent.
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();

  EXPECT_TRUE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsUpgradeFallbacks));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
  EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
}

// Checks that Typically Secure Heuristic must observe navigations for at least
// a week before enabling HFM pref.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
       EnablePrefWhenObservedForLongEnough) {
  base::Time now = clock()->Now();
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(3));
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());

  // We haven't observed the profile for long enough. HFM shouldn't be enabled
  // yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs shouldn't be modified yet.
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(8));
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());

  // We have observed for long enough, and we don't have too many fallback
  // events (2). However, last fallback event was too recent, don't enable just
  // yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(9) + base::Hours(1));
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());

  // Last fallback event is now a day old, but we don't have enough recent
  // navigations. Don't enable yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  // Do lots of navigations. Should enable HFM now.
  IncrementRecentNavigationCount(100u);
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_TRUE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
  // Shouldn't modify strict mode pref:
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
}

// Checks that Typically Secure Heuristic must observe navigations for at least
// a week before enabling HFM pref.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
       DontEnablePrefWhenObservedForLongEnoughWithManyWarnings) {
  base::Time now = clock()->Now();
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(3));
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();

  // We haven't observed the profile for long enough. HFM shouldn't be enabled
  // yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs shouldn't be modified yet.
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(8));
  // We have observed for long enough, but we have too many fallback events (3).
  // HFM should not be enabled.
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(3u, hfm_service()->GetFallbackEntryCountForTesting());

  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
}

// Tests the usual flow of the Typically Secure User heuristic. Repeatedly calls
// RecordHttpsUpgradeFallbackEvent which is normally called
// from HTTPS-Upgrade fallbacks in production code. It then checks if the
// HTTPS-First Balanced Mode pref is enabled as expected.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
       BalancedModeEnabled) {
  IncrementRecentNavigationCount(5u);

  base::Time now = clock()->Now();
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  // Fallback #1. There are no previous HTTPS upgrade fallbacks (i.e.
  // would-be warnings), so this would normally auto-enable HFM, but
  // observation hasn't happened for a long time yet.
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs shouldn't be modified yet.
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(3));

  // Fallback #2. The fallback entry list is now [#1, #2]. HFM is still not
  // auto-enabled because we haven't observed navigations for long enough yet.
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs shouldn't be modified yet.
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));

  // Move forward, this drops fallback #1 as it's now 9 days old.
  clock()->SetNow(now + base::Days(9));
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());
  // Fallback list is now [#2] and we observed for long enough. HFM will be
  // enabled.
  EXPECT_TRUE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));

  // Fallback #3. The fallback list is now [#2, #3]. Balanced Mode is still
  // auto-enabled.
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
  EXPECT_TRUE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs are now set.
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));

  // Fallback #4. The fallback list is now [#2, #3, #4]. This is too many
  // fallbacks, but we don't auto-disable the user pref once it's
  // auto-enabled. (Note that this isn't realistic scenario, as the
  // interstitial is already enabled after fallback #3, and we don't record a
  // fallback when the interstitial is enabled).
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(3u, hfm_service()->GetFallbackEntryCountForTesting());
  EXPECT_TRUE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs are still set.
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
  EXPECT_TRUE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
}

// Checks that manually changing the HFM pref in the UI clears the HTTP
// allowlist. A variant of this test
// (TypicallySecureUserTest.PrefUpdatedByHeuristic_ShouldNotClearAllowlist)
// checks that a heuristic auto-enabling HFM does NOT clear the allowlist.
TEST_F(HttpsFirstModeSettingsTrackerTest, PrefUpdated_ShouldClearAllowlist) {
  feature_list()->InitAndDisableFeature(features::kHttpsFirstBalancedMode);

  // Instantiate the service so that it can track pref changes.
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));

  // Allowlist a host for for http.
  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);
  content::StoragePartition* storage_partition =
      profile()->GetDefaultStoragePartition();
  state->AllowHttpForHost("http-allowed.com", storage_partition);
  EXPECT_TRUE(
      state->IsHttpAllowedForHost("http-allowed.com", storage_partition));

  // Change the UI setting. This should clear the http allowlist.
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, true);
  EXPECT_FALSE(
      state->IsHttpAllowedForHost("http-allowed.com", storage_partition));
}

TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
       PrefUpdatedByHeuristic_ShouldNotClearAllowlist) {
  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);
  content::StoragePartition* storage_partition =
      profile()->GetDefaultStoragePartition();
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  state->AllowHttpForHost("http-allowed.com", storage_partition);
  EXPECT_TRUE(
      state->IsHttpAllowedForHost("http-allowed.com", storage_partition));

  // From here on, do a bunch of navigations and advance the clock so that
  // Typically Secure heuristic eventually auto-enables HFM.

  base::Time now = clock()->Now();
  clock()->SetNow(now + base::Days(3));
  // Record a fallback event to start the Typically Secure observation.
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());

  // Move forward and record another event.
  clock()->SetNow(now + base::Days(8));
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());

  // We have observed for long enough, and we don't have too many fallback
  // events (2). However, last fallback event was too recent. Move forward
  // again.
  clock()->SetNow(now + base::Days(9) + base::Hours(1));
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());

  // Last fallback event is now a day old, but we don't have enough recent
  // navigations. Don't enable yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  // Finally, do lots of navigations. Should auto-enable HFM now.
  IncrementRecentNavigationCount(100u);
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_TRUE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_TRUE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsFirstBalancedMode));
  EXPECT_TRUE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  // Typically Secure heuristic auto-enabling HFM should not clear the
  // allowlist.
  EXPECT_TRUE(
      state->IsHttpAllowedForHost("http-allowed.com", storage_partition));
}

// Same as DontEnablePrefWhenObservedForLongEnoughWithManyWarnings, but with
// an enterprise managed device. Should never enable HFM.
TEST_F(HttpsFirstModeSettingsTrackerTypicallySecureUserTest,
       EnterpriseManaged_ShouldNotEnable) {
  ChromeSecurityBlockingPageFactory::SetEnterpriseManagedForTesting(true);

  base::Time now = clock()->Now();
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(3));
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  // Fallback entries are recorded even on enterprise managed devices.
  EXPECT_EQ(1u, hfm_service()->GetFallbackEntryCountForTesting());

  // We haven't observed the profile for long enough. HFM shouldn't be enabled
  // yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  // Prefs shouldn't be modified yet.
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(8));
  RecordFallbackEventAndMaybeEnableHttpsFirstMode();
  // Fallback entries are recorded even on enterprise managed devices.
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());

  // We have observed for long enough, and we don't have too many fallback
  // events (2). However, last fallback event was too recent, don't enable just
  // yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  clock()->SetNow(now + base::Days(9) + base::Hours(1));
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());

  // Last fallback event is now a day old, but we don't have enough recent
  // navigations. Don't enable yet.
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));

  // Do lots of navigations. Should still not enable HFM because the device is
  // managed.
  IncrementRecentNavigationCount(100u);
  hfm_service()
      ->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstBalancedMode();
  EXPECT_FALSE(
      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
  EXPECT_FALSE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsFirstBalancedMode));
  EXPECT_FALSE(
      profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeAutoEnabled));
  // Shouldn't modify strict mode pref:
  EXPECT_FALSE(
      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
}

// Tests that the correct setting at startup is logged, when the Balanced Mode
// feature flag is enabled but not on by default.
TEST_F(HttpsFirstModeSettingsTrackerTest, StartupBalancedModeAvailable) {
  feature_list()->InitAndEnableFeature(features::kHttpsFirstBalancedMode);

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  // Creating the HttpsFirstModeService should emit the "setting at startup"
  // histogram.
  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartup2",
      HttpsFirstModeSetting::kDisabled, 1);
}

// Tests that the correct setting at startup is logged, when Balanced Mode
// is auto-enabled.
TEST_F(HttpsFirstModeSettingsTrackerTest, StartupBalancedModeAutoEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{features::kHttpsFirstBalancedMode,
                            features::kHttpsFirstBalancedModeAutoEnable},
      /*disabled_features=*/{});

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  // Creating the HttpsFirstModeService should emit the "setting at startup"
  // histogram.
  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartup2",
      HttpsFirstModeSetting::kEnabledBalanced, 1);
}

// Tests that Advanced Protection status changes do not affect the synced
// preference, but do correctly update the current setting.
// Regression test for crbug.com/480099712.
TEST_F(HttpsFirstModeSettingsTrackerTest, AdvancedProtectionStatusChange) {
  feature_list()->InitAndEnableFeature(
      features::kHttpsFirstModeForAdvancedProtectionUsers);

  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  safe_browsing::AdvancedProtectionStatusManager* aps_manager =
      safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
          profile());
  ASSERT_TRUE(aps_manager);

  // Initially, the Strict HFM pref is disabled.
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_EQ(service->GetCurrentSetting(), HttpsFirstModeSetting::kDisabled);

  // Enable Advanced Protection. This should not change the pref, but
  // GetCurrentSetting should now return kEnabledFull.
  aps_manager->SetAdvancedProtectionStatusForTesting(true);
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_EQ(service->GetCurrentSetting(), HttpsFirstModeSetting::kEnabledFull);

  // Disable Advanced Protection. GetCurrentSetting should return to kDisabled.
  aps_manager->SetAdvancedProtectionStatusForTesting(false);
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kHttpsOnlyModeEnabled));
  EXPECT_EQ(service->GetCurrentSetting(), HttpsFirstModeSetting::kDisabled);
}

TEST_F(HttpsFirstModeSettingsTrackerTest, BalancedModeEnabledForEsbUsers) {
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  StatefulSSLHostStateDelegate* state =
      StatefulSSLHostStateDelegateFactory::GetForProfile(profile());
  ASSERT_TRUE(state);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{features::kHttpsFirstBalancedMode,
                            features::
                                kHttpsFirstModeDefaultSettingPairsWithEsb},
      /*disabled_features=*/{});

  // 1. Add a host to the HTTP allowlist to test that it gets cleared on pref
  // changes.
  state->AllowHttpForHost("example.com",
                          profile()->GetDefaultStoragePartition());
  EXPECT_TRUE(state->IsHttpAllowedForHost(
      "example.com", profile()->GetDefaultStoragePartition()));

  // 2. Initially ESB is disabled. Balanced Mode should be disabled by default.
  EXPECT_FALSE(
      safe_browsing::IsEnhancedProtectionEnabled(*profile()->GetPrefs()));
  EXPECT_FALSE(IsBalancedModeEnabled(profile()->GetPrefs()));
  EXPECT_EQ(service->GetCurrentSetting(), HttpsFirstModeSetting::kDisabled);

  // 3. Enable ESB. Balanced Mode should now be enabled by default.
  safe_browsing::SetEnhancedProtectionPref(profile()->GetPrefs(), true);
  EXPECT_TRUE(
      safe_browsing::IsEnhancedProtectionEnabled(*profile()->GetPrefs()));
  EXPECT_TRUE(IsBalancedModeEnabled(profile()->GetPrefs()));
  EXPECT_EQ(service->GetCurrentSetting(),
            HttpsFirstModeSetting::kEnabledBalanced);

  // The allowlist should be cleared on this pref change.
  EXPECT_FALSE(state->IsHttpAllowedForHost(
      "example.com", profile()->GetDefaultStoragePartition()));

  // 4. If the user explicitly disables Balanced Mode, it should stay disabled.
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsFirstBalancedMode, false);
  EXPECT_FALSE(IsBalancedModeEnabled(profile()->GetPrefs()));
  EXPECT_EQ(service->GetCurrentSetting(), HttpsFirstModeSetting::kDisabled);

  // 5. If the user explicitly enables Balanced Mode, it should stay enabled
  // even if ESB is disabled.
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsFirstBalancedMode, true);
  safe_browsing::SetEnhancedProtectionPref(profile()->GetPrefs(), false);
  EXPECT_TRUE(IsBalancedModeEnabled(profile()->GetPrefs()));
  EXPECT_EQ(service->GetCurrentSetting(),
            HttpsFirstModeSetting::kEnabledBalanced);
}

TEST_F(HttpsFirstModeSettingsTrackerTest, StartupDetailedState_Disabled) {
  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kDisabled, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest, StartupDetailedState_EnabledFull) {
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeEnabled, true);

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kEnabledFull, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest,
       StartupDetailedState_EnabledBalancedExplicit) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(features::kHttpsFirstBalancedMode);

  profile()->GetPrefs()->SetBoolean(prefs::kHttpsFirstBalancedMode, true);

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kEnabledBalancedExplicit, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest,
       StartupDetailedState_EnabledBalancedTypicallySecure) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(features::kHttpsFirstBalancedMode);

  profile()->GetPrefs()->SetBoolean(prefs::kHttpsFirstBalancedMode, true);
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsOnlyModeAutoEnabled, true);

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kEnabledBalancedTypicallySecure, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest,
       StartupDetailedState_EnabledBalancedEsbPairing) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{features::kHttpsFirstBalancedMode,
                            features::
                                kHttpsFirstModeDefaultSettingPairsWithEsb},
      /*disabled_features=*/{});

  safe_browsing::SetEnhancedProtectionPref(profile()->GetPrefs(), true);

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kEnabledBalancedEsbPairing, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest,
       StartupDetailedState_EnabledBalancedAutoEnable) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{features::kHttpsFirstBalancedMode,
                            features::kHttpsFirstBalancedModeAutoEnable},
      /*disabled_features=*/{});

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kEnabledBalancedAutoEnable, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest,
       StartupDetailedState_AdvancedProtection) {
  feature_list()->InitAndEnableFeature(
      features::kHttpsFirstModeForAdvancedProtectionUsers);

  safe_browsing::AdvancedProtectionStatusManager* aps_manager =
      safe_browsing::AdvancedProtectionStatusManagerFactory::GetForProfile(
          profile());
  ASSERT_TRUE(aps_manager);
  aps_manager->SetAdvancedProtectionStatusForTesting(true);

  base::HistogramTester histograms;
  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingEnabledAtStartupDetailed",
      HttpsFirstModeStartupState::kEnabledFull, 1);
}

TEST_F(HttpsFirstModeSettingsTrackerTest, ImplicitTransition_EsbToggled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{features::kHttpsFirstBalancedMode,
                            features::
                                kHttpsFirstModeDefaultSettingPairsWithEsb},
      /*disabled_features=*/{});

  HttpsFirstModeService* service =
      HttpsFirstModeServiceFactory::GetForProfile(profile());
  ASSERT_TRUE(service);

  base::HistogramTester histograms;

  // 1. Enable ESB. HFM is in default state, so it should implicitly enable HFM
  // Balanced.
  safe_browsing::SetEnhancedProtectionPref(profile()->GetPrefs(), true);
  histograms.ExpectUniqueSample(
      "Security.HttpsFirstMode.SettingImplicitlyChanged",
      HttpsFirstModeImplicitStateChange::kBalancedEnabledByEsb, 1);

  // 2. Disable ESB. Should implicitly disable HFM Balanced.
  safe_browsing::SetEnhancedProtectionPref(profile()->GetPrefs(), false);
  histograms.ExpectBucketCount(
      "Security.HttpsFirstMode.SettingImplicitlyChanged",
      HttpsFirstModeImplicitStateChange::kBalancedDisabledByEsb, 1);
  histograms.ExpectTotalCount(
      "Security.HttpsFirstMode.SettingImplicitlyChanged", 2);

  // 3. Explicitly set HFM Balanced to false. Now HFM is NOT in default state.
  profile()->GetPrefs()->SetBoolean(prefs::kHttpsFirstBalancedMode, false);

  // 4. Enable ESB again. Should NOT log any implicit change.
  safe_browsing::SetEnhancedProtectionPref(profile()->GetPrefs(), true);
  histograms.ExpectTotalCount(
      "Security.HttpsFirstMode.SettingImplicitlyChanged", 2);
}

TEST_F(HttpsFirstModeSettingsTrackerTest, GuestOTRProfileHasService) {
  TestingProfile::Builder builder;
  builder.SetGuestSession();
  std::unique_ptr<TestingProfile> guest_profile = builder.Build();
  // For a guest OTR profile, the factory should still return a service
  // instance.
  EXPECT_TRUE(HttpsFirstModeServiceFactory::GetForProfile(
      guest_profile->GetPrimaryOTRProfile(true)));
}
