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/cookie_settings.h"
6
7#include "base/prefs/pref_service.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/browser/content_settings/content_settings_utils.h"
10#include "chrome/browser/content_settings/host_content_settings_map.h"
11#include "chrome/browser/profiles/incognito_helpers.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/common/pref_names.h"
14#include "components/content_settings/core/common/content_settings_pattern.h"
15#include "components/keyed_service/content/browser_context_dependency_manager.h"
16#include "components/keyed_service/core/keyed_service.h"
17#include "components/pref_registry/pref_registry_syncable.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/browser/notification_service.h"
20#include "content/public/browser/notification_source.h"
21#include "content/public/browser/user_metrics.h"
22#include "extensions/common/constants.h"
23#include "net/base/net_errors.h"
24#include "net/base/static_cookie_policy.h"
25#include "url/gurl.h"
26
27using base::UserMetricsAction;
28using content::BrowserThread;
29
30namespace {
31
32bool IsValidSetting(ContentSetting setting) {
33  return (setting == CONTENT_SETTING_ALLOW ||
34          setting == CONTENT_SETTING_SESSION_ONLY ||
35          setting == CONTENT_SETTING_BLOCK);
36}
37
38bool IsAllowed(ContentSetting setting) {
39  DCHECK(IsValidSetting(setting));
40  return (setting == CONTENT_SETTING_ALLOW ||
41          setting == CONTENT_SETTING_SESSION_ONLY);
42}
43
44}  // namespace
45
46// static
47scoped_refptr<CookieSettings> CookieSettings::Factory::GetForProfile(
48    Profile* profile) {
49  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
50  return static_cast<CookieSettings*>(
51      GetInstance()->GetServiceForBrowserContext(profile, true).get());
52}
53
54// static
55CookieSettings::Factory* CookieSettings::Factory::GetInstance() {
56  return Singleton<CookieSettings::Factory>::get();
57}
58
59CookieSettings::Factory::Factory()
60    : RefcountedBrowserContextKeyedServiceFactory(
61        "CookieSettings",
62        BrowserContextDependencyManager::GetInstance()) {
63}
64
65CookieSettings::Factory::~Factory() {}
66
67void CookieSettings::Factory::RegisterProfilePrefs(
68    user_prefs::PrefRegistrySyncable* registry) {
69  registry->RegisterBooleanPref(
70      prefs::kBlockThirdPartyCookies,
71      false,
72      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
73}
74
75content::BrowserContext* CookieSettings::Factory::GetBrowserContextToUse(
76    content::BrowserContext* context) const {
77  return chrome::GetBrowserContextRedirectedInIncognito(context);
78}
79
80scoped_refptr<RefcountedBrowserContextKeyedService>
81CookieSettings::Factory::BuildServiceInstanceFor(
82    content::BrowserContext* context) const {
83  Profile* profile = static_cast<Profile*>(context);
84  return new CookieSettings(profile->GetHostContentSettingsMap(),
85                            profile->GetPrefs());
86}
87
88CookieSettings::CookieSettings(
89    HostContentSettingsMap* host_content_settings_map,
90    PrefService* prefs)
91    : host_content_settings_map_(host_content_settings_map),
92      block_third_party_cookies_(
93          prefs->GetBoolean(prefs::kBlockThirdPartyCookies)) {
94  if (block_third_party_cookies_) {
95    content::RecordAction(
96        UserMetricsAction("ThirdPartyCookieBlockingEnabled"));
97  } else {
98    content::RecordAction(
99        UserMetricsAction("ThirdPartyCookieBlockingDisabled"));
100  }
101
102  pref_change_registrar_.Init(prefs);
103  pref_change_registrar_.Add(
104      prefs::kBlockThirdPartyCookies,
105      base::Bind(&CookieSettings::OnBlockThirdPartyCookiesChanged,
106                 base::Unretained(this)));
107}
108
109ContentSetting
110CookieSettings::GetDefaultCookieSetting(std::string* provider_id) const {
111  return host_content_settings_map_->GetDefaultContentSetting(
112      CONTENT_SETTINGS_TYPE_COOKIES, provider_id);
113}
114
115bool CookieSettings::IsReadingCookieAllowed(const GURL& url,
116                                            const GURL& first_party_url) const {
117  ContentSetting setting = GetCookieSetting(url, first_party_url, false, NULL);
118  return IsAllowed(setting);
119}
120
121bool CookieSettings::IsSettingCookieAllowed(const GURL& url,
122                                            const GURL& first_party_url) const {
123  ContentSetting setting = GetCookieSetting(url, first_party_url, true, NULL);
124  return IsAllowed(setting);
125}
126
127bool CookieSettings::IsCookieSessionOnly(const GURL& origin) const {
128  ContentSetting setting = GetCookieSetting(origin, origin, true, NULL);
129  DCHECK(IsValidSetting(setting));
130  return (setting == CONTENT_SETTING_SESSION_ONLY);
131}
132
133void CookieSettings::GetCookieSettings(
134    ContentSettingsForOneType* settings) const {
135  return host_content_settings_map_->GetSettingsForOneType(
136      CONTENT_SETTINGS_TYPE_COOKIES, std::string(), settings);
137}
138
139void CookieSettings::SetDefaultCookieSetting(ContentSetting setting) {
140  DCHECK(IsValidSetting(setting));
141  host_content_settings_map_->SetDefaultContentSetting(
142      CONTENT_SETTINGS_TYPE_COOKIES, setting);
143}
144
145void CookieSettings::SetCookieSetting(
146    const ContentSettingsPattern& primary_pattern,
147    const ContentSettingsPattern& secondary_pattern,
148    ContentSetting setting) {
149  DCHECK(IsValidSetting(setting));
150  if (setting == CONTENT_SETTING_SESSION_ONLY) {
151    DCHECK(secondary_pattern == ContentSettingsPattern::Wildcard());
152  }
153  host_content_settings_map_->SetContentSetting(primary_pattern,
154                                                secondary_pattern,
155                                                CONTENT_SETTINGS_TYPE_COOKIES,
156                                                std::string(),
157                                                setting);
158}
159
160void CookieSettings::ResetCookieSetting(
161    const ContentSettingsPattern& primary_pattern,
162    const ContentSettingsPattern& secondary_pattern) {
163  host_content_settings_map_->SetContentSetting(primary_pattern,
164                                                secondary_pattern,
165                                                CONTENT_SETTINGS_TYPE_COOKIES,
166                                                std::string(),
167                                                CONTENT_SETTING_DEFAULT);
168}
169
170void CookieSettings::ShutdownOnUIThread() {
171  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172  pref_change_registrar_.RemoveAll();
173}
174
175ContentSetting CookieSettings::GetCookieSetting(
176    const GURL& url,
177    const GURL& first_party_url,
178    bool setting_cookie,
179    content_settings::SettingSource* source) const {
180  if (HostContentSettingsMap::ShouldAllowAllContent(
181        url, first_party_url, CONTENT_SETTINGS_TYPE_COOKIES))
182    return CONTENT_SETTING_ALLOW;
183
184  // First get any host-specific settings.
185  content_settings::SettingInfo info;
186  scoped_ptr<base::Value> value = host_content_settings_map_->GetWebsiteSetting(
187      url,
188      first_party_url,
189      CONTENT_SETTINGS_TYPE_COOKIES,
190      std::string(),
191      &info);
192  if (source)
193    *source = info.source;
194
195  // If no explicit exception has been made and third-party cookies are blocked
196  // by default, apply that rule.
197  if (info.primary_pattern.MatchesAllHosts() &&
198      info.secondary_pattern.MatchesAllHosts() &&
199      ShouldBlockThirdPartyCookies() &&
200      !first_party_url.SchemeIs(extensions::kExtensionScheme)) {
201    net::StaticCookiePolicy policy(
202        net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
203    int rv;
204    if (setting_cookie)
205      rv = policy.CanSetCookie(url, first_party_url);
206    else
207      rv = policy.CanGetCookies(url, first_party_url);
208    DCHECK_NE(net::ERR_IO_PENDING, rv);
209    if (rv != net::OK)
210      return CONTENT_SETTING_BLOCK;
211  }
212
213  // We should always have a value, at least from the default provider.
214  DCHECK(value.get());
215  return content_settings::ValueToContentSetting(value.get());
216}
217
218CookieSettings::~CookieSettings() {}
219
220void CookieSettings::OnBlockThirdPartyCookiesChanged() {
221  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222
223  base::AutoLock auto_lock(lock_);
224  block_third_party_cookies_ = pref_change_registrar_.prefs()->GetBoolean(
225      prefs::kBlockThirdPartyCookies);
226}
227
228bool CookieSettings::ShouldBlockThirdPartyCookies() const {
229  base::AutoLock auto_lock(lock_);
230  return block_third_party_cookies_;
231}
232