Home | History | Annotate | Download | only in content_settings
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/content_settings/content_settings_pref_provider.h"
      6 
      7 #include <string>
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include "base/command_line.h"
     12 #include "chrome/browser/content_settings/content_settings_details.h"
     13 #include "chrome/browser/content_settings/content_settings_pattern.h"
     14 #include "chrome/browser/metrics/user_metrics.h"
     15 #include "chrome/browser/prefs/pref_service.h"
     16 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/common/chrome_switches.h"
     19 #include "chrome/common/content_settings.h"
     20 #include "chrome/common/pref_names.h"
     21 #include "content/browser/browser_thread.h"
     22 #include "content/common/notification_details.h"
     23 #include "content/common/notification_service.h"
     24 #include "content/common/notification_source.h"
     25 #include "googleurl/src/gurl.h"
     26 #include "net/base/net_util.h"
     27 
     28 namespace {
     29 
     30 // The preference keys where resource identifiers are stored for
     31 // ContentSettingsType values that support resource identifiers.
     32 const char* kResourceTypeNames[] = {
     33   NULL,
     34   NULL,
     35   NULL,
     36   "per_plugin",
     37   NULL,
     38   NULL,  // Not used for Geolocation
     39   NULL,  // Not used for Notifications
     40   NULL,  // Not used for Prerender.
     41 };
     42 COMPILE_ASSERT(arraysize(kResourceTypeNames) == CONTENT_SETTINGS_NUM_TYPES,
     43                resource_type_names_incorrect_size);
     44 
     45 // The default setting for each content type.
     46 const ContentSetting kDefaultSettings[] = {
     47   CONTENT_SETTING_ALLOW,  // CONTENT_SETTINGS_TYPE_COOKIES
     48   CONTENT_SETTING_ALLOW,  // CONTENT_SETTINGS_TYPE_IMAGES
     49   CONTENT_SETTING_ALLOW,  // CONTENT_SETTINGS_TYPE_JAVASCRIPT
     50   CONTENT_SETTING_ALLOW,  // CONTENT_SETTINGS_TYPE_PLUGINS
     51   CONTENT_SETTING_BLOCK,  // CONTENT_SETTINGS_TYPE_POPUPS
     52   CONTENT_SETTING_ASK,    // Not used for Geolocation
     53   CONTENT_SETTING_ASK,    // CONTENT_SETTINGS_TYPE_NOTIFICATIONS
     54   CONTENT_SETTING_ALLOW,  // CONTENT_SETTINGS_TYPE_PRERENDER
     55 };
     56 COMPILE_ASSERT(arraysize(kDefaultSettings) == CONTENT_SETTINGS_NUM_TYPES,
     57                default_settings_incorrect_size);
     58 
     59 // The names of the ContentSettingsType values, for use with dictionary prefs.
     60 const char* kTypeNames[] = {
     61   "cookies",
     62   "images",
     63   "javascript",
     64   "plugins",
     65   "popups",
     66   NULL,  // Not used for Geolocation
     67   // TODO(markusheintz): Refactoring in progress. Content settings exceptions
     68   // for notifications will be added next.
     69   "notifications",  // Only used for default Notifications settings.
     70   NULL,  // Not used for Prerender
     71 };
     72 COMPILE_ASSERT(arraysize(kTypeNames) == CONTENT_SETTINGS_NUM_TYPES,
     73                type_names_incorrect_size);
     74 
     75 void SetDefaultContentSettings(DictionaryValue* default_settings) {
     76   default_settings->Clear();
     77 
     78   for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
     79     if (kTypeNames[i] != NULL) {
     80       default_settings->SetInteger(kTypeNames[i],
     81                                    kDefaultSettings[i]);
     82     }
     83   }
     84 }
     85 
     86 }  // namespace
     87 
     88 namespace content_settings {
     89 
     90 PrefDefaultProvider::PrefDefaultProvider(Profile* profile)
     91     : profile_(profile),
     92       is_incognito_(profile_->IsOffTheRecord()),
     93       updating_preferences_(false) {
     94   initializing_ = true;
     95   PrefService* prefs = profile->GetPrefs();
     96 
     97   MigrateObsoleteNotificationPref(prefs);
     98 
     99   // Read global defaults.
    100   DCHECK_EQ(arraysize(kTypeNames),
    101             static_cast<size_t>(CONTENT_SETTINGS_NUM_TYPES));
    102   ReadDefaultSettings(true);
    103   if (default_content_settings_.settings[CONTENT_SETTINGS_TYPE_COOKIES] ==
    104       CONTENT_SETTING_BLOCK) {
    105     UserMetrics::RecordAction(
    106         UserMetricsAction("CookieBlockingEnabledPerDefault"));
    107   } else {
    108     UserMetrics::RecordAction(
    109         UserMetricsAction("CookieBlockingDisabledPerDefault"));
    110   }
    111 
    112   pref_change_registrar_.Init(prefs);
    113   pref_change_registrar_.Add(prefs::kDefaultContentSettings, this);
    114   notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
    115                               Source<Profile>(profile_));
    116   initializing_ = false;
    117 }
    118 
    119 PrefDefaultProvider::~PrefDefaultProvider() {
    120   UnregisterObservers();
    121 }
    122 
    123 ContentSetting PrefDefaultProvider::ProvideDefaultSetting(
    124     ContentSettingsType content_type) const {
    125   base::AutoLock lock(lock_);
    126   return default_content_settings_.settings[content_type];
    127 }
    128 
    129 void PrefDefaultProvider::UpdateDefaultSetting(
    130     ContentSettingsType content_type,
    131     ContentSetting setting) {
    132   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    133   DCHECK(kTypeNames[content_type] != NULL);  // Don't call this for Geolocation.
    134   DCHECK(content_type != CONTENT_SETTINGS_TYPE_PLUGINS ||
    135          setting != CONTENT_SETTING_ASK ||
    136          CommandLine::ForCurrentProcess()->HasSwitch(
    137             switches::kEnableClickToPlay));
    138 
    139   // The default settings may not be directly modified for OTR sessions.
    140   // Instead, they are synced to the main profile's setting.
    141   if (is_incognito_)
    142     return;
    143 
    144   PrefService* prefs = profile_->GetPrefs();
    145 
    146   std::string dictionary_path(kTypeNames[content_type]);
    147   updating_preferences_ = true;
    148   {
    149     base::AutoLock lock(lock_);
    150     DictionaryPrefUpdate update(prefs, prefs::kDefaultContentSettings);
    151     DictionaryValue* default_settings_dictionary = update.Get();
    152     if ((setting == CONTENT_SETTING_DEFAULT) ||
    153         (setting == kDefaultSettings[content_type])) {
    154       default_content_settings_.settings[content_type] =
    155           kDefaultSettings[content_type];
    156       default_settings_dictionary->RemoveWithoutPathExpansion(dictionary_path,
    157                                                               NULL);
    158     } else {
    159       default_content_settings_.settings[content_type] = setting;
    160       default_settings_dictionary->SetWithoutPathExpansion(
    161           dictionary_path, Value::CreateIntegerValue(setting));
    162     }
    163   }
    164   updating_preferences_ = false;
    165 
    166   NotifyObservers(
    167       ContentSettingsDetails(ContentSettingsPattern(), content_type, ""));
    168 }
    169 
    170 bool PrefDefaultProvider::DefaultSettingIsManaged(
    171     ContentSettingsType content_type) const {
    172   return false;
    173 }
    174 
    175 void PrefDefaultProvider::ResetToDefaults() {
    176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    177   base::AutoLock lock(lock_);
    178   default_content_settings_ = ContentSettings();
    179   ForceDefaultsToBeExplicit();
    180 
    181   if (!is_incognito_) {
    182     PrefService* prefs = profile_->GetPrefs();
    183     updating_preferences_ = true;
    184     prefs->ClearPref(prefs::kDefaultContentSettings);
    185     updating_preferences_ = false;
    186   }
    187 }
    188 
    189 void PrefDefaultProvider::Observe(NotificationType type,
    190                                   const NotificationSource& source,
    191                                   const NotificationDetails& details) {
    192   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    193 
    194   if (type == NotificationType::PREF_CHANGED) {
    195     DCHECK_EQ(profile_->GetPrefs(), Source<PrefService>(source).ptr());
    196     if (updating_preferences_)
    197       return;
    198 
    199     std::string* name = Details<std::string>(details).ptr();
    200     if (*name == prefs::kDefaultContentSettings) {
    201       ReadDefaultSettings(true);
    202     } else {
    203       NOTREACHED() << "Unexpected preference observed";
    204       return;
    205     }
    206 
    207     if (!is_incognito_) {
    208       NotifyObservers(ContentSettingsDetails(
    209             ContentSettingsPattern(), CONTENT_SETTINGS_TYPE_DEFAULT, ""));
    210     }
    211   } else if (type == NotificationType::PROFILE_DESTROYED) {
    212     DCHECK_EQ(profile_, Source<Profile>(source).ptr());
    213     UnregisterObservers();
    214   } else {
    215     NOTREACHED() << "Unexpected notification";
    216   }
    217 }
    218 
    219 void PrefDefaultProvider::UnregisterObservers() {
    220   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    221   if (!profile_)
    222     return;
    223   pref_change_registrar_.RemoveAll();
    224   notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
    225                                  Source<Profile>(profile_));
    226   profile_ = NULL;
    227 }
    228 
    229 void PrefDefaultProvider::ReadDefaultSettings(bool overwrite) {
    230   PrefService* prefs = profile_->GetPrefs();
    231   const DictionaryValue* default_settings_dictionary =
    232       prefs->GetDictionary(prefs::kDefaultContentSettings);
    233 
    234   base::AutoLock lock(lock_);
    235 
    236   if (overwrite)
    237     default_content_settings_ = ContentSettings();
    238 
    239   // Careful: The returned value could be NULL if the pref has never been set.
    240   if (default_settings_dictionary != NULL) {
    241     GetSettingsFromDictionary(default_settings_dictionary,
    242                               &default_content_settings_);
    243   }
    244   ForceDefaultsToBeExplicit();
    245 }
    246 
    247 void PrefDefaultProvider::ForceDefaultsToBeExplicit() {
    248   DCHECK_EQ(arraysize(kDefaultSettings),
    249             static_cast<size_t>(CONTENT_SETTINGS_NUM_TYPES));
    250 
    251   for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
    252     if (default_content_settings_.settings[i] == CONTENT_SETTING_DEFAULT)
    253       default_content_settings_.settings[i] = kDefaultSettings[i];
    254   }
    255 }
    256 
    257 void PrefDefaultProvider::GetSettingsFromDictionary(
    258     const DictionaryValue* dictionary,
    259     ContentSettings* settings) {
    260   for (DictionaryValue::key_iterator i(dictionary->begin_keys());
    261        i != dictionary->end_keys(); ++i) {
    262     const std::string& content_type(*i);
    263     for (size_t type = 0; type < arraysize(kTypeNames); ++type) {
    264       if ((kTypeNames[type] != NULL) && (kTypeNames[type] == content_type)) {
    265         int setting = CONTENT_SETTING_DEFAULT;
    266         bool found = dictionary->GetIntegerWithoutPathExpansion(content_type,
    267                                                                 &setting);
    268         DCHECK(found);
    269         settings->settings[type] = IntToContentSetting(setting);
    270         break;
    271       }
    272     }
    273   }
    274   // Migrate obsolete cookie prompt mode.
    275   if (settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] ==
    276       CONTENT_SETTING_ASK)
    277     settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] = CONTENT_SETTING_BLOCK;
    278 
    279   settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS] =
    280       BaseProvider::ClickToPlayFixup(
    281           CONTENT_SETTINGS_TYPE_PLUGINS,
    282           settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS]);
    283 }
    284 
    285 void PrefDefaultProvider::NotifyObservers(
    286     const ContentSettingsDetails& details) {
    287   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    288   if (initializing_ || profile_ == NULL)
    289     return;
    290   NotificationService::current()->Notify(
    291       NotificationType::CONTENT_SETTINGS_CHANGED,
    292       Source<HostContentSettingsMap>(profile_->GetHostContentSettingsMap()),
    293       Details<const ContentSettingsDetails>(&details));
    294 }
    295 
    296 void PrefDefaultProvider::MigrateObsoleteNotificationPref(PrefService* prefs) {
    297   if (prefs->HasPrefPath(prefs::kDesktopNotificationDefaultContentSetting)) {
    298     ContentSetting setting = IntToContentSetting(
    299         prefs->GetInteger(prefs::kDesktopNotificationDefaultContentSetting));
    300     UpdateDefaultSetting(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
    301     prefs->ClearPref(prefs::kDesktopNotificationDefaultContentSetting);
    302   }
    303 }
    304 
    305 // static
    306 void PrefDefaultProvider::RegisterUserPrefs(PrefService* prefs) {
    307   // The registration of the preference prefs::kDefaultContentSettings should
    308   // also include the default values for default content settings. This allows
    309   // functional tests to get default content settings by reading the preference
    310   // prefs::kDefaultContentSettings via pyauto.
    311   // TODO(markusheintz): Write pyauto hooks for the content settings map as
    312   // content settings should be read from the host content settings map.
    313   DictionaryValue* default_content_settings = new DictionaryValue();
    314   SetDefaultContentSettings(default_content_settings);
    315   prefs->RegisterDictionaryPref(prefs::kDefaultContentSettings,
    316                                 default_content_settings);
    317 
    318   // Obsolete prefs, for migrations:
    319   prefs->RegisterIntegerPref(
    320       prefs::kDesktopNotificationDefaultContentSetting,
    321           kDefaultSettings[CONTENT_SETTINGS_TYPE_NOTIFICATIONS]);
    322 }
    323 
    324 // ////////////////////////////////////////////////////////////////////////////
    325 // PrefProvider:
    326 //
    327 
    328 // static
    329 void PrefProvider::RegisterUserPrefs(PrefService* prefs) {
    330   prefs->RegisterIntegerPref(prefs::kContentSettingsVersion,
    331       ContentSettingsPattern::kContentSettingsPatternVersion);
    332   prefs->RegisterDictionaryPref(prefs::kContentSettingsPatterns);
    333 
    334   // Obsolete prefs, for migration:
    335   prefs->RegisterListPref(prefs::kPopupWhitelistedHosts);
    336   prefs->RegisterDictionaryPref(prefs::kPerHostContentSettings);
    337 }
    338 
    339 PrefProvider::PrefProvider(Profile* profile)
    340   : BaseProvider(profile->IsOffTheRecord()),
    341     profile_(profile),
    342     updating_preferences_(false) {
    343   Init();
    344 }
    345 
    346 void PrefProvider::Init() {
    347   initializing_ = true;
    348   PrefService* prefs = profile_->GetPrefs();
    349 
    350   // Migrate obsolete preferences.
    351   MigrateObsoletePerhostPref(prefs);
    352   MigrateObsoletePopupsPref(prefs);
    353 
    354   // Verify preferences version.
    355   if (!prefs->HasPrefPath(prefs::kContentSettingsVersion)) {
    356     prefs->SetInteger(prefs::kContentSettingsVersion,
    357                       ContentSettingsPattern::kContentSettingsPatternVersion);
    358   }
    359   if (prefs->GetInteger(prefs::kContentSettingsVersion) >
    360       ContentSettingsPattern::kContentSettingsPatternVersion) {
    361     LOG(ERROR) << "Unknown content settings version in preferences.";
    362     return;
    363   }
    364 
    365   // Read exceptions.
    366   ReadExceptions(false);
    367 
    368   pref_change_registrar_.Init(prefs);
    369   pref_change_registrar_.Add(prefs::kContentSettingsPatterns, this);
    370 
    371   notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
    372                               Source<Profile>(profile_));
    373   initializing_ = false;
    374 }
    375 
    376 bool PrefProvider::ContentSettingsTypeIsManaged(
    377     ContentSettingsType content_type) {
    378   return false;
    379 }
    380 
    381 void PrefProvider::SetContentSetting(
    382     const ContentSettingsPattern& requesting_pattern,
    383     const ContentSettingsPattern& embedding_pattern,
    384     ContentSettingsType content_type,
    385     const ResourceIdentifier& resource_identifier,
    386     ContentSetting setting) {
    387   // Support for embedding_patterns is not implemented yet.
    388   DCHECK(requesting_pattern == embedding_pattern);
    389 
    390   DCHECK(kTypeNames[content_type] != NULL);  // Don't call this for Geolocation.
    391   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    392   DCHECK_NE(RequiresResourceIdentifier(content_type),
    393             resource_identifier.empty());
    394   DCHECK(content_type != CONTENT_SETTINGS_TYPE_PLUGINS ||
    395          setting != CONTENT_SETTING_ASK ||
    396          CommandLine::ForCurrentProcess()->HasSwitch(
    397             switches::kEnableClickToPlay));
    398 
    399   const ContentSettingsPattern pattern(
    400       requesting_pattern.CanonicalizePattern());
    401 
    402   bool early_exit = false;
    403   std::string pattern_str(pattern.AsString());
    404   DictionaryValue* all_settings_dictionary = NULL;
    405 
    406   updating_preferences_ = true;
    407   {  // Begin scope of update.
    408     // profile_ may be NULL in unit tests.
    409     DictionaryPrefUpdate update(profile_ ? profile_->GetPrefs() : NULL,
    410                                 prefs::kContentSettingsPatterns);
    411 
    412     // Select content-settings-map to write to.
    413     HostContentSettings* map_to_modify = incognito_settings();
    414     if (!is_incognito()) {
    415       all_settings_dictionary = update.Get();
    416 
    417       map_to_modify = host_content_settings();
    418     }
    419 
    420     // Update content-settings-map.
    421     {
    422       base::AutoLock auto_lock(lock());
    423       if (!map_to_modify->count(pattern_str))
    424         (*map_to_modify)[pattern_str].content_settings = ContentSettings();
    425       HostContentSettings::iterator i(map_to_modify->find(pattern_str));
    426       ContentSettings& settings = i->second.content_settings;
    427       if (RequiresResourceIdentifier(content_type)) {
    428         settings.settings[content_type] = CONTENT_SETTING_DEFAULT;
    429         if (setting != CONTENT_SETTING_DEFAULT) {
    430           i->second.content_settings_for_resources[
    431               ContentSettingsTypeResourceIdentifierPair(content_type,
    432               resource_identifier)] = setting;
    433         } else {
    434           i->second.content_settings_for_resources.erase(
    435               ContentSettingsTypeResourceIdentifierPair(content_type,
    436               resource_identifier));
    437         }
    438       } else {
    439         settings.settings[content_type] = setting;
    440       }
    441       if (AllDefault(i->second)) {
    442         map_to_modify->erase(i);
    443         if (all_settings_dictionary)
    444           all_settings_dictionary->RemoveWithoutPathExpansion(
    445               pattern_str, NULL);
    446 
    447         // We can't just return because |NotifyObservers()| needs to be called,
    448         // without lock() being held.
    449         early_exit = true;
    450       }
    451     }
    452 
    453     // Update the content_settings preference.
    454     if (!early_exit && all_settings_dictionary) {
    455       DictionaryValue* host_settings_dictionary = NULL;
    456       bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    457           pattern_str, &host_settings_dictionary);
    458       if (!found) {
    459         host_settings_dictionary = new DictionaryValue;
    460         all_settings_dictionary->SetWithoutPathExpansion(
    461             pattern_str, host_settings_dictionary);
    462         DCHECK_NE(setting, CONTENT_SETTING_DEFAULT);
    463       }
    464       if (RequiresResourceIdentifier(content_type)) {
    465         std::string dictionary_path(kResourceTypeNames[content_type]);
    466         DictionaryValue* resource_dictionary = NULL;
    467         found = host_settings_dictionary->GetDictionary(
    468             dictionary_path, &resource_dictionary);
    469         if (!found) {
    470           resource_dictionary = new DictionaryValue;
    471           host_settings_dictionary->Set(dictionary_path, resource_dictionary);
    472         }
    473         if (setting == CONTENT_SETTING_DEFAULT) {
    474           resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
    475                                                           NULL);
    476         } else {
    477           resource_dictionary->SetWithoutPathExpansion(
    478               resource_identifier, Value::CreateIntegerValue(setting));
    479         }
    480       } else {
    481         std::string dictionary_path(kTypeNames[content_type]);
    482         if (setting == CONTENT_SETTING_DEFAULT) {
    483           host_settings_dictionary->RemoveWithoutPathExpansion(dictionary_path,
    484                                                                NULL);
    485         } else {
    486           host_settings_dictionary->SetWithoutPathExpansion(
    487               dictionary_path, Value::CreateIntegerValue(setting));
    488         }
    489       }
    490     }
    491   }  // End scope of update.
    492   updating_preferences_ = false;
    493 
    494   NotifyObservers(ContentSettingsDetails(pattern, content_type, ""));
    495 }
    496 
    497 void PrefProvider::ResetToDefaults() {
    498   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    499 
    500   {
    501     base::AutoLock auto_lock(lock());
    502     host_content_settings()->clear();
    503     incognito_settings()->clear();
    504   }
    505 
    506   if (!is_incognito()) {
    507     PrefService* prefs = profile_->GetPrefs();
    508     updating_preferences_ = true;
    509     prefs->ClearPref(prefs::kContentSettingsPatterns);
    510     updating_preferences_ = false;
    511   }
    512 }
    513 
    514 void PrefProvider::ClearAllContentSettingsRules(
    515     ContentSettingsType content_type) {
    516   DCHECK(kTypeNames[content_type] != NULL);  // Don't call this for Geolocation.
    517 
    518   DictionaryValue* all_settings_dictionary = NULL;
    519   HostContentSettings* map_to_modify = incognito_settings();
    520 
    521   updating_preferences_ = true;
    522   {  // Begin scope of update.
    523     DictionaryPrefUpdate update(profile_->GetPrefs(),
    524                                 prefs::kContentSettingsPatterns);
    525 
    526     if (!is_incognito()) {
    527       all_settings_dictionary = update.Get();
    528       map_to_modify = host_content_settings();
    529     }
    530 
    531     {
    532       base::AutoLock auto_lock(lock());
    533       for (HostContentSettings::iterator i(map_to_modify->begin());
    534            i != map_to_modify->end(); ) {
    535         if (RequiresResourceIdentifier(content_type) ||
    536             i->second.content_settings.settings[content_type] !=
    537                 CONTENT_SETTING_DEFAULT) {
    538           if (RequiresResourceIdentifier(content_type))
    539             i->second.content_settings_for_resources.clear();
    540           i->second.content_settings.settings[content_type] =
    541               CONTENT_SETTING_DEFAULT;
    542           std::string host(i->first);
    543           if (AllDefault(i->second)) {
    544             if (all_settings_dictionary)
    545               all_settings_dictionary->RemoveWithoutPathExpansion(host, NULL);
    546             map_to_modify->erase(i++);
    547           } else if (all_settings_dictionary) {
    548             DictionaryValue* host_settings_dictionary;
    549             bool found =
    550                 all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    551                     host, &host_settings_dictionary);
    552             DCHECK(found);
    553             host_settings_dictionary->RemoveWithoutPathExpansion(
    554                 kTypeNames[content_type], NULL);
    555             ++i;
    556           }
    557         } else {
    558           ++i;
    559         }
    560       }
    561     }
    562   }  // End scope of update.
    563   updating_preferences_ = false;
    564 
    565   NotifyObservers(
    566       ContentSettingsDetails(ContentSettingsPattern(), content_type, ""));
    567 }
    568 
    569 void PrefProvider::Observe(
    570     NotificationType type,
    571     const NotificationSource& source,
    572     const NotificationDetails& details) {
    573     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    574 
    575   if (type == NotificationType::PREF_CHANGED) {
    576     DCHECK_EQ(profile_->GetPrefs(), Source<PrefService>(source).ptr());
    577     if (updating_preferences_)
    578       return;
    579 
    580     std::string* name = Details<std::string>(details).ptr();
    581     if (*name == prefs::kContentSettingsPatterns) {
    582       ReadExceptions(true);
    583     } else {
    584       NOTREACHED() << "Unexpected preference observed";
    585       return;
    586     }
    587 
    588     if (!is_incognito()) {
    589       NotifyObservers(ContentSettingsDetails(ContentSettingsPattern(),
    590                                              CONTENT_SETTINGS_TYPE_DEFAULT,
    591                                              ""));
    592     }
    593   } else if (type == NotificationType::PROFILE_DESTROYED) {
    594     DCHECK_EQ(profile_, Source<Profile>(source).ptr());
    595     UnregisterObservers();
    596   } else {
    597     NOTREACHED() << "Unexpected notification";
    598   }
    599 }
    600 
    601 PrefProvider::~PrefProvider() {
    602   UnregisterObservers();
    603 }
    604 
    605 // ////////////////////////////////////////////////////////////////////////////
    606 // Private
    607 
    608 void PrefProvider::ReadExceptions(bool overwrite) {
    609   base::AutoLock auto_lock(lock());
    610 
    611   PrefService* prefs = profile_->GetPrefs();
    612   const DictionaryValue* all_settings_dictionary =
    613       prefs->GetDictionary(prefs::kContentSettingsPatterns);
    614 
    615   if (overwrite)
    616     host_content_settings()->clear();
    617 
    618   updating_preferences_ = true;
    619 
    620   // Careful: The returned value could be NULL if the pref has never been set.
    621   if (all_settings_dictionary != NULL) {
    622     DictionaryPrefUpdate update(prefs, prefs::kContentSettingsPatterns);
    623     DictionaryValue* mutable_settings;
    624     scoped_ptr<DictionaryValue> mutable_settings_scope;
    625 
    626     if (!is_incognito()) {
    627       mutable_settings = update.Get();
    628     } else {
    629       // Create copy as we do not want to persist anything in OTR prefs.
    630       mutable_settings = all_settings_dictionary->DeepCopy();
    631       mutable_settings_scope.reset(mutable_settings);
    632     }
    633 
    634     // Convert all Unicode patterns into punycode form, then read.
    635     CanonicalizeContentSettingsExceptions(mutable_settings);
    636 
    637     for (DictionaryValue::key_iterator i(mutable_settings->begin_keys());
    638          i != mutable_settings->end_keys(); ++i) {
    639       const std::string& pattern(*i);
    640       if (!ContentSettingsPattern(pattern).IsValid())
    641         LOG(WARNING) << "Invalid pattern stored in content settings";
    642       DictionaryValue* pattern_settings_dictionary = NULL;
    643       bool found = mutable_settings->GetDictionaryWithoutPathExpansion(
    644           pattern, &pattern_settings_dictionary);
    645       DCHECK(found);
    646 
    647       ExtendedContentSettings extended_settings;
    648       GetSettingsFromDictionary(pattern_settings_dictionary,
    649                                 &extended_settings.content_settings);
    650       GetResourceSettingsFromDictionary(
    651           pattern_settings_dictionary,
    652           &extended_settings.content_settings_for_resources);
    653 
    654       (*host_content_settings())[pattern] = extended_settings;
    655     }
    656   }
    657   updating_preferences_ = false;
    658 }
    659 
    660 void PrefProvider::CanonicalizeContentSettingsExceptions(
    661     DictionaryValue* all_settings_dictionary) {
    662   DCHECK(all_settings_dictionary);
    663 
    664   std::vector<std::string> remove_items;
    665   std::vector<std::pair<std::string, std::string> > move_items;
    666   for (DictionaryValue::key_iterator i(all_settings_dictionary->begin_keys());
    667        i != all_settings_dictionary->end_keys(); ++i) {
    668     const std::string& pattern(*i);
    669     const std::string canonicalized_pattern =
    670         ContentSettingsPattern(pattern).CanonicalizePattern();
    671 
    672     if (canonicalized_pattern.empty() || canonicalized_pattern == pattern)
    673       continue;
    674 
    675     // Clear old pattern if prefs already have canonicalized pattern.
    676     DictionaryValue* new_pattern_settings_dictionary = NULL;
    677     if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    678             canonicalized_pattern, &new_pattern_settings_dictionary)) {
    679       remove_items.push_back(pattern);
    680       continue;
    681     }
    682 
    683     // Move old pattern to canonicalized pattern.
    684     DictionaryValue* old_pattern_settings_dictionary = NULL;
    685     if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    686             pattern, &old_pattern_settings_dictionary)) {
    687       move_items.push_back(std::make_pair(pattern, canonicalized_pattern));
    688     }
    689   }
    690 
    691   for (size_t i = 0; i < remove_items.size(); ++i) {
    692     all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
    693   }
    694 
    695   for (size_t i = 0; i < move_items.size(); ++i) {
    696     Value* pattern_settings_dictionary = NULL;
    697     all_settings_dictionary->RemoveWithoutPathExpansion(
    698         move_items[i].first, &pattern_settings_dictionary);
    699     all_settings_dictionary->SetWithoutPathExpansion(
    700         move_items[i].second, pattern_settings_dictionary);
    701   }
    702 }
    703 
    704 void PrefProvider::GetSettingsFromDictionary(
    705     const DictionaryValue* dictionary,
    706     ContentSettings* settings) {
    707   for (DictionaryValue::key_iterator i(dictionary->begin_keys());
    708        i != dictionary->end_keys(); ++i) {
    709     const std::string& content_type(*i);
    710     for (size_t type = 0; type < arraysize(kTypeNames); ++type) {
    711       if ((kTypeNames[type] != NULL) && (kTypeNames[type] == content_type)) {
    712         int setting = CONTENT_SETTING_DEFAULT;
    713         bool found = dictionary->GetIntegerWithoutPathExpansion(content_type,
    714                                                                 &setting);
    715         DCHECK(found);
    716         settings->settings[type] = IntToContentSetting(setting);
    717         break;
    718       }
    719     }
    720   }
    721   // Migrate obsolete cookie prompt mode.
    722   if (settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] ==
    723       CONTENT_SETTING_ASK)
    724     settings->settings[CONTENT_SETTINGS_TYPE_COOKIES] = CONTENT_SETTING_BLOCK;
    725 
    726   settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS] =
    727       ClickToPlayFixup(CONTENT_SETTINGS_TYPE_PLUGINS,
    728                        settings->settings[CONTENT_SETTINGS_TYPE_PLUGINS]);
    729 }
    730 
    731 void PrefProvider::GetResourceSettingsFromDictionary(
    732     const DictionaryValue* dictionary,
    733     ResourceContentSettings* settings) {
    734   for (DictionaryValue::key_iterator i(dictionary->begin_keys());
    735        i != dictionary->end_keys(); ++i) {
    736     const std::string& content_type(*i);
    737     for (size_t type = 0; type < arraysize(kResourceTypeNames); ++type) {
    738       if ((kResourceTypeNames[type] != NULL) &&
    739           (kResourceTypeNames[type] == content_type)) {
    740         DictionaryValue* resource_dictionary = NULL;
    741         bool found = dictionary->GetDictionary(content_type,
    742                                                &resource_dictionary);
    743         DCHECK(found);
    744         for (DictionaryValue::key_iterator j(resource_dictionary->begin_keys());
    745              j != resource_dictionary->end_keys(); ++j) {
    746           const std::string& resource_identifier(*j);
    747           int setting = CONTENT_SETTING_DEFAULT;
    748           bool found = resource_dictionary->GetIntegerWithoutPathExpansion(
    749               resource_identifier, &setting);
    750           DCHECK(found);
    751           (*settings)[ContentSettingsTypeResourceIdentifierPair(
    752               ContentSettingsType(type), resource_identifier)] =
    753                   ClickToPlayFixup(ContentSettingsType(type),
    754                                    ContentSetting(setting));
    755         }
    756 
    757         break;
    758       }
    759     }
    760   }
    761 }
    762 
    763 void PrefProvider::NotifyObservers(
    764     const ContentSettingsDetails& details) {
    765   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    766   if (initializing_ || profile_ == NULL)
    767     return;
    768   NotificationService::current()->Notify(
    769       NotificationType::CONTENT_SETTINGS_CHANGED,
    770       Source<HostContentSettingsMap>(
    771           profile_->GetHostContentSettingsMap()),
    772       Details<const ContentSettingsDetails>(&details));
    773 }
    774 
    775 void PrefProvider::UnregisterObservers() {
    776   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    777   if (!profile_)
    778     return;
    779   pref_change_registrar_.RemoveAll();
    780   notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
    781                                  Source<Profile>(profile_));
    782   profile_ = NULL;
    783 }
    784 
    785 void PrefProvider::MigrateObsoletePerhostPref(PrefService* prefs) {
    786   if (prefs->HasPrefPath(prefs::kPerHostContentSettings)) {
    787     const DictionaryValue* all_settings_dictionary =
    788         prefs->GetDictionary(prefs::kPerHostContentSettings);
    789     DCHECK(all_settings_dictionary);
    790     for (DictionaryValue::key_iterator
    791          i(all_settings_dictionary->begin_keys());
    792          i != all_settings_dictionary->end_keys(); ++i) {
    793       const std::string& host(*i);
    794       ContentSettingsPattern pattern(
    795           std::string(ContentSettingsPattern::kDomainWildcard) + host);
    796       DictionaryValue* host_settings_dictionary = NULL;
    797       bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    798           host, &host_settings_dictionary);
    799       DCHECK(found);
    800       ContentSettings settings;
    801       GetSettingsFromDictionary(host_settings_dictionary, &settings);
    802       for (int j = 0; j < CONTENT_SETTINGS_NUM_TYPES; ++j) {
    803         if (settings.settings[j] != CONTENT_SETTING_DEFAULT &&
    804             !RequiresResourceIdentifier(ContentSettingsType(j))) {
    805           SetContentSetting(
    806               pattern,
    807               pattern,
    808               ContentSettingsType(j),
    809               "",
    810               settings.settings[j]);
    811         }
    812       }
    813     }
    814     prefs->ClearPref(prefs::kPerHostContentSettings);
    815   }
    816 }
    817 
    818 void PrefProvider::MigrateObsoletePopupsPref(PrefService* prefs) {
    819   if (prefs->HasPrefPath(prefs::kPopupWhitelistedHosts)) {
    820     const ListValue* whitelist_pref =
    821         prefs->GetList(prefs::kPopupWhitelistedHosts);
    822     for (ListValue::const_iterator i(whitelist_pref->begin());
    823          i != whitelist_pref->end(); ++i) {
    824       std::string host;
    825       (*i)->GetAsString(&host);
    826       SetContentSetting(ContentSettingsPattern(host),
    827                         ContentSettingsPattern(host),
    828                         CONTENT_SETTINGS_TYPE_POPUPS,
    829                         "",
    830                         CONTENT_SETTING_ALLOW);
    831     }
    832     prefs->ClearPref(prefs::kPopupWhitelistedHosts);
    833   }
    834 }
    835 
    836 }  // namespace content_settings
    837