blob: a5edb4d124328d46f4006976e28e0ed860347300 [file] [log] [blame]
// Copyright 2018 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/bind.h"
#include "base/callback.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/embedder_support/switches.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/observers/ad_metrics/frame_tree_data.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "components/subresource_filter/content/browser/ad_tagging_browser_test_utils.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
#include "components/ukm/content/source_url_recorder.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/frame/from_ad_state.h"
#include "url/gurl.h"
namespace subresource_filter {
namespace {
using content::RenderFrameHost;
using testing::CreateAllowlistSuffixRule;
using testing::CreateSuffixRule;
// Creates a link element with rel="stylesheet" and href equal to the specified
// url. Returns immediately after adding the element to the DOM.
void AddExternalStylesheet(const content::ToRenderFrameHost& adapter,
const GURL& url) {
std::string script_template = R"(
link = document.createElement("link");
link.rel = "stylesheet";
link.href = $1;
document.head.appendChild(link);
)";
EXPECT_TRUE(content::ExecJs(adapter.render_frame_host(),
content::JsReplace(script_template, url)));
}
class AdTaggingPageLoadMetricsTestWaiter
: public page_load_metrics::PageLoadMetricsTestWaiter {
public:
explicit AdTaggingPageLoadMetricsTestWaiter(
content::WebContents* web_contents)
: page_load_metrics::PageLoadMetricsTestWaiter(web_contents) {}
void AddMinimumCompleteAdResourcesExpectation(int num_ad_resources) {
expected_minimum_complete_ad_resources_ = num_ad_resources;
}
void AddMinimumCompleteVanillaResourcesExpectation(
int num_vanilla_resources) {
expected_minimum_complete_vanilla_resources_ = num_vanilla_resources;
}
int current_complete_ad_resources() { return current_complete_ad_resources_; }
int current_complete_vanilla_resources() {
return current_complete_vanilla_resources_;
}
protected:
bool ExpectationsSatisfied() const override {
return current_complete_ad_resources_ >=
expected_minimum_complete_ad_resources_ &&
current_complete_vanilla_resources_ >=
expected_minimum_complete_vanilla_resources_ &&
PageLoadMetricsTestWaiter::ExpectationsSatisfied();
}
void HandleResourceUpdate(
const page_load_metrics::mojom::ResourceDataUpdatePtr& resource)
override {
// Due to cache-aware loading of font resources, we see two resource loads
// for resources not in the cache. We ignore the first request, which
// results in a cache miss, by using the fact that it will have no received
// data. Note that this only impacts this class's expectations, not those in
// PageLoadMetricsTestWaiter itself.
if (resource->is_complete && resource->received_data_length != 0) {
if (resource->reported_as_ad_resource) {
current_complete_ad_resources_++;
} else {
current_complete_vanilla_resources_++;
}
}
}
private:
int current_complete_ad_resources_ = 0;
int expected_minimum_complete_ad_resources_ = 0;
int current_complete_vanilla_resources_ = 0;
int expected_minimum_complete_vanilla_resources_ = 0;
};
class AdTaggingBrowserTest : public SubresourceFilterBrowserTest {
public:
AdTaggingBrowserTest() : SubresourceFilterBrowserTest() {}
AdTaggingBrowserTest(const AdTaggingBrowserTest&) = delete;
AdTaggingBrowserTest& operator=(const AdTaggingBrowserTest&) = delete;
~AdTaggingBrowserTest() override {}
void SetUpOnMainThread() override {
SubresourceFilterBrowserTest::SetUpOnMainThread();
// For subdocument resources, allowlist rules are always checked.
SetRulesetWithRules({CreateSuffixRule("ad_script.js"),
CreateSuffixRule("ad=true"),
CreateAllowlistSuffixRule("allowed=true")});
}
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
// Creates a frame and doc.writes the frame into it. Returns after
// navigation has completed.
content::RenderFrameHost* CreateDocWrittenFrame(
const content::ToRenderFrameHost& adapter) {
return CreateDocWrittenFrameImpl(adapter, false /* ad_script */);
}
// Creates a frame and doc.writes the frame into it. The script creating the
// frame is an ad script. Returns after navigation has completed.
content::RenderFrameHost* CreateDocWrittenFrameFromAdScript(
const content::ToRenderFrameHost& adapter) {
return CreateDocWrittenFrameImpl(adapter, true /* ad_script */);
}
// Creates a frame and aborts the initial load with a doc.write. Returns after
// navigation has completed.
content::RenderFrameHost* CreateFrameWithDocWriteAbortedLoad(
const content::ToRenderFrameHost& adapter) {
return CreateFrameWithDocWriteAbortedLoadImpl(adapter,
false /* ad_script */);
}
// Creates a frame and aborts the initial load with a doc.write. The script
// creating the frame is an ad script. Returns after navigation has completed.
content::RenderFrameHost* CreateFrameWithDocWriteAbortedLoadFromAdScript(
const content::ToRenderFrameHost& adapter) {
return CreateFrameWithDocWriteAbortedLoadImpl(adapter,
true /* ad_script */);
}
// Creates a frame and aborts the initial load with a window.stop. Returns
// after navigation has completed.
content::RenderFrameHost* CreateFrameWithWindowStopAbortedLoad(
const content::ToRenderFrameHost& adapter) {
return CreateFrameWithWindowStopAbortedLoadImpl(adapter,
false /* ad_script */);
}
// Creates a frame and aborts the initial load with a window.stop. The script
// creating the frame is an ad script. Returns after navigation has completed.
content::RenderFrameHost* CreateFrameWithWindowStopAbortedLoadFromAdScript(
const content::ToRenderFrameHost& adapter) {
return CreateFrameWithWindowStopAbortedLoadImpl(adapter,
true /* ad_script */);
}
// Given a RenderFrameHost, navigates the page to the given |url| and waits
// for the navigation to complete before returning.
void NavigateFrame(content::RenderFrameHost* render_frame_host,
const GURL& url);
GURL GetURL(const std::string& page) {
return embedded_test_server()->GetURL("/ad_tagging/" + page);
}
std::unique_ptr<AdTaggingPageLoadMetricsTestWaiter>
CreateAdTaggingPageLoadMetricsTestWaiter() {
return std::make_unique<AdTaggingPageLoadMetricsTestWaiter>(
GetWebContents());
}
private:
content::RenderFrameHost* CreateDocWrittenFrameImpl(
const content::ToRenderFrameHost& adapter,
bool ad_script);
content::RenderFrameHost* CreateFrameWithDocWriteAbortedLoadImpl(
const content::ToRenderFrameHost& adapter,
bool ad_script);
content::RenderFrameHost* CreateFrameWithWindowStopAbortedLoadImpl(
const content::ToRenderFrameHost& adapter,
bool ad_script);
};
content::RenderFrameHost* AdTaggingBrowserTest::CreateDocWrittenFrameImpl(
const content::ToRenderFrameHost& adapter,
bool ad_script) {
content::RenderFrameHost* rfh = adapter.render_frame_host();
std::string name = GetUniqueFrameName();
std::string script = base::StringPrintf(
"%s('%s', '%s');",
ad_script ? "createDocWrittenAdFrame" : "createDocWrittenFrame",
name.c_str(), GetURL("").spec().c_str());
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(rfh);
content::TestNavigationObserver navigation_observer(web_contents, 1);
EXPECT_EQ(true, content::EvalJs(rfh, script,
content::EXECUTE_SCRIPT_USE_MANUAL_REPLY));
navigation_observer.Wait();
EXPECT_TRUE(navigation_observer.last_navigation_succeeded())
<< navigation_observer.last_net_error_code();
return content::FrameMatchingPredicate(
rfh->GetPage(), base::BindRepeating(&content::FrameMatchesName, name));
}
content::RenderFrameHost*
AdTaggingBrowserTest::CreateFrameWithDocWriteAbortedLoadImpl(
const content::ToRenderFrameHost& adapter,
bool ad_script) {
content::RenderFrameHost* rfh = adapter.render_frame_host();
std::string name = GetUniqueFrameName();
std::string script =
base::StringPrintf("%s('%s');",
ad_script ? "createAdFrameWithDocWriteAbortedLoad"
: "createFrameWithDocWriteAbortedLoad",
name.c_str());
// The executed scripts set the title to be the frame name when they have
// finished loading.
content::TitleWatcher title_watcher(GetWebContents(),
base::ASCIIToUTF16(name));
EXPECT_TRUE(content::ExecJs(rfh, script));
EXPECT_EQ(base::ASCIIToUTF16(name), title_watcher.WaitAndGetTitle());
return content::FrameMatchingPredicate(
rfh->GetPage(), base::BindRepeating(&content::FrameMatchesName, name));
}
content::RenderFrameHost*
AdTaggingBrowserTest::CreateFrameWithWindowStopAbortedLoadImpl(
const content::ToRenderFrameHost& adapter,
bool ad_script) {
content::RenderFrameHost* rfh = adapter.render_frame_host();
std::string name = GetUniqueFrameName();
std::string script =
base::StringPrintf("%s('%s');",
ad_script ? "createAdFrameWithWindowStopAbortedLoad"
: "createFrameWithWindowStopAbortedLoad",
name.c_str());
// The executed scripts set the title to be the frame name when they have
// finished loading.
content::TitleWatcher title_watcher(GetWebContents(),
base::ASCIIToUTF16(name));
EXPECT_TRUE(content::ExecJs(rfh, script));
EXPECT_EQ(base::ASCIIToUTF16(name), title_watcher.WaitAndGetTitle());
return content::FrameMatchingPredicate(
rfh->GetPage(), base::BindRepeating(&content::FrameMatchesName, name));
}
// Given a RenderFrameHost, navigates the page to the given |url| and waits
// for the navigation to complete before returning.
void AdTaggingBrowserTest::NavigateFrame(
content::RenderFrameHost* render_frame_host,
const GURL& url) {
std::string script =
base::StringPrintf(R"(window.location='%s')", url.spec().c_str());
content::TestNavigationObserver navigation_observer(GetWebContents(), 1);
EXPECT_TRUE(content::ExecJs(render_frame_host, script));
navigation_observer.Wait();
EXPECT_TRUE(navigation_observer.last_navigation_succeeded())
<< navigation_observer.last_net_error_code();
}
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
AdContentSettingAllowed_AdTaggingDisabled) {
HostContentSettingsMapFactory::GetForProfile(
web_contents()->GetBrowserContext())
->SetDefaultContentSetting(ContentSettingsType::ADS,
CONTENT_SETTING_ALLOW);
TestSubresourceFilterObserver observer(web_contents());
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Create an ad frame.
GURL ad_url = GetURL("frame_factory.html?2&ad=true");
RenderFrameHost* ad_frame = CreateSrcFrame(GetWebContents(), ad_url);
// Verify that we are not evaluating subframe loads.
EXPECT_FALSE(observer.GetSubframeLoadPolicy(ad_url).has_value());
EXPECT_FALSE(observer.GetIsAdSubframe(ad_frame->GetFrameTreeNodeId()));
// Child frame created by ad script.
RenderFrameHost* ad_frame_tagged_by_script = CreateSrcFrameFromAdScript(
GetWebContents(), GetURL("frame_factory.html?1"));
// No frames should be detected by script heuristics.
EXPECT_FALSE(observer.GetIsAdSubframe(
ad_frame_tagged_by_script->GetFrameTreeNodeId()));
}
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
AdContentSettingBlocked_AdTaggingEnabled) {
HostContentSettingsMapFactory::GetForProfile(
web_contents()->GetBrowserContext())
->SetDefaultContentSetting(ContentSettingsType::ADS,
CONTENT_SETTING_BLOCK);
TestSubresourceFilterObserver observer(web_contents());
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Create an ad frame.
GURL ad_url = GetURL("frame_factory.html?2&ad=true");
RenderFrameHost* ad_frame = CreateSrcFrame(GetWebContents(), ad_url);
// Verify that we are evaluating subframe loads.
EXPECT_TRUE(observer.GetSubframeLoadPolicy(ad_url).has_value());
EXPECT_TRUE(observer.GetIsAdSubframe(ad_frame->GetFrameTreeNodeId()));
// Child frame created by ad script.
RenderFrameHost* ad_frame_tagged_by_script = CreateSrcFrameFromAdScript(
GetWebContents(), GetURL("frame_factory.html?1"));
// Frames should be detected by script heuristics.
EXPECT_TRUE(observer.GetIsAdSubframe(
ad_frame_tagged_by_script->GetFrameTreeNodeId()));
}
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, FramesByURL) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
EXPECT_FALSE(observer.GetIsAdSubframe(
GetWebContents()->GetMainFrame()->GetFrameTreeNodeId()));
// (1) Vanilla child.
content::RenderFrameHost* vanilla_child =
CreateSrcFrame(GetWebContents(), GetURL("frame_factory.html?1"));
EXPECT_FALSE(observer.GetIsAdSubframe(vanilla_child->GetFrameTreeNodeId()));
// (2) Ad child.
RenderFrameHost* ad_child =
CreateSrcFrame(GetWebContents(), GetURL("frame_factory.html?2&ad=true"));
EXPECT_TRUE(observer.GetIsAdSubframe(ad_child->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kMatchedBlockingRule,
blink::mojom::FrameCreationStackEvidence::kNotCreatedByAdScript));
// (3) Ad child of 2.
RenderFrameHost* ad_child_2 =
CreateSrcFrame(ad_child, GetURL("frame_factory.html?sub=1&3&ad=true"));
EXPECT_TRUE(observer.GetIsAdSubframe(ad_child_2->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child_2, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedBlockingRule,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// (4) Vanilla child of 2.
RenderFrameHost* vanilla_child_2 =
CreateSrcFrame(ad_child, GetURL("frame_factory.html?4"));
EXPECT_TRUE(observer.GetIsAdSubframe(vanilla_child_2->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
vanilla_child_2, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// (5) Vanilla child of 1. This tests something subtle.
// frame_factory.html?ad=true loads the same script that frame_factory.html
// uses to load frames. This tests that even though the script is tagged as an
// ad in the ad iframe, it's not considered an ad in the main frame, hence
// it's able to create an iframe that's not labeled as an ad.
RenderFrameHost* vanilla_child_3 =
CreateSrcFrame(vanilla_child, GetURL("frame_factory.html?5"));
EXPECT_FALSE(observer.GetIsAdSubframe(vanilla_child_3->GetFrameTreeNodeId()));
}
const char kSubresourceFilterOriginStatusHistogram[] =
"PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
"OriginStatus";
const char kWindowOpenFromAdStateHistogram[] = "Blink.WindowOpen.FromAdState";
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, VerifySameOriginWithoutNavigate) {
// The test assumes pages gets deleted after navigation, triggering histogram
// recording. Disable back/forward cache to ensure that pages don't get
// preserved in the cache.
// TODO(https://crbug.com/1229122): Investigate if this needs further fix.
content::DisableBackForwardCacheForTesting(
browser()->tab_strip_model()->GetActiveWebContents(),
content::BackForwardCache::TEST_ASSUMES_NO_CACHING);
base::HistogramTester histogram_tester;
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Ad frame via doc write.
CreateDocWrittenFrameFromAdScript(GetWebContents());
// Navigate away and ensure we report same origin.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
histogram_tester.ExpectUniqueSample(kSubresourceFilterOriginStatusHistogram,
page_load_metrics::OriginStatus::kSame,
1);
}
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, VerifyCrossOriginWithoutNavigate) {
base::HistogramTester histogram_tester;
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Regular frame that's cross origin and has a doc write ad of its own.
RenderFrameHost* regular_child = CreateSrcFrame(
GetWebContents(), embedded_test_server()->GetURL(
"b.com", "/ad_tagging/frame_factory.html"));
CreateDocWrittenFrameFromAdScript(regular_child);
// Navigate away and ensure we report cross origin.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
// TODO(johnidel): Check that frame was reported properly. See
// crbug.com/914893.
}
// Ad script creates a frame and navigates it cross origin.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
VerifyCrossOriginWithImmediateNavigate) {
base::HistogramTester histogram_tester;
auto waiter = CreateAdTaggingPageLoadMetricsTestWaiter();
// Create the main frame and cross origin subframe from an ad script.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
CreateSrcFrameFromAdScript(GetWebContents(),
embedded_test_server()->GetURL(
"b.com", "/ads_observer/same_origin_ad.html"));
// Wait for all of the subresources to finish loading (includes favicon).
// Waiting for the navigation to finish is not sufficient, as it blocks on the
// main resource load finishing, not the iframe resource. Page loads 4
// resources, a favicon, and 2 resources for the iframe.
waiter->AddMinimumCompleteResourcesExpectation(8);
waiter->Wait();
// Navigate away and ensure we report cross origin.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
histogram_tester.ExpectUniqueSample(kSubresourceFilterOriginStatusHistogram,
page_load_metrics::OriginStatus::kCross,
1);
}
// Ad script creates a frame and navigates it same origin.
// It is then renavigated cross origin.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
VerifySameOriginWithCrossOriginRenavigate) {
base::HistogramTester histogram_tester;
// Create the main frame and same origin subframe from an ad script.
// This triggers the subresource_filter ad detection.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
RenderFrameHost* ad_child = CreateSrcFrameFromAdScript(
GetWebContents(), GetURL("frame_factory.html?ad=true"));
// Navigate the subframe to a cross origin site.
NavigateFrame(ad_child, embedded_test_server()->GetURL(
"b.com", "/ad_tagging/frame_factory.html"));
// Navigate away and ensure we report same origin.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
histogram_tester.ExpectUniqueSample(kSubresourceFilterOriginStatusHistogram,
page_load_metrics::OriginStatus::kSame,
1);
}
// Test that a subframe with a non-ad url but loaded by ad script is an ad.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, FrameLoadedByAdScript) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Child frame created by ad script.
RenderFrameHost* ad_child = CreateSrcFrameFromAdScript(
GetWebContents(), GetURL("frame_factory.html?1"));
EXPECT_TRUE(observer.GetIsAdSubframe(ad_child->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that same-origin doc.write created iframes are tagged as ads.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, SameOriginFrameTagging) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// (1) Vanilla child.
content::RenderFrameHost* vanilla_frame =
CreateDocWrittenFrame(GetWebContents());
EXPECT_FALSE(observer.GetIsAdSubframe(vanilla_frame->GetFrameTreeNodeId()));
// (2) Ad child.
content::RenderFrameHost* ad_frame =
CreateDocWrittenFrameFromAdScript(GetWebContents());
EXPECT_TRUE(EvidenceForFrameComprises(
ad_frame, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that the children same-origin doc.write created iframes are tagged as
// ads where appropriate.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
ChildrenOfSameOriginFrames_CorrectlyTagged) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Vanilla frame and descendants
content::RenderFrameHost* vanilla_frame =
CreateDocWrittenFrame(GetWebContents());
EXPECT_FALSE(observer.GetIsAdSubframe(vanilla_frame->GetFrameTreeNodeId()));
content::RenderFrameHost* vanilla_child_of_vanilla =
CreateSrcFrame(vanilla_frame, GetURL("frame_factory.html"));
EXPECT_FALSE(
observer.GetIsAdSubframe(vanilla_child_of_vanilla->GetFrameTreeNodeId()));
content::RenderFrameHost* ad_child_of_vanilla =
CreateSrcFrameFromAdScript(vanilla_frame, GetURL("frame_factory.html"));
EXPECT_TRUE(
observer.GetIsAdSubframe(ad_child_of_vanilla->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child_of_vanilla, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// Ad frame and descendants
content::RenderFrameHost* ad_frame =
CreateDocWrittenFrameFromAdScript(GetWebContents());
EXPECT_TRUE(EvidenceForFrameComprises(
ad_frame, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
content::RenderFrameHost* vanilla_child_of_ad =
CreateSrcFrame(ad_frame, GetURL("frame_factory.html"));
EXPECT_TRUE(
observer.GetIsAdSubframe(vanilla_child_of_ad->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
vanilla_child_of_ad, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
content::RenderFrameHost* ad_child_of_ad =
CreateSrcFrameFromAdScript(ad_frame, GetURL("frame_factory.html"));
EXPECT_TRUE(observer.GetIsAdSubframe(ad_child_of_ad->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child_of_ad, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that frames with an aborted initial load due to a doc.write are still
// correctly tagged as ads or not.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
FramesWithDocWriteAbortedLoad_StillCorrectlyTagged) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Vanilla child.
content::RenderFrameHost* vanilla_frame_with_aborted_load =
CreateFrameWithDocWriteAbortedLoad(GetWebContents());
EXPECT_FALSE(observer.GetIsAdSubframe(
vanilla_frame_with_aborted_load->GetFrameTreeNodeId()));
// Child created by ad script.
content::RenderFrameHost* ad_frame_with_aborted_load =
CreateFrameWithDocWriteAbortedLoadFromAdScript(GetWebContents());
EXPECT_TRUE(observer.GetIsAdSubframe(
ad_frame_with_aborted_load->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_frame_with_aborted_load,
/*parent_is_ad=*/false, blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// Child with ad parent.
content::RenderFrameHost* ad_frame = CreateSrcFrameFromAdScript(
GetWebContents(), GetURL("frame_factory.html"));
content::RenderFrameHost* child_frame_of_ad_with_aborted_load =
CreateFrameWithDocWriteAbortedLoad(ad_frame);
EXPECT_TRUE(observer.GetIsAdSubframe(ad_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(observer.GetIsAdSubframe(
child_frame_of_ad_with_aborted_load->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
child_frame_of_ad_with_aborted_load,
/*parent_is_ad=*/true, blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that frames with an aborted initial load due to a window.stop are still
// correctly tagged as ads or not.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
FramesWithWindowStopAbortedLoad_StillCorrectlyTagged) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Vanilla child.
content::RenderFrameHost* vanilla_frame_with_aborted_load =
CreateFrameWithWindowStopAbortedLoad(GetWebContents());
EXPECT_FALSE(observer.GetIsAdSubframe(
vanilla_frame_with_aborted_load->GetFrameTreeNodeId()));
// Child created by ad script.
content::RenderFrameHost* ad_frame_with_aborted_load =
CreateFrameWithWindowStopAbortedLoadFromAdScript(GetWebContents());
EXPECT_TRUE(observer.GetIsAdSubframe(
ad_frame_with_aborted_load->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_frame_with_aborted_load,
/*parent_is_ad=*/false, blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// Child with ad parent.
content::RenderFrameHost* ad_frame = CreateSrcFrameFromAdScript(
GetWebContents(), GetURL("frame_factory.html"));
content::RenderFrameHost* child_frame_of_ad_with_aborted_load =
CreateFrameWithWindowStopAbortedLoad(ad_frame);
EXPECT_TRUE(observer.GetIsAdSubframe(ad_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(observer.GetIsAdSubframe(
child_frame_of_ad_with_aborted_load->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
child_frame_of_ad_with_aborted_load,
/*parent_is_ad=*/true, blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that the children of a frame with its initial load aborted due to a
// doc.write are reported correctly as vanilla or ad frames.
IN_PROC_BROWSER_TEST_F(
AdTaggingBrowserTest,
ChildrenOfFrameWithDocWriteAbortedLoad_StillCorrectlyTagged) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Create a frame and abort its initial load in vanilla script. The children
// of this vanilla frame should be taggged correctly.
content::RenderFrameHost* vanilla_frame_with_aborted_load =
CreateFrameWithDocWriteAbortedLoad(GetWebContents());
content::RenderFrameHost* vanilla_child_of_vanilla = CreateSrcFrame(
vanilla_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_FALSE(
observer.GetIsAdSubframe(vanilla_child_of_vanilla->GetFrameTreeNodeId()));
content::RenderFrameHost* ad_child_of_vanilla = CreateSrcFrameFromAdScript(
vanilla_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_TRUE(
observer.GetIsAdSubframe(ad_child_of_vanilla->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child_of_vanilla, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// Create a frame and abort its initial load in ad script. The children of
// this ad frame should be tagged as ads.
content::RenderFrameHost* ad_frame_with_aborted_load =
CreateFrameWithDocWriteAbortedLoadFromAdScript(GetWebContents());
EXPECT_TRUE(observer.GetIsAdSubframe(
ad_frame_with_aborted_load->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_frame_with_aborted_load,
/*parent_is_ad=*/false, blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
content::RenderFrameHost* vanilla_child_of_ad =
CreateSrcFrame(ad_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_TRUE(
observer.GetIsAdSubframe(vanilla_child_of_ad->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
vanilla_child_of_ad, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
content::RenderFrameHost* ad_child_of_ad = CreateSrcFrameFromAdScript(
ad_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_TRUE(observer.GetIsAdSubframe(ad_child_of_ad->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child_of_ad, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that the children of a frame with its initial load aborted due to a
// window.stop are reported correctly as vanilla or ad frames.
// This test is flaky. See crbug.com/1069346.
IN_PROC_BROWSER_TEST_F(
AdTaggingBrowserTest,
ChildrenOfFrameWithWindowStopAbortedLoad_StillCorrectlyTagged) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
// Create a frame and abort its initial load in vanilla script. The children
// of this vanilla frame should be taggged correctly.
content::RenderFrameHost* vanilla_frame_with_aborted_load =
CreateFrameWithWindowStopAbortedLoad(GetWebContents());
content::RenderFrameHost* vanilla_child_of_vanilla = CreateSrcFrame(
vanilla_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_FALSE(
observer.GetIsAdSubframe(vanilla_child_of_vanilla->GetFrameTreeNodeId()));
content::RenderFrameHost* ad_child_of_vanilla = CreateSrcFrameFromAdScript(
vanilla_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_TRUE(
observer.GetIsAdSubframe(ad_child_of_vanilla->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_child_of_vanilla, /*parent_is_ad=*/false,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
// Create a frame and abort its initial load in ad script. The children of
// this ad frame should be tagged as ads.
content::RenderFrameHost* ad_frame_with_aborted_load =
CreateFrameWithWindowStopAbortedLoadFromAdScript(GetWebContents());
EXPECT_TRUE(observer.GetIsAdSubframe(
ad_frame_with_aborted_load->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
ad_frame_with_aborted_load,
/*parent_is_ad=*/false, blink::mojom::FilterListResult::kNotChecked,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
content::RenderFrameHost* vanilla_child_of_ad =
CreateSrcFrame(ad_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_TRUE(
observer.GetIsAdSubframe(vanilla_child_of_ad->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
vanilla_child_of_ad, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
content::RenderFrameHost* ad_child_of_ad = CreateSrcFrameFromAdScript(
ad_frame_with_aborted_load, GetURL("frame_factory.html"));
EXPECT_TRUE(observer.GetIsAdSubframe(ad_child_of_ad->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
vanilla_child_of_ad, /*parent_is_ad=*/true,
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kCreatedByAdScript));
}
// Test that navigating a frame to a URL with a less restrictive load policy
// updates the latest filter list result, but not the most-restrictive one.
IN_PROC_BROWSER_TEST_F(
AdTaggingBrowserTest,
NavigationToLessRestrictiveUrl_MostRestrictiveLoadPolicyUnchanged) {
TestSubresourceFilterObserver observer(web_contents());
// Main frame.
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html")));
content::RenderFrameHost* test_frame = CreateSrcFrame(
GetWebContents(), GetURL("frame_factory.html?allowed=true"));
EXPECT_FALSE(observer.GetIsAdSubframe(test_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
test_frame, /*parent_is_ad=*/false,
/*latest_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedAllowingRule,
/*most_restrictive_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedAllowingRule,
blink::mojom::FrameCreationStackEvidence::kNotCreatedByAdScript));
NavigateFrame(test_frame, GetURL("frame_factory.html"));
EXPECT_FALSE(observer.GetIsAdSubframe(test_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
test_frame, /*parent_is_ad=*/false,
/*latest_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedNoRules,
/*most_restrictive_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kNotCreatedByAdScript));
NavigateFrame(test_frame, GetURL("frame_factory.html?allowed=true"));
EXPECT_FALSE(observer.GetIsAdSubframe(test_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
test_frame, /*parent_is_ad=*/false,
/*latest_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedAllowingRule,
/*most_restrictive_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedNoRules,
blink::mojom::FrameCreationStackEvidence::kNotCreatedByAdScript));
NavigateFrame(test_frame, GetURL("frame_factory.html?ad=true"));
EXPECT_TRUE(observer.GetIsAdSubframe(test_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
test_frame, /*parent_is_ad=*/false,
/*latest_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedBlockingRule,
/*most_restrictive_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedBlockingRule,
blink::mojom::FrameCreationStackEvidence::kNotCreatedByAdScript));
NavigateFrame(test_frame, GetURL("frame_factory.html"));
EXPECT_TRUE(observer.GetIsAdSubframe(test_frame->GetFrameTreeNodeId()));
EXPECT_TRUE(EvidenceForFrameComprises(
test_frame, /*parent_is_ad=*/false,
/*latest_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedNoRules,
/*most_restrictive_filter_list_result=*/
blink::mojom::FilterListResult::kMatchedBlockingRule,
blink::mojom::FrameCreationStackEvidence::kNotCreatedByAdScript));
}
// Basic vanilla stylesheet with vanilla font and image.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
VanillaExternalStylesheet_ResourcesNotTagged) {
auto waiter = CreateAdTaggingPageLoadMetricsTestWaiter();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetURL("test_div.html")));
AddExternalStylesheet(GetWebContents(),
GetURL("sheet_with_vanilla_resources.css"));
// Document, favicon, stylesheet, font, background-image
waiter->AddMinimumCompleteVanillaResourcesExpectation(5);
waiter->Wait();
EXPECT_EQ(waiter->current_complete_vanilla_resources(), 5);
EXPECT_EQ(waiter->current_complete_ad_resources(), 0);
}
// Basic ad stylesheet with vanilla font and image.
IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest,
AdExternalStylesheet_ResourcesTagged) {
auto waiter = CreateAdTaggingPageLoadMetricsTestWaiter();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetURL("test_div.html")));
AddExternalStylesheet(GetWebContents(),
GetURL("sheet_with_vanilla_resources.css?ad=true"));
// Document, favicon
waiter->AddMinimumCompleteVanillaResourcesExpectation(2);
// Stylesheet, font, background-image
waiter->AddMinimumCompleteAdResourcesExpectation(3);
waiter->Wait();
EXPECT_EQ(waiter->current_complete_vanilla_resources(), 2);
EXPECT_EQ(waiter->current_complete_ad_resources(), 3);
}
void ExpectWindowOpenUkmEntry(const ukm::TestUkmRecorder& ukm_recorder,
bool from_main_frame,
const GURL& main_frame_url,
bool from_ad_subframe,
bool from_ad_script) {
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::AbusiveExperienceHeuristic_WindowOpen::kEntryName);
EXPECT_EQ(1u, entries.size());
// Check that the event is keyed to |main_frame_url| only if it was from the
// top frame.
if (from_main_frame) {
ukm_recorder.ExpectEntrySourceHasUrl(entries.back(), main_frame_url);
} else {
EXPECT_FALSE(ukm_recorder.GetSourceForSourceId(entries.back()->source_id));
}
// Check that a DocumentCreated entry was created, and it's keyed to
// |main_frame_url| only if it was from the top frame. However, we can always
// use the navigation source ID to link this source to |main_frame_url|.
const ukm::mojom::UkmEntry* dc_entry =
ukm_recorder.GetDocumentCreatedEntryForSourceId(
entries.back()->source_id);
EXPECT_TRUE(dc_entry);
EXPECT_EQ(entries.back()->source_id, dc_entry->source_id);
if (from_main_frame) {
ukm_recorder.ExpectEntrySourceHasUrl(dc_entry, main_frame_url);
} else {
EXPECT_FALSE(ukm_recorder.GetSourceForSourceId(dc_entry->source_id));
}
const ukm::UkmSource* navigation_source =
ukm_recorder.GetSourceForSourceId(*ukm_recorder.GetEntryMetric(
dc_entry, ukm::builders::DocumentCreated::kNavigationSourceIdName));
EXPECT_EQ(main_frame_url, navigation_source->url());
EXPECT_EQ(from_main_frame,
*ukm_recorder.GetEntryMetric(
dc_entry, ukm::builders::DocumentCreated::kIsMainFrameName));
ukm_recorder.ExpectEntryMetric(
entries.back(),
ukm::builders::AbusiveExperienceHeuristic_WindowOpen::kFromAdSubframeName,
from_ad_subframe);
ukm_recorder.ExpectEntryMetric(
entries.back(),
ukm::builders::AbusiveExperienceHeuristic_WindowOpen::kFromAdScriptName,
from_ad_script);
}
void ExpectWindowOpenUmaEntry(const base::HistogramTester& histogram_tester,
bool from_ad_subframe,
bool from_ad_script) {
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
blink::FromAdState state =
blink::GetFromAdState(from_ad_subframe, from_ad_script);
histogram_tester.ExpectBucketCount(kWindowOpenFromAdStateHistogram, state,
1 /* expected_count */);
}
enum class NavigationInitiationType {
kWindowOpen,
kSetLocation,
kAnchorLinkActivate,
};
class AdClickNavigationBrowserTest
: public AdTaggingBrowserTest,
public ::testing::WithParamInterface<
std::tuple<NavigationInitiationType, bool /* gesture */>> {
void SetUpCommandLine(base::CommandLine* command_line) override {
// Popups without user gesture is blocked by default. Turn off the switch
// here to unblock that, so as to be able to test that the UseCounter not
// being recorded was due to the filtering in our recording function, rather
// than the default popup blocking.
command_line->AppendSwitch(embedder_support::kDisablePopupBlocking);
}
};
IN_PROC_BROWSER_TEST_P(AdClickNavigationBrowserTest, UseCounter) {
NavigationInitiationType type;
bool gesture;
std::tie(type, gesture) = GetParam();
auto web_feature_waiter =
std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
GetWebContents());
blink::mojom::WebFeature ad_click_navigation_feature =
blink::mojom::WebFeature::kAdClickNavigation;
web_feature_waiter->AddWebFeatureExpectation(ad_click_navigation_feature);
GURL url =
embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::WebContents* main_tab = GetWebContents();
RenderFrameHost* child = CreateSrcFrame(
main_tab, embedded_test_server()->GetURL(
"a.com", "/ad_tagging/frame_factory.html?1&ad=true"));
std::string script;
switch (type) {
case NavigationInitiationType::kWindowOpen:
script = "window.open('frame_factory.html')";
break;
case NavigationInitiationType::kSetLocation:
script = "location='frame_factory.html'";
break;
case NavigationInitiationType::kAnchorLinkActivate:
script =
"var a = document.createElement('a');"
"a.setAttribute('href', 'frame_factory.html');"
"a.click();";
break;
}
if (gesture) {
EXPECT_TRUE(ExecJs(child, script));
web_feature_waiter->Wait();
} else {
switch (type) {
case NavigationInitiationType::kSetLocation:
case NavigationInitiationType::kAnchorLinkActivate: {
content::TestNavigationObserver navigation_observer(web_contents());
EXPECT_TRUE(content::ExecJs(child, script,
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
// To report metrics.
navigation_observer.Wait();
break;
}
case NavigationInitiationType::kWindowOpen: {
EXPECT_TRUE(content::ExecJs(child, script,
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
// To report metrics.
ASSERT_EQ(2, browser()->tab_strip_model()->count());
browser()->tab_strip_model()->MoveSelectedTabsTo(0);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
break;
}
}
EXPECT_FALSE(
web_feature_waiter->DidObserveWebFeature(ad_click_navigation_feature));
}
}
INSTANTIATE_TEST_SUITE_P(
All,
AdClickNavigationBrowserTest,
::testing::Combine(
::testing::Values(NavigationInitiationType::kWindowOpen,
NavigationInitiationType::kSetLocation,
NavigationInitiationType::kAnchorLinkActivate),
::testing::Bool()));
class AdTaggingEventFromSubframeBrowserTest
: public AdTaggingBrowserTest,
public ::testing::WithParamInterface<
std::tuple<bool /* cross_origin */, bool /* from_ad_subframe */>> {};
// crbug.com/997410. The test is flaky on multiple platforms.
IN_PROC_BROWSER_TEST_P(AdTaggingEventFromSubframeBrowserTest,
DISABLED_WindowOpenFromSubframe) {
bool cross_origin;
bool from_ad_subframe;
std::tie(cross_origin, from_ad_subframe) = GetParam();
SCOPED_TRACE(::testing::Message()
<< "cross_origin = " << cross_origin << ", "
<< "from_ad_subframe = " << from_ad_subframe);
ukm::TestAutoSetUkmRecorder ukm_recorder;
base::HistogramTester histogram_tester;
GURL main_frame_url =
embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
content::WebContents* main_tab = GetWebContents();
std::string hostname = cross_origin ? "b.com" : "a.com";
std::string suffix = from_ad_subframe ? "&ad=true" : "";
RenderFrameHost* child = CreateSrcFrame(
main_tab, embedded_test_server()->GetURL(
hostname, "/ad_tagging/frame_factory.html?1" + suffix));
EXPECT_TRUE(content::ExecJs(child, "window.open();"));
bool from_ad_script = from_ad_subframe;
ExpectWindowOpenUkmEntry(ukm_recorder, false /* from_main_frame */,
main_frame_url, from_ad_subframe, from_ad_script);
ExpectWindowOpenUmaEntry(histogram_tester, from_ad_subframe, from_ad_script);
}
INSTANTIATE_TEST_SUITE_P(
All,
AdTaggingEventFromSubframeBrowserTest,
::testing::Combine(::testing::Bool(), ::testing::Bool()));
class AdTaggingEventWithScriptInStackBrowserTest
: public AdTaggingBrowserTest,
public ::testing::WithParamInterface<bool /* from_ad_script */> {};
// crbug.com/998405. The test is flaky on multiple platforms.
IN_PROC_BROWSER_TEST_P(AdTaggingEventWithScriptInStackBrowserTest,
DISABLED_WindowOpenWithScriptInStack) {
bool from_ad_script = GetParam();
SCOPED_TRACE(::testing::Message() << "from_ad_script = " << from_ad_script);
ukm::TestAutoSetUkmRecorder ukm_recorder;
base::HistogramTester histogram_tester;
GURL main_frame_url = GetURL("frame_factory.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
content::WebContents* main_tab = GetWebContents();
std::string script = from_ad_script ? "windowOpenFromAdScript();"
: "windowOpenFromNonAdScript();";
EXPECT_TRUE(content::ExecJs(main_tab, script));
bool from_ad_subframe = false;
ExpectWindowOpenUkmEntry(ukm_recorder, true /* from_main_frame */,
main_frame_url, from_ad_subframe, from_ad_script);
ExpectWindowOpenUmaEntry(histogram_tester, from_ad_subframe, from_ad_script);
}
INSTANTIATE_TEST_SUITE_P(
All,
AdTaggingEventWithScriptInStackBrowserTest,
::testing::Bool());
} // namespace
} // namespace subresource_filter