• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SetDefaultContentSettings(DictionaryValue * default_settings)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 
PrefDefaultProvider(Profile * profile)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 
~PrefDefaultProvider()119 PrefDefaultProvider::~PrefDefaultProvider() {
120   UnregisterObservers();
121 }
122 
ProvideDefaultSetting(ContentSettingsType content_type) const123 ContentSetting PrefDefaultProvider::ProvideDefaultSetting(
124     ContentSettingsType content_type) const {
125   base::AutoLock lock(lock_);
126   return default_content_settings_.settings[content_type];
127 }
128 
UpdateDefaultSetting(ContentSettingsType content_type,ContentSetting setting)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 
DefaultSettingIsManaged(ContentSettingsType content_type) const170 bool PrefDefaultProvider::DefaultSettingIsManaged(
171     ContentSettingsType content_type) const {
172   return false;
173 }
174 
ResetToDefaults()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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
UnregisterObservers()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 
ReadDefaultSettings(bool overwrite)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 
ForceDefaultsToBeExplicit()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 
GetSettingsFromDictionary(const DictionaryValue * dictionary,ContentSettings * settings)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 
NotifyObservers(const ContentSettingsDetails & details)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 
MigrateObsoleteNotificationPref(PrefService * prefs)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
RegisterUserPrefs(PrefService * prefs)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
RegisterUserPrefs(PrefService * prefs)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 
PrefProvider(Profile * profile)339 PrefProvider::PrefProvider(Profile* profile)
340   : BaseProvider(profile->IsOffTheRecord()),
341     profile_(profile),
342     updating_preferences_(false) {
343   Init();
344 }
345 
Init()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 
ContentSettingsTypeIsManaged(ContentSettingsType content_type)376 bool PrefProvider::ContentSettingsTypeIsManaged(
377     ContentSettingsType content_type) {
378   return false;
379 }
380 
SetContentSetting(const ContentSettingsPattern & requesting_pattern,const ContentSettingsPattern & embedding_pattern,ContentSettingsType content_type,const ResourceIdentifier & resource_identifier,ContentSetting setting)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 
ResetToDefaults()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 
ClearAllContentSettingsRules(ContentSettingsType content_type)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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
~PrefProvider()601 PrefProvider::~PrefProvider() {
602   UnregisterObservers();
603 }
604 
605 // ////////////////////////////////////////////////////////////////////////////
606 // Private
607 
ReadExceptions(bool overwrite)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 
CanonicalizeContentSettingsExceptions(DictionaryValue * all_settings_dictionary)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 
GetSettingsFromDictionary(const DictionaryValue * dictionary,ContentSettings * settings)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 
GetResourceSettingsFromDictionary(const DictionaryValue * dictionary,ResourceContentSettings * settings)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 
NotifyObservers(const ContentSettingsDetails & details)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 
UnregisterObservers()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 
MigrateObsoletePerhostPref(PrefService * prefs)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 
MigrateObsoletePopupsPref(PrefService * prefs)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