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 37const ContentSetting 38 GeolocationContentSettingsMap::kDefaultSetting = CONTENT_SETTING_ASK; 39 40GeolocationContentSettingsMap::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 51void GeolocationContentSettingsMap::RegisterUserPrefs(PrefService* prefs) { 52 prefs->RegisterIntegerPref(prefs::kGeolocationDefaultContentSetting, 53 CONTENT_SETTING_ASK); 54 prefs->RegisterDictionaryPref(prefs::kGeolocationContentSettings); 55} 56 57ContentSetting 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 69bool 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 78ContentSetting 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 110GeolocationContentSettingsMap::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 138void 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 148void 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 188void 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 197void 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 205void 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 224void 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 234GeolocationContentSettingsMap::~GeolocationContentSettingsMap() { 235 UnregisterObservers(); 236} 237 238// static 239void 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