blob: 94d98efc69a831165aa2f0b4822e80f07bd9c2fa [file] [log] [blame]
// Copyright 2020 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 <memory>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/values_test_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "content/browser/attribution_reporting/attribution_manager.h"
#include "content/browser/attribution_reporting/attribution_manager_impl.h"
#include "content/browser/attribution_reporting/attribution_test_utils.h"
#include "content/browser/attribution_reporting/storable_source.h"
#include "content/browser/storage_partition_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_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_utils.h"
#include "content/public/test/prerender_test_util.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/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 "url/gurl.h"
namespace content {
namespace {
using ::testing::_;
using ::testing::Return;
constexpr char kBaseDataDir[] = "content/test/data/";
// 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::DictionaryValue(),
server) {
expected_body.SetStringKey("attribution_destination",
std::move(attribution_destination));
expected_body.SetStringKey("source_event_id", std::move(source_event_id));
expected_body.SetStringKey("source_type", std::move(source_type));
expected_body.SetStringKey("trigger_data", std::move(trigger_data));
}
// ControllableHTTPResponses can only wait for relative urls, so only supply
// the path.
ExpectedReportWaiter(GURL report_url,
base::Value 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.path())) {}
GURL expected_url;
base::Value expected_body;
std::string source_debug_key;
std::string trigger_debug_key;
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.host();
GURL::Replacements replace_host;
replace_host.SetHostStr(host);
base::Value body = base::test::ParseJson(request.content);
EXPECT_THAT(body, base::test::DictionaryHasValues(expected_body));
const base::Value::Dict& body_dict = body.GetDict();
// The report ID is random, so just test that the field exists here and is a
// valid GUID.
const std::string* report_id = body_dict.FindString("report_id");
ASSERT_TRUE(report_id);
EXPECT_TRUE(base::GUID::ParseLowercase(*report_id).is_valid());
EXPECT_TRUE(body_dict.FindDouble("randomized_trigger_rate"));
if (source_debug_key.empty()) {
EXPECT_FALSE(body_dict.FindString("source_debug_key"));
} else {
base::ExpectDictStringValue(source_debug_key, body_dict,
"source_debug_key");
}
if (trigger_debug_key.empty()) {
EXPECT_FALSE(body_dict.FindString("trigger_debug_key"));
} else {
base::ExpectDictStringValue(trigger_debug_key, body_dict,
"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 AttributionsBrowserTest : public ContentBrowserTest {
public:
AttributionsBrowserTest() { AttributionManagerImpl::RunInMemoryForTesting(); }
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kConversionsDebugMode);
// Sets up the blink runtime feature for ConversionMeasurement.
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
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");
}
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;
}
private:
std::unique_ptr<net::EmbeddedTestServer> https_server_;
std::unique_ptr<network::TestNetworkConnectionTracker>
network_connection_tracker_;
};
// Verifies that storage initialization does not hang when initialized in a
// browsertest context, see https://crbug.com/1080764).
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
FeatureEnabled_StorageInitWithoutHang) {}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
ImpressionConversion_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));
// Create an anchor tag with impression attributes and click the link. By
// default the target is set to "_top".
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");
CreateAndClickSource(web_contents(), conversion_url,
register_source_url.spec());
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_F(AttributionsBrowserTest,
ImpressionNavigationRedirect_ReportSent) {
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://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());
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(
"Attribution-Reporting-Register-Source",
R"({"source_event_id":"1","destination":"https://c.test"})");
http_response->AddCustomHeader(
"Location",
https_server()
->GetURL("c.test",
"/attribution_reporting/page_with_conversion_redirect.html")
.spec());
register_response->Send(http_response->ToResponseString());
register_response->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();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
ImpressionNavigationMultipleRedirects_FirstReportSent) {
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(
"Attribution-Reporting-Register-Source",
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(
"Attribution-Reporting-Register-Source",
R"({"source_event_id":"2","destination":"https://c.test"})");
http_response2->AddCustomHeader(
"Location",
https_server()
->GetURL("c.test",
"/attribution_reporting/page_with_conversion_redirect.html")
.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();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
ImpressionNavigationRedirectWindowOpen_ReportSent) {
// 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://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");
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",
"/attribution_reporting/register_source_navigation_redirect.html");
TestNavigationObserver observer(web_contents());
EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(window.open($1, '_top',
"attributionsrc="+$2);)",
register_source_url, "")));
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();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
AttributionEligibleNavigation_SetsEligibleHeader) {
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 eligibility header.
register_response1->WaitForRequest();
EXPECT_EQ(register_response1->http_request()->headers.at(
"Attribution-Reporting-Eligible"),
"navigation-source");
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();
ASSERT_EQ(register_response2->http_request()->headers.at(
"Attribution-Reporting-Eligible"),
"navigation-source");
}
IN_PROC_BROWSER_TEST_F(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"));
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_F(AttributionsBrowserTest,
WindowOpenDeprecatedAPI_NoException) {
// Expected reports must be registered before the server starts.
ExpectedReportWaiter expected_report(
GURL("https://a.test/.well-known/attribution-reporting/"
"report-event-attribution"),
/*body=*/base::Value(), 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 conversion_url = https_server()->GetURL(
"b.test", "/attribution_reporting/page_with_conversion_redirect.html");
TestNavigationObserver observer(web_contents());
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace(R"(window.open($1, '_top', '',
{attributionSourceEventId: '1', attributeOn: $2});)",
conversion_url, url::Origin::Create(conversion_url))));
observer.Wait();
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)));
base::RunLoop run_loop;
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
run_loop.Run();
EXPECT_FALSE(expected_report.HasRequest());
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
WindowOpenImpressionConversion_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 register_url = https_server()->GetURL(
"a.test", "/attribution_reporting/register_source_headers.html");
GURL conversion_url = https_server()->GetURL(
"d.test", "/attribution_reporting/page_with_conversion_redirect.html");
TestNavigationObserver observer(web_contents());
EXPECT_TRUE(ExecJs(web_contents(), JsReplace(R"(window.open($1, '_top',
"attributionsrc="+$2);)",
conversion_url, register_url)));
observer.Wait();
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_F(AttributionsBrowserTest,
ImpressionFromCrossOriginSubframe_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 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();
}
IN_PROC_BROWSER_TEST_F(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_F(AttributionsBrowserTest,
ImpressionConversionSameDomain_ReportSent) {
// 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://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(
"d.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(
"d.test", "/attribution_reporting/register_source_headers.html");
CreateAndClickSource(web_contents(), conversion_url,
register_source_url.spec());
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();
}
IN_PROC_BROWSER_TEST_F(
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_F(
AttributionsBrowserTest,
MultipleImpressionsPerConversion_ReportSentWithAttribution) {
ExpectedReportWaiter expected_report(
GURL("https://b.test/.well-known/attribution-reporting/"
"report-event-attribution"),
/*attribution_destination=*/"https://d.test",
/*source_event_id=*/"2", /*source_type=*/"navigation",
/*trigger_data=*/"7", https_server());
ASSERT_TRUE(https_server()->Start());
GURL first_impression_url = https_server()->GetURL(
"a.test", "/attribution_reporting/page_with_impression_creator.html");
EXPECT_TRUE(NavigateToURL(web_contents(), first_impression_url));
GURL second_impression_url = https_server()->GetURL(
"c.test", "/attribution_reporting/page_with_impression_creator.html");
Shell* shell2 =
Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
GURL(), nullptr, gfx::Size(100, 100));
EXPECT_TRUE(NavigateToURL(shell2->web_contents(), second_impression_url));
GURL conversion_url = https_server()->GetURL(
"d.test", "/attribution_reporting/page_with_conversion_redirect.html");
GURL register_source_url = https_server()->GetURL(
"b.test", "/attribution_reporting/register_source_headers.html");
CreateAndClickSource(shell()->web_contents(), conversion_url,
register_source_url.spec());
GURL register_source_url_2 = https_server()->GetURL(
"b.test", "/attribution_reporting/register_source_headers_2.html");
CreateAndClickSource(shell2->web_contents(), conversion_url,
register_source_url_2.spec());
GURL register_trigger_url = https_server()->GetURL(
"b.test", "/attribution_reporting/register_trigger_headers.html");
EXPECT_TRUE(ExecJs(
shell2, JsReplace("createAttributionSrcImg($1);", register_trigger_url)));
expected_report.WaitForReport();
}
IN_PROC_BROWSER_TEST_F(
AttributionsBrowserTest,
MultipleImpressionsPerConversion_ReportSentWithHighestPriority) {
// Report will be sent for the impression with highest priority.
ExpectedReportWaiter expected_report(
GURL("https://b.test/.well-known/attribution-reporting/"
"report-event-attribution"),
/*attribution_destination=*/"https://d.test",
/*source_event_id=*/"3", /*source_type=*/"navigation",
/*trigger_data=*/"7", https_server());
ASSERT_TRUE(https_server()->Start());
GURL first_impression_url = https_server()->GetURL(
"a.test", "/attribution_reporting/page_with_impression_creator.html");
EXPECT_TRUE(NavigateToURL(web_contents(), first_impression_url));
GURL second_impression_url = https_server()->GetURL(
"c.test", "/attribution_reporting/page_with_impression_creator.html");
Shell* shell2 =
Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
GURL(), nullptr, gfx::Size(100, 100));
EXPECT_TRUE(NavigateToURL(shell2->web_contents(), second_impression_url));
// Register impressions from both windows.
GURL conversion_url = https_server()->GetURL(
"d.test", "/attribution_reporting/page_with_conversion_redirect.html");
GURL register_source_url = https_server()->GetURL(
"b.test",
"/attribution_reporting/register_source_headers_high_priority.html");
CreateAndClickSource(shell()->web_contents(), conversion_url,
register_source_url.spec());
GURL register_source_url_2 = https_server()->GetURL(
"b.test", "/attribution_reporting/register_source_headers.html");
CreateAndClickSource(shell2->web_contents(), conversion_url,
register_source_url_2.spec());
GURL register_trigger_url = https_server()->GetURL(
"b.test", "/attribution_reporting/register_trigger_headers.html");
EXPECT_TRUE(ExecJs(
shell2, JsReplace("createAttributionSrcImg($1);", register_trigger_url)));
expected_report.WaitForReport();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
EventSourceWithDebugKeyConversion_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://b.test",
/*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
https_server());
expected_report.source_debug_key = "789";
ASSERT_TRUE(https_server()->Start());
EXPECT_TRUE(NavigateToURL(
web_contents(),
https_server()->GetURL(
"a.test", "/set-cookie?ar_debug=1;HttpOnly;Secure;SameSite=None")));
GURL impression_url = https_server()->GetURL(
"a.test", "/attribution_reporting/page_with_impression_creator.html");
EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));
RegisterSource(https_server()->GetURL(
"a.test",
"/attribution_reporting/register_source_headers_debug_key.html"));
GURL conversion_url = https_server()->GetURL(
"b.test", "/attribution_reporting/page_with_conversion_redirect.html");
EXPECT_TRUE(NavigateToURL(web_contents(), 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_F(AttributionsBrowserTest,
SourceAndDebugCookieRegisteredInSameResponse) {
// 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://b.test",
/*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
https_server());
expected_report.source_debug_key = "789";
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));
RegisterSource(
https_server()->GetURL("a.test",
"/attribution_reporting/"
"register_source_headers_debug_key_cookie.html"));
GURL conversion_url = https_server()->GetURL(
"b.test", "/attribution_reporting/page_with_conversion_redirect.html");
EXPECT_TRUE(NavigateToURL(web_contents(), 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_F(AttributionsBrowserTest,
AttributionSrcSourceAndTrigger_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=*/"event", /*trigger_data=*/"1",
https_server());
expected_report.trigger_debug_key = "789";
ASSERT_TRUE(https_server()->Start());
EXPECT_TRUE(NavigateToURL(
web_contents(),
https_server()->GetURL(
"a.test", "/set-cookie?ar_debug=1;HttpOnly;Secure;SameSite=None")));
EXPECT_TRUE(NavigateToURL(
web_contents(),
https_server()->GetURL(
"b.test",
"/attribution_reporting/page_with_impression_creator.html")));
RegisterSource(https_server()->GetURL(
"a.test", "/attribution_reporting/register_source_headers.html"));
EXPECT_TRUE(NavigateToURL(
web_contents(),
https_server()->GetURL(
"d.test",
"/attribution_reporting/page_with_impression_creator.html")));
EXPECT_TRUE(ExecJs(
web_contents(),
JsReplace(
"createAttributionSrcImg($1);",
https_server()->GetURL("a.test",
"/attribution_reporting/"
"register_trigger_headers_all_params.html"))));
expected_report.WaitForReport();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
AttributionSrcNavigationSourceAndTrigger_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=*/"1", https_server());
ASSERT_TRUE(https_server()->Start());
EXPECT_TRUE(NavigateToURL(
web_contents(),
https_server()->GetURL(
"b.test",
"/attribution_reporting/page_with_impression_creator.html")));
TestNavigationObserver observer(web_contents());
EXPECT_TRUE(ExecJs(
web_contents(),
JsReplace(R"(createAndClickAttributionSrcAnchor({url: $1,
attributionsrc: $2});)",
https_server()->GetURL(
"d.test",
"/attribution_reporting/page_with_impression_creator.html"),
https_server()->GetURL(
"a.test",
"/attribution_reporting/register_source_headers.html"))));
observer.Wait();
EXPECT_TRUE(ExecJs(
web_contents(),
JsReplace(
"createAttributionSrcImg($1);",
https_server()->GetURL("a.test",
"/attribution_reporting/"
"register_trigger_headers_all_params.html"))));
expected_report.WaitForReport();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
ImpressionConversionWithDedupKey_Deduped) {
// Expected reports must be registered before the server starts.
ExpectedReportWaiter expected_report1(
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=*/"1", https_server());
// 12 below is sanitized to 4 here by `SanitizeTriggerData()`.
ExpectedReportWaiter expected_report2(
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");
CreateAndClickSource(web_contents(), conversion_url,
register_source_url.spec());
GURL register_trigger_with_dedup_url = https_server()->GetURL(
"a.test", "/attribution_reporting/register_trigger_headers_dedup.html");
EXPECT_TRUE(
ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
register_trigger_with_dedup_url)));
expected_report1.WaitForReport();
// This report should be deduped against the previous one.
EXPECT_TRUE(
ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
register_trigger_with_dedup_url)));
// This report should be received, as it has a different dedupKey.
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_report2.WaitForReport();
}
IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
TriggerAndSourceSameRedirectChain_Handled) {
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));
MockAttributionObserver observer;
base::ScopedObservation<AttributionManager, AttributionObserver> observation(
&observer);
observation.Observe(attribution_manager());
base::RunLoop loop;
int count = 0;
EXPECT_CALL(observer, OnTriggerHandled).WillRepeatedly([&]() {
count++;
if (count < 2)
return;
loop.Quit();
});
bool received_source = false;
base::RunLoop source_loop;
EXPECT_CALL(observer, OnSourceHandled).WillOnce([&]() {
received_source = true;
source_loop.Quit();
});
GURL register_url = https_server()->GetURL(
"a.test", "/attribution_reporting/register_trigger_source_trigger.html");
EXPECT_TRUE(
ExecJs(web_contents(),
JsReplace("createAttributionEligibleImgSrc($1);", register_url)));
// Ensure we don't error out processing the redirect chain.
if (count < 2)
loop.Run();
if (!received_source)
source_loop.Run();
}
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_;
};
// TODO(crbug.com/1344264): these tests are flaky on most release bots.
#if defined(NDEBUG)
#define ATTRIBUTION_PRERENDER_BROWSER_TEST(TEST_NAME) \
IN_PROC_BROWSER_TEST_F(AttributionsPrerenderBrowserTest, DISABLED_##TEST_NAME)
#else
#define ATTRIBUTION_PRERENDER_BROWSER_TEST(TEST_NAME) \
IN_PROC_BROWSER_TEST_F(AttributionsPrerenderBrowserTest, TEST_NAME)
#endif
ATTRIBUTION_PRERENDER_BROWSER_TEST(NoConversionsOnPrerender) {
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());
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");
int 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.
GURL register_trigger_url = https_server()->GetURL(
"a.test", "/attribution_reporting/register_trigger_headers.html");
EXPECT_TRUE(ExecJs(prerender_rfh, JsReplace("createAttributionSrcImg($1);",
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::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
run_loop.Run();
EXPECT_FALSE(expected_report.HasRequest());
}
ATTRIBUTION_PRERENDER_BROWSER_TEST(ConversionsRegisteredOnActivatedPrerender) {
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=*/"event", /*trigger_data=*/"1",
https_server());
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");
int 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);
GURL register_trigger_url = https_server()->GetURL(
"a.test", "/attribution_reporting/register_trigger_headers.html");
EXPECT_TRUE(ExecJs(prerender_rfh, JsReplace("createAttributionSrcImg($1);",
register_trigger_url)));
// Navigate to pre-rendered page, bringing it to the fore.
prerender_helper_.NavigatePrimaryPage(kConversionUrl);
ASSERT_EQ(kConversionUrl, web_contents()->GetLastCommittedURL());
// Confirm that reports work as expected, and impressions were retrieved from
// the pre-rendered page, once it became a primary page.
expected_report.WaitForReport();
}
} // namespace content