blob: 309f1230eb131912b562c3ba1fad4d67559403ca [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/permissions/permission_decision_auto_blocker.h"
#include <memory>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/permissions/permission_util.h"
#include "chrome/common/chrome_features.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/permission_type.h"
#include "url/gurl.h"
namespace {
// The number of times that users may explicitly dismiss a permission prompt
// from an origin before it is automatically blocked.
int g_prompt_dismissals_before_block = 3;
std::unique_ptr<base::DictionaryValue> GetOriginDict(
HostContentSettingsMap* settings,
const GURL& origin_url) {
std::unique_ptr<base::DictionaryValue> dict =
base::DictionaryValue::From(settings->GetWebsiteSetting(
origin_url, origin_url,
CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT, std::string(),
nullptr));
if (!dict)
return base::MakeUnique<base::DictionaryValue>();
return dict;
}
base::DictionaryValue* GetOrCreatePermissionDict(
base::DictionaryValue* origin_dict,
const std::string& permission) {
base::DictionaryValue* permission_dict = nullptr;
if (!origin_dict->GetDictionaryWithoutPathExpansion(permission,
&permission_dict)) {
permission_dict = new base::DictionaryValue();
origin_dict->SetWithoutPathExpansion(permission,
base::WrapUnique(permission_dict));
}
return permission_dict;
}
int RecordActionInWebsiteSettings(const GURL& url,
content::PermissionType permission,
const char* key,
Profile* profile) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
std::unique_ptr<base::DictionaryValue> dict = GetOriginDict(map, url);
base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
dict.get(), PermissionUtil::GetPermissionString(permission));
int current_count = 0;
permission_dict->GetInteger(key, &current_count);
permission_dict->SetInteger(key, ++current_count);
map->SetWebsiteSettingDefaultScope(
url, GURL(), CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
std::string(), std::move(dict));
return current_count;
}
int GetActionCount(const GURL& url,
content::PermissionType permission,
const char* key,
Profile* profile) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
std::unique_ptr<base::DictionaryValue> dict = GetOriginDict(map, url);
base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
dict.get(), PermissionUtil::GetPermissionString(permission));
int current_count = 0;
permission_dict->GetInteger(key, &current_count);
return current_count;
}
} // namespace
// static
const char PermissionDecisionAutoBlocker::kPromptDismissCountKey[] =
"dismiss_count";
// static
const char PermissionDecisionAutoBlocker::kPromptIgnoreCountKey[] =
"ignore_count";
// static
void PermissionDecisionAutoBlocker::RemoveCountsByUrl(
Profile* profile,
base::Callback<bool(const GURL& url)> filter) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile);
std::unique_ptr<ContentSettingsForOneType> settings(
new ContentSettingsForOneType);
map->GetSettingsForOneType(CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
std::string(), settings.get());
for (const auto& site : *settings) {
GURL origin(site.primary_pattern.ToString());
if (origin.is_valid() && filter.Run(origin)) {
map->SetWebsiteSettingDefaultScope(
origin, GURL(), CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
std::string(), nullptr);
}
}
}
// static
int PermissionDecisionAutoBlocker::GetDismissCount(
const GURL& url,
content::PermissionType permission,
Profile* profile) {
return GetActionCount(url, permission, kPromptDismissCountKey, profile);
}
// static
int PermissionDecisionAutoBlocker::GetIgnoreCount(
const GURL& url,
content::PermissionType permission,
Profile* profile) {
return GetActionCount(url, permission, kPromptIgnoreCountKey, profile);
}
// static
int PermissionDecisionAutoBlocker::RecordDismiss(
const GURL& url,
content::PermissionType permission,
Profile* profile) {
return RecordActionInWebsiteSettings(url, permission, kPromptDismissCountKey,
profile);
}
// static
int PermissionDecisionAutoBlocker::RecordIgnore(
const GURL& url,
content::PermissionType permission,
Profile* profile) {
return RecordActionInWebsiteSettings(url, permission, kPromptIgnoreCountKey,
profile);
}
// static
bool PermissionDecisionAutoBlocker::ShouldChangeDismissalToBlock(
const GURL& url,
content::PermissionType permission,
Profile* profile) {
int current_dismissal_count = RecordDismiss(url, permission, profile);
if (!base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften))
return false;
return current_dismissal_count >= g_prompt_dismissals_before_block;
}
// static
void PermissionDecisionAutoBlocker::UpdateFromVariations() {
int prompt_dismissals = -1;
std::string value = variations::GetVariationParamValueByFeature(
features::kBlockPromptsIfDismissedOften, kPromptDismissCountKey);
// If converting the value fails, stick with the current value.
if (base::StringToInt(value, &prompt_dismissals) && prompt_dismissals > 0)
g_prompt_dismissals_before_block = prompt_dismissals;
}