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
28namespace {
29
30// The preference keys where resource identifiers are stored for
31// ContentSettingsType values that support resource identifiers.
32const 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};
42COMPILE_ASSERT(arraysize(kResourceTypeNames) == CONTENT_SETTINGS_NUM_TYPES,
43               resource_type_names_incorrect_size);
44
45// The default setting for each content type.
46const 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};
56COMPILE_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.
60const 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};
72COMPILE_ASSERT(arraysize(kTypeNames) == CONTENT_SETTINGS_NUM_TYPES,
73               type_names_incorrect_size);
74
75void 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
88namespace content_settings {
89
90PrefDefaultProvider::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
119PrefDefaultProvider::~PrefDefaultProvider() {
120  UnregisterObservers();
121}
122
123ContentSetting PrefDefaultProvider::ProvideDefaultSetting(
124    ContentSettingsType content_type) const {
125  base::AutoLock lock(lock_);
126  return default_content_settings_.settings[content_type];
127}
128
129void 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
170bool PrefDefaultProvider::DefaultSettingIsManaged(
171    ContentSettingsType content_type) const {
172  return false;
173}
174
175void 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
189void 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
219void 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
229void 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
247void 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
257void 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
285void 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
296void 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
306void 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
329void 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
339PrefProvider::PrefProvider(Profile* profile)
340  : BaseProvider(profile->IsOffTheRecord()),
341    profile_(profile),
342    updating_preferences_(false) {
343  Init();
344}
345
346void 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
376bool PrefProvider::ContentSettingsTypeIsManaged(
377    ContentSettingsType content_type) {
378  return false;
379}
380
381void 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
497void 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
514void 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
569void 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
601PrefProvider::~PrefProvider() {
602  UnregisterObservers();
603}
604
605// ////////////////////////////////////////////////////////////////////////////
606// Private
607
608void 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
660void 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
704void 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
731void 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
763void 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
775void 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
785void 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
818void 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