| // Copyright (c) 2012 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_default_provider.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/basictypes.h" |
| #include "base/command_line.h" |
| #include "base/metrics/histogram.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/prefs/scoped_user_pref_update.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/content_settings/content_settings_rule.h" |
| #include "chrome/browser/content_settings/content_settings_utils.h" |
| #include "chrome/common/content_settings.h" |
| #include "chrome/common/content_settings_pattern.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "url/gurl.h" |
| |
| using base::UserMetricsAction; |
| using content::BrowserThread; |
| |
| namespace { |
| |
| // The default setting for each content type. |
| const ContentSetting kDefaultSettings[] = { |
| CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_COOKIES |
| CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_IMAGES |
| CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_JAVASCRIPT |
| CONTENT_SETTING_ALLOW, // CONTENT_SETTINGS_TYPE_PLUGINS |
| CONTENT_SETTING_BLOCK, // CONTENT_SETTINGS_TYPE_POPUPS |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_GEOLOCATION |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_NOTIFICATIONS |
| CONTENT_SETTING_DEFAULT, // CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_FULLSCREEN |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_MOUSELOCK |
| CONTENT_SETTING_DEFAULT, // CONTENT_SETTINGS_TYPE_MIXEDSCRIPT |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_MEDIASTREAM |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA |
| CONTENT_SETTING_DEFAULT, // CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_PPAPI_BROKER |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_MIDI_SYSEX |
| #if defined(OS_WIN) |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP |
| #elif defined(OS_ANDROID) || defined(OS_CHROMEOS) |
| CONTENT_SETTING_ASK, // CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER |
| #endif |
| #if defined(OS_ANDROID) |
| CONTENT_SETTING_DEFAULT, // CONTENT_SETTINGS_TYPE_APP_BANNER |
| #endif |
| }; |
| COMPILE_ASSERT(arraysize(kDefaultSettings) == CONTENT_SETTINGS_NUM_TYPES, |
| default_settings_incorrect_size); |
| |
| } // namespace |
| |
| namespace content_settings { |
| |
| namespace { |
| |
| class DefaultRuleIterator : public RuleIterator { |
| public: |
| explicit DefaultRuleIterator(const base::Value* value) { |
| if (value) |
| value_.reset(value->DeepCopy()); |
| } |
| |
| virtual bool HasNext() const OVERRIDE { |
| return value_.get() != NULL; |
| } |
| |
| virtual Rule Next() OVERRIDE { |
| DCHECK(value_.get()); |
| return Rule(ContentSettingsPattern::Wildcard(), |
| ContentSettingsPattern::Wildcard(), |
| value_.release()); |
| } |
| |
| private: |
| scoped_ptr<base::Value> value_; |
| }; |
| |
| } // namespace |
| |
| // static |
| void DefaultProvider::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| // The registration of the preference prefs::kDefaultContentSettings should |
| // also include the default values for default content settings. This allows |
| // functional tests to get default content settings by reading the preference |
| // prefs::kDefaultContentSettings via pyauto. |
| // TODO(markusheintz): Write pyauto hooks for the content settings map as |
| // content settings should be read from the host content settings map. |
| base::DictionaryValue* default_content_settings = new base::DictionaryValue(); |
| registry->RegisterDictionaryPref( |
| prefs::kDefaultContentSettings, |
| default_content_settings, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| } |
| |
| DefaultProvider::DefaultProvider(PrefService* prefs, bool incognito) |
| : prefs_(prefs), |
| is_incognito_(incognito), |
| updating_preferences_(false) { |
| DCHECK(prefs_); |
| |
| // Read global defaults. |
| ReadDefaultSettings(true); |
| |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultCookiesSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_COOKIES].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultImagesSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_IMAGES].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultJavaScriptSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_JAVASCRIPT].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultPluginsSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_PLUGINS].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultPopupsSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_POPUPS].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultLocationSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_GEOLOCATION].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultNotificationsSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_NOTIFICATIONS].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultMouseCursorSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_MOUSELOCK].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultMediaStreamSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_MEDIASTREAM].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| UMA_HISTOGRAM_ENUMERATION( |
| "ContentSettings.DefaultMIDISysExSetting", |
| ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_MIDI_SYSEX].get()), |
| CONTENT_SETTING_NUM_SETTINGS); |
| |
| pref_change_registrar_.Init(prefs_); |
| PrefChangeRegistrar::NamedChangeCallback callback = base::Bind( |
| &DefaultProvider::OnPreferenceChanged, base::Unretained(this)); |
| pref_change_registrar_.Add(prefs::kDefaultContentSettings, callback); |
| } |
| |
| DefaultProvider::~DefaultProvider() { |
| } |
| |
| bool DefaultProvider::SetWebsiteSetting( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| const ResourceIdentifier& resource_identifier, |
| base::Value* in_value) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(prefs_); |
| |
| // Ignore non default settings |
| if (primary_pattern != ContentSettingsPattern::Wildcard() || |
| secondary_pattern != ContentSettingsPattern::Wildcard()) { |
| return false; |
| } |
| |
| // The default settings may not be directly modified for OTR sessions. |
| // Instead, they are synced to the main profile's setting. |
| if (is_incognito_) |
| return false; |
| |
| // Put |in_value| in a scoped pointer to ensure that it gets cleaned up |
| // properly if we don't pass on the ownership. |
| scoped_ptr<base::Value> value(in_value); |
| { |
| base::AutoReset<bool> auto_reset(&updating_preferences_, true); |
| |
| // |DefaultProvider| should not send any notifications when holding |
| // |lock_|. |DictionaryPrefUpdate| destructor and |
| // |PrefService::SetInteger()| send out notifications. As a response, the |
| // upper layers may call |GetAllContentSettingRules| which acquires |lock_| |
| // again. |
| DictionaryPrefUpdate update(prefs_, prefs::kDefaultContentSettings); |
| base::DictionaryValue* default_settings_dictionary = update.Get(); |
| base::AutoLock lock(lock_); |
| if (value.get() == NULL || |
| ValueToContentSetting(value.get()) == kDefaultSettings[content_type]) { |
| // If |value| is NULL we need to reset the default setting the the |
| // hardcoded default. |
| default_settings_[content_type].reset( |
| base::Value::CreateIntegerValue(kDefaultSettings[content_type])); |
| |
| // Remove the corresponding pref entry since the hardcoded default value |
| // is used. |
| default_settings_dictionary->RemoveWithoutPathExpansion( |
| GetTypeName(content_type), NULL); |
| } else { |
| default_settings_[content_type].reset(value->DeepCopy()); |
| // Transfer ownership of |value| to the |default_settings_dictionary|. |
| default_settings_dictionary->SetWithoutPathExpansion( |
| GetTypeName(content_type), value.release()); |
| } |
| } |
| |
| NotifyObservers(ContentSettingsPattern(), |
| ContentSettingsPattern(), |
| content_type, |
| std::string()); |
| |
| return true; |
| } |
| |
| RuleIterator* DefaultProvider::GetRuleIterator( |
| ContentSettingsType content_type, |
| const ResourceIdentifier& resource_identifier, |
| bool incognito) const { |
| base::AutoLock lock(lock_); |
| if (resource_identifier.empty()) { |
| ValueMap::const_iterator it(default_settings_.find(content_type)); |
| if (it != default_settings_.end()) { |
| return new DefaultRuleIterator(it->second.get()); |
| } |
| NOTREACHED(); |
| } |
| return new EmptyRuleIterator(); |
| } |
| |
| void DefaultProvider::ClearAllContentSettingsRules( |
| ContentSettingsType content_type) { |
| // TODO(markusheintz): This method is only called when the |
| // |DesktopNotificationService| calls |ClearAllSettingsForType| method on the |
| // |HostContentSettingsMap|. Don't implement this method yet, otherwise the |
| // default notification settings will be cleared as well. |
| } |
| |
| void DefaultProvider::ShutdownOnUIThread() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(prefs_); |
| RemoveAllObservers(); |
| pref_change_registrar_.RemoveAll(); |
| prefs_ = NULL; |
| } |
| |
| void DefaultProvider::OnPreferenceChanged(const std::string& name) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (updating_preferences_) |
| return; |
| |
| if (name == prefs::kDefaultContentSettings) { |
| ReadDefaultSettings(true); |
| } else { |
| NOTREACHED() << "Unexpected preference observed"; |
| return; |
| } |
| |
| NotifyObservers(ContentSettingsPattern(), |
| ContentSettingsPattern(), |
| CONTENT_SETTINGS_TYPE_DEFAULT, |
| std::string()); |
| } |
| |
| void DefaultProvider::ReadDefaultSettings(bool overwrite) { |
| base::AutoLock lock(lock_); |
| const base::DictionaryValue* default_settings_dictionary = |
| prefs_->GetDictionary(prefs::kDefaultContentSettings); |
| |
| if (overwrite) |
| default_settings_.clear(); |
| |
| // Careful: The returned value could be NULL if the pref has never been set. |
| if (default_settings_dictionary) |
| GetSettingsFromDictionary(default_settings_dictionary); |
| |
| ForceDefaultsToBeExplicit(); |
| } |
| |
| void DefaultProvider::ForceDefaultsToBeExplicit() { |
| for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { |
| ContentSettingsType type = ContentSettingsType(i); |
| if (!default_settings_[type].get() && |
| kDefaultSettings[i] != CONTENT_SETTING_DEFAULT) { |
| default_settings_[type].reset( |
| base::Value::CreateIntegerValue(kDefaultSettings[i])); |
| } |
| } |
| } |
| |
| void DefaultProvider::GetSettingsFromDictionary( |
| const base::DictionaryValue* dictionary) { |
| for (base::DictionaryValue::Iterator i(*dictionary); |
| !i.IsAtEnd(); i.Advance()) { |
| const std::string& content_type(i.key()); |
| for (size_t type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) { |
| if (content_type == GetTypeName(ContentSettingsType(type))) { |
| int int_value = CONTENT_SETTING_DEFAULT; |
| bool is_integer = i.value().GetAsInteger(&int_value); |
| DCHECK(is_integer); |
| default_settings_[ContentSettingsType(type)].reset( |
| base::Value::CreateIntegerValue(int_value)); |
| break; |
| } |
| } |
| } |
| // Migrate obsolete cookie prompt mode. |
| if (ValueToContentSetting( |
| default_settings_[CONTENT_SETTINGS_TYPE_COOKIES].get()) == |
| CONTENT_SETTING_ASK) { |
| default_settings_[CONTENT_SETTINGS_TYPE_COOKIES].reset( |
| base::Value::CreateIntegerValue(CONTENT_SETTING_BLOCK)); |
| } |
| } |
| |
| } // namespace content_settings |