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
6 #include "chrome/browser/content_settings/content_settings_notification_provider.h"
7
8 #include "base/string_util.h"
9 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
10 #include "chrome/browser/notifications/notification.h"
11 #include "chrome/browser/notifications/notifications_prefs_cache.h"
12 #include "chrome/browser/notifications/notification_ui_manager.h"
13 #include "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/prefs/scoped_user_pref_update.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/content_settings_types.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/common/url_constants.h"
19 #include "content/common/notification_service.h"
20 #include "content/common/notification_type.h"
21 #include "googleurl/src/gurl.h"
22
23 namespace {
24
25 const ContentSetting kDefaultSetting = CONTENT_SETTING_ASK;
26
27 } // namespace
28
29 namespace content_settings {
30
31 // ////////////////////////////////////////////////////////////////////////////
32 // NotificationProvider
33 //
34
35 // static
RegisterUserPrefs(PrefService * user_prefs)36 void NotificationProvider::RegisterUserPrefs(PrefService* user_prefs) {
37 if (!user_prefs->FindPreference(prefs::kDesktopNotificationAllowedOrigins))
38 user_prefs->RegisterListPref(prefs::kDesktopNotificationAllowedOrigins);
39 if (!user_prefs->FindPreference(prefs::kDesktopNotificationDeniedOrigins))
40 user_prefs->RegisterListPref(prefs::kDesktopNotificationDeniedOrigins);
41 }
42
43 // TODO(markusheintz): Re-factoring in progress. Do not move or touch the
44 // following two static methods as you might cause trouble. Thanks!
45
46 // static
ToContentSettingsPattern(const GURL & origin)47 ContentSettingsPattern NotificationProvider::ToContentSettingsPattern(
48 const GURL& origin) {
49 // Fix empty GURLs.
50 if (origin.spec().empty()) {
51 std::string pattern_spec(chrome::kFileScheme);
52 pattern_spec += chrome::kStandardSchemeSeparator;
53 return ContentSettingsPattern(pattern_spec);
54 }
55 return ContentSettingsPattern::FromURLNoWildcard(origin);
56 }
57
58 // static
ToGURL(const ContentSettingsPattern & pattern)59 GURL NotificationProvider::ToGURL(const ContentSettingsPattern& pattern) {
60 std::string pattern_spec(pattern.AsString());
61
62 if (pattern_spec.empty() ||
63 StartsWithASCII(pattern_spec,
64 std::string(ContentSettingsPattern::kDomainWildcard),
65 true)) {
66 NOTREACHED();
67 }
68
69 std::string url_spec("");
70 if (StartsWithASCII(pattern_spec, std::string(chrome::kFileScheme), false)) {
71 url_spec += pattern_spec;
72 } else if (!pattern.scheme().empty()) {
73 url_spec += pattern.scheme();
74 url_spec += chrome::kStandardSchemeSeparator;
75 url_spec += pattern_spec;
76 }
77
78 return GURL(url_spec);
79 }
80
NotificationProvider(Profile * profile)81 NotificationProvider::NotificationProvider(
82 Profile* profile)
83 : profile_(profile) {
84 prefs_registrar_.Init(profile_->GetPrefs());
85 StartObserving();
86 }
87
~NotificationProvider()88 NotificationProvider::~NotificationProvider() {
89 StopObserving();
90 }
91
ContentSettingsTypeIsManaged(ContentSettingsType content_type)92 bool NotificationProvider::ContentSettingsTypeIsManaged(
93 ContentSettingsType content_type) {
94 return false;
95 }
96
GetContentSetting(const GURL & requesting_url,const GURL & embedding_url,ContentSettingsType content_type,const ResourceIdentifier & resource_identifier) const97 ContentSetting NotificationProvider::GetContentSetting(
98 const GURL& requesting_url,
99 const GURL& embedding_url,
100 ContentSettingsType content_type,
101 const ResourceIdentifier& resource_identifier) const {
102 if (content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS)
103 return CONTENT_SETTING_DEFAULT;
104
105 return GetContentSetting(requesting_url);
106 }
107
SetContentSetting(const ContentSettingsPattern & requesting_url_pattern,const ContentSettingsPattern & embedding_url_pattern,ContentSettingsType content_type,const ResourceIdentifier & resource_identifier,ContentSetting content_setting)108 void NotificationProvider::SetContentSetting(
109 const ContentSettingsPattern& requesting_url_pattern,
110 const ContentSettingsPattern& embedding_url_pattern,
111 ContentSettingsType content_type,
112 const ResourceIdentifier& resource_identifier,
113 ContentSetting content_setting) {
114 if (content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS)
115 return;
116
117 GURL origin = ToGURL(requesting_url_pattern);
118 if (CONTENT_SETTING_ALLOW == content_setting) {
119 GrantPermission(origin);
120 } else if (CONTENT_SETTING_BLOCK == content_setting) {
121 DenyPermission(origin);
122 } else if (CONTENT_SETTING_DEFAULT == content_setting) {
123 ContentSetting current_setting = GetContentSetting(origin);
124 if (CONTENT_SETTING_ALLOW == current_setting) {
125 ResetAllowedOrigin(origin);
126 } else if (CONTENT_SETTING_BLOCK == current_setting) {
127 ResetBlockedOrigin(origin);
128 } else {
129 NOTREACHED();
130 }
131 } else {
132 NOTREACHED();
133 }
134 }
135
GetAllContentSettingsRules(ContentSettingsType content_type,const ResourceIdentifier & resource_identifier,Rules * content_setting_rules) const136 void NotificationProvider::GetAllContentSettingsRules(
137 ContentSettingsType content_type,
138 const ResourceIdentifier& resource_identifier,
139 Rules* content_setting_rules) const {
140 if (content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS)
141 return;
142
143 std::vector<GURL> allowed_origins = GetAllowedOrigins();
144 std::vector<GURL> denied_origins = GetBlockedOrigins();
145
146 for (std::vector<GURL>::iterator url = allowed_origins.begin();
147 url != allowed_origins.end();
148 ++url) {
149 ContentSettingsPattern pattern =
150 ContentSettingsPattern::FromURLNoWildcard(*url);
151 content_setting_rules->push_back(Rule(
152 pattern,
153 pattern,
154 CONTENT_SETTING_ALLOW));
155 }
156 for (std::vector<GURL>::iterator url = denied_origins.begin();
157 url != denied_origins.end();
158 ++url) {
159 ContentSettingsPattern pattern =
160 ContentSettingsPattern::FromURLNoWildcard(*url);
161 content_setting_rules->push_back(Rule(
162 pattern,
163 pattern,
164 CONTENT_SETTING_BLOCK));
165 }
166 }
167
ClearAllContentSettingsRules(ContentSettingsType content_type)168 void NotificationProvider::ClearAllContentSettingsRules(
169 ContentSettingsType content_type) {
170 if (content_type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS)
171 ResetAllOrigins();
172 }
173
ResetToDefaults()174 void NotificationProvider::ResetToDefaults() {
175 ResetAllOrigins();
176 }
177
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)178 void NotificationProvider::Observe(NotificationType type,
179 const NotificationSource& source,
180 const NotificationDetails& details) {
181 if (NotificationType::PREF_CHANGED == type) {
182 const std::string& name = *Details<std::string>(details).ptr();
183 OnPrefsChanged(name);
184 } else if (NotificationType::PROFILE_DESTROYED == type) {
185 StopObserving();
186 }
187 }
188
189 /////////////////////////////////////////////////////////////////////
190 // Private
191 //
192
StartObserving()193 void NotificationProvider::StartObserving() {
194 if (!profile_->IsOffTheRecord()) {
195 prefs_registrar_.Add(prefs::kDesktopNotificationDefaultContentSetting,
196 this);
197 prefs_registrar_.Add(prefs::kDesktopNotificationAllowedOrigins, this);
198 prefs_registrar_.Add(prefs::kDesktopNotificationDeniedOrigins, this);
199
200 notification_registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
201 NotificationService::AllSources());
202 }
203
204 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
205 Source<Profile>(profile_));
206 }
207
StopObserving()208 void NotificationProvider::StopObserving() {
209 if (!profile_->IsOffTheRecord()) {
210 prefs_registrar_.RemoveAll();
211 }
212 notification_registrar_.RemoveAll();
213 }
214
OnPrefsChanged(const std::string & pref_name)215 void NotificationProvider::OnPrefsChanged(const std::string& pref_name) {
216 if (pref_name == prefs::kDesktopNotificationAllowedOrigins) {
217 NotifySettingsChange();
218 } else if (pref_name == prefs::kDesktopNotificationDeniedOrigins) {
219 NotifySettingsChange();
220 }
221 }
222
NotifySettingsChange()223 void NotificationProvider::NotifySettingsChange() {
224 // TODO(markusheintz): Re-factoring work in progress: Replace the
225 // DESKTOP_NOTIFICATION_SETTINGS_CHANGED with a CONTENT_SETTINGS_CHANGED
226 // notification, and use the HostContentSettingsMap as source once this
227 // content settings provider in integrated in the HostContentSetttingsMap.
228 NotificationService::current()->Notify(
229 NotificationType::DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
230 Source<DesktopNotificationService>(
231 DesktopNotificationServiceFactory::GetForProfile(profile_)),
232 NotificationService::NoDetails());
233 }
234
GetAllowedOrigins() const235 std::vector<GURL> NotificationProvider::GetAllowedOrigins() const {
236 std::vector<GURL> allowed_origins;
237 PrefService* prefs = profile_->GetPrefs();
238 const ListValue* allowed_sites =
239 prefs->GetList(prefs::kDesktopNotificationAllowedOrigins);
240 if (allowed_sites) {
241 // TODO(markusheintz): Remove dependency to PrefsCache
242 NotificationsPrefsCache::ListValueToGurlVector(*allowed_sites,
243 &allowed_origins);
244 }
245 return allowed_origins;
246 }
247
GetBlockedOrigins() const248 std::vector<GURL> NotificationProvider::GetBlockedOrigins() const {
249 std::vector<GURL> denied_origins;
250 PrefService* prefs = profile_->GetPrefs();
251 const ListValue* denied_sites =
252 prefs->GetList(prefs::kDesktopNotificationDeniedOrigins);
253 if (denied_sites) {
254 // TODO(markusheintz): Remove dependency to PrefsCache
255 NotificationsPrefsCache::ListValueToGurlVector(*denied_sites,
256 &denied_origins);
257 }
258 return denied_origins;
259 }
260
GrantPermission(const GURL & origin)261 void NotificationProvider::GrantPermission(const GURL& origin) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
263 PersistPermissionChange(origin, true);
264 NotifySettingsChange();
265 }
266
DenyPermission(const GURL & origin)267 void NotificationProvider::DenyPermission(const GURL& origin) {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
269 PersistPermissionChange(origin, false);
270 NotifySettingsChange();
271 }
272
PersistPermissionChange(const GURL & origin,bool is_allowed)273 void NotificationProvider::PersistPermissionChange(
274 const GURL& origin, bool is_allowed) {
275 // Don't persist changes when incognito.
276 if (profile_->IsOffTheRecord())
277 return;
278 PrefService* prefs = profile_->GetPrefs();
279
280 // |Observe()| updates the whole permission set in the cache, but only a
281 // single origin has changed. Hence, callers of this method manually
282 // schedule a task to update the prefs cache, and the prefs observer is
283 // disabled while the update runs.
284 StopObserving();
285
286 bool allowed_changed = false;
287 bool denied_changed = false;
288
289 {
290 ListPrefUpdate update_allowed_sites(
291 prefs, prefs::kDesktopNotificationAllowedOrigins);
292 ListPrefUpdate update_denied_sites(
293 prefs, prefs::kDesktopNotificationDeniedOrigins);
294 ListValue* allowed_sites = update_allowed_sites.Get();
295 ListValue* denied_sites = update_denied_sites.Get();
296 // |value| is passed to the preferences list, or deleted.
297 StringValue* value = new StringValue(origin.spec());
298
299 // Remove from one list and add to the other.
300 if (is_allowed) {
301 // Remove from the denied list.
302 if (denied_sites->Remove(*value) != -1)
303 denied_changed = true;
304
305 // Add to the allowed list.
306 if (allowed_sites->AppendIfNotPresent(value))
307 allowed_changed = true;
308 } else {
309 // Remove from the allowed list.
310 if (allowed_sites->Remove(*value) != -1)
311 allowed_changed = true;
312
313 // Add to the denied list.
314 if (denied_sites->AppendIfNotPresent(value))
315 denied_changed = true;
316 }
317 }
318
319 // Persist the pref if anthing changed, but only send updates for the
320 // list that changed.
321 if (allowed_changed || denied_changed)
322 prefs->ScheduleSavePersistentPrefs();
323 StartObserving();
324 }
325
GetContentSetting(const GURL & origin) const326 ContentSetting NotificationProvider::GetContentSetting(
327 const GURL& origin) const {
328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
329
330 if (profile_->IsOffTheRecord())
331 return kDefaultSetting;
332
333 std::vector<GURL> allowed_origins(GetAllowedOrigins());
334 if (std::find(allowed_origins.begin(), allowed_origins.end(), origin) !=
335 allowed_origins.end())
336 return CONTENT_SETTING_ALLOW;
337
338 std::vector<GURL> denied_origins(GetBlockedOrigins());
339 if (std::find(denied_origins.begin(), denied_origins.end(), origin) !=
340 denied_origins.end())
341 return CONTENT_SETTING_BLOCK;
342
343 return CONTENT_SETTING_DEFAULT;
344 }
345
ResetAllowedOrigin(const GURL & origin)346 void NotificationProvider::ResetAllowedOrigin(const GURL& origin) {
347 if (profile_->IsOffTheRecord())
348 return;
349
350 // Since this isn't called often, let the normal observer behavior update the
351 // cache in this case.
352 PrefService* prefs = profile_->GetPrefs();
353 {
354 ListPrefUpdate update(prefs, prefs::kDesktopNotificationAllowedOrigins);
355 ListValue* allowed_sites = update.Get();
356 StringValue value(origin.spec());
357 int removed_index = allowed_sites->Remove(value);
358 DCHECK_NE(-1, removed_index) << origin << " was not allowed";
359 }
360 prefs->ScheduleSavePersistentPrefs();
361 }
362
ResetBlockedOrigin(const GURL & origin)363 void NotificationProvider::ResetBlockedOrigin(const GURL& origin) {
364 if (profile_->IsOffTheRecord())
365 return;
366
367 // Since this isn't called often, let the normal observer behavior update the
368 // cache in this case.
369 PrefService* prefs = profile_->GetPrefs();
370 {
371 ListPrefUpdate update(prefs, prefs::kDesktopNotificationDeniedOrigins);
372 ListValue* denied_sites = update.Get();
373 StringValue value(origin.spec());
374 int removed_index = denied_sites->Remove(value);
375 DCHECK_NE(-1, removed_index) << origin << " was not blocked";
376 }
377 prefs->ScheduleSavePersistentPrefs();
378 }
379
ResetAllOrigins()380 void NotificationProvider::ResetAllOrigins() {
381 PrefService* prefs = profile_->GetPrefs();
382 prefs->ClearPref(prefs::kDesktopNotificationAllowedOrigins);
383 prefs->ClearPref(prefs::kDesktopNotificationDeniedOrigins);
384 }
385
386 } // namespace content_settings
387