blob: 02a40d28c638ca4b4ed156be9bd069f4ea71eab5 [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 "components/safe_browsing/triggers/ad_sampler_trigger.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "components/prefs/testing_pref_service.h"
#include "components/safe_browsing/features.h"
#include "components/safe_browsing/triggers/mock_trigger_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gmock/include/gmock/gmock-generated-function-mockers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::NavigationSimulator;
using content::RenderFrameHost;
using content::RenderFrameHostTester;
using testing::_;
using testing::Return;
namespace safe_browsing {
namespace {
const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
const char kNonAdUrl[] = "https://foo.com/";
const char kAdName[] = "google_ads_iframe_1";
const char kNonAdName[] = "foo";
} // namespace
class AdSamplerTriggerTest : public content::RenderViewHostTestHarness {
public:
AdSamplerTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
~AdSamplerTriggerTest() override {}
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
// Enable any prefs required for the trigger to run.
safe_browsing::RegisterProfilePrefs(prefs_.registry());
prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
}
void CreateTriggerWithFrequency(const size_t denominator) {
safe_browsing::AdSamplerTrigger::CreateForWebContents(
web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr);
safe_browsing::AdSamplerTrigger* ad_sampler =
safe_browsing::AdSamplerTrigger::FromWebContents(web_contents());
ad_sampler->SetSamplerFrequencyForTest(denominator);
// Give the trigger a test task runner that we can synchronize on.
ad_sampler->SetTaskRunnerForTest(task_runner_);
}
// Returns the final RenderFrameHost after navigation commits.
RenderFrameHost* NavigateFrame(const std::string& url,
RenderFrameHost* frame) {
return NavigationSimulator::NavigateAndCommitFromDocument(GURL(url), frame);
}
// Returns the final RenderFrameHost after navigation commits.
RenderFrameHost* NavigateMainFrame(const std::string& url) {
return NavigateFrame(url, web_contents()->GetMainFrame());
}
// Returns the final RenderFrameHost after navigation commits.
RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
const std::string& frame_name,
RenderFrameHost* parent) {
RenderFrameHost* subframe =
RenderFrameHostTester::For(parent)->AppendChild(frame_name);
return NavigateFrame(url, subframe);
}
void WaitForTaskRunnerIdle() {
task_runner_->RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
base::HistogramTester* get_histograms() { return &histograms_; }
private:
TestingPrefServiceSimple prefs_;
MockTriggerManager trigger_manager_;
base::HistogramTester histograms_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
};
TEST_F(AdSamplerTriggerTest, TriggerDisabledBySamplingFrequency) {
// Make sure the trigger doesn't fire when the sampling frequency is set to
// zero, which disables the trigger.
CreateTriggerWithFrequency(kAdSamplerFrequencyDisabled);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetails(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
.Times(0);
// This page contains two ads - one identifiable by its URL, the other by the
// name of the frame.
RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
// Three navigations (main frame, two subframes). One frame with no ads, and
// two skipped ad samples.
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
TRIGGER_CHECK, 3);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_NO_AD, 1);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY, 2);
}
TEST_F(AdSamplerTriggerTest, DISABLED_PageWithNoAds) {
// Make sure the trigger doesn't fire when there are no ads on the page.
CreateTriggerWithFrequency(/*denominator=*/1);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetails(_, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(_, _, _, _, _, _))
.Times(0);
RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
// Three navigations (main frame, two subframes), each with no ad.
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
TRIGGER_CHECK, 3);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_NO_AD, 3);
}
TEST_F(AdSamplerTriggerTest, PageWithMultipleAds) {
// Make sure the trigger fires when there are ads on the page. We expect
// one call for each ad detected.
CreateTriggerWithFrequency(/*denominator=*/1);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
web_contents(), _, _, _, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
web_contents(), _, _, _, _))
.Times(2);
// This page contains two ads - one identifiable by its URL, the other by the
// name of the frame.
RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
// Wait for any posted tasks to finish.
WaitForTaskRunnerIdle();
// Three navigations (main frame, two subframes). Main frame with no ads, and
// two sampled ads
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
TRIGGER_CHECK, 3);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_NO_AD, 1);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
AD_SAMPLED, 2);
}
TEST_F(AdSamplerTriggerTest, ReportRejectedByTriggerManager) {
// If the trigger manager rejects the report, we don't try to finish/send the
// report.
CreateTriggerWithFrequency(/*denominator=*/1);
EXPECT_CALL(*get_trigger_manager(),
StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
web_contents(), _, _, _, _))
.Times(1)
.WillOnce(Return(false));
EXPECT_CALL(*get_trigger_manager(),
FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
web_contents(), _, _, _, _))
.Times(0);
// One ad on the page, identified by its URL.
RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
// Wait for any posted tasks to finish.
WaitForTaskRunnerIdle();
// Three navigations (main frame, two subframes). Two frames with no ads, and
// one ad rejected by trigger manager.
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
TRIGGER_CHECK, 3);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_NO_AD, 2);
get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
NO_SAMPLE_COULD_NOT_START_REPORT, 1);
}
TEST(AdSamplerTriggerTestFinch, FrequencyDenominatorFeature) {
// Make sure that setting the frequency denominator via Finch params works as
// expected, and that the default frequency is used when no Finch config is
// given.
content::BrowserTaskEnvironment task_environment;
AdSamplerTrigger trigger_default(nullptr, nullptr, nullptr, nullptr, nullptr);
EXPECT_EQ(kAdSamplerDefaultFrequency,
trigger_default.sampler_frequency_denominator_);
const size_t kDenominatorInt = 12345;
base::FieldTrialParams feature_params;
feature_params[std::string(
safe_browsing::kAdSamplerFrequencyDenominatorParam)] =
base::NumberToString(kDenominatorInt);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
safe_browsing::kAdSamplerTriggerFeature, feature_params);
AdSamplerTrigger trigger_finch(nullptr, nullptr, nullptr, nullptr, nullptr);
EXPECT_EQ(kDenominatorInt, trigger_finch.sampler_frequency_denominator_);
}
} // namespace safe_browsing