普通文本  |  256行  |  10.52 KB

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