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

#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "content/browser/frame_host/navigation_handle_impl.h"
#include "content/browser/web_package/signed_exchange_handler.h"
#include "content/browser/web_package/signed_exchange_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/page_type.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/content_cert_verifier_browser_test.h"
#include "content/public/test/test_navigation_throttle.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_download_manager_delegate.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/test_data_directory.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_interceptor.h"
#include "services/network/loader_util.h"
#include "services/network/public/cpp/features.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/service_worker/service_worker_utils.h"

namespace content {

namespace {

const uint64_t kSignatureHeaderDate = 1520834000;  // 2018-03-12T05:53:20Z
const uint64_t kSignatureHeaderExpires = 1520837600;  // 2018-03-12T06:53:20Z

constexpr char kExpectedSXGEnabledAcceptHeaderForPrefetch[] =
    "application/signed-exchange;v=b3;q=0.9,*/*;q=0.8";

class RedirectObserver : public WebContentsObserver {
 public:
  explicit RedirectObserver(WebContents* web_contents)
      : WebContentsObserver(web_contents) {}
  ~RedirectObserver() override = default;

  void DidRedirectNavigation(NavigationHandle* handle) override {
    const net::HttpResponseHeaders* response = handle->GetResponseHeaders();
    if (response)
      response_code_ = response->response_code();
  }

  const base::Optional<int>& response_code() const { return response_code_; }

 private:
  base::Optional<int> response_code_;

  DISALLOW_COPY_AND_ASSIGN(RedirectObserver);
};

class AssertNavigationHandleFlagObserver : public WebContentsObserver {
 public:
  explicit AssertNavigationHandleFlagObserver(WebContents* web_contents)
      : WebContentsObserver(web_contents) {}
  ~AssertNavigationHandleFlagObserver() override = default;

  void DidFinishNavigation(NavigationHandle* handle) override {
    EXPECT_TRUE(static_cast<NavigationHandleImpl*>(handle)->IsSignedExchangeInnerResponse());
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(AssertNavigationHandleFlagObserver);
};

}  // namespace

class SignedExchangeRequestHandlerBrowserTestBase
    : public CertVerifierBrowserTest {
 public:
  SignedExchangeRequestHandlerBrowserTestBase() {
    // This installs "root_ca_cert.pem" from which our test certificates are
    // created. (Needed for the tests that use real certificate, i.e.
    // RealCertVerifier)
    net::EmbeddedTestServer::RegisterTestCerts();
  }

  void SetUp() override {
    SignedExchangeHandler::SetVerificationTimeForTesting(
        base::Time::UnixEpoch() +
        base::TimeDelta::FromSeconds(kSignatureHeaderDate));
    feature_list_.InitWithFeatures({features::kSignedHTTPExchange}, {});
    CertVerifierBrowserTest::SetUp();
  }

  void TearDownOnMainThread() override {
    interceptor_.reset();
    SignedExchangeHandler::SetVerificationTimeForTesting(
        base::Optional<base::Time>());
  }

 protected:
  static scoped_refptr<net::X509Certificate> LoadCertificate(
      const std::string& cert_file) {
    base::ScopedAllowBlockingForTesting allow_io;
    base::FilePath dir_path;
    base::PathService::Get(content::DIR_TEST_DATA, &dir_path);
    dir_path = dir_path.AppendASCII("sxg");

    return net::CreateCertificateChainFromFile(
        dir_path, cert_file, net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
  }

  void InstallUrlInterceptor(const GURL& url, const std::string& data_path) {
    if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
      if (!interceptor_) {
        interceptor_ = std::make_unique<
            URLLoaderInterceptor>(base::BindRepeating(
            &SignedExchangeRequestHandlerBrowserTestBase::OnInterceptCallback,
            base::Unretained(this)));
      }
      interceptor_data_path_map_[url] = data_path;
    } else {
      base::PostTaskWithTraits(
          FROM_HERE, {BrowserThread::IO},
          base::BindOnce(&InstallMockInterceptors, url, data_path));
    }
  }

  void InstallMockCert() {
    // Make the MockCertVerifier treat the certificate
    // "prime256v1-sha256.public.pem" as valid for "test.example.org".
    scoped_refptr<net::X509Certificate> original_cert =
        LoadCertificate("prime256v1-sha256.public.pem");
    net::CertVerifyResult dummy_result;
    dummy_result.verified_cert = original_cert;
    dummy_result.cert_status = net::OK;
    dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::PROVIDED;
    dummy_result.ocsp_result.revocation_status =
        net::OCSPRevocationStatus::GOOD;
    mock_cert_verifier()->AddResultForCertAndHost(
        original_cert, "test.example.org", dummy_result, net::OK);
  }

  void TriggerPrefetch(const GURL& url, bool expect_success) {
    const GURL prefetch_html_url = embedded_test_server()->GetURL(
        std::string("/sxg/prefetch.html#") + url.spec());
    base::string16 expected_title =
        base::ASCIIToUTF16(expect_success ? "OK" : "FAIL");
    TitleWatcher title_watcher(shell()->web_contents(), expected_title);
    NavigateToURL(shell(), prefetch_html_url);
    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
  }

  const base::HistogramTester histogram_tester_;

 private:
  static void InstallMockInterceptors(const GURL& url,
                                      const std::string& data_path) {
    base::FilePath root_path;
    CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path));
    net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
        url, net::URLRequestMockHTTPJob::CreateInterceptorForSingleFile(
                 root_path.AppendASCII(data_path)));
  }

  bool OnInterceptCallback(URLLoaderInterceptor::RequestParams* params) {
    const auto it = interceptor_data_path_map_.find(params->url_request.url);
    if (it == interceptor_data_path_map_.end())
      return false;
    URLLoaderInterceptor::WriteResponse(it->second, params->client.get());
    return true;
  }

  base::test::ScopedFeatureList feature_list_;
  std::unique_ptr<URLLoaderInterceptor> interceptor_;
  std::map<GURL, std::string> interceptor_data_path_map_;

  DISALLOW_COPY_AND_ASSIGN(SignedExchangeRequestHandlerBrowserTestBase);
};

enum class SignedExchangeRequestHandlerBrowserTestPrefetchParam {
  kPrefetchDisabled,
  kPrefetchEnabled
};

class SignedExchangeRequestHandlerBrowserTest
    : public SignedExchangeRequestHandlerBrowserTestBase,
      public testing::WithParamInterface<
          SignedExchangeRequestHandlerBrowserTestPrefetchParam> {
 public:
  SignedExchangeRequestHandlerBrowserTest() = default;

 protected:
  bool PrefetchIsEnabled() {
    return GetParam() == SignedExchangeRequestHandlerBrowserTestPrefetchParam::
                             kPrefetchEnabled;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(SignedExchangeRequestHandlerBrowserTest);
};

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, Simple) {
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallMockCert();

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");
  if (PrefetchIsEnabled())
    TriggerPrefetch(url, true);

  base::string16 title = base::ASCIIToUTF16("https://test.example.org/test/");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  RedirectObserver redirect_observer(shell()->web_contents());
  AssertNavigationHandleFlagObserver assert_navigation_handle_flag_observer(
      shell()->web_contents());

  NavigateToURL(shell(), url);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  EXPECT_EQ(303, redirect_observer.response_code());

  NavigationEntry* entry =
      shell()->web_contents()->GetController().GetVisibleEntry();
  EXPECT_TRUE(entry->GetSSL().initialized);
  EXPECT_FALSE(!!(entry->GetSSL().content_status &
                  SSLStatus::DISPLAYED_INSECURE_CONTENT));
  ASSERT_TRUE(entry->GetSSL().certificate);

  // "test.example.org.public.pem.cbor" is generated from
  // "prime256v1-sha256.public.pem". So the SHA256 of the certificates must
  // match.
  const net::SHA256HashValue fingerprint =
      net::X509Certificate::CalculateFingerprint256(
          entry->GetSSL().certificate->cert_buffer());
  scoped_refptr<net::X509Certificate> original_cert =
      LoadCertificate("prime256v1-sha256.public.pem");
  const net::SHA256HashValue original_fingerprint =
      net::X509Certificate::CalculateFingerprint256(
          original_cert->cert_buffer());
  EXPECT_EQ(original_fingerprint, fingerprint);
  histogram_tester_.ExpectUniqueSample("SignedExchange.LoadResult",
                                       SignedExchangeLoadResult::kSuccess,
                                       PrefetchIsEnabled() ? 2 : 1);
  histogram_tester_.ExpectTotalCount(
      "SignedExchange.Time.CertificateFetch.Success",
      PrefetchIsEnabled() ? 2 : 1);
  if (PrefetchIsEnabled()) {
    histogram_tester_.ExpectUniqueSample("SignedExchange.Prefetch.LoadResult",
                                         SignedExchangeLoadResult::kSuccess, 1);
    histogram_tester_.ExpectUniqueSample(
        "SignedExchange.Prefetch.Recall.30Seconds", true, 1);
    histogram_tester_.ExpectUniqueSample(
        "SignedExchange.Prefetch.Precision.30Seconds", true, 1);
  } else {
    histogram_tester_.ExpectUniqueSample(
        "SignedExchange.Prefetch.Recall.30Seconds", false, 1);
  }
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
                       MissingNosniff) {
  InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                        "content/test/data/sxg/fallback.html");
  InstallMockCert();

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL(
      "/sxg/test.example.org_test_missing_nosniff.sxg");
  if (PrefetchIsEnabled())
    TriggerPrefetch(url, false);

  base::string16 title = base::ASCIIToUTF16("Fallback URL response");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  RedirectObserver redirect_observer(shell()->web_contents());
  NavigateToURL(shell(), url);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  EXPECT_EQ(303, redirect_observer.response_code());
  histogram_tester_.ExpectUniqueSample(
      "SignedExchange.LoadResult",
      SignedExchangeLoadResult::kSXGServedWithoutNosniff,
      PrefetchIsEnabled() ? 2 : 1);
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
                       InvalidContentType) {
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                        "content/test/data/sxg/fallback.html");
  InstallMockCert();

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL(
      "/sxg/test.example.org_test_invalid_content_type.sxg");
  if (PrefetchIsEnabled())
    TriggerPrefetch(url, false);

  base::string16 title = base::ASCIIToUTF16("Fallback URL response");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  RedirectObserver redirect_observer(shell()->web_contents());
  NavigateToURL(shell(), url);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  EXPECT_EQ(303, redirect_observer.response_code());
  histogram_tester_.ExpectUniqueSample(
      "SignedExchange.LoadResult", SignedExchangeLoadResult::kVersionMismatch,
      PrefetchIsEnabled() ? 2 : 1);
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, Expired) {
  SignedExchangeHandler::SetVerificationTimeForTesting(
      base::Time::UnixEpoch() +
      base::TimeDelta::FromSeconds(kSignatureHeaderExpires + 1));

  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                        "content/test/data/sxg/fallback.html");
  InstallMockCert();

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");

  base::string16 title = base::ASCIIToUTF16("Fallback URL response");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  RedirectObserver redirect_observer(shell()->web_contents());
  NavigateToURL(shell(), url);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  EXPECT_EQ(303, redirect_observer.response_code());
  histogram_tester_.ExpectUniqueSample(
      "SignedExchange.LoadResult",
      SignedExchangeLoadResult::kSignatureVerificationError, 1);
  histogram_tester_.ExpectUniqueSample(
      "SignedExchange.SignatureVerificationResult",
      SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, 1);
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
                       RedirectBrokenSignedExchanges) {
  InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                        "content/test/data/sxg/fallback.html");

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());

  constexpr const char* kBrokenExchanges[] = {
      "/sxg/test.example.org_test_invalid_magic_string.sxg",
      "/sxg/test.example.org_test_invalid_cbor_header.sxg",
  };

  for (const auto* broken_exchange : kBrokenExchanges) {
    SCOPED_TRACE(testing::Message()
                 << "testing broken exchange: " << broken_exchange);

    GURL url = embedded_test_server()->GetURL(broken_exchange);

    if (PrefetchIsEnabled())
      TriggerPrefetch(url, false);

    base::string16 title = base::ASCIIToUTF16("Fallback URL response");
    TitleWatcher title_watcher(shell()->web_contents(), title);
    NavigateToURL(shell(), url);
    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  }
  histogram_tester_.ExpectTotalCount("SignedExchange.LoadResult",
                                     PrefetchIsEnabled() ? 4 : 2);
  histogram_tester_.ExpectBucketCount(
      "SignedExchange.LoadResult", SignedExchangeLoadResult::kVersionMismatch,
      PrefetchIsEnabled() ? 2 : 1);
  histogram_tester_.ExpectBucketCount(
      "SignedExchange.LoadResult", SignedExchangeLoadResult::kHeaderParseError,
      PrefetchIsEnabled() ? 2 : 1);
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, CertNotFound) {
  InstallUrlInterceptor(GURL("https://cert.example.org/cert.msg"),
                        "content/test/data/sxg/404.msg");
  InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                        "content/test/data/sxg/fallback.html");

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");

  if (PrefetchIsEnabled())
    TriggerPrefetch(url, false);

  base::string16 title = base::ASCIIToUTF16("Fallback URL response");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  NavigateToURL(shell(), url);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  histogram_tester_.ExpectUniqueSample(
      "SignedExchange.LoadResult", SignedExchangeLoadResult::kCertFetchError,
      PrefetchIsEnabled() ? 2 : 1);
  histogram_tester_.ExpectTotalCount(
      "SignedExchange.Time.CertificateFetch.Failure",
      PrefetchIsEnabled() ? 2 : 1);
}

INSTANTIATE_TEST_CASE_P(
    SignedExchangeRequestHandlerBrowserTest,
    SignedExchangeRequestHandlerBrowserTest,
    testing::Values(
        SignedExchangeRequestHandlerBrowserTestPrefetchParam::kPrefetchDisabled,
        SignedExchangeRequestHandlerBrowserTestPrefetchParam::
            kPrefetchEnabled));

class SignedExchangeRequestHandlerDownloadBrowserTest
    : public SignedExchangeRequestHandlerBrowserTestBase {
 public:
  SignedExchangeRequestHandlerDownloadBrowserTest() = default;
  ~SignedExchangeRequestHandlerDownloadBrowserTest() override = default;

 protected:
  class DownloadObserver : public DownloadManager::Observer {
   public:
    DownloadObserver(DownloadManager* manager) : manager_(manager) {
      manager_->AddObserver(this);
    }
    ~DownloadObserver() override { manager_->RemoveObserver(this); }

    void WaitUntilDownloadCreated() { run_loop_.Run(); }

    const GURL& observed_url() const { return url_; }
    const std::string& observed_content_disposition() const {
      return content_disposition_;
    }

    // content::DownloadManager::Observer implementation.
    void OnDownloadCreated(content::DownloadManager* manager,
                           download::DownloadItem* item) override {
      url_ = item->GetURL();
      content_disposition_ = item->GetContentDisposition();
      run_loop_.Quit();
    }

   private:
    DownloadManager* manager_;
    base::RunLoop run_loop_;
    GURL url_;
    std::string content_disposition_;
  };

  void SetUpOnMainThread() override {
    SignedExchangeRequestHandlerBrowserTestBase::SetUpOnMainThread();
    ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
    ShellDownloadManagerDelegate* delegate =
        static_cast<ShellDownloadManagerDelegate*>(
            shell()
                ->web_contents()
                ->GetBrowserContext()
                ->GetDownloadManagerDelegate());
    delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath());
  }

 private:
  base::ScopedTempDir downloads_directory_;
};

IN_PROC_BROWSER_TEST_F(SignedExchangeRequestHandlerDownloadBrowserTest,
                       Download) {
  std::unique_ptr<DownloadObserver> observer =
      std::make_unique<DownloadObserver>(BrowserContext::GetDownloadManager(
          shell()->web_contents()->GetBrowserContext()));

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html"));

  const std::string load_sxg =
      "const iframe = document.createElement('iframe');"
      "iframe.src = './sxg/test.example.org_test_download.sxg';"
      "document.body.appendChild(iframe);";
  EXPECT_TRUE(ExecuteScript(shell()->web_contents(), load_sxg));
  observer->WaitUntilDownloadCreated();
  EXPECT_EQ(
      embedded_test_server()->GetURL("/sxg/test.example.org_test_download.sxg"),
      observer->observed_url());
  EXPECT_EQ("attachment; filename=test.sxg",
            observer->observed_content_disposition());
}

IN_PROC_BROWSER_TEST_F(SignedExchangeRequestHandlerDownloadBrowserTest,
                       DataURLDownload) {
  const GURL sxg_url = GURL("data:application/signed-exchange,");
  std::unique_ptr<DownloadObserver> observer =
      std::make_unique<DownloadObserver>(BrowserContext::GetDownloadManager(
          shell()->web_contents()->GetBrowserContext()));

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html"));

  const std::string load_sxg = base::StringPrintf(
      "const iframe = document.createElement('iframe');"
      "iframe.src = '%s';"
      "document.body.appendChild(iframe);",
      sxg_url.spec().c_str());
  EXPECT_TRUE(ExecuteScript(shell()->web_contents(), load_sxg));
  observer->WaitUntilDownloadCreated();
  EXPECT_EQ(sxg_url, observer->observed_url());
}

class SignedExchangeRequestHandlerRealCertVerifierBrowserTest
    : public SignedExchangeRequestHandlerBrowserTestBase {
 public:
  SignedExchangeRequestHandlerRealCertVerifierBrowserTest() {
    // Use "real" CertVerifier.
    disable_mock_cert_verifier();
  }
};

IN_PROC_BROWSER_TEST_F(SignedExchangeRequestHandlerRealCertVerifierBrowserTest,
                       Basic) {
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                        "content/test/data/sxg/fallback.html");

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");

  // "test.example.org_test.sxg" should pass CertVerifier::Verify() and then
  // fail at SignedExchangeHandler::CheckOCSPStatus() because of the dummy OCSP
  // response.
  // TODO(https://crbug.com/815024): Make this test pass the OCSP check. We'll
  // need to either generate an OCSP response on the fly, or override the OCSP
  // verification time.
  base::string16 title = base::ASCIIToUTF16("Fallback URL response");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  NavigateToURL(shell(), url);
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  // Verify that it failed at the OCSP check step.
  histogram_tester_.ExpectUniqueSample("SignedExchange.LoadResult",
                                       SignedExchangeLoadResult::kOCSPError, 1);
}

enum class SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam {
  kLegacy,
  kServiceWorkerServicification,
  kNetworkService
};

class SignedExchangeRequestHandlerWithServiceWorkerBrowserTest
    : public SignedExchangeRequestHandlerBrowserTestBase,
      public testing::WithParamInterface<
          SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam> {
 public:
  SignedExchangeRequestHandlerWithServiceWorkerBrowserTest() = default;
  void SetUp() override {
    switch (GetParam()) {
      case SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam::
          kLegacy:
        feature_list_.InitWithFeatures(
            {}, {blink::features::kServiceWorkerServicification,
                 network::features::kNetworkService});
        break;
      case SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam::
          kServiceWorkerServicification:
        feature_list_.InitWithFeatures(
            {blink::features::kServiceWorkerServicification},
            {network::features::kNetworkService});
        break;
      case SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam::
          kNetworkService:
        feature_list_.InitAndEnableFeature(network::features::kNetworkService);
        break;
    }
    SignedExchangeRequestHandlerBrowserTestBase::SetUp();
  }

 private:
  base::test::ScopedFeatureList feature_list_;

  DISALLOW_COPY_AND_ASSIGN(
      SignedExchangeRequestHandlerWithServiceWorkerBrowserTest);
};

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerWithServiceWorkerBrowserTest,
                       LogicalUrlInServiceWorkerScope) {
  // SW-scope: https://test.example.org/test/
  // SXG physical URL: http://127.0.0.1:PORT/sxg/test.example.org_test.sxg
  // SXG logical URL: https://test.example.org/test/
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallMockCert();

  const GURL install_sw_url =
      GURL("https://test.example.org/test/publisher-service-worker.html");

  InstallUrlInterceptor(install_sw_url,
                        "content/test/data/sxg/publisher-service-worker.html");
  InstallUrlInterceptor(
      GURL("https://test.example.org/test/publisher-service-worker.js"),
      "content/test/data/sxg/publisher-service-worker.js");
  {
    base::string16 title = base::ASCIIToUTF16("Done");
    TitleWatcher title_watcher(shell()->web_contents(), title);
    NavigateToURL(shell(), install_sw_url);
    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  }
  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");

  base::string16 title = base::ASCIIToUTF16("https://test.example.org/test/");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("Generated"));
  NavigateToURL(shell(), url);
  // The page content shoud be served from the signed exchange.
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerWithServiceWorkerBrowserTest,
                       NotControlledByDistributorsSW) {
  // SW-scope: http://127.0.0.1:PORT/sxg/
  // SXG physical URL: http://127.0.0.1:PORT/sxg/test.example.org_test.sxg
  // SXG logical URL: https://test.example.org/test/
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallMockCert();
  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL install_sw_url = embedded_test_server()->GetURL(
      "/sxg/no-respond-with-service-worker.html");

  {
    base::string16 title = base::ASCIIToUTF16("Done");
    TitleWatcher title_watcher(shell()->web_contents(), title);
    NavigateToURL(shell(), install_sw_url);
    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  }

  base::string16 title = base::ASCIIToUTF16("https://test.example.org/test/");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  NavigateToURL(shell(), embedded_test_server()->GetURL(
                             "/sxg/test.example.org_test.sxg"));
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());

  // The page must not be controlled by the service worker of the physical URL.
  EXPECT_EQ(false, EvalJs(shell(), "!!navigator.serviceWorker.controller"));
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerWithServiceWorkerBrowserTest,
                       NotControlledBySameOriginDistributorsSW) {
  // SW-scope: https://test.example.org/scope/
  // SXG physical URL: https://test.example.org/scope/test.example.org_test.sxg
  // SXG logical URL: https://test.example.org/test/
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallMockCert();

  InstallUrlInterceptor(GURL("https://test.example.org/scope/test.sxg"),
                        "content/test/data/sxg/test.example.org_test.sxg");

  const GURL install_sw_url = GURL(
      "https://test.example.org/scope/no-respond-with-service-worker.html");

  InstallUrlInterceptor(
      install_sw_url,
      "content/test/data/sxg/no-respond-with-service-worker.html");
  InstallUrlInterceptor(
      GURL("https://test.example.org/scope/no-respond-with-service-worker.js"),
      "content/test/data/sxg/no-respond-with-service-worker.js");

  {
    base::string16 title = base::ASCIIToUTF16("Done");
    TitleWatcher title_watcher(shell()->web_contents(), title);
    NavigateToURL(shell(), install_sw_url);
    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  }

  base::string16 title = base::ASCIIToUTF16("https://test.example.org/test/");
  TitleWatcher title_watcher(shell()->web_contents(), title);
  NavigateToURL(shell(), GURL("https://test.example.org/scope/test.sxg"));
  EXPECT_EQ(title, title_watcher.WaitAndGetTitle());

  // The page must not be controlled by the service worker of the physical URL.
  EXPECT_EQ(false, EvalJs(shell(), "!!navigator.serviceWorker.controller"));
}

IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerWithServiceWorkerBrowserTest,
                       RegisterServiceWorkerFromSignedExchange) {
  // SXG physical URL: http://127.0.0.1:PORT/sxg/test.example.org_test.sxg
  // SXG logical URL: https://test.example.org/test/
  InstallUrlInterceptor(
      GURL("https://cert.example.org/cert.msg"),
      "content/test/data/sxg/test.example.org.public.pem.cbor");
  InstallMockCert();

  InstallUrlInterceptor(
      GURL("https://test.example.org/test/publisher-service-worker.js"),
      "content/test/data/sxg/publisher-service-worker.js");

  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");

  {
    base::string16 title = base::ASCIIToUTF16("https://test.example.org/test/");
    TitleWatcher title_watcher(shell()->web_contents(), title);
    NavigateToURL(shell(), url);
    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
  }

  const std::string register_sw_script =
      "(async function() {"
      "  try {"
      "    const registration = await navigator.serviceWorker.register("
      "        'publisher-service-worker.js', {scope: './'});"
      "    window.domAutomationController.send(true);"
      "  } catch (e) {"
      "    window.domAutomationController.send(false);"
      "  }"
      "})();";
  bool result = false;
  EXPECT_TRUE(ExecuteScriptAndExtractBool(shell()->web_contents(),
                                          register_sw_script, &result));
  // serviceWorker.register() fails because the document URL of
  // ServiceWorkerProviderHost is empty.
  EXPECT_FALSE(result);
}

INSTANTIATE_TEST_CASE_P(
    SignedExchangeRequestHandlerWithServiceWorkerBrowserTest,
    SignedExchangeRequestHandlerWithServiceWorkerBrowserTest,
    testing::Values(
        SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam::kLegacy,
        SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam::
            kServiceWorkerServicification,
        SignedExchangeRequestHandlerWithServiceWorkerBrowserTestParam::
            kNetworkService));

struct SignedExchangeAcceptHeaderBrowserTestParam {
  SignedExchangeAcceptHeaderBrowserTestParam(
      bool sxg_enabled,
      bool sxg_origin_trial_enabled,
      bool sxg_accept_header_enabled,
      bool service_worker_servicification_enabled)
      : sxg_enabled(sxg_enabled),
        sxg_origin_trial_enabled(sxg_origin_trial_enabled),
        sxg_accept_header_enabled(sxg_accept_header_enabled),
        service_worker_servicification_enabled(
            service_worker_servicification_enabled) {}
  const bool sxg_enabled;
  const bool sxg_origin_trial_enabled;
  const bool sxg_accept_header_enabled;
  const bool service_worker_servicification_enabled;
};

class SignedExchangeAcceptHeaderBrowserTest
    : public ContentBrowserTest,
      public testing::WithParamInterface<
          SignedExchangeAcceptHeaderBrowserTestParam> {
 public:
  using self = SignedExchangeAcceptHeaderBrowserTest;
  SignedExchangeAcceptHeaderBrowserTest()
      : enabled_https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
        disabled_https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
  ~SignedExchangeAcceptHeaderBrowserTest() override = default;

 protected:
  void SetUp() override {
    std::vector<base::Feature> enable_features;
    std::vector<base::Feature> disable_features;
    if (GetParam().sxg_enabled) {
      enable_features.push_back(features::kSignedHTTPExchange);
    } else {
      disable_features.push_back(features::kSignedHTTPExchange);
    }
    if (GetParam().sxg_origin_trial_enabled) {
      enable_features.push_back(features::kSignedHTTPExchangeOriginTrial);
    } else {
      disable_features.push_back(features::kSignedHTTPExchangeOriginTrial);
    }
    if (GetParam().service_worker_servicification_enabled) {
      enable_features.push_back(blink::features::kServiceWorkerServicification);
    } else {
      disable_features.push_back(
          blink::features::kServiceWorkerServicification);
    }
    feature_list_.InitWithFeatures(enable_features, disable_features);

    enabled_https_server_.ServeFilesFromSourceDirectory("content/test/data");
    enabled_https_server_.RegisterRequestHandler(
        base::BindRepeating(&self::RedirectResponseHandler));
    enabled_https_server_.RegisterRequestMonitor(
        base::BindRepeating(&self::MonitorRequest, base::Unretained(this)));
    ASSERT_TRUE(enabled_https_server_.Start());

    disabled_https_server_.ServeFilesFromSourceDirectory("content/test/data");
    disabled_https_server_.RegisterRequestHandler(
        base::BindRepeating(&self::RedirectResponseHandler));
    disabled_https_server_.RegisterRequestMonitor(
        base::BindRepeating(&self::MonitorRequest, base::Unretained(this)));
    ASSERT_TRUE(disabled_https_server_.Start());

    if (GetParam().sxg_accept_header_enabled) {
      std::map<std::string, std::string> feature_parameters;
      feature_parameters["OriginsList"] =
          base::StringPrintf("127.0.0.1:%u", enabled_https_server_.port());
      feature_list_for_accept_header_.InitAndEnableFeatureWithParameters(
          features::kSignedHTTPExchangeAcceptHeader, feature_parameters);
    }
    ContentBrowserTest::SetUp();
  }

  void NavigateAndWaitForTitle(const GURL& url, const std::string title) {
    base::string16 expected_title = base::ASCIIToUTF16(title);
    TitleWatcher title_watcher(shell()->web_contents(), expected_title);
    NavigateToURL(shell(), url);
    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
  }

  bool ShouldHaveSXGAcceptHeaderInEnabledOrigin() const {
    return GetParam().sxg_enabled || (GetParam().sxg_origin_trial_enabled &&
                                      GetParam().sxg_accept_header_enabled);
  }

  bool ShouldHaveSXGAcceptHeaderInDisabledOrigin() const {
    return GetParam().sxg_enabled;
  }

  void CheckAcceptHeader(const GURL& url, bool is_navigation) {
    const bool is_enabled_origin =
        url.IntPort() == enabled_https_server_.port();
    const bool should_have_sxg =
        is_enabled_origin ? ShouldHaveSXGAcceptHeaderInEnabledOrigin()
                          : ShouldHaveSXGAcceptHeaderInDisabledOrigin();
    const auto accept_header = GetInterceptedAcceptHeader(url);
    ASSERT_TRUE(accept_header);
    EXPECT_EQ(
        *accept_header,
        should_have_sxg
            ? (is_navigation
                   ? std::string(network::kFrameAcceptHeader) +
                         std::string(kAcceptHeaderSignedExchangeSuffix)
                   : std::string(kExpectedSXGEnabledAcceptHeaderForPrefetch))
            : (is_navigation ? std::string(network::kFrameAcceptHeader)
                             : std::string(network::kDefaultAcceptHeader)));
  }

  void CheckNavigationAcceptHeader(const std::vector<GURL>& urls) {
    for (const auto& url : urls) {
      SCOPED_TRACE(url);
      CheckAcceptHeader(url, true /* is_navigation */);
    }
  }

  void CheckPrefetchAcceptHeader(const std::vector<GURL>& urls) {
    for (const auto& url : urls) {
      SCOPED_TRACE(url);
      CheckAcceptHeader(url, false /* is_navigation */);
    }
  }

  base::Optional<std::string> GetInterceptedAcceptHeader(
      const GURL& url) const {
    const auto it = url_accept_header_map_.find(url);
    if (it == url_accept_header_map_.end())
      return base::nullopt;
    return it->second;
  }

  void ClearInterceptedAcceptHeaders() { url_accept_header_map_.clear(); }

  net::EmbeddedTestServer enabled_https_server_;
  net::EmbeddedTestServer disabled_https_server_;

 private:
  static std::unique_ptr<net::test_server::HttpResponse>
  RedirectResponseHandler(const net::test_server::HttpRequest& request) {
    if (!base::StartsWith(request.relative_url, "/r?",
                          base::CompareCase::SENSITIVE)) {
      return std::unique_ptr<net::test_server::HttpResponse>();
    }
    std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
        new net::test_server::BasicHttpResponse);
    http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
    http_response->AddCustomHeader("Location", request.relative_url.substr(3));
    http_response->AddCustomHeader("Cache-Control", "no-cache");
    return std::move(http_response);
  }

  void MonitorRequest(const net::test_server::HttpRequest& request) {
    const auto it = request.headers.find(std::string(network::kAcceptHeader));
    if (it == request.headers.end())
      return;
    url_accept_header_map_[request.base_url.Resolve(request.relative_url)] =
        it->second;
  }

  base::test::ScopedFeatureList feature_list_;
  base::test::ScopedFeatureList feature_list_for_accept_header_;

  std::map<GURL, std::string> url_accept_header_map_;
};

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest, EnabledOrigin) {
  const GURL enabled_test_url = enabled_https_server_.GetURL("/sxg/test.html");
  EXPECT_EQ(ShouldHaveSXGAcceptHeaderInEnabledOrigin(),
            signed_exchange_utils::ShouldAdvertiseAcceptHeader(
                url::Origin::Create(enabled_test_url)));
  NavigateAndWaitForTitle(enabled_test_url, enabled_test_url.spec());
  CheckNavigationAcceptHeader({enabled_test_url});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest, DisabledOrigin) {
  const GURL disabled_test_url =
      disabled_https_server_.GetURL("/sxg/test.html");
  EXPECT_EQ(GetParam().sxg_enabled,
            signed_exchange_utils::ShouldAdvertiseAcceptHeader(
                url::Origin::Create(disabled_test_url)));

  NavigateAndWaitForTitle(disabled_test_url, disabled_test_url.spec());
  CheckNavigationAcceptHeader({disabled_test_url});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       RedirectEnabledToDisabledToEnabled) {
  const GURL enabled_test_url = enabled_https_server_.GetURL("/sxg/test.html");
  const GURL redirect_disabled_to_enabled_url =
      disabled_https_server_.GetURL("/r?" + enabled_test_url.spec());
  const GURL redirect_enabled_to_disabled_to_enabled_url =
      enabled_https_server_.GetURL("/r?" +
                                   redirect_disabled_to_enabled_url.spec());
  NavigateAndWaitForTitle(redirect_enabled_to_disabled_to_enabled_url,
                          enabled_test_url.spec());

  CheckNavigationAcceptHeader({redirect_enabled_to_disabled_to_enabled_url,
                               redirect_disabled_to_enabled_url,
                               enabled_test_url});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       RedirectDisabledToEnabledToDisabled) {
  const GURL disabled_test_url =
      disabled_https_server_.GetURL("/sxg/test.html");
  const GURL redirect_enabled_to_disabled_url =
      enabled_https_server_.GetURL("/r?" + disabled_test_url.spec());
  const GURL redirect_disabled_to_enabled_to_disabled_url =
      disabled_https_server_.GetURL("/r?" +
                                    redirect_enabled_to_disabled_url.spec());
  NavigateAndWaitForTitle(redirect_disabled_to_enabled_to_disabled_url,
                          disabled_test_url.spec());

  CheckNavigationAcceptHeader({redirect_disabled_to_enabled_to_disabled_url,
                               redirect_enabled_to_disabled_url,
                               disabled_test_url});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       PrefetchEnabledPageEnabledTarget) {
  const GURL enabled_target = enabled_https_server_.GetURL("/sxg/hello.txt");
  const GURL enabled_page_url = enabled_https_server_.GetURL(
      std::string("/sxg/prefetch.html#") + enabled_target.spec());
  NavigateAndWaitForTitle(enabled_page_url, "OK");
  CheckPrefetchAcceptHeader({enabled_target});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       PrefetchEnabledPageDisabledTarget) {
  const GURL disabled_target = disabled_https_server_.GetURL("/sxg/hello.txt");
  const GURL enabled_page_url = enabled_https_server_.GetURL(
      std::string("/sxg/prefetch.html#") + disabled_target.spec());
  NavigateAndWaitForTitle(enabled_page_url, "OK");
  CheckPrefetchAcceptHeader({disabled_target});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       PrefetchDisabledPageEnabledTarget) {
  const GURL enabled_target = enabled_https_server_.GetURL("/sxg/hello.txt");
  const GURL disabled_page_url = disabled_https_server_.GetURL(
      std::string("/sxg/prefetch.html#") + enabled_target.spec());
  NavigateAndWaitForTitle(disabled_page_url, "OK");
  CheckPrefetchAcceptHeader({enabled_target});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       PrefetchDisabledPageDisabledTarget) {
  const GURL disabled_target = disabled_https_server_.GetURL("/sxg/hello.txt");
  const GURL disabled_page_url = disabled_https_server_.GetURL(
      std::string("/sxg/prefetch.html#") + disabled_target.spec());
  NavigateAndWaitForTitle(disabled_page_url, "OK");
  CheckPrefetchAcceptHeader({disabled_target});
}

IN_PROC_BROWSER_TEST_P(
    SignedExchangeAcceptHeaderBrowserTest,
    PrefetchEnabledPageRedirectFromDisabledToEnabledToDisabledTarget) {
  const GURL disabled_target = disabled_https_server_.GetURL("/sxg/hello.txt");
  const GURL redirect_enabled_to_disabled_url =
      enabled_https_server_.GetURL("/r?" + disabled_target.spec());
  const GURL redirect_disabled_to_enabled_to_disabled_url =
      disabled_https_server_.GetURL("/r?" +
                                    redirect_enabled_to_disabled_url.spec());

  const GURL enabled_page_url = enabled_https_server_.GetURL(
      std::string("/sxg/prefetch.html#") +
      redirect_disabled_to_enabled_to_disabled_url.spec());

  NavigateAndWaitForTitle(enabled_page_url, "OK");

  CheckPrefetchAcceptHeader({redirect_disabled_to_enabled_to_disabled_url,
                             redirect_enabled_to_disabled_url,
                             disabled_target});
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest, ServiceWorker) {
  NavigateAndWaitForTitle(
      enabled_https_server_.GetURL("/sxg/service-worker.html"), "Done");
  NavigateAndWaitForTitle(
      disabled_https_server_.GetURL("/sxg/service-worker.html"), "Done");

  const std::string frame_accept = std::string(network::kFrameAcceptHeader);
  const std::string frame_accept_with_sxg =
      frame_accept + std::string(kAcceptHeaderSignedExchangeSuffix);
  const std::vector<std::string> scopes = {"/sxg/sw-scope-generated/",
                                           "/sxg/sw-scope-navigation-preload/",
                                           "/sxg/sw-scope-no-respond-with/"};
  for (const auto& scope : scopes) {
    SCOPED_TRACE(scope);
    const bool is_generated_scope =
        scope == std::string("/sxg/sw-scope-generated/");
    const GURL enabled_target_url =
        enabled_https_server_.GetURL(scope + "test.html");
    const GURL disabled_target_url =
        disabled_https_server_.GetURL(scope + "test.html");
    const GURL redirect_disabled_to_enabled_target_url =
        disabled_https_server_.GetURL("/r?" + enabled_target_url.spec());
    const GURL redirect_enabled_to_disabled_to_enabled_target_url =
        enabled_https_server_.GetURL(
            "/r?" + redirect_disabled_to_enabled_target_url.spec());
    const GURL redirect_enabled_to_disabled_target_url =
        enabled_https_server_.GetURL("/r?" + disabled_target_url.spec());
    const GURL redirect_disabled_to_enabled_to_disabled_target_url =
        disabled_https_server_.GetURL(
            "/r?" + redirect_enabled_to_disabled_target_url.spec());

    const std::string expected_enabled_title =
        is_generated_scope ? (ShouldHaveSXGAcceptHeaderInEnabledOrigin()
                                  ? frame_accept_with_sxg
                                  : frame_accept)
                           : "Done";
    const std::string expected_disabled_title =
        is_generated_scope ? (ShouldHaveSXGAcceptHeaderInDisabledOrigin()
                                  ? frame_accept_with_sxg
                                  : frame_accept)
                           : "Done";
    const base::Optional<std::string> expected_enabled_target_accept_header =
        is_generated_scope ? base::nullopt
                           : base::Optional<std::string>(
                                 ShouldHaveSXGAcceptHeaderInEnabledOrigin()
                                     ? frame_accept_with_sxg
                                     : frame_accept);
    const base::Optional<std::string> expected_disabled_target_accept_header =
        is_generated_scope ? base::nullopt
                           : base::Optional<std::string>(
                                 ShouldHaveSXGAcceptHeaderInDisabledOrigin()
                                     ? frame_accept_with_sxg
                                     : frame_accept);

    NavigateAndWaitForTitle(enabled_target_url, expected_enabled_title);
    EXPECT_EQ(expected_enabled_target_accept_header,
              GetInterceptedAcceptHeader(enabled_target_url));
    ClearInterceptedAcceptHeaders();

    NavigateAndWaitForTitle(disabled_target_url, expected_disabled_title);
    EXPECT_EQ(expected_disabled_target_accept_header,
              GetInterceptedAcceptHeader(disabled_target_url));
    ClearInterceptedAcceptHeaders();

    NavigateAndWaitForTitle(redirect_disabled_to_enabled_target_url,
                            expected_enabled_title);
    CheckNavigationAcceptHeader({redirect_disabled_to_enabled_target_url});
    EXPECT_EQ(expected_enabled_target_accept_header,
              GetInterceptedAcceptHeader(enabled_target_url));
    ClearInterceptedAcceptHeaders();

    NavigateAndWaitForTitle(redirect_enabled_to_disabled_target_url,
                            expected_disabled_title);
    CheckNavigationAcceptHeader({redirect_enabled_to_disabled_target_url});
    EXPECT_EQ(expected_disabled_target_accept_header,
              GetInterceptedAcceptHeader(disabled_target_url));
    ClearInterceptedAcceptHeaders();

    NavigateAndWaitForTitle(redirect_enabled_to_disabled_to_enabled_target_url,
                            expected_enabled_title);
    CheckNavigationAcceptHeader(
        {redirect_enabled_to_disabled_to_enabled_target_url,
         redirect_disabled_to_enabled_target_url});
    EXPECT_EQ(expected_enabled_target_accept_header,
              GetInterceptedAcceptHeader(enabled_target_url));
    ClearInterceptedAcceptHeaders();

    NavigateAndWaitForTitle(redirect_disabled_to_enabled_to_disabled_target_url,
                            expected_disabled_title);
    CheckNavigationAcceptHeader(
        {redirect_disabled_to_enabled_to_disabled_target_url,
         redirect_enabled_to_disabled_target_url});
    EXPECT_EQ(expected_disabled_target_accept_header,
              GetInterceptedAcceptHeader(disabled_target_url));
  }
}

IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
                       ServiceWorkerPrefetch) {
  NavigateAndWaitForTitle(
      enabled_https_server_.GetURL("/sxg/service-worker-prefetch.html"),
      "Done");
  NavigateAndWaitForTitle(
      disabled_https_server_.GetURL("/sxg/service-worker-prefetch.html"),
      "Done");
  const std::string scope = "/sxg/sw-prefetch-scope/";
  const GURL enabled_target_url =
      enabled_https_server_.GetURL(scope + "test.html");
  const GURL disabled_target_url =
      disabled_https_server_.GetURL(scope + "test.html");

  const GURL enabled_prefetch_target =
      enabled_https_server_.GetURL(std::string("/sxg/hello.txt"));
  const GURL disabled_prefetch_target =
      disabled_https_server_.GetURL(std::string("/sxg/hello.txt"));
  const std::string load_prefetch_script = base::StringPrintf(
      "(function loadPrefetch(urls) {"
      "  for (let url of urls) {"
      "    let link = document.createElement('link');"
      "    link.rel = 'prefetch';"
      "    link.href = url;"
      "    document.body.appendChild(link);"
      "  }"
      "  function check() {"
      "    const entries = performance.getEntriesByType('resource');"
      "    const url_set = new Set(urls);"
      "    for (let entry of entries) {"
      "      url_set.delete(entry.name);"
      "    }"
      "    if (!url_set.size) {"
      "      window.domAutomationController.send(true);"
      "    } else {"
      "      setTimeout(check, 100);"
      "    }"
      "  }"
      "  check();"
      "})(['%s','%s'])",
      enabled_prefetch_target.spec().c_str(),
      disabled_prefetch_target.spec().c_str());
  bool unused = false;

  NavigateAndWaitForTitle(enabled_target_url, "Done");
  EXPECT_TRUE(ExecuteScriptAndExtractBool(shell()->web_contents(),
                                          load_prefetch_script, &unused));
  CheckPrefetchAcceptHeader(
      {enabled_prefetch_target, disabled_prefetch_target});
  ClearInterceptedAcceptHeaders();

  NavigateAndWaitForTitle(disabled_target_url, "Done");
  EXPECT_TRUE(ExecuteScriptAndExtractBool(shell()->web_contents(),
                                          load_prefetch_script, &unused));
  CheckPrefetchAcceptHeader(
      {enabled_prefetch_target, disabled_prefetch_target});
}

INSTANTIATE_TEST_CASE_P(
    SignedExchangeAcceptHeaderBrowserTest,
    SignedExchangeAcceptHeaderBrowserTest,
    testing::Values(
        SignedExchangeAcceptHeaderBrowserTestParam(false, false, false, false),
        SignedExchangeAcceptHeaderBrowserTestParam(false, false, false, true),
        SignedExchangeAcceptHeaderBrowserTestParam(false, false, true, false),
        SignedExchangeAcceptHeaderBrowserTestParam(false, false, true, true),
        SignedExchangeAcceptHeaderBrowserTestParam(false, true, false, false),
        SignedExchangeAcceptHeaderBrowserTestParam(false, true, false, true),
        SignedExchangeAcceptHeaderBrowserTestParam(false, true, true, false),
        SignedExchangeAcceptHeaderBrowserTestParam(false, true, true, true),
        SignedExchangeAcceptHeaderBrowserTestParam(true, false, false, false),
        SignedExchangeAcceptHeaderBrowserTestParam(true, false, false, true),
        SignedExchangeAcceptHeaderBrowserTestParam(true, false, true, false),
        SignedExchangeAcceptHeaderBrowserTestParam(true, false, true, true),
        SignedExchangeAcceptHeaderBrowserTestParam(true, true, false, false),
        SignedExchangeAcceptHeaderBrowserTestParam(true, true, false, true),
        SignedExchangeAcceptHeaderBrowserTestParam(true, true, true, false),
        SignedExchangeAcceptHeaderBrowserTestParam(true, true, true, true)));

}  // namespace content
