// Copyright (c) 2010 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/host_content_settings_map.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "net/base/dns_util.h" #include "net/base/static_cookie_policy.h" // static const wchar_t* HostContentSettingsMap::kTypeNames[CONTENT_SETTINGS_NUM_TYPES] = { L"cookies", L"images", L"javascript", L"plugins", L"popups", }; // static const ContentSetting HostContentSettingsMap::kDefaultSettings[CONTENT_SETTINGS_NUM_TYPES] = { 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 }; HostContentSettingsMap::HostContentSettingsMap(Profile* profile) : profile_(profile), block_third_party_cookies_(false) { PrefService* prefs = profile_->GetPrefs(); // Migrate obsolete cookie pref. if (prefs->HasPrefPath(prefs::kCookieBehavior)) { int cookie_behavior = prefs->GetInteger(prefs::kCookieBehavior); prefs->ClearPref(prefs::kCookieBehavior); if (!prefs->HasPrefPath(prefs::kDefaultContentSettings)) { SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_COOKIES, (cookie_behavior == net::StaticCookiePolicy::BLOCK_ALL_COOKIES) ? CONTENT_SETTING_BLOCK : CONTENT_SETTING_ALLOW); } if (!prefs->HasPrefPath(prefs::kBlockThirdPartyCookies)) { SetBlockThirdPartyCookies(cookie_behavior == net::StaticCookiePolicy::BLOCK_THIRD_PARTY_COOKIES); } } // Migrate obsolete popups pref. if (prefs->HasPrefPath(prefs::kPopupWhitelistedHosts)) { const ListValue* whitelist_pref = prefs->GetList(prefs::kPopupWhitelistedHosts); for (ListValue::const_iterator i(whitelist_pref->begin()); i != whitelist_pref->end(); ++i) { std::string host; (*i)->GetAsString(&host); SetContentSetting(host, CONTENT_SETTINGS_TYPE_POPUPS, CONTENT_SETTING_ALLOW); } prefs->ClearPref(prefs::kPopupWhitelistedHosts); } // Read global defaults. DCHECK_EQ(arraysize(kTypeNames), static_cast(CONTENT_SETTINGS_NUM_TYPES)); const DictionaryValue* default_settings_dictionary = prefs->GetDictionary(prefs::kDefaultContentSettings); // Careful: The returned value could be NULL if the pref has never been set. if (default_settings_dictionary != NULL) { GetSettingsFromDictionary(default_settings_dictionary, &default_content_settings_); } ForceDefaultsToBeExplicit(); // Read host-specific exceptions. const DictionaryValue* all_settings_dictionary = prefs->GetDictionary(prefs::kPerHostContentSettings); // Careful: The returned value could be NULL if the pref has never been set. if (all_settings_dictionary != NULL) { for (DictionaryValue::key_iterator i(all_settings_dictionary->begin_keys()); i != all_settings_dictionary->end_keys(); ++i) { std::wstring wide_host(*i); DictionaryValue* host_settings_dictionary = NULL; bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion( wide_host, &host_settings_dictionary); DCHECK(found); ContentSettings settings; GetSettingsFromDictionary(host_settings_dictionary, &settings); host_content_settings_[WideToUTF8(wide_host)] = settings; } } // Read misc. global settings. block_third_party_cookies_ = prefs->GetBoolean(prefs::kBlockThirdPartyCookies); } // static void HostContentSettingsMap::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterDictionaryPref(prefs::kDefaultContentSettings); prefs->RegisterDictionaryPref(prefs::kPerHostContentSettings); prefs->RegisterBooleanPref(prefs::kBlockThirdPartyCookies, false); prefs->RegisterIntegerPref(prefs::kContentSettingsWindowLastTabIndex, 0); // Obsolete prefs, for migration: prefs->RegisterIntegerPref(prefs::kCookieBehavior, net::StaticCookiePolicy::ALLOW_ALL_COOKIES); prefs->RegisterListPref(prefs::kPopupWhitelistedHosts); } ContentSetting HostContentSettingsMap::GetDefaultContentSetting( ContentSettingsType content_type) const { AutoLock auto_lock(lock_); return default_content_settings_.settings[content_type]; } ContentSetting HostContentSettingsMap::GetContentSetting( const std::string& host, ContentSettingsType content_type) const { AutoLock auto_lock(lock_); HostContentSettings::const_iterator i(host_content_settings_.find( net::TrimEndingDot(host))); if (i != host_content_settings_.end()) { ContentSetting setting = i->second.settings[content_type]; if (setting != CONTENT_SETTING_DEFAULT) return setting; } return default_content_settings_.settings[content_type]; } ContentSetting HostContentSettingsMap::GetContentSetting( const GURL& url, ContentSettingsType content_type) const { return ShouldAllowAllContent(url) ? CONTENT_SETTING_ALLOW : GetContentSetting(url.host(), content_type); } ContentSettings HostContentSettingsMap::GetContentSettings( const std::string& host) const { AutoLock auto_lock(lock_); HostContentSettings::const_iterator i(host_content_settings_.find( net::TrimEndingDot(host))); if (i == host_content_settings_.end()) return default_content_settings_; ContentSettings output = i->second; for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) { if (output.settings[j] == CONTENT_SETTING_DEFAULT) output.settings[j] = default_content_settings_.settings[j]; } return output; } ContentSettings HostContentSettingsMap::GetContentSettings( const GURL& url) const { return ShouldAllowAllContent(url) ? ContentSettings(CONTENT_SETTING_ALLOW) : GetContentSettings(url.host()); } void HostContentSettingsMap::GetSettingsForOneType( ContentSettingsType content_type, SettingsForOneType* settings) const { DCHECK(settings); settings->clear(); AutoLock auto_lock(lock_); for (HostContentSettings::const_iterator i(host_content_settings_.begin()); i != host_content_settings_.end(); ++i) { ContentSetting setting = i->second.settings[content_type]; if (setting != CONTENT_SETTING_DEFAULT) { // Use of push_back() relies on the map iterator traversing in order of // ascending keys. settings->push_back(std::make_pair(i->first, setting)); } } } void HostContentSettingsMap::SetDefaultContentSetting( ContentSettingsType content_type, ContentSetting setting) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); DictionaryValue* default_settings_dictionary = profile_->GetPrefs()->GetMutableDictionary( prefs::kDefaultContentSettings); std::wstring dictionary_path(kTypeNames[content_type]); { AutoLock auto_lock(lock_); if ((setting == CONTENT_SETTING_DEFAULT) || (setting == kDefaultSettings[content_type])) { default_content_settings_.settings[content_type] = kDefaultSettings[content_type]; default_settings_dictionary->RemoveWithoutPathExpansion(dictionary_path, NULL); } else { default_content_settings_.settings[content_type] = setting; default_settings_dictionary->SetWithoutPathExpansion( dictionary_path, Value::CreateIntegerValue(setting)); } } NotifyObservers(std::string()); } void HostContentSettingsMap::SetContentSetting(const std::string& host, ContentSettingsType content_type, ContentSetting setting) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); bool early_exit = false; std::wstring wide_host(UTF8ToWide(host)); DictionaryValue* all_settings_dictionary = profile_->GetPrefs()->GetMutableDictionary( prefs::kPerHostContentSettings); { AutoLock auto_lock(lock_); if (!host_content_settings_.count(host)) host_content_settings_[host] = ContentSettings(); HostContentSettings::iterator i(host_content_settings_.find(host)); ContentSettings& settings = i->second; settings.settings[content_type] = setting; if (AllDefault(settings)) { host_content_settings_.erase(i); all_settings_dictionary->RemoveWithoutPathExpansion(wide_host, NULL); // We can't just return because |NotifyObservers()| needs to be called, // without |lock_| being held. early_exit = true; } } if (!early_exit) { DictionaryValue* host_settings_dictionary; bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion( wide_host, &host_settings_dictionary); if (!found) { host_settings_dictionary = new DictionaryValue; all_settings_dictionary->SetWithoutPathExpansion( wide_host, host_settings_dictionary); DCHECK_NE(setting, CONTENT_SETTING_DEFAULT); } std::wstring dictionary_path(kTypeNames[content_type]); if (setting == CONTENT_SETTING_DEFAULT) { host_settings_dictionary->RemoveWithoutPathExpansion(dictionary_path, NULL); } else { host_settings_dictionary->SetWithoutPathExpansion( dictionary_path, Value::CreateIntegerValue(setting)); } } NotifyObservers(host); } void HostContentSettingsMap::ClearSettingsForOneType( ContentSettingsType content_type) { { AutoLock auto_lock(lock_); for (HostContentSettings::iterator i(host_content_settings_.begin()); i != host_content_settings_.end(); ) { if (i->second.settings[content_type] != CONTENT_SETTING_DEFAULT) { i->second.settings[content_type] = CONTENT_SETTING_DEFAULT; std::wstring wide_host(UTF8ToWide(i->first)); DictionaryValue* all_settings_dictionary = profile_->GetPrefs()->GetMutableDictionary( prefs::kPerHostContentSettings); if (AllDefault(i->second)) { all_settings_dictionary->RemoveWithoutPathExpansion(wide_host, NULL); host_content_settings_.erase(i++); } else { DictionaryValue* host_settings_dictionary; bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion( wide_host, &host_settings_dictionary); DCHECK(found); host_settings_dictionary->RemoveWithoutPathExpansion( kTypeNames[content_type], NULL); ++i; } } else { ++i; } } } NotifyObservers(std::string()); } void HostContentSettingsMap::SetBlockThirdPartyCookies(bool block) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); { AutoLock auto_lock(lock_); block_third_party_cookies_ = block; } PrefService* prefs = profile_->GetPrefs(); if (block) prefs->SetBoolean(prefs::kBlockThirdPartyCookies, true); else prefs->ClearPref(prefs::kBlockThirdPartyCookies); } void HostContentSettingsMap::ResetToDefaults() { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); { AutoLock auto_lock(lock_); default_content_settings_ = ContentSettings(); ForceDefaultsToBeExplicit(); host_content_settings_.clear(); block_third_party_cookies_ = false; } PrefService* prefs = profile_->GetPrefs(); prefs->ClearPref(prefs::kDefaultContentSettings); prefs->ClearPref(prefs::kPerHostContentSettings); prefs->ClearPref(prefs::kBlockThirdPartyCookies); NotifyObservers(std::string()); } HostContentSettingsMap::~HostContentSettingsMap() { } // static bool HostContentSettingsMap::ShouldAllowAllContent(const GURL& url) { return url.SchemeIs(chrome::kChromeInternalScheme) || url.SchemeIs(chrome::kChromeUIScheme) || url.SchemeIs(chrome::kExtensionScheme) || url.SchemeIs(chrome::kGearsScheme) || url.SchemeIs(chrome::kUserScriptScheme); } void HostContentSettingsMap::GetSettingsFromDictionary( const DictionaryValue* dictionary, ContentSettings* settings) { for (DictionaryValue::key_iterator i(dictionary->begin_keys()); i != dictionary->end_keys(); ++i) { std::wstring content_type(*i); int setting = CONTENT_SETTING_DEFAULT; bool found = dictionary->GetIntegerWithoutPathExpansion(content_type, &setting); DCHECK(found); for (size_t type = 0; type < arraysize(kTypeNames); ++type) { if (std::wstring(kTypeNames[type]) == content_type) { settings->settings[type] = static_cast(setting); break; } } } } void HostContentSettingsMap::ForceDefaultsToBeExplicit() { DCHECK_EQ(arraysize(kDefaultSettings), static_cast(CONTENT_SETTINGS_NUM_TYPES)); for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { if (default_content_settings_.settings[i] == CONTENT_SETTING_DEFAULT) default_content_settings_.settings[i] = kDefaultSettings[i]; } } bool HostContentSettingsMap::AllDefault(const ContentSettings& settings) const { for (size_t i = 0; i < arraysize(settings.settings); ++i) { if (settings.settings[i] != CONTENT_SETTING_DEFAULT) return false; } return true; } void HostContentSettingsMap::NotifyObservers(const std::string& host) { ContentSettingsDetails details(host); NotificationService::current()->Notify( NotificationType::CONTENT_SETTINGS_CHANGED, Source(this), Details(&details)); }