// 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); } }