| // Copyright 2017 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 "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/macros.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/time/default_clock.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/heavy_ad_intervention/heavy_ad_blocklist.h" |
| #include "chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h" |
| #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h" |
| #include "chrome/browser/subresource_filter/subresource_filter_test_harness.h" |
| #include "chrome/common/chrome_features.h" |
| #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h" |
| #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h" |
| #include "components/blacklist/opt_out_blacklist/opt_out_store.h" |
| #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" |
| #include "components/page_load_metrics/browser/page_load_metrics_observer.h" |
| #include "components/page_load_metrics/browser/page_load_tracker.h" |
| #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h" |
| #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h" |
| #include "components/subresource_filter/core/common/load_policy.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "content/public/browser/global_request_id.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/navigation_throttle.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/resource_type.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/public/test/test_navigation_throttle.h" |
| #include "content/public/test/test_navigation_throttle_inserter.h" |
| #include "content/public/test/test_renderer_host.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "net/base/host_port_pair.h" |
| #include "services/metrics/public/cpp/metrics_utils.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "url/gurl.h" |
| |
| using content::NavigationSimulator; |
| using content::RenderFrameHost; |
| using content::RenderFrameHostTester; |
| using content::TestNavigationThrottle; |
| |
| namespace { |
| |
| struct ExpectedFrameBytes { |
| ExpectedFrameBytes(size_t cached_kb, size_t uncached_kb) |
| : cached_kb(cached_kb), uncached_kb(uncached_kb) {} |
| size_t cached_kb; |
| size_t uncached_kb; |
| |
| bool operator<(const ExpectedFrameBytes& other) const { |
| return cached_kb < other.cached_kb || |
| (cached_kb == other.cached_kb && uncached_kb < other.uncached_kb); |
| } |
| }; |
| |
| enum class ResourceCached { kNotCached = 0, kCachedHttp, kCachedMemory }; |
| enum class FrameType { AD = 0, NON_AD }; |
| |
| const char kAdUrl[] = "https://ads.com/ad/disallowed.html"; |
| const char kNonAdUrl[] = "https://foo.com/"; |
| const char kNonAdUrlSameOrigin[] = "https://ads.com/foo"; |
| |
| const int kMaxHeavyAdNetworkBytes = |
| heavy_ad_thresholds::kMaxNetworkBytes + |
| AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider:: |
| kMaxNetworkThresholdNoiseBytes; |
| |
| // Asynchronously cancels the navigation at WillProcessResponse. Before |
| // cancelling, simulates loading a main frame resource. |
| class ResourceLoadingCancellingThrottle |
| : public content::TestNavigationThrottle { |
| public: |
| static std::unique_ptr<content::NavigationThrottle> Create( |
| content::NavigationHandle* handle) { |
| return std::make_unique<ResourceLoadingCancellingThrottle>(handle); |
| } |
| |
| explicit ResourceLoadingCancellingThrottle( |
| content::NavigationHandle* navigation_handle) |
| : content::TestNavigationThrottle(navigation_handle) { |
| SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE, |
| TestNavigationThrottle::ASYNCHRONOUS, CANCEL); |
| } |
| |
| private: |
| // content::TestNavigationThrottle: |
| void OnWillRespond(NavigationThrottle::ThrottleCheckResult result) { |
| if (result.action() != CANCEL) { |
| return; |
| } |
| |
| auto* observer = |
| page_load_metrics::MetricsWebContentsObserver::FromWebContents( |
| navigation_handle()->GetWebContents()); |
| DCHECK(observer); |
| |
| // Load a resource for the main frame before it commits. |
| std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources; |
| page_load_metrics::mojom::ResourceDataUpdatePtr resource = |
| page_load_metrics::mojom::ResourceDataUpdate::New(); |
| resource->received_data_length = 10 * 1024; |
| resource->delta_bytes = 10 * 1024; |
| resource->encoded_body_length = 10 * 1024; |
| resource->cache_type = page_load_metrics::mojom::CacheType::kNotCached; |
| resource->is_complete = true; |
| resource->is_primary_frame_resource = true; |
| resources.push_back(std::move(resource)); |
| auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place); |
| page_load_metrics::InitPageLoadTimingForTest(timing.get()); |
| observer->OnTimingUpdated( |
| navigation_handle()->GetRenderFrameHost(), std::move(timing), |
| page_load_metrics::mojom::PageLoadMetadataPtr(base::in_place), |
| page_load_metrics::mojom::PageLoadFeaturesPtr(base::in_place), |
| resources, |
| page_load_metrics::mojom::FrameRenderDataUpdatePtr(base::in_place), |
| page_load_metrics::mojom::CpuTimingPtr(base::in_place), |
| page_load_metrics::mojom::DeferredResourceCountsPtr(base::in_place)); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(ResourceLoadingCancellingThrottle); |
| }; |
| |
| // Mock noise provider which always gives a supplied value of noise for the |
| // heavy ad intervention thresholds. |
| class MockNoiseProvider |
| : public AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider { |
| public: |
| explicit MockNoiseProvider(int noise) |
| : HeavyAdThresholdNoiseProvider(), noise_(noise) {} |
| ~MockNoiseProvider() override = default; |
| |
| int GetNetworkThresholdNoiseForFrame() const override { return noise_; } |
| |
| private: |
| int noise_; |
| }; |
| |
| std::string SuffixedHistogram(const std::string& suffix) { |
| return base::StringPrintf("PageLoad.Clients.Ads.%s", suffix.c_str()); |
| } |
| |
| // Verifies that the histograms match what is expected. Frames that should not |
| // be recorded (due to zero bytes and zero CPU usage) should be not be |
| // represented in |ad_frames|. |
| void TestHistograms(const base::HistogramTester& histograms, |
| const ukm::TestAutoSetUkmRecorder& ukm_recorder, |
| const std::vector<ExpectedFrameBytes>& ad_frames, |
| size_t non_ad_cached_kb, |
| size_t non_ad_uncached_kb) { |
| size_t total_ad_cached_kb = 0; |
| size_t total_ad_uncached_kb = 0; |
| size_t total_ad_kb = 0; |
| size_t ad_frame_count = 0; |
| |
| std::map<size_t, int> frames_with_total_byte_count; |
| std::map<size_t, int> frames_with_network_byte_count; |
| std::map<size_t, int> frames_with_percent_network_count; |
| |
| // This map is keyed by (total bytes, network bytes). |
| std::map<ExpectedFrameBytes, int> frame_byte_counts; |
| |
| // Perform some initial calculations on the number of bytes, of each type, |
| // in each ad frame. |
| for (const ExpectedFrameBytes& bytes : ad_frames) { |
| total_ad_cached_kb += bytes.cached_kb; |
| total_ad_uncached_kb += bytes.uncached_kb; |
| total_ad_kb += bytes.cached_kb + bytes.uncached_kb; |
| |
| ad_frame_count += 1; |
| |
| size_t total_frame_kb = bytes.cached_kb + bytes.uncached_kb; |
| |
| frames_with_total_byte_count[total_frame_kb] += 1; |
| frames_with_network_byte_count[bytes.uncached_kb] += 1; |
| if (total_frame_kb > 0) { |
| frames_with_percent_network_count[(bytes.uncached_kb * 100) / |
| total_frame_kb] += 1; |
| } |
| frame_byte_counts[bytes] += 1; |
| } |
| |
| // Test the histograms. |
| histograms.ExpectUniqueSample(SuffixedHistogram("FrameCounts.AdFrames.Total"), |
| ad_frame_count, 1); |
| |
| if (ad_frame_count == 0) |
| return; |
| |
| for (const auto& total_bytes_and_count : frames_with_total_byte_count) { |
| histograms.ExpectBucketCount( |
| SuffixedHistogram("Bytes.AdFrames.PerFrame.Total2"), |
| total_bytes_and_count.first, total_bytes_and_count.second); |
| } |
| for (const auto& network_bytes_and_count : frames_with_network_byte_count) { |
| histograms.ExpectBucketCount( |
| SuffixedHistogram("Bytes.AdFrames.PerFrame.Network"), |
| network_bytes_and_count.first, network_bytes_and_count.second); |
| } |
| for (const auto& percent_network_and_count : |
| frames_with_percent_network_count) { |
| histograms.ExpectBucketCount( |
| SuffixedHistogram("Bytes.AdFrames.PerFrame.PercentNetwork2"), |
| percent_network_and_count.first, percent_network_and_count.second); |
| } |
| |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.AdFrames.Aggregate.Total2"), total_ad_kb, 1); |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.AdFrames.Aggregate.Network"), |
| total_ad_uncached_kb, 1); |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.FullPage.Total2"), |
| non_ad_cached_kb + non_ad_uncached_kb + total_ad_kb, 1); |
| histograms.ExpectUniqueSample(SuffixedHistogram("Bytes.FullPage.Network"), |
| non_ad_uncached_kb + total_ad_uncached_kb, 1); |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.NonAdFrames.Aggregate.Total2"), |
| non_ad_cached_kb + non_ad_uncached_kb, 1); |
| if (total_ad_kb + non_ad_cached_kb + non_ad_uncached_kb > 0) { |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.FullPage.Total.PercentAds2"), |
| (total_ad_kb * 100) / |
| (total_ad_kb + non_ad_cached_kb + non_ad_uncached_kb), |
| 1); |
| } |
| if (total_ad_kb > 0) { |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.AdFrames.Aggregate.PercentNetwork2"), |
| ((total_ad_uncached_kb * 100) / total_ad_kb), 1); |
| } |
| if (total_ad_uncached_kb + non_ad_uncached_kb > 0) { |
| histograms.ExpectUniqueSample( |
| SuffixedHistogram("Bytes.FullPage.Network.PercentAds"), |
| (total_ad_uncached_kb * 100) / |
| (total_ad_uncached_kb + non_ad_uncached_kb), |
| 1); |
| } |
| |
| // Verify AdFrameLoad UKM metrics. |
| auto entries = |
| ukm_recorder.GetEntriesByName(ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(ad_frame_count, entries.size()); |
| |
| for (const auto& byte_count : frame_byte_counts) { |
| size_t cached_bytes = byte_count.first.cached_kb * 1024; |
| size_t network_bytes = byte_count.first.uncached_kb * 1024; |
| int matching_entries = 0; |
| for (auto const* entry : entries) { |
| int64_t entry_cache_bytes = *ukm_recorder.GetEntryMetric( |
| entry, ukm::builders::AdFrameLoad::kLoading_CacheBytes2Name); |
| int64_t entry_network_bytes = *ukm_recorder.GetEntryMetric( |
| entry, ukm::builders::AdFrameLoad::kLoading_NetworkBytesName); |
| if (entry_cache_bytes == |
| ukm::GetExponentialBucketMinForBytes(cached_bytes) && |
| entry_network_bytes == |
| ukm::GetExponentialBucketMinForBytes(network_bytes)) |
| matching_entries++; |
| } |
| EXPECT_EQ(matching_entries, byte_count.second); |
| } |
| } |
| |
| } // namespace |
| |
| class AdsPageLoadMetricsObserverTest |
| : public SubresourceFilterTestHarness, |
| public blacklist::OptOutBlacklistDelegate { |
| public: |
| AdsPageLoadMetricsObserverTest() |
| : test_blocklist_(std::make_unique<HeavyAdBlocklist>( |
| nullptr, |
| base::DefaultClock::GetInstance(), |
| this)) {} |
| |
| void SetUp() override { |
| SubresourceFilterTestHarness::SetUp(); |
| tester_ = |
| std::make_unique<page_load_metrics::PageLoadMetricsObserverTester>( |
| web_contents(), this, |
| base::BindRepeating( |
| &AdsPageLoadMetricsObserverTest::RegisterObservers, |
| base::Unretained(this))); |
| ConfigureAsSubresourceFilterOnlyURL(GURL(kAdUrl)); |
| } |
| |
| // Returns the final RenderFrameHost after navigation commits. |
| RenderFrameHost* NavigateFrame(const std::string& url, |
| content::RenderFrameHost* frame) { |
| auto navigation_simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(url), frame); |
| navigation_simulator->Commit(); |
| return navigation_simulator->GetFinalRenderFrameHost(); |
| } |
| |
| // Returns the final RenderFrameHost after navigation commits. |
| RenderFrameHost* NavigateMainFrame(const std::string& url) { |
| return NavigateFrame(url, web_contents()->GetMainFrame()); |
| } |
| |
| // Frame creation doesn't trigger a mojo call since unit tests have no render |
| // process. Just mock them for now. |
| void OnAdSubframeDetected(RenderFrameHost* render_frame_host) { |
| subresource_filter::SubresourceFilterObserverManager::FromWebContents( |
| web_contents()) |
| ->NotifyAdSubframeDetected(render_frame_host); |
| } |
| |
| // Set the interactive status of the main frame. |
| void OnMainFrameInteractive(base::TimeDelta frame_interactive_offset) { |
| auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place); |
| page_load_metrics::InitPageLoadTimingForTest(timing.get()); |
| timing->interactive_timing->interactive = |
| base::Optional<base::TimeDelta>(frame_interactive_offset); |
| // Call directly since main frame timing updates may be delayed. |
| ads_observer_->OnPageInteractive(*timing); |
| } |
| |
| void OnCpuTimingUpdate(RenderFrameHost* render_frame_host, |
| base::TimeDelta cpu_time_spent) { |
| page_load_metrics::mojom::CpuTiming cpu_timing(cpu_time_spent); |
| tester_->SimulateCpuTimingUpdate(cpu_timing, render_frame_host); |
| } |
| |
| // Sends |total_time| in CPU timing updates spread across a variable amount of |
| // 30 second windows to not hit the peak window usage cap for the heavy ad |
| // intervention. |
| void UseCpuTimeUnderThreshold(RenderFrameHost* render_frame_host, |
| base::TimeDelta total_time) { |
| const base::TimeDelta peak_threshold = base::TimeDelta::FromMilliseconds( |
| heavy_ad_thresholds::kMaxPeakWindowedPercent * 30000 / 100 - 1); |
| for (; total_time > peak_threshold; total_time -= peak_threshold) { |
| OnCpuTimingUpdate(render_frame_host, peak_threshold); |
| AdvancePageDuration(base::TimeDelta::FromSeconds(31)); |
| } |
| OnCpuTimingUpdate(render_frame_host, total_time); |
| } |
| |
| void OnHidden() { web_contents()->WasHidden(); } |
| |
| void OnShown() { web_contents()->WasShown(); } |
| |
| void TriggerFirstUserActivation(RenderFrameHost* render_frame_host) { |
| tester_->SimulateFrameReceivedFirstUserActivation(render_frame_host); |
| } |
| |
| void AdvancePageDuration(base::TimeDelta delta) { clock_->Advance(delta); } |
| |
| // Returns the final RenderFrameHost after navigation commits. |
| RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url, |
| content::RenderFrameHost* parent) { |
| RenderFrameHost* subframe = |
| RenderFrameHostTester::For(parent)->AppendChild("frame_name"); |
| auto navigation_simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(url), subframe); |
| navigation_simulator->Commit(); |
| return navigation_simulator->GetFinalRenderFrameHost(); |
| } |
| |
| void ResourceDataUpdate(RenderFrameHost* render_frame_host, |
| ResourceCached resource_cached, |
| int resource_size_in_kbyte, |
| std::string mime_type = "", |
| bool is_ad_resource = false, |
| bool is_main_frame_resource = false) { |
| std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources; |
| page_load_metrics::mojom::ResourceDataUpdatePtr resource = |
| page_load_metrics::mojom::ResourceDataUpdate::New(); |
| resource->received_data_length = |
| static_cast<bool>(resource_cached) ? 0 : resource_size_in_kbyte << 10; |
| resource->delta_bytes = resource->received_data_length; |
| resource->encoded_body_length = resource_size_in_kbyte << 10; |
| resource->reported_as_ad_resource = is_ad_resource; |
| resource->is_complete = true; |
| switch (resource_cached) { |
| case ResourceCached::kNotCached: |
| resource->cache_type = page_load_metrics::mojom::CacheType::kNotCached; |
| break; |
| case ResourceCached::kCachedHttp: |
| resource->cache_type = page_load_metrics::mojom::CacheType::kHttp; |
| break; |
| case ResourceCached::kCachedMemory: |
| resource->cache_type = page_load_metrics::mojom::CacheType::kMemory; |
| break; |
| } |
| resource->mime_type = mime_type; |
| resource->is_primary_frame_resource = true; |
| resource->is_main_frame_resource = |
| render_frame_host->GetFrameTreeNodeId() == |
| main_rfh()->GetFrameTreeNodeId(); |
| resources.push_back(std::move(resource)); |
| tester_->SimulateResourceDataUseUpdate(resources, render_frame_host); |
| } |
| |
| void TimingUpdate(const page_load_metrics::mojom::PageLoadTiming& timing) { |
| tester_->SimulateTimingUpdate(timing); |
| } |
| |
| page_load_metrics::PageLoadMetricsObserverTester* tester() { |
| return tester_.get(); |
| } |
| |
| base::HistogramTester& histogram_tester() { return histogram_tester_; } |
| |
| const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const { |
| return test_ukm_recorder_; |
| } |
| |
| HeavyAdBlocklist* blocklist() { return test_blocklist_.get(); } |
| |
| void OverrideVisibilityTrackerWithMockClock() { |
| clock_ = std::make_unique<base::SimpleTestTickClock>(); |
| clock_->SetNowTicks(base::TimeTicks::Now()); |
| } |
| |
| void OverrideHeavyAdNoiseProvider( |
| std::unique_ptr<MockNoiseProvider> noise_provider) { |
| ads_observer_->SetHeavyAdThresholdNoiseProviderForTesting( |
| std::move(noise_provider)); |
| } |
| |
| // Given the prefix of the CPU histogram to check, either "Cpu.FullPage" or |
| // "Cpu.AdFrames.PerFrame", as well as the type, one of "" (for "FullPage"), |
| // "Activated", or "Unactivated", along with the total pre and post cpu time |
| // and total time, check all the relevant cpu histograms. |
| void CheckCpuHistograms(const std::string& prefix, |
| std::string type, |
| int pre_task_time, |
| int pre_time, |
| int post_task_time, |
| int post_time) { |
| int total_task_time = pre_task_time + post_task_time; |
| int total_time = pre_time + post_time; |
| std::string suffix = type == "Activated" ? "Activation" : "Interactive"; |
| type = type.empty() ? "" : "." + type; |
| |
| CheckSpecificCpuHistogram(SuffixedHistogram(prefix + ".TotalUsage" + type), |
| total_task_time, total_time); |
| CheckSpecificCpuHistogram( |
| SuffixedHistogram(prefix + ".TotalUsage" + type + ".Pre" + suffix), |
| pre_task_time, pre_time); |
| CheckSpecificCpuHistogram( |
| SuffixedHistogram(prefix + ".TotalUsage" + type + ".Post" + suffix), |
| post_task_time, post_time); |
| } |
| |
| private: |
| void CheckSpecificCpuHistogram(std::string total_histogram, |
| int total_task_time, |
| int total_time) { |
| if (total_time) { |
| histogram_tester().ExpectUniqueSample(total_histogram, total_task_time, |
| 1); |
| } else { |
| histogram_tester().ExpectTotalCount(total_histogram, 0); |
| } |
| } |
| |
| void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) { |
| auto observer = std::make_unique<AdsPageLoadMetricsObserver>( |
| clock_.get(), test_blocklist_.get()); |
| ads_observer_ = observer.get(); |
| tracker->AddObserver(std::move(observer)); |
| // Swap out the ui::ScopedVisibilityTracker to use the test clock. |
| if (clock_) { |
| ui::ScopedVisibilityTracker visibility_tracker(clock_.get(), true); |
| tracker->SetVisibilityTrackerForTesting(visibility_tracker); |
| } |
| } |
| |
| std::unique_ptr<HeavyAdBlocklist> test_blocklist_; |
| base::HistogramTester histogram_tester_; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder_; |
| std::unique_ptr<page_load_metrics::PageLoadMetricsObserverTester> tester_; |
| |
| // The clock used by the ui::ScopedVisibilityTracker, assigned if non-null. |
| std::unique_ptr<base::SimpleTestTickClock> clock_; |
| |
| // A pointer to the AdsPageLoadMetricsObserver used by the tests. |
| AdsPageLoadMetricsObserver* ads_observer_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserverTest); |
| }; |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, PageWithNoAds) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(frame1, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(frame2, ResourceCached::kNotCached, 10); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), |
| std::vector<ExpectedFrameBytes>(), 0 /* non_ad_cached_kb */, |
| 30 /* non_ad_uncached_kb */); |
| |
| // Verify that other UMA wasn't written. |
| histogram_tester().ExpectTotalCount( |
| "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, PageWithAds) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(frame1, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(frame2, ResourceCached::kNotCached, 10); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 10}}, |
| 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, AdFrameMimeTypeBytes) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate( |
| ad_frame, ResourceCached::kNotCached, 10 /* resource_size_in_kbyte */, |
| "application/javascript" /* mime_type */, true /* is_ad_resource */); |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| 20 /* resource_size_in_kbyte */, |
| "image/png" /* mime_type */, true /* is_ad_resource */); |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| 30 /* resource_size_in_kbyte */, |
| "video/webm" /* mime_type */, true /* is_ad_resource */); |
| |
| // Cached resource not counted. |
| ResourceDataUpdate(ad_frame, ResourceCached::kCachedHttp, |
| 40 /* resource_size_in_kbyte */, |
| "video/webm" /* mime_type */, true /* is_ad_resource */); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kLoading_JavascriptBytesName, |
| ukm::GetExponentialBucketMinForBytes(10 * 1024)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kLoading_ImageBytesName, |
| ukm::GetExponentialBucketMinForBytes(20 * 1024)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kLoading_VideoBytesName, |
| ukm::GetExponentialBucketMinForBytes(30 * 1024)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kLoading_NetworkBytesName, |
| ukm::GetExponentialBucketMinForBytes(60 * 1024)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kLoading_CacheBytes2Name, |
| ukm::GetExponentialBucketMinForBytes(40 * 1024)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kLoading_NumResourcesName, |
| 4); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, ResourceBeforeAdFrameCommits) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| // Create subframe and load resource before commit. |
| RenderFrameHost* subframe = |
| RenderFrameHostTester::For(main_frame)->AppendChild("foo"); |
| auto navigation_simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe); |
| ResourceDataUpdate(subframe, ResourceCached::kNotCached, 10); |
| navigation_simulator->Commit(); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 10}}, |
| 0 /* non_ad_cached_kb */, 10 /*non_ad_uncached_kb*/); |
| } |
| |
| // Test that the cross-origin ad subframe navigation metric works as it's |
| // supposed to, triggering a false addition with each ad that's in the same |
| // origin as the main page, and a true when when the ad has a separate origin. |
| TEST_F(AdsPageLoadMetricsObserverTest, AdsOriginStatusMetrics) { |
| const char kCrossOriginHistogramId[] = |
| "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame." |
| "OriginStatus"; |
| |
| // Test that when the main frame origin is different from a direct ad |
| // subframe it is correctly identified as cross-origin, but do not count |
| // indirect ad subframes. |
| { |
| base::HistogramTester histograms; |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_sub_frame = |
| CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(ad_sub_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, ad_sub_frame), |
| ResourceCached::kNotCached, 10); |
| // Trigger histograms by navigating away, then test them. |
| NavigateFrame(kAdUrl, main_frame); |
| histograms.ExpectUniqueSample(kCrossOriginHistogramId, |
| FrameData::OriginStatus::kCross, 1); |
| auto entries = |
| ukm_recorder.GetEntriesByName(ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| ukm_recorder.ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kStatus_CrossOriginName, |
| static_cast<int64_t>(FrameData::OriginStatus::kCross)); |
| } |
| |
| // Add a non-ad subframe and an ad subframe and make sure the total count |
| // only adjusts by one. |
| { |
| base::HistogramTester histograms; |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame), |
| ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(CreateAndNavigateSubFrame(kNonAdUrl, main_frame), |
| ResourceCached::kNotCached, 10); |
| // Trigger histograms by navigating away, then test them. |
| NavigateFrame(kAdUrl, main_frame); |
| histograms.ExpectUniqueSample(kCrossOriginHistogramId, |
| FrameData::OriginStatus::kCross, 1); |
| auto entries = |
| ukm_recorder.GetEntriesByName(ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| ukm_recorder.ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kStatus_CrossOriginName, |
| static_cast<int64_t>(FrameData::OriginStatus::kCross)); |
| } |
| |
| // Add an ad subframe in the same origin as the parent frame and make sure it |
| // gets identified as non-cross-origin. Note: top-level navigations are never |
| // considered to be ads. |
| { |
| base::HistogramTester histograms; |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrlSameOrigin); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame), |
| ResourceCached::kNotCached, 10); |
| // Trigger histograms by navigating away, then test them. |
| NavigateFrame(kAdUrl, main_frame); |
| histograms.ExpectUniqueSample(kCrossOriginHistogramId, |
| FrameData::OriginStatus::kSame, 1); |
| auto entries = |
| ukm_recorder.GetEntriesByName(ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| ukm_recorder.ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kStatus_CrossOriginName, |
| static_cast<int64_t>(FrameData::OriginStatus::kSame)); |
| } |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, PageWithAdFrameThatRenavigates) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Navigate the ad frame again. |
| ad_frame = NavigateFrame(kNonAdUrl, ad_frame); |
| |
| // In total, 30KB for entire page and 20 in one ad frame. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 20}}, |
| 0 /* non_ad_cached_kb */, 10 /* non_ad_uncached_kb */); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, PageWithNonAdFrameThatRenavigatesToAd) { |
| // Main frame. |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| // Sub frame that is not an ad. |
| RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| |
| // Child of the sub-frame that is an ad. |
| RenderFrameHost* sub_frame_child_ad = |
| CreateAndNavigateSubFrame(kAdUrl, sub_frame); |
| |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(sub_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(sub_frame_child_ad, ResourceCached::kNotCached, 10); |
| |
| // Navigate the subframe again, this time it's an ad. |
| sub_frame = NavigateFrame(kAdUrl, sub_frame); |
| ResourceDataUpdate(sub_frame, ResourceCached::kNotCached, 10); |
| |
| // In total, 40KB was loaded for the entire page and 20KB from ad |
| // frames (the original child ad frame and the renavigated frame which |
| // turned into an ad). |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 10}, {0, 10}}, |
| 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedNavigation) { |
| // If the first navigation in a frame is aborted, keep track of its bytes. |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| // Create an ad subframe that aborts before committing. |
| RenderFrameHost* subframe_ad = |
| RenderFrameHostTester::For(main_frame)->AppendChild("foo"); |
| auto navigation_simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad); |
| // The sub-frame renavigates before it commits. |
| navigation_simulator->Start(); |
| OnAdSubframeDetected(subframe_ad); |
| navigation_simulator->Fail(net::ERR_ABORTED); |
| |
| // Load resources for the aborted frame (e.g., simulate the navigation |
| // aborting due to a doc.write during provisional navigation). They should |
| // be counted. |
| ResourceDataUpdate(subframe_ad, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(subframe_ad, ResourceCached::kNotCached, 10); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 20}}, |
| 0 /* non_ad_cached_kb */, 10 /* non_ad_uncached_kb */); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedSecondNavigationForFrame) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| // Sub frame that is not an ad. |
| RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| ResourceDataUpdate(sub_frame, ResourceCached::kNotCached, 10); |
| |
| // Now navigate (and abort) the subframe to an ad. |
| auto navigation_simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), sub_frame); |
| // The sub-frame renavigates before it commits. |
| navigation_simulator->Start(); |
| OnAdSubframeDetected(sub_frame); |
| navigation_simulator->Fail(net::ERR_ABORTED); |
| |
| // Load resources for the aborted frame (e.g., simulate the navigation |
| // aborting due to a doc.write during provisional navigation). Since the |
| // frame attempted to load an ad, the frame is tagged forever as an ad. |
| ResourceDataUpdate(sub_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(sub_frame, ResourceCached::kNotCached, 10); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 20}}, |
| 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TwoResourceLoadsBeforeCommit) { |
| // Main frame. |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| // Now open a subframe and have its resource load before notification of |
| // navigation finishing. |
| RenderFrameHost* subframe_ad = |
| RenderFrameHostTester::For(main_frame)->AppendChild("foo"); |
| auto navigation_simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad); |
| ResourceDataUpdate(subframe_ad, ResourceCached::kNotCached, 10); |
| |
| // The sub-frame renavigates before it commits. |
| navigation_simulator->Start(); |
| OnAdSubframeDetected(subframe_ad); |
| navigation_simulator->Fail(net::ERR_ABORTED); |
| |
| // Renavigate the subframe to a successful commit. But again, the resource |
| // loads before the observer sees the finished navigation. |
| ResourceDataUpdate(subframe_ad, ResourceCached::kNotCached, 10); |
| NavigateFrame(kNonAdUrl, subframe_ad); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 20}}, |
| 0 /* non_ad_cached_kb */, 10 /* non_ad_uncached_kb */); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, MainFrameResource) { |
| // Start main-frame navigation |
| auto navigation_simulator = NavigationSimulator::CreateRendererInitiated( |
| GURL(kNonAdUrl), web_contents()->GetMainFrame()); |
| navigation_simulator->Start(); |
| navigation_simulator->Commit(); |
| |
| ResourceDataUpdate(navigation_simulator->GetFinalRenderFrameHost(), |
| ResourceCached::kNotCached, 10); |
| |
| NavigateMainFrame(kNonAdUrl); |
| |
| // We only log histograms if we observed bytes for the page. Verify that the |
| // main frame resource was properly tracked and attributed. |
| histogram_tester().ExpectUniqueSample( |
| "PageLoad.Clients.Ads.FrameCounts.AdFrames.Total", 0, 1); |
| |
| // Verify that this histogram is also recorded for the Visible and NonVisible |
| // suffixes. |
| histogram_tester().ExpectTotalCount( |
| "PageLoad.Clients.Ads.Visible.FrameCounts.AdFrames.Total", 1); |
| histogram_tester().ExpectTotalCount( |
| "PageLoad.Clients.Ads.NonVisible.FrameCounts.AdFrames.Total", 1); |
| |
| // There are three FrameCounts.AdFrames.Total histograms |
| // recorded for each page load, one for each visibility type. There shouldn't |
| // be any other histograms for a page with no ad resources. |
| EXPECT_EQ(3u, histogram_tester() |
| .GetTotalCountsForPrefix("PageLoad.Clients.Ads.") |
| .size()); |
| EXPECT_EQ(0u, test_ukm_recorder() |
| .GetEntriesByName(ukm::builders::AdFrameLoad::kEntryName) |
| .size()); |
| } |
| |
| // Make sure that ads histograms aren't recorded if the tracker never commits |
| // (see https://crbug.com/723219). |
| TEST_F(AdsPageLoadMetricsObserverTest, NoHistogramWithoutCommit) { |
| { |
| // Once the metrics observer has the GlobalRequestID, throttle. |
| content::TestNavigationThrottleInserter throttle_inserter( |
| web_contents(), |
| base::BindRepeating(&ResourceLoadingCancellingThrottle::Create)); |
| |
| // Start main-frame navigation. The commit will defer after calling |
| // WillProcessNavigationResponse, it will load a resource, and then the |
| // throttle will cancel the commit. |
| SimulateNavigateAndCommit(GURL(kNonAdUrl), main_rfh()); |
| } |
| |
| // Force navigation to a new page to make sure OnComplete() runs for the |
| // previous failed navigation. |
| NavigateMainFrame(kNonAdUrl); |
| |
| // There shouldn't be any histograms for an aborted main frame. |
| EXPECT_EQ(0u, histogram_tester() |
| .GetTotalCountsForPrefix("PageLoad.Clients.Ads.") |
| .size()); |
| EXPECT_EQ(0u, test_ukm_recorder() |
| .GetEntriesByName(ukm::builders::AdFrameLoad::kEntryName) |
| .size()); |
| } |
| |
| // Frames that are disallowed (and filtered) by the subresource filter should |
| // not be counted. |
| TEST_F(AdsPageLoadMetricsObserverTest, FilterAds_DoNotLogMetrics) { |
| ConfigureAsSubresourceFilterOnlyURL(GURL(kNonAdUrl)); |
| NavigateMainFrame(kNonAdUrl); |
| |
| ResourceDataUpdate(main_rfh(), ResourceCached::kNotCached, 10, |
| "" /* mime_type */, false /* is_ad_resource */); |
| |
| RenderFrameHost* subframe = |
| RenderFrameHostTester::For(main_rfh())->AppendChild("foo"); |
| std::unique_ptr<NavigationSimulator> simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(kDefaultDisallowedUrl), |
| subframe); |
| ResourceDataUpdate(subframe, ResourceCached::kNotCached, 10, |
| "" /* mime_type */, true /* is_ad_resource */); |
| simulator->Commit(); |
| |
| EXPECT_NE(content::NavigationThrottle::PROCEED, |
| simulator->GetLastThrottleCheckResult()); |
| |
| NavigateMainFrame(kNonAdUrl); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("FrameCounts.AdFrames.Total"), 0); |
| } |
| |
| // Per-frame histograms recorded when root ad frame is destroyed. |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| FrameDestroyed_PerFrameHistogramsLogged) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| RenderFrameHost* child_ad_frame = CreateAndNavigateSubFrame(kAdUrl, ad_frame); |
| |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| // Add some data to the ad frame so it gets reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(child_ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Just delete the child frame this time. |
| content::RenderFrameHostTester::For(child_ad_frame)->Detach(); |
| |
| // Verify per-frame histograms not recorded. |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Bytes.AdFrames.PerFrame.Total2"), 0); |
| |
| // Delete the root ad frame. |
| content::RenderFrameHostTester::For(ad_frame)->Detach(); |
| |
| // Verify per-frame histograms are recorded. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Bytes.AdFrames.PerFrame.Total2"), 20, 1); |
| |
| // Verify page totals not reported yet. |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("FrameCounts.AdFrames.Total"), 0); |
| |
| NavigateMainFrame(kNonAdUrl); |
| |
| // Verify histograms are logged correctly for the whole page. |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 20}}, |
| 0 /* non_ad_cached_kb */, 10 /* non_ad_uncached_kb */); |
| } |
| |
| // Tests that a non ad frame that is deleted does not cause any unspecified |
| // behavior (see https://crbug.com/973954). |
| TEST_F(AdsPageLoadMetricsObserverTest, NonAdFrameDestroyed_FrameDeleted) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* vanilla_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| content::RenderFrameHostTester::For(vanilla_frame)->Detach(); |
| |
| NavigateMainFrame(kNonAdUrl); |
| } |
| |
| // Tests that main frame ad bytes are recorded correctly. |
| TEST_F(AdsPageLoadMetricsObserverTest, MainFrameAdBytesRecorded) { |
| NavigateMainFrame(kNonAdUrl); |
| |
| ResourceDataUpdate(main_rfh(), ResourceCached::kNotCached, 10, |
| "" /* mime_type */, true /* is_ad_resource */); |
| ResourceDataUpdate(main_rfh(), ResourceCached::kCachedHttp, 10, |
| "" /* mime_type */, true /* is_ad_resource */); |
| |
| RenderFrameHost* subframe = |
| RenderFrameHostTester::For(main_rfh())->AppendChild("foo"); |
| std::unique_ptr<NavigationSimulator> simulator = |
| NavigationSimulator::CreateRendererInitiated(GURL(kDefaultDisallowedUrl), |
| subframe); |
| ResourceDataUpdate(subframe, ResourceCached::kNotCached, 10, |
| "" /* mime_type */, true /* is_ad_resource */); |
| ResourceDataUpdate(subframe, ResourceCached::kCachedHttp, 10, |
| "" /* mime_type */, true /* is_ad_resource */); |
| simulator->Commit(); |
| |
| NavigateMainFrame(kNonAdUrl); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Bytes.MainFrame.Ads.Total2"), 20, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Bytes.MainFrame.Ads.Network"), 10, 1); |
| |
| // Verify page total for network bytes. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Resources.Bytes.Ads2"), 20, 1); |
| } |
| |
| // Tests that memory cache ad bytes are recorded correctly. |
| TEST_F(AdsPageLoadMetricsObserverTest, MemoryCacheAdBytesRecorded) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| ResourceDataUpdate(frame1, ResourceCached::kCachedMemory, 10); |
| ResourceDataUpdate(frame2, ResourceCached::kCachedMemory, 10); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{10, 0}}, |
| 10 /* non_ad_cached_kb */, 10 /* non_ad_uncached_kb */); |
| } |
| |
| // UKM metrics for ad page load are recorded correctly. |
| TEST_F(AdsPageLoadMetricsObserverTest, AdPageLoadUKM) { |
| ukm::TestAutoSetUkmRecorder ukm_recorder; |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| page_load_metrics::mojom::PageLoadTiming timing; |
| page_load_metrics::InitPageLoadTimingForTest(&timing); |
| timing.navigation_start = base::Time::Now(); |
| timing.response_start = base::TimeDelta::FromSeconds(0); |
| timing.interactive_timing->interactive = base::TimeDelta::FromSeconds(0); |
| PopulateRequiredTimingFields(&timing); |
| TimingUpdate(timing); |
| ResourceDataUpdate( |
| main_rfh(), ResourceCached::kNotCached, 10 /* resource_size_in_kbyte */, |
| "application/javascript" /* mime_type */, false /* is_ad_resource */); |
| ResourceDataUpdate( |
| main_rfh(), ResourceCached::kNotCached, 10 /* resource_size_in_kbyte */, |
| "application/javascript" /* mime_type */, true /* is_ad_resource */); |
| ResourceDataUpdate(main_rfh(), ResourceCached::kNotCached, |
| 10 /* resource_size_in_kbyte */, |
| "video/webm" /* mime_type */, true /* is_ad_resource */); |
| |
| // Update cpu timings. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| OnCpuTimingUpdate(main_rfh(), base::TimeDelta::FromMilliseconds(500)); |
| NavigateMainFrame(kNonAdUrl); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdPageLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| |
| EXPECT_EQ(*test_ukm_recorder().GetEntryMetric( |
| entries.front(), ukm::builders::AdPageLoad::kTotalBytesName), |
| 30); |
| EXPECT_EQ(*test_ukm_recorder().GetEntryMetric( |
| entries.front(), ukm::builders::AdPageLoad::kAdBytesName), |
| 20); |
| EXPECT_EQ( |
| *test_ukm_recorder().GetEntryMetric( |
| entries.front(), ukm::builders::AdPageLoad::kAdJavascriptBytesName), |
| 10); |
| EXPECT_EQ(*test_ukm_recorder().GetEntryMetric( |
| entries.front(), ukm::builders::AdPageLoad::kAdVideoBytesName), |
| 10); |
| EXPECT_GT( |
| *test_ukm_recorder().GetEntryMetric( |
| entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName), |
| 0); |
| EXPECT_GT( |
| *test_ukm_recorder().GetEntryMetric( |
| entries.front(), |
| ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName), |
| 0); |
| EXPECT_EQ(*ukm_recorder.GetEntryMetric( |
| entries.front(), ukm::builders::AdPageLoad::kAdCpuTimeName), |
| 500); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, ZeroBytesZeroCpuUseFrame_NotRecorded) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // We expect frames with no bytes and no CPU usage to be ignored |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("FrameCounts.AdFrames.Total"), 0); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, ZeroBytesNonZeroCpuFrame_Recorded) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| ResourceDataUpdate(main_frame, ResourceCached::kNotCached, 10); |
| |
| // Use CPU but maintain zero bytes in the ad frame |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // We expect the frame to be recorded as it has non-zero CPU usage |
| TestHistograms(histogram_tester(), test_ukm_recorder(), {{0, 0}}, |
| 0 /* non_ad_cached_kb */, 10 /* non_ad_uncached_kb */); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.FullPage.TotalUsage"), 1000, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsWindowed) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it gets reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. Usage 1%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Advance time by twelve seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Do some more work on the ad frame. Usage 5%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Advance time by twelve more seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Do some more work on the ad frame. Usage 8%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Advance time by twelve more seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Perform some updates on ad and non-ad frames. Usage 10%/13%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Advance time by twelve more seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Perform some updates on ad and non-ad frames. Usage 8%/11%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away and check the peak windowed cpu usage. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // 10% is the maximum for the individual ad frame. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowedPercent"), 10, 1); |
| |
| // The peak window started at 12 seconds into the page load |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowStartTime"), 12000, 1); |
| |
| // 13% is the maximum for all frames (including main). |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowedPercent"), 13, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowStartTime"), 12000, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsWindowedActivated) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it gets reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. Usage 1%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Advance time by twelve seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Do some more work on the ad frame. Usage 8%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(2000)); |
| |
| // Advance time by twelve more seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Do some more work on the ad frame. Usage 11%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Set the page activation and advance time by twelve more seconds. |
| TriggerFirstUserActivation(ad_frame); |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Perform some updates on ad and main frames. Usage 13%/16%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Advance time by twelve more seconds. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(12)); |
| |
| // Perform some updates on ad and non-ad frames. Usage 8%/11%. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away and check the peak windowed cpu usage. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // 11% is the maximum before activation for the ad frame. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowedPercent"), 11, 1); |
| |
| // The peak window started at 0 seconds into the page load |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowStartTime"), 0, 1); |
| |
| // 16% is the maximum for all frames (including main), ignores activation. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowedPercent"), 16, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowStartTime"), 12000, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetrics) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* non_ad_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it gets reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Set the main frame as interactive after 2 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000)); |
| |
| // Do some more work on the ad frame. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Do some more work on the main frame. |
| OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away after 4 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // Check the cpu histograms. |
| CheckCpuHistograms("Cpu.FullPage", "", /*pre_tasks=*/500 + 500, |
| /*pre_time=*/2000, /*post_tasks=*/1000 + 500, |
| /*post_time=*/2000); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", /*pre_tasks=*/500, |
| /*pre_time=*/2000, /*post_tasks=*/1000, |
| /*post_time=*/2000); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 1500); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName, |
| 100 * 1500 / 30000); |
| EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_PreActivationName)); |
| EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kTiming_PreActivationForegroundDurationName)); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| TestCpuTimingMetricsStopWhenBackgrounded) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* non_ad_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Set the main frame as interactive after 2 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000)); |
| |
| // Do some more work on the ad frame. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Set the page as hidden after 3.5 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(1500)); |
| OnHidden(); |
| |
| // Do some more work on the main frame, shouldn't count to total. |
| OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away after 4 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(500)); |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // Check the cpu histograms. |
| CheckCpuHistograms("Cpu.FullPage", "", /*pre_tasks=*/500 + 500, |
| /*pre_time=*/2000, /*post_tasks=*/1000, |
| /*post_time=*/1500); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", /*pre_tasks=*/500, |
| /*pre_time=*/2000, /*post_tasks=*/1000, |
| /*post_time=*/1500); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 1500); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName, |
| 100 * 1500 / 30000); |
| EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_PreActivationName)); |
| EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kTiming_PreActivationForegroundDurationName)); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsOnActivation) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* non_ad_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Set the main frame as interactive after 2 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000)); |
| |
| // Do some more work on the ad frame. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Set the frame as interactive after 2.5 seconds |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(500)); |
| TriggerFirstUserActivation(ad_frame); |
| |
| // Do some more work on the main frame. |
| OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Do some more work on the ad frame. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away after 4 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(1500)); |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // Check the cpu histograms. |
| CheckCpuHistograms("Cpu.FullPage", "", /*pre_tasks=*/500 + 500, |
| /*pre_time=*/2000, /*post_tasks=*/1000 + 500, |
| /*post_time=*/2000); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", |
| /*pre_tasks=*/500 + 500, /*pre_time=*/2500, |
| /*post_tasks=*/500, |
| /*post_time=*/1500); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 1500); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName, |
| 100 * 1000 / 30000); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_PreActivationName, |
| 1000); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kTiming_PreActivationForegroundDurationName, |
| 2500); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestNoReportingWhenAlwaysBackgrounded) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* non_ad_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Set the frame as backgrounded, so all updates below shouldn't report. |
| OnHidden(); |
| |
| // Perform some updates on ad and non-ad frames. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Set the main frame as interactive after 2 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000)); |
| |
| // Do some more work on the ad frame. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Do some more work on the main frame. |
| OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away after 4 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // Ensure that all metrics are zero. |
| CheckCpuHistograms("Cpu.FullPage", "", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", 0, 0, 0, 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowedPercent"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowStartTime"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowedPercent"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowStartTime"), 0); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 0); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName, 0); |
| EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_PreActivationName)); |
| EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kTiming_PreActivationForegroundDurationName)); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsNoInteractive) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* non_ad_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away after 2 seconds. |
| AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000)); |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // Check the cpu histograms. |
| CheckCpuHistograms("Cpu.FullPage", "", /*pre_tasks=*/500 + 500, |
| /*pre_time=*/2000, /*post_tasks=*/0, /*post_time=*/0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", /*pre_tasks=*/500, |
| /*pre_time=*/2000, /*post_tasks=*/0, /*post_time=*/0); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 500); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName, |
| 100 * 500 / 30000); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsShortTimeframes) { |
| OverrideVisibilityTrackerWithMockClock(); |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* non_ad_frame = |
| CreateAndNavigateSubFrame(kNonAdUrl, main_frame); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| // Perform some updates on ad and non-ad frames. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Set interactive after 1 microsecond. |
| AdvancePageDuration(base::TimeDelta::FromMicroseconds(1)); |
| OnMainFrameInteractive(base::TimeDelta::FromMicroseconds(1)); |
| |
| // Do some more work on the ad frame. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000)); |
| |
| // Do some more work on the main frame. |
| OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500)); |
| |
| // Navigate away after 2 microseconds. |
| AdvancePageDuration(base::TimeDelta::FromMicroseconds(1)); |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| // Make sure there are no numbers reported, as the timeframes are too short. |
| CheckCpuHistograms("Cpu.FullPage", "", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", 0, 0, 0, 0); |
| CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", 0, 0, 0, 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowedPercent"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.AdFrames.PerFrame.PeakWindowStartTime"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowedPercent"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("Cpu.FullPage.PeakWindowStartTime"), 0); |
| |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 1500); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName, |
| 100 * 1500 / 30000); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, AdFrameLoadTiming) { |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Load bytes in frame to record ukm event. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10); |
| |
| page_load_metrics::mojom::PageLoadTiming subframe_timing; |
| page_load_metrics::InitPageLoadTimingForTest(&subframe_timing); |
| subframe_timing.navigation_start = base::Time::FromDoubleT(2); |
| subframe_timing.paint_timing->first_contentful_paint = |
| base::TimeDelta::FromMilliseconds(0); |
| subframe_timing.interactive_timing->interactive = |
| base::TimeDelta::FromMilliseconds(0); |
| PopulateRequiredTimingFields(&subframe_timing); |
| tester()->SimulateTimingUpdate(subframe_timing, ad_frame); |
| |
| // Send an updated timing that should be recorded. |
| page_load_metrics::InitPageLoadTimingForTest(&subframe_timing); |
| subframe_timing.navigation_start = base::Time::FromDoubleT(2); |
| subframe_timing.paint_timing->first_contentful_paint = |
| base::TimeDelta::FromMilliseconds(5); |
| subframe_timing.interactive_timing->interactive = |
| base::TimeDelta::FromMilliseconds(20); |
| PopulateRequiredTimingFields(&subframe_timing); |
| tester()->SimulateTimingUpdate(subframe_timing, ad_frame); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::AdFrameLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), |
| ukm::builders::AdFrameLoad::kTiming_FirstContentfulPaintName, 5); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries.front(), ukm::builders::AdFrameLoad::kTiming_InteractiveName, 20); |
| } |
| |
| // Tests that even when the intervention is not enabled, we still record the |
| // computed heavy ad types for ad frames |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdFeatureOff_UMARecorded) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kHeavyAdIntervention); |
| OverrideVisibilityTrackerWithMockClock(); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(0 /* network noise */)); |
| |
| RenderFrameHost* ad_frame_none = |
| CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| RenderFrameHost* ad_frame_net = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| RenderFrameHost* ad_frame_cpu = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| RenderFrameHost* ad_frame_total_cpu = |
| CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Load some bytes in each frame so they are considered ad iframes. |
| ResourceDataUpdate(ad_frame_none, ResourceCached::kNotCached, 1); |
| ResourceDataUpdate(ad_frame_net, ResourceCached::kNotCached, 1); |
| ResourceDataUpdate(ad_frame_cpu, ResourceCached::kNotCached, 1); |
| ResourceDataUpdate(ad_frame_total_cpu, ResourceCached::kNotCached, 1); |
| |
| // Make three of the ad frames hit thresholds for heavy ads. |
| ResourceDataUpdate(ad_frame_net, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024)); |
| OnCpuTimingUpdate( |
| ad_frame_cpu, |
| base::TimeDelta::FromMilliseconds( |
| heavy_ad_thresholds::kMaxPeakWindowedPercent * 30000 / 100)); |
| UseCpuTimeUnderThreshold( |
| ad_frame_total_cpu, |
| base::TimeDelta::FromMilliseconds(heavy_ad_thresholds::kMaxCpuTime)); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.ComputedType2"), 4); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kNone, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kPeakCpu, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kTotalCpu, 1); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), 4); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), |
| FrameData::HeavyAdStatus::kNone, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), |
| FrameData::HeavyAdStatus::kPeakCpu, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), |
| FrameData::HeavyAdStatus::kTotalCpu, 1); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdNetworkUsage_InterventionFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(0 /* network noise */)); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Load just under the threshold amount of bytes. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) - 1); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| |
| // Load enough bytes to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 2); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| HeavyAdNetworkUsageWithNoise_InterventionFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(2048 /* network noise */)); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Load just under the threshold amount of bytes with noise included. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| |
| // Load enough bytes to meet the noised threshold criteria. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 1); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.DisallowedByBlocklist"), false, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| HeavyAdNetworkUsageLessThanNoisedThreshold_NotFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(2048 /* network noise */)); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Load network bytes that trip the heavy ad threshold without noise. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| heavy_ad_thresholds::kMaxNetworkBytes / 1024 + 1); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), |
| FrameData::HeavyAdStatus::kNone, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| HeavyAdNetworkUsageLessThanNoisedThreshold_CpuTriggers) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| OverrideVisibilityTrackerWithMockClock(); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(2048 /* network noise */)); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Load network bytes that trip the heavy ad threshold without noise. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| heavy_ad_thresholds::kMaxNetworkBytes / 1024 + 1); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| |
| // Verify the frame can still trip the CPU threshold. |
| UseCpuTimeUnderThreshold(ad_frame, base::TimeDelta::FromMilliseconds( |
| heavy_ad_thresholds::kMaxCpuTime + 1)); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kTotalCpu, 1); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), |
| FrameData::HeavyAdStatus::kTotalCpu, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdTotalCpuUsage_InterventionFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| OverrideVisibilityTrackerWithMockClock(); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 1); |
| |
| // Use just under the threshold amount of CPU.Needs to spread across enough |
| // windows to not trigger peak threshold. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(30)); |
| UseCpuTimeUnderThreshold(ad_frame, base::TimeDelta::FromMilliseconds( |
| heavy_ad_thresholds::kMaxCpuTime - 1)); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| AdvancePageDuration(base::TimeDelta::FromSeconds(30)); |
| // Use enough CPU to trigger the intervention. |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1)); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kTotalCpu, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdPeakCpuUsage_InterventionFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| OverrideVisibilityTrackerWithMockClock(); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add some data to the ad frame so it get reported. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 1); |
| |
| // Use just under the peak threshold amount of CPU. |
| OnCpuTimingUpdate( |
| ad_frame, |
| base::TimeDelta::FromMilliseconds( |
| heavy_ad_thresholds::kMaxPeakWindowedPercent * 30000 / 100 - 1)); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| |
| // Use enough CPU to trigger the intervention. |
| AdvancePageDuration(base::TimeDelta::FromSeconds(10)); |
| OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1)); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kPeakCpu, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdFeatureDisabled_NotFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kHeavyAdIntervention); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add enough data to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (kMaxHeavyAdNetworkBytes / 1024) + 1); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.DisallowedByBlocklist"), 0); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| HeavyAdWithUserGesture_NotConsideredHeavy) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Give the frame a user activation before the threshold would be hit. |
| TriggerFirstUserActivation(ad_frame); |
| |
| // Add enough data to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| |
| // Navigate again to trigger histograms. |
| NavigateFrame(kNonAdUrl, main_frame); |
| |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.ComputedType2"), |
| FrameData::HeavyAdStatus::kNone, 1); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdBlocklistFull_NotFired) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| |
| // Five interventions are allowed to occur, per origin per day. Add five |
| // entries to the blocklist. |
| for (int i = 0; i < 5; i++) |
| blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add enough data to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 0); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, |
| HeavyAdBlocklistDisabled_InterventionNotBlocked) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures({features::kHeavyAdIntervention}, |
| {features::kHeavyAdBlocklist}); |
| |
| // Fill up the blocklist to verify the blocklist logic is correctly ignored |
| // when disabled. |
| for (int i = 0; i < 5; i++) |
| blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(0 /* network noise */)); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add enough data to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1); |
| |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.InterventionType2"), 1); |
| |
| // This histogram should not be recorded when the blocklist is disabled. |
| histogram_tester().ExpectTotalCount( |
| SuffixedHistogram("HeavyAds.DisallowedByBlocklist"), 0); |
| } |
| |
| TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdBlocklist_InterventionReported) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kHeavyAdIntervention); |
| |
| // Five interventions are allowed to occur, per origin per day. Add four |
| // entries to the blocklist. |
| for (int i = 0; i < 4; i++) |
| blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0); |
| |
| RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl); |
| |
| OverrideHeavyAdNoiseProvider( |
| std::make_unique<MockNoiseProvider>(0 /* network noise */)); |
| RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add enough data to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1); |
| |
| // Verify the intervention triggered. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.DisallowedByBlocklist"), false, 1); |
| |
| // Verify the blocklist blocks the next intervention. |
| ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame); |
| |
| // Add enough data to trigger the intervention. |
| ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, |
| (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1); |
| |
| // Verify the intervention did not occur again. |
| histogram_tester().ExpectUniqueSample( |
| SuffixedHistogram("HeavyAds.InterventionType2"), |
| FrameData::HeavyAdStatus::kNetwork, 1); |
| histogram_tester().ExpectBucketCount( |
| SuffixedHistogram("HeavyAds.DisallowedByBlocklist"), true, 1); |
| } |