Home | History | Annotate | Download | only in geolocation
      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 // Implementation of the geolocation content settings map. Styled on
      6 // HostContentSettingsMap however unlike that class, this one does not hold
      7 // an additional in-memory copy of the settings as it does not need to support
      8 // thread safe synchronous access to the settings; all geolocation permissions
      9 // are read and written in the UI thread. (If in future this is no longer the
     10 // case, refer to http://codereview.chromium.org/1525018 for a previous version
     11 // with caching. Note that as we must observe the prefs store for settings
     12 // changes, e.g. coming from the sync engine, the simplest design would be to
     13 // always write-through changes straight to the prefs store, and rely on the
     14 // notification observer to subsequently update any cached copy).
     15 
     16 #include "chrome/browser/geolocation/geolocation_content_settings_map.h"
     17 
     18 #include <string>
     19 
     20 #include "base/string_piece.h"
     21 #include "base/utf_string_conversions.h"
     22 #include "chrome/browser/content_settings/content_settings_details.h"
     23 #include "chrome/browser/content_settings/content_settings_pattern.h"
     24 #include "chrome/browser/prefs/pref_service.h"
     25 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     26 #include "chrome/browser/profiles/profile.h"
     27 #include "chrome/common/pref_names.h"
     28 #include "chrome/common/url_constants.h"
     29 #include "content/browser/browser_thread.h"
     30 #include "content/common/notification_service.h"
     31 #include "content/common/notification_source.h"
     32 #include "content/common/notification_type.h"
     33 #include "net/base/dns_util.h"
     34 #include "net/base/static_cookie_policy.h"
     35 
     36 // static
     37 const ContentSetting
     38     GeolocationContentSettingsMap::kDefaultSetting = CONTENT_SETTING_ASK;
     39 
     40 GeolocationContentSettingsMap::GeolocationContentSettingsMap(Profile* profile)
     41     : profile_(profile) {
     42   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     43   prefs_registrar_.Init(profile_->GetPrefs());
     44   prefs_registrar_.Add(prefs::kGeolocationDefaultContentSetting, this);
     45   prefs_registrar_.Add(prefs::kGeolocationContentSettings, this);
     46   notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
     47                               Source<Profile>(profile_));
     48 }
     49 
     50 // static
     51 void GeolocationContentSettingsMap::RegisterUserPrefs(PrefService* prefs) {
     52   prefs->RegisterIntegerPref(prefs::kGeolocationDefaultContentSetting,
     53                              CONTENT_SETTING_ASK);
     54   prefs->RegisterDictionaryPref(prefs::kGeolocationContentSettings);
     55 }
     56 
     57 ContentSetting GeolocationContentSettingsMap::GetDefaultContentSetting() const {
     58   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     59   // If the profile is destroyed (and set to NULL) return CONTENT_SETTING_BLOCK.
     60   if (!profile_)
     61     return CONTENT_SETTING_BLOCK;
     62   const PrefService* prefs = profile_->GetPrefs();
     63   const ContentSetting default_content_setting = IntToContentSetting(
     64       prefs->GetInteger(prefs::kGeolocationDefaultContentSetting));
     65   return default_content_setting == CONTENT_SETTING_DEFAULT ?
     66          kDefaultSetting : default_content_setting;
     67 }
     68 
     69 bool GeolocationContentSettingsMap::IsDefaultContentSettingManaged() const {
     70   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     71   // If the profile is destroyed (and set to NULL) return true.
     72   if (!profile_)
     73     return true;
     74   return profile_->GetPrefs()->IsManagedPreference(
     75       prefs::kGeolocationDefaultContentSetting);
     76 }
     77 
     78 ContentSetting GeolocationContentSettingsMap::GetContentSetting(
     79     const GURL& requesting_url,
     80     const GURL& embedding_url) const {
     81   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     82   DCHECK(requesting_url.is_valid() && embedding_url.is_valid());
     83   GURL requesting_origin(requesting_url.GetOrigin());
     84   GURL embedding_origin(embedding_url.GetOrigin());
     85   DCHECK(requesting_origin.is_valid() && embedding_origin.is_valid());
     86   // If the profile is destroyed (and set to NULL) return CONTENT_SETTING_BLOCK.
     87   if (!profile_)
     88     return CONTENT_SETTING_BLOCK;
     89   const DictionaryValue* all_settings_dictionary =
     90       profile_->GetPrefs()->GetDictionary(prefs::kGeolocationContentSettings);
     91   // Careful: The returned value could be NULL if the pref has never been set.
     92   if (all_settings_dictionary != NULL) {
     93     DictionaryValue* requesting_origin_settings;
     94     if (all_settings_dictionary->GetDictionaryWithoutPathExpansion(
     95         requesting_origin.spec(), &requesting_origin_settings)) {
     96       int setting;
     97       if (requesting_origin_settings->GetIntegerWithoutPathExpansion(
     98           embedding_origin.spec(), &setting))
     99         return IntToContentSetting(setting);
    100       // Check for any-embedder setting
    101       if (requesting_origin != embedding_origin &&
    102           requesting_origin_settings->GetIntegerWithoutPathExpansion(
    103           "", &setting))
    104         return IntToContentSetting(setting);
    105     }
    106   }
    107   return GetDefaultContentSetting();
    108 }
    109 
    110 GeolocationContentSettingsMap::AllOriginsSettings
    111     GeolocationContentSettingsMap::GetAllOriginsSettings() const {
    112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    113   AllOriginsSettings content_settings;
    114   const DictionaryValue* all_settings_dictionary =
    115       profile_->GetPrefs()->GetDictionary(prefs::kGeolocationContentSettings);
    116   // Careful: The returned value could be NULL if the pref has never been set.
    117   if (all_settings_dictionary != NULL) {
    118     for (DictionaryValue::key_iterator i(all_settings_dictionary->begin_keys());
    119          i != all_settings_dictionary->end_keys(); ++i) {
    120       const std::string& origin(*i);
    121       GURL origin_as_url(origin);
    122       if (!origin_as_url.is_valid())
    123         continue;
    124       DictionaryValue* requesting_origin_settings_dictionary = NULL;
    125       bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    126           origin, &requesting_origin_settings_dictionary);
    127       DCHECK(found);
    128       if (!requesting_origin_settings_dictionary)
    129         continue;
    130       GetOneOriginSettingsFromDictionary(
    131           requesting_origin_settings_dictionary,
    132           &content_settings[origin_as_url]);
    133     }
    134   }
    135   return content_settings;
    136 }
    137 
    138 void GeolocationContentSettingsMap::SetDefaultContentSetting(
    139     ContentSetting setting) {
    140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    141   if (!profile_)
    142     return;
    143   profile_->GetPrefs()->SetInteger(prefs::kGeolocationDefaultContentSetting,
    144                                    setting == CONTENT_SETTING_DEFAULT ?
    145                                        kDefaultSetting : setting);
    146 }
    147 
    148 void GeolocationContentSettingsMap::SetContentSetting(
    149     const GURL& requesting_url,
    150     const GURL& embedding_url,
    151     ContentSetting setting) {
    152   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    153   DCHECK(requesting_url.is_valid());
    154   DCHECK(embedding_url.is_valid() || embedding_url.is_empty());
    155   GURL requesting_origin(requesting_url.GetOrigin());
    156   GURL embedding_origin(embedding_url.GetOrigin());
    157   DCHECK(requesting_origin.is_valid());
    158   DCHECK(embedding_origin.is_valid() || embedding_url.is_empty());
    159   if (!profile_)
    160     return;
    161   PrefService* prefs = profile_->GetPrefs();
    162 
    163   DictionaryPrefUpdate update(prefs, prefs::kGeolocationContentSettings);
    164   DictionaryValue* all_settings_dictionary = update.Get();
    165   DictionaryValue* requesting_origin_settings_dictionary = NULL;
    166   all_settings_dictionary->GetDictionaryWithoutPathExpansion(
    167       requesting_origin.spec(), &requesting_origin_settings_dictionary);
    168   if (setting == CONTENT_SETTING_DEFAULT) {
    169     if (requesting_origin_settings_dictionary) {
    170       requesting_origin_settings_dictionary->RemoveWithoutPathExpansion(
    171           embedding_origin.spec(), NULL);
    172       if (requesting_origin_settings_dictionary->empty())
    173         all_settings_dictionary->RemoveWithoutPathExpansion(
    174             requesting_origin.spec(), NULL);
    175     }
    176   } else {
    177     if (!requesting_origin_settings_dictionary) {
    178       requesting_origin_settings_dictionary = new DictionaryValue;
    179       all_settings_dictionary->SetWithoutPathExpansion(
    180           requesting_origin.spec(), requesting_origin_settings_dictionary);
    181     }
    182     DCHECK(requesting_origin_settings_dictionary);
    183     requesting_origin_settings_dictionary->SetWithoutPathExpansion(
    184         embedding_origin.spec(), Value::CreateIntegerValue(setting));
    185   }
    186 }
    187 
    188 void GeolocationContentSettingsMap::ResetToDefault() {
    189   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    190   if (!profile_)
    191     return;
    192   PrefService* prefs = profile_->GetPrefs();
    193   prefs->ClearPref(prefs::kGeolocationDefaultContentSetting);
    194   prefs->ClearPref(prefs::kGeolocationContentSettings);
    195 }
    196 
    197 void GeolocationContentSettingsMap::NotifyObservers(
    198     const ContentSettingsDetails& details) {
    199   NotificationService::current()->Notify(
    200       NotificationType::GEOLOCATION_SETTINGS_CHANGED,
    201       Source<GeolocationContentSettingsMap>(this),
    202       Details<const ContentSettingsDetails>(&details));
    203 }
    204 
    205 void GeolocationContentSettingsMap::Observe(
    206     NotificationType type,
    207     const NotificationSource& source,
    208     const NotificationDetails& details) {
    209   if (type == NotificationType::PREF_CHANGED) {
    210     const std::string& name = *Details<std::string>(details).ptr();
    211     if (name == prefs::kGeolocationDefaultContentSetting) {
    212       NotifyObservers(ContentSettingsDetails(
    213       ContentSettingsPattern(),
    214       CONTENT_SETTINGS_TYPE_DEFAULT,
    215       ""));
    216     }
    217   } else if (NotificationType::PROFILE_DESTROYED == type) {
    218     UnregisterObservers();
    219   } else {
    220     NOTREACHED();
    221   }
    222 }
    223 
    224 void GeolocationContentSettingsMap::UnregisterObservers() {
    225   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    226   if (!profile_)
    227     return;
    228   prefs_registrar_.RemoveAll();
    229   notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED,
    230                                  Source<Profile>(profile_));
    231   profile_ = NULL;
    232 }
    233 
    234 GeolocationContentSettingsMap::~GeolocationContentSettingsMap() {
    235   UnregisterObservers();
    236 }
    237 
    238 // static
    239 void GeolocationContentSettingsMap::GetOneOriginSettingsFromDictionary(
    240     const DictionaryValue* dictionary,
    241     OneOriginSettings* one_origin_settings) {
    242   for (DictionaryValue::key_iterator i(dictionary->begin_keys());
    243        i != dictionary->end_keys(); ++i) {
    244     const std::string& target(*i);
    245     int setting = kDefaultSetting;
    246     bool found = dictionary->GetIntegerWithoutPathExpansion(target, &setting);
    247     DCHECK(found);
    248     GURL target_url(target);
    249     // An empty URL has a special meaning (wildcard), so only accept invalid
    250     // URLs if the original version was empty (avoids treating corrupted prefs
    251     // as the wildcard entry; see http://crbug.com/39685)
    252     if (target_url.is_valid() || target.empty())
    253       (*one_origin_settings)[target_url] = IntToContentSetting(setting);
    254   }
    255 }
    256