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

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/strcat.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/test/values_test_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/attribution_reporting/constants.h"
#include "components/ukm/content/source_url_recorder.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/attribution_reporting/attribution_manager.h"
#include "content/browser/attribution_reporting/attribution_manager_impl.h"
#include "content/browser/attribution_reporting/attribution_os_level_manager.h"
#include "content/browser/attribution_reporting/attribution_test_utils.h"
#include "content/browser/attribution_reporting/event_level_result.mojom.h"
#include "content/browser/attribution_reporting/storable_source.h"
#include "content/browser/attribution_reporting/store_source_result.mojom.h"
#include "content/browser/attribution_reporting/test/mock_attribution_observer.h"
#include "content/browser/attribution_reporting/test/mock_content_browser_client.h"
#include "content/browser/fenced_frame/fenced_frame_reporter.h"
#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
#include "content/browser/private_aggregation/private_aggregation_manager.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/page_impl.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_context_core_observer.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/fenced_frame_test_utils.h"
#include "content/test/test_content_browser_client.h"
#include "net/dns/mock_host_resolver.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/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
#include "third_party/blink/public/common/fenced_frame/redacted_fenced_frame_config.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace content {

namespace {

using ::testing::_;
using ::testing::Return;

constexpr char kBaseDataDir[] = "content/test/data/";

using attribution_reporting::kAttributionReportingRegisterSourceHeader;
using attribution_reporting::mojom::EventLevelResult;

using StoreSourceStatus = attribution_reporting::mojom::StoreSourceResult;

void ExpectRegisterResultAndRun(blink::ServiceWorkerStatusCode expected,
                                base::RepeatingClosure continuation,
                                blink::ServiceWorkerStatusCode actual) {
  EXPECT_EQ(expected, actual);
  continuation.Run();
}

struct UkmEntry {
  GURL url;
  int64_t value;
};

void VerifyUkmEntries(const ukm::TestAutoSetUkmRecorder& ukm_recorder,
                      std::string_view entry_name,
                      std::string_view metric_name,
                      base::span<const UkmEntry> expected_entries) {
  std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> ukm_entries =
      ukm_recorder.GetEntries(std::string(entry_name),
                              {std::string(metric_name)});
  ASSERT_EQ(ukm_entries.size(), expected_entries.size());
  for (size_t i = 0; i < ukm_entries.size(); ++i) {
    EXPECT_EQ(
        ukm_recorder.GetSourceForSourceId(ukm_entries[i].source_id)->url(),
        expected_entries[i].url);
    EXPECT_THAT(ukm_entries[i].metrics,
                testing::ElementsAre(
                    testing::Pair(metric_name, expected_entries[i].value)));
  }
}

void VerifySourceUkmEntries(const ukm::TestAutoSetUkmRecorder& ukm_recorder,
                            const std::vector<UkmEntry>& expected_entries) {
  VerifyUkmEntries(ukm_recorder, "Conversions.SourceRegistration",
                   "StoreSourceResult", expected_entries);
}

void VerifyEventLevelResultUkmEntries(
    const ukm::TestAutoSetUkmRecorder& ukm_recorder,
    const std::vector<UkmEntry>& expected_entries) {
  VerifyUkmEntries(ukm_recorder, "Conversions.TriggerRegistration",
                   "CreateEventLevelReportStatus", expected_entries);
}

// Observer which waits for a service worker to register in the browser process
// by observing worker activation status.
class WorkerStateObserver : public ServiceWorkerContextCoreObserver {
 public:
  WorkerStateObserver(scoped_refptr<ServiceWorkerContextWrapper> context,
                      ServiceWorkerVersion::Status target)
      : context_(std::move(context)), target_(target) {
    observation_.Observe(context_.get());
  }

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

  ~WorkerStateObserver() override = default;

  // ServiceWorkerContextCoreObserver overrides.
  void OnVersionStateChanged(int64_t version_id,
                             const GURL& scope,
                             const blink::StorageKey& key,
                             ServiceWorkerVersion::Status) override {
    const ServiceWorkerVersion* version = context_->GetLiveVersion(version_id);
    if (version->status() == target_) {
      context_->RemoveObserver(this);
      run_loop_.Quit();
    }
  }
  void Wait() { run_loop_.Run(); }

 private:
  base::RunLoop run_loop_;
  scoped_refptr<ServiceWorkerContextWrapper> context_;
  const ServiceWorkerVersion::Status target_;
  base::ScopedObservation<ServiceWorkerContextWrapper,
                          ServiceWorkerContextCoreObserver>
      observation_{this};
};

// Waits for the a given |report_url| to be received by the test server. Wraps a
// ControllableHttpResponse so that it can wait for the server request in a
// thread-safe manner. Therefore, these must be registered prior to |server|
// starting.
struct ExpectedReportWaiter {
  ExpectedReportWaiter(GURL report_url,
                       std::string attribution_destination,
                       std::string source_event_id,
                       std::string source_type,
                       std::string trigger_data,
                       net::EmbeddedTestServer* server)
      : ExpectedReportWaiter(std::move(report_url),
                             base::Value::Dict(),
                             server) {
    expected_body.Set("attribution_destination",
                      std::move(attribution_destination));
    expected_body.Set("source_event_id", std::move(source_event_id));
    expected_body.Set("source_type", std::move(source_type));
    expected_body.Set("trigger_data", std::move(trigger_data));
  }

  // ControllableHTTPResponses can only wait for relative urls, so only supply
  // the path.
  ExpectedReportWaiter(GURL report_url,
                       base::Value::Dict body,
                       net::EmbeddedTestServer* server)
      : expected_url(std::move(report_url)),
        expected_body(std::move(body)),
        response(std::make_unique<net::test_server::ControllableHttpResponse>(
            server,
            expected_url.GetPath())) {}

  GURL expected_url;
  base::Value::Dict expected_body;
  std::unique_ptr<net::test_server::ControllableHttpResponse> response;

  bool HasRequest() { return !!response->http_request(); }

  // Waits for a report to be received matching the report url. Verifies that
  // the report url and report body were set correctly.
  void WaitForReport() {
    if (!response->http_request()) {
      response->WaitForRequest();
    }

    // The embedded test server resolves all urls to 127.0.0.1, so get the real
    // request host from the request headers.
    const net::test_server::HttpRequest& request = *response->http_request();
    DCHECK(base::Contains(request.headers, "Host"));
    const GURL& request_url = request.GetURL();
    GURL header_url = GURL("https://" + request.headers.at("Host"));
    std::string host = header_url.GetHost();
    GURL::Replacements replace_host;
    replace_host.SetHostStr(host);

    base::Value::Dict body = base::test::ParseJsonDict(request.content);
    EXPECT_THAT(body, base::test::DictionaryHasValues(expected_body));

    // The report ID is random, so just test that the field exists here and is a
    // valid GUID.
    const std::string* report_id = body.FindString("report_id");
    ASSERT_TRUE(report_id);
    EXPECT_TRUE(base::Uuid::ParseLowercase(*report_id).is_valid());

    EXPECT_TRUE(body.FindDouble("randomized_trigger_rate"));

    EXPECT_FALSE(body.FindString("source_debug_key"));

    EXPECT_FALSE(body.FindString("trigger_debug_key"));

    // Clear the port as it is assigned by the EmbeddedTestServer at runtime.
    replace_host.SetPortStr("");

    // Compare the expected report url with a URL formatted with the host
    // defined in the headers. This would not match |expected_url| if the host
    // for report url was not set properly.
    EXPECT_EQ(expected_url, request_url.ReplaceComponents(replace_host));

    EXPECT_TRUE(base::Contains(request.headers, "User-Agent"));
    EXPECT_EQ(request.headers.at("Content-Type"), "application/json");
  }
};

}  // namespace

class InterestGroupEnabledContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {
 public:
  explicit InterestGroupEnabledContentBrowserClient() = default;

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

  // ContentBrowserClient overrides:
  // This is needed so that the interest group related APIs can run without
  // failing with the result AuctionResult::kSellerRejected.
  bool IsPrivacySandboxReportingDestinationAttested(
      content::BrowserContext* browser_context,
      const url::Origin& destination_origin,
      content::PrivacySandboxInvokingAPI invoking_api) override {
    return true;
  }
};

class AttributionsBrowserTestBase : public ContentBrowserTest {
 public:
  AttributionsBrowserTestBase() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kAttributionReportingDebugMode);

    // Sets up the blink runtime feature for ConversionMeasurement.
    command_line->AppendSwitch(
        switches::kEnableExperimentalWebPlatformFeatures);
  }

  void PreRunTestOnMainThread() override {
    ContentBrowserTest::PreRunTestOnMainThread();
    ukm::InitializeSourceUrlRecorderForWebContents(web_contents());
  }

  void SetUpOnMainThread() override {
    // These tests don't cover online/offline behavior; that is covered by
    // `AttributionManagerImpl`'s unit tests. Here we use a fake tracker that
    // always indicates online. See crbug.com/1285057 for details.
    network_connection_tracker_ =
        network::TestNetworkConnectionTracker::CreateInstance();
    SetNetworkConnectionTrackerForTesting(nullptr);
    SetNetworkConnectionTrackerForTesting(network_connection_tracker_.get());

    host_resolver()->AddRule("*", "127.0.0.1");

    https_server_ = std::make_unique<net::EmbeddedTestServer>(
        net::EmbeddedTestServer::TYPE_HTTPS);
    https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
    net::test_server::RegisterDefaultHandlers(https_server_.get());
    https_server_->ServeFilesFromSourceDirectory("content/test/data");
    https_server_->ServeFilesFromSourceDirectory(
        "content/test/data/attribution_reporting");

    StoragePartition* partition = shell()
                                      ->web_contents()
                                      ->GetBrowserContext()
                                      ->GetDefaultStoragePartition();
    wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
        partition->GetServiceWorkerContext());
    content_browser_client_ =
        std::make_unique<InterestGroupEnabledContentBrowserClient>();
  }

  void TearDownOnMainThread() override {
    SetNetworkConnectionTrackerForTesting(nullptr);
  }

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

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

  AttributionManager* attribution_manager() {
    return static_cast<StoragePartitionImpl*>(
               web_contents()
                   ->GetBrowserContext()
                   ->GetDefaultStoragePartition())
        ->GetAttributionManager();
  }

  void RegisterSource(const GURL& attribution_src_url) {
    MockAttributionObserver observer;
    base::ScopedObservation<AttributionManager, AttributionObserver>
        observation(&observer);
    observation.Observe(attribution_manager());

    base::RunLoop loop;
    EXPECT_CALL(observer,
                OnSourceHandled(_, _, _, StorableSource::Result::kSuccess))
        .WillOnce([&]() { loop.Quit(); });

    EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                                 attribution_src_url)));

    // Wait until the source has been stored before registering the trigger;
    // otherwise the trigger could be processed before the source, in which case
    // there would be no matching source: crbug.com/1309173.
    loop.Run();
  }

  void CreateAndClickSource(WebContents* web_contents,
                            const GURL& href,
                            const std::string& attribution_src) {
    CreateAndClickSourceInFrame(web_contents,
                                web_contents->GetPrimaryMainFrame(), href,
                                attribution_src,
                                /*target=*/"_top");
  }

  WebContents* CreateAndClickPopupSource(WebContents* web_contents,
                                         const GURL& href,
                                         const std::string& attribution_src,
                                         const std::string& target) {
    return CreateAndClickSourceInFrame(nullptr,
                                       web_contents->GetPrimaryMainFrame(),
                                       href, attribution_src, target);
  }

  WebContents* CreateAndClickSourceInFrame(WebContents* web_contents,
                                           RenderFrameHost* rfh,
                                           const GURL& href,
                                           const std::string& attribution_src,
                                           const std::string& target) {
    EXPECT_TRUE(ExecJs(rfh, JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: $2,
                        target: $3});)",
                                      href, attribution_src, target)));

    MockAttributionObserver source_observer;
    base::ScopedObservation<AttributionManager, AttributionObserver>
        observation(&source_observer);
    observation.Observe(attribution_manager());

    base::RunLoop loop;
    bool received = false;
    EXPECT_CALL(source_observer,
                OnSourceHandled(_, _, _, StorableSource::Result::kSuccess))
        .WillOnce([&]() {
          received = true;
          loop.Quit();
        });

    WebContents* popup_contents = nullptr;
    if (!web_contents) {
      ShellAddedObserver new_shell_observer;
      TestNavigationObserver observer(nullptr);
      observer.StartWatchingNewWebContents();
      EXPECT_TRUE(ExecJs(rfh, "simulateClick('link');"));
      popup_contents = new_shell_observer.GetShell()->web_contents();
      observer.Wait();
    } else {
      TestNavigationObserver observer(web_contents);
      EXPECT_TRUE(ExecJs(rfh, "simulateClick('link');"));
      observer.Wait();
    }

    // If the source wasn't processed, wait to ensure we handle events in test
    // order. See https://crbug.com/1309173.
    if (!received) {
      loop.Run();
    }

    return popup_contents;
  }

  ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
  ServiceWorkerContext* public_context() { return wrapper(); }

 private:
  AttributionManagerImpl::ScopedUseInMemoryStorageForTesting
      attribution_manager_in_memory_setting_;

  std::unique_ptr<net::EmbeddedTestServer> https_server_;

  std::unique_ptr<network::TestNetworkConnectionTracker>
      network_connection_tracker_;

  scoped_refptr<ServiceWorkerContextWrapper> wrapper_;

  std::unique_ptr<InterestGroupEnabledContentBrowserClient>
      content_browser_client_;
};

class AttributionsBrowserTest : public AttributionsBrowserTestBase,
                                public ::testing::WithParamInterface<bool> {
 public:
  explicit AttributionsBrowserTest(
      std::vector<base::test::FeatureRef> enabled_features = {},
      std::vector<base::test::FeatureRef> disabled_features = {}) {
    const bool enable_in_browser_migration = GetParam();

    if (enable_in_browser_migration) {
      enabled_features.emplace_back(
          blink::features::kKeepAliveInBrowserMigration);
    } else {
      disabled_features.emplace_back(
          blink::features::kKeepAliveInBrowserMigration);
    }

    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(All, AttributionsBrowserTest, ::testing::Bool());

// Verifies that storage initialization does not hang when initialized in a
// browsertest context, see https://crbug.com/1080764).
IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       FeatureEnabled_StorageInitWithoutHang) {}

IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       ImpressionNavigationMultipleRedirects_BothReportsSent) {
  ukm::TestAutoSetUkmRecorder ukm_recorder;

  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");

  auto register_response2 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");

  // Expected reports must be registered before the server starts.
  ExpectedReportWaiter expected_report(
      GURL("https://d.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://c.test",
      /*source_event_id=*/"1", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ExpectedReportWaiter expected_report2(
      GURL("https://b.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://c.test",
      /*source_event_id=*/"2", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  // Create an anchor tag with impression attributes and click the link. By
  // default the target is set to "_top".
  GURL register_source_url =
      https_server()->GetURL("d.test", "/register_source_redirect");

  EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: '',
                        target: $2});)",
                                               register_source_url, "_top")));

  TestNavigationObserver observer(web_contents());
  EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  register_response->WaitForRequest();
  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader(
      kAttributionReportingRegisterSourceHeader,
      R"({"source_event_id":"1","destination":"https://c.test"})");

  http_response->AddCustomHeader(
      "Location",
      https_server()->GetURL("b.test", "/register_source_redirect").spec());
  register_response->Send(http_response->ToResponseString());
  register_response->Done();

  register_response2->WaitForRequest();
  auto http_response2 = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response2->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response2->AddCustomHeader(
      kAttributionReportingRegisterSourceHeader,
      R"({"source_event_id":"2","destination":"https://c.test"})");

  GURL conversion_url = https_server()->GetURL(
      "c.test", "/attribution_reporting/page_with_conversion_redirect.html");

  http_response2->AddCustomHeader("Location", conversion_url.spec());
  register_response2->Send(http_response2->ToResponseString());
  register_response2->Done();

  // Wait for navigation to complete.
  observer.Wait();

  GURL register_trigger_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));
  expected_report.WaitForReport();

  GURL register_trigger_url2 = https_server()->GetURL(
      "b.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url2)));
  expected_report2.WaitForReport();

  VerifySourceUkmEntries(
      ukm_recorder,
      {UkmEntry(impression_url,
                static_cast<int64_t>(StoreSourceStatus::kSuccess)),
       UkmEntry(impression_url,
                static_cast<int64_t>(StoreSourceStatus::kSuccess))});

  VerifyEventLevelResultUkmEntries(
      ukm_recorder,
      {UkmEntry(conversion_url,
                static_cast<int64_t>(EventLevelResult::kSuccess)),
       UkmEntry(conversion_url,
                static_cast<int64_t>(EventLevelResult::kSuccess))});
}

IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       ImpressionsRegisteredOnNavigation_ReportSent) {
  ukm::TestAutoSetUkmRecorder ukm_recorder;

  // Expected reports must be registered before the server starts.
  ExpectedReportWaiter expected_report(
      GURL("https://c.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://c.test",
      /*source_event_id=*/"1", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  // 1 - Navigate on a page that can register a source
  GURL impression_url = https_server()->GetURL(
      "b.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  // 2 - Create an anchor tag with impression attributes and click the link.
  GURL register_source_url = https_server()->GetURL(
      "c.test",
      "/attribution_reporting/"
      "page_with_source_registration_and_conversion.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: ''});)",
                                               register_source_url)));

  TestNavigationObserver observer(web_contents());
  EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  // Wait for navigation to complete.
  observer.Wait();

  // 3 - On the landing page and destination origin, register a trigger that
  // should match the navigation-source.
  GURL register_trigger_url = https_server()->GetURL(
      "c.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));
  expected_report.WaitForReport();

  VerifySourceUkmEntries(
      ukm_recorder,
      {UkmEntry(impression_url,
                static_cast<int64_t>(StoreSourceStatus::kSuccess))});

  VerifyEventLevelResultUkmEntries(
      ukm_recorder,
      {UkmEntry(register_source_url,
                static_cast<int64_t>(EventLevelResult::kSuccess))});
}

IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       NonAttributionEligibleNavigation_NoEligibleHeader) {
  auto register_response1 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL register_source_url =
      https_server()->GetURL("d.test", "/register_source_redirect");

  // Create a non-attribution eligible anchor and click.
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(
    const anchor = document.createElement('a');
    anchor.href = $1;
    anchor.target = '_top';
    anchor.id = 'link';
    document.body.appendChild(anchor);)",
                                               register_source_url)));
  EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  // Verify the navigation request does not contain the eligibility header.
  register_response1->WaitForRequest();
  EXPECT_FALSE(base::Contains(register_response1->http_request()->headers,
                              "Attribution-Reporting-Eligible"));
  EXPECT_FALSE(base::Contains(register_response1->http_request()->headers,
                              "Attribution-Reporting-Support"));

  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_OK);
  register_response1->Send(http_response->ToResponseString());
  register_response1->Done();
}

IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       ImpressionFromCrossOriginSubframe_ReportSent) {
  ukm::TestAutoSetUkmRecorder ukm_recorder;

  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://d.test",
      /*source_event_id=*/"5", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL page_url = https_server()->GetURL("a.test", "/page_with_iframe.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), page_url));

  GURL subframe_url = https_server()->GetURL(
      "c.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(ExecJs(shell(), R"(
    let frame= document.getElementById('test_iframe');
    frame.setAttribute('allow', 'attribution-reporting');)"));
  NavigateIframeToURL(web_contents(), "test_iframe", subframe_url);
  RenderFrameHost* subframe =
      ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0);

  GURL conversion_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
  GURL register_source_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_source_headers.html");

  // Create an impression tag in the subframe and target a popup window.
  auto* popup_contents = CreateAndClickSourceInFrame(
      /*web-contents=*/nullptr, subframe, conversion_url,
      register_source_url.spec(),
      /*target=*/"new_frame");

  GURL register_trigger_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(popup_contents, JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));

  expected_report.WaitForReport();

  VerifySourceUkmEntries(
      ukm_recorder,
      {UkmEntry(page_url, static_cast<int64_t>(StoreSourceStatus::kSuccess))});

  VerifyEventLevelResultUkmEntries(
      ukm_recorder,
      {UkmEntry(conversion_url,
                static_cast<int64_t>(EventLevelResult::kSuccess))});
}

// Regression test for crbug.com/1366513.
// TODO(b/331159758): Disabled due to flakiness.
IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       DISABLED_AttributionSrcInSandboxedIframe_NoCrash) {
  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://a.test",
      /*source_event_id=*/"5", /*source_type=*/"event",
      /*trigger_data=*/"1", https_server());

  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");

  auto register_response2 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_trigger");

  ASSERT_TRUE(https_server()->Start());

  GURL page_url = https_server()->GetURL("a.test", "/page_with_iframe.html");
  ASSERT_TRUE(NavigateToURL(web_contents(), page_url));

  // Setting the frame's sandbox attribute causes its origin to be opaque.
  ASSERT_TRUE(ExecJs(shell(), R"(
    const frame = document.getElementById('test_iframe');
    frame.setAttribute('sandbox', '');
    frame.setAttribute('srcdoc', '<img attributionsrc=/register_source_redirect>');
  )"));

  {
    register_response->WaitForRequest();
    auto http_response =
        std::make_unique<net::test_server::BasicHttpResponse>();
    http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
    http_response->AddCustomHeader(
        kAttributionReportingRegisterSourceHeader,
        R"({"source_event_id":"5","destination":"https://a.test"})");
    http_response->AddCustomHeader(
        "Location",
        https_server()->GetURL("a.test", "/register_trigger").spec());
    register_response->Send(http_response->ToResponseString());
    register_response->Done();
  }

  {
    register_response2->WaitForRequest();
    auto http_response2 =
        std::make_unique<net::test_server::BasicHttpResponse>();
    http_response2->AddCustomHeader(
        "Attribution-Reporting-Register-Trigger",
        R"({"event_trigger_data":[{"trigger_data":"1"}]})");
    register_response2->Send(http_response2->ToResponseString());
    register_response2->Done();
  }

  expected_report.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       ImpressionOnNoOpenerNavigation_ReportSent) {
  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://d.test",
      /*source_event_id=*/"5", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL conversion_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
  GURL register_source_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_source_headers.html");

  // target="_blank" navs are rel="noopener" by default.
  CreateAndClickPopupSource(web_contents(), conversion_url,
                            register_source_url.spec(),
                            /*target=*/"_blank");

  GURL register_trigger_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(
      ExecJs(Shell::windows()[1]->web_contents(),
             JsReplace("createAttributionSrcImg($1);", register_trigger_url)));

  expected_report.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ConversionOnDifferentSubdomainThanLandingPage_ReportSent) {
  // Expected reports must be registered before the server starts.
  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://d.test",
      /*source_event_id=*/"5", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL conversion_url = https_server()->GetURL(
      "sub.d.test",
      "/attribution_reporting/page_with_conversion_redirect.html");
  GURL register_source_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_source_headers.html");

  CreateAndClickSource(web_contents(), conversion_url,
                       register_source_url.spec());

  // Navigate to a same domain origin that is different than the landing page
  // for the click and convert there. A report should still be sent.
  GURL other_conversion_url = https_server()->GetURL(
      "other.d.test",
      "/attribution_reporting/page_with_conversion_redirect.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), other_conversion_url));

  GURL register_trigger_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));

  expected_report.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionSrcRedirect_ReporterSet) {
  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/attribution_reporting/register_source_redirect");

  ExpectedReportWaiter expected_report(
      GURL("https://c.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://d.test",
      /*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
      https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");

  // Setup our service worker.
  WorkerStateObserver sw_observer(wrapper(), ServiceWorkerVersion::ACTIVATED);
  blink::mojom::ServiceWorkerRegistrationOptions options(
      impression_url, blink::mojom::ScriptType::kClassic,
      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
  const blink::StorageKey key =
      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
  public_context()->RegisterServiceWorker(
      https_server()->GetURL("a.test",
                             "/attribution_reporting/service_worker.js"),
      key, options,
      base::BindOnce(&ExpectRegisterResultAndRun,
                     blink::ServiceWorkerStatusCode::kOk, base::DoNothing()));
  sw_observer.Wait();

  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  MockAttributionObserver observer;
  base::ScopedObservation<AttributionManager, AttributionObserver> observation(
      &observer);
  observation.Observe(attribution_manager());

  base::RunLoop loop;
  EXPECT_CALL(observer,
              OnSourceHandled(_, _, _, StorableSource::Result::kSuccess))
      .WillOnce([&]() { loop.Quit(); });

  EXPECT_TRUE(ExecJs(
      web_contents(),
      JsReplace(
          "createAttributionSrcImg($1);",
          https_server()->GetURL(
              "a.test", "/attribution_reporting/register_source_redirect"))));

  register_response->WaitForRequest();
  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader(
      "Location",
      https_server()
          ->GetURL("c.test",
                   "/attribution_reporting/register_source_headers.html")
          .spec());
  register_response->Send(http_response->ToResponseString());
  register_response->Done();

  // Wait until the source has been stored before registering the trigger;
  // otherwise the trigger could be processed before the source, in which case
  // there would be no matching source: crbug.com/1309173.
  loop.Run();

  GURL conversion_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));

  GURL register_trigger_url = https_server()->GetURL(
      "c.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));

  expected_report.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionEligibleRedirect_ReporterSet) {
  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/attribution_reporting/register_source_redirect");

  ExpectedReportWaiter expected_report(
      GURL("https://c.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://d.test",
      /*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
      https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");

  // Setup our service worker.
  WorkerStateObserver sw_observer(wrapper(), ServiceWorkerVersion::ACTIVATED);
  blink::mojom::ServiceWorkerRegistrationOptions options(
      impression_url, blink::mojom::ScriptType::kClassic,
      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
  const blink::StorageKey key =
      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
  public_context()->RegisterServiceWorker(
      https_server()->GetURL("a.test",
                             "/attribution_reporting/service_worker.js"),
      key, options,
      base::BindOnce(&ExpectRegisterResultAndRun,
                     blink::ServiceWorkerStatusCode::kOk, base::DoNothing()));
  sw_observer.Wait();

  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  MockAttributionObserver observer;
  base::ScopedObservation<AttributionManager, AttributionObserver> observation(
      &observer);
  observation.Observe(attribution_manager());

  base::RunLoop loop;
  EXPECT_CALL(observer,
              OnSourceHandled(_, _, _, StorableSource::Result::kSuccess))
      .WillOnce([&]() { loop.Quit(); });

  EXPECT_TRUE(ExecJs(
      web_contents(),
      JsReplace(
          "createAttributionEligibleImgSrc($1);",
          https_server()->GetURL(
              "a.test", "/attribution_reporting/register_source_redirect"))));

  register_response->WaitForRequest();
  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader(
      "Location",
      https_server()
          ->GetURL("c.test",
                   "/attribution_reporting/register_source_headers.html")
          .spec());
  register_response->Send(http_response->ToResponseString());
  register_response->Done();

  // Wait until the source has been stored before registering the trigger;
  // otherwise the trigger could be processed before the source, in which case
  // there would be no matching source: crbug.com/1309173.
  loop.Run();

  GURL conversion_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));

  GURL register_trigger_url = https_server()->GetURL(
      "c.test", "/attribution_reporting/register_trigger_headers.html");
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));

  expected_report.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(AttributionsBrowserTest,
                       NavigationNoneSupported_EligibleHeaderNotSet) {
  MockAttributionReportingContentBrowserClientBase<
      ContentBrowserTestContentBrowserClient>
      browser_client;
  EXPECT_CALL(
      browser_client,
      GetAttributionSupport(
          ContentBrowserClient::AttributionReportingOsApiState::kDisabled,
          /*client_os_disabled=*/false))
      .WillRepeatedly(Return(network::mojom::AttributionSupport::kNone));

  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source");
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL register_source_url =
      https_server()->GetURL("d.test", "/register_source");

  EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: '',
                        target: $2});)",
                                               register_source_url, "_top")));
  EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  register_response->WaitForRequest();
  ExpectEmptyAttributionReportingEligibleHeader(
      register_response->http_request()->headers.at(
          "Attribution-Reporting-Eligible"));
  ExpectValidAttributionReportingSupportHeader(
      register_response->http_request()->headers.at(
          "Attribution-Reporting-Support"),
      /*web_expected=*/false,
      /*os_expected=*/false);
}

class AttributionsPrerenderBrowserTest : public AttributionsBrowserTest {
 public:
  AttributionsPrerenderBrowserTest()
      : prerender_helper_(
            base::BindRepeating(&AttributionsBrowserTest::web_contents,
                                base::Unretained(this))) {}
  ~AttributionsPrerenderBrowserTest() override = default;

 protected:
  content::test::PrerenderTestHelper prerender_helper_;
};

INSTANTIATE_TEST_SUITE_P(All,
                         AttributionsPrerenderBrowserTest,
                         ::testing::Bool());

// TODO(crbug.com/40231714): these tests are flaky on most release bots.
#if defined(NDEBUG)
#define ATTRIBUTION_PRERENDER_BROWSER_TEST(TEST_NAME) \
  IN_PROC_BROWSER_TEST_P(AttributionsPrerenderBrowserTest, DISABLED_##TEST_NAME)
#else
#define ATTRIBUTION_PRERENDER_BROWSER_TEST(TEST_NAME) \
  IN_PROC_BROWSER_TEST_P(AttributionsPrerenderBrowserTest, TEST_NAME)
#endif

ATTRIBUTION_PRERENDER_BROWSER_TEST(NoConversionsOnPrerender) {
  const char* kTestCases[] = {
      "createAttributionSrcImg($1);",
      "createTrackingPixel($1);",
      R"(fetch($1, {keepalive: true}))",
  };

  for (const char* registration_js : kTestCases) {
    auto https_server = std::make_unique<net::EmbeddedTestServer>(
        net::EmbeddedTestServer::TYPE_HTTPS);
    https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
    https_server->ServeFilesFromSourceDirectory("content/test/data");

    ExpectedReportWaiter expected_report(
        GURL("https://a.test/.well-known/attribution-reporting/"
             "report-event-attribution"),
        /*attribution_destination=*/"https://d.test",
        /*source_event_id=*/"7", /*source_type=*/"event", /*trigger_data=*/"1",
        https_server.get());
    ASSERT_TRUE(https_server->Start());

    // Navigate to a page with impression creator.
    const GURL kImpressionUrl = https_server->GetURL(
        "a.test", "/attribution_reporting/page_with_impression_creator.html");
    EXPECT_TRUE(NavigateToURL(web_contents(), kImpressionUrl));

    // Register impression for the target conversion url.
    GURL register_url = https_server->GetURL(
        "a.test", "/attribution_reporting/register_source_headers.html");

    EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                                 register_url)));

    // Navigate to a starting same origin page with the conversion url.
    const GURL kEmptyUrl = https_server->GetURL("d.test", "/empty.html");
    {
      auto url_loader_interceptor =
          content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
              kBaseDataDir, kEmptyUrl.DeprecatedGetOriginAsURL());
      EXPECT_TRUE(NavigateToURL(web_contents(), kEmptyUrl));
    }

    // Pre-render the conversion url.
    const GURL kConversionUrl = https_server->GetURL(
        "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
    FrameTreeNodeId host_id = prerender_helper_.AddPrerender(kConversionUrl);
    content::test::PrerenderHostObserver host_observer(*web_contents(),
                                                       host_id);

    prerender_helper_.WaitForPrerenderLoadCompletion(kConversionUrl);
    content::RenderFrameHost* prerender_rfh =
        prerender_helper_.GetPrerenderedMainFrameHost(host_id);

    // Register a conversion with the original page as the reporting origin
    // during pre-rendering.
    const GURL register_trigger_url = https_server->GetURL(
        "a.test", "/attribution_reporting/register_trigger_headers.html");
    EXPECT_TRUE(ExecJs(prerender_rfh,
                       JsReplace(registration_js, register_trigger_url)));

    // Verify that registering a conversion had no effect on reports, as the
    // impressions were never passed to the conversion URL, as the page was only
    // pre-rendered.
    base::RunLoop run_loop;
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
    run_loop.Run();
    EXPECT_FALSE(expected_report.HasRequest());
  }
}

ATTRIBUTION_PRERENDER_BROWSER_TEST(ConversionsRegisteredOnActivatedPrerender) {
  const char* kTestCases[] = {
      "createAttributionSrcImg($1);",
      "createTrackingPixel($1);",
      R"(fetch($1, {keepalive: true}))",
  };

  ASSERT_TRUE(https_server()->Start());

  for (const char* registration_js : kTestCases) {
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    // Navigate to a starting same origin page with the conversion url.
    const GURL kEmptyUrl = https_server()->GetURL("d.test", "/empty.html");
    {
      auto url_loader_interceptor =
          content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
              kBaseDataDir, kEmptyUrl.DeprecatedGetOriginAsURL());
      EXPECT_TRUE(NavigateToURL(web_contents(), kEmptyUrl));
    }

    // Pre-render the conversion url.
    const GURL kConversionUrl = https_server()->GetURL(
        "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
    FrameTreeNodeId host_id = prerender_helper_.AddPrerender(kConversionUrl);
    content::test::PrerenderHostObserver host_observer(*web_contents(),
                                                       host_id);

    prerender_helper_.WaitForPrerenderLoadCompletion(kConversionUrl);
    content::RenderFrameHost* prerender_rfh =
        prerender_helper_.GetPrerenderedMainFrameHost(host_id);

    const GURL register_trigger_url = https_server()->GetURL(
        "a.test", "/attribution_reporting/register_trigger_headers.html");
    EXPECT_TRUE(ExecJs(prerender_rfh,
                       JsReplace(registration_js, register_trigger_url)));

    MockAttributionObserver observer;
    base::ScopedObservation<AttributionManager, AttributionObserver>
        observation(&observer);
    observation.Observe(attribution_manager());
    base::RunLoop loop;
    EXPECT_CALL(observer, OnTriggerHandled).WillOnce([&]() { loop.Quit(); });

    // Navigate to pre-rendered page, bringing it to the foreground.
    prerender_helper_.NavigatePrimaryPage(kConversionUrl);

    ASSERT_EQ(kConversionUrl, web_contents()->GetLastCommittedURL());
    ASSERT_TRUE(host_observer.was_activated());

    loop.Run();

    VerifyEventLevelResultUkmEntries(
        ukm_recorder,
        {UkmEntry(
            kConversionUrl,
            static_cast<int64_t>(EventLevelResult::kNoMatchingImpressions))});
  }
}

ATTRIBUTION_PRERENDER_BROWSER_TEST(NoConversionsInSubframeOnPrerender) {
  const char* kTestCases[] = {
      "createAttributionSrcImg($1);",
      "createTrackingPixel($1);",
      R"(fetch($1, {keepalive: true}))",
  };

  ASSERT_TRUE(https_server()->Start());

  for (const char* registration_js : kTestCases) {
    // Navigate to a starting same origin page with the conversion url.
    const GURL kEmptyUrl = https_server()->GetURL("d.test", "/empty.html");
    {
      auto url_loader_interceptor =
          content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
              kBaseDataDir, kEmptyUrl.DeprecatedGetOriginAsURL());
      EXPECT_TRUE(NavigateToURL(web_contents(), kEmptyUrl));
    }

    MockAttributionObserver observer;
    base::ScopedObservation<AttributionManager, AttributionObserver>
        observation(&observer);
    observation.Observe(attribution_manager());
    EXPECT_CALL(observer, OnTriggerHandled).Times(0);

    // Pre-render the conversion url.
    const GURL kConversionUrl = https_server()->GetURL(
        "d.test",
        "/attribution_reporting/page_with_conversion_redirect_in_iframe.html");
    FrameTreeNodeId host_id = prerender_helper_.AddPrerender(kConversionUrl);
    content::test::PrerenderHostObserver host_observer(*web_contents(),
                                                       host_id);

    prerender_helper_.WaitForPrerenderLoadCompletion(kConversionUrl);
    content::RenderFrameHost* prerender_rfh =
        prerender_helper_.GetPrerenderedMainFrameHost(host_id);

    RenderFrameHost* subframe = ChildFrameAt(prerender_rfh, 0);

    const GURL register_trigger_url = https_server()->GetURL(
        "a.test", "/attribution_reporting/register_trigger_headers.html");
    EXPECT_TRUE(
        ExecJs(subframe, JsReplace(registration_js, register_trigger_url)));

    base::RunLoop run_loop;
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
    run_loop.Run();
  }
}

ATTRIBUTION_PRERENDER_BROWSER_TEST(
    ConversionsRegisteredInSubframeActivatedPrerender) {
  const char* kTestCases[] = {
      "createAttributionSrcImg($1);",
      "createTrackingPixel($1);",
      R"(fetch($1, {keepalive: true}))",
  };

  ASSERT_TRUE(https_server()->Start());

  for (const char* registration_js : kTestCases) {
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    // Navigate to a starting same origin page with the conversion url.
    const GURL kEmptyUrl = https_server()->GetURL("d.test", "/empty.html");
    {
      auto url_loader_interceptor =
          content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
              kBaseDataDir, kEmptyUrl.DeprecatedGetOriginAsURL());
      EXPECT_TRUE(NavigateToURL(web_contents(), kEmptyUrl));
    }

    // Pre-render the conversion url.
    const GURL kConversionUrl = https_server()->GetURL(
        "d.test",
        "/attribution_reporting/page_with_conversion_redirect_in_iframe.html");
    FrameTreeNodeId host_id = prerender_helper_.AddPrerender(kConversionUrl);
    content::test::PrerenderHostObserver host_observer(*web_contents(),
                                                       host_id);

    prerender_helper_.WaitForPrerenderLoadCompletion(kConversionUrl);
    content::RenderFrameHost* prerender_rfh =
        prerender_helper_.GetPrerenderedMainFrameHost(host_id);

    RenderFrameHost* subframe = ChildFrameAt(prerender_rfh, 0);

    const GURL register_trigger_url = https_server()->GetURL(
        "a.test", "/attribution_reporting/register_trigger_headers.html");
    EXPECT_TRUE(
        ExecJs(subframe, JsReplace(registration_js, register_trigger_url)));

    MockAttributionObserver observer;
    base::ScopedObservation<AttributionManager, AttributionObserver>
        observation(&observer);
    observation.Observe(attribution_manager());
    base::RunLoop loop;
    EXPECT_CALL(observer, OnTriggerHandled).WillOnce([&]() { loop.Quit(); });

    // Navigate to pre-rendered page, bringing it to the foreground.
    prerender_helper_.NavigatePrimaryPage(kConversionUrl);

    ASSERT_EQ(kConversionUrl, web_contents()->GetLastCommittedURL());
    ASSERT_TRUE(host_observer.was_activated());

    loop.Run();

    VerifyEventLevelResultUkmEntries(
        ukm_recorder,
        {UkmEntry(
            kConversionUrl,
            static_cast<int64_t>(EventLevelResult::kNoMatchingImpressions))});
  }
}

ATTRIBUTION_PRERENDER_BROWSER_TEST(
    NoConversionsAfterSubframeNavigationActivatedPrerenderer) {
  const char* kTestCases[] = {
      "createAttributionSrcImg($1);",
      "createTrackingPixel($1);",
      R"(fetch($1, {keepalive: true}))",
  };

  ASSERT_TRUE(https_server()->Start());

  for (const char* registration_js : kTestCases) {
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    // Navigate to a starting same origin page with the conversion url.
    const GURL kEmptyUrl = https_server()->GetURL("d.test", "/empty.html");
    {
      auto url_loader_interceptor =
          content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
              kBaseDataDir, kEmptyUrl.DeprecatedGetOriginAsURL());
      EXPECT_TRUE(NavigateToURL(web_contents(), kEmptyUrl));
    }

    MockAttributionObserver observer;
    base::ScopedObservation<AttributionManager, AttributionObserver>
        observation(&observer);
    observation.Observe(attribution_manager());
    EXPECT_CALL(observer, OnTriggerHandled).Times(0);

    // Pre-render the conversion url.
    const GURL kConversionUrl = https_server()->GetURL(
        "d.test",
        "/attribution_reporting/page_with_conversion_redirect_in_iframe.html");
    FrameTreeNodeId host_id = prerender_helper_.AddPrerender(kConversionUrl);
    content::test::PrerenderHostObserver host_observer(*web_contents(),
                                                       host_id);

    prerender_helper_.WaitForPrerenderLoadCompletion(kConversionUrl);
    content::RenderFrameHost* prerender_rfh =
        prerender_helper_.GetPrerenderedMainFrameHost(host_id);

    RenderFrameHost* subframe = ChildFrameAt(prerender_rfh, 0);

    const GURL register_trigger_url = https_server()->GetURL(
        "a.test", "/attribution_reporting/register_trigger_headers.html");
    EXPECT_TRUE(
        ExecJs(subframe, JsReplace(registration_js, register_trigger_url)));

    const GURL new_subframe_url = https_server()->GetURL(
        "d.test", "/attribution_reporting/page_with_conversion_redirect.html");

    // Navigate subframe.
    TestNavigationManager subframe_nav_manager(web_contents(),
                                               new_subframe_url);
    ASSERT_TRUE(
        ExecJs(prerender_rfh,
               JsReplace("document.getElementById('test_iframe').src = $1",
                         new_subframe_url)));
    ASSERT_TRUE(subframe_nav_manager.WaitForNavigationFinished());

    //  Navigate to pre-rendered page, bringing it to the foreground.
    prerender_helper_.NavigatePrimaryPage(kConversionUrl);

    ASSERT_EQ(kConversionUrl, web_contents()->GetLastCommittedURL());
    ASSERT_TRUE(host_observer.was_activated());

    base::RunLoop run_loop;
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
    run_loop.Run();
  }
}

class AttributionsCrossAppWebEnabledBrowserTest
    : public AttributionsBrowserTest {};

INSTANTIATE_TEST_SUITE_P(All,
                         AttributionsCrossAppWebEnabledBrowserTest,
                         ::testing::Bool());

IN_PROC_BROWSER_TEST_P(AttributionsCrossAppWebEnabledBrowserTest,
                       AttributionEligibleNavigation_SetsSupportHeader) {
  auto register_response1 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");
  auto register_response2 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect2");
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL register_source_url =
      https_server()->GetURL("d.test", "/register_source_redirect");

  // Don't use `CreateAndClickSource()` as we need to observe navigation
  // redirects prior to the navigation finishing.
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: '',
                        target: $2});)",
                                               register_source_url, "_top")));
  EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  // Verify the navigation redirects contain the support header.
  register_response1->WaitForRequest();
  ExpectValidAttributionReportingSupportHeader(
      register_response1->http_request()->headers.at(
          "Attribution-Reporting-Support"),
      /*web_expected=*/true,
      /*os_expected=*/false);

  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader("Location", "/register_source_redirect2");
  register_response1->Send(http_response->ToResponseString());
  register_response1->Done();

  // Ensure that redirect requests also contain the header.
  register_response2->WaitForRequest();
  ExpectValidAttributionReportingSupportHeader(
      register_response2->http_request()->headers.at(
          "Attribution-Reporting-Support"),
      /*web_expected=*/true,
      /*os_expected=*/false);
}

IN_PROC_BROWSER_TEST_P(
    AttributionsCrossAppWebEnabledBrowserTest,
    AttributionEligibleNavigationOsLevelEnabled_SetsSupportHeader) {
  auto register_response1 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");
  auto register_response2 =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect2");
  ASSERT_TRUE(https_server()->Start());

  AttributionOsLevelManager::ScopedApiStateForTesting scoped_api_state_setting(
      AttributionOsLevelManager::ApiState::kEnabled);

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL register_source_url =
      https_server()->GetURL("d.test", "/register_source_redirect");

  // Don't use `CreateAndClickSource()` as we need to observe navigation
  // redirects prior to the navigation finishing.
  EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: '',
                        target: $2});)",
                                               register_source_url, "_top")));
  EXPECT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  // Verify the navigation redirects contain the support header.
  register_response1->WaitForRequest();
  ExpectValidAttributionReportingSupportHeader(
      register_response1->http_request()->headers.at(
          "Attribution-Reporting-Support"),
      /*web_expected=*/true,
      /*os_expected=*/true);

  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader("Location", "/register_source_redirect2");
  register_response1->Send(http_response->ToResponseString());
  register_response1->Done();

  // Ensure that redirect requests also contain the header.
  register_response2->WaitForRequest();
  ExpectValidAttributionReportingSupportHeader(
      register_response2->http_request()->headers.at(
          "Attribution-Reporting-Support"),
      /*web_expected=*/true,
      /*os_expected=*/true);
}

class AttributionsFencedFrameBrowserTest : public AttributionsBrowserTest {
 public:
  AttributionsFencedFrameBrowserTest()
      : AttributionsBrowserTest(
            /*enabled_features=*/{blink::features::kFencedFrames,
                                  features::kPrivacySandboxAdsAPIsOverride,
                                  blink::features::kFencedFramesAPIChanges,
                                  blink::features::kFencedFramesDefaultMode}) {}

  FrameTreeNode* AddFencedFrame(
      FrameTreeNode* root,
      const GURL& fenced_frame_url,
      scoped_refptr<FencedFrameReporter> fenced_frame_reporter) {
    static constexpr char kAddFencedFrameScript[] = R"({
        var f = document.createElement('fencedframe');
        document.body.appendChild(f);
    })";
    EXPECT_TRUE(ExecJs(root, kAddFencedFrameScript));

    EXPECT_EQ(1U, root->child_count());
    FrameTreeNode* fenced_frame_root_node =
        GetFencedFrameRootNode(root->child_at(0));
    EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
    EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());

    // Get the urn mapping object.
    FencedFrameURLMapping& url_mapping =
        root->current_frame_host()->GetPage().fenced_frame_urls_map();

    // Add url and its reporting metadata to fenced frame url mapping.
    auto urn_uuid = AddAndVerifyFencedFrameURL(
        &url_mapping, fenced_frame_url, std::move(fenced_frame_reporter));

    TestFrameNavigationObserver observer(
        fenced_frame_root_node->current_frame_host());

    // Navigate the fenced frame.
    EXPECT_TRUE(ExecJs(
        root, JsReplace("f.config = new FencedFrameConfig($1);", urn_uuid)));

    observer.WaitForCommit();

    return fenced_frame_root_node;
  }

  scoped_refptr<FencedFrameReporter> CreateFencedFrameReporter() {
    return FencedFrameReporter::CreateForFledge(
        web_contents()
            ->GetPrimaryMainFrame()
            ->GetStoragePartition()
            ->GetURLLoaderFactoryForBrowserProcess(),
        web_contents()->GetBrowserContext(),
        /*direct_seller_is_seller=*/false,
        PrivateAggregationManager::GetManager(
            *web_contents()->GetBrowserContext()),
        /*main_frame_origin=*/
        web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
        /*winner_origin=*/url::Origin::Create(GURL("https://a.test")),
        /*winner_aggregation_coordinator_origin=*/std::nullopt);
  }

 private:
  GURL AddAndVerifyFencedFrameURL(
      FencedFrameURLMapping* fenced_frame_url_mapping,
      const GURL& https_url,
      scoped_refptr<FencedFrameReporter> fenced_frame_reporter) {
    std::optional<GURL> urn_uuid =
        fenced_frame_url_mapping->AddFencedFrameURLForTesting(
            https_url, std::move(fenced_frame_reporter));
    EXPECT_TRUE(urn_uuid.has_value());
    EXPECT_TRUE(urn_uuid->is_valid());
    return urn_uuid.value();
  }

  base::test::ScopedFeatureList scoped_feature_list_;
};

INSTANTIATE_TEST_SUITE_P(All,
                         AttributionsFencedFrameBrowserTest,
                         ::testing::Bool());

IN_PROC_BROWSER_TEST_P(AttributionsFencedFrameBrowserTest,
                       ReportEvent_ReportSent) {
  // Expected reports must be registered before the server starts.
  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://a.test",
      /*source_event_id=*/"5", /*source_type=*/"event",
      /*trigger_data=*/"1", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL main_url =
      https_server()->GetURL("a.test", "/page_with_impression_creator.html");
  ASSERT_TRUE(NavigateToURL(shell(), main_url));
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();
  GURL fenced_frame_url(
      https_server()->GetURL("a.test", "/page_with_impression_creator.html"));

  GURL reporting_url = https_server()->GetURL(
      "a.test", "/register_source_headers_trigger_same_origin.html");

  GURL buyer_url = https_server()->GetURL("c.test", "/");

  scoped_refptr<FencedFrameReporter> fenced_frame_reporter =
      CreateFencedFrameReporter();
  // Set valid reporting metadata for buyer.
  fenced_frame_reporter->OnUrlMappingReady(
      blink::FencedFrame::ReportingDestination::kBuyer,
      url::Origin::Create(GURL(buyer_url)), {{"click", reporting_url}});

  FrameTreeNode* fenced_frame_root_node =
      AddFencedFrame(root, fenced_frame_url, std::move(fenced_frame_reporter));

  MockAttributionObserver observer;
  base::ScopedObservation<AttributionManager, AttributionObserver> observation(
      &observer);
  observation.Observe(attribution_manager());

  base::RunLoop loop;
  EXPECT_CALL(observer,
              OnSourceHandled(_, _, _, StorableSource::Result::kSuccess))
      .WillOnce([&]() { loop.Quit(); });

  ASSERT_TRUE(ExecJs(fenced_frame_root_node, R"(
        window.fence.reportEvent({
          eventType: 'click',
          eventData: 'this is a click',
          destination: ['buyer'],
        });
      )"));
  loop.Run();

  ASSERT_TRUE(ExecJs(root, JsReplace("createAttributionSrcImg($1);",
                                     https_server()->GetURL(
                                         "a.test",
                                         "/attribution_reporting/"
                                         "register_trigger_headers.html"))));

  expected_report.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(AttributionsFencedFrameBrowserTest,
                       ReportEventRedirect_BothReportsSent) {
  MockAttributionObserver attribution_manager_observer;
  base::ScopedObservation<AttributionManager, AttributionObserver> observation(
      &attribution_manager_observer);
  observation.Observe(attribution_manager());

  base::RunLoop loop;

  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source_redirect");

  // Expected reports must be registered before the server starts.
  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://a.test",
      /*source_event_id=*/"1", /*source_type=*/"event",
      /*trigger_data=*/"1", https_server());

  ExpectedReportWaiter expected_report2(
      GURL("https://b.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://a.test",
      /*source_event_id=*/"5", /*source_type=*/"event",
      /*trigger_data=*/"1", https_server());

  ASSERT_TRUE(https_server()->Start());

  GURL main_url =
      https_server()->GetURL("a.test", "/page_with_impression_creator.html");
  ASSERT_TRUE(NavigateToURL(shell(), main_url));
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();

  GURL fenced_frame_url(
      https_server()->GetURL("a.test", "/page_with_impression_creator.html"));

  GURL reporting_url =
      https_server()->GetURL("a.test", "/register_source_redirect");

  GURL buyer_url = https_server()->GetURL("c.test", "/");

  scoped_refptr<FencedFrameReporter> fenced_frame_reporter =
      CreateFencedFrameReporter();
  // Set valid reporting metadata for buyer.
  fenced_frame_reporter->OnUrlMappingReady(
      blink::FencedFrame::ReportingDestination::kBuyer,
      url::Origin::Create(buyer_url), {{"click", reporting_url}});

  FrameTreeNode* fenced_frame_root_node =
      AddFencedFrame(root, fenced_frame_url, std::move(fenced_frame_reporter));

  // Perform the reportEvent call, with a unique body. "this is a click");
  ASSERT_TRUE(ExecJs(fenced_frame_root_node, R"(
        window.fence.reportEvent({
          eventType: 'click',
          eventData: 'this is a click',
          destination: ['buyer'],
        });
      )"));

  register_response->WaitForRequest();
  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader(
      kAttributionReportingRegisterSourceHeader,
      R"({"source_event_id":"1","destination":"https://a.test"})");

  http_response->AddCustomHeader(
      "Location",
      https_server()
          ->GetURL("b.test",
                   "/register_source_headers_trigger_same_origin.html")
          .spec());
  http_response->AddCustomHeader("Access-Control-Allow-Origin", "*");
  register_response->Send(http_response->ToResponseString());
  register_response->Done();

  // We wait for the 2 sources to be processed before registering triggers.
  EXPECT_CALL(attribution_manager_observer,
              OnSourceHandled(_, _, _, StorableSource::Result::kSuccess))
      .WillOnce([]() {})
      .WillOnce([&loop]() { loop.Quit(); });
  loop.Run();

  GURL register_trigger_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/register_trigger_headers.html");
  ASSERT_TRUE(ExecJs(
      root, JsReplace("createAttributionSrcImg($1);", register_trigger_url)));
  expected_report.WaitForReport();

  GURL register_trigger_url2 = https_server()->GetURL(
      "b.test", "/attribution_reporting/register_trigger_headers.html");
  ASSERT_TRUE(ExecJs(
      root, JsReplace("createAttributionSrcImg($1);", register_trigger_url2)));
  expected_report2.WaitForReport();
}

IN_PROC_BROWSER_TEST_P(AttributionsFencedFrameBrowserTest,
                       AutomaticBeacon_ReportSent) {
  // Expected reports must be registered before the server starts.
  ExpectedReportWaiter expected_report(
      GURL("https://a.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://a.test",
      /*source_event_id=*/"5", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL main_url =
      https_server()->GetURL("a.test", "/page_with_impression_creator.html");
  ASSERT_TRUE(NavigateToURL(shell(), main_url));
  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                            ->GetPrimaryFrameTree()
                            .root();
  TestFrameNavigationObserver root_observer(root);
  GURL fenced_frame_url(
      https_server()->GetURL("a.test", "/page_with_impression_creator.html"));

  GURL reporting_url = https_server()->GetURL(
      "a.test", "/register_source_headers_trigger_same_origin.html");

  GURL buyer_url = https_server()->GetURL("c.test", "/");

  scoped_refptr<FencedFrameReporter> fenced_frame_reporter =
      CreateFencedFrameReporter();
  // Set valid reporting metadata for buyer.
  fenced_frame_reporter->OnUrlMappingReady(
      blink::FencedFrame::ReportingDestination::kBuyer,
      url::Origin::Create(GURL(buyer_url)),
      {{blink::kDeprecatedFencedFrameTopNavigationBeaconType, reporting_url}});

  FrameTreeNode* fenced_frame_root_node =
      AddFencedFrame(root, fenced_frame_url, std::move(fenced_frame_reporter));

  ASSERT_TRUE(
      ExecJs(fenced_frame_root_node,
             JsReplace(R"(
    window.fence.setReportEventDataForAutomaticBeacons({
      eventType: $1,
      eventData: 'This is the event data!',
      destination: ['buyer']
    });
    )",
                       blink::kDeprecatedFencedFrameTopNavigationBeaconType)));

  GURL navigation_url(
      https_server()->GetURL("a.test", "/page_with_impression_creator.html"));

  ASSERT_TRUE(
      ExecJs(fenced_frame_root_node,
             JsReplace("window.open($1, '_unfencedTop');", navigation_url)));

  // The page must fully load before it can do anything involving attribution
  // reporting.
  root_observer.Wait();

  ASSERT_TRUE(ExecJs(root, JsReplace("createAttributionSrcImg($1);",
                                     https_server()->GetURL(
                                         "a.test",
                                         "/attribution_reporting/"
                                         "register_trigger_headers.html"))));

  expected_report.WaitForReport();
}

class AttributionsBrowserTestWithKeepAliveMigration
    : public AttributionsBrowserTestBase {
 public:
  AttributionsBrowserTestWithKeepAliveMigration() {
    scoped_feature_list_.InitWithFeatures(
        {blink::features::kKeepAliveInBrowserMigration}, {});
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Regression test for crbug.com/1374121.
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTestWithKeepAliveMigration,
                       SourceRegisteredAfterNavigation) {
  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server(), "/register_source");

  ExpectedReportWaiter expected_report(
      GURL("https://d.test/.well-known/attribution-reporting/"
           "report-event-attribution"),
      /*attribution_destination=*/"https://d.test",
      /*source_event_id=*/"1", /*source_type=*/"navigation",
      /*trigger_data=*/"7", https_server());
  ASSERT_TRUE(https_server()->Start());

  GURL impression_url = https_server()->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");
  ASSERT_TRUE(NavigateToURL(web_contents(), impression_url));

  GURL register_source_url =
      https_server()->GetURL("d.test", "/register_source");

  GURL conversion_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");

  ASSERT_TRUE(
      ExecJs(web_contents(), JsReplace(R"(
    createAttributionSrcAnchor({id: 'link',
                        url: $1,
                        attributionsrc: $2,
                        target: '_top'});)",
                                       conversion_url, register_source_url)));

  TestNavigationObserver observer(web_contents());
  ASSERT_TRUE(ExecJs(web_contents(), "simulateClick('link');"));

  // Wait for navigation to complete before registering the source.
  observer.Wait();

  register_response->WaitForRequest();
  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
  http_response->AddCustomHeader(
      "Attribution-Reporting-Register-Source",
      R"({"source_event_id":"1","destination":"https://d.test"})");
  register_response->Send(http_response->ToResponseString());
  register_response->Done();

  GURL register_trigger_url = https_server()->GetURL(
      "d.test", "/attribution_reporting/register_trigger_headers.html");
  ASSERT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
                                               register_trigger_url)));

  expected_report.WaitForReport();
}

void TestServiceWorker(const char* registration_js,
                       WebContents* web_contents,
                       ServiceWorkerContextWrapper* sw_wrapper,
                       net::EmbeddedTestServer* https_server) {
  auto register_response =
      std::make_unique<net::test_server::ControllableHttpResponse>(
          https_server, "/attribution_reporting/register_source");
  ASSERT_TRUE(https_server->Start());

  GURL page_url = https_server->GetURL(
      "a.test", "/attribution_reporting/page_with_impression_creator.html");

  // Setup our service worker.
  WorkerStateObserver sw_observer(sw_wrapper, ServiceWorkerVersion::ACTIVATED);
  blink::mojom::ServiceWorkerRegistrationOptions options(
      page_url, blink::mojom::ScriptType::kClassic,
      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
  const blink::StorageKey key =
      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
  sw_wrapper->RegisterServiceWorker(
      https_server->GetURL("a.test",
                           "/attribution_reporting/service_worker.js"),
      key, options,
      base::BindOnce(&ExpectRegisterResultAndRun,
                     blink::ServiceWorkerStatusCode::kOk, base::DoNothing()));
  sw_observer.Wait();

  EXPECT_TRUE(NavigateToURL(web_contents, page_url));

  EXPECT_TRUE(ExecJs(
      web_contents,
      JsReplace(registration_js,
                https_server->GetURL(
                    "a.test", "/attribution_reporting/register_source"))));

  register_response->WaitForRequest();
  EXPECT_TRUE(base::Contains(register_response->http_request()->headers,
                             "Attribution-Reporting-Eligible"));
  EXPECT_TRUE(base::Contains(register_response->http_request()->headers,
                             "Attribution-Reporting-Support"));
}

IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionSrcRegistration_SupportHeaderSet_createAttributionEligibleImgSrc) {
  TestServiceWorker("createAttributionEligibleImgSrc($1);", web_contents(),
                    wrapper(), https_server());
}
IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionSrcRegistration_SupportHeaderSet_createAttributionSrcScript) {
  TestServiceWorker("createAttributionSrcScript($1);", web_contents(),
                    wrapper(), https_server());
}
IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionSrcRegistration_SupportHeaderSet_doAttributionEligibleFetch) {
  TestServiceWorker("doAttributionEligibleFetch($1);", web_contents(),
                    wrapper(), https_server());
}

IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionSrcRegistration_SupportHeaderSet_doAttributionEligibleXHR) {
  TestServiceWorker("doAttributionEligibleXHR($1);", web_contents(), wrapper(),
                    https_server());
}

IN_PROC_BROWSER_TEST_P(
    AttributionsBrowserTest,
    ServiceWorkerPerformsAttributionSrcRegistration_SupportHeaderSet_createAttributionEligibleScriptSrc) {
  TestServiceWorker("createAttributionEligibleScriptSrc($1);", web_contents(),
                    wrapper(), https_server());
}

}  // namespace content