blob: bc9d0411ccf53746c2da752b9ec04ef79004b8fa [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
#include <string>
#include <utility>
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/tab_specific_content_settings.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
#include "chrome/browser/subresource_filter/subresource_filter_profile_context.h"
#include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/rappor/public/rappor_parameters.h"
#include "components/rappor/public/rappor_utils.h"
#include "components/rappor/rappor_service_impl.h"
#include "components/safe_browsing/db/database_manager.h"
#include "components/subresource_filter/content/browser/content_ruleset_service.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/subresource_filter/core/common/activation_level.h"
#include "components/subresource_filter/core/common/activation_scope.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#if defined(OS_ANDROID)
#include "chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.h"
#endif
DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromeSubresourceFilterClient);
namespace {
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> GetDatabaseManager() {
safe_browsing::SafeBrowsingService* safe_browsing_service =
g_browser_process->safe_browsing_service();
bool has_supported_manager =
safe_browsing_service &&
safe_browsing_service->database_manager()->IsSupported() &&
safe_browsing_service->database_manager()->CanCheckSubresourceFilter();
return has_supported_manager ? safe_browsing_service->database_manager()
: nullptr;
}
} // namespace
ChromeSubresourceFilterClient::ChromeSubresourceFilterClient(
content::WebContents* web_contents)
: web_contents_(web_contents) {
DCHECK(web_contents);
SubresourceFilterProfileContext* context =
SubresourceFilterProfileContextFactory::GetForProfile(
Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
settings_manager_ = context->settings_manager();
subresource_filter::ContentSubresourceFilterDriverFactory::
CreateForWebContents(web_contents, this);
}
ChromeSubresourceFilterClient::~ChromeSubresourceFilterClient() {}
void ChromeSubresourceFilterClient::MaybeAppendNavigationThrottles(
content::NavigationHandle* navigation_handle,
std::vector<std::unique_ptr<content::NavigationThrottle>>* throttles) {
if (navigation_handle->IsInMainFrame()) {
throttles->push_back(
base::MakeUnique<subresource_filter::
SubresourceFilterSafeBrowsingActivationThrottle>(
navigation_handle, this,
content::BrowserThread::GetTaskRunnerForThread(
content::BrowserThread::IO),
GetDatabaseManager()));
}
auto* driver_factory =
subresource_filter::ContentSubresourceFilterDriverFactory::
FromWebContents(navigation_handle->GetWebContents());
driver_factory->throttle_manager()->MaybeAppendNavigationThrottles(
navigation_handle, throttles);
}
void ChromeSubresourceFilterClient::OnReloadRequested() {
UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.Prompt.NumReloads", true);
const GURL& whitelist_url = web_contents_->GetLastCommittedURL();
// Only whitelist via content settings when using the experimental UI,
// otherwise could get into a situation where content settings cannot be
// adjusted.
if (base::FeatureList::IsEnabled(
subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI)) {
WhitelistByContentSettings(whitelist_url);
} else {
WhitelistInCurrentWebContents(whitelist_url);
}
web_contents_->GetController().Reload(content::ReloadType::NORMAL, true);
}
void ChromeSubresourceFilterClient::ShowNotification() {
// Do not show the UI if we're forcing activation due to a devtools toggle.
// This complicates the meaning of our persistent storage (e.g. our metadata
// that assumes showing UI implies site is blacklisted).
if (activated_via_devtools_) {
LogAction(kActionForcedActivationNoUIResourceBlocked);
return;
}
if (did_show_ui_for_navigation_)
return;
const GURL& top_level_url = web_contents_->GetLastCommittedURL();
if (settings_manager_->ShouldShowUIForSite(top_level_url)) {
ShowUI(top_level_url);
} else {
LogAction(kActionUISuppressed);
}
}
void ChromeSubresourceFilterClient::OnNewNavigationStarted() {
did_show_ui_for_navigation_ = false;
LogAction(kActionNavigationStarted);
}
bool ChromeSubresourceFilterClient::OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
bool activated,
bool suppressing_notifications) {
const GURL& url(navigation_handle->GetURL());
DCHECK(navigation_handle->IsInMainFrame());
if (url.SchemeIsHTTPOrHTTPS()) {
// With respect to persistent metadata, do not consider the site activated
// if it is forced via devtools, or if we are suppressing notifications.
settings_manager_->ResetSiteMetadataBasedOnActivation(
url,
activated && !activated_via_devtools_ && !suppressing_notifications);
}
// Return whether the activation should be whitelisted.
// Note: Could consider skipping this if forcing activation, but it isn't
// critical.
return whitelisted_hosts_.count(url.host()) ||
settings_manager_->GetSitePermission(url) == CONTENT_SETTING_ALLOW;
// TODO(csharrison): Consider setting the metadata to an empty dict here if
// the site is activated and not whitelisted. Need to be careful about various
// edge cases like |should_suppress_notification| and DRYRUN activation.
}
void ChromeSubresourceFilterClient::WhitelistByContentSettings(
const GURL& top_level_url) {
settings_manager_->WhitelistSite(top_level_url);
}
bool ChromeSubresourceFilterClient::ForceActivationInCurrentWebContents() {
return activated_via_devtools_;
}
void ChromeSubresourceFilterClient::WhitelistInCurrentWebContents(
const GURL& url) {
if (url.SchemeIsHTTPOrHTTPS())
whitelisted_hosts_.insert(url.host());
}
void ChromeSubresourceFilterClient::ToggleForceActivationInCurrentWebContents(
bool force_activation) {
if (!activated_via_devtools_ && force_activation)
LogAction(kActionForcedActivationEnabled);
activated_via_devtools_ = force_activation;
}
// static
void ChromeSubresourceFilterClient::LogAction(SubresourceFilterAction action) {
UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.Actions", action,
kActionLastEntry);
}
subresource_filter::VerifiedRulesetDealer::Handle*
ChromeSubresourceFilterClient::GetRulesetDealer() {
subresource_filter::ContentRulesetService* ruleset_service =
g_browser_process->subresource_filter_ruleset_service();
return ruleset_service ? ruleset_service->ruleset_dealer() : nullptr;
}
void ChromeSubresourceFilterClient::ShowUI(const GURL& url) {
DCHECK(!activated_via_devtools_);
DCHECK_NE(subresource_filter::ContentSubresourceFilterDriverFactory::
FromWebContents(web_contents_)
->GetMatchedConfigurationForLastCommittedPageLoad(),
subresource_filter::Configuration::MakeForForcedActivation());
#if defined(OS_ANDROID)
InfoBarService* infobar_service =
InfoBarService::FromWebContents(web_contents_);
AdsBlockedInfobarDelegate::Create(infobar_service);
#endif
TabSpecificContentSettings* content_settings =
TabSpecificContentSettings::FromWebContents(web_contents_);
content_settings->OnContentBlocked(CONTENT_SETTINGS_TYPE_ADS);
LogAction(kActionUIShown);
if (rappor::RapporService* rappor_service =
g_browser_process->rappor_service()) {
rappor_service->RecordSampleString(
"SubresourceFilter.UIShown", rappor::UMA_RAPPOR_TYPE,
rappor::GetDomainAndRegistrySampleFromGURL(url));
}
did_show_ui_for_navigation_ = true;
settings_manager_->OnDidShowUI(url);
}