chromium/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc

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

#include "components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h"

#include <memory>
#include <string>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/heavy_ad_intervention/heavy_ad_features.h"
#include "components/page_load_metrics/browser/ads_page_load_metrics_test_waiter.h"
#include "components/page_load_metrics/browser/observers/ad_metrics/ad_intervention_browser_test_utils.h"
#include "components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.h"
#include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
#include "components/subresource_filter/content/shared/browser/ruleset_service.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/subresource_filter/core/common/activation_scope.h"
#include "components/subresource_filter/core/common/common_features.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_features.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "media/base/media_switches.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/network/public/cpp/features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
#include "url/url_constants.h"

namespace {

OriginStatus;
OriginStatusWithThrottling;

FrameTreeNodeId;

const char kAdsInterventionRecordedHistogram[] =;

const char kCrossOriginHistogramId[] =;

const char kCreativeOriginHistogramId[] =;

const char kCreativeOriginWithThrottlingHistogramId[] =;

const char kAdUserActivationHistogramId[] =;

const char kPeakWindowdPercentHistogramId[] =;

const char kHeavyAdInterventionTypeHistogramId[] =;

const char kMemoryMainFrameMaxHistogramId[] =;

const char kMemoryUpdateCountHistogramId[] =;

}  // namespace

class AdsPageLoadMetricsObserverBrowserTest
    : public subresource_filter::SubresourceFilterBrowserTest {};

// Test that an embedded ad is same origin.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       OriginStatusMetricEmbedded) {}

// Test that an empty embedded ad isn't reported at all.
// TODO(crbug.com/40188872): This test is flaky.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DISABLED_OriginStatusMetricEmbeddedEmpty) {}

// Test that an ad with the same origin as the main page is same origin.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       OriginStatusMetricSame) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       OriginStatusMetricCross) {}

// TODO(crbug.com/40840626): Re-enable this test
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DISABLED_AverageViewportAdDensity) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       AverageViewportAdDensity_ImageAd) {}

// Verifies that the page ad density records the maximum value during
// a page's lifecycling by creating a large ad frame, destroying it, and
// creating a smaller iframe. The ad density recorded is the density with
// the first larger frame.
// Flaky on Lacros bots. crbug.com/1338035
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_PageAdDensityRecordsPageMax
#else
#define MAYBE_PageAdDensityRecordsPageMax
#endif
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       MAYBE_PageAdDensityRecordsPageMax) {}

// TODO(crbug.com/40857704): Flaky on Lacros bots.
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_PageAdDensityMultipleFrames
#else
#define MAYBE_PageAdDensityMultipleFrames
#endif
// Creates multiple overlapping frames and verifies the page ad density.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       MAYBE_PageAdDensityMultipleFrames) {}

// Creates a frame with display:none styling and verifies that it has an
// empty intersection with the main frame.
//
// TODO(crbug.com/40715497): This test is disabled due to flaky failures on
// multiple platforms.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DISABLED_PageAdDensityIgnoreDisplayNoneFrame) {}

// Each CreativeOriginStatus* browser test inputs a pointer to a frame object
// representing the frame tree path of a website with with a (possibly null)
// ad subframe, which itself may have linearly nested subframes.
// Each test then queries cross_site_iframe_factory.html with the query string
// corresponding to the frame tree path, computes the actual creative origin
// status and compares it to the expected creative origin status.
class CreativeOriginAdsPageLoadMetricsObserverBrowserTest
    : public AdsPageLoadMetricsObserverBrowserTest {};

// Test that an ad with same origin as the main page is same-origin.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusSame) {}

// Test that an ad with a different origin as the main page is cross-origin.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusCross) {}

// Test that an ad creative with the same origin as the main page,
// but nested in a cross-origin root ad frame, is same-origin.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusSameNested) {}

// Test that an ad creative with a different origin as the main page,
// but nested in a same-origin root ad frame, is cross-origin.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusCrossNested) {}

// Test that an ad creative with a different origin as the main page,
// but nested two deep in a same-origin root ad frame, is cross-origin.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusCrossDoubleNested) {}

// Test that if no iframe renders text, the creative origin status is
// indeterminate. The creative origin status with throttling can be
// either kUnknownAndThrottled or kUnknownAndUnthrottled in this case
// due to race conditions, as nothing is painted.
// TODO(cammie): Find a better workaround for testing COSwT in this
// edge case.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusNoCreativeDesignated) {}

// Test that if no iframe is created, there is no histogram set.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       CreativeOriginStatusNoSubframes) {}

// Flakily fails (crbug.com/1099758)
// Test that a throttled ad with a different origin as the main page is
// marked as throttled, with indeterminate creative origin status.
IN_PROC_BROWSER_TEST_F(CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
                       DISABLED_CreativeOriginStatusWithThrottlingUnknown) {}

// Disabled due to flakiness https://crbug.com/1229601
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || \
    BUILDFLAG(IS_MAC)
#define MAYBE_CreativeOriginStatusWithThrottlingNestedThrottled
#else
#define MAYBE_CreativeOriginStatusWithThrottlingNestedThrottled
#endif

// Test that an ad creative with the same origin as the main page,
// but nested in a throttled cross-origin root ad frame, is marked as
// throttled, with indeterminate creative origin status.
IN_PROC_BROWSER_TEST_F(
    CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
    MAYBE_CreativeOriginStatusWithThrottlingNestedThrottled) {}

// Flakily fails. https://crbug.com/1099545
// Test that an ad creative with a different origin as the main page,
// but nested in a same-origin root ad frame, such that its root ad frame
// is outside the main frame but not throttled (because the root is
// same-origin), will be marked as having unknown creative origin status
// (since there will be no FCP) and being unthrottled.
IN_PROC_BROWSER_TEST_F(
    CreativeOriginAdsPageLoadMetricsObserverBrowserTest,
    DISABLED_CreativeOriginStatusWithThrottlingNestedUnthrottled) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       UserActivationSetOnFrame) {}

// See https://crbug.com/1193885.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       UserActivationSetOnFrameAfterSameOriginActivation) {}

// TODO(https://crbug.com/40286659): Fix this test.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || \
    BUILDFLAG(IS_MAC)
#define MAYBE_DocOverwritesNavigation
#else
#define MAYBE_DocOverwritesNavigation
#endif
// Test that a subframe that aborts (due to doc.write) doesn't cause a crash
// if it continues to load resources.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       MAYBE_DocOverwritesNavigation) {}

// Test that a blank ad subframe that is docwritten correctly reports metrics.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DocWriteAboutBlankAdframe) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       SubresourceFilter) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest, FrameDepth) {}

// Test that an ad frame with visible resource gets a FCP.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       FirstContentfulPaintRecorded) {}

// Test that a frame without display:none is reported as visible.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       VisibleAdframeRecorded) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DisplayNoneAdframeRecorded) {}

// TODO(crbug.com/41439596): Investigate why setting display: none on the
// frame will cause size updates to not be received. Verify that we record the
// correct sizes for display: none iframes.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest, FramePixelSize) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       FrameWithSmallAreaNotConsideredVisible) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       AdFrameRecordMediaStatusNotPlayed) {}

// Flaky on all platforms, http://crbug.com/972822.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DISABLED_AdFrameRecordMediaStatusPlayed) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       SameDomainFrameCreatedByAdScript_NotRecorddedAsAd) {}

IN_PROC_BROWSER_TEST_F(
    AdsPageLoadMetricsObserverBrowserTest,
    FrameCreatedByAdScriptNavigatedToAllowListRule_NotRecorddedAsAd) {}

enum class ReduceTransferSizeUpdatedIPCTestCase {};

// This test harness does not start the test server and allows
// ControllableHttpResponses to be declared.
class AdsPageLoadMetricsObserverResourceBrowserTest
    : public subresource_filter::SubresourceFilterBrowserTest,
      public ::testing::WithParamInterface<
          ReduceTransferSizeUpdatedIPCTestCase> {};

INSTANTIATE_TEST_SUITE_P();

IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       ReceivedAdResources) {}

// Main resources for adframes are counted as ad resources.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       ReceivedMainResourceAds) {}

// Subframe navigations report ad resources correctly.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       ReceivedSubframeNavigationAds) {}

// Verify that per-resource metrics are recorded correctly.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       ReceivedAdResourceMetrics) {}

IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       IncompleteResourcesRecordedToFrameMetrics) {}

// Verifies that the ad unloaded by the heavy ad intervention receives an
// intervention report prior to being unloaded.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       HeavyAdInterventionFired_ReportSent) {}

// Verifies that reports are sent to all children.
// crbug.com/1189635: flaky on win and linux.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_HeavyAdInterventionFired_ReportsToAllChildren
#else
#define MAYBE_HeavyAdInterventionFired_ReportsToAllChildren
#endif
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       MAYBE_HeavyAdInterventionFired_ReportsToAllChildren) {}

// Verifies that the frame is navigated to the intervention page when a
// heavy ad intervention triggers.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       HeavyAdInterventionEnabled_ErrorPageLoaded) {}

class AdsPageLoadMetricsObserverResourceBrowserTestWithoutHeavyAdIntervention
    : public AdsPageLoadMetricsObserverResourceBrowserTest {};

INSTANTIATE_TEST_SUITE_P();

// Check that when the heavy ad feature is disabled we don't navigate
// the frame.
IN_PROC_BROWSER_TEST_P(
    AdsPageLoadMetricsObserverResourceBrowserTestWithoutHeavyAdIntervention,
    ErrorPageNotLoaded) {}

// Check that we don't activate a HeavyAdIntervention field trial if we don't
// have a heavy ad.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       HeavyAdInterventionNoHeavyAd_FieldTrialNotActive) {}

// Check that the Heavy Ad Intervention fires the correct number of times to
// protect privacy, and that after that limit is hit, the Ads Intervention
// Framework takes over for future navigations.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       HeavyAdInterventionBlocklistFull_InterventionBlocked) {}

// Verifies that the blocklist is setup correctly and the intervention triggers
// in incognito mode.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       HeavyAdInterventionIncognitoMode_InterventionFired) {}

// Verify that UKM metrics are recorded correctly.
IN_PROC_BROWSER_TEST_P(AdsPageLoadMetricsObserverResourceBrowserTest,
                       RecordedUKMMetrics) {}

void WaitForRAF(content::DOMMessageQueue* message_queue) {}

// Test that rAF events are measured as part of the cpu metrics.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       FrameRAFTriggersCpuUpdate) {}

// Test that rAF events are measured as part of the cpu metrics.
// TODO(crbug.com/40826975): Flaky on multiple platforms.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS) || \
    BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_TwoRAFFramesTriggerCpuUpdates
#else
#define MAYBE_TwoRAFFramesTriggerCpuUpdates
#endif
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       MAYBE_TwoRAFFramesTriggerCpuUpdates) {}

// Test that cpu time aggregation across a subframe navigation is cumulative.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       AggregateCpuTriggersCpuUpdateOverSubframeNavigate) {}

// Test that cpu metrics are cumulative across subframe navigations.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       SubframeNavigate_CpuTimesCumulative) {}

IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                       DisallowedAdFrames_NotMeasured) {}

// DummyMemoryObserver is a subclass of V8DetailedMemoryObserverAnySeq so
// that we can spin up a request in the AdsMemoryMeasurementBrowserTest with
// MeasurementMode::kEagerForTesting, which will make measurements available
// to the PageLoadMetricsMemoryTracker much more quickly than they would be
// otherwise.
class DummyMemoryObserver
    : public performance_manager::v8_memory::V8DetailedMemoryObserverAnySeq {};

class AdsMemoryMeasurementBrowserTest
    : public subresource_filter::SubresourceFilterBrowserTest {};

IN_PROC_BROWSER_TEST_F(AdsMemoryMeasurementBrowserTest,
                       SingleAdFrame_MaxMemoryBytesRecorded) {}

class AdsPageLoadMetricsObserverPrerenderingBrowserTest
    : public AdsPageLoadMetricsObserverBrowserTest {};

// Test that prerendering doesn't have metrics by AdsPageLoadMetricsObserver.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverPrerenderingBrowserTest,
                       NoMetricsInPrerendering) {}