blob: 91ae1c7d91e90e5c73bfb4772b19ea4a679a2a0d [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 "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/time/default_clock.h"
#include "base/values.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
#include "components/content_settings/core/browser/content_settings_details.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_pattern.h"
#include "components/history/core/browser/history_service.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "url/gurl.h"
namespace {
// Key into the website setting dict for the smart UI.
const char kInfobarLastShownTimeKey[] = "InfobarLastShownTime";
bool ShouldUseSmartUI() {
#if defined(OS_ANDROID)
return base::FeatureList::IsEnabled(
subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI);
#endif
return false;
}
} // namespace
constexpr base::TimeDelta
SubresourceFilterContentSettingsManager::kDelayBeforeShowingInfobarAgain;
SubresourceFilterContentSettingsManager::
SubresourceFilterContentSettingsManager(Profile* profile)
: history_observer_(this),
settings_map_(HostContentSettingsMapFactory::GetForProfile(profile)),
clock_(base::MakeUnique<base::DefaultClock>(base::DefaultClock())),
should_use_smart_ui_(ShouldUseSmartUI()) {
DCHECK(profile);
DCHECK(settings_map_);
settings_map_->AddObserver(this);
if (auto* history_service = HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS)) {
history_observer_.Add(history_service);
}
cached_global_setting_for_metrics_ = settings_map_->GetDefaultContentSetting(
CONTENT_SETTINGS_TYPE_ADS, nullptr);
}
SubresourceFilterContentSettingsManager::
~SubresourceFilterContentSettingsManager() {
settings_map_->RemoveObserver(this);
settings_map_ = nullptr;
history_observer_.RemoveAll();
}
ContentSetting SubresourceFilterContentSettingsManager::GetSitePermission(
const GURL& url) const {
return settings_map_->GetContentSetting(
url, GURL(), ContentSettingsType::CONTENT_SETTINGS_TYPE_ADS,
std::string());
}
void SubresourceFilterContentSettingsManager::WhitelistSite(const GURL& url) {
DCHECK(base::FeatureList::IsEnabled(
subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI));
base::AutoReset<bool> resetter(&ignore_settings_changes_, true);
settings_map_->SetContentSettingDefaultScope(
url, GURL(), ContentSettingsType::CONTENT_SETTINGS_TYPE_ADS,
std::string(), CONTENT_SETTING_ALLOW);
ChromeSubresourceFilterClient::LogAction(kActionContentSettingsAllowedFromUI);
}
void SubresourceFilterContentSettingsManager::OnDidShowUI(const GURL& url) {
auto dict = base::MakeUnique<base::DictionaryValue>();
double now = clock_->Now().ToDoubleT();
dict->SetDouble(kInfobarLastShownTimeKey, now);
SetSiteMetadata(url, std::move(dict));
}
bool SubresourceFilterContentSettingsManager::ShouldShowUIForSite(
const GURL& url) const {
if (!should_use_smart_ui())
return true;
std::unique_ptr<base::DictionaryValue> dict = GetSiteMetadata(url);
if (!dict)
return true;
double last_shown_time_double = 0;
if (dict->GetDouble(kInfobarLastShownTimeKey, &last_shown_time_double)) {
base::Time last_shown = base::Time::FromDoubleT(last_shown_time_double);
if (clock_->Now() - last_shown < kDelayBeforeShowingInfobarAgain)
return false;
}
return true;
}
void SubresourceFilterContentSettingsManager::
ResetSiteMetadataBasedOnActivation(const GURL& url, bool is_activated) {
if (!is_activated) {
SetSiteMetadata(url, nullptr);
} else if (!GetSiteMetadata(url)) {
SetSiteMetadata(url, base::MakeUnique<base::DictionaryValue>());
}
}
std::unique_ptr<base::DictionaryValue>
SubresourceFilterContentSettingsManager::GetSiteMetadata(
const GURL& url) const {
return base::DictionaryValue::From(settings_map_->GetWebsiteSetting(
url, GURL(), CONTENT_SETTINGS_TYPE_ADS_DATA, std::string(), nullptr));
}
void SubresourceFilterContentSettingsManager::SetSiteMetadata(
const GURL& url,
std::unique_ptr<base::DictionaryValue> dict) {
if (!base::FeatureList::IsEnabled(
subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI))
return;
settings_map_->SetWebsiteSettingDefaultScope(
url, GURL(), ContentSettingsType::CONTENT_SETTINGS_TYPE_ADS_DATA,
std::string(), std::move(dict));
}
void SubresourceFilterContentSettingsManager::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
std::string resource_identifier) {
if (content_type != CONTENT_SETTINGS_TYPE_ADS || ignore_settings_changes_)
return;
const ContentSettingsDetails details(primary_pattern, secondary_pattern,
content_type, resource_identifier);
DCHECK(!details.update_all_types());
if (details.update_all()) {
ContentSetting global_setting = settings_map_->GetDefaultContentSetting(
CONTENT_SETTINGS_TYPE_ADS, nullptr);
// Ignore changes which retain the status quo. This also avoids logging
// metrics for changes which somehow notify this observer multiple times in
// a row. This shouldn't discount real user initiated changes.
if (global_setting == cached_global_setting_for_metrics_)
return;
cached_global_setting_for_metrics_ = global_setting;
if (global_setting == CONTENT_SETTING_ALLOW) {
ChromeSubresourceFilterClient::LogAction(
kActionContentSettingsAllowedGlobal);
} else if (global_setting == CONTENT_SETTING_BLOCK) {
ChromeSubresourceFilterClient::LogAction(
kActionContentSettingsBlockedGlobal);
} else {
NOTREACHED();
}
return;
}
// Remove this DCHECK if extension APIs or admin policies are given the
// ability to set secondary patterns for this setting.
DCHECK(secondary_pattern == ContentSettingsPattern::Wildcard());
DCHECK(primary_pattern.IsValid());
// An invalid URL indicates that this is a wildcard pattern.
GURL url = GURL(primary_pattern.ToString());
if (!url.is_valid()) {
ChromeSubresourceFilterClient::LogAction(
kActionContentSettingsWildcardUpdate);
return;
}
ContentSetting setting = settings_map_->GetContentSetting(
url, url, ContentSettingsType::CONTENT_SETTINGS_TYPE_ADS, std::string());
if (setting == CONTENT_SETTING_ALLOW) {
ChromeSubresourceFilterClient::LogAction(kActionContentSettingsAllowed);
} else if (setting == CONTENT_SETTING_BLOCK) {
ChromeSubresourceFilterClient::LogAction(kActionContentSettingsBlocked);
} else {
NOTREACHED();
}
if (!ShouldShowUIForSite(url)) {
ChromeSubresourceFilterClient::LogAction(
kActionContentSettingsAllowedWhileUISuppressed);
}
}
// When history URLs are deleted, clear the metadata for the smart UI.
void SubresourceFilterContentSettingsManager::OnURLsDeleted(
history::HistoryService* history_service,
bool all_history,
bool expired,
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) {
if (all_history) {
settings_map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_ADS_DATA);
return;
}
// Careful note: This clears the setting even if there are other URLs with the
// same origin in the history. i.e. this is an aggressive policy.
for (const auto& deleted_row : deleted_rows) {
SetSiteMetadata(deleted_row.url(), nullptr);
}
}