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 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 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 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 81 NotificationProvider::NotificationProvider( 82 Profile* profile) 83 : profile_(profile) { 84 prefs_registrar_.Init(profile_->GetPrefs()); 85 StartObserving(); 86 } 87 88 NotificationProvider::~NotificationProvider() { 89 StopObserving(); 90 } 91 92 bool NotificationProvider::ContentSettingsTypeIsManaged( 93 ContentSettingsType content_type) { 94 return false; 95 } 96 97 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 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 136 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 168 void NotificationProvider::ClearAllContentSettingsRules( 169 ContentSettingsType content_type) { 170 if (content_type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) 171 ResetAllOrigins(); 172 } 173 174 void NotificationProvider::ResetToDefaults() { 175 ResetAllOrigins(); 176 } 177 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 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 208 void NotificationProvider::StopObserving() { 209 if (!profile_->IsOffTheRecord()) { 210 prefs_registrar_.RemoveAll(); 211 } 212 notification_registrar_.RemoveAll(); 213 } 214 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 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 235 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 248 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 261 void NotificationProvider::GrantPermission(const GURL& origin) { 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 263 PersistPermissionChange(origin, true); 264 NotifySettingsChange(); 265 } 266 267 void NotificationProvider::DenyPermission(const GURL& origin) { 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 269 PersistPermissionChange(origin, false); 270 NotifySettingsChange(); 271 } 272 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 326 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 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 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 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