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