blob: 4d640202d7d31aa2956f3585f42403ebb3f66af0 [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 <string>
#include "base/bind.h"
#include "base/check.h"
#include "base/time/default_clock.h"
#include "base/values.h"
#include "components/content_settings/core/browser/content_settings_constraints.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/keyed_service/core/service_access_type.h"
#include "url/gurl.h"
namespace {
// Key into the website setting dict for the smart UI.
const char kInfobarLastShownTimeKey[] = "InfobarLastShownTime";
const char kActivatedKey[] = "Activated";
const char kNonRenewingExpiryTime[] = "NonRenewingExpiryTime";
bool ShouldUseSmartUI() {
#if defined(OS_ANDROID)
return true;
#endif
return false;
}
} // namespace
constexpr base::TimeDelta
SubresourceFilterContentSettingsManager::kDelayBeforeShowingInfobarAgain;
constexpr base::TimeDelta
SubresourceFilterContentSettingsManager::kMaxPersistMetadataDuration;
SubresourceFilterContentSettingsManager::
SubresourceFilterContentSettingsManager(
HostContentSettingsMap* settings_map,
history::HistoryService* history_service)
: settings_map_(settings_map),
clock_(std::make_unique<base::DefaultClock>(base::DefaultClock())),
should_use_smart_ui_(ShouldUseSmartUI()) {
DCHECK(settings_map_);
if (history_service)
history_observer_.Add(history_service);
}
SubresourceFilterContentSettingsManager::
~SubresourceFilterContentSettingsManager() = default;
ContentSetting SubresourceFilterContentSettingsManager::GetSitePermission(
const GURL& url) const {
return settings_map_->GetContentSetting(url, GURL(), ContentSettingsType::ADS,
std::string());
}
void SubresourceFilterContentSettingsManager::AllowlistSite(const GURL& url) {
settings_map_->SetContentSettingDefaultScope(
url, GURL(), ContentSettingsType::ADS, std::string(),
CONTENT_SETTING_ALLOW);
}
void SubresourceFilterContentSettingsManager::OnDidShowUI(const GURL& url) {
std::unique_ptr<base::DictionaryValue> dict = GetSiteMetadata(url);
if (!dict)
dict = CreateMetadataDictWithActivation(true /* is_activated */);
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::SetSiteMetadataBasedOnActivation(
const GURL& url,
bool is_activated,
ActivationSource activation_source,
std::unique_ptr<base::DictionaryValue> additional_data) {
std::unique_ptr<base::DictionaryValue> dict = GetSiteMetadata(url);
if (!is_activated &&
ShouldDeleteDataWithNoActivation(dict.get(), activation_source)) {
// If we are clearing metadata, there should be no additional_data dict.
DCHECK(!additional_data);
SetSiteMetadata(url, nullptr);
return;
}
// Do not create new metadata if it exists already, it could clobber
// existing data.
if (!dict)
dict = CreateMetadataDictWithActivation(is_activated /* is_activated */);
else
dict->SetBoolKey(kActivatedKey, is_activated);
if (additional_data)
dict->MergeDictionary(additional_data.get());
// Ads intervention metadata should not be deleted by changes in activation
// during the metrics collection period (kMaxPersistMetadataDuration).
// Setting the key kNonRenewingExpiryTime enforces this behavior in
// SetSiteMetadata.
if (activation_source == ActivationSource::kAdsIntervention) {
// If we have an expiry time set, then we are already tracking
// an ads intervention. Since we should not be able to trigger a new ads
// intervention once we should be blocking ads, do not change the expiry
// time or overwrite existing ads intervention metadata,
if (dict->FindDoubleKey(kNonRenewingExpiryTime))
return;
double expiry_time =
(clock_->Now() + kMaxPersistMetadataDuration).ToDoubleT();
dict->SetDoubleKey(kNonRenewingExpiryTime, expiry_time);
}
SetSiteMetadata(url, std::move(dict));
}
std::unique_ptr<base::DictionaryValue>
SubresourceFilterContentSettingsManager::GetSiteMetadata(
const GURL& url) const {
return base::DictionaryValue::From(settings_map_->GetWebsiteSetting(
url, GURL(), ContentSettingsType::ADS_DATA, std::string(), nullptr));
}
void SubresourceFilterContentSettingsManager::SetSiteMetadataForTesting(
const GURL& url,
std::unique_ptr<base::DictionaryValue> dict) {
SetSiteMetadata(url, std::move(dict));
}
void SubresourceFilterContentSettingsManager::SetSiteMetadata(
const GURL& url,
std::unique_ptr<base::DictionaryValue> dict) {
// Metadata expires after kMaxPersistMetadataDuration by default. If
// kNonRenewingExpiryTime was previously set, then we are storing ads
// intervention metadata and should not override the expiry time that
// was previously set.
base::Time expiry_time = base::Time::Now() + kMaxPersistMetadataDuration;
if (dict && dict->HasKey(kNonRenewingExpiryTime)) {
base::Optional<double> metadata_expiry_time =
dict->FindDoubleKey(kNonRenewingExpiryTime);
DCHECK(metadata_expiry_time);
expiry_time = base::Time::FromDoubleT(*metadata_expiry_time);
}
content_settings::ContentSettingConstraints constraints = {expiry_time};
settings_map_->SetWebsiteSettingDefaultScope(
url, GURL(), ContentSettingsType::ADS_DATA, std::string(),
std::move(dict), constraints);
}
std::unique_ptr<base::DictionaryValue>
SubresourceFilterContentSettingsManager::CreateMetadataDictWithActivation(
bool is_activated) {
auto dict = std::make_unique<base::DictionaryValue>();
dict->SetBoolKey(kActivatedKey, is_activated);
return dict;
}
bool SubresourceFilterContentSettingsManager::ShouldDeleteDataWithNoActivation(
base::DictionaryValue* dict,
ActivationSource activation_source) {
// For the ads intervention dry run experiment we want to make sure that
// non activated pages get properly tagged for metrics collection. Don't
// delete them from storage until their associated intervention _would have_
// expired.
if (activation_source != ActivationSource::kSafeBrowsing)
return false;
if (!dict)
return true;
base::Optional<double> metadata_expiry_time =
dict->FindDoubleKey(kNonRenewingExpiryTime);
if (!metadata_expiry_time)
return true;
base::Time expiry_time = base::Time::FromDoubleT(*metadata_expiry_time);
return clock_->Now() > expiry_time;
}
// When history URLs are deleted, clear the metadata for the smart UI.
void SubresourceFilterContentSettingsManager::OnURLsDeleted(
history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
if (deletion_info.IsAllHistory()) {
settings_map_->ClearSettingsForOneType(ContentSettingsType::ADS_DATA);
return;
}
for (const auto& entry : deletion_info.deleted_urls_origin_map()) {
const GURL& origin = entry.first;
int remaining_urls = entry.second.first;
if (!origin.is_empty() && remaining_urls == 0)
SetSiteMetadata(origin, nullptr);
}
}
bool SubresourceFilterContentSettingsManager::GetSiteActivationFromMetadata(
const GURL& url) {
std::unique_ptr<base::DictionaryValue> dict = GetSiteMetadata(url);
// If there is no dict, this is metadata V1, absence of metadata
// implies no activation.
if (!dict)
return false;
base::Optional<bool> site_activation_status =
dict->FindBoolKey(kActivatedKey);
// If there is no explicit site activation status, it is metadata V1:
// use the presence of metadata as indicative of the site activation.
// Otherwise it is metadata V2, we return the activation stored in
// kActivatedKey.
return !site_activation_status || *site_activation_status;
}