Home | History | Annotate | Download | only in content_settings
      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