| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/fenced_frame/fenced_frame_reporter.h" |
| |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "content/browser/attribution_reporting/attribution_manager.h" |
| #include "content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h" |
| #include "content/browser/attribution_reporting/test/mock_attribution_manager.h" |
| #include "content/browser/interest_group/test_interest_group_private_aggregation_manager.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/browser/storage_partition_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom.h" |
| #include "content/test/test_content_browser_client.h" |
| #include "net/base/isolation_info.h" |
| #include "net/base/network_isolation_key.h" |
| #include "net/http/http_request_headers.h" |
| #include "services/network/public/cpp/data_element.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.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/mojom/aggregation_service/aggregatable_report.mojom.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| namespace { |
| |
| using ::testing::_; |
| |
| using FinalizedPrivateAggregationRequests = |
| FencedFrameReporter::FinalizedPrivateAggregationRequests; |
| |
| const auction_worklet::mojom::FinalizedPrivateAggregationRequestPtr |
| kPrivateAggregationRequest = |
| auction_worklet::mojom::FinalizedPrivateAggregationRequest::New( |
| blink::mojom::AggregatableReportHistogramContribution::New( |
| /*bucket=*/1, |
| /*value=*/2, |
| /*filtering_id=*/std::nullopt), |
| blink::mojom::DebugModeDetails::New(), |
| /*error_event=*/std::nullopt); |
| |
| const auction_worklet::mojom::FinalizedPrivateAggregationRequestPtr |
| kPrivateAggregationRequest2 = |
| auction_worklet::mojom::FinalizedPrivateAggregationRequest::New( |
| blink::mojom::AggregatableReportHistogramContribution::New( |
| /*bucket=*/3, |
| /*value=*/4, |
| /*filtering_id=*/1), |
| blink::mojom::DebugModeDetails::New(), |
| /*error_event=*/std::nullopt); |
| |
| // Helper to avoid excess boilerplate. |
| template <typename... Ts> |
| auto ElementsAreRequests(Ts&... requests) { |
| static_assert( |
| std::conjunction<std::is_same< |
| std::remove_const_t<Ts>, |
| auction_worklet::mojom::FinalizedPrivateAggregationRequestPtr>...>:: |
| value); |
| // Need to use `std::ref` as `mojo::StructPtr`s are move-only. |
| return testing::UnorderedElementsAre(testing::Eq(std::ref(requests))...); |
| } |
| |
| class InterestGroupEnabledContentBrowserClient |
| : public TestContentBrowserClient { |
| public: |
| // 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 FencedFrameReporterTest : public RenderViewHostTestHarness { |
| public: |
| FencedFrameReporterTest() { |
| scoped_feature_list_.InitWithFeatures( |
| /*enabled_features=*/ |
| {blink::features::kFencedFramesAutomaticBeaconCredentials, |
| blink::features::kFencedFramesReportEventHeaderChanges}, |
| /*disabled_features=*/{}); |
| } |
| |
| scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory() { |
| return test_url_loader_factory_.GetSafeWeakWrapper(); |
| } |
| |
| AttributionManager* attribution_manager() { |
| return AttributionManager::FromBrowserContext(browser_context()); |
| } |
| |
| void SetUp() override { |
| old_content_browser_client_ = |
| SetBrowserClientForTesting(&test_content_browser_client_); |
| RenderViewHostTestHarness::SetUp(); |
| NavigateAndCommit(main_frame_url_); |
| } |
| |
| void TearDown() override { |
| SetBrowserClientForTesting(old_content_browser_client_); |
| RenderViewHostTestHarness::TearDown(); |
| } |
| |
| void ValidateRequest(const network::ResourceRequest& request, |
| const GURL& expected_url, |
| const std::optional<std::string>& event_data) { |
| EXPECT_EQ(request.url, expected_url); |
| EXPECT_EQ(request.mode, network::mojom::RequestMode::kCors); |
| EXPECT_EQ(request.credentials_mode, network::mojom::CredentialsMode::kOmit); |
| EXPECT_TRUE(request.trusted_params->isolation_info.network_isolation_key() |
| .IsTransient()); |
| EXPECT_EQ(request.referrer, main_frame_origin_.GetURL()); |
| EXPECT_NE(request.referrer, main_frame_url_); |
| EXPECT_EQ( |
| request.referrer_policy, |
| net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN); |
| |
| // Checks specific to DestinationURL events. |
| if (!event_data.has_value()) { |
| EXPECT_EQ(request.method, net::HttpRequestHeaders::kGetMethod); |
| EXPECT_EQ(request.request_initiator, main_frame_origin_); |
| return; |
| } |
| |
| // Checks specific to DestinationEnum + AutomaticBeacon events. |
| EXPECT_EQ(request.request_initiator, report_url_declarer_origin_); |
| EXPECT_EQ(request.method, net::HttpRequestHeaders::kPostMethod); |
| |
| EXPECT_EQ(request.headers.GetHeader(net::HttpRequestHeaders::kContentType), |
| "text/plain;charset=UTF-8"); |
| |
| ASSERT_TRUE(request.request_body); |
| ASSERT_EQ(request.request_body->elements()->size(), 1u); |
| ASSERT_EQ((*request.request_body->elements())[0].type(), |
| network::DataElement::Tag::kBytes); |
| EXPECT_EQ((*request.request_body->elements())[0] |
| .As<network::DataElementBytes>() |
| .AsStringPiece(), |
| *event_data); |
| } |
| |
| protected: |
| RenderFrameHostImpl* main_rfh_impl() { |
| return static_cast<RenderFrameHostImpl*>(main_rfh()); |
| } |
| |
| const base::HistogramTester& histogram_tester() const { |
| return histogram_tester_; |
| } |
| |
| void ShutDownAttributionManager() { |
| auto* partition = static_cast<StoragePartitionImpl*>( |
| browser_context()->GetDefaultStoragePartition()); |
| partition->OverrideAttributionManagerForTesting( |
| /*attribution_manager=*/nullptr); |
| } |
| |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| |
| const GURL main_frame_url_{"https://main_frame.test/mypage.html"}; |
| const GURL report_url_declarer_{"https://report_declarer.test/"}; |
| const GURL report_destination_{"https://report_destination.test"}; |
| const GURL report_destination2_{"https://report_destination2.test"}; |
| const GURL report_destination3_{"https://report_destination3.test"}; |
| const url::Origin main_frame_origin_ = url::Origin::Create(main_frame_url_); |
| const url::Origin report_url_declarer_origin_ = |
| url::Origin::Create(report_url_declarer_); |
| const url::Origin report_destination_origin_ = |
| url::Origin::Create(report_destination_); |
| const url::Origin report_destination2_origin_ = |
| url::Origin::Create(report_destination2_); |
| const url::Origin report_destination3_origin_ = |
| url::Origin::Create(report_destination3_); |
| |
| TestInterestGroupPrivateAggregationManager private_aggregation_manager_{ |
| main_frame_origin_}; |
| |
| InterestGroupEnabledContentBrowserClient test_content_browser_client_; |
| raw_ptr<ContentBrowserClient> old_content_browser_client_; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| // ReportingDestination has no map. |
| TEST_F(FencedFrameReporterTest, NoReportNoMap) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage( |
| shared_url_loader_factory(), browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination_}}); |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| |
| // A Shared Storage FencedFrameReporter has no map for FLEDGE destinations. |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'Buyer'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'Seller'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kDirectSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'ComponentSeller'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'ComponentSeller'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'Buyer'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| // A FLEDGE FencedFrameReporter has no map for Shared Storage. |
| reporter = FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt); |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'SharedStorageSelectUrl'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| // No requests should have been made. |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // ReportingDestination has an empty map. |
| TEST_F(FencedFrameReporterTest, NoReportEmptyMap) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage(shared_url_loader_factory(), |
| browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{}); |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame did not register reporting metadata for destination " |
| "'SharedStorageSelectUrl'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| // No requests should have been made. |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // Non-empty reporting URL map, but passed in event type isn't registered. |
| TEST_F(FencedFrameReporterTest, NoReportEventTypeNotRegistered) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage( |
| shared_url_loader_factory(), browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{"registered_event_type", report_destination_}}); |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("unregistered_event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ( |
| error_message, |
| "This frame did not register reporting url for destination " |
| "'SharedStorageSelectUrl' and event_type 'unregistered_event_type'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kWarning); |
| |
| // No requests should have been made. |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // Event types map to disallowed URLs (empty URLs, non-HTTP/HTTPS URLs). |
| TEST_F(FencedFrameReporterTest, NoReportBadUrl) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage( |
| shared_url_loader_factory(), browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{"no_url", GURL()}, |
| {"data_url", GURL("data:,only http is allowed")}}); |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("no_url", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame registered invalid reporting url for destination " |
| "'SharedStorageSelectUrl' and event_type 'no_url'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationEnumEvent("data_url", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame registered invalid reporting url for destination " |
| "'SharedStorageSelectUrl' and event_type 'data_url'."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| |
| // No requests should have been made. |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| TEST_F(FencedFrameReporterTest, SendReports) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage( |
| shared_url_loader_factory(), browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{"event_type", report_destination_}, |
| {"event_type2", report_destination2_}}); |
| |
| // Make a report. |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, "event_data"); |
| |
| // Make another report to the same URL with different data. Should also |
| // succeed. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data2"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 2); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[1].request, |
| report_destination_, "event_data2"); |
| |
| // Make a report using another event type. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type2", "event_data3"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 3); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[2].request, |
| report_destination2_, "event_data3"); |
| } |
| |
| // Test reports in the FLEDGE case, where reporting URL maps are received before |
| // SendReport() calls. |
| TEST_F(FencedFrameReporterTest, SendFledgeReportsAfterMapsReceived) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/{{report_destination_origin_}}); |
| |
| // Receive all mappings. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination_}}); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination2_}}); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination3_}}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| // Make reports. Each should be sent immediately. |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, "event_data"); |
| |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 2); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[1].request, |
| report_destination2_, "event_data"); |
| |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 3); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[2].request, |
| report_destination3_, "event_data"); |
| |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kDirectSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 4); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[3].request, |
| report_destination2_, "event_data"); |
| |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 5); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[4].request, |
| report_destination_, std::nullopt); |
| } |
| |
| // Test reports in the FLEDGE case, where reporting URL maps are received after |
| // SendReport() calls. |
| TEST_F(FencedFrameReporterTest, SendReportsFledgeBeforeMapsReceived) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/true, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/{{report_destination_origin_}}); |
| |
| // Make reports. They should be queued, since mappings haven't been received |
| // yet. |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kDirectSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| // Each report should be sent as its mapping is received. |
| |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination_}}); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 2); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, "event_data"); |
| // This one is from the "DirectSeller" destination, which was aliased to |
| // kSeller. |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[1].request, |
| report_destination_, "event_data"); |
| |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination2_}}); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 3); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[2].request, |
| report_destination2_, "event_data"); |
| |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination3_}}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 5); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[3].request, |
| report_destination3_, "event_data"); |
| // This one is from the DestinationURLEvent report. |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[4].request, |
| report_destination_, std::nullopt); |
| } |
| |
| // Test reports in the FLEDGE case, where reporting URL maps are received after |
| // SendReport() calls, but no reports are sent because of errors (bad URL, no |
| // URL, missing event types). No error messages are generated in this case |
| // because there's nowhere to pass them |
| TEST_F(FencedFrameReporterTest, SendFledgeReportsBeforeMapsReceivedWithErrors) { |
| auto attribution_data_host_manager = |
| std::make_unique<MockAttributionDataHostManager>(); |
| auto* mock_attribution_data_host_manager = |
| attribution_data_host_manager.get(); |
| |
| auto mock_manager = std::make_unique<MockAttributionManager>(); |
| mock_manager->SetDataHostManager(std::move(attribution_data_host_manager)); |
| static_cast<StoragePartitionImpl*>( |
| browser_context()->GetDefaultStoragePartition()) |
| ->OverrideAttributionManagerForTesting(std::move(mock_manager)); |
| |
| // `AttributionDataHostManager` is notified for the errors. |
| EXPECT_CALL(*mock_attribution_data_host_manager, |
| NotifyFencedFrameReportingBeaconData(_, _, /*headers=*/nullptr, |
| /*is_final_response=*/true)) |
| .Times(3); |
| |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/{{report_destination_origin_}}); |
| |
| // SendReport() is called, and then a mapping is received that doesn't have |
| // the report's event type. No request should be made. |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type2", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination_}}); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| // SendReport() is called, and then a mapping is received that maps the |
| // report's event type to a data URL. No request should be made. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| main_rfh_impl(), error_message, console_message_level)); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{"event_type", GURL("data:,only http is allowed")}}); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| // SendReport() is called, and then a mapping is received with an empty map. |
| // Only the DestinationURLEvent request should be sent. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, std::nullopt); |
| } |
| |
| // Test that both absence of an allowlist and empty allowlist disable custom |
| // destination URL reports. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLNoOrEmptyAllowlist) { |
| static const std::optional<std::vector<url::Origin>> test_cases[] = { |
| std::nullopt, {{}}}; |
| for (const auto& test_case : test_cases) { |
| SCOPED_TRACE(test_case.has_value()); |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/test_case); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame attempted to send a report to a custom destination " |
| "URL with macro substitution, but no origins are allowed by its " |
| "allowlist."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| } |
| |
| // Test that absence of an ad macro map (i.e., being std::nullopt) disables |
| // custom destination URL reports. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLNoAdMacroMap) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/{}); |
| |
| // Receive a buyer mapping whose `reporting_ad_macro_map` is std::nullopt. |
| reporter->OnUrlMappingReady(blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/std::nullopt); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame attempted to send a report to a custom destination URL " |
| "with macro substitution, which is not supported by the API that " |
| "created this frame's fenced frame config."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // Test macro substitution for reports to custom destination URLs, where macro |
| // map is empty. The reports can still be sent, with macros not substituted. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLEmptyAdMacroMap) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady(blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macro_map=*/{{}}); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL report_destination_template{ |
| "https://report_destination.test?foo=${FOO}&bar=${BAR}&foo2=${FOO}"}; |
| GURL report_destination_substituted{ |
| "https://report_destination.test?foo=${FOO}&bar=${BAR}&foo2=${FOO}"}; |
| |
| // Send a request from an allowed origin. (It should succeed.) |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_template), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_substituted, std::nullopt); |
| } |
| |
| // Test macro substitution for reports to custom destination URLs, where all |
| // macros are defined. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLCompleteMacroSubstitution) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/ |
| FencedFrameReporter::ReportingMacros( |
| {{"${FOO}", "foosub"}, {"${BAR}", "barsub"}})); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL report_destination_template{ |
| "https://report_destination.test?foo=${FOO}&bar=${BAR}&foo2=${FOO}"}; |
| GURL report_destination_substituted{ |
| "https://report_destination.test?foo=foosub&bar=barsub&foo2=foosub"}; |
| |
| // Send a request from an allowed origin. (It should succeed.) |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_template), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_substituted, std::nullopt); |
| } |
| |
| // Test macro substitution for reports to custom destination URLs, where only |
| // some macros are defined. Also test that macros are not substituted |
| // recursively. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLPartialMacroSubstitution) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/ |
| FencedFrameReporter::ReportingMacros({{"${FOO}", "${FOO}${FOO}"}})); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL report_destination_template{ |
| "https://report_destination.test?foo=${FOO}&bar=${BAR}&foo2=${FOO}"}; |
| GURL report_destination_substituted{ |
| "https://" |
| "report_destination.test?foo=${FOO}${FOO}&bar=${BAR}&foo2=${FOO}${FOO}"}; |
| |
| // Send a request from an allowed origin. (It should succeed.) |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_template), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_substituted, std::nullopt); |
| } |
| |
| // Test macro substitution for reports to custom destination URLs, where the |
| // macro is nested (e.g., ${${FOO}}). The macros are not substituted |
| // recursively. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLNestedMacro) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/ |
| FencedFrameReporter::ReportingMacros( |
| {{"${FOO}", "foo"}, {"${${FOO}}", "${FOO}"}})); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL report_destination_template{ |
| "https://report_destination.test?foo=${${FOO}}&foo2=${FOO}"}; |
| GURL report_destination_substituted{ |
| "https://" |
| "report_destination.test?foo=${FOO}&foo2=foo"}; |
| |
| // Send a request from an allowed origin. (It should succeed.) |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_template), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_substituted, std::nullopt); |
| } |
| |
| // Test that reports to HTTP custom destination URLs fail. |
| TEST_F(FencedFrameReporterTest, CustomDestinationHTTPURL) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady(blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/ |
| FencedFrameReporter::ReportingMacros({})); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL custom_report_destination{"http://report_destination.test"}; |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(custom_report_destination), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ( |
| error_message, |
| "This frame attempted to send a report to an invalid custom " |
| "destination URL. No further reports to custom destination URLs will " |
| "be allowed for this fenced frame config."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // Test that reports to invalid custom destination URLs fail. |
| TEST_F(FencedFrameReporterTest, CustomDestinationInvalidURL) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady(blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/ |
| FencedFrameReporter::ReportingMacros({})); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL custom_report_destination{"https://"}; |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(custom_report_destination), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ( |
| error_message, |
| "This frame attempted to send a report to an invalid custom " |
| "destination URL. No further reports to custom destination URLs will " |
| "be allowed for this fenced frame config."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // Test that reports to custom destination URLs that are invalid after macro |
| // substitution will fail. |
| TEST_F(FencedFrameReporterTest, CustomDestinationInvalidURLAfterMacros) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_}}); |
| |
| // Receive buyer mapping. |
| // This macro isn't the format that will be used by Protected Audience, but it |
| // makes it easy to test this particular case. |
| reporter->OnUrlMappingReady(blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{}}, |
| /*reporting_ad_macros=*/ |
| FencedFrameReporter::ReportingMacros( |
| {{"https://report_destination.test", ""}})); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| GURL custom_report_destination{"https://report_destination.test"}; |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(custom_report_destination), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(error_message, |
| "This frame attempted to send a report to a custom destination URL " |
| "that is invalid after macro substitution. No further reports to " |
| "custom destination URLs will be allowed for this fenced frame " |
| "config."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| |
| // Test allowlist for reports to custom destination URLs. |
| TEST_F(FencedFrameReporterTest, CustomDestinationURLAllowlist) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/ |
| {{report_destination_origin_, report_destination2_origin_}}); |
| |
| // Receive buyer mapping. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination_}}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| // Send a request to an allowed origin. (It should succeed.) |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, std::nullopt); |
| |
| // Send a request to a different allowed origin. (It should succeed.) |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination2_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 2); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[1].request, |
| report_destination2_, std::nullopt); |
| |
| // Send a request to a disallowed origin. (It should fail, and disable |
| // future custom destination URL requests..) |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(report_destination3_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ( |
| error_message, |
| "This frame attempted to send a report to a custom destination URL " |
| "with macro substitution to a disallowed origin. No further " |
| "reports to custom destination URLs will be allowed for this fenced " |
| "frame config."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 2); |
| |
| // Send a custom URL request to an allowed origin again. (It should still |
| // fail.) |
| EXPECT_FALSE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ( |
| error_message, |
| "This frame attempted to send a report to a custom destination URL " |
| "with macro substitution, but this functionality is disabled because " |
| "a request was previously attempted to a disallowed origin."); |
| EXPECT_EQ(console_message_level, blink::mojom::ConsoleMessageLevel::kError); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 2); |
| |
| // Send a regular report to an allowed origin. (It should succeed, because |
| // the allowlist is only for custom URL reports.) |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 3); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[2].request, |
| report_destination_, "event_data"); |
| |
| // Send a regular report to a disallowed origin. (It should succeed, because |
| // the allowlist is only for custom URL reports.) |
| NavigateAndCommit(report_destination2_); |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 4); |
| } |
| |
| // Test reports in the FLEDGE case, where reporting URL map is never received. |
| TEST_F(FencedFrameReporterTest, SendFledgeReportsNoMapReceived) { |
| auto attribution_data_host_manager = |
| std::make_unique<MockAttributionDataHostManager>(); |
| auto* mock_attribution_data_host_manager = |
| attribution_data_host_manager.get(); |
| |
| auto mock_manager = std::make_unique<MockAttributionManager>(); |
| mock_manager->SetDataHostManager(std::move(attribution_data_host_manager)); |
| static_cast<StoragePartitionImpl*>( |
| browser_context()->GetDefaultStoragePartition()) |
| ->OverrideAttributionManagerForTesting(std::move(mock_manager)); |
| |
| // `AttributionDataHostManager` is notified for the pending events. |
| EXPECT_CALL(*mock_attribution_data_host_manager, |
| NotifyFencedFrameReportingBeaconData(_, _, /*headers=*/nullptr, |
| /*is_final_response=*/true)); |
| { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt); |
| |
| // SendReport() is called, but a mapping is never received. |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type2", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSeller, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| } |
| } |
| |
| // Test sending non-reserved private aggregation requests, when events from |
| // fenced frame is received after FLEDGE non-reserved PA requests are ready. |
| TEST_F(FencedFrameReporterTest, FledgeEventsReceivedAfterRequestsReady) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt); |
| |
| // Receive all non-reserved private aggregation requests. |
| std::map<std::string, FinalizedPrivateAggregationRequests> |
| private_aggregation_event_map; |
| private_aggregation_event_map["event_type"].push_back( |
| kPrivateAggregationRequest.Clone()); |
| private_aggregation_event_map["event_type2"].push_back( |
| kPrivateAggregationRequest2.Clone()); |
| |
| std::map<std::string, FinalizedPrivateAggregationRequests> |
| private_aggregation_event_map2; |
| private_aggregation_event_map2["event_type"].push_back( |
| kPrivateAggregationRequest2.Clone()); |
| private_aggregation_event_map2["event_type3"].push_back( |
| kPrivateAggregationRequest2.Clone()); |
| |
| reporter->OnForEventPrivateAggregationRequestsReceived( |
| std::move(private_aggregation_event_map)); |
| reporter->OnForEventPrivateAggregationRequestsReceived( |
| std::move(private_aggregation_event_map2)); |
| // Reporter received private_aggregation_event_map. |
| EXPECT_THAT( |
| reporter->GetPrivateAggregationEventMapForTesting(), |
| testing::UnorderedElementsAre( |
| testing::Pair("event_type", |
| ElementsAreRequests(kPrivateAggregationRequest, |
| kPrivateAggregationRequest2)), |
| testing::Pair("event_type2", |
| ElementsAreRequests(kPrivateAggregationRequest2)), |
| testing::Pair("event_type3", |
| ElementsAreRequests(kPrivateAggregationRequest2)))); |
| // No event received from fenced frame yet, so no PA request gets sent. |
| EXPECT_TRUE( |
| private_aggregation_manager_.TakePrivateAggregationRequests().empty()); |
| |
| // Each call to SendPrivateAggregationRequestsForEvent() should send |
| // corresponding PA requests immediately, and the entry for the event type |
| // should be removed from reporter's private_aggregation_event_map. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type"); |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre(testing::Pair( |
| report_destination_origin_, |
| ElementsAreRequests(kPrivateAggregationRequest, |
| kPrivateAggregationRequest2)))); |
| EXPECT_THAT( |
| reporter->GetPrivateAggregationEventMapForTesting(), |
| testing::UnorderedElementsAre( |
| testing::Pair("event_type2", |
| ElementsAreRequests(kPrivateAggregationRequest2)), |
| testing::Pair("event_type3", |
| ElementsAreRequests(kPrivateAggregationRequest2)))); |
| |
| reporter->SendPrivateAggregationRequestsForEvent("event_type2"); |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre(testing::Pair( |
| report_destination_origin_, |
| ElementsAreRequests(kPrivateAggregationRequest2)))); |
| EXPECT_THAT( |
| reporter->GetPrivateAggregationEventMapForTesting(), |
| testing::UnorderedElementsAre(testing::Pair( |
| "event_type3", ElementsAreRequests(kPrivateAggregationRequest2)))); |
| |
| // Private aggregation requests for "event_type" has already been sent and |
| // cleared, so no more such requests for the type to send when receiving it |
| // again. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type"); |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre()); |
| |
| // No private aggregation requests for "event_type4", so there's no effect |
| // when "event_type4" is received. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type4"); |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre()); |
| } |
| |
| // Test sending non-reserved private aggregation requests, when events from |
| // fenced frame is received before FLEDGE non-reserved PA requests are ready. |
| TEST_F(FencedFrameReporterTest, FledgeEventsReceivedBeforeRequestsReady) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt); |
| |
| // Calls SendPrivateAggregationRequestsForEvent() with event types. The event |
| // types should be queued, since non-reserved private aggregation requests |
| // haven't been received yet. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type"); |
| reporter->SendPrivateAggregationRequestsForEvent("event_type"); |
| reporter->SendPrivateAggregationRequestsForEvent("event_type3"); |
| // ReceivedPaEvents is a std::set, so duplicate event types are only stored |
| // once. |
| EXPECT_THAT(reporter->GetReceivedPaEventsForTesting(), |
| testing::UnorderedElementsAre("event_type", "event_type3")); |
| EXPECT_TRUE( |
| private_aggregation_manager_.TakePrivateAggregationRequests().empty()); |
| |
| // Receive all non-reserved private aggregation requests. |
| std::map<std::string, FinalizedPrivateAggregationRequests> |
| private_aggregation_event_map; |
| private_aggregation_event_map["event_type"].push_back( |
| kPrivateAggregationRequest.Clone()); |
| private_aggregation_event_map["event_type2"].push_back( |
| kPrivateAggregationRequest2.Clone()); |
| |
| reporter->OnForEventPrivateAggregationRequestsReceived( |
| std::move(private_aggregation_event_map)); |
| |
| // `received_pa_events_` is kept, in case needed for new private aggregation |
| // requests from reportWin(). |
| EXPECT_THAT(reporter->GetReceivedPaEventsForTesting(), |
| testing::UnorderedElementsAre("event_type", "event_type3")); |
| // All pending pa events' PA requests in private_aggregation_event_map should |
| // be sent after private_aggregation_event_map is ready. |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre(testing::Pair( |
| report_destination_origin_, |
| ElementsAreRequests(kPrivateAggregationRequest)))); |
| |
| // Calling SendPrivateAggregationRequestsForEvent() should send |
| // corresponding PA requests immediately. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type2"); |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre(testing::Pair( |
| report_destination_origin_, |
| ElementsAreRequests(kPrivateAggregationRequest2)))); |
| // Although requests for "event_type2" are sent immediately, still store |
| // "event_type2" in reporter's `received_pa_events_`, so that further |
| // received PA requests of the type can still be triggered. |
| EXPECT_THAT(reporter->GetReceivedPaEventsForTesting(), |
| testing::UnorderedElementsAre("event_type", "event_type3", |
| "event_type2")); |
| |
| // Private aggregation requests for "event_type" has already been sent and |
| // cleared, so no more such requests for the type to sends. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type"); |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre()); |
| |
| // Receive more non-reserved private aggregation requests. It happens when |
| // reportWin() completes and then |
| // OnForEventPrivateAggregationRequestsReceived() is called. |
| std::map<std::string, FinalizedPrivateAggregationRequests> |
| private_aggregation_event_map2; |
| private_aggregation_event_map2["event_type"].push_back( |
| kPrivateAggregationRequest2.Clone()); |
| private_aggregation_event_map2["event_type2"].push_back( |
| kPrivateAggregationRequest.Clone()); |
| |
| reporter->OnForEventPrivateAggregationRequestsReceived( |
| std::move(private_aggregation_event_map2)); |
| |
| // Requests for both event types are sent immediately since there were such |
| // pending PA events. |
| EXPECT_THAT(private_aggregation_manager_.TakePrivateAggregationRequests(), |
| testing::UnorderedElementsAre(testing::Pair( |
| report_destination_origin_, |
| ElementsAreRequests(kPrivateAggregationRequest, |
| kPrivateAggregationRequest2)))); |
| } |
| |
| // FencedFrameReporter's `private_aggregation_manager` is nullptr but fenced |
| // frame sends events unexpectedly. This could happen if the renderer is |
| // compromised. Should just ignore the events. |
| TEST_F(FencedFrameReporterTest, FledgeEventsReceivedUnexpectedly) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, |
| /*private_aggregation_manager=*/nullptr, main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt); |
| |
| // Calls SendPrivateAggregationRequestsForEvent() with "event_type". |
| // "event_type" should be ignored and not be queued. |
| reporter->SendPrivateAggregationRequestsForEvent("event_type"); |
| EXPECT_TRUE(reporter->GetReceivedPaEventsForTesting().empty()); |
| EXPECT_TRUE( |
| private_aggregation_manager_.TakePrivateAggregationRequests().empty()); |
| } |
| |
| TEST_F(FencedFrameReporterTest, AttributionManagerShutDown_NoCrash) { |
| EXPECT_TRUE(attribution_manager()); |
| |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage( |
| shared_url_loader_factory(), browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{"event_type", report_destination_}}); |
| |
| // Make a report. |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, "event_data"); |
| |
| ShutDownAttributionManager(); |
| |
| EXPECT_FALSE(attribution_manager()); |
| |
| EXPECT_TRUE(test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination_.spec(), "")); |
| } |
| |
| // Histogram tests. Separate from existing tests because we need to account |
| // for HTTP request failures, and actually simulate HTTP responses instead of |
| // just leaving them pending. |
| |
| TEST_F(FencedFrameReporterTest, SendReportsRecordHistogramsEnum) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForSharedStorage( |
| shared_url_loader_factory(), browser_context(), |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{"event_type", report_destination_}, |
| {"event_type2", report_destination2_}}); |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| |
| // Make an enum report that succeeds. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, "event_data"); |
| |
| test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination_.spec(), ""); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| histogram_tester().ExpectTotalCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, 1); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kDestinationEnumSuccess, 1); |
| |
| // Make an enum report that fails due to HTTP status 404. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationEnumEvent("event_type", "event_data"), |
| blink::FencedFrame::ReportingDestination::kSharedStorageSelectUrl, |
| main_rfh_impl(), error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, "event_data"); |
| |
| test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination_.spec(), "", net::HTTP_NOT_FOUND); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| histogram_tester().ExpectTotalCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, 2); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kDestinationEnumSuccess, 1); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kDestinationEnumFailure, 1); |
| } |
| |
| TEST_F(FencedFrameReporterTest, SendReportsRecordHistogramsURL) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/{{report_destination_origin_}}); |
| |
| // Receive all mappings. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination_}}); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination2_}}); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/{{"event_type", report_destination3_}}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| |
| // Make a URL report that succeeds. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, std::nullopt); |
| |
| test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination_.spec(), ""); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| histogram_tester().ExpectTotalCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, 1); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kDestinationUrlSuccess, 1); |
| |
| // Make a URL report that fails with HTTP status 404. |
| EXPECT_TRUE(reporter->SendReport( |
| DestinationURLEvent(report_destination_), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination_, std::nullopt); |
| |
| test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination_.spec(), "", net::HTTP_NOT_FOUND); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| histogram_tester().ExpectTotalCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, 2); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kDestinationUrlSuccess, 1); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kDestinationUrlFailure, 1); |
| } |
| |
| TEST_F(FencedFrameReporterTest, SendReportsRecordHistogramsAutomaticBeacon) { |
| scoped_refptr<FencedFrameReporter> reporter = |
| FencedFrameReporter::CreateForFledge( |
| shared_url_loader_factory(), browser_context(), |
| /*direct_seller_is_seller=*/false, &private_aggregation_manager_, |
| main_frame_origin_, |
| /*winner_origin=*/report_destination_origin_, |
| /*winner_aggregation_coordinator_origin=*/std::nullopt, |
| /*allowed_reporting_origins=*/{{report_destination_origin_}}); |
| |
| // Receive all mappings. |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{blink::kFencedFrameTopNavigationStartBeaconType, report_destination_}}); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kComponentSeller, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{blink::kFencedFrameTopNavigationStartBeaconType, |
| report_destination2_}}); |
| reporter->OnUrlMappingReady( |
| blink::FencedFrame::ReportingDestination::kBuyer, |
| report_url_declarer_origin_, |
| /*reporting_url_map=*/ |
| {{blink::kFencedFrameTopNavigationStartBeaconType, report_destination3_}}, |
| /*reporting_ad_macros=*/FencedFrameReporter::ReportingMacros()); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| std::string error_message; |
| blink::mojom::ConsoleMessageLevel console_message_level = |
| blink::mojom::ConsoleMessageLevel::kError; |
| |
| // Make an automatic beacon report that succeeds. |
| EXPECT_TRUE(reporter->SendReport( |
| AutomaticBeaconEvent( |
| blink::mojom::AutomaticBeaconType::kTopNavigationStart, |
| "event_data3"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination3_, "event_data3"); |
| |
| test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination3_.spec(), ""); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| histogram_tester().ExpectTotalCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, 1); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kAutomaticSuccess, 1); |
| |
| // Make an automatic beacon report that fails with HTTP status 404. |
| EXPECT_TRUE(reporter->SendReport( |
| AutomaticBeaconEvent( |
| blink::mojom::AutomaticBeaconType::kTopNavigationStart, |
| "event_data3"), |
| blink::FencedFrame::ReportingDestination::kBuyer, main_rfh_impl(), |
| error_message, console_message_level)); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 1); |
| ValidateRequest((*test_url_loader_factory_.pending_requests())[0].request, |
| report_destination3_, "event_data3"); |
| |
| test_url_loader_factory_.SimulateResponseForPendingRequest( |
| report_destination3_.spec(), "", net::HTTP_NOT_FOUND); |
| EXPECT_EQ(test_url_loader_factory_.NumPending(), 0); |
| |
| histogram_tester().ExpectTotalCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, 2); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kAutomaticSuccess, 1); |
| histogram_tester().ExpectBucketCount( |
| blink::kFencedFrameBeaconReportingHttpResultUMA, |
| blink::FencedFrameBeaconReportingResult::kAutomaticFailure, 1); |
| } |
| |
| } // namespace |
| } // namespace content |