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

#include <memory>

#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/back_forward_cache_test_util.h"
#include "content/browser/browsing_data/shared_storage_clear_site_data_tester.h"
#include "content/browser/preloading/prefetch/prefetch_document_manager.h"
#include "content/browser/preloading/prefetch/prefetch_features.h"
#include "content/browser/preloading/prefetch/prefetch_status.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/prefetch_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "net/base/features.h"
#include "net/base/net_errors.h"
#include "net/cookies/cookie_base.h"
#include "net/cookies/cookie_partition_key.h"
#include "net/cookies/cookie_partition_key_collection.h"
#include "net/cookies/cookie_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"

namespace {

const char kHstsPath[] = "/hsts";
const char kHttpAuthPath[] = "/http_auth";
const char kHstsResponseBody[] = "HSTS set";
// Use a.test because 127.0.0.1/localhost cannot use HSTS.
const char kHstsHostname[] = "a.test";

std::unique_ptr<net::test_server::HttpResponse> HandleHstsRequest(
    const net::test_server::HttpRequest& request) {
  if (request.relative_url == kHstsPath) {
    std::unique_ptr<net::test_server::BasicHttpResponse> hsts_response =
        std::make_unique<net::test_server::BasicHttpResponse>();
    hsts_response->AddCustomHeader("Strict-Transport-Security",
                                   "max-age=1000000");
    hsts_response->set_content(kHstsResponseBody);
    return hsts_response;
  }
  return nullptr;
}

// Handles |request| to "/http_auth". If "Authorization" header is present,
// responds with a non-empty HTTP 200 page (regardless of auth credentials).
// Otherwise serves a Basic Auth challenge.
std::unique_ptr<net::test_server::HttpResponse> HandleHttpAuthRequest(
    const net::test_server::HttpRequest& request) {
  if (request.relative_url != kHttpAuthPath) {
    return nullptr;
  }

  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  if (base::Contains(request.headers, "Authorization")) {
    http_response->set_code(net::HTTP_OK);
    http_response->set_content("Success!");
  } else {
    http_response->set_code(net::HTTP_UNAUTHORIZED);
    http_response->AddCustomHeader("WWW-Authenticate",
                                   "Basic realm=\"test realm\"");
  }
  return http_response;
}

}  // namespace

namespace content {

class BrowsingDataRemoverImplBrowserTest
    : public ContentBrowserTest,
      public BackForwardCacheMetricsTestMatcher {
 public:
  BrowsingDataRemoverImplBrowserTest()
      : ssl_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
    // HSTS tests must run on non-localhost, while other tests use localhost.
    ssl_server_.SetCertHostnames({kHstsHostname, "localhost"});
    ssl_server_.AddDefaultHandlers(GetTestDataFilePath());
    ssl_server_.RegisterRequestHandler(base::BindRepeating(&HandleHstsRequest));
    ssl_server_.RegisterRequestHandler(
        base::BindRepeating(&HandleHttpAuthRequest));
    EXPECT_TRUE(ssl_server_.Start());
  }

  void SetUpOnMainThread() override {
    ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
    histogram_tester_ = std::make_unique<base::HistogramTester>();
    host_resolver()->AddRule(kHstsHostname, "127.0.0.1");
  }

  void RemoveAndWait(uint64_t remove_mask) {
    content::BrowsingDataRemover* remover =
        shell()->web_contents()->GetBrowserContext()->GetBrowsingDataRemover();
    content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
    remover->RemoveAndReply(
        base::Time(), base::Time::Max(), remove_mask,
        content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
        &completion_observer);
    completion_observer.BlockUntilCompletion();
  }

  void RemoveWithFilterAndWait(
      uint64_t remove_mask,
      std::unique_ptr<BrowsingDataFilterBuilder> filter) {
    content::BrowsingDataRemover* remover =
        shell()->web_contents()->GetBrowserContext()->GetBrowsingDataRemover();
    content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
    remover->RemoveWithFilterAndReply(
        base::Time(), base::Time::Max(), remove_mask,
        content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
        std::move(filter), &completion_observer);
    completion_observer.BlockUntilCompletion();
  }

  // Issues a request for kHstsPath on localhost, and expects it to enable HSTS
  // for the domain.
  void IssueRequestThatSetsHsts() {
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    request->url = ssl_server_.GetURL(kHstsHostname, kHstsPath);

    SimpleURLLoaderTestHelper loader_helper;
    std::unique_ptr<network::SimpleURLLoader> loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);
    loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        url_loader_factory(), loader_helper.GetCallback());
    loader_helper.WaitForCallback();
    ASSERT_TRUE(loader_helper.response_body());
    EXPECT_EQ(kHstsResponseBody, *loader_helper.response_body());

    EXPECT_TRUE(IsHstsSet());
  }

  // Returns true if HSTS is set on localhost.  Does this by issuing a main
  // frame HTTP request to the embedded test server, and expecting it to be
  // redirected from HTTP to HTTPS if HSTS is enabled.  If the request succeeds,
  // it was sent over HTTPS, so HSTS is enabled. If it fails, the request was
  // send using HTTP instead, so HSTS is not enabled for the domain.
  //
  // That the request be main frame is necessary when
  // kHstsTopLevelNavigationsOnly is enabled.
  bool IsHstsSet() {
    GURL url = ssl_server_.GetURL(kHstsHostname, "/echo");
    GURL::Replacements replacements;
    replacements.SetSchemeStr("http");
    url = url.ReplaceComponents(replacements);
    url::Origin origin = url::Origin::Create(url);
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    request->url = url;
    request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);
    request->update_first_party_url_on_redirect = true;
    request->trusted_params.emplace();
    request->trusted_params->isolation_info = net::IsolationInfo::Create(
        net::IsolationInfo::RequestType::kMainFrame, origin, origin,
        net::SiteForCookies::FromOrigin(origin));

    std::unique_ptr<network::SimpleURLLoader> loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);
    SimpleURLLoaderTestHelper loader_helper;
    loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        url_loader_factory(), loader_helper.GetCallback());
    loader_helper.WaitForCallback();

    // On success, HSTS was enabled for the domain.
    if (loader_helper.response_body()) {
      EXPECT_EQ("Echo", *loader_helper.response_body());
      return true;
    }

    // On failure, the server just hangs up, since it didn't receive an SSL
    // handshake.
    EXPECT_EQ(net::ERR_EMPTY_RESPONSE, loader->NetError());
    return false;
  }

  // Sets HTTP auth cache by making a request with credentials specified in the
  // URL to a page with an auth challenge.
  void IssueRequestThatSetsHttpAuthCache() {
    GURL url = ssl_server_.GetURL(kHttpAuthPath);
    GURL::Replacements replacements;
    replacements.SetUsernameStr("user");
    replacements.SetPasswordStr("password");
    GURL url_with_creds = url.ReplaceComponents(replacements);
    ASSERT_TRUE(NavigateToURL(shell(), url_with_creds));

    ASSERT_TRUE(IsHttpAuthCacheSet());
  }

  // Determines if auth cache is populated by seeing if a request to a page with
  // an auth challenge succeeds.
  bool IsHttpAuthCacheSet() {
    // Set a login request callback to be used instead of a login dialog since
    // such a dialog is difficult to control programmatically and doesn't work
    // on all platforms.
    bool login_requested = false;
    ShellContentBrowserClient::Get()->set_login_request_callback(
        base::BindLambdaForTesting(
            [&](bool is_primary_main_frame_navigation /* unused */,
                bool is_navigation /* unused */) { login_requested = true; }));

    GURL url = ssl_server_.GetURL(kHttpAuthPath);
    bool navigation_suceeded = NavigateToURL(shell(), url);

    // Because our login request callback does nothing, navigation should
    // succeed iff login is not needed unless some other unexpected error
    // occurs.
    EXPECT_NE(navigation_suceeded, login_requested);

    return !login_requested && navigation_suceeded;
  }

  network::mojom::URLLoaderFactory* url_loader_factory() {
    return shell()
        ->web_contents()
        ->GetBrowserContext()
        ->GetDefaultStoragePartition()
        ->GetURLLoaderFactoryForBrowserProcess()
        .get();
  }

  network::mojom::NetworkContext* network_context() {
    return shell()
        ->web_contents()
        ->GetBrowserContext()
        ->GetDefaultStoragePartition()
        ->GetNetworkContext();
  }

  const ukm::TestAutoSetUkmRecorder& ukm_recorder() override {
    return *ukm_recorder_;
  }
  const base::HistogramTester& histogram_tester() override {
    return *histogram_tester_;
  }

  const net::test_server::EmbeddedTestServer& ssl_server() {
    return ssl_server_;
  }

 private:
  net::test_server::EmbeddedTestServer ssl_server_;
  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

// Verify that TransportSecurityState data is cleared for REMOVE_CACHE.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
                       ClearTransportSecurityState) {
  IssueRequestThatSetsHsts();

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_CACHE);
  EXPECT_FALSE(IsHstsSet());
}

// Verify that TransportSecurityState data is not cleared if REMOVE_CACHE is not
// set or there is a deletelist filter.
// TODO(crbug.com/40667157): Add support for filtered deletions and update test.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
                       PreserveTransportSecurityState) {
  IssueRequestThatSetsHsts();

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
  EXPECT_TRUE(IsHstsSet());

  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain("foobar.com");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));
  EXPECT_TRUE(IsHstsSet());
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, ClearHttpAuthCache) {
  ASSERT_FALSE(IsHttpAuthCacheSet());
  IssueRequestThatSetsHttpAuthCache();

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
  EXPECT_FALSE(IsHttpAuthCacheSet());
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
                       PreserveHttpAuthCache) {
  ASSERT_FALSE(IsHttpAuthCacheSet());
  IssueRequestThatSetsHttpAuthCache();

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
  EXPECT_TRUE(IsHttpAuthCacheSet());
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
                       ClearHttpAuthCacheWhenEmpty) {
  ASSERT_FALSE(IsHttpAuthCacheSet());

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
  EXPECT_FALSE(IsHttpAuthCacheSet());
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
                       ClearBackForwardCacheEntries) {
  if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
    return;
  }

  GURL url_1 = ssl_server().GetURL("/title1.html");
  GURL url_2 = ssl_server().GetURL("/title2.html");

  // 1) Navigate to url_1, then to url_2.
  ASSERT_TRUE(NavigateToURL(shell(), url_1));
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 2) Go back, the page should be restored from BFCache.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectRestored(FROM_HERE);

  // 3) Navigate to url_2 again.
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 4) Remove the browsing data with DATA_TYPE_CACHE and go back, the page
  // should not be restored from BFCache since the BFCache entry should be
  // flushed.
  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_CACHE);
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kCacheFlushed},
                    {}, {}, {}, {}, FROM_HERE);
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
                       ClearBackForwardCacheEntriesWithOrigin_DataTypeCache) {
  if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
    return;
  }

  GURL url_1 = ssl_server().GetURL("/title1.html");
  GURL url_2 = ssl_server().GetURL("/title2.html");

  // 1) Navigate to url_1, then to url_2.
  ASSERT_TRUE(NavigateToURL(shell(), url_1));
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 2) Go back, the page should be restored from BFCache.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectRestored(FROM_HERE);

  // 3) Navigate to url_2 again.
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 4) Remove the browsing data with DATA_TYPE_CACHE and some random domain
  // that doesn't match the BFCached document's origin.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain("foobar.com");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));

  // 5) Go back, the page should be restored from BFCache.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectRestored(FROM_HERE);

  // 6) Navigate to url_2 again.
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 7) Remove the browsing data with DATA_TYPE_CACHE and the domain that
  // matches the BFCached document's origin.
  filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));

  // 8) Go back, and the page should not be restored from BFCache since the
  // BFCache entry should be flushed.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kCacheFlushed},
                    {}, {}, {}, {}, FROM_HERE);
}

class BrowsingDataRemoverImplForCacheControlNoStorePageBrowserTest
    : public BrowsingDataRemoverImplBrowserTest {
 protected:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    feature_list_.InitAndEnableFeatureWithParameters(
        features::kCacheControlNoStoreEnterBackForwardCache,
        {{"level", "store-and-evict"}});
    BrowsingDataRemoverImplBrowserTest::SetUpCommandLine(command_line);
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_F(
    BrowsingDataRemoverImplForCacheControlNoStorePageBrowserTest,
    DoesClearNonCacheControlNoStoreBackForwardCacheEntries_DataTypeCookie) {
  if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
    return;
  }

  GURL url_1 = ssl_server().GetURL("/title1.html");
  GURL url_2 = ssl_server().GetURL("/title2.html");

  // 1) Navigate to url_1, then to url_2.
  ASSERT_TRUE(NavigateToURL(shell(), url_1));
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 2) Go back, the page should be restored from BFCache.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectRestored(FROM_HERE);

  // 3) Navigate to url_2 again.
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 4) Remove the browsing data with DATA_TYPE_COOKIES and the domain that
  // matches the BFCached document's origin.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                          std::move(filter));

  // 5) Go back, and the page should be restored from BFCache.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectRestored(FROM_HERE);
}

IN_PROC_BROWSER_TEST_F(
    BrowsingDataRemoverImplForCacheControlNoStorePageBrowserTest,
    ClearCacheControlNoStoreBackForwardCacheEntries_DataTypeCookie) {
  if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
    return;
  }

  GURL url_1 = ssl_server().GetURL("/echoall/nocache");
  GURL url_2 = ssl_server().GetURL("/title1.html");

  // 1) Navigate to url_1 with CCNS response, then to url_2.
  ASSERT_TRUE(NavigateToURL(shell(), url_1));
  ASSERT_TRUE(NavigateToURL(shell(), url_2));

  // 2) Remove the browsing data with DATA_TYPE_COOKIES and the domain that
  // matches the BFCached document's origin.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                          std::move(filter));

  // 3) Go back, and the page should not be restored from BFCache since the
  // BFCache entry should be flushed.
  ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
  ExpectNotRestored(
      {BackForwardCacheMetrics::NotRestoredReason::kCookieFlushed,
       BackForwardCacheMetrics::NotRestoredReason::kCacheControlNoStore},
      {}, {}, {}, {}, FROM_HERE);
}

class CookiesBrowsingDataRemoverImplBrowserTest
    : public BrowsingDataRemoverImplBrowserTest {
 public:
  void SetUpOnMainThread() override {
    network_context()->GetCookieManager(
        cookie_manager_.BindNewPipeAndPassReceiver());
  }

  bool SetCookie(
      const GURL& url,
      const std::string& cookie_line,
      const std::optional<net::CookiePartitionKey>& cookie_partition_key) {
    auto cookie_obj = net::CanonicalCookie::CreateForTesting(
        url, cookie_line, base::Time::Now(), /*server_time=*/std::nullopt,
        cookie_partition_key);

    base::test::TestFuture<net::CookieAccessResult> future;
    cookie_manager_->SetCanonicalCookie(*cookie_obj, url,
                                        net::CookieOptions::MakeAllInclusive(),
                                        future.GetCallback());
    return future.Take().status.IsInclude();
  }

  net::CookieList GetAllCookies() {
    base::test::TestFuture<const net::CookieList&> future;
    cookie_manager_->GetAllCookies(future.GetCallback());
    return future.Take();
  }

 private:
  mojo::Remote<network::mojom::CookieManager> cookie_manager_;
};

IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
                       ClearsAllCookiesByDefault) {
  // Set unpartitioned cookies.
  ASSERT_TRUE(SetCookie(GURL("http://a.com"), "A=0", std::nullopt));
  ASSERT_TRUE(SetCookie(GURL("https://a.com"), "B=1; secure; samesite=none",
                        std::nullopt));
  ASSERT_TRUE(SetCookie(GURL("https://b.com"),
                        "C=2; secure; samesite=none; max-age=10000",
                        std::nullopt));
  ASSERT_EQ(3u, GetAllCookies().size());

  // Set partitioned cookies.
  ASSERT_TRUE(SetCookie(
      GURL("https://c.com"), "A=0; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://d.com"))));
  ASSERT_TRUE(SetCookie(
      GURL("https://c.com"), "A=0; secure; samesite=none; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("http://e.com"))));
  ASSERT_TRUE(SetCookie(
      GURL("https://f.com"),
      "B=1; secure; samesite=none; max-age=10000; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://g.com"))));
  ASSERT_TRUE(
      SetCookie(GURL("https://f.com"), "C=2; secure; samesite=none",
                net::CookiePartitionKey::FromURLForTesting(
                    GURL("https://g.com"),
                    net::CookiePartitionKey::AncestorChainBit::kCrossSite,
                    base::UnguessableToken::Create())));

  ASSERT_EQ(7u, GetAllCookies().size());
  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
  EXPECT_EQ(0u, GetAllCookies().size());
}

IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
                       ClearingCookiesByHostKey) {
  // Cookies set by a.com, should be removed.
  // partition_key: null, host_key: a.com
  ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure; partitioned",
                        /*cookie_partition_key=*/std::nullopt));
  // partition_key: a.com, host_key: a.com
  ASSERT_TRUE(SetCookie(
      GURL("https://a.com"), "B=1; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://a.com"))));
  // partition_key: b.com, host_key: a.com
  ASSERT_TRUE(SetCookie(
      GURL("https://a.com"), "C=2; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));

  // Cookies set by b.com, should not be removed.
  // partition_key: null, host_key: b.com
  ASSERT_TRUE(SetCookie(GURL("https://b.com"), "D=3; secure; partitioned",
                        /*cookie_partition_key=*/std::nullopt));
  // partition_key: a.com, host_key: b.com
  ASSERT_TRUE(SetCookie(
      GURL("https://b.com"), "E=4; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://a.com"))));
  // partition_key: b.com, host_key: b.com
  ASSERT_TRUE(SetCookie(
      GURL("https://b.com"), "F=5; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));

  ASSERT_EQ(6u, GetAllCookies().size());

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kDelete));
  builder->AddRegisterableDomain("a.com");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                          std::move(builder));
  auto cookies = GetAllCookies();
  EXPECT_EQ(3u, cookies.size());
  EXPECT_EQ("D", cookies[0].Name());
  EXPECT_EQ("E", cookies[1].Name());
  EXPECT_EQ("F", cookies[2].Name());
}

// Regression test for https://crbug.com/1457600.
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
                       ClearCookiesWithEmptyFilter) {
  ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure",
                        /*cookie_partition_key=*/std::nullopt));
  ASSERT_EQ(1u, GetAllCookies().size());

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kDelete));
  EXPECT_TRUE(builder->MatchesNothing());
  // `builder` produces a malformed filter, so it fails a CHECK in
  // `BrowsingDataRemoverImpl`.
  EXPECT_DEATH_IF_SUPPORTED(
      RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                              std::move(builder)),
      "");

  EXPECT_EQ(1u, GetAllCookies().size());
}

IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
                       ClearSiteData_PartitionedCookiesOnly) {
  // Unpartitioned cookie should not be removed when third-party cookie blocking
  // applies to the request that sent Clear-Site-Data.
  ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure;",
                        /*cookie_partition_key=*/std::nullopt));
  // Partitioned cookies should still be removed.
  ASSERT_TRUE(SetCookie(
      GURL("https://a.com"), "B=1; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
  // Nonced partitioned cookies should still be removed.
  ASSERT_TRUE(
      SetCookie(GURL("https://a.com"), "C=2; secure;",
                net::CookiePartitionKey::FromURLForTesting(
                    GURL("https://b.com"),
                    net::CookiePartitionKey::AncestorChainBit::kCrossSite,
                    base::UnguessableToken::Create())));

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kDelete));
  builder->AddRegisterableDomain("a.com");
  builder->SetPartitionedCookiesOnly(true);

  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                          std::move(builder));

  auto cookies = GetAllCookies();
  EXPECT_EQ(1u, cookies.size());
  EXPECT_EQ("A", cookies[0].Name());
}

IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
                       ClearSiteData_AllDomainsPartitionedCookiesOnly) {
  // Unpartitioned cookies should not be removed when
  // SetPartitionedCookiesOnly(true)
  ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure;",
                        /*cookie_partition_key=*/std::nullopt));
  // All partitioned cookies should be removed.
  ASSERT_TRUE(SetCookie(
      GURL("https://a.com"), "B=1; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kPreserve));
  // Mode::kPreserve + no origins/domains = delete everything.
  builder->SetPartitionedCookiesOnly(true);

  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                          std::move(builder));

  EXPECT_THAT(GetAllCookies(), testing::ElementsAre(testing::Property(
                                   &net::CookieBase::Name, "A")));
}

IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
                       ClearSiteData_AllDomainsCookiePartitionKeyCollection) {
  // All unpartitioned cookies should be removed.
  ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure;",
                        /*cookie_partition_key=*/std::nullopt));
  // Cookies partitioned under b.com should also be removed.
  ASSERT_TRUE(SetCookie(
      GURL("https://a.com"), "B=1; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
  // Cookies partitioned under other sites should NOT be removed.
  ASSERT_TRUE(SetCookie(
      GURL("https://a.com"), "C=2; secure; partitioned",
      net::CookiePartitionKey::FromURLForTesting(GURL("https://c.com"))));

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kPreserve));
  // Mode::kPreserve + no origins/domains = delete everything.
  builder->SetCookiePartitionKeyCollection(net::CookiePartitionKeyCollection(
      net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));

  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
                          std::move(builder));

  EXPECT_THAT(GetAllCookies(), testing::ElementsAre(testing::Property(
                                   &net::CookieBase::Name, "C")));
}

namespace {

// Tests Trust Tokens clearing by calling
// TrustTokenQueryAnswerer::HasTrustTokens with a TrustTokenQueryAnswerer
// obtained from the provided NetworkContext.
//
// The Trust Tokens functionality places a cap of 2 distinct arguments to the
// |issuer| argument of
//       TrustTokenQueryAnswerer(origin)::HasTrustTokens(issuer)
// for each top-frame origin |origin|. (This limit is recorded in persistent
// storage scoped to the origin |origin| and is not related to the lifetime of
// the specific TrustTokenQueryAnswerer object.)
//
// To add an origin, the tester creates a TrustTokenQueryAnswerer parameterized
// by |origin| and calls HasTrustTokens with two distinct "priming" issuer
// arguments. This will make the Trust Tokens persistent storage record that
// |origin| is associated with each of these issuers, with the effect that
// (barring a data clear) subsequent HasTrustTokens calls with different issuer
// arguments will fail. To check if an origin is present, the tester calls
//    TrustTokenQueryAnswerer(origin)::HasTrustTokens(issuer)
// with an |issuer| argument distinct from the two earlier "priming" issuers.
// This third HasTrustTokens call will error out exactly if |origin| was
// previously added by AddOrigin.
//
// Usage:
//   >= 0 AddOrigin() - origins must be HTTPS
//   (clear data)
//   >= 0 HasOrigin()
class TrustTokensTester {
 public:
  explicit TrustTokensTester(network::mojom::NetworkContext* network_context)
      : network_context_(network_context) {}

  void AddOrigin(const url::Origin& origin) {
    mojo::Remote<network::mojom::TrustTokenQueryAnswerer> answerer;
    network_context_->GetTrustTokenQueryAnswerer(
        answerer.BindNewPipeAndPassReceiver(), origin);

    // Calling HasTrustTokens will associate the issuer argument with the
    // origin |origin|.
    //
    // Do this until the |origin| is associated with
    // network::kTrustTokenPerToplevelMaxNumberOfAssociatedIssuers many issuers
    // (namely 2; this value is not expected to change frequently).
    //
    // After the limit is reached, subsequent HasTrustToken(origin, issuer)
    // queries will fail for any issuers not in {https://prime0.example,
    // https://prime1.example} --- unless data for |origin| is cleared.
    for (int i = 0; i < 2; ++i) {
      base::RunLoop run_loop;
      answerer->HasTrustTokens(
          url::Origin::Create(
              GURL(base::StringPrintf("https://prime%d.example", i))),
          base::BindLambdaForTesting(
              [&](network::mojom::HasTrustTokensResultPtr) {
                run_loop.Quit();
              }));
      run_loop.Run();
    }
  }

  bool HasOrigin(const url::Origin& origin) {
    mojo::Remote<network::mojom::TrustTokenQueryAnswerer> answerer;
    network_context_->GetTrustTokenQueryAnswerer(
        answerer.BindNewPipeAndPassReceiver(), origin);

    base::RunLoop run_loop;
    bool has_origin = false;

    // Since https://probe.example is not among the issuer origins previously
    // provided to HasTrustTokens(origin, _) calls in AddOrigin:
    // - If data has not been cleared,
    //     HasTrustToken(origin, https://probe.example)
    //   is expected to fail with kSiteIssuerLimit because |origin| is at
    //   its number-of-associated-issuers limit, so the answerer will refuse
    //   to answer a query for an origin it has not yet seen.
    // - If data has been cleared, the answerer should be able to fulfill the
    //   query.
    answerer->HasTrustTokens(
        url::Origin::Create(GURL("https://probe.example")),
        base::BindLambdaForTesting(
            [&](network::mojom::HasTrustTokensResultPtr result) {
              // HasTrustTokens will error out with kSiteIssuerLimit exactly
              // when the top-frame origin |origin| was previously added by
              // AddOrigin.
              if (result->status ==
                  network::mojom::TrustTokenOperationStatus::kSiteIssuerLimit) {
                has_origin = true;
              }

              run_loop.Quit();
            }));

    run_loop.Run();

    return has_origin;
  }

 private:
  raw_ptr<network::mojom::NetworkContext> network_context_ = nullptr;
};

}  // namespace

using BrowsingDataRemoverImplTrustTokenTest =
    BrowsingDataRemoverImplBrowserTest;

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplTrustTokenTest, Remove) {
  TrustTokensTester tester(network_context());

  auto origin = url::Origin::Create(GURL("https://topframe.example"));

  tester.AddOrigin(origin);
  ASSERT_TRUE(tester.HasOrigin(origin));

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS);

  EXPECT_FALSE(tester.HasOrigin(origin));
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplTrustTokenTest, RemoveByDomain) {
  TrustTokensTester tester(network_context());

  auto origin = url::Origin::Create(GURL("https://topframe.example"));
  auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
  auto another_origin =
      url::Origin::Create(GURL("https://another-topframe.example"));

  tester.AddOrigin(origin);
  tester.AddOrigin(sub_origin);
  tester.AddOrigin(another_origin);

  ASSERT_TRUE(tester.HasOrigin(origin));
  ASSERT_TRUE(tester.HasOrigin(sub_origin));
  ASSERT_TRUE(tester.HasOrigin(another_origin));

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kDelete));
  builder->AddRegisterableDomain("topframe.example");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS,
                          std::move(builder));

  EXPECT_FALSE(tester.HasOrigin(origin));
  EXPECT_FALSE(tester.HasOrigin(sub_origin));
  EXPECT_TRUE(tester.HasOrigin(another_origin));
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplTrustTokenTest,
                       PreserveByDomain) {
  TrustTokensTester tester(network_context());

  auto origin = url::Origin::Create(GURL("https://topframe.example"));
  auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
  auto another_origin =
      url::Origin::Create(GURL("https://another-topframe.example"));

  tester.AddOrigin(origin);
  tester.AddOrigin(sub_origin);
  tester.AddOrigin(another_origin);
  ASSERT_TRUE(tester.HasOrigin(origin));
  ASSERT_TRUE(tester.HasOrigin(sub_origin));
  ASSERT_TRUE(tester.HasOrigin(another_origin));

  // Delete all data *except* that specified by the filter.
  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kPreserve));
  builder->AddRegisterableDomain("topframe.example");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS,
                          std::move(builder));

  EXPECT_TRUE(tester.HasOrigin(origin));
  EXPECT_TRUE(tester.HasOrigin(sub_origin));
  EXPECT_FALSE(tester.HasOrigin(another_origin));
}

class BrowsingDataRemoverImplSharedStorageBrowserTest
    : public BrowsingDataRemoverImplBrowserTest {
 public:
  BrowsingDataRemoverImplSharedStorageBrowserTest() {
    feature_list_.InitAndEnableFeature(network::features::kSharedStorageAPI);
  }

  StoragePartition* storage_partition() {
    return shell()
        ->web_contents()
        ->GetBrowserContext()
        ->GetDefaultStoragePartition();
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplSharedStorageBrowserTest,
                       Remove) {
  SharedStorageClearSiteDataTester tester(storage_partition());

  auto origin = url::Origin::Create(GURL("https://topframe.example"));

  tester.AddConsecutiveSharedStorageEntries(origin, u"key", u"value", 10);
  EXPECT_THAT(tester.GetSharedStorageOrigins(),
              testing::UnorderedElementsAre(origin));

  // Note that u"key" concatenated with a single digit has 4 char16_t's and
  // hence 8 bytes. Similarly, u"value" concatenated with one digit has
  // 6 char16_t's and hence 12 bytes. A pair of these together thus has
  // 20 bytes.
  const int kNumBytesPerEntry = 20;
  EXPECT_EQ(10 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());

  RemoveAndWait(BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE);

  EXPECT_TRUE(tester.GetSharedStorageOrigins().empty());
  EXPECT_EQ(0, tester.GetSharedStorageTotalBytes());
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplSharedStorageBrowserTest,
                       RemoveByDomain) {
  SharedStorageClearSiteDataTester tester(storage_partition());

  auto origin = url::Origin::Create(GURL("https://topframe.example"));
  auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
  auto another_origin =
      url::Origin::Create(GURL("https://another-topframe.example"));

  tester.AddConsecutiveSharedStorageEntries(origin, u"key", u"value", 5);
  tester.AddConsecutiveSharedStorageEntries(sub_origin, u"key", u"value", 10);
  tester.AddConsecutiveSharedStorageEntries(another_origin, u"key", u"value",
                                            1);
  EXPECT_THAT(
      tester.GetSharedStorageOrigins(),
      testing::UnorderedElementsAre(origin, sub_origin, another_origin));

  // Note that u"key" concatenated with a single digit has 4 char16_t's and
  // hence 8 bytes. Similarly, u"value" concatenated with one digit has
  // 6 char16_t's and hence 12 bytes. A pair of these together thus has
  // 20 bytes.
  const int kNumBytesPerEntry = 20;
  EXPECT_EQ(16 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());

  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kDelete));
  builder->AddRegisterableDomain("topframe.example");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE,
                          std::move(builder));

  EXPECT_THAT(tester.GetSharedStorageOrigins(),
              testing::UnorderedElementsAre(another_origin));

  EXPECT_EQ(1 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplSharedStorageBrowserTest,
                       PreserveByDomain) {
  SharedStorageClearSiteDataTester tester(storage_partition());

  auto origin = url::Origin::Create(GURL("https://topframe.example"));
  auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
  auto another_origin =
      url::Origin::Create(GURL("https://another-topframe.example"));

  tester.AddConsecutiveSharedStorageEntries(origin, u"key", u"value", 5);
  tester.AddConsecutiveSharedStorageEntries(sub_origin, u"key", u"value", 10);
  tester.AddConsecutiveSharedStorageEntries(another_origin, u"key", u"value",
                                            1);
  EXPECT_THAT(
      tester.GetSharedStorageOrigins(),
      testing::UnorderedElementsAre(origin, sub_origin, another_origin));

  // Note that u"key" concatenated with a single digit has 4 char16_t's and
  // hence 8 bytes. Similarly, u"value" concatenated with one digit has
  // 6 char16_t's and hence 12 bytes. A pair of these together thus has
  // 20 bytes.
  const int kNumBytesPerEntry = 20;
  EXPECT_EQ(16 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());

  // Delete all data *except* that specified by the filter.
  std::unique_ptr<BrowsingDataFilterBuilder> builder(
      BrowsingDataFilterBuilder::Create(
          BrowsingDataFilterBuilder::Mode::kPreserve));
  builder->AddRegisterableDomain("topframe.example");
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE,
                          std::move(builder));

  EXPECT_THAT(tester.GetSharedStorageOrigins(),
              testing::UnorderedElementsAre(origin, sub_origin));
  EXPECT_EQ(15 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
}

class BrowsingDataRemoverImplPrerenderingBrowserTest
    : public BrowsingDataRemoverImplBrowserTest {
 public:
  BrowsingDataRemoverImplPrerenderingBrowserTest() {
    prerender_helper_ =
        std::make_unique<test::PrerenderTestHelper>(base::BindRepeating(
            &BrowsingDataRemoverImplPrerenderingBrowserTest::web_contents,
            base::Unretained(this)));
  }
  ~BrowsingDataRemoverImplPrerenderingBrowserTest() override = default;

 protected:
  test::PrerenderTestHelper& prerender_helper() { return *prerender_helper_; }

  WebContents* web_contents() { return shell()->web_contents(); }

  std::unique_ptr<test::PrerenderTestHelper> prerender_helper_;
};

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrerenderingBrowserTest,
                       ClearCacheCancelsPrerendering) {
  GURL initial_url = ssl_server().GetURL("/title1.html");
  GURL prerendering_url = ssl_server().GetURL("/empty.html");

  // 1) Navigate to the initial url.
  ASSERT_TRUE(NavigateToURL(shell(), initial_url));

  // 2) Add and wait for prerendering of the prerendering url to complete.
  FrameTreeNodeId host_id = prerender_helper().AddPrerender(prerendering_url);
  content::test::PrerenderHostObserver host_observer(*web_contents(), host_id);

  // 3) Remove the browsing data with DATA_TYPE_CACHE.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));
  host_observer.WaitForDestroyed();

  // 4) Verify that prerendering was canceled due to removing browsing data.
  histogram_tester().ExpectUniqueSample(
      "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule",
      PrerenderFinalStatus::kBrowsingDataRemoved, 1);
}

class BrowsingDataRemoverImplPrefetchBrowserTest
    : public BrowsingDataRemoverImplBrowserTest {
 public:
  void StartPrefetch(const GURL& url, Shell* shell) {
    auto* prefetch_document_manager =
        PrefetchDocumentManager::GetOrCreateForCurrentDocument(
            shell->web_contents()->GetPrimaryMainFrame());
    auto candidate = blink::mojom::SpeculationCandidate::New();
    candidate->url = url;
    candidate->action = blink::mojom::SpeculationAction::kPrefetch;
    candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
    candidate->referrer = Referrer::SanitizeForRequest(
        url, blink::mojom::Referrer(
                 shell->web_contents()->GetURL(),
                 network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
    std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
    candidates.push_back(std::move(candidate));
    prefetch_document_manager->ProcessCandidates(candidates);
  }

  ~BrowsingDataRemoverImplPrefetchBrowserTest() override = default;
};

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrefetchBrowserTest,
                       ClearCacheCancelsPrefetch) {
  GURL initial_url = ssl_server().GetURL("/empty.html");
  GURL prefetch_url = ssl_server().GetURL("/title1.html");

  // 1) Navigate to the initial url.
  ASSERT_TRUE(NavigateToURL(shell(), initial_url));

  test::TestPrefetchWatcher test_prefetch_watcher;

  // 2) Start prefetching the prefetch_url.
  StartPrefetch(prefetch_url, shell());

  // 3) Wait for the prefetch_url to finish prefetching.
  test_prefetch_watcher.WaitUntilPrefetchResponseCompleted(
      static_cast<RenderFrameHostImpl*>(
          shell()->web_contents()->GetPrimaryMainFrame())
          ->GetDocumentToken(),
      prefetch_url);

  // 4) Remove the browsing data with DATA_TYPE_CACHE.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));

  // 5) Verify that the prefetch failed due to removing browsing data.
  histogram_tester().ExpectUniqueSample(
      "Preloading.Prefetch.PrefetchStatus",
      PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 1);
}

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrefetchBrowserTest,
                       ClearCacheCancelsPrefetchMultipleOrigins) {
  // Test prefetch cancellation for two different origins.
  // As part of the Clear Browsing Data trigger, prefetches that have
  // referral origins in the BrowsingDataFilterBuilder should be canceled.
  GURL referral_url1 = ssl_server().GetURL("/empty.html");
  GURL prefetch_url1 = ssl_server().GetURL("/title1.html");

  GURL referral_url2 = ssl_server().GetURL("a.test", "/title1.html");
  GURL prefetch_url2 = ssl_server().GetURL("a.test", "/empty.html");

  // 1) Navigate to the referral url for origin 1.
  ASSERT_TRUE(NavigateToURL(shell(), referral_url1));

  // 2) Open a new tab and navigate to the referral url for origin 2.
  Shell* new_shell =
      Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
                             GURL(url::kAboutBlankURL), nullptr, gfx::Size());

  ASSERT_TRUE(NavigateToURL(new_shell, referral_url2));

  test::TestPrefetchWatcher test_prefetch_watcher;

  // 3) Start prefetching the first url and wait for it to finish prefetching.
  StartPrefetch(prefetch_url1, shell());

  test_prefetch_watcher.WaitUntilPrefetchResponseCompleted(
      static_cast<RenderFrameHostImpl*>(
          shell()->web_contents()->GetPrimaryMainFrame())
          ->GetDocumentToken(),
      prefetch_url1);

  // 4) Start prefetching the second url and wait for it to finish prefetching.
  StartPrefetch(prefetch_url2, new_shell);

  test_prefetch_watcher.WaitUntilPrefetchResponseCompleted(
      static_cast<RenderFrameHostImpl*>(
          new_shell->web_contents()->GetPrimaryMainFrame())
          ->GetDocumentToken(),
      prefetch_url2);

  // 5) Remove the browsing data with DATA_TYPE_CACHE.
  // Mode::kPreserve + no origins/domains = delete everything.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kPreserve);
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));

  // 6) Verify that both prefetches failed due to removing browsing data.
  histogram_tester().ExpectUniqueSample(
      "Preloading.Prefetch.PrefetchStatus",
      PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 2);
}

class BrowsingDataRemoverImplPrefetchHoldbackBrowserTest
    : public BrowsingDataRemoverImplPrefetchBrowserTest {
 public:
  BrowsingDataRemoverImplPrefetchHoldbackBrowserTest() {
    feature_list_.InitAndEnableFeatureWithParameters(
        features::kPreloadingConfig, {{"preloading_config", R"(
            [{
              "preloading_type": "Prefetch",
              "preloading_predictor": "SpeculationRules",
              "holdback": true,
              "sampling_likelihood": 1
            }])"}});
  }

  ~BrowsingDataRemoverImplPrefetchHoldbackBrowserTest() override = default;

 private:
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrefetchHoldbackBrowserTest,
                       ClearCacheCancelsHeldbackPrefetch) {
  GURL initial_url = ssl_server().GetURL("/empty.html");
  GURL prefetch_url = ssl_server().GetURL("/title1.html");

  // 1) Navigate to the initial url.
  ASSERT_TRUE(NavigateToURL(shell(), initial_url));

  // 2) Start prefetching the prefetch_url.
  StartPrefetch(prefetch_url, shell());

  // 3) Remove the browsing data with DATA_TYPE_CACHE.
  auto filter = BrowsingDataFilterBuilder::Create(
      BrowsingDataFilterBuilder::Mode::kDelete);
  filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
  RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
                          std::move(filter));

  // 4) Verify that the prefetch failed due to removing browsing data.
  histogram_tester().ExpectUniqueSample(
      "Preloading.Prefetch.PrefetchStatus",
      PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 1);
}
}  // namespace content