- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / content_settings / content_settings_pref_provider.cc
1 // Copyright (c) 2012 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 <map>
8 #include <string>
9 #include <utility>
10
11 #include "base/auto_reset.h"
12 #include "base/command_line.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/prefs/scoped_user_pref_update.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/content_settings/content_settings_rule.h"
19 #include "chrome/browser/content_settings/content_settings_utils.h"
20 #include "chrome/browser/content_settings/host_content_settings_map.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/content_settings.h"
23 #include "chrome/common/content_settings_pattern.h"
24 #include "chrome/common/pref_names.h"
25 #include "components/user_prefs/pref_registry_syncable.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_source.h"
29 #include "content/public/browser/user_metrics.h"
30 #include "url/gurl.h"
31
32 using content::BrowserThread;
33 using content::UserMetricsAction;
34
35 namespace {
36
37 typedef std::pair<std::string, std::string> StringPair;
38 typedef std::map<std::string, std::string> StringMap;
39
40 const char kPerPluginPrefName[] = "per_plugin";
41 const char kAudioKey[] = "audio";
42 const char kVideoKey[] = "video";
43
44 ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type,
45                                            ContentSetting setting) {
46   if (content_type == CONTENT_SETTINGS_TYPE_COOKIES &&
47       setting == CONTENT_SETTING_ASK) {
48     return CONTENT_SETTING_BLOCK;
49   }
50   return setting;
51 }
52
53 // If the given content type supports resource identifiers in user preferences,
54 // returns true and sets |pref_key| to the key in the content settings
55 // dictionary under which per-resource content settings are stored.
56 // Otherwise, returns false.
57 bool GetResourceTypeName(ContentSettingsType content_type,
58                          std::string* pref_key) {
59   if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
60     *pref_key = kPerPluginPrefName;
61     return true;
62   }
63   return false;
64 }
65
66 }  // namespace
67
68 namespace content_settings {
69
70 // ////////////////////////////////////////////////////////////////////////////
71 // PrefProvider:
72 //
73
74 // static
75 void PrefProvider::RegisterProfilePrefs(
76     user_prefs::PrefRegistrySyncable* registry) {
77   registry->RegisterIntegerPref(
78       prefs::kContentSettingsVersion,
79       ContentSettingsPattern::kContentSettingsPatternVersion,
80       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
81   registry->RegisterDictionaryPref(
82       prefs::kContentSettingsPatternPairs,
83       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
84 }
85
86 PrefProvider::PrefProvider(PrefService* prefs,
87                            bool incognito)
88   : prefs_(prefs),
89     is_incognito_(incognito),
90     updating_preferences_(false) {
91   DCHECK(prefs_);
92   // Verify preferences version.
93   if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) {
94     prefs_->SetInteger(prefs::kContentSettingsVersion,
95                       ContentSettingsPattern::kContentSettingsPatternVersion);
96   }
97   if (prefs_->GetInteger(prefs::kContentSettingsVersion) >
98       ContentSettingsPattern::kContentSettingsPatternVersion) {
99     return;
100   }
101
102   // Read content settings exceptions.
103   ReadContentSettingsFromPref(false);
104
105   if (!is_incognito_) {
106     UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfExceptions",
107                          value_map_.size());
108   }
109
110   // Migrate the obsolete media content setting exceptions to the new settings.
111   // This needs to be done after ReadContentSettingsFromPref().
112   if (!is_incognito_)
113     MigrateObsoleteMediaContentSetting();
114
115   pref_change_registrar_.Init(prefs_);
116   pref_change_registrar_.Add(
117       prefs::kContentSettingsPatternPairs,
118       base::Bind(&PrefProvider::OnContentSettingsPatternPairsChanged,
119                  base::Unretained(this)));
120 }
121
122 bool PrefProvider::SetWebsiteSetting(
123     const ContentSettingsPattern& primary_pattern,
124     const ContentSettingsPattern& secondary_pattern,
125     ContentSettingsType content_type,
126     const ResourceIdentifier& resource_identifier,
127     Value* in_value) {
128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
129   DCHECK(prefs_);
130   // Default settings are set using a wildcard pattern for both
131   // |primary_pattern| and |secondary_pattern|. Don't store default settings in
132   // the |PrefProvider|. The |PrefProvider| handles settings for specific
133   // sites/origins defined by the |primary_pattern| and the |secondary_pattern|.
134   // Default settings are handled by the |DefaultProvider|.
135   if (primary_pattern == ContentSettingsPattern::Wildcard() &&
136       secondary_pattern == ContentSettingsPattern::Wildcard() &&
137       resource_identifier.empty()) {
138     return false;
139   }
140
141   // At this point take the ownership of the |in_value|.
142   scoped_ptr<base::Value> value(in_value);
143   // Update in memory value map.
144   OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
145   if (!is_incognito_)
146     map_to_modify = &value_map_;
147
148   {
149     base::AutoLock auto_lock(lock_);
150     if (value.get()) {
151       map_to_modify->SetValue(
152           primary_pattern,
153           secondary_pattern,
154           content_type,
155           resource_identifier,
156           value->DeepCopy());
157     } else {
158       map_to_modify->DeleteValue(
159           primary_pattern,
160           secondary_pattern,
161           content_type,
162           resource_identifier);
163     }
164   }
165   // Update the content settings preference.
166   if (!is_incognito_) {
167     UpdatePref(primary_pattern,
168                secondary_pattern,
169                content_type,
170                resource_identifier,
171                value.get());
172   }
173
174   NotifyObservers(
175       primary_pattern, secondary_pattern, content_type, resource_identifier);
176
177   return true;
178 }
179
180 void PrefProvider::ClearAllContentSettingsRules(
181     ContentSettingsType content_type) {
182   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183   DCHECK(prefs_);
184
185   OriginIdentifierValueMap* map_to_modify = &incognito_value_map_;
186   if (!is_incognito_)
187     map_to_modify = &value_map_;
188
189   std::vector<Rule> rules_to_delete;
190   {
191     base::AutoLock auto_lock(lock_);
192     scoped_ptr<RuleIterator> rule_iterator(
193         map_to_modify->GetRuleIterator(content_type, std::string(), NULL));
194     // Copy the rules; we cannot call |UpdatePref| while holding |lock_|.
195     while (rule_iterator->HasNext())
196       rules_to_delete.push_back(rule_iterator->Next());
197
198     map_to_modify->DeleteValues(content_type, std::string());
199   }
200
201   for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
202        it != rules_to_delete.end(); ++it) {
203     UpdatePref(it->primary_pattern,
204                it->secondary_pattern,
205                content_type,
206                std::string(),
207                NULL);
208   }
209   NotifyObservers(ContentSettingsPattern(),
210                   ContentSettingsPattern(),
211                   content_type,
212                   std::string());
213 }
214
215 PrefProvider::~PrefProvider() {
216   DCHECK(!prefs_);
217 }
218
219 RuleIterator* PrefProvider::GetRuleIterator(
220     ContentSettingsType content_type,
221     const ResourceIdentifier& resource_identifier,
222     bool incognito) const {
223   if (incognito)
224     return incognito_value_map_.GetRuleIterator(content_type,
225                                                 resource_identifier,
226                                                 &lock_);
227   return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_);
228 }
229
230 // ////////////////////////////////////////////////////////////////////////////
231 // Private
232
233 void PrefProvider::UpdatePref(
234     const ContentSettingsPattern& primary_pattern,
235     const ContentSettingsPattern& secondary_pattern,
236     ContentSettingsType content_type,
237     const ResourceIdentifier& resource_identifier,
238     const base::Value* value) {
239   // Ensure that |lock_| is not held by this thread, since this function will
240   // send out notifications (by |~DictionaryPrefUpdate|).
241   AssertLockNotHeld();
242
243   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
244   {
245     DictionaryPrefUpdate update(prefs_,
246                                 prefs::kContentSettingsPatternPairs);
247     DictionaryValue* pattern_pairs_settings = update.Get();
248
249     // Get settings dictionary for the given patterns.
250     std::string pattern_str(CreatePatternString(primary_pattern,
251                                                 secondary_pattern));
252     DictionaryValue* settings_dictionary = NULL;
253     bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion(
254         pattern_str, &settings_dictionary);
255
256     if (!found && value) {
257       settings_dictionary = new DictionaryValue;
258       pattern_pairs_settings->SetWithoutPathExpansion(
259           pattern_str, settings_dictionary);
260     }
261
262     if (settings_dictionary) {
263       std::string res_dictionary_path;
264       if (GetResourceTypeName(content_type, &res_dictionary_path) &&
265           !resource_identifier.empty()) {
266         DictionaryValue* resource_dictionary = NULL;
267         found = settings_dictionary->GetDictionary(
268             res_dictionary_path, &resource_dictionary);
269         if (!found) {
270           if (value == NULL)
271             return;  // Nothing to remove. Exit early.
272           resource_dictionary = new DictionaryValue;
273           settings_dictionary->Set(res_dictionary_path, resource_dictionary);
274         }
275         // Update resource dictionary.
276         if (value == NULL) {
277           resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
278                                                           NULL);
279           if (resource_dictionary->empty()) {
280             settings_dictionary->RemoveWithoutPathExpansion(
281                 res_dictionary_path, NULL);
282           }
283         } else {
284           resource_dictionary->SetWithoutPathExpansion(
285               resource_identifier, value->DeepCopy());
286         }
287       } else {
288         // Update settings dictionary.
289         std::string setting_path = GetTypeName(content_type);
290         if (value == NULL) {
291           settings_dictionary->RemoveWithoutPathExpansion(setting_path,
292                                                           NULL);
293         } else {
294           settings_dictionary->SetWithoutPathExpansion(
295               setting_path, value->DeepCopy());
296         }
297       }
298       // Remove the settings dictionary if it is empty.
299       if (settings_dictionary->empty()) {
300         pattern_pairs_settings->RemoveWithoutPathExpansion(
301             pattern_str, NULL);
302       }
303     }
304   }
305 }
306
307
308 void PrefProvider::MigrateObsoleteMediaContentSetting() {
309   std::vector<Rule> rules_to_delete;
310   {
311     scoped_ptr<RuleIterator> rule_iterator(GetRuleIterator(
312         CONTENT_SETTINGS_TYPE_MEDIASTREAM, std::string(), false));
313     while (rule_iterator->HasNext()) {
314       // Skip default setting and rules without a value.
315       const content_settings::Rule& rule = rule_iterator->Next();
316       DCHECK(rule.primary_pattern != ContentSettingsPattern::Wildcard());
317       if (!rule.value.get())
318         continue;
319       rules_to_delete.push_back(rule);
320     }
321   }
322
323   for (std::vector<Rule>::const_iterator it = rules_to_delete.begin();
324        it != rules_to_delete.end(); ++it) {
325     const DictionaryValue* value_dict = NULL;
326     if (!it->value->GetAsDictionary(&value_dict) || value_dict->empty())
327       return;
328
329     std::string audio_device, video_device;
330     value_dict->GetString(kAudioKey, &audio_device);
331     value_dict->GetString(kVideoKey, &video_device);
332     // Add the exception to the new microphone content setting.
333     if (!audio_device.empty()) {
334       SetWebsiteSetting(it->primary_pattern,
335                         it->secondary_pattern,
336                         CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
337                         std::string(),
338                         Value::CreateIntegerValue(CONTENT_SETTING_ALLOW));
339     }
340     // Add the exception to the new camera content setting.
341     if (!video_device.empty()) {
342       SetWebsiteSetting(it->primary_pattern,
343                         it->secondary_pattern,
344                         CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
345                         std::string(),
346                         Value::CreateIntegerValue(CONTENT_SETTING_ALLOW));
347     }
348
349     // Remove the old exception in CONTENT_SETTINGS_TYPE_MEDIASTREAM.
350     SetWebsiteSetting(it->primary_pattern,
351                       it->secondary_pattern,
352                       CONTENT_SETTINGS_TYPE_MEDIASTREAM,
353                       std::string(),
354                       NULL);
355   }
356 }
357
358 void PrefProvider::ReadContentSettingsFromPref(bool overwrite) {
359   // |DictionaryPrefUpdate| sends out notifications when destructed. This
360   // construction order ensures |AutoLock| gets destroyed first and |lock_| is
361   // not held when the notifications are sent. Also, |auto_reset| must be still
362   // valid when the notifications are sent, so that |Observe| skips the
363   // notification.
364   base::AutoReset<bool> auto_reset(&updating_preferences_, true);
365   DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs);
366   base::AutoLock auto_lock(lock_);
367
368   const DictionaryValue* all_settings_dictionary =
369       prefs_->GetDictionary(prefs::kContentSettingsPatternPairs);
370
371   if (overwrite)
372     value_map_.clear();
373
374   // Careful: The returned value could be NULL if the pref has never been set.
375   if (!all_settings_dictionary)
376     return;
377
378   DictionaryValue* mutable_settings;
379   scoped_ptr<DictionaryValue> mutable_settings_scope;
380
381   if (!is_incognito_) {
382     mutable_settings = update.Get();
383   } else {
384     // Create copy as we do not want to persist anything in OTR prefs.
385     mutable_settings = all_settings_dictionary->DeepCopy();
386     mutable_settings_scope.reset(mutable_settings);
387   }
388   // Convert all Unicode patterns into punycode form, then read.
389   CanonicalizeContentSettingsExceptions(mutable_settings);
390
391   size_t cookies_block_exception_count = 0;
392   size_t cookies_allow_exception_count = 0;
393   size_t cookies_session_only_exception_count = 0;
394   for (DictionaryValue::Iterator i(*mutable_settings); !i.IsAtEnd();
395        i.Advance()) {
396     const std::string& pattern_str(i.key());
397     std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
398         ParsePatternString(pattern_str);
399     if (!pattern_pair.first.IsValid() ||
400         !pattern_pair.second.IsValid()) {
401       // TODO: Change this to DFATAL when crbug.com/132659 is fixed.
402       LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
403       continue;
404     }
405
406     // Get settings dictionary for the current pattern string, and read
407     // settings from the dictionary.
408     const DictionaryValue* settings_dictionary = NULL;
409     bool is_dictionary = i.value().GetAsDictionary(&settings_dictionary);
410     DCHECK(is_dictionary);
411
412     for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
413       ContentSettingsType content_type = static_cast<ContentSettingsType>(i);
414
415       std::string res_dictionary_path;
416       if (GetResourceTypeName(content_type, &res_dictionary_path)) {
417         const DictionaryValue* resource_dictionary = NULL;
418         if (settings_dictionary->GetDictionary(
419                 res_dictionary_path, &resource_dictionary)) {
420           for (DictionaryValue::Iterator j(*resource_dictionary); !j.IsAtEnd();
421                j.Advance()) {
422             const std::string& resource_identifier(j.key());
423             int setting = CONTENT_SETTING_DEFAULT;
424             bool is_integer = j.value().GetAsInteger(&setting);
425             DCHECK(is_integer);
426             DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
427             value_map_.SetValue(pattern_pair.first,
428                                 pattern_pair.second,
429                                 content_type,
430                                 resource_identifier,
431                                 Value::CreateIntegerValue(setting));
432           }
433         }
434       }
435       Value* value = NULL;
436       if (HostContentSettingsMap::ContentTypeHasCompoundValue(content_type)) {
437         const DictionaryValue* setting = NULL;
438         // TODO(xians): Handle the non-dictionary types.
439         if (settings_dictionary->GetDictionaryWithoutPathExpansion(
440             GetTypeName(ContentSettingsType(i)), &setting)) {
441           DCHECK(!setting->empty());
442           value = setting->DeepCopy();
443         }
444       } else {
445         int setting = CONTENT_SETTING_DEFAULT;
446         if (settings_dictionary->GetIntegerWithoutPathExpansion(
447                 GetTypeName(ContentSettingsType(i)), &setting)) {
448           DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
449           setting = FixObsoleteCookiePromptMode(content_type,
450                                                 ContentSetting(setting));
451           value = Value::CreateIntegerValue(setting);
452         }
453       }
454
455       // |value_map_| will take the ownership of |value|.
456       if (value != NULL) {
457         value_map_.SetValue(pattern_pair.first,
458                             pattern_pair.second,
459                             content_type,
460                             ResourceIdentifier(),
461                             value);
462         if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
463           ContentSetting s = ValueToContentSetting(value);
464           switch (s) {
465             case CONTENT_SETTING_ALLOW :
466               ++cookies_allow_exception_count;
467               break;
468             case CONTENT_SETTING_BLOCK :
469               ++cookies_block_exception_count;
470               break;
471             case CONTENT_SETTING_SESSION_ONLY :
472               ++cookies_session_only_exception_count;
473               break;
474             default:
475               NOTREACHED();
476               break;
477           }
478         }
479       }
480     }
481   }
482   UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfBlockCookiesExceptions",
483                        cookies_block_exception_count);
484   UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfAllowCookiesExceptions",
485                        cookies_allow_exception_count);
486   UMA_HISTOGRAM_COUNTS("ContentSettings.NumberOfSessionOnlyCookiesExceptions",
487                        cookies_session_only_exception_count);
488 }
489
490 void PrefProvider::OnContentSettingsPatternPairsChanged() {
491   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
492
493   if (updating_preferences_)
494     return;
495
496   ReadContentSettingsFromPref(true);
497
498   NotifyObservers(ContentSettingsPattern(),
499                   ContentSettingsPattern(),
500                   CONTENT_SETTINGS_TYPE_DEFAULT,
501                   std::string());
502 }
503
504 // static
505 void PrefProvider::CanonicalizeContentSettingsExceptions(
506     DictionaryValue* all_settings_dictionary) {
507   DCHECK(all_settings_dictionary);
508
509   std::vector<std::string> remove_items;
510   std::vector<std::pair<std::string, std::string> > move_items;
511   for (DictionaryValue::Iterator i(*all_settings_dictionary); !i.IsAtEnd();
512        i.Advance()) {
513     const std::string& pattern_str(i.key());
514     std::pair<ContentSettingsPattern, ContentSettingsPattern> pattern_pair =
515          ParsePatternString(pattern_str);
516     if (!pattern_pair.first.IsValid() ||
517         !pattern_pair.second.IsValid()) {
518       LOG(ERROR) << "Invalid pattern strings: " << pattern_str;
519       continue;
520     }
521
522     const std::string canonicalized_pattern_str = CreatePatternString(
523         pattern_pair.first, pattern_pair.second);
524
525     if (canonicalized_pattern_str.empty() ||
526         canonicalized_pattern_str == pattern_str) {
527       continue;
528     }
529
530     // Clear old pattern if prefs already have canonicalized pattern.
531     const DictionaryValue* new_pattern_settings_dictionary = NULL;
532     if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
533             canonicalized_pattern_str, &new_pattern_settings_dictionary)) {
534       remove_items.push_back(pattern_str);
535       continue;
536     }
537
538     // Move old pattern to canonicalized pattern.
539     const DictionaryValue* old_pattern_settings_dictionary = NULL;
540     if (i.value().GetAsDictionary(&old_pattern_settings_dictionary)) {
541       move_items.push_back(
542           std::make_pair(pattern_str, canonicalized_pattern_str));
543     }
544   }
545
546   for (size_t i = 0; i < remove_items.size(); ++i) {
547     all_settings_dictionary->RemoveWithoutPathExpansion(remove_items[i], NULL);
548   }
549
550   for (size_t i = 0; i < move_items.size(); ++i) {
551     scoped_ptr<Value> pattern_settings_dictionary;
552     all_settings_dictionary->RemoveWithoutPathExpansion(
553         move_items[i].first, &pattern_settings_dictionary);
554     all_settings_dictionary->SetWithoutPathExpansion(
555         move_items[i].second, pattern_settings_dictionary.release());
556   }
557 }
558
559 void PrefProvider::ShutdownOnUIThread() {
560   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
561   DCHECK(prefs_);
562   RemoveAllObservers();
563   pref_change_registrar_.RemoveAll();
564   prefs_ = NULL;
565 }
566
567 void PrefProvider::AssertLockNotHeld() const {
568 #if !defined(NDEBUG)
569   // |Lock::Acquire()| will assert if the lock is held by this thread.
570   lock_.Acquire();
571   lock_.Release();
572 #endif
573 }
574
575 }  // namespace content_settings