| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Implementation of the geolocation content settings map. Styled on |
| // HostContentSettingsMap however unlike that class, this one does not hold |
| // an additional in-memory copy of the settings as it does not need to support |
| // thread safe synchronous access to the settings; all geolocation permissions |
| // are read and written in the UI thread. (If in future this is no longer the |
| // case, refer to http://codereview.chromium.org/1525018 for a previous version |
| // with caching. Note that as we must observe the prefs store for settings |
| // changes, e.g. coming from the sync engine, the simplest design would be to |
| // always write-through changes straight to the prefs store, and rely on the |
| // notification observer to subsequently update any cached copy). |
| |
| #include "chrome/browser/geolocation/geolocation_content_settings_map.h" |
| |
| #include <string> |
| |
| #include "base/string_piece.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/content_settings/content_settings_details.h" |
| #include "chrome/browser/content_settings/content_settings_pattern.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/prefs/scoped_user_pref_update.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/common/notification_service.h" |
| #include "content/common/notification_source.h" |
| #include "content/common/notification_type.h" |
| #include "net/base/dns_util.h" |
| #include "net/base/static_cookie_policy.h" |
| |
| // static |
| const ContentSetting |
| GeolocationContentSettingsMap::kDefaultSetting = CONTENT_SETTING_ASK; |
| |
| GeolocationContentSettingsMap::GeolocationContentSettingsMap(Profile* profile) |
| : profile_(profile) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| prefs_registrar_.Init(profile_->GetPrefs()); |
| prefs_registrar_.Add(prefs::kGeolocationDefaultContentSetting, this); |
| prefs_registrar_.Add(prefs::kGeolocationContentSettings, this); |
| notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED, |
| Source<Profile>(profile_)); |
| } |
| |
| // static |
| void GeolocationContentSettingsMap::RegisterUserPrefs(PrefService* prefs) { |
| prefs->RegisterIntegerPref(prefs::kGeolocationDefaultContentSetting, |
| CONTENT_SETTING_ASK); |
| prefs->RegisterDictionaryPref(prefs::kGeolocationContentSettings); |
| } |
| |
| ContentSetting GeolocationContentSettingsMap::GetDefaultContentSetting() const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| // If the profile is destroyed (and set to NULL) return CONTENT_SETTING_BLOCK. |
| if (!profile_) |
| return CONTENT_SETTING_BLOCK; |
| const PrefService* prefs = profile_->GetPrefs(); |
| const ContentSetting default_content_setting = IntToContentSetting( |
| prefs->GetInteger(prefs::kGeolocationDefaultContentSetting)); |
| return default_content_setting == CONTENT_SETTING_DEFAULT ? |
| kDefaultSetting : default_content_setting; |
| } |
| |
| bool GeolocationContentSettingsMap::IsDefaultContentSettingManaged() const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| // If the profile is destroyed (and set to NULL) return true. |
| if (!profile_) |
| return true; |
| return profile_->GetPrefs()->IsManagedPreference( |
| prefs::kGeolocationDefaultContentSetting); |
| } |
| |
| ContentSetting GeolocationContentSettingsMap::GetContentSetting( |
| const GURL& requesting_url, |
| const GURL& embedding_url) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(requesting_url.is_valid() && embedding_url.is_valid()); |
| GURL requesting_origin(requesting_url.GetOrigin()); |
| GURL embedding_origin(embedding_url.GetOrigin()); |
| DCHECK(requesting_origin.is_valid() && embedding_origin.is_valid()); |
| // If the profile is destroyed (and set to NULL) return CONTENT_SETTING_BLOCK. |
| if (!profile_) |
| return CONTENT_SETTING_BLOCK; |
| const DictionaryValue* all_settings_dictionary = |
| profile_->GetPrefs()->GetDictionary(prefs::kGeolocationContentSettings); |
| // Careful: The returned value could be NULL if the pref has never been set. |
| if (all_settings_dictionary != NULL) { |
| DictionaryValue* requesting_origin_settings; |
| if (all_settings_dictionary->GetDictionaryWithoutPathExpansion( |
| requesting_origin.spec(), &requesting_origin_settings)) { |
| int setting; |
| if (requesting_origin_settings->GetIntegerWithoutPathExpansion( |
| embedding_origin.spec(), &setting)) |
| return IntToContentSetting(setting); |
| // Check for any-embedder setting |
| if (requesting_origin != embedding_origin && |
| requesting_origin_settings->GetIntegerWithoutPathExpansion( |
| "", &setting)) |
| return IntToContentSetting(setting); |
| } |
| } |
| return GetDefaultContentSetting(); |
| } |
| |
| GeolocationContentSettingsMap::AllOriginsSettings |
| GeolocationContentSettingsMap::GetAllOriginsSettings() const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| AllOriginsSettings content_settings; |
| const DictionaryValue* all_settings_dictionary = |
| profile_->GetPrefs()->GetDictionary(prefs::kGeolocationContentSettings); |
| // Careful: The returned value could be NULL if the pref has never been set. |
| if (all_settings_dictionary != NULL) { |
| for (DictionaryValue::key_iterator i(all_settings_dictionary->begin_keys()); |
| i != all_settings_dictionary->end_keys(); ++i) { |
| const std::string& origin(*i); |
| GURL origin_as_url(origin); |
| if (!origin_as_url.is_valid()) |
| continue; |
| DictionaryValue* requesting_origin_settings_dictionary = NULL; |
| bool found = all_settings_dictionary->GetDictionaryWithoutPathExpansion( |
| origin, &requesting_origin_settings_dictionary); |
| DCHECK(found); |
| if (!requesting_origin_settings_dictionary) |
| continue; |
| GetOneOriginSettingsFromDictionary( |
| requesting_origin_settings_dictionary, |
| &content_settings[origin_as_url]); |
| } |
| } |
| return content_settings; |
| } |
| |
| void GeolocationContentSettingsMap::SetDefaultContentSetting( |
| ContentSetting setting) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (!profile_) |
| return; |
| profile_->GetPrefs()->SetInteger(prefs::kGeolocationDefaultContentSetting, |
| setting == CONTENT_SETTING_DEFAULT ? |
| kDefaultSetting : setting); |
| } |
| |
| void GeolocationContentSettingsMap::SetContentSetting( |
| const GURL& requesting_url, |
| const GURL& embedding_url, |
| ContentSetting setting) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(requesting_url.is_valid()); |
| DCHECK(embedding_url.is_valid() || embedding_url.is_empty()); |
| GURL requesting_origin(requesting_url.GetOrigin()); |
| GURL embedding_origin(embedding_url.GetOrigin()); |
| DCHECK(requesting_origin.is_valid()); |
| DCHECK(embedding_origin.is_valid() || embedding_url.is_empty()); |
| if (!profile_) |
| return; |
| PrefService* prefs = profile_->GetPrefs(); |
| |
| DictionaryPrefUpdate update(prefs, prefs::kGeolocationContentSettings); |
| DictionaryValue* all_settings_dictionary = update.Get(); |
| DictionaryValue* requesting_origin_settings_dictionary = NULL; |
| all_settings_dictionary->GetDictionaryWithoutPathExpansion( |
| requesting_origin.spec(), &requesting_origin_settings_dictionary); |
| if (setting == CONTENT_SETTING_DEFAULT) { |
| if (requesting_origin_settings_dictionary) { |
| requesting_origin_settings_dictionary->RemoveWithoutPathExpansion( |
| embedding_origin.spec(), NULL); |
| if (requesting_origin_settings_dictionary->empty()) |
| all_settings_dictionary->RemoveWithoutPathExpansion( |
| requesting_origin.spec(), NULL); |
| } |
| } else { |
| if (!requesting_origin_settings_dictionary) { |
| requesting_origin_settings_dictionary = new DictionaryValue; |
| all_settings_dictionary->SetWithoutPathExpansion( |
| requesting_origin.spec(), requesting_origin_settings_dictionary); |
| } |
| DCHECK(requesting_origin_settings_dictionary); |
| requesting_origin_settings_dictionary->SetWithoutPathExpansion( |
| embedding_origin.spec(), Value::CreateIntegerValue(setting)); |
| } |
| } |
| |
| void GeolocationContentSettingsMap::ResetToDefault() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (!profile_) |
| return; |
| PrefService* prefs = profile_->GetPrefs(); |
| prefs->ClearPref(prefs::kGeolocationDefaultContentSetting); |
| prefs->ClearPref(prefs::kGeolocationContentSettings); |
| } |
| |
| void GeolocationContentSettingsMap::NotifyObservers( |
| const ContentSettingsDetails& details) { |
| NotificationService::current()->Notify( |
| NotificationType::GEOLOCATION_SETTINGS_CHANGED, |
| Source<GeolocationContentSettingsMap>(this), |
| Details<const ContentSettingsDetails>(&details)); |
| } |
| |
| void GeolocationContentSettingsMap::Observe( |
| NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| if (type == NotificationType::PREF_CHANGED) { |
| const std::string& name = *Details<std::string>(details).ptr(); |
| if (name == prefs::kGeolocationDefaultContentSetting) { |
| NotifyObservers(ContentSettingsDetails( |
| ContentSettingsPattern(), |
| CONTENT_SETTINGS_TYPE_DEFAULT, |
| "")); |
| } |
| } else if (NotificationType::PROFILE_DESTROYED == type) { |
| UnregisterObservers(); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void GeolocationContentSettingsMap::UnregisterObservers() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| if (!profile_) |
| return; |
| prefs_registrar_.RemoveAll(); |
| notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED, |
| Source<Profile>(profile_)); |
| profile_ = NULL; |
| } |
| |
| GeolocationContentSettingsMap::~GeolocationContentSettingsMap() { |
| UnregisterObservers(); |
| } |
| |
| // static |
| void GeolocationContentSettingsMap::GetOneOriginSettingsFromDictionary( |
| const DictionaryValue* dictionary, |
| OneOriginSettings* one_origin_settings) { |
| for (DictionaryValue::key_iterator i(dictionary->begin_keys()); |
| i != dictionary->end_keys(); ++i) { |
| const std::string& target(*i); |
| int setting = kDefaultSetting; |
| bool found = dictionary->GetIntegerWithoutPathExpansion(target, &setting); |
| DCHECK(found); |
| GURL target_url(target); |
| // An empty URL has a special meaning (wildcard), so only accept invalid |
| // URLs if the original version was empty (avoids treating corrupted prefs |
| // as the wildcard entry; see http://crbug.com/39685) |
| if (target_url.is_valid() || target.empty()) |
| (*one_origin_settings)[target_url] = IntToContentSetting(setting); |
| } |
| } |