Commit ecd22f2f authored by Colin Blundell's avatar Colin Blundell Committed by Commit Bot

[WebLayer] Initial bringup of ad blocking

This CL does the initial bringup of ad tagging and blocking
functionality in WebLayer. To do so, we do the following:
- Bring up an implementation of SubresourceFilterClient and attach it
  to WebContents instances
- Instantiate subresource filter navigation throttles on navigation
- Instantiates and configures SubresourceFilterAgent instances in the
  renderer

The organization and code added is modeled after that in //chrome. We
have left TODOs for significant pieces of functionality to be added
in followups (e.g., showing of the infobar on Android). One thing to
note is that ad tagging/blocking only triggers on Android in WebLayer,
as it requires having a safe browsing database instance and WebLayer
does not have one on platforms other than Android.

This CL also adds browsertests of this initial functionality:
- that the subresource filter activation state is computed on page loads
- that ads are blocked on activated pages if they are flagged by the
  ruleset

Bug: 1116095
Change-Id: I1db0136709014004c221bb0251551eaeac3538e3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2526087
Commit-Queue: Colin Blundell <blundell@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827735}
parent 32410c4f
......@@ -255,6 +255,8 @@ source_set("weblayer_lib_base") {
"browser/ssl_error_controller_client.h",
"browser/stateful_ssl_host_state_delegate_factory.cc",
"browser/stateful_ssl_host_state_delegate_factory.h",
"browser/subresource_filter_client_impl.cc",
"browser/subresource_filter_client_impl.h",
"browser/system_network_context_manager.cc",
"browser/system_network_context_manager.h",
"browser/tab_impl.cc",
......@@ -410,6 +412,7 @@ source_set("weblayer_lib_base") {
"//components/safe_browsing/content/common:interfaces",
"//components/safe_browsing/content/renderer:throttles",
"//components/safe_browsing/core:features",
"//components/safe_browsing/core/db:database_manager",
"//components/security_interstitials/content:security_interstitial_page",
"//components/security_interstitials/content/renderer:security_interstitial_page_controller",
"//components/security_interstitials/core",
......@@ -423,7 +426,10 @@ source_set("weblayer_lib_base") {
"//components/startup_metric_utils/browser",
"//components/strings",
"//components/subresource_filter/content/browser",
"//components/subresource_filter/content/renderer",
"//components/subresource_filter/core/browser",
"//components/subresource_filter/core/browser",
"//components/subresource_filter/core/common",
"//components/translate/content/browser",
"//components/translate/content/renderer",
"//components/translate/core/browser",
......
......@@ -44,6 +44,7 @@ include_rules = [
"+components/no_state_prefetch/browser",
"+components/no_state_prefetch/common",
"+components/resources/android",
"+components/safe_browsing/android",
"+components/safe_browsing/core",
"+components/safe_browsing/core/common",
"+components/safe_browsing/core/features.h",
......@@ -60,6 +61,8 @@ include_rules = [
"+components/strings",
"+components/subresource_filter/content/browser",
"+components/subresource_filter/core/browser",
"+components/subresource_filter/core/common",
"+components/subresource_filter/core/mojom",
"+components/translate/content/android",
"+components/translate/content/browser",
"+components/translate/core/browser",
......
......@@ -39,6 +39,7 @@
#include "components/site_isolation/preloaded_isolated_origins.h"
#include "components/site_isolation/site_isolation_policy.h"
#include "components/strings/grit/components_locale_settings.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
#include "components/subresource_filter/content/browser/ruleset_version.h"
#include "components/user_prefs/user_prefs.h"
#include "components/variations/service/variations_service.h"
......@@ -706,6 +707,12 @@ ContentBrowserClientImpl::CreateThrottlesForNavigation(
throttles.push_back(std::move(insecure_form_throttle));
}
if (auto* throttle_manager =
subresource_filter::ContentSubresourceFilterThrottleManager::
FromWebContents(handle->GetWebContents())) {
throttle_manager->MaybeAppendNavigationThrottles(handle, &throttles);
}
#if defined(OS_ANDROID)
if (handle->IsInMainFrame()) {
if (base::FeatureList::IsEnabled(features::kWebLayerSafeBrowsing) &&
......
......@@ -3,15 +3,42 @@
// found in the LICENSE file.
#include "base/json/json_reader.h"
#include "build/build_config.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
#include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
#include "components/subresource_filter/content/browser/ruleset_service.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
#include "components/subresource_filter/content/browser/test_ruleset_publisher.h"
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/subresource_filter_client_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/grit/weblayer_resources.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
namespace {
// Returns whether a script resource that sets document.scriptExecuted to true
// on load was loaded.
bool WasParsedScriptElementLoaded(content::RenderFrameHost* rfh) {
DCHECK(rfh);
bool script_resource_was_loaded = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
rfh, "domAutomationController.send(!!document.scriptExecuted)",
&script_resource_was_loaded));
return script_resource_was_loaded;
}
} // namespace
class SubresourceFilterBrowserTest : public WebLayerBrowserTest {
public:
SubresourceFilterBrowserTest() = default;
......@@ -19,6 +46,23 @@ class SubresourceFilterBrowserTest : public WebLayerBrowserTest {
SubresourceFilterBrowserTest(const SubresourceFilterBrowserTest&) = delete;
SubresourceFilterBrowserTest& operator=(const SubresourceFilterBrowserTest&) =
delete;
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
}
protected:
void SetRulesetToDisallowURLsWithPathSuffix(const std::string& suffix) {
subresource_filter::testing::TestRulesetPair test_ruleset_pair;
subresource_filter::testing::TestRulesetCreator test_ruleset_creator;
test_ruleset_creator.CreateRulesetToDisallowURLsWithPathSuffix(
suffix, &test_ruleset_pair);
subresource_filter::testing::TestRulesetPublisher test_ruleset_publisher(
BrowserProcess::GetInstance()->subresource_filter_ruleset_service());
ASSERT_NO_FATAL_FAILURE(
test_ruleset_publisher.SetRuleset(test_ruleset_pair.unindexed));
}
};
// Tests that the ruleset service is available.
......@@ -59,4 +103,127 @@ IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest, RulesArePublished) {
EXPECT_EQ(most_recently_indexed_content_version, *packaged_content_version);
}
// The below test is restricted to Android as it tests activation of the
// subresource filter in its default production configuration and WebLayer
// currently has a safe browsing database available in production only on
// Android; the safe browsing database being non-null is a prerequisite for
// subresource filter operation.
#if defined(OS_ANDROID)
// Tests that page activation state is computed as part of a pageload.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
PageActivationStateComputed) {
// Set up prereqs.
auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
content::WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern(subresource_filter::kActivationConsoleMessage);
GURL test_url(embedded_test_server()->GetURL("/simple_page.html"));
subresource_filter::TestSubresourceFilterObserver observer(web_contents);
base::Optional<subresource_filter::mojom::ActivationLevel> page_activation =
observer.GetPageActivation(test_url);
EXPECT_FALSE(page_activation);
// Verify that a navigation results in both (a) the page activation level
// being computed, and (b) the result of that computation being the default
// level of "dry run" due to AdTagging.
NavigateAndWaitForCompletion(test_url, shell());
page_activation = observer.GetPageActivation(test_url);
EXPECT_TRUE(page_activation);
EXPECT_EQ(subresource_filter::mojom::ActivationLevel::kDryRun,
page_activation.value());
EXPECT_TRUE(console_observer.messages().empty());
}
#endif // (OS_ANDROID)
// Verifies that subframes that are flagged by the subresource filter ruleset
// are blocked from loading on activated URLs.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
DisallowedSubframeURLBlockedOnActivatedURL) {
auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
content::WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern(subresource_filter::kActivationConsoleMessage);
GURL test_url(
embedded_test_server()->GetURL("/frame_with_included_script.html"));
subresource_filter::TestSubresourceFilterObserver observer(web_contents);
base::Optional<subresource_filter::mojom::ActivationLevel> page_activation =
observer.GetPageActivation(test_url);
EXPECT_FALSE(page_activation);
// Configure the database manager to activate on this URL.
scoped_refptr<FakeSafeBrowsingDatabaseManager> database_manager =
base::MakeRefCounted<FakeSafeBrowsingDatabaseManager>();
database_manager->AddBlocklistedUrl(
test_url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
auto* client_impl = static_cast<SubresourceFilterClientImpl*>(
subresource_filter::ContentSubresourceFilterThrottleManager::
FromWebContents(web_contents)
->client());
client_impl->set_database_manager_for_testing(database_manager);
// Verify that the "ad" subframe is loaded if it is not flagged by the
// ruleset.
ASSERT_NO_FATAL_FAILURE(SetRulesetToDisallowURLsWithPathSuffix(
"suffix-that-does-not-match-anything"));
NavigateAndWaitForCompletion(test_url, shell());
// The subresource filter should have been activated on this navigation...
page_activation = observer.GetPageActivation(test_url);
EXPECT_TRUE(page_activation);
EXPECT_EQ(subresource_filter::mojom::ActivationLevel::kEnabled,
page_activation.value());
EXPECT_FALSE(console_observer.messages().empty());
// ... but it should not have blocked the subframe from being loaded.
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// Verify that the "ad" subframe is blocked if it is flagged by the
// ruleset.
ASSERT_NO_FATAL_FAILURE(
SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// The main frame document should never be filtered.
SetRulesetToDisallowURLsWithPathSuffix("frame_with_included_script.html");
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
}
// Verifies that subframes are not blocked on non-activated URLs.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
DisallowedSubframeURLNotBlockedOnNonActivatedURL) {
auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
GURL test_url(
embedded_test_server()->GetURL("/frame_with_included_script.html"));
// Verify that the "ad" subframe is loaded if it is not flagged by the
// ruleset.
ASSERT_NO_FATAL_FAILURE(SetRulesetToDisallowURLsWithPathSuffix(
"suffix-that-does-not-match-anything"));
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// Verify that the "ad" subframe is loaded if even it is flagged by the
// ruleset as the URL is not activated.
ASSERT_NO_FATAL_FAILURE(
SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
}
} // namespace weblayer
// Copyright 2020 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 "weblayer/browser/subresource_filter_client_impl.h"
#include <string>
#include <utility>
#include "base/macros.h"
#include "build/build_config.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.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_decision.h"
#include "components/subresource_filter/core/common/activation_scope.h"
#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
#if defined(OS_ANDROID)
#include "components/safe_browsing/android/remote_database_manager.h"
#endif
namespace weblayer {
namespace {
// Returns a scoped refptr to the SafeBrowsingService's database manager, if
// available. Otherwise returns nullptr.
const scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
GetDatabaseManagerFromSafeBrowsingService() {
#if defined(OS_ANDROID)
SafeBrowsingService* safe_browsing_service =
BrowserProcess::GetInstance()->GetSafeBrowsingService();
return safe_browsing_service
? scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>(
safe_browsing_service->GetSafeBrowsingDBManager())
: nullptr;
#else
return nullptr;
#endif
}
} // namespace
SubresourceFilterClientImpl::SubresourceFilterClientImpl()
: database_manager_(GetDatabaseManagerFromSafeBrowsingService()) {}
SubresourceFilterClientImpl::~SubresourceFilterClientImpl() = default;
// static
void SubresourceFilterClientImpl::CreateThrottleManagerWithClientForWebContents(
content::WebContents* web_contents) {
subresource_filter::RulesetService* ruleset_service =
BrowserProcess::GetInstance()->subresource_filter_ruleset_service();
subresource_filter::VerifiedRulesetDealer::Handle* dealer =
ruleset_service ? ruleset_service->GetRulesetDealer() : nullptr;
subresource_filter::ContentSubresourceFilterThrottleManager::
CreateForWebContents(web_contents,
std::make_unique<SubresourceFilterClientImpl>(),
dealer);
}
void SubresourceFilterClientImpl::OnReloadRequested() {
// TODO(crbug.com/1116095): Bring up this flow on Android when user requests
// it via the infobar.
NOTREACHED();
}
void SubresourceFilterClientImpl::ShowNotification() {
// TODO(crbug.com/1116095): Show infobar on Android.
}
subresource_filter::mojom::ActivationLevel
SubresourceFilterClientImpl::OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
subresource_filter::mojom::ActivationLevel initial_activation_level,
subresource_filter::ActivationDecision* decision) {
DCHECK(navigation_handle->IsInMainFrame());
return initial_activation_level;
}
void SubresourceFilterClientImpl::OnAdsViolationTriggered(
content::RenderFrameHost* rfh,
subresource_filter::mojom::AdsViolation triggered_violation) {}
const scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
SubresourceFilterClientImpl::GetSafeBrowsingDatabaseManager() {
return database_manager_;
}
} // namespace weblayer
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_SUBRESOURCE_FILTER_CLIENT_IMPL_H_
#define WEBLAYER_BROWSER_SUBRESOURCE_FILTER_CLIENT_IMPL_H_
#include <memory>
#include "components/safe_browsing/core/db/database_manager.h"
#include "components/subresource_filter/content/browser/subresource_filter_client.h"
#include "url/gurl.h"
namespace content {
class WebContents;
} // namespace content
namespace subresource_filter {
class ContentSubresourceFilterThrottleManager;
} // namespace subresource_filter
namespace weblayer {
// WebLayer implementation of SubresourceFilterClient. Instances are associated
// with and owned by ContentSubresourceFilterThrottleManager instances.
class SubresourceFilterClientImpl
: public subresource_filter::SubresourceFilterClient {
public:
SubresourceFilterClientImpl();
~SubresourceFilterClientImpl() override;
SubresourceFilterClientImpl(const SubresourceFilterClientImpl&) = delete;
SubresourceFilterClientImpl& operator=(const SubresourceFilterClientImpl&) =
delete;
// Creates a ContentSubresourceFilterThrottleManager and attaches it to
// |web_contents|, passing it an instance of this client and other
// embedder-level state.
static void CreateThrottleManagerWithClientForWebContents(
content::WebContents* web_contents);
// SubresourceFilterClient:
void ShowNotification() override;
subresource_filter::mojom::ActivationLevel OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
subresource_filter::mojom::ActivationLevel initial_activation_level,
subresource_filter::ActivationDecision* decision) override;
void OnAdsViolationTriggered(
content::RenderFrameHost* rfh,
subresource_filter::mojom::AdsViolation triggered_violation) override;
const scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
GetSafeBrowsingDatabaseManager() override;
void OnReloadRequested() override;
// Sets the SafeBrowsingDatabaseManager instance used to |database_manager|.
void set_database_manager_for_testing(
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
database_manager) {
database_manager_ = database_manager;
}
private:
std::unique_ptr<subresource_filter::ContentSubresourceFilterThrottleManager>
throttle_manager_;
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager_;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_SUBRESOURCE_FILTER_CLIENT_IMPL_H_
......@@ -71,6 +71,7 @@
#include "weblayer/browser/persistence/browser_persister.h"
#include "weblayer/browser/popup_navigation_delegate_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/subresource_filter_client_impl.h"
#include "weblayer/browser/translate_client_impl.h"
#include "weblayer/browser/weblayer_features.h"
#include "weblayer/common/isolated_world_ids.h"
......@@ -307,6 +308,9 @@ TabImpl::TabImpl(ProfileImpl* profile,
TranslateClientImpl::CreateForWebContents(web_contents_.get());
SubresourceFilterClientImpl::CreateThrottleManagerWithClientForWebContents(
web_contents_.get());
sessions::SessionTabHelper::CreateForWebContents(
web_contents_.get(),
base::BindRepeating(&TabImpl::GetSessionServiceTabHelperDelegate));
......
......@@ -14,6 +14,8 @@ include_rules = [
"+components/security_interstitials/content/renderer",
"+components/security_interstitials/core/common",
"+components/spellcheck/renderer",
"+components/subresource_filter/content/renderer",
"+components/subresource_filter/core/common",
"+components/translate/content/renderer",
"+components/translate/core/common",
"+content/public/common",
......
......@@ -19,6 +19,9 @@
#include "components/no_state_prefetch/renderer/prerender_utils.h"
#include "components/no_state_prefetch/renderer/prerenderer_client.h"
#include "components/page_load_metrics/renderer/metrics_render_frame_observer.h"
#include "components/subresource_filter/content/renderer/subresource_filter_agent.h"
#include "components/subresource_filter/content/renderer/unverified_ruleset_dealer.h"
#include "components/subresource_filter/core/common/common_features.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
......@@ -85,6 +88,10 @@ void ContentRendererClientImpl::RenderThreadStarted() {
browser_interface_broker_ =
blink::Platform::Current()->GetBrowserInterfaceBroker();
subresource_filter_ruleset_dealer_ =
std::make_unique<subresource_filter::UnverifiedRulesetDealer>();
thread->AddObserver(subresource_filter_ruleset_dealer_.get());
}
void ContentRendererClientImpl::RenderFrameCreated(
......@@ -108,6 +115,11 @@ void ContentRendererClientImpl::RenderFrameCreated(
new page_load_metrics::MetricsRenderFrameObserver(render_frame);
// TODO(crbug.com/1116095): Bring up AdResourceTracker?
new subresource_filter::SubresourceFilterAgent(
render_frame, subresource_filter_ruleset_dealer_.get(),
/*ad_resource_tracker=*/nullptr);
#if defined(OS_ANDROID)
// |SpellCheckProvider| manages its own lifetime (and destroys itself when the
// RenderFrame is destroyed).
......
......@@ -16,6 +16,10 @@ namespace service_manager {
class LocalInterfaceProvider;
} // namespace service_manager
namespace subresource_filter {
class UnverifiedRulesetDealer;
}
namespace weblayer {
class WebLayerRenderThreadObserver;
......@@ -54,6 +58,9 @@ class ContentRendererClientImpl : public content::ContentRendererClient {
std::unique_ptr<SpellCheck> spellcheck_;
#endif
std::unique_ptr<subresource_filter::UnverifiedRulesetDealer>
subresource_filter_ruleset_dealer_;
std::unique_ptr<WebLayerRenderThreadObserver> weblayer_observer_;
scoped_refptr<blink::ThreadSafeBrowserInterfaceBrokerProxy>
......
......@@ -112,6 +112,9 @@ test("weblayer_browsertests") {
"//components/site_isolation",
"//components/strings",
"//components/subresource_filter/content/browser",
"//components/subresource_filter/content/browser:test_support",
"//components/subresource_filter/core/browser",
"//components/subresource_filter/core/common:test_support",
"//components/translate/content/browser",
"//components/translate/content/browser:test_support",
"//components/ukm:test_support",
......
<html>
<head>
<script src="included_script.js"></script>
</head>
</html>
// Copyright 2020 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.
document.scriptExecuted = true;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment