// 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 <atomic>
#include <optional>
#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/threading/thread_restrictions.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/embedder_support/switches.h"
#include "components/embedder_support/user_agent_utils.h"
#include "components/language/core/browser/pref_names.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/common/child_process_id_util.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/base/address_list.h"
#include "net/base/features.h"
#include "net/base/filename_util.h"
#include "net/base/host_port_pair.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_options.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/resolve_error_info.h"
#include "net/http/http_response_headers.h"
#include "net/reporting/reporting_policy.h"
#include "net/ssl/ssl_config.h"
#include "net/ssl/ssl_server_config.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/install_default_websocket_handlers.h"
#include "net/test/gtest_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/referrer_policy.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/network_connection_tracker.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/websocket.mojom.h"
#include "services/network/test/test_dns_util.h"
#include "services/network/test/test_url_loader_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif

#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
#include "extensions/browser/browsertest_util.h"
#include "extensions/test/test_extension_dir.h"
#endif

namespace {

constexpr char kHostname[] = "foo.test";
constexpr char kHstsHostname[] = "hsts.test";
constexpr char kAddress[] = "5.8.13.21";

const char kCacheRandomPath[] = "/cacherandom";

// Path using a ControllableHttpResponse that's part of the test fixture.
const char kControllablePath[] = "/controllable";

enum class NetworkServiceState {
  kEnabled,
  // Similar to |kEnabled|, but will simulate a crash and run tests again the
  // restarted Network Service process.
  kRestarted,
};

enum class NetworkContextType {
  kSystem,
  kSafeBrowsing,
  kProfile,
  kIncognitoProfile,
  kOnDiskApp,
  kInMemoryApp,
  kOnDiskAppWithIncognitoProfile,
};

// This list should be kept in sync with the NetworkContextType enum.
const NetworkContextType kNetworkContextTypes[] = {
    NetworkContextType::kSystem,
    NetworkContextType::kSafeBrowsing,
    NetworkContextType::kProfile,
    NetworkContextType::kIncognitoProfile,
    NetworkContextType::kOnDiskApp,
    NetworkContextType::kInMemoryApp,
    NetworkContextType::kOnDiskAppWithIncognitoProfile,
};

struct TestCase {
  NetworkServiceState network_service_state;
  NetworkContextType network_context_type;
};

// Waits for the network connection type to be the specified value.
class ConnectionTypeWaiter
    : public network::NetworkConnectionTracker::NetworkConnectionObserver {
 public:
  ConnectionTypeWaiter() : tracker_(content::GetNetworkConnectionTracker()) {
    tracker_->AddNetworkConnectionObserver(this);
  }

  ~ConnectionTypeWaiter() override {
    tracker_->RemoveNetworkConnectionObserver(this);
  }

  void Wait(net::NetworkChangeNotifier::ConnectionType expected_type) {
    auto current_type =
        net::NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN;
    for (;;) {
      network::NetworkConnectionTracker::ConnectionTypeCallback callback =
          base::BindOnce(&ConnectionTypeWaiter::OnConnectionChanged,
                         base::Unretained(this));
      if (tracker_->GetConnectionType(&current_type, std::move(callback)) &&
          current_type == expected_type)
        break;
      run_loop_ = std::make_unique<base::RunLoop>(
          base::RunLoop::Type::kNestableTasksAllowed);
      run_loop_->Run();
    }
  }

 private:
  // network::NetworkConnectionTracker::NetworkConnectionObserver:
  void OnConnectionChanged(
      net::NetworkChangeNotifier::ConnectionType type) override {
    if (run_loop_)
      run_loop_->Quit();
  }

  raw_ptr<network::NetworkConnectionTracker> tracker_;
  std::unique_ptr<base::RunLoop> run_loop_;
};

// Tests the system, profile, and incognito profile NetworkContexts.
class NetworkContextConfigurationBrowserTest
    : public InProcessBrowserTest,
      public testing::WithParamInterface<TestCase> {
 public:
  // Backing storage types that can used for various things (HTTP cache,
  // cookies, etc).
  enum class StorageType {
    kNone,
    kMemory,
    kDisk,
  };

  enum class CookieType {
    kFirstParty,
    kThirdParty,
  };

  enum class CookiePersistenceType {
    kSession,
    kPersistent,
  };

  NetworkContextConfigurationBrowserTest()
      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
    // TODO(crbug.com/334954143) Fix the tests when turning on the reduce
    // accept-language feature.
    scoped_feature_list_.InitWithFeatures(
        {}, {network::features::kReduceAcceptLanguage});

    // Have to get a port before setting up the command line, but can only set
    // up the connection listener after there's a main thread, so can't start
    // the test server here.
    EXPECT_TRUE(embedded_test_server()->InitializeAndListen());
    EXPECT_TRUE(https_server()->InitializeAndListen());
  }

  // Returns a cacheable response (10 hours) that is some random text.
  static std::unique_ptr<net::test_server::HttpResponse> HandleCacheRandom(
      const net::test_server::HttpRequest& request) {
    if (request.relative_url != kCacheRandomPath)
      return nullptr;

    std::unique_ptr<net::test_server::BasicHttpResponse> response =
        std::make_unique<net::test_server::BasicHttpResponse>();
    response->set_content(base::Uuid::GenerateRandomV4().AsLowercaseString());
    response->set_content_type("text/plain");
    response->AddCustomHeader("Cache-Control", "max-age=60000");
    return std::move(response);
  }

  NetworkContextConfigurationBrowserTest(
      const NetworkContextConfigurationBrowserTest&) = delete;
  NetworkContextConfigurationBrowserTest& operator=(
      const NetworkContextConfigurationBrowserTest&) = delete;

  ~NetworkContextConfigurationBrowserTest() override = default;

  void SetUpOnMainThread() override {
    // Used in a bunch of proxy tests. Should not resolve.
    host_resolver()->AddSimulatedFailure("does.not.resolve.test");

    host_resolver()->AddRule(kHostname, kAddress);
    host_resolver()->AddRule(kHstsHostname, "127.0.0.1");

    controllable_http_response_ =
        std::make_unique<net::test_server::ControllableHttpResponse>(
            embedded_test_server(), kControllablePath);
    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
        &NetworkContextConfigurationBrowserTest::HandleCacheRandom));
    embedded_test_server()->StartAcceptingConnections();
    net::test_server::RegisterDefaultHandlers(https_server());
    https_server()->StartAcceptingConnections();

    if (is_incognito())
      incognito_ = CreateIncognitoBrowser();
    SimulateNetworkServiceCrashIfNecessary();

#if BUILDFLAG(IS_CHROMEOS)
    // On ChromeOS the connection type comes from a fake Shill service, which
    // is configured with a fake ethernet connection asynchronously. Wait for
    // the connection type to be available to avoid getting notified of the
    // connection change halfway through the test.
    ConnectionTypeWaiter().Wait(
        net::NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET);
#endif
  }

  // Returns true if the NetworkContext being tested is associated with an
  // incognito profile.
  bool is_incognito() const {
    return GetParam().network_context_type ==
               NetworkContextType::kIncognitoProfile ||
           GetParam().network_context_type ==
               NetworkContextType::kOnDiskAppWithIncognitoProfile;
  }

  void TearDownOnMainThread() override {
    // Have to destroy this before the main message loop is torn down. Need to
    // leave the embedded test server up for tests that use
    // |live_during_shutdown_simple_loader_|. It's safe to destroy the
    // ControllableHttpResponse before the test server.
    controllable_http_response_.reset();
  }

  // Returns, as a string, a PAC script that will use the EmbeddedTestServer as
  // a proxy.
  std::string GetPacScript() const {
    return base::StringPrintf(
        "function FindProxyForURL(url, host){ return 'PROXY %s;' }",
        net::HostPortPair::FromURL(embedded_test_server()->base_url())
            .ToString()
            .c_str());
  }

  net::EmbeddedTestServer* https_server() { return &https_server_; }

  content::StoragePartition* GetStoragePartition() {
    return GetStoragePartitionForContextType(GetParam().network_context_type);
  }

  content::StoragePartition* GetStoragePartitionForContextType(
      NetworkContextType network_context_type) {
    const auto kOnDiskConfig = content::StoragePartitionConfig::Create(
        browser()->GetProfile(), "foo", /*partition_name=*/"",
        /*in_memory=*/false);
    const auto kInMemoryConfig = content::StoragePartitionConfig::Create(
        browser()->GetProfile(), "foo", /*partition_name=*/"",
        /*in_memory=*/true);

    switch (network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kSafeBrowsing:
        NOTREACHED() << "Network context has no storage partition";
      case NetworkContextType::kProfile:
        return browser()->GetProfile()->GetDefaultStoragePartition();
      case NetworkContextType::kIncognitoProfile:
        DCHECK(incognito_);
        return incognito_->GetProfile()->GetDefaultStoragePartition();
      case NetworkContextType::kOnDiskApp:
        return browser()->GetProfile()->GetStoragePartition(kOnDiskConfig);
      case NetworkContextType::kInMemoryApp:
        return browser()->GetProfile()->GetStoragePartition(kInMemoryConfig);
      case NetworkContextType::kOnDiskAppWithIncognitoProfile: {
        DCHECK(incognito_);
        // Note: Even though we are requesting an on-disk config, the function
        // will return an in-memory config because incognito profiles are not
        // supposed to to use on-disk storage.
        const auto kIncognitoConfig = content::StoragePartitionConfig::Create(
            incognito_->GetProfile(), "foo", /*partition_name=*/"",
            /*in_memory=*/false);
        DCHECK(kIncognitoConfig.in_memory());

        return incognito_->GetProfile()->GetStoragePartition(kIncognitoConfig);
      }
    }
    NOTREACHED();
  }

  network::mojom::URLLoaderFactory* loader_factory() {
    return GetLoaderFactoryForContextType(GetParam().network_context_type);
  }

  network::mojom::URLLoaderFactory* GetLoaderFactoryForContextType(
      NetworkContextType network_context_type) {
    switch (network_context_type) {
      case NetworkContextType::kSystem:
        return g_browser_process->system_network_context_manager()
            ->GetURLLoaderFactory();
      case NetworkContextType::kSafeBrowsing:
        return g_browser_process->safe_browsing_service()
            ->GetURLLoaderFactory(browser()->GetProfile())
            .get();
      case NetworkContextType::kProfile:
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kOnDiskApp:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        return GetStoragePartitionForContextType(network_context_type)
            ->GetURLLoaderFactoryForBrowserProcess()
            .get();
    }
    NOTREACHED();
  }

  network::mojom::NetworkContext* network_context() {
    return GetNetworkContextForContextType(GetParam().network_context_type);
  }

  network::mojom::NetworkContext* GetNetworkContextForContextType(
      NetworkContextType network_context_type) {
    switch (network_context_type) {
      case NetworkContextType::kSystem:
        return g_browser_process->system_network_context_manager()
            ->GetContext();
      case NetworkContextType::kSafeBrowsing:
        return g_browser_process->safe_browsing_service()->GetNetworkContext(
            browser()->GetProfile());
      case NetworkContextType::kProfile:
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kOnDiskApp:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        return GetStoragePartitionForContextType(network_context_type)
            ->GetNetworkContext();
    }
    NOTREACHED();
  }

  StorageType GetHttpCacheType() const {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kSafeBrowsing:
        return StorageType::kNone;
      case NetworkContextType::kProfile:
      case NetworkContextType::kOnDiskApp:
        return StorageType::kDisk;
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        return StorageType::kMemory;
    }
    NOTREACHED();
  }

  StorageType GetCookieStorageType() const {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        return StorageType::kMemory;
      case NetworkContextType::kSafeBrowsing:
      case NetworkContextType::kProfile:
      case NetworkContextType::kOnDiskApp:
        return StorageType::kDisk;
    }
    NOTREACHED();
  }

  // Returns the pref service with most prefs related to the NetworkContext
  // being tested.
  PrefService* GetPrefService() {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
        return g_browser_process->local_state();
      case NetworkContextType::kSafeBrowsing:
      case NetworkContextType::kProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskApp:
        return browser()->GetProfile()->GetPrefs();
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        // Incognito actually uses the non-incognito prefs, so this should end
        // up being the same pref store as in the KProfile case.
        return browser()
            ->GetProfile()
            ->GetPrimaryOTRProfile(/*create_if_needed=*/true)
            ->GetPrefs();
    }
  }

  // Sets the proxy preference on a PrefService based on the NetworkContextType,
  // and waits for it to be applied.
  void SetProxyPref(const net::HostPortPair& host_port_pair) {
    GetPrefService()->SetDict(proxy_config::prefs::kProxy,
                              ProxyConfigDictionary::CreateFixedServers(
                                  host_port_pair.ToString(), std::string()));

    // Wait for the new ProxyConfig to be passed over the pipe. Needed because
    // Mojo doesn't guarantee ordering of events on different Mojo pipes, and
    // requests are sent on a separate pipe from ProxyConfigs.
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kSafeBrowsing:
        g_browser_process->system_network_context_manager()
            ->FlushProxyConfigMonitorForTesting();
        break;
      case NetworkContextType::kProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskApp:
        ProfileNetworkContextServiceFactory::GetForContext(
            browser()->GetProfile())
            ->FlushProxyConfigMonitorForTesting();
        break;
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        ProfileNetworkContextServiceFactory::GetForContext(
            browser()->GetProfile()->GetPrimaryOTRProfile(
                /*create_if_needed=*/true))
            ->FlushProxyConfigMonitorForTesting();
        break;
    }
  }

  // Sends a request and expects it to be handled by embedded_test_server()
  // acting as a proxy;
  void TestProxyConfigured(bool expect_success) {
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    // This URL should be directed to the test server because of the proxy.
    request->url = GURL("http://does.not.resolve.test:1872/echo");
    request->credentials_mode = network::mojom::CredentialsMode::kOmit;

    content::SimpleURLLoaderTestHelper simple_loader_helper;
    std::unique_ptr<network::SimpleURLLoader> simple_loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);

    simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        loader_factory(), simple_loader_helper.GetCallback());
    simple_loader_helper.WaitForCallback();

    if (expect_success) {
      EXPECT_EQ(net::OK, simple_loader->NetError());
      ASSERT_TRUE(simple_loader_helper.response_body());
      EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
    } else {
      EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, simple_loader->NetError());
      ASSERT_FALSE(simple_loader_helper.response_body());
    }
  }

  // Makes a request that hangs, and will live until browser shutdown.
  void MakeLongLivedRequestThatHangsUntilShutdown() {
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    request->url = embedded_test_server()->GetURL(kControllablePath);
    request->credentials_mode = network::mojom::CredentialsMode::kOmit;
    live_during_shutdown_simple_loader_ = network::SimpleURLLoader::Create(
        std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);

    live_during_shutdown_simple_loader_->DownloadHeadersOnly(loader_factory(),
                                                             base::DoNothing());

    // Don't actually care about controlling the response, just need to wait
    // until it sees the request, to make sure that a URLRequest has been
    // created to potentially leak. Since the |controllable_http_response_| is
    // not used to send a response to the request, the request just hangs until
    // the NetworkContext is destroyed (Or the test server is shut down, but the
    // NetworkContext should be destroyed before that happens, in this test).
    controllable_http_response_->WaitForRequest();
  }

  // Sends a request to the test server's echoheader URL and sets
  // |header_value_out| to the value of the specified response header. Returns
  // false if the request fails. If non-null, uses |request| to make the
  // request, after setting its |url| value.
  [[nodiscard]] bool FetchHeaderEcho(
      const std::string& header_name,
      std::string* header_value_out,
      std::unique_ptr<network::ResourceRequest> request = nullptr) {
    if (!request)
      request = std::make_unique<network::ResourceRequest>();
    request->url = embedded_test_server()->GetURL(
        base::StrCat({"/echoheader?", header_name}));
    content::SimpleURLLoaderTestHelper simple_loader_helper;
    std::unique_ptr<network::SimpleURLLoader> simple_loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);
    simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        loader_factory(), simple_loader_helper.GetCallback());
    simple_loader_helper.WaitForCallback();
    if (simple_loader_helper.response_body()) {
      *header_value_out = *simple_loader_helper.response_body();
      return true;
    }
    return false;
  }

  // |server| should be |TYPE_HTTPS| if the cookie is third-party, because
  // SameSite=None cookies must be Secure.
  void SetCookie(CookieType cookie_type,
                 CookiePersistenceType cookie_expiration_type,
                 net::EmbeddedTestServer* server) {
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    std::string cookie_line = "cookie";
    if (cookie_expiration_type == CookiePersistenceType::kPersistent)
      cookie_line += ";max-age=3600";
    // Third party (i.e. SameSite=None) cookies must be secure.
    if (cookie_type == CookieType::kThirdParty)
      cookie_line += ";SameSite=None;Secure";
    request->url = server->GetURL("/set-cookie?" + cookie_line);
    url::Origin origin;
    if (cookie_type == CookieType::kThirdParty)
      origin = url::Origin::Create(GURL("http://example.com"));
    else
      origin = url::Origin::Create(server->base_url());
    request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);

    request->trusted_params = network::ResourceRequest::TrustedParams();
    request->trusted_params->isolation_info =
        net::IsolationInfo::CreateForInternalRequest(origin);

    base::RunLoop run_loop;
    std::unique_ptr<network::SimpleURLLoader> simple_loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);

    simple_loader->DownloadHeadersOnly(
        loader_factory(),
        base::BindLambdaForTesting(
            [&](scoped_refptr<net::HttpResponseHeaders> headers) {
              run_loop.Quit();
            }));
    run_loop.Run();
    EXPECT_EQ(net::OK, simple_loader->NetError());
  }

  void FlushNetworkInterface() {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
        g_browser_process->system_network_context_manager()
            ->FlushNetworkInterfaceForTesting();
        break;
      case NetworkContextType::kSafeBrowsing:
        g_browser_process->safe_browsing_service()
            ->FlushNetworkInterfaceForTesting(GetProfile());
        break;
      case NetworkContextType::kProfile:
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        GetStoragePartition()->FlushNetworkInterfaceForTesting();
        break;
    }
  }

  Profile* GetProfile() {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kSafeBrowsing:
      case NetworkContextType::kProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskApp:
        return browser()->GetProfile();
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        DCHECK(incognito_);
        return incognito_->GetProfile();
    }
  }

  // Gets all cookies for given URL, as a single string.
  std::string GetCookies(const GURL& url) {
    return GetCookiesForContextType(GetParam().network_context_type, url);
  }

  std::string GetCookiesForContextType(NetworkContextType network_context_type,
                                       const GURL& url) {
    std::string cookies;
    base::RunLoop run_loop;
    mojo::Remote<network::mojom::CookieManager> cookie_manager;
    GetNetworkContextForContextType(network_context_type)
        ->GetCookieManager(cookie_manager.BindNewPipeAndPassReceiver());
    cookie_manager->GetCookieList(
        url, net::CookieOptions::MakeAllInclusive(),
        net::CookiePartitionKeyCollection(),
        base::BindOnce(
            [](std::string* cookies_out, base::RunLoop* run_loop,
               const net::CookieAccessResultList& cookies,
               const net::CookieAccessResultList& excluded_cookies) {
              *cookies_out = net::CanonicalCookie::BuildCookieLine(cookies);
              run_loop->Quit();
            },
            &cookies, &run_loop));
    run_loop.Run();
    return cookies;
  }

  void ForEachOtherContext(
      base::RepeatingCallback<void(NetworkContextType network_context_type)>
          callback) {
    // Create Incognito Profile if needed.
    if (!incognito_)
      incognito_ = CreateIncognitoBrowser();

    // True if the |network_context_type| corresponding to GetParam() has been
    // found. Used to verify that kNetworkContextTypes is kept up to date, and
    // contains no duplicates.
    bool found_matching_type = false;
    for (const auto network_context_type : kNetworkContextTypes) {
      if (network_context_type == GetParam().network_context_type) {
        EXPECT_FALSE(found_matching_type);
        found_matching_type = true;
        continue;
      }
      callback.Run(network_context_type);
    }
    EXPECT_TRUE(found_matching_type);
  }

  bool IsRestartStateWithInProcessNetworkService() {
    return GetParam().network_service_state ==
               NetworkServiceState::kRestarted &&
           content::IsInProcessNetworkService();
  }

 private:
  void SimulateNetworkServiceCrashIfNecessary() {
    if (GetParam().network_service_state != NetworkServiceState::kRestarted ||
        content::IsInProcessNetworkService()) {
      return;
    }

    // Make sure |network_context()| is working as expected. Use '/echoheader'
    // instead of '/echo' to avoid a disk_cache bug.
    // See https://crbug.com/40553335.
    auto request = std::make_unique<network::ResourceRequest>();
    request->url = embedded_test_server()->GetURL("/echoheader");
    request->load_flags = net::LOAD_BYPASS_PROXY;
    content::SimpleURLLoaderTestHelper simple_loader_helper;
    std::unique_ptr<network::SimpleURLLoader> simple_loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);
    // Choice of URLLoaderFactory doesn't matter here, other than that it's
    // trusted so can bypass proxies, since this is just to make sure the
    // network service has fully started up.
    simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        g_browser_process->system_network_context_manager()
            ->GetURLLoaderFactory(),
        simple_loader_helper.GetCallback());
    simple_loader_helper.WaitForCallback();
    EXPECT_THAT(simple_loader->NetError(), net::test::IsOk());

    // Crash the NetworkService process. Existing interfaces should receive
    // error notifications at some point.
    SimulateNetworkServiceCrash();
    // Flush the interface to make sure the error notification was received.
    FlushNetworkInterface();
  }

  raw_ptr<BrowserWindowInterface, AcrossTasksDanglingUntriaged> incognito_ =
      nullptr;

  net::EmbeddedTestServer https_server_;
  std::unique_ptr<net::test_server::ControllableHttpResponse>
      controllable_http_response_;

  // Used in tests that need a live request during browser shutdown.
  std::unique_ptr<network::SimpleURLLoader> live_during_shutdown_simple_loader_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       SecureCookiesAllowedForChromeScheme) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The system and SafeBrowsing network contexts don't support cookie options,
  // including the special scheme-based rules.
  bool system =
      GetParam().network_context_type == NetworkContextType::kSystem ||
      GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
  if (system)
    return;

  // Cookies are only allowed for chrome:// schemes requesting a secure origin,
  // so create an HTTPS server.
  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  net::test_server::RegisterDefaultHandlers(&https_server);
  ASSERT_TRUE(https_server.Start());
  if (GetPrefService()->FindPreference(prefs::kCookieControlsMode)) {
    GetPrefService()->SetInteger(
        prefs::kCookieControlsMode,
        static_cast<int>(
            content_settings::CookieControlsMode::kBlockThirdParty));
  }
  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
            embedded_test_server());

  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = https_server.GetURL("/echoheader?Cookie");
  url::Origin origin = url::Origin::Create(GURL(chrome::kChromeUIPrintURL));
  request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);
  request->trusted_params = network::ResourceRequest::TrustedParams();
  request->trusted_params->isolation_info =
      net::IsolationInfo::CreateForInternalRequest(origin);
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
  ASSERT_TRUE(simple_loader_helper.response_body());
  EXPECT_EQ("cookie", *simple_loader_helper.response_body());
}

#if BUILDFLAG(ENABLE_EXTENSIONS)
std::unique_ptr<net::test_server::HttpResponse> EchoCookieHeader(
    const net::test_server::HttpRequest& request) {
  auto response = std::make_unique<net::test_server::BasicHttpResponse>();
  auto it = request.headers.find("Cookie");
  if (it != request.headers.end())
    response->set_content(it->second);
  response->AddCustomHeader("Access-Control-Allow-Origin", "*");
  return response;
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       ThirdPartyCookiesAllowedForExtensions) {
  // TODO(https://crbug.com/40804030): Remove this when updated to use MV3.
  extensions::ScopedTestMV2Enabler mv2_enabler;

  if (IsRestartStateWithInProcessNetworkService()) {
    return;
  }

  // Loading an extension only makes sense for profile contexts.
  if (GetParam().network_context_type != NetworkContextType::kProfile &&
      GetParam().network_context_type !=
          NetworkContextType::kIncognitoProfile) {
    return;
  }
  net::EmbeddedTestServer test_server;
  net::test_server::RegisterDefaultHandlers(&test_server);
  test_server.RegisterRequestHandler(base::BindRepeating(&EchoCookieHeader));
  ASSERT_TRUE(test_server.Start());

  GetPrefService()->SetInteger(
      prefs::kCookieControlsMode,
      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
            embedded_test_server());

  extensions::TestExtensionDir extension_dir;
  extension_dir.WriteManifest(R"({
    "name": "Cookie Test",
    "manifest_version": 2,
    "version": "1.0",
    "background": {
      "scripts": ["background.js"]
    },
    "incognito": "split",
    "permissions": ["<all_urls>"]
   })");
  extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), "");
  extensions::ChromeTestExtensionLoader loader(GetProfile());
  loader.set_allow_incognito_access(true);
  scoped_refptr<const extensions::Extension> extension =
      loader.LoadExtension(extension_dir.UnpackedPath());
  ASSERT_TRUE(extension);

  // This request will show up as cross-site because the chrome-extension URL
  // won't match the test_server domain (127.0.0.1), but because we set
  // |force_ignore_site_for_cookies| to true for extension-initiated requests,
  // this will actually be able to get the cookie.
  GURL url = test_server.GetURL("/echocookieheader");
  std::string script = R"((url => {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onload = () => chrome.test.sendScriptResult(xhr.responseText);
    xhr.send();
  }))";
  base::Value result =
      extensions::browsertest_util::ExecuteScriptInBackgroundPage(
          GetProfile(), extension->id(), script + "('" + url.spec() + "')");
  EXPECT_EQ("cookie", result);
}
#endif

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, BasicRequest) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = embedded_test_server()->GetURL("/echo");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  ASSERT_TRUE(simple_loader->ResponseInfo());
  ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
  EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
  ASSERT_TRUE(simple_loader_helper.response_body());
  EXPECT_EQ("Echo", *simple_loader_helper.response_body());
}

// Make sure a cache is used when expected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, Cache) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // Crashing the network service may corrupt the disk cache, so skip this test
  // in tests that crash the network service and use an on-disk cache.
  if (GetParam().network_service_state == NetworkServiceState::kRestarted &&
      GetHttpCacheType() == StorageType::kDisk) {
    return;
  }

  // Make a request whose response should be cached.
  GURL request_url = embedded_test_server()->GetURL(kCacheRandomPath);
  url::Origin request_origin =
      url::Origin::Create(embedded_test_server()->base_url());
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = request_url;
  request->trusted_params = network::ResourceRequest::TrustedParams();
  request->trusted_params->isolation_info =
      net::IsolationInfo::CreateForInternalRequest(request_origin);
  request->site_for_cookies =
      request->trusted_params->isolation_info.site_for_cookies();

  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  ASSERT_TRUE(simple_loader_helper.response_body());
  EXPECT_GT(simple_loader_helper.response_body()->size(), 0u);

  // Make the request again, and make sure it's cached or not, according to
  // expectations. Reuse the content::ResourceRequest, but nothing else.
  std::unique_ptr<network::ResourceRequest> request2 =
      std::make_unique<network::ResourceRequest>();
  request2->url = request_url;
  request2->trusted_params = network::ResourceRequest::TrustedParams();
  request2->trusted_params->isolation_info =
      net::IsolationInfo::CreateForInternalRequest(request_origin);
  request2->site_for_cookies =
      request2->trusted_params->isolation_info.site_for_cookies();

  content::SimpleURLLoaderTestHelper simple_loader_helper2;
  std::unique_ptr<network::SimpleURLLoader> simple_loader2 =
      network::SimpleURLLoader::Create(std::move(request2),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);
  simple_loader2->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper2.GetCallback());
  simple_loader_helper2.WaitForCallback();
  ASSERT_TRUE(simple_loader_helper2.response_body());
  if (GetHttpCacheType() == StorageType::kNone) {
    // If there's no cache, the request should have returned a different
    // response than before.
    EXPECT_NE(*simple_loader_helper.response_body(),
              *simple_loader_helper2.response_body());
  } else {
    // Otherwise, the request should have returned the same result as before.
    EXPECT_EQ(*simple_loader_helper.response_body(),
              *simple_loader_helper2.response_body());
  }
}

// Make sure that NetworkContexts can't access each other's disk caches.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, CacheIsolation) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // Make a request whose response should be cached.
  GURL request_url = embedded_test_server()->GetURL("/cachetime");
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = request_url;
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  ASSERT_TRUE(simple_loader_helper.response_body());
  EXPECT_GT(simple_loader_helper.response_body()->size(), 0u);

  // Stop the server.
  ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());

  // Make the request again, for each other network context, and make sure the
  // response is not cached.
  ForEachOtherContext(
      base::BindLambdaForTesting([&](NetworkContextType network_context_type) {
        std::unique_ptr<network::ResourceRequest> request2 =
            std::make_unique<network::ResourceRequest>();
        request2->url = request_url;
        request2->credentials_mode = network::mojom::CredentialsMode::kOmit;
        content::SimpleURLLoaderTestHelper simple_loader_helper2;
        std::unique_ptr<network::SimpleURLLoader> simple_loader2 =
            network::SimpleURLLoader::Create(std::move(request2),
                                             TRAFFIC_ANNOTATION_FOR_TESTS);
        simple_loader2->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
            GetLoaderFactoryForContextType(network_context_type),
            simple_loader_helper2.GetCallback());
        simple_loader_helper2.WaitForCallback();
        EXPECT_FALSE(simple_loader_helper2.response_body());
        EXPECT_EQ(net::ERR_CONNECTION_REFUSED, simple_loader2->NetError());
      }));
}

// Make sure an on-disk cache is used when expected. PRE_DiskCache populates the
// cache. DiskCache then makes sure the cache entry is still there (Or not) as
// expected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, PRE_DiskCache) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // Save test URL to disk, so it can be used in the next test (Test server uses
  // a random port, so need to know the port to try and retrieve it from the
  // cache in the next test). The profile directory is preserved between the
  // PRE_DiskCache and DiskCache run, so can just keep a file there.
  GURL test_url = embedded_test_server()->GetURL(kCacheRandomPath);
  url::Origin test_origin = url::Origin::Create(test_url);
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::FilePath save_url_file_path = browser()->GetProfile()->GetPath().Append(
      FILE_PATH_LITERAL("url_for_test.txt"));

  // Make a request whose response should be cached.
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = test_url;
  request->trusted_params = network::ResourceRequest::TrustedParams();
  request->trusted_params->isolation_info =
      net::IsolationInfo::CreateForInternalRequest(test_origin);
  request->site_for_cookies =
      request->trusted_params->isolation_info.site_for_cookies();

  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);
  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  EXPECT_EQ(net::OK, simple_loader->NetError());
  ASSERT_TRUE(simple_loader_helper.response_body());
  EXPECT_FALSE(simple_loader_helper.response_body()->empty());

  // Write the URL and expected response to a file.
  std::string file_data =
      test_url.spec() + "\n" + *simple_loader_helper.response_body();
  ASSERT_TRUE(base::WriteFile(save_url_file_path, file_data));

  EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
}

// Check if the URL loaded in PRE_DiskCache is still in the cache, across a
// browser restart.
// TODO(crbug.com/40922934): Fix test flake and re-enable
#if BUILDFLAG(IS_MAC)
#define MAYBE_DiskCache DISABLED_DiskCache
#else
#define MAYBE_DiskCache DiskCache
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       MAYBE_DiskCache) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // Crashing the network service may corrupt the disk cache, so skip this phase
  // in tests that crash the network service and use an on-disk cache.
  if (GetParam().network_service_state == NetworkServiceState::kRestarted &&
      GetHttpCacheType() == StorageType::kDisk) {
    return;
  }

  // Load URL from the above test body to disk.
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::FilePath save_url_file_path = browser()->GetProfile()->GetPath().Append(
      FILE_PATH_LITERAL("url_for_test.txt"));
  std::string file_data;
  ASSERT_TRUE(ReadFileToString(save_url_file_path, &file_data));

  size_t newline_pos = file_data.find('\n');
  ASSERT_NE(newline_pos, std::string::npos);

  GURL test_url = GURL(file_data.substr(0, newline_pos));
  ASSERT_TRUE(test_url.is_valid()) << test_url.possibly_invalid_spec();
  url::Origin test_origin = url::Origin::Create(test_url);

  std::string original_response = file_data.substr(newline_pos + 1);

  // Request the same test URL as may have been cached by PRE_DiskCache.
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = test_url;
  request->trusted_params = network::ResourceRequest::TrustedParams();
  request->trusted_params->isolation_info =
      net::IsolationInfo::CreateForInternalRequest(test_origin);
  request->site_for_cookies =
      request->trusted_params->isolation_info.site_for_cookies();

  content::SimpleURLLoaderTestHelper simple_loader_helper;
  request->load_flags = net::LOAD_ONLY_FROM_CACHE;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);
  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  // The request should only succeed if there is an on-disk cache.
  if (GetHttpCacheType() != StorageType::kDisk) {
    EXPECT_FALSE(simple_loader_helper.response_body());
  } else {
    DCHECK_NE(GetParam().network_service_state,
              NetworkServiceState::kRestarted);
    ASSERT_TRUE(simple_loader_helper.response_body());
    EXPECT_EQ(original_response, *simple_loader_helper.response_body());
  }
}

// Make sure that NetworkContexts have separate DNS caches.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       DnsCacheIsolation) {
  net::NetworkAnonymizationKey network_anonymization_key =
      net::NetworkAnonymizationKey::CreateTransient();
  net::HostPortPair host_port_pair(kHostname, 0);
  network::mojom::ResolveHostParametersPtr params =
      network::mojom::ResolveHostParameters::New();
  // Use A queries, to avoid running into issues with the IPv6 probe, the
  // results of which can change during runtime, and can affect the DNS cache
  // key.
  params->dns_query_type = net::DnsQueryType ::A;
  // Resolve |host_port_pair|, which should succeed and put it in the
  // NetworkContext's cache.
  network::DnsLookupResult result =
      network::BlockingDnsLookup(network_context(), host_port_pair,
                                 std::move(params), network_anonymization_key);
  EXPECT_EQ(net::OK, result.error);
  ASSERT_EQ(1u, result.resolved_addresses.size());
  EXPECT_EQ(kAddress, result.resolved_addresses[0].ToStringWithoutPort());
  // Make a cache-only request for the same hostname, for each other network
  // context, and make sure no result is returned.
  ForEachOtherContext(
      base::BindLambdaForTesting([&](NetworkContextType network_context_type) {
        params = network::mojom::ResolveHostParameters::New();
        params->dns_query_type = net::DnsQueryType ::A;
        // Cache only lookup.
        params->cache_usage =
            network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
        params->source = net::HostResolverSource::LOCAL_ONLY;
        network::DnsLookupResult result = network::BlockingDnsLookup(
            GetNetworkContextForContextType(network_context_type),
            host_port_pair, std::move(params), network_anonymization_key);
        EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, result.error);
      }));
  // Do a cache-only lookup using the original network context, which should
  // return the same result it initially did.
  params = network::mojom::ResolveHostParameters::New();
  // Cache only lookup.
  params->dns_query_type = net::DnsQueryType ::A;
  params->source = net::HostResolverSource::LOCAL_ONLY;
  params->cache_usage =
      network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
  result =
      network::BlockingDnsLookup(network_context(), host_port_pair,
                                 std::move(params), network_anonymization_key);
  EXPECT_EQ(net::OK, result.error);
  ASSERT_EQ(1u, result.resolved_addresses.size());
  EXPECT_EQ(kAddress, result.resolved_addresses[0].ToStringWithoutPort());
}

// Visits a URL with an HSTS header, and makes sure it is respected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, PRE_Hsts) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  net::test_server::EmbeddedTestServer ssl_server(
      net::test_server::EmbeddedTestServer::TYPE_HTTPS);
  ssl_server.SetCertHostnames({kHstsHostname});
  ssl_server.AddDefaultHandlers(GetChromeTestDataDir());
  ASSERT_TRUE(ssl_server.Start());

  // Make a request whose response has an STS header.
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = ssl_server.GetURL(
      kHstsHostname, "/set-header?Strict-Transport-Security: max-age%3D600000");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;

  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);
  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  EXPECT_EQ(net::OK, simple_loader->NetError());
  ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
  EXPECT_TRUE(simple_loader->ResponseInfo()->headers->HasHeaderValue(
      "Strict-Transport-Security", "max-age=600000"));

  // Make a cache-only request to make sure the HSTS entry is respected. Using a
  // cache-only request both prevents the result from being cached, and makes
  // the check identical to the one in the next test, which is run after the
  // test server has been shut down.
  GURL exected_ssl_url = ssl_server.GetURL(kHstsHostname, "/");
  GURL::Replacements replacements;
  replacements.SetSchemeStr("http");
  GURL start_url = exected_ssl_url.ReplaceComponents(replacements);
  url::Origin origin = url::Origin::Create(start_url);

  request = std::make_unique<network::ResourceRequest>();
  request->url = start_url;
  request->load_flags = net::LOAD_ONLY_FROM_CACHE;
  // Have this request simulate a main frame request. This is necessary when
  // kHstsTopLevelNavigationsOnly is enabled.
  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));

  content::SimpleURLLoaderTestHelper simple_loader_helper2;
  simple_loader = network::SimpleURLLoader::Create(
      std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper2.GetCallback());
  simple_loader_helper2.WaitForCallback();
  EXPECT_FALSE(simple_loader_helper2.response_body());
  EXPECT_EQ(exected_ssl_url, simple_loader->GetFinalURL());

  // Write the URL with HSTS information to a file, so it can be loaded in the
  // next test. Have to use a file for this, since the server's port is random.
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::FilePath save_url_file_path = browser()->GetProfile()->GetPath().Append(
      FILE_PATH_LITERAL("url_for_test.txt"));
  std::string file_data = start_url.spec();
  ASSERT_TRUE(base::WriteFile(save_url_file_path, file_data));
}

// Checks if the HSTS information from the last test is still available after a
// restart.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, Hsts) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The network service must be cleanly shut down to guarantee HSTS information
  // is flushed to disk, but that currently generally doesn't happen. See
  // https://crbug.com/40566707.
  if (GetHttpCacheType() == StorageType::kDisk) {
    return;
  }
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::FilePath save_url_file_path = browser()->GetProfile()->GetPath().Append(
      FILE_PATH_LITERAL("url_for_test.txt"));
  std::string file_data;
  ASSERT_TRUE(ReadFileToString(save_url_file_path, &file_data));
  GURL start_url = GURL(file_data);
  url::Origin origin = url::Origin::Create(start_url);

  // Unfortunately, loading HSTS information is loaded asynchronously from
  // disk, so there's no way to guarantee it has loaded by the time a
  // request is made. As a result, may have to try multiple times to verify that
  // cached HSTS information has been loaded, when it's stored on disk.
  while (true) {
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    request->url = start_url;
    request->load_flags = net::LOAD_ONLY_FROM_CACHE;
    // Have this request simulate a main frame request. This is necessary when
    // kHstsTopLevelNavigationsOnly is enabled.
    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));

    content::SimpleURLLoaderTestHelper simple_loader_helper;
    std::unique_ptr<network::SimpleURLLoader> simple_loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);
    simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        loader_factory(), simple_loader_helper.GetCallback());
    simple_loader_helper.WaitForCallback();
    EXPECT_FALSE(simple_loader_helper.response_body());

    // HSTS information is currently stored on disk if-and-only-if there's an
    // on-disk HTTP cache.
    if (GetHttpCacheType() == StorageType::kDisk) {
      GURL::Replacements replacements;
      replacements.SetSchemeStr("https");
      GURL expected_https_url = start_url.ReplaceComponents(replacements);
      // The file may not have been loaded yet, so if the cached HSTS
      // information was not respected, try again.
      if (expected_https_url != simple_loader->GetFinalURL())
        continue;
      EXPECT_EQ(expected_https_url, simple_loader->GetFinalURL());
      break;
    } else {
      EXPECT_EQ(start_url, simple_loader->GetFinalURL());
      break;
    }
  }
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, ProxyConfig) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  SetProxyPref(embedded_test_server()->host_port_pair());
  TestProxyConfigured(/*expect_success=*/true);
}

// This test should not end in an AssertNoURLLRequests CHECK.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       ShutdownWithLiveRequest) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  MakeLongLivedRequestThatHangsUntilShutdown();
}

// Disabled due to flakiness. See crbug.com/40755205.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC)
#define MAYBE_UserAgentAndLanguagePrefs DISABLED_UserAgentAndLanguagePrefs
#else
#define MAYBE_UserAgentAndLanguagePrefs UserAgentAndLanguagePrefs
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       MAYBE_UserAgentAndLanguagePrefs) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The system and SafeBrowsing network contexts aren't associated with any
  // profile, so changing the language settings for the profile's main network
  // context won't affect what they send.
  bool system =
      (GetParam().network_context_type == NetworkContextType::kSystem ||
       GetParam().network_context_type == NetworkContextType::kSafeBrowsing);
  // echoheader returns "None" when the header isn't there in the first place.
  const char kNoAcceptLanguage[] = "None";

  std::string accept_language, user_agent;
  // Check default.
  ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language));
  EXPECT_EQ(system ? kNoAcceptLanguage : "en-US,en;q=0.9", accept_language);
  ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent));
  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent);

  // Change AcceptLanguages preferences, and check that headers are updated.
  // First, A single language.
  browser()->GetProfile()->GetPrefs()->SetString(
      language::prefs::kAcceptLanguages, "zu");
  FlushNetworkInterface();
  std::string accept_language2, user_agent2;
  ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language2));
  EXPECT_EQ(system ? kNoAcceptLanguage : "zu", accept_language2);
  ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent2));
  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent2);

  // Second, a single language with locale.
  browser()->GetProfile()->GetPrefs()->SetString(
      language::prefs::kAcceptLanguages, "zu-ZA");
  FlushNetworkInterface();
  std::string accept_language3, user_agent3;
  ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language3));
  EXPECT_EQ(system ? kNoAcceptLanguage : "zu-ZA,zu;q=0.9", accept_language3);
  ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent3));
  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent3);

  // Third, a list with multiple languages. Incognito mode should return only
  // the first language's default set of languages.
  browser()->GetProfile()->GetPrefs()->SetString(
      language::prefs::kAcceptLanguages, "ar,am,en-GB,ru,zu");
  FlushNetworkInterface();
  std::string accept_language4;
  std::string user_agent4;
  ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language4));
  if (GetProfile()->IsOffTheRecord()) {
    EXPECT_EQ(system ? kNoAcceptLanguage : "ar,en-US;q=0.9,en;q=0.8",
              accept_language4);
  } else {
    EXPECT_EQ(system ? kNoAcceptLanguage
                     : "ar,am;q=0.9,en-GB;q=0.8,en;q=0.7,ru;q=0.6,zu;q=0.5",
              accept_language4);
  }
  ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent4));
  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent4);
}

// First part of testing enable referrers. Check that referrers are enabled by
// default at browser start, and referrers are indeed sent. Then disable
// referrers, and make sure that they aren't set.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       PRE_EnableReferrers) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  const GURL kReferrer("http://referrer/");

  // Referrers should be enabled by default.
  EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kEnableReferrers));

  // Send a request, make sure the referrer is sent.
  std::string referrer;
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->referrer = kReferrer;
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  ASSERT_TRUE(FetchHeaderEcho("referer", &referrer, std::move(request)));

  // SafeBrowsing never sends the referrer since it doesn't need to.
  if (GetParam().network_context_type == NetworkContextType::kSafeBrowsing) {
    EXPECT_EQ("None", referrer);
  } else {
    EXPECT_EQ(kReferrer.spec(), referrer);
  }

  // Disable referrers, and flush the NetworkContext mojo interface it's set on,
  // to avoid any races with the URLLoaderFactory pipe.
  GetPrefService()->SetBoolean(prefs::kEnableReferrers, false);
  FlushNetworkInterface();

  // Send a request and make sure its referer is not sent.
  std::string referrer2;
  std::unique_ptr<network::ResourceRequest> request2 =
      std::make_unique<network::ResourceRequest>();
  request2->referrer = kReferrer;
  request2->credentials_mode = network::mojom::CredentialsMode::kOmit;
  ASSERT_TRUE(FetchHeaderEcho("referer", &referrer2, std::move(request2)));
  EXPECT_EQ("None", referrer2);
}

// Second part of enable referrer test. Referrer should still be disabled. Make
// sure that disable referrers option is respected after startup, as to just
// after changing it.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       EnableReferrers) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  const GURL kReferrer("http://referrer/");

  // The preference is expected to be reset in incognito mode.
  if (is_incognito()) {
    EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kEnableReferrers));
    return;
  }

  // Referrers should still be disabled.
  EXPECT_FALSE(GetPrefService()->GetBoolean(prefs::kEnableReferrers));

  // Send a request and make sure its referer is not sent.
  std::string referrer;
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->referrer = kReferrer;
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  ASSERT_TRUE(FetchHeaderEcho("referer", &referrer, std::move(request)));
  EXPECT_EQ("None", referrer);
}

// Make sure that sending referrers that violate the referrer policy results in
// errors.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       PolicyViolatingReferrers) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = embedded_test_server()->GetURL("/echoheader?Referer");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  request->referrer = GURL("http://referrer/");
  request->referrer_policy = net::ReferrerPolicy::NO_REFERRER;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();
  if (GetParam().network_context_type == NetworkContextType::kSafeBrowsing) {
    // Safebrowsing ignores referrers so the requests succeed.
    EXPECT_EQ(net::OK, simple_loader->NetError());
    ASSERT_TRUE(simple_loader_helper.response_body());
    EXPECT_EQ("None", *simple_loader_helper.response_body());
  } else {
    // In all other cases, the invalid referrer causes the request to fail.
    EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, simple_loader->NetError());
  }
}

// Makes sure cookies are enabled by default, and saved to disk / not saved to
// disk as expected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       PRE_CookiesEnabled) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  EXPECT_TRUE(GetCookies(embedded_test_server()->base_url()).empty());

  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
            embedded_test_server());
  EXPECT_FALSE(GetCookies(embedded_test_server()->base_url()).empty());
}

// Disabled due to flakiness. See https://crbug.com/40807215.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       DISABLED_CookiesEnabled) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // Check that the cookie from the first stage of the test was / was not
  // preserved between browser restarts, as expected.
  bool has_cookies = !GetCookies(embedded_test_server()->base_url()).empty();
  EXPECT_EQ(GetCookieStorageType() == StorageType::kDisk, has_cookies);
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       CookieIsolation) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
            embedded_test_server());
  EXPECT_FALSE(GetCookies(embedded_test_server()->base_url()).empty());

  ForEachOtherContext(
      base::BindLambdaForTesting([&](NetworkContextType network_context_type) {
        EXPECT_TRUE(GetCookiesForContextType(network_context_type,
                                             embedded_test_server()->base_url())
                        .empty());
      }));
}

// Disabled due to flakiness. See https://crbug.com/40718681.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_PRE_ThirdPartyCookiesBlocked DISABLED_PRE_ThirdPartyCookiesBlocked
#define MAYBE_ThirdPartyCookiesBlocked DISABLED_ThirdPartyCookiesBlocked
#else
#define MAYBE_PRE_ThirdPartyCookiesBlocked PRE_ThirdPartyCookiesBlocked
#define MAYBE_ThirdPartyCookiesBlocked ThirdPartyCookiesBlocked
#endif
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       MAYBE_PRE_ThirdPartyCookiesBlocked) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The system and SafeBrowsing network contexts don't support the third party
  // cookie blocking options, since they have no notion of third parties.
  bool system =
      GetParam().network_context_type == NetworkContextType::kSystem ||
      GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
  if (system)
    return;

  GetPrefService()->SetInteger(
      prefs::kCookieControlsMode,
      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
  SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
            https_server());

  EXPECT_TRUE(GetCookies(https_server()->base_url()).empty());
}

// Disabled due to flakiness. See https://crbug.com/40718681.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       MAYBE_ThirdPartyCookiesBlocked) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The system and SafeBrowsing network contexts don't support the third party
  // cookie blocking options, since they have no notion of third parties.
  bool system =
      GetParam().network_context_type == NetworkContextType::kSystem ||
      GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
  if (system)
    return;

  // The third-party cookies pref should carry over to the next session.
  EXPECT_EQ(
      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty),
      GetPrefService()->GetInteger(prefs::kCookieControlsMode));
  SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
            https_server());

  EXPECT_TRUE(GetCookies(https_server()->base_url()).empty());

  // Add exception, third party cookies should be allowed now.
  CookieSettingsFactory::GetForProfile(browser()->GetProfile())
      ->SetCookieSetting(https_server()->base_url(), CONTENT_SETTING_ALLOW);
  // Set a third-party cookie. It should actually get set this time.
  SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
            https_server());

  EXPECT_FALSE(GetCookies(https_server()->base_url()).empty());
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest,
                       PRE_CookieSettings) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The system and SafeBrowsing network contexts don't respect cookie blocking
  // options, which are per-profile.
  bool system =
      GetParam().network_context_type == NetworkContextType::kSystem ||
      GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
  if (system)
    return;

  CookieSettingsFactory::GetForProfile(browser()->GetProfile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
  FlushNetworkInterface();
  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kSession,
            embedded_test_server());

  EXPECT_TRUE(GetCookies(embedded_test_server()->base_url()).empty());
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, CookieSettings) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  // The system and SafeBrowsing network contexts don't respect cookie blocking
  // options, which are per-profile.
  bool system =
      GetParam().network_context_type == NetworkContextType::kSystem ||
      GetParam().network_context_type == NetworkContextType::kSafeBrowsing;
  if (system)
    return;

  // The content settings should carry over to the next session.
  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kSession,
            embedded_test_server());

  EXPECT_TRUE(GetCookies(embedded_test_server()->base_url()).empty());

  // Set default setting to allow, cookies should be set now.
  CookieSettingsFactory::GetForProfile(browser()->GetProfile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_ALLOW);
  FlushNetworkInterface();
  SetCookie(CookieType::kFirstParty, CookiePersistenceType::kSession,
            embedded_test_server());

  EXPECT_FALSE(GetCookies(embedded_test_server()->base_url()).empty());
}

// Make sure file uploads work.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationBrowserTest, UploadFile) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->method = "POST";
  request->url = embedded_test_server()->GetURL("/echo");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);
  base::FilePath dir_test_data;
  base::PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data);
  base::FilePath path =
      dir_test_data.Append(base::FilePath(FILE_PATH_LITERAL("simple.html")));
  simple_loader->AttachFileForUpload(path, "text/html");

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  ASSERT_TRUE(simple_loader->ResponseInfo());
  ASSERT_TRUE(simple_loader->ResponseInfo()->headers);
  EXPECT_EQ(200, simple_loader->ResponseInfo()->headers->response_code());
  ASSERT_TRUE(simple_loader_helper.response_body());
  base::ScopedAllowBlockingForTesting allow_blocking;
  std::string expected_response;
  base::ReadFileToString(path, &expected_response);
  EXPECT_EQ(expected_response.c_str(), *simple_loader_helper.response_body());
}

class NetworkContextConfigurationFixedPortBrowserTest
    : public NetworkContextConfigurationBrowserTest {
 public:
  NetworkContextConfigurationFixedPortBrowserTest() = default;
  ~NetworkContextConfigurationFixedPortBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitchASCII(
        switches::kTestingFixedHttpPort,
        base::StringPrintf("%u", embedded_test_server()->port()));
    LOG(WARNING) << base::StringPrintf("%u", embedded_test_server()->port());
  }
};

// Test that the command line switch makes it to the network service and is
// respected.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationFixedPortBrowserTest,
                       TestingFixedPort) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  // This URL does not use the port the embedded test server is using. The
  // command line switch should make it result in the request being directed to
  // the test server anyways.
  request->url = GURL("http://127.0.0.1/echo");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();

  EXPECT_EQ(net::OK, simple_loader->NetError());
  ASSERT_TRUE(simple_loader_helper.response_body());
  EXPECT_EQ(*simple_loader_helper.response_body(), "Echo");
}

class NetworkContextConfigurationProxyOnStartBrowserTest
    : public NetworkContextConfigurationBrowserTest {
 public:
  NetworkContextConfigurationProxyOnStartBrowserTest() = default;

  NetworkContextConfigurationProxyOnStartBrowserTest(
      const NetworkContextConfigurationProxyOnStartBrowserTest&) = delete;
  NetworkContextConfigurationProxyOnStartBrowserTest& operator=(
      const NetworkContextConfigurationProxyOnStartBrowserTest&) = delete;

  ~NetworkContextConfigurationProxyOnStartBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitchASCII(
        switches::kProxyServer,
        embedded_test_server()->host_port_pair().ToString());
  }
};

// Test that when there's a proxy configuration at startup, the initial requests
// use that configuration.
IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationProxyOnStartBrowserTest,
                       TestInitialProxyConfig) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  TestProxyConfigured(/*expect_success=*/true);
}

// Make sure the system URLRequestContext can handle fetching PAC scripts from
// http URLs.
class NetworkContextConfigurationHttpPacBrowserTest
    : public NetworkContextConfigurationBrowserTest {
 public:
  NetworkContextConfigurationHttpPacBrowserTest()
      : pac_test_server_(net::test_server::EmbeddedTestServer::TYPE_HTTP) {}

  NetworkContextConfigurationHttpPacBrowserTest(
      const NetworkContextConfigurationHttpPacBrowserTest&) = delete;
  NetworkContextConfigurationHttpPacBrowserTest& operator=(
      const NetworkContextConfigurationHttpPacBrowserTest&) = delete;

  ~NetworkContextConfigurationHttpPacBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    pac_test_server_.RegisterRequestHandler(base::BindRepeating(
        &NetworkContextConfigurationHttpPacBrowserTest::HandlePacRequest,
        GetPacScript()));
    EXPECT_TRUE(pac_test_server_.Start());

    command_line->AppendSwitchASCII(switches::kProxyPacUrl,
                                    pac_test_server_.base_url().spec().c_str());
  }

  static std::unique_ptr<net::test_server::HttpResponse> HandlePacRequest(
      const std::string& pac_script,
      const net::test_server::HttpRequest& request) {
    std::unique_ptr<net::test_server::BasicHttpResponse> response =
        std::make_unique<net::test_server::BasicHttpResponse>();
    response->set_content(pac_script);
    return response;
  }

 private:
  net::test_server::EmbeddedTestServer pac_test_server_;
};

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationHttpPacBrowserTest, HttpPac) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  TestProxyConfigured(/*expect_success=*/true);
}

// TODO(crbug.com/494643383): Fails on some desktop Android bots.
#if !BUILDFLAG(IS_ANDROID)
// Make sure the system URLRequestContext can handle fetching PAC scripts from
// file URLs.
class NetworkContextConfigurationFilePacBrowserTest
    : public NetworkContextConfigurationBrowserTest {
 public:
  NetworkContextConfigurationFilePacBrowserTest() = default;

  NetworkContextConfigurationFilePacBrowserTest(
      const NetworkContextConfigurationFilePacBrowserTest&) = delete;
  NetworkContextConfigurationFilePacBrowserTest& operator=(
      const NetworkContextConfigurationFilePacBrowserTest&) = delete;

  ~NetworkContextConfigurationFilePacBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    const char kPacFileName[] = "foo.pac";

    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    base::FilePath pac_file_path =
        temp_dir_.GetPath().AppendASCII(kPacFileName);

    std::string pac_script = GetPacScript();
    ASSERT_TRUE(base::WriteFile(pac_file_path, pac_script));

    command_line->AppendSwitchASCII(
        switches::kProxyPacUrl, net::FilePathToFileURL(pac_file_path).spec());
  }

 private:
  base::ScopedTempDir temp_dir_;
};

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationFilePacBrowserTest, FilePac) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  TestProxyConfigured(false);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Make sure the system URLRequestContext can handle fetching PAC scripts from
// data URLs.
class NetworkContextConfigurationDataPacBrowserTest
    : public NetworkContextConfigurationBrowserTest {
 public:
  NetworkContextConfigurationDataPacBrowserTest() = default;

  NetworkContextConfigurationDataPacBrowserTest(
      const NetworkContextConfigurationDataPacBrowserTest&) = delete;
  NetworkContextConfigurationDataPacBrowserTest& operator=(
      const NetworkContextConfigurationDataPacBrowserTest&) = delete;

  ~NetworkContextConfigurationDataPacBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    std::string contents;
    // Read in kPACScript contents.
    command_line->AppendSwitchASCII(switches::kProxyPacUrl,
                                    "data:," + GetPacScript());
  }
};

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationDataPacBrowserTest, DataPac) {
  if (IsRestartStateWithInProcessNetworkService())
    return;
  TestProxyConfigured(/*expect_success=*/true);
}

class WaitingHandshakeClient : public network::mojom::WebSocketHandshakeClient {
 public:
  WaitingHandshakeClient() = default;
  WaitingHandshakeClient(const WaitingHandshakeClient&) = delete;
  WaitingHandshakeClient& operator=(const WaitingHandshakeClient&) = delete;

  mojo::PendingRemote<network::mojom::WebSocketHandshakeClient> Bind() {
    return receiver_.BindNewPipeAndPassRemote();
  }

  // Implementation of WebSocketHandshakeClient
  void OnOpeningHandshakeStarted(
      network::mojom::WebSocketHandshakeRequestPtr) override {}

  void OnFailure(const std::string& message,
                 int net_error,
                 int response_code) override {}

  void OnConnectionEstablished(
      mojo::PendingRemote<network::mojom::WebSocket> websocket,
      mojo::PendingReceiver<network::mojom::WebSocketClient> client_receiver,
      network::mojom::WebSocketHandshakeResponsePtr,
      mojo::ScopedDataPipeConsumerHandle readable,
      mojo::ScopedDataPipeProducerHandle writable) override {}

 private:
  mojo::Receiver<network::mojom::WebSocketHandshakeClient> receiver_{this};
};

class NetworkContextConfigurationProxySettingsBrowserTest
    : public NetworkContextConfigurationHttpPacBrowserTest {
 public:
  // Using the full 128 was causing flakes, so we're using the minimum for now.
  const size_t kTestMaxConnectionsPerProxy = 7;

  NetworkContextConfigurationProxySettingsBrowserTest() {
    // Disable `kPermitTcpSocketPoolConnectBackupJobs`, as backup jobs
    // cause extra connections without opening new WebSockets, breaking tests.
    scoped_feature_list_.InitAndDisableFeature(
        net::features::kPermitTcpSocketPoolConnectBackupJobs);
  }

  NetworkContextConfigurationProxySettingsBrowserTest(
      const NetworkContextConfigurationProxySettingsBrowserTest&) = delete;
  NetworkContextConfigurationProxySettingsBrowserTest& operator=(
      const NetworkContextConfigurationProxySettingsBrowserTest&) = delete;

  ~NetworkContextConfigurationProxySettingsBrowserTest() override = default;

  void SetUpOnMainThread() override {
    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
        &NetworkContextConfigurationProxySettingsBrowserTest::TrackConnections,
        base::Unretained(this)));

    expected_connections_loop_ptr_.store(nullptr);

    NetworkContextConfigurationHttpPacBrowserTest::SetUpOnMainThread();
  }

  void SetUpLocalStatePrefService(PrefService* local_state) override {
    local_state->SetInteger(prefs::kMaxConnectionsPerProxy,
                            static_cast<int>(kTestMaxConnectionsPerProxy));
    local_state->SetInteger(prefs::kMaxConnectionsPerProxyForWebSocket,
                            static_cast<int>(kTestMaxConnectionsPerProxy));
    local_state->SetBoolean(prefs::kAllowSocketPoolSizeRandomizationForProxies,
                            false);
  }

  std::unique_ptr<net::test_server::HttpResponse> TrackConnections(
      const net::test_server::HttpRequest& request) {
    if (!base::StartsWith(request.relative_url,
                          is_websocket_test_ ? "foo" : "/hung_",
                          base::CompareCase::INSENSITIVE_ASCII)) {
      return nullptr;
    }

    // Record the number of connections we're seeing.
    CHECK(!observed_request_urls_.contains(request.relative_url));
    observed_request_urls_.emplace(request.relative_url);
    CHECK_GE(kTestMaxConnectionsPerProxy, observed_request_urls_.size());

    // Once we've seen at least as many connections as we expect, we can quit
    // the loop on the main test thread. The test may choose to wait for
    // longer to see if there are any additional unexpected connections.
    if (kTestMaxConnectionsPerProxy == observed_request_urls_.size() &&
        expected_connections_loop_ptr_.load() != nullptr) {
      expected_connections_loop_ptr_.load()->Quit();
    }

    // To test the number of connections per proxy, we'll hang all responses.
    return std::make_unique<net::test_server::HungResponse>();
  }

  void RunMaxConnectionsPerProxyTest() {
    // At this point in the test, we've set up a proxy that points to our
    // embedded test server. We've also set things up to hang all incoming
    // requests and record how many concurrent connections we have running. To
    // detect the maximum number of requests per proxy, we now just have to
    // attempt to make as many requests as possible to ensure we don't see an
    // incorrect number of concurrent requests.

    // First of all, we're going to want to wait for at least as many
    // connections as we expect.
    base::RunLoop expected_connections_run_loop;
    expected_connections_loop_ptr_.store(&expected_connections_run_loop);

    std::vector<std::unique_ptr<network::SimpleURLLoader>> loaders;
    for (unsigned int i = 0; i < kTestMaxConnectionsPerProxy + 1; ++i) {
      std::unique_ptr<network::ResourceRequest> request =
          std::make_unique<network::ResourceRequest>();
      request->url =
          embedded_test_server()->GetURL(base::StringPrintf("foo%u.test", i),
                                         base::StringPrintf("/hung_%u", i));
      request->credentials_mode = network::mojom::CredentialsMode::kOmit;

      content::SimpleURLLoaderTestHelper simple_loader_helper;
      std::unique_ptr<network::SimpleURLLoader> simple_loader =
          network::SimpleURLLoader::Create(std::move(request),
                                           TRAFFIC_ANNOTATION_FOR_TESTS);

      simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
          loader_factory(), simple_loader_helper.GetCallback());
      loaders.emplace_back(std::move(simple_loader));
    }
    expected_connections_run_loop.Run();

    // Then wait for any remaining connections that we should NOT get.
    base::RunLoop ugly_10ms_wait;
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, ugly_10ms_wait.QuitClosure(), base::Milliseconds(10));
    ugly_10ms_wait.Run();

    // Stop the server.
    ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
  }

  void RunMaxConnectionsPerProxyForWebSocketTest() {
    // See the comment at the top of `RunMaxConnectionsPerProxyTest`, this is
    // the same except that we have `TrackConnections` look for WebSockets
    // requests.
    is_websocket_test_ = true;

    // First of all, we're going to want to wait for at least as many
    // connections as we expect.
    base::RunLoop expected_connections_run_loop;
    expected_connections_loop_ptr_.store(&expected_connections_run_loop);

    std::vector<std::unique_ptr<WaitingHandshakeClient>> waiters;
    for (unsigned int i = 0; i < kTestMaxConnectionsPerProxy + 1; ++i) {
      const GURL url = net::test_server::GetWebSocketURL(
          *embedded_test_server(), base::StringPrintf("foo%u.test", i),
          base::StringPrintf("/hung_%u", i));

      auto client = std::make_unique<WaitingHandshakeClient>();
      content::RenderFrameHost* const frame = browser()
                                                  ->tab_strip_model()
                                                  ->GetActiveWebContents()
                                                  ->GetPrimaryMainFrame();
      content::RenderProcessHost* const process = frame->GetProcess();
      const std::vector<std::string> requested_protocols;
      std::vector<network::mojom::HttpHeaderPtr> additional_headers;
      const url::Origin origin = url::Origin::Create(url);

      process->GetStoragePartition()->GetNetworkContext()->CreateWebSocket(
          url, requested_protocols, net::StorageAccessApiStatus::kNone,
          net::IsolationInfo::CreateForInternalRequest(origin),
          std::move(additional_headers),
          ToOriginatingProcessId(process->GetID()), origin,
          network::mojom::ClientSecurityState::New(),
          network::mojom::kWebSocketOptionNone,
          net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
          client->Bind(),
          process->GetStoragePartition()
              ->CreateURLLoaderNetworkObserverForFrame(
                  content::GlobalRenderFrameHostId(process->GetID(),
                                                   frame->GetRoutingID())),
          mojo::NullRemote(), mojo::NullRemote(),
          /*throttling_profile_id=*/std::nullopt,
          /*network_restrictions_id=*/
          network::GetTestNetworkRestrictionsId(),
          /*target_address_space=*/network::mojom::IPAddressSpace::kUnknown);
      waiters.emplace_back(std::move(client));
    }
    expected_connections_run_loop.Run();

    // Then wait for any remaining connections that we should NOT get.
    base::RunLoop ugly_10ms_wait;
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, ugly_10ms_wait.QuitClosure(), base::Milliseconds(10));
    ugly_10ms_wait.Run();

    // Stop the server.
    ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
  }

 private:
  std::atomic<base::RunLoop*> expected_connections_loop_ptr_{nullptr};

  // In RunMaxConnectionsPerProxy(WebSocket)Test(), we'll make several network
  // requests that hang. These hung requests are assumed to last for the
  // duration of the test. This member, which is only accessed from the server's
  // IO thread, records each observed request to ensure we see only as many
  // connections as we expect.
  absl::flat_hash_set<std::string> observed_request_urls_;
  bool is_websocket_test_ = false;
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationProxySettingsBrowserTest,
                       MaxConnectionsPerProxy) {
  RunMaxConnectionsPerProxyTest();
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationProxySettingsBrowserTest,
                       MaxConnectionsPerProxyForWebSocket) {
  RunMaxConnectionsPerProxyForWebSocketTest();
}

class NetworkContextConfigurationManagedProxySettingsBrowserTest
    : public NetworkContextConfigurationProxySettingsBrowserTest {
 public:
  NetworkContextConfigurationManagedProxySettingsBrowserTest() = default;

  NetworkContextConfigurationManagedProxySettingsBrowserTest(
      const NetworkContextConfigurationManagedProxySettingsBrowserTest&) =
      delete;
  NetworkContextConfigurationManagedProxySettingsBrowserTest& operator=(
      const NetworkContextConfigurationManagedProxySettingsBrowserTest&) =
      delete;

  ~NetworkContextConfigurationManagedProxySettingsBrowserTest() override =
      default;

  void SetUpInProcessBrowserTestFixture() override {
    provider_.SetDefaultReturns(
        /*is_initialization_complete_return=*/true,
        /*is_first_policy_load_complete_return=*/true);
    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
    policy::PolicyMap policies;
    policies.Set(policy::key::kMaxConnectionsPerProxy,
                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
                 policy::POLICY_SOURCE_CLOUD,
                 base::Value(static_cast<int>(kTestMaxConnectionsPerProxy)),
                 /*external_data_fetcher=*/nullptr);
    policies.Set(policy::key::kMaxConnectionsPerProxyForWebSocket,
                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
                 policy::POLICY_SOURCE_CLOUD,
                 base::Value(static_cast<int>(kTestMaxConnectionsPerProxy)),
                 /*external_data_fetcher=*/nullptr);
    policies.Set(policy::key::kAllowSocketPoolSizeRandomizationForProxies,
                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
                 policy::POLICY_SOURCE_CLOUD, base::Value(false),
                 /*external_data_fetcher=*/nullptr);
    provider_.UpdateChromePolicy(policies);
    NetworkContextConfigurationProxySettingsBrowserTest::
        SetUpInProcessBrowserTestFixture();
  }

  void SetUpLocalStatePrefService(PrefService* local_state) override {
    // We use the policy to test the behavior, so we don't set the prefs.
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
};

IN_PROC_BROWSER_TEST_P(
    NetworkContextConfigurationManagedProxySettingsBrowserTest,
    MaxConnectionsPerManagedProxy) {
  RunMaxConnectionsPerProxyTest();
}

IN_PROC_BROWSER_TEST_P(
    NetworkContextConfigurationManagedProxySettingsBrowserTest,
    MaxConnectionsPerManagedProxyForWebSocket) {
  RunMaxConnectionsPerProxyForWebSocketTest();
}

#if BUILDFLAG(ENABLE_REPORTING)
// Used to test that we persist Reporting clients and NEL policies to disk, but
// only when appropriate.
class NetworkContextConfigurationReportingAndNelBrowserTest
    : public NetworkContextConfigurationBrowserTest {
 public:
  struct RequestState {
    content::SimpleURLLoaderTestHelper helper;
    std::unique_ptr<network::SimpleURLLoader> loader;
  };

  NetworkContextConfigurationReportingAndNelBrowserTest()
      : https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
    // Set up an SSL certificate so we can make requests to "https://localhost"
    https_server_.SetSSLConfig(
        net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
    // Only start the server if we actually need it in the test (i.e., we'll
    // call StartAcceptingConnections somewhere in the test).
    if (!IsRestartStateWithInProcessNetworkService() &&
        AreReportingAndNelEnabled()) {
      EXPECT_TRUE(https_server_.InitializeAndListen());
    }

    // Make report delivery happen instantly.
    net::ReportingPolicy policy;
    policy.delivery_interval = base::Seconds(0);
    net::ReportingPolicy::UsePolicyForTesting(policy);
  }

  std::unique_ptr<RequestState> NewSimpleRequest(const GURL& url) {
    auto request_state = std::make_unique<RequestState>();
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    request->url = url;
    request->credentials_mode = network::mojom::CredentialsMode::kOmit;
    request_state->loader = network::SimpleURLLoader::Create(
        std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
    request_state->loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
        loader_factory(), request_state->helper.GetCallback());
    return request_state;
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(embedder_support::kShortReportingDelay);
    // This switch will cause traffic to *any* port to go to https_server_,
    // regardless of which arbitrary port https_server_ decides to run on.
    // NEL and Reporting policies are only valid for a single origin.
    if (!IsRestartStateWithInProcessNetworkService() &&
        AreReportingAndNelEnabled()) {
      command_line->AppendSwitchASCII(switches::kTestingFixedHttpsPort,
                                      base::StringPrintf("%u", port()));
    }
  }

  net::EmbeddedTestServer* http_server() { return &https_server_; }
  int port() const { return https_server_.port(); }

  static GURL GetCollectorURL() { return GURL("https://localhost/upload"); }

  static std::string GetReportToHeader() {
    return "Report-To: {\"endpoints\":[{\"url\":\"" + GetCollectorURL().spec() +
           "\"}],\"max_age\":86400}\r\n";
  }

  static std::string GetNELHeader() {
    return "NEL: "
           "{\"report_to\":\"default\",\"max_age\":86400,\"success_fraction\":"
           "1.0}\r\n";
  }

  void RespondWithHeaders(net::test_server::ControllableHttpResponse* resp) {
    resp->WaitForRequest();
    resp->Send("HTTP/1.1 200 OK\r\n");
    resp->Send(GetReportToHeader());
    resp->Send(GetNELHeader());
    resp->Send("\r\n");
    resp->Done();
  }

  void RespondWithoutHeaders(net::test_server::ControllableHttpResponse* resp) {
    resp->WaitForRequest();
    resp->Send("HTTP/1.1 200 OK\r\n");
    resp->Send("\r\n");
    resp->Done();
  }

  bool AreReportingAndNelEnabled() const {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kSafeBrowsing:
        return false;
      case NetworkContextType::kProfile:
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kOnDiskApp:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        return true;
    }
  }

  bool ExpectPersistenceEnabled() const {
    switch (GetParam().network_context_type) {
      case NetworkContextType::kSystem:
      case NetworkContextType::kSafeBrowsing:
      case NetworkContextType::kIncognitoProfile:
      case NetworkContextType::kInMemoryApp:
      case NetworkContextType::kOnDiskAppWithIncognitoProfile:
        return false;
      case NetworkContextType::kProfile:
      case NetworkContextType::kOnDiskApp:
        return true;
    }
  }

 private:
  net::EmbeddedTestServer https_server_;
};

namespace {

constexpr char kReportingEnabledURL[] = "https://localhost/page";
constexpr char kCrossOriginReportingEnabledURL[] = "https://localhost:444/page";

}  // namespace

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationReportingAndNelBrowserTest,
                       PRE_PersistReportingAndNel) {
  if (IsRestartStateWithInProcessNetworkService() ||
      !AreReportingAndNelEnabled()) {
    return;
  }

  net::test_server::ControllableHttpResponse original_response(http_server(),
                                                               "/page");
  net::test_server::ControllableHttpResponse upload_response(http_server(),
                                                             "/upload");
  http_server()->StartAcceptingConnections();

  // Make a request to /page and respond with Report-To and NEL headers, which
  // will turn on NEL for the localhost:443 origin.
  std::unique_ptr<RequestState> request =
      NewSimpleRequest(GURL(kReportingEnabledURL));
  RespondWithHeaders(&original_response);
  request->helper.WaitForCallback();

  // Wait for a request to /upload, which should happen because the previous
  // request precipitated a NEL report.
  upload_response.WaitForRequest();
  EXPECT_EQ(net::test_server::METHOD_POST,
            upload_response.http_request()->method);
}

IN_PROC_BROWSER_TEST_P(NetworkContextConfigurationReportingAndNelBrowserTest,
                       PersistReportingAndNel) {
  if (IsRestartStateWithInProcessNetworkService() ||
      !AreReportingAndNelEnabled()) {
    return;
  }

  net::test_server::ControllableHttpResponse original_response(http_server(),
                                                               "/page");
  net::test_server::ControllableHttpResponse cross_origin_response(
      http_server(), "/page");
  net::test_server::ControllableHttpResponse upload_response(http_server(),
                                                             "/upload");
  http_server()->StartAcceptingConnections();

  // Make a request that will only precipitate a NEL report if we loaded a NEL
  // policy from the store.
  std::unique_ptr<RequestState> request =
      NewSimpleRequest(GURL(kReportingEnabledURL));
  RespondWithoutHeaders(&original_response);
  request->helper.WaitForCallback();

  // Make another request that will generate another upload to the same
  // collector. If the previous request didn't generate a NEL report (e.g.,
  // because persistence is disabled) then this'll be first upload to the NEL
  // collector. Note that because this upload is to a different origin than the
  // request, Reporting will make a CORS preflight request, which is what we
  // look for.
  std::unique_ptr<RequestState> cross_origin_request =
      NewSimpleRequest(GURL(kCrossOriginReportingEnabledURL));
  RespondWithHeaders(&cross_origin_response);
  cross_origin_request->helper.WaitForCallback();

  // Wait for a request to /upload, which should happen because the previous
  // requests precipitated NEL reports.
  //
  // We assume that Reporting/NEL will send reports in the order we made the
  // requests above, which seems to be the case today but may not necessarily
  // true in general.
  upload_response.WaitForRequest();
  if (ExpectPersistenceEnabled()) {
    // A NEL upload about https://localhost
    EXPECT_EQ(net::test_server::METHOD_POST,
              upload_response.http_request()->method);
  } else {
    // A CORS preflight request before a cross-origin NEL upload about
    // https://localhost:444
    EXPECT_EQ(net::test_server::METHOD_OPTIONS,
              upload_response.http_request()->method);
  }
}
#endif  // BUILDFLAG(ENABLE_REPORTING)

// Instantiates tests with a prefix indicating which NetworkContext is being
// tested, and a suffix of "/0" if the network service is enabled, "/1" if it's
// enabled and restarted.
#define TEST_CASES(network_context_type)                           \
  TestCase({NetworkServiceState::kEnabled, network_context_type}), \
      TestCase({NetworkServiceState::kRestarted, network_context_type})

#if BUILDFLAG(ENABLE_EXTENSIONS)
#define INSTANTIATE_EXTENSION_TESTS(TestFixture)                        \
  INSTANTIATE_TEST_SUITE_P(                                             \
      OnDiskApp, TestFixture,                                           \
      ::testing::Values(TEST_CASES(NetworkContextType::kOnDiskApp)));   \
                                                                        \
  INSTANTIATE_TEST_SUITE_P(                                             \
      InMemoryApp, TestFixture,                                         \
      ::testing::Values(TEST_CASES(NetworkContextType::kInMemoryApp))); \
                                                                        \
  INSTANTIATE_TEST_SUITE_P(                                             \
      OnDiskAppWithIncognitoProfile, TestFixture,                       \
      ::testing::Values(                                                \
          TEST_CASES(NetworkContextType::kOnDiskAppWithIncognitoProfile)));
#else  // !BUILDFLAG(ENABLE_EXTENSIONS)
#define INSTANTIATE_EXTENSION_TESTS(TestFixture)
#endif  // !BUILDFLAG(ENABLE_EXTENSIONS)

#define INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(TestFixture)             \
  INSTANTIATE_EXTENSION_TESTS(TestFixture)                               \
  INSTANTIATE_TEST_SUITE_P(                                              \
      SystemNetworkContext, TestFixture,                                 \
      ::testing::Values(TEST_CASES(NetworkContextType::kSystem)));       \
                                                                         \
  INSTANTIATE_TEST_SUITE_P(                                              \
      SafeBrowsingNetworkContext, TestFixture,                           \
      ::testing::Values(TEST_CASES(NetworkContextType::kSafeBrowsing))); \
                                                                         \
  INSTANTIATE_TEST_SUITE_P(                                              \
      ProfileMainNetworkContext, TestFixture,                            \
      ::testing::Values(TEST_CASES(NetworkContextType::kProfile)));      \
                                                                         \
  INSTANTIATE_TEST_SUITE_P(                                              \
      IncognitoProfileMainNetworkContext, TestFixture,                   \
      ::testing::Values(TEST_CASES(NetworkContextType::kIncognitoProfile)))

INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(NetworkContextConfigurationBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationFixedPortBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationProxyOnStartBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationHttpPacBrowserTest);
#if !BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationFilePacBrowserTest);
#endif
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationDataPacBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationProxySettingsBrowserTest);
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationManagedProxySettingsBrowserTest);
#if BUILDFLAG(ENABLE_REPORTING)
INSTANTIATE_TEST_CASES_FOR_TEST_FIXTURE(
    NetworkContextConfigurationReportingAndNelBrowserTest);
#endif

}  // namespace
