// Copyright (c) 2011 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/content_settings/content_settings_policy_provider.h"
#include <string>
#include <vector>
#include "base/command_line.h"
#include "chrome/browser/content_settings/content_settings_details.h"
#include "chrome/browser/content_settings/content_settings_pattern.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_details.h"
#include "content/common/notification_service.h"
#include "content/common/notification_source.h"
#include "webkit/plugins/npapi/plugin_group.h"
#include "webkit/plugins/npapi/plugin_list.h"
namespace {
// Base pref path of the prefs that contain the managed default content
// settings values.
const std::string kManagedSettings =
"profile.managed_default_content_settings";
// The preferences used to manage ContentSettingsTypes.
const char* kPrefToManageType[CONTENT_SETTINGS_NUM_TYPES] = {
prefs::kManagedDefaultCookiesSetting,
prefs::kManagedDefaultImagesSetting,
prefs::kManagedDefaultJavaScriptSetting,
prefs::kManagedDefaultPluginsSetting,
prefs::kManagedDefaultPopupsSetting,
NULL, // Not used for Geolocation
NULL, // Not used for Notifications
};
struct PrefsForManagedContentSettingsMapEntry {
const char* pref_name;
ContentSettingsType content_type;
ContentSetting setting;
};
const PrefsForManagedContentSettingsMapEntry
kPrefsForManagedContentSettingsMap[] = {
{
prefs::kManagedCookiesAllowedForUrls,
CONTENT_SETTINGS_TYPE_COOKIES,
CONTENT_SETTING_ALLOW
}, {
prefs::kManagedCookiesSessionOnlyForUrls,
CONTENT_SETTINGS_TYPE_COOKIES,
CONTENT_SETTING_SESSION_ONLY
}, {
prefs::kManagedCookiesBlockedForUrls,
CONTENT_SETTINGS_TYPE_COOKIES,
CONTENT_SETTING_BLOCK
}, {
prefs::kManagedImagesAllowedForUrls,
CONTENT_SETTINGS_TYPE_IMAGES,
CONTENT_SETTING_ALLOW
}, {
prefs::kManagedImagesBlockedForUrls,
CONTENT_SETTINGS_TYPE_IMAGES,
CONTENT_SETTING_BLOCK
}, {
prefs::kManagedJavaScriptAllowedForUrls,
CONTENT_SETTINGS_TYPE_JAVASCRIPT,
CONTENT_SETTING_ALLOW
}, {
prefs::kManagedJavaScriptBlockedForUrls,
CONTENT_SETTINGS_TYPE_JAVASCRIPT,
CONTENT_SETTING_BLOCK
}, {
prefs::kManagedPluginsAllowedForUrls,
CONTENT_SETTINGS_TYPE_PLUGINS,
CONTENT_SETTING_ALLOW
}, {
prefs::kManagedPluginsBlockedForUrls,
CONTENT_SETTINGS_TYPE_PLUGINS,
CONTENT_SETTING_BLOCK
}, {
prefs::kManagedPopupsAllowedForUrls,
CONTENT_SETTINGS_TYPE_POPUPS,
CONTENT_SETTING_ALLOW
}, {
prefs::kManagedPopupsBlockedForUrls,
CONTENT_SETTINGS_TYPE_POPUPS,
CONTENT_SETTING_BLOCK
}
};
} // namespace
namespace content_settings {
PolicyDefaultProvider::PolicyDefaultProvider(Profile* profile)
: profile_(profile),
is_off_the_record_(profile_->IsOffTheRecord()) {
PrefService* prefs = profile->GetPrefs();
// Read global defaults.
DCHECK_EQ(arraysize(kPrefToManageType),
static_cast<size_t>(CONTENT_SETTINGS_NUM_TYPES));
ReadManagedDefaultSettings();
pref_change_registrar_.Init(prefs);
// The following preferences are only used to indicate if a
// default-content-setting is managed and to hold the managed default-setting
// value. If the value for any of the following perferences is set then the
// corresponding default-content-setting is managed. These preferences exist
// in parallel to the preference default-content-settings. If a
// default-content-settings-type is managed any user defined excpetions
// (patterns) for this type are ignored.
pref_change_registrar_.Add(prefs::kManagedDefaultCookiesSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultImagesSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultJavaScriptSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultPluginsSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultPopupsSetting, this);
notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
Source<Profile>(profile_));
}
PolicyDefaultProvider::~PolicyDefaultProvider() {
UnregisterObservers();
}
ContentSetting PolicyDefaultProvider::ProvideDefaultSetting(
ContentSettingsType content_type) const {
base::AutoLock auto_lock(lock_);
return managed_default_content_settings_.settings[content_type];
}
void PolicyDefaultProvider::UpdateDefaultSetting(
ContentSettingsType content_type,
ContentSetting setting) {
}
bool PolicyDefaultProvider::DefaultSettingIsManaged(
ContentSettingsType content_type) const {
base::AutoLock lock(lock_);
if (managed_default_content_settings_.settings[content_type] !=
CONTENT_SETTING_DEFAULT) {
return true;
} else {
return false;
}
}
void PolicyDefaultProvider::ResetToDefaults() {
}
void PolicyDefaultProvider::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (type == NotificationType::PREF_CHANGED) {
DCHECK_EQ(profile_->GetPrefs(), Source<PrefService>(source).ptr());
std::string* name = Details<std::string>(details).ptr();
if (*name == prefs::kManagedDefaultCookiesSetting) {
UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_COOKIES);
} else if (*name == prefs::kManagedDefaultImagesSetting) {
UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_IMAGES);
} else if (*name == prefs::kManagedDefaultJavaScriptSetting) {
UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
} else if (*name == prefs::kManagedDefaultPluginsSetting) {
UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_PLUGINS);
} else if (*name == prefs::kManagedDefaultPopupsSetting) {
UpdateManagedDefaultSetting(CONTENT_SETTINGS_TYPE_POPUPS);
} else {
NOTREACHED() << "Unexpected preference observed";
return;
}
if (!is_off_the_record_) {
NotifyObservers(ContentSettingsDetails(
ContentSettingsPattern(), CONTENT_SETTINGS_TYPE_DEFAULT, ""));
}
} else if (type == NotificationType::PROFILE_DESTROYED) {
DCHECK_EQ(profile_, Source<Profile>(source).ptr());
UnregisterObservers();
} else {
NOTREACHED() << "Unexpected notification";
}
}
void PolicyDefaultProvider::UnregisterObservers() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!profile_)
return;
pref_change_registrar_.RemoveAll();
notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
Source<Profile>(profile_));
profile_ = NULL;
}
void PolicyDefaultProvider::NotifyObservers(
const ContentSettingsDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (profile_ == NULL)
return;
NotificationService::current()->Notify(
NotificationType::CONTENT_SETTINGS_CHANGED,
Source<HostContentSettingsMap>(profile_->GetHostContentSettingsMap()),
Details<const ContentSettingsDetails>(&details));
}
void PolicyDefaultProvider::ReadManagedDefaultSettings() {
for (size_t type = 0; type < arraysize(kPrefToManageType); ++type) {
if (kPrefToManageType[type] == NULL) {
continue;
}
UpdateManagedDefaultSetting(ContentSettingsType(type));
}
}
void PolicyDefaultProvider::UpdateManagedDefaultSetting(
ContentSettingsType type) {
// If a pref to manage a default-content-setting was not set (NOTICE:
// "HasPrefPath" returns false if no value was set for a registered pref) then
// the default value of the preference is used. The default value of a
// preference to manage a default-content-settings is CONTENT_SETTING_DEFAULT.
// This indicates that no managed value is set. If a pref was set, than it
// MUST be managed.
PrefService* prefs = profile_->GetPrefs();
DCHECK(!prefs->HasPrefPath(kPrefToManageType[type]) ||
prefs->IsManagedPreference(kPrefToManageType[type]));
base::AutoLock auto_lock(lock_);
managed_default_content_settings_.settings[type] = IntToContentSetting(
prefs->GetInteger(kPrefToManageType[type]));
}
// static
void PolicyDefaultProvider::RegisterUserPrefs(PrefService* prefs) {
// Preferences for default content setting policies. A policy is not set of
// the corresponding preferences below is set to CONTENT_SETTING_DEFAULT.
prefs->RegisterIntegerPref(prefs::kManagedDefaultCookiesSetting,
CONTENT_SETTING_DEFAULT);
prefs->RegisterIntegerPref(prefs::kManagedDefaultImagesSetting,
CONTENT_SETTING_DEFAULT);
prefs->RegisterIntegerPref(prefs::kManagedDefaultJavaScriptSetting,
CONTENT_SETTING_DEFAULT);
prefs->RegisterIntegerPref(prefs::kManagedDefaultPluginsSetting,
CONTENT_SETTING_DEFAULT);
prefs->RegisterIntegerPref(prefs::kManagedDefaultPopupsSetting,
CONTENT_SETTING_DEFAULT);
}
// ////////////////////////////////////////////////////////////////////////////
// PolicyProvider
// static
void PolicyProvider::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterListPref(prefs::kManagedCookiesAllowedForUrls);
prefs->RegisterListPref(prefs::kManagedCookiesBlockedForUrls);
prefs->RegisterListPref(prefs::kManagedCookiesSessionOnlyForUrls);
prefs->RegisterListPref(prefs::kManagedImagesAllowedForUrls);
prefs->RegisterListPref(prefs::kManagedImagesBlockedForUrls);
prefs->RegisterListPref(prefs::kManagedJavaScriptAllowedForUrls);
prefs->RegisterListPref(prefs::kManagedJavaScriptBlockedForUrls);
prefs->RegisterListPref(prefs::kManagedPluginsAllowedForUrls);
prefs->RegisterListPref(prefs::kManagedPluginsBlockedForUrls);
prefs->RegisterListPref(prefs::kManagedPopupsAllowedForUrls);
prefs->RegisterListPref(prefs::kManagedPopupsBlockedForUrls);
}
PolicyProvider::PolicyProvider(Profile* profile)
: BaseProvider(profile->IsOffTheRecord()),
profile_(profile) {
Init();
}
PolicyProvider::~PolicyProvider() {
UnregisterObservers();
}
void PolicyProvider::ReadManagedContentSettingsTypes(
ContentSettingsType content_type) {
PrefService* prefs = profile_->GetPrefs();
if (kPrefToManageType[content_type] == NULL) {
content_type_is_managed_[content_type] = false;
} else {
content_type_is_managed_[content_type] =
prefs->IsManagedPreference(kPrefToManageType[content_type]);
}
}
void PolicyProvider::Init() {
PrefService* prefs = profile_->GetPrefs();
ReadManagedContentSettings(false);
for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i)
ReadManagedContentSettingsTypes(ContentSettingsType(i));
pref_change_registrar_.Init(prefs);
pref_change_registrar_.Add(prefs::kManagedCookiesBlockedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedCookiesAllowedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedCookiesSessionOnlyForUrls, this);
pref_change_registrar_.Add(prefs::kManagedImagesBlockedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedImagesAllowedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedJavaScriptBlockedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedJavaScriptAllowedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedPluginsBlockedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedPluginsAllowedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedPopupsBlockedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedPopupsAllowedForUrls, this);
pref_change_registrar_.Add(prefs::kManagedDefaultCookiesSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultImagesSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultJavaScriptSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultPluginsSetting, this);
pref_change_registrar_.Add(prefs::kManagedDefaultPopupsSetting, this);
notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
Source<Profile>(profile_));
}
bool PolicyProvider::ContentSettingsTypeIsManaged(
ContentSettingsType content_type) {
return content_type_is_managed_[content_type];
}
void PolicyProvider::GetContentSettingsFromPreferences(
PrefService* prefs,
ContentSettingsRules* rules) {
for (size_t i = 0; i < arraysize(kPrefsForManagedContentSettingsMap); ++i) {
const char* pref_name = kPrefsForManagedContentSettingsMap[i].pref_name;
// Skip unset policies.
if (!prefs->HasPrefPath(pref_name)) {
VLOG(2) << "Skipping unset preference: " << pref_name;
continue;
}
const PrefService::Preference* pref = prefs->FindPreference(pref_name);
DCHECK(pref->IsManaged());
DCHECK_EQ(Value::TYPE_LIST, pref->GetType());
const ListValue* pattern_str_list =
static_cast<const ListValue*>(pref->GetValue());
for (size_t j = 0; j < pattern_str_list->GetSize(); ++j) {
std::string original_pattern_str;
pattern_str_list->GetString(j, &original_pattern_str);
ContentSettingsPattern pattern(original_pattern_str);
// Ignore invalid patterns.
if (!pattern.IsValid()) {
VLOG(1) << "Ignoring invalid content settings pattern: " <<
pattern.AsString();
continue;
}
rules->push_back(MakeTuple(
pattern,
pattern,
kPrefsForManagedContentSettingsMap[i].content_type,
ProviderInterface::ResourceIdentifier(NO_RESOURCE_IDENTIFIER),
kPrefsForManagedContentSettingsMap[i].setting));
}
}
}
void PolicyProvider::ReadManagedContentSettings(bool overwrite) {
ContentSettingsRules rules;
PrefService* prefs = profile_->GetPrefs();
GetContentSettingsFromPreferences(prefs, &rules);
{
base::AutoLock auto_lock(lock());
HostContentSettings* content_settings_map = host_content_settings();
if (overwrite)
content_settings_map->clear();
for (ContentSettingsRules::iterator rule = rules.begin();
rule != rules.end();
++rule) {
DispatchToMethod(this, &PolicyProvider::UpdateContentSettingsMap, *rule);
}
}
}
// Since the PolicyProvider is a read only content settings provider, all
// methodes of the ProviderInterface that set or delete any settings do nothing.
void PolicyProvider::SetContentSetting(
const ContentSettingsPattern& requesting_pattern,
const ContentSettingsPattern& embedding_pattern,
ContentSettingsType content_type,
const ResourceIdentifier& resource_identifier,
ContentSetting content_setting) {
}
ContentSetting PolicyProvider::GetContentSetting(
const GURL& requesting_url,
const GURL& embedding_url,
ContentSettingsType content_type,
const ResourceIdentifier& resource_identifier) const {
return BaseProvider::GetContentSetting(
requesting_url,
embedding_url,
content_type,
NO_RESOURCE_IDENTIFIER);
}
void PolicyProvider::ClearAllContentSettingsRules(
ContentSettingsType content_type) {
}
void PolicyProvider::ResetToDefaults() {
}
void PolicyProvider::UnregisterObservers() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!profile_)
return;
pref_change_registrar_.RemoveAll();
notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
Source<Profile>(profile_));
profile_ = NULL;
}
void PolicyProvider::NotifyObservers(
const ContentSettingsDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (profile_ == NULL)
return;
NotificationService::current()->Notify(
NotificationType::CONTENT_SETTINGS_CHANGED,
Source<HostContentSettingsMap>(profile_->GetHostContentSettingsMap()),
Details<const ContentSettingsDetails>(&details));
}
void PolicyProvider::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (type == NotificationType::PREF_CHANGED) {
DCHECK_EQ(profile_->GetPrefs(), Source<PrefService>(source).ptr());
std::string* name = Details<std::string>(details).ptr();
if (*name == prefs::kManagedCookiesAllowedForUrls ||
*name == prefs::kManagedCookiesBlockedForUrls ||
*name == prefs::kManagedCookiesSessionOnlyForUrls ||
*name == prefs::kManagedImagesAllowedForUrls ||
*name == prefs::kManagedImagesBlockedForUrls ||
*name == prefs::kManagedJavaScriptAllowedForUrls ||
*name == prefs::kManagedJavaScriptBlockedForUrls ||
*name == prefs::kManagedPluginsAllowedForUrls ||
*name == prefs::kManagedPluginsBlockedForUrls ||
*name == prefs::kManagedPopupsAllowedForUrls ||
*name == prefs::kManagedPopupsBlockedForUrls) {
ReadManagedContentSettings(true);
NotifyObservers(ContentSettingsDetails(
ContentSettingsPattern(), CONTENT_SETTINGS_TYPE_DEFAULT, ""));
// We do not want to sent a notification when managed default content
// settings change. The DefaultProvider will take care of that. We are
// only a passive observer.
// TODO(markusheintz): NOTICE: This is still work in progress and part of
// a larger refactoring. The code will change and be much cleaner and
// clearer in the end.
} else if (*name == prefs::kManagedDefaultCookiesSetting) {
ReadManagedContentSettingsTypes(CONTENT_SETTINGS_TYPE_COOKIES);
} else if (*name == prefs::kManagedDefaultImagesSetting) {
ReadManagedContentSettingsTypes(CONTENT_SETTINGS_TYPE_IMAGES);
} else if (*name == prefs::kManagedDefaultJavaScriptSetting) {
ReadManagedContentSettingsTypes(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
} else if (*name == prefs::kManagedDefaultPluginsSetting) {
ReadManagedContentSettingsTypes(CONTENT_SETTINGS_TYPE_PLUGINS);
} else if (*name == prefs::kManagedDefaultPopupsSetting) {
ReadManagedContentSettingsTypes(CONTENT_SETTINGS_TYPE_POPUPS);
}
} else if (type == NotificationType::PROFILE_DESTROYED) {
DCHECK_EQ(profile_, Source<Profile>(source).ptr());
UnregisterObservers();
} else {
NOTREACHED() << "Unexpected notification";
}
}
} // namespace content_settings