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_default_provider.h"
6
7#include <string>
8#include <vector>
9
10#include "base/auto_reset.h"
11#include "base/basictypes.h"
12#include "base/command_line.h"
13#include "base/metrics/histogram.h"
14#include "base/prefs/pref_service.h"
15#include "base/prefs/scoped_user_pref_update.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/content_settings/content_settings_utils.h"
18#include "chrome/common/pref_names.h"
19#include "components/content_settings/core/browser/content_settings_rule.h"
20#include "components/content_settings/core/common/content_settings.h"
21#include "components/content_settings/core/common/content_settings_pattern.h"
22#include "components/pref_registry/pref_registry_syncable.h"
23#include "content/public/browser/browser_thread.h"
24#include "content/public/browser/notification_details.h"
25#include "content/public/browser/notification_source.h"
26#include "content/public/browser/user_metrics.h"
27#include "url/gurl.h"
28
29using base::UserMetricsAction;
30using content::BrowserThread;
31
32namespace {
33
34// The default setting for each content type.
35const ContentSetting kDefaultSettings[] = {
36  CONTENT_SETTING_ALLOW,    // CONTENT_SETTINGS_TYPE_COOKIES
37  CONTENT_SETTING_ALLOW,    // CONTENT_SETTINGS_TYPE_IMAGES
38  CONTENT_SETTING_ALLOW,    // CONTENT_SETTINGS_TYPE_JAVASCRIPT
39  CONTENT_SETTING_ALLOW,    // CONTENT_SETTINGS_TYPE_PLUGINS
40  CONTENT_SETTING_BLOCK,    // CONTENT_SETTINGS_TYPE_POPUPS
41  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_GEOLOCATION
42  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_NOTIFICATIONS
43  CONTENT_SETTING_DEFAULT,  // CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE
44  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_FULLSCREEN
45  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_MOUSELOCK
46  CONTENT_SETTING_DEFAULT,  // CONTENT_SETTINGS_TYPE_MIXEDSCRIPT
47  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_MEDIASTREAM
48  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
49  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
50  CONTENT_SETTING_DEFAULT,  // CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS
51  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_PPAPI_BROKER
52  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS
53  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_MIDI_SYSEX
54  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_PUSH_MESSAGING
55  CONTENT_SETTING_ALLOW,    // CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS
56#if defined(OS_WIN)
57  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP
58#elif defined(OS_ANDROID) || defined(OS_CHROMEOS)
59  CONTENT_SETTING_ASK,      // CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER
60#endif
61#if defined(OS_ANDROID)
62  CONTENT_SETTING_DEFAULT,  // CONTENT_SETTINGS_TYPE_APP_BANNER
63#endif
64};
65COMPILE_ASSERT(arraysize(kDefaultSettings) == CONTENT_SETTINGS_NUM_TYPES,
66               default_settings_incorrect_size);
67
68}  // namespace
69
70namespace content_settings {
71
72namespace {
73
74class DefaultRuleIterator : public RuleIterator {
75 public:
76  explicit DefaultRuleIterator(const base::Value* value) {
77    if (value)
78      value_.reset(value->DeepCopy());
79  }
80
81  virtual bool HasNext() const OVERRIDE {
82    return value_.get() != NULL;
83  }
84
85  virtual Rule Next() OVERRIDE {
86    DCHECK(value_.get());
87    return Rule(ContentSettingsPattern::Wildcard(),
88                ContentSettingsPattern::Wildcard(),
89                value_.release());
90  }
91
92 private:
93  scoped_ptr<base::Value> value_;
94};
95
96}  // namespace
97
98// static
99void DefaultProvider::RegisterProfilePrefs(
100    user_prefs::PrefRegistrySyncable* registry) {
101  // The registration of the preference prefs::kDefaultContentSettings should
102  // also include the default values for default content settings. This allows
103  // functional tests to get default content settings by reading the preference
104  // prefs::kDefaultContentSettings via pyauto.
105  // TODO(markusheintz): Write pyauto hooks for the content settings map as
106  // content settings should be read from the host content settings map.
107  base::DictionaryValue* default_content_settings = new base::DictionaryValue();
108  registry->RegisterDictionaryPref(
109      prefs::kDefaultContentSettings,
110      default_content_settings,
111      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
112}
113
114DefaultProvider::DefaultProvider(PrefService* prefs, bool incognito)
115    : prefs_(prefs),
116      is_incognito_(incognito),
117      updating_preferences_(false) {
118  DCHECK(prefs_);
119
120  // Read global defaults.
121  ReadDefaultSettings(true);
122
123  UMA_HISTOGRAM_ENUMERATION(
124      "ContentSettings.DefaultCookiesSetting",
125      ValueToContentSetting(
126          default_settings_[CONTENT_SETTINGS_TYPE_COOKIES].get()),
127      CONTENT_SETTING_NUM_SETTINGS);
128  UMA_HISTOGRAM_ENUMERATION(
129      "ContentSettings.DefaultImagesSetting",
130      ValueToContentSetting(
131          default_settings_[CONTENT_SETTINGS_TYPE_IMAGES].get()),
132      CONTENT_SETTING_NUM_SETTINGS);
133  UMA_HISTOGRAM_ENUMERATION(
134      "ContentSettings.DefaultJavaScriptSetting",
135      ValueToContentSetting(
136          default_settings_[CONTENT_SETTINGS_TYPE_JAVASCRIPT].get()),
137      CONTENT_SETTING_NUM_SETTINGS);
138  UMA_HISTOGRAM_ENUMERATION(
139      "ContentSettings.DefaultPluginsSetting",
140      ValueToContentSetting(
141          default_settings_[CONTENT_SETTINGS_TYPE_PLUGINS].get()),
142      CONTENT_SETTING_NUM_SETTINGS);
143  UMA_HISTOGRAM_ENUMERATION(
144      "ContentSettings.DefaultPopupsSetting",
145      ValueToContentSetting(
146          default_settings_[CONTENT_SETTINGS_TYPE_POPUPS].get()),
147      CONTENT_SETTING_NUM_SETTINGS);
148  UMA_HISTOGRAM_ENUMERATION(
149      "ContentSettings.DefaultLocationSetting",
150      ValueToContentSetting(
151          default_settings_[CONTENT_SETTINGS_TYPE_GEOLOCATION].get()),
152      CONTENT_SETTING_NUM_SETTINGS);
153  UMA_HISTOGRAM_ENUMERATION(
154      "ContentSettings.DefaultNotificationsSetting",
155      ValueToContentSetting(
156          default_settings_[CONTENT_SETTINGS_TYPE_NOTIFICATIONS].get()),
157      CONTENT_SETTING_NUM_SETTINGS);
158  UMA_HISTOGRAM_ENUMERATION(
159      "ContentSettings.DefaultMouseCursorSetting",
160      ValueToContentSetting(
161          default_settings_[CONTENT_SETTINGS_TYPE_MOUSELOCK].get()),
162      CONTENT_SETTING_NUM_SETTINGS);
163  UMA_HISTOGRAM_ENUMERATION(
164      "ContentSettings.DefaultMediaStreamSetting",
165      ValueToContentSetting(
166          default_settings_[CONTENT_SETTINGS_TYPE_MEDIASTREAM].get()),
167      CONTENT_SETTING_NUM_SETTINGS);
168  UMA_HISTOGRAM_ENUMERATION(
169      "ContentSettings.DefaultMIDISysExSetting",
170      ValueToContentSetting(
171          default_settings_[CONTENT_SETTINGS_TYPE_MIDI_SYSEX].get()),
172      CONTENT_SETTING_NUM_SETTINGS);
173  UMA_HISTOGRAM_ENUMERATION(
174      "ContentSettings.DefaultPushMessagingSetting",
175      ValueToContentSetting(
176          default_settings_[CONTENT_SETTINGS_TYPE_PUSH_MESSAGING].get()),
177      CONTENT_SETTING_NUM_SETTINGS);
178
179  pref_change_registrar_.Init(prefs_);
180  PrefChangeRegistrar::NamedChangeCallback callback = base::Bind(
181      &DefaultProvider::OnPreferenceChanged, base::Unretained(this));
182  pref_change_registrar_.Add(prefs::kDefaultContentSettings, callback);
183}
184
185DefaultProvider::~DefaultProvider() {
186}
187
188bool DefaultProvider::SetWebsiteSetting(
189    const ContentSettingsPattern& primary_pattern,
190    const ContentSettingsPattern& secondary_pattern,
191    ContentSettingsType content_type,
192    const ResourceIdentifier& resource_identifier,
193    base::Value* in_value) {
194  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195  DCHECK(prefs_);
196
197  // Ignore non default settings
198  if (primary_pattern != ContentSettingsPattern::Wildcard() ||
199      secondary_pattern != ContentSettingsPattern::Wildcard()) {
200    return false;
201  }
202
203  // The default settings may not be directly modified for OTR sessions.
204  // Instead, they are synced to the main profile's setting.
205  if (is_incognito_)
206    return false;
207
208  // Put |in_value| in a scoped pointer to ensure that it gets cleaned up
209  // properly if we don't pass on the ownership.
210  scoped_ptr<base::Value> value(in_value);
211  {
212    base::AutoReset<bool> auto_reset(&updating_preferences_, true);
213
214    // |DefaultProvider| should not send any notifications when holding
215    // |lock_|. |DictionaryPrefUpdate| destructor and
216    // |PrefService::SetInteger()| send out notifications. As a response, the
217    // upper layers may call |GetAllContentSettingRules| which acquires |lock_|
218    // again.
219    DictionaryPrefUpdate update(prefs_, prefs::kDefaultContentSettings);
220    base::DictionaryValue* default_settings_dictionary = update.Get();
221    base::AutoLock lock(lock_);
222    if (value.get() == NULL ||
223        ValueToContentSetting(value.get()) == kDefaultSettings[content_type]) {
224      // If |value| is NULL we need to reset the default setting the the
225      // hardcoded default.
226      default_settings_[content_type].reset(
227          new base::FundamentalValue(kDefaultSettings[content_type]));
228
229      // Remove the corresponding pref entry since the hardcoded default value
230      // is used.
231      default_settings_dictionary->RemoveWithoutPathExpansion(
232          GetTypeName(content_type), NULL);
233    } else {
234      default_settings_[content_type].reset(value->DeepCopy());
235      // Transfer ownership of |value| to the |default_settings_dictionary|.
236      default_settings_dictionary->SetWithoutPathExpansion(
237          GetTypeName(content_type), value.release());
238    }
239  }
240
241  NotifyObservers(ContentSettingsPattern(),
242                  ContentSettingsPattern(),
243                  content_type,
244                  std::string());
245
246  return true;
247}
248
249RuleIterator* DefaultProvider::GetRuleIterator(
250    ContentSettingsType content_type,
251    const ResourceIdentifier& resource_identifier,
252    bool incognito) const {
253  base::AutoLock lock(lock_);
254  if (resource_identifier.empty()) {
255    ValueMap::const_iterator it(default_settings_.find(content_type));
256    if (it != default_settings_.end()) {
257      return new DefaultRuleIterator(it->second.get());
258    }
259    NOTREACHED();
260  }
261  return new EmptyRuleIterator();
262}
263
264void DefaultProvider::ClearAllContentSettingsRules(
265    ContentSettingsType content_type) {
266  // TODO(markusheintz): This method is only called when the
267  // |DesktopNotificationService| calls |ClearAllSettingsForType| method on the
268  // |HostContentSettingsMap|. Don't implement this method yet, otherwise the
269  // default notification settings will be cleared as well.
270}
271
272void DefaultProvider::ShutdownOnUIThread() {
273  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274  DCHECK(prefs_);
275  RemoveAllObservers();
276  pref_change_registrar_.RemoveAll();
277  prefs_ = NULL;
278}
279
280void DefaultProvider::OnPreferenceChanged(const std::string& name) {
281  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
282  if (updating_preferences_)
283    return;
284
285  if (name == prefs::kDefaultContentSettings) {
286    ReadDefaultSettings(true);
287  } else {
288    NOTREACHED() << "Unexpected preference observed";
289    return;
290  }
291
292  NotifyObservers(ContentSettingsPattern(),
293                  ContentSettingsPattern(),
294                  CONTENT_SETTINGS_TYPE_DEFAULT,
295                  std::string());
296}
297
298void DefaultProvider::ReadDefaultSettings(bool overwrite) {
299  base::AutoLock lock(lock_);
300  const base::DictionaryValue* default_settings_dictionary =
301      prefs_->GetDictionary(prefs::kDefaultContentSettings);
302
303  if (overwrite)
304    default_settings_.clear();
305
306  // Careful: The returned value could be NULL if the pref has never been set.
307  if (default_settings_dictionary)
308    GetSettingsFromDictionary(default_settings_dictionary);
309
310  ForceDefaultsToBeExplicit();
311}
312
313void DefaultProvider::ForceDefaultsToBeExplicit() {
314  for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
315    ContentSettingsType type = ContentSettingsType(i);
316    if (!default_settings_[type].get() &&
317        kDefaultSettings[i] != CONTENT_SETTING_DEFAULT) {
318      default_settings_[type].reset(
319          new base::FundamentalValue(kDefaultSettings[i]));
320    }
321  }
322}
323
324void DefaultProvider::GetSettingsFromDictionary(
325    const base::DictionaryValue* dictionary) {
326  for (base::DictionaryValue::Iterator i(*dictionary);
327       !i.IsAtEnd(); i.Advance()) {
328    const std::string& content_type(i.key());
329    for (size_t type = 0; type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
330      if (content_type == GetTypeName(ContentSettingsType(type))) {
331        int int_value = CONTENT_SETTING_DEFAULT;
332        bool is_integer = i.value().GetAsInteger(&int_value);
333        DCHECK(is_integer);
334        default_settings_[ContentSettingsType(type)].reset(
335            new base::FundamentalValue(int_value));
336        break;
337      }
338    }
339  }
340  // Migrate obsolete cookie prompt mode.
341  if (ValueToContentSetting(
342          default_settings_[CONTENT_SETTINGS_TYPE_COOKIES].get()) ==
343              CONTENT_SETTING_ASK) {
344    default_settings_[CONTENT_SETTINGS_TYPE_COOKIES].reset(
345        new base::FundamentalValue(CONTENT_SETTING_BLOCK));
346  }
347}
348
349}  // namespace content_settings
350