blob: 29142e68e9ddcb99429173debd7365f5f7414bfd [file] [log] [blame]
// 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 <string>
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/subprocess_metrics_provider.h"
#include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.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/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/subresource_filter/content/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/mojom/subresource_filter.mojom.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/test_navigation_observer.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/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/download/download_stats.h"
#include "url/gurl.h"
#include "url/url_constants.h"
namespace {
const char kCrossOriginHistogramId[] =
"PageLoad.Clients.Ads.Google.FrameCounts.AdFrames.PerFrame.OriginStatus";
enum class Origin {
kNavigation,
kAnchorAttribute,
};
std::ostream& operator<<(std::ostream& os, Origin origin) {
switch (origin) {
case Origin::kNavigation:
return os << "Navigation";
case Origin::kAnchorAttribute:
return os << "AnchorAttribute";
}
}
enum class SandboxOption {
kNoSandbox,
kSandboxDisallowDownloads,
kSandboxAllowDownloads,
};
std::ostream& operator<<(std::ostream& os, SandboxOption sandbox_option) {
switch (sandbox_option) {
case SandboxOption::kNoSandbox:
return os << "NoSandbox";
case SandboxOption::kSandboxDisallowDownloads:
return os << "SandboxDisallowDownloads";
case SandboxOption::kSandboxAllowDownloads:
return os << "SandboxAllowDownloads";
}
}
} // namespace
class AdsPageLoadMetricsObserverBrowserTest
: public subresource_filter::SubresourceFilterBrowserTest {
public:
AdsPageLoadMetricsObserverBrowserTest()
: subresource_filter::SubresourceFilterBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(features::kAdsFeature);
}
~AdsPageLoadMetricsObserverBrowserTest() override {}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserverBrowserTest);
};
// Test that an embedded ad is same origin.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
OriginStatusMetricEmbedded) {
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/ads_observer/srcdoc_embedded_ad.html"));
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
histogram_tester.ExpectUniqueSample(
kCrossOriginHistogramId,
AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
}
// Test that an empty embedded ad isn't reported at all.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
OriginStatusMetricEmbeddedEmpty) {
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/ads_observer/srcdoc_embedded_ad_empty.html"));
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
histogram_tester.ExpectTotalCount(kCrossOriginHistogramId, 0);
}
// Test that an ad with the same origin as the main page is same origin.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
OriginStatusMetricSame) {
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/ads_observer/same_origin_ad.html"));
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
histogram_tester.ExpectUniqueSample(
kCrossOriginHistogramId,
AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
}
// Test that an ad with a different origin as the main page is cross origin.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
OriginStatusMetricCross) {
// Note: Cannot navigate cross-origin without dynamically generating the URL.
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/iframe_blank.html"));
// Note that the initial iframe is not an ad, so the metric doesn't observe
// it initially as same origin. However, on re-navigating to a cross
// origin site that has an ad at its origin, the ad on that page is cross
// origin from the original page.
NavigateIframeToURL(web_contents(), "test",
embedded_test_server()->GetURL(
"a.com", "/ads_observer/same_origin_ad.html"));
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
histogram_tester.ExpectUniqueSample(
kCrossOriginHistogramId,
AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
}
// 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,
DocOverwritesNavigation) {
content::DOMMessageQueue msg_queue;
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/ads_observer/docwrite_provisional_frame.html"));
std::string status;
EXPECT_TRUE(msg_queue.WaitForMessage(&status));
EXPECT_EQ("\"loaded\"", status);
// Navigate away to force the histogram recording.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
histogram_tester.ExpectUniqueSample(
"PageLoad.Clients.Ads.Google.FrameCounts.AnyParentFrame.AdFrames", 1, 1);
histogram_tester.ExpectUniqueSample(
"PageLoad.Clients.Ads.Google.Bytes.AdFrames.Aggregate.Total",
0 /* < 1 KB */, 1);
}
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
SubresourceFilter) {
ResetConfiguration(subresource_filter::Configuration(
subresource_filter::mojom::ActivationLevel::kDryRun,
subresource_filter::ActivationScope::ALL_SITES));
base::HistogramTester histogram_tester;
// cross_site_iframe_factory loads URLs like:
// http://b.com:40919/cross_site_iframe_factory.html?b()
SetRulesetToDisallowURLsWithPathSuffix("b()");
const GURL main_url(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b,b,c,d)"));
ui_test_utils::NavigateToURL(browser(), main_url);
// Navigate away to force the histogram recording.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
histogram_tester.ExpectUniqueSample(
"PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
"AdFrames",
2, 1);
histogram_tester.ExpectUniqueSample(
"PageLoad.Clients.Ads.All.FrameCounts.AnyParentFrame.AdFrames", 2, 1);
}
class AdsPageLoadMetricsTestWaiter
: public page_load_metrics::PageLoadMetricsTestWaiter {
public:
explicit AdsPageLoadMetricsTestWaiter(content::WebContents* web_contents)
: page_load_metrics::PageLoadMetricsTestWaiter(web_contents) {}
void AddMinimumAdResourceExpectation(int num_ad_resources) {
expected_minimum_num_ad_resources_ = num_ad_resources;
}
protected:
bool ExpectationsSatisfied() const override {
int num_ad_resources = 0;
for (auto& kv : page_resources_) {
if (kv.second->reported_as_ad_resource)
num_ad_resources++;
}
return num_ad_resources >= expected_minimum_num_ad_resources_ &&
PageLoadMetricsTestWaiter::ExpectationsSatisfied();
};
private:
int expected_minimum_num_ad_resources_ = 0;
};
class AdsPageLoadMetricsObserverResourceBrowserTest
: public subresource_filter::SubresourceFilterBrowserTest {
public:
AdsPageLoadMetricsObserverResourceBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(features::kAdsFeature);
}
~AdsPageLoadMetricsObserverResourceBrowserTest() override {}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
SetRulesetWithRules(
{subresource_filter::testing::CreateSuffixRule("ad_script.js"),
subresource_filter::testing::CreateSuffixRule("ad_script_2.js"),
subresource_filter::testing::CreateSuffixRule("disallow.zip")});
}
void OpenLinkInFrame(const content::ToRenderFrameHost& adapter,
const std::string& link_id,
bool gesture) {
std::string open_link_script = base::StringPrintf(
R"(
var evt = document.createEvent("MouseEvent");
evt.initMouseEvent('click', true, true);
document.getElementById('%s').dispatchEvent(evt);
)",
link_id.c_str());
if (gesture) {
EXPECT_TRUE(ExecuteScript(adapter, open_link_script));
} else {
EXPECT_TRUE(ExecuteScriptWithoutUserGesture(adapter, open_link_script));
}
}
protected:
std::unique_ptr<AdsPageLoadMetricsTestWaiter>
CreateAdsPageLoadMetricsTestWaiter() {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
return std::make_unique<AdsPageLoadMetricsTestWaiter>(web_contents);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
ReceivedAdResources) {
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
// Two subresources should have been reported as ads.
waiter->AddMinimumAdResourceExpectation(2);
waiter->Wait();
}
// Main resources for adframes are counted as ad resources.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
ReceivedMainResourceAds) {
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
contents->GetMainFrame()->ExecuteJavaScriptForTests(
base::ASCIIToUTF16("createAdFrame('frame_factory.html', '');"));
// Two pages subresources should have been reported as ad. The iframe resource
// should also be reported as an ad.
waiter->AddMinimumAdResourceExpectation(5);
waiter->Wait();
}
// Subframe navigations report ad resources correctly.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
ReceivedSubframeNavigationAds) {
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
contents->GetMainFrame()->ExecuteJavaScriptForTests(
base::ASCIIToUTF16("createAdFrame('frame_factory.html', 'test');"));
waiter->AddMinimumAdResourceExpectation(5);
waiter->Wait();
NavigateIframeToURL(
web_contents(), "test",
embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
// The new subframe as well as two of its page subresources should be reported
// as an ad.
waiter->AddMinimumAdResourceExpectation(8);
waiter->Wait();
}
// Verify that per-resource metrics are recorded correctly.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
ReceivedAdResourceMetrics) {
base::HistogramTester histogram_tester;
const char kHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n";
auto main_html_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/mock_page.html",
true /*relative_url_is_prefix*/);
auto ad_script_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/ad_script.js",
true /*relative_url_is_prefix*/);
auto iframe_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/iframe.html",
true /*relative_url_is_prefix*/);
auto vanilla_script_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/vanilla_script.js",
true /*relative_url_is_prefix*/);
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
main_html_response->WaitForRequest();
main_html_response->Send(kHttpResponseHeader);
main_html_response->Send(
"<html><body></body><script src=\"ad_script.js\"></script></html>");
main_html_response->Done();
ad_script_response->WaitForRequest();
ad_script_response->Send(kHttpResponseHeader);
ad_script_response->Send(
"var iframe = document.createElement(\"iframe\");"
"iframe.src =\"iframe.html\";"
"document.body.appendChild(iframe);");
ad_script_response->Send(std::string(1000, ' '));
ad_script_response->Done();
iframe_response->WaitForRequest();
iframe_response->Send(kHttpResponseHeader);
iframe_response->Send("<html><script src=\"vanilla_script.js\"></script>");
iframe_response->Send(std::string(2000, ' '));
iframe_response->Send("</html>");
iframe_response->Done();
vanilla_script_response->WaitForRequest();
vanilla_script_response->Send(kHttpResponseHeader);
vanilla_script_response->Send(std::string(1024, ' '));
waiter->AddMinimumNetworkBytesExpectation(4000);
waiter->Wait();
// Verify correct numbers of resources are recorded.
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 1);
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Network.Mainframe.AdResource", 1);
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Network.Subframe.AdResource", 1);
// Verify unfinished resource not yet recorded.
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Network.Subframe.VanillaResource", 0);
// Close all tabs instead of navigating as the embedded_test_server will
// hang waiting for loads to finish when we have an unfinished
// ControlledHttpReseonse.
browser()->tab_strip_model()->CloseAllTabs();
// Verify unfinished resource recorded when page is destroyed.
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Network.Subframe.AdResource", 2);
histogram_tester.ExpectBucketCount(
"PageLoad.Clients.Ads.Resources.Bytes.Total", 4, 1);
// We have received 4 KB of ads and 1 KB of toplevel ads.
histogram_tester.ExpectBucketCount("PageLoad.Clients.Ads.Resources.Bytes.Ads",
4, 1);
histogram_tester.ExpectBucketCount(
"PageLoad.Clients.Ads.Resources.Bytes.TopLevelAds", 1, 1);
// 4 resources loaded, one unfinished.
histogram_tester.ExpectBucketCount(
"PageLoad.Clients.Ads.Resources.Bytes.Unfinished", 1, 1);
}
// Verify that per-resource metrics are reported for cached resources and
// resources loaded by the network.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
RecordedCacheResourceMetrics) {
base::HistogramTester histogram_tester;
SetRulesetWithRules(
{subresource_filter::testing::CreateSuffixRule("create_frame.js")});
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
// Wait for the favicon to be fetched.
waiter->AddMinimumCompleteResourcesExpectation(2);
waiter->Wait();
// All resources should have been loaded by network.
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 2);
// Open a new tab and navigate so that resources are fetched via the disk
// cache. Navigating to the same URL in the same tab triggers a refresh which
// will not check the disk cache.
ui_test_utils::NavigateToURLWithDisposition(
browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
waiter = CreateAdsPageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
// Wait for the resource to be fetched.
waiter->AddMinimumCompleteResourcesExpectation(1);
waiter->Wait();
// Resource should be recorded as loaded from the cache. Favicon not
// fetched this time.
histogram_tester.ExpectTotalCount(
"Ads.ResourceUsage.Size.Cache.Mainframe.VanillaResource", 1);
}
// Verify that Mime type metrics are recorded correctly.
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
RecordedMimeMetrics) {
base::HistogramTester histogram_tester;
ukm::TestAutoSetUkmRecorder ukm_recorder;
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreateAdsPageLoadMetricsTestWaiter();
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
GURL url = embedded_test_server()->GetURL("foo.com", "/frame_factory.html");
ui_test_utils::NavigateToURL(browser(), url);
contents->GetMainFrame()->ExecuteJavaScriptForTests(
base::ASCIIToUTF16("createAdFrame('multiple_mimes.html', 'test');"));
waiter->AddMinimumAdResourceExpectation(8);
waiter->Wait();
// Close all tabs to log metrics, as the video resource request is incomplete.
browser()->tab_strip_model()->CloseAllTabs();
histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.HTML",
1);
histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.CSS",
1);
histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.JS",
3);
// Note: png and video/webm mime types are not set explicitly by the
// embedded_test_server.
histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Image",
1);
histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Video",
1);
histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Other",
1);
// Verify UKM Metrics recorded.
auto entries =
ukm_recorder.GetEntriesByName(ukm::builders::AdPageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
ukm_recorder.ExpectEntrySourceHasUrl(entries.front(), url);
EXPECT_GT(*ukm_recorder.GetEntryMetric(
entries.front(), ukm::builders::AdPageLoad::kAdBytesName),
0);
EXPECT_GT(
*ukm_recorder.GetEntryMetric(
entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName),
0);
// TTI is not reached by this page and thus should not have this recorded.
EXPECT_FALSE(ukm_recorder.EntryHasMetric(
entries.front(),
ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName));
EXPECT_GT(
*ukm_recorder.GetEntryMetric(
entries.front(), ukm::builders::AdPageLoad::kAdJavascriptBytesName),
0);
EXPECT_GT(*ukm_recorder.GetEntryMetric(
entries.front(), ukm::builders::AdPageLoad::kAdVideoBytesName),
0);
}
// Download gets blocked when LoadPolicy is DISALLOW for the navigation
// to download
IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
DownloadBlocked) {
ResetConfiguration(subresource_filter::Configuration(
subresource_filter::mojom::ActivationLevel::kEnabled,
subresource_filter::ActivationScope::ALL_SITES));
base::HistogramTester histogram_tester;
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
std::string host_name = "foo.com";
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(host_name, "/frame_factory.html"));
content::TestNavigationObserver navigation_observer(web_contents());
contents->GetMainFrame()->ExecuteJavaScriptForTests(
base::ASCIIToUTF16("createFrame('download.html', 'test');"));
navigation_observer.Wait();
content::RenderFrameHost* rfh = content::FrameMatchingPredicate(
web_contents(), base::BindRepeating(&content::FrameMatchesName, "test"));
OpenLinkInFrame(rfh, "blocked_nav_download_id", false /* gesture*/);
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester.ExpectTotalCount("Download.FrameGesture", 0);
}
class MainFrameDownloadHasGestureBrowserTest
: public AdsPageLoadMetricsObserverResourceBrowserTest,
public ::testing::WithParamInterface<
std::tuple<Origin, bool /* gesture */>> {};
// Main frame download events are reported correctly.
IN_PROC_BROWSER_TEST_P(MainFrameDownloadHasGestureBrowserTest, Download) {
Origin origin;
bool gesture;
std::tie(origin, gesture) = GetParam();
SCOPED_TRACE(::testing::Message() << "origin = " << origin << ", "
<< "gesture = " << gesture);
base::HistogramTester histogram_tester;
std::unique_ptr<content::DownloadTestObserver> download_observer(
new content::DownloadTestObserverTerminal(
content::BrowserContext::GetDownloadManager(browser()->profile()),
1 /* wait_count */,
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("foo.com", "/download.html"));
std::string link_id =
origin == Origin::kNavigation ? "nav_download_id" : "anchor_download_id";
OpenLinkInFrame(web_contents(), link_id, gesture);
download_observer->WaitForFinished();
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester.ExpectUniqueSample("Download.MainFrame.HasGesture", gesture,
1 /* expected_count */);
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
MainFrameDownloadHasGestureBrowserTest,
::testing::Combine(::testing::Values(Origin::kNavigation,
Origin::kAnchorAttribute),
::testing::Bool()));
class SubframeDownloadSandboxOriginAdGestureBrowserTest
: public AdsPageLoadMetricsObserverResourceBrowserTest,
public ::testing::WithParamInterface<
std::tuple<Origin,
bool /* enable_blocking_downloads_in_sandbox */,
SandboxOption,
bool /* cross_origin */,
bool /* ad_frame */,
bool /* gesture */>> {
void SetUpCommandLine(base::CommandLine* command_line) override {
bool enable_blocking_downloads_in_sandbox;
std::tie(std::ignore, enable_blocking_downloads_in_sandbox, std::ignore,
std::ignore, std::ignore, std::ignore) = GetParam();
std::string cmd = enable_blocking_downloads_in_sandbox
? "enable-blink-features"
: "disable-blink-features";
command_line->AppendSwitchASCII(cmd, "BlockingDownloadsInSandbox");
}
};
// Subframe download events are reported correctly.
IN_PROC_BROWSER_TEST_P(SubframeDownloadSandboxOriginAdGestureBrowserTest,
Download) {
Origin origin;
bool enable_blocking_downloads_in_sandbox;
SandboxOption sandbox_option;
bool cross_origin;
bool ad_frame;
bool gesture;
std::tie(origin, enable_blocking_downloads_in_sandbox, sandbox_option,
cross_origin, ad_frame, gesture) = GetParam();
SCOPED_TRACE(::testing::Message()
<< "origin = " << origin << ", "
<< "enable_blocking_downloads_in_sandbox = "
<< enable_blocking_downloads_in_sandbox << ", "
<< "sandbox_option = " << sandbox_option << ", "
<< "cross_origin = " << cross_origin << ", "
<< "ad_frame = " << ad_frame << ", "
<< "gesture = " << gesture);
base::HistogramTester histogram_tester;
size_t expected_download_count =
enable_blocking_downloads_in_sandbox &&
sandbox_option == SandboxOption::kSandboxDisallowDownloads
? 0
: 1;
std::unique_ptr<content::DownloadTestObserver> download_observer(
new content::DownloadTestObserverTerminal(
content::BrowserContext::GetDownloadManager(browser()->profile()),
expected_download_count /* wait_count */,
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/ad_tagging");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
std::string host_name = "foo.com";
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(host_name, "/frame_factory.html"));
std::string link_id =
origin == Origin::kNavigation ? "nav_download_id" : "anchor_download_id";
const char* method = ad_frame ? "createAdFrame" : "createFrame";
std::string url =
embedded_test_server()
->GetURL(cross_origin ? "bar.com" : host_name, "/download.html")
.spec();
const char* id = "test";
const char* sandbox_param =
sandbox_option == SandboxOption::kNoSandbox
? "undefined"
: sandbox_option == SandboxOption::kSandboxDisallowDownloads
? "'allow-scripts allow-same-origin'"
: "'allow-scripts allow-same-origin allow-downloads'";
content::TestNavigationObserver navigation_observer(web_contents());
std::string script = base::StringPrintf("%s('%s','%s',%s);", method,
url.c_str(), id, sandbox_param);
contents->GetMainFrame()->ExecuteJavaScriptForTests(
base::ASCIIToUTF16(script));
navigation_observer.Wait();
content::RenderFrameHost* rfh = content::FrameMatchingPredicate(
web_contents(), base::BindRepeating(&content::FrameMatchesName, id));
OpenLinkInFrame(rfh, link_id, gesture);
download_observer->WaitForFinished();
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
if (expected_download_count == 0) {
histogram_tester.ExpectTotalCount(
"Download.Subframe.SandboxOriginAdGesture", 0 /* expected_count */);
return;
}
unsigned expected_value = 0;
bool expected_sandbox_bit =
enable_blocking_downloads_in_sandbox
? sandbox_option == SandboxOption::kSandboxDisallowDownloads
: sandbox_option != SandboxOption::kNoSandbox;
if (expected_sandbox_bit)
expected_value |= blink::DownloadStats::kSandboxBit;
if (cross_origin)
expected_value |= blink::DownloadStats::kCrossOriginBit;
if (ad_frame)
expected_value |= blink::DownloadStats::kAdBit;
if (gesture)
expected_value |= blink::DownloadStats::kGestureBit;
histogram_tester.ExpectUniqueSample(
"Download.Subframe.SandboxOriginAdGesture", expected_value,
1 /* expected_count */);
if (expected_sandbox_bit && origin == Origin::kNavigation) {
blink::mojom::WebFeature feature =
gesture ? blink::mojom::WebFeature::
kNavigationDownloadInSandboxWithUserGesture
: blink::mojom::WebFeature::
kNavigationDownloadInSandboxWithoutUserGesture;
histogram_tester.ExpectBucketCount(internal::kFeaturesHistogramName,
feature, 1 /* expected_count */);
}
}
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
SubframeDownloadSandboxOriginAdGestureBrowserTest,
::testing::Combine(
::testing::Values(Origin::kNavigation, Origin::kAnchorAttribute),
::testing::Bool(),
::testing::Values(SandboxOption::kNoSandbox,
SandboxOption::kSandboxDisallowDownloads,
SandboxOption::kSandboxAllowDownloads),
::testing::Bool(),
::testing::Bool(),
::testing::Bool()));