| Index: components/content_settings/core/browser/content_settings_pref.cc
|
| diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
|
| index f32e3018b4cc0828d4b5b7efa7a596d5f022d82b..c2bc342cb9a66a579478199641fdf8103c9377dc 100644
|
| --- a/components/content_settings/core/browser/content_settings_pref.cc
|
| +++ b/components/content_settings/core/browser/content_settings_pref.cc
|
| @@ -9,6 +9,7 @@
|
| #include "base/auto_reset.h"
|
| #include "base/bind.h"
|
| #include "base/metrics/histogram_macros.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_split.h"
|
| #include "components/content_settings/core/browser/content_settings_info.h"
|
| #include "components/content_settings/core/browser/content_settings_registry.h"
|
| @@ -24,6 +25,8 @@
|
| namespace {
|
|
|
| const char kSettingPath[] = "setting";
|
| +const char kLastModifiedPath[] = "last_modified";
|
| +const char kPerResourceLastModifiedPath[] = "per_resource_last_modified";
|
| const char kPerResourceIdentifierPrefName[] = "per_resource";
|
|
|
| // If the given content type supports resource identifiers in user preferences,
|
| @@ -51,6 +54,21 @@ bool IsValueAllowedForType(const base::Value* value, ContentSettingsType type) {
|
| return value->GetType() == base::Value::Type::DICTIONARY;
|
| }
|
|
|
| +// Extract a timestamp from |dictionary[path]|. Will return base::Time() if no
|
| +// timestamp exists or |dictionary| is NULL.
|
| +base::Time GetTimeStamp(const base::DictionaryValue* dictionary,
|
| + const std::string& path) {
|
| + if (!dictionary) {
|
| + return base::Time();
|
| + }
|
| + std::string timestamp_str;
|
| + dictionary->GetStringWithoutPathExpansion(path, ×tamp_str);
|
| + int64_t timestamp = 0;
|
| + base::StringToInt64(timestamp_str, ×tamp);
|
| + base::Time last_modified = base::Time::FromInternalValue(timestamp);
|
| + return last_modified;
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace content_settings {
|
| @@ -61,12 +79,14 @@ ContentSettingsPref::ContentSettingsPref(
|
| PrefChangeRegistrar* registrar,
|
| const std::string& pref_name,
|
| bool incognito,
|
| + bool store_last_modified,
|
| NotifyObserversCallback notify_callback)
|
| : content_type_(content_type),
|
| prefs_(prefs),
|
| registrar_(registrar),
|
| pref_name_(pref_name),
|
| is_incognito_(incognito),
|
| + store_last_modified_(store_last_modified),
|
| updating_preferences_(false),
|
| notify_callback_(notify_callback) {
|
| DCHECK(prefs_);
|
| @@ -111,15 +131,15 @@ bool ContentSettingsPref::SetWebsiteSetting(
|
| if (!is_incognito_)
|
| map_to_modify = &value_map_;
|
|
|
| + base::Time modified_time =
|
| + store_last_modified_ ? base::Time::Now() : base::Time();
|
| +
|
| {
|
| base::AutoLock auto_lock(lock_);
|
| if (value.get()) {
|
| - map_to_modify->SetValue(
|
| - primary_pattern,
|
| - secondary_pattern,
|
| - content_type_,
|
| - resource_identifier,
|
| - value->DeepCopy());
|
| + map_to_modify->SetValue(primary_pattern, secondary_pattern, content_type_,
|
| + resource_identifier, modified_time,
|
| + value->DeepCopy());
|
| } else {
|
| map_to_modify->DeleteValue(
|
| primary_pattern,
|
| @@ -130,10 +150,8 @@ bool ContentSettingsPref::SetWebsiteSetting(
|
| }
|
| // Update the content settings preference.
|
| if (!is_incognito_) {
|
| - UpdatePref(primary_pattern,
|
| - secondary_pattern,
|
| - resource_identifier,
|
| - value.get());
|
| + UpdatePref(primary_pattern, secondary_pattern, resource_identifier,
|
| + modified_time, value.get());
|
| }
|
|
|
| notify_callback_.Run(
|
| @@ -244,19 +262,22 @@ void ContentSettingsPref::ReadContentSettingsFromPref() {
|
| const base::DictionaryValue* resource_dictionary = NULL;
|
| if (settings_dictionary->GetDictionary(
|
| kPerResourceIdentifierPrefName, &resource_dictionary)) {
|
| + const base::DictionaryValue* timestamp_dictionary = NULL;
|
| + settings_dictionary->GetDictionary(kPerResourceLastModifiedPath,
|
| + ×tamp_dictionary);
|
| for (base::DictionaryValue::Iterator j(*resource_dictionary);
|
| !j.IsAtEnd();
|
| j.Advance()) {
|
| const std::string& resource_identifier(j.key());
|
| + base::Time last_modified =
|
| + GetTimeStamp(timestamp_dictionary, resource_identifier);
|
| int setting = CONTENT_SETTING_DEFAULT;
|
| bool is_integer = j.value().GetAsInteger(&setting);
|
| DCHECK(is_integer);
|
| DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
|
| std::unique_ptr<base::Value> setting_ptr(new base::Value(setting));
|
| - value_map_.SetValue(pattern_pair.first,
|
| - pattern_pair.second,
|
| - content_type_,
|
| - resource_identifier,
|
| + value_map_.SetValue(pattern_pair.first, pattern_pair.second,
|
| + content_type_, resource_identifier, last_modified,
|
| setting_ptr->DeepCopy());
|
| }
|
| }
|
| @@ -264,13 +285,12 @@ void ContentSettingsPref::ReadContentSettingsFromPref() {
|
|
|
| const base::Value* value = nullptr;
|
| settings_dictionary->GetWithoutPathExpansion(kSettingPath, &value);
|
| -
|
| if (value) {
|
| + base::Time last_modified =
|
| + GetTimeStamp(settings_dictionary, kLastModifiedPath);
|
| DCHECK(IsValueAllowedForType(value, content_type_));
|
| - value_map_.SetValue(pattern_pair.first,
|
| - pattern_pair.second,
|
| - content_type_,
|
| - ResourceIdentifier(),
|
| + value_map_.SetValue(pattern_pair.first, pattern_pair.second,
|
| + content_type_, ResourceIdentifier(), last_modified,
|
| value->DeepCopy());
|
| if (content_type_ == CONTENT_SETTINGS_TYPE_COOKIES) {
|
| ContentSetting s = ValueToContentSetting(value);
|
| @@ -321,6 +341,7 @@ void ContentSettingsPref::UpdatePref(
|
| const ContentSettingsPattern& primary_pattern,
|
| const ContentSettingsPattern& secondary_pattern,
|
| const ResourceIdentifier& resource_identifier,
|
| + const base::Time last_modified,
|
| const base::Value* value) {
|
| // Ensure that |lock_| is not held by this thread, since this function will
|
| // send out notifications (by |~DictionaryPrefUpdate|).
|
| @@ -353,29 +374,49 @@ void ContentSettingsPref::UpdatePref(
|
| if (!found) {
|
| if (value == NULL)
|
| return; // Nothing to remove. Exit early.
|
| - resource_dictionary = new base::DictionaryValue;
|
| + resource_dictionary = new base::DictionaryValue();
|
| settings_dictionary->Set(
|
| kPerResourceIdentifierPrefName, resource_dictionary);
|
| }
|
| + base::DictionaryValue* timestamp_dictionary = NULL;
|
| + bool has_timestamps = settings_dictionary->GetDictionary(
|
| + kPerResourceLastModifiedPath, ×tamp_dictionary);
|
| + if (!has_timestamps) {
|
| + timestamp_dictionary = new base::DictionaryValue();
|
| + settings_dictionary->Set(kPerResourceLastModifiedPath,
|
| + timestamp_dictionary);
|
| + }
|
| // Update resource dictionary.
|
| if (value == NULL) {
|
| resource_dictionary->RemoveWithoutPathExpansion(resource_identifier,
|
| NULL);
|
| + timestamp_dictionary->RemoveWithoutPathExpansion(resource_identifier,
|
| + NULL);
|
| if (resource_dictionary->empty()) {
|
| settings_dictionary->RemoveWithoutPathExpansion(
|
| kPerResourceIdentifierPrefName, NULL);
|
| + settings_dictionary->RemoveWithoutPathExpansion(
|
| + kPerResourceLastModifiedPath, NULL);
|
| }
|
| } else {
|
| resource_dictionary->SetWithoutPathExpansion(
|
| resource_identifier, value->DeepCopy());
|
| + timestamp_dictionary->SetStringWithoutPathExpansion(
|
| + resource_identifier,
|
| + base::Int64ToString(last_modified.ToInternalValue()));
|
| }
|
| } else {
|
| // Update settings dictionary.
|
| if (value == NULL) {
|
| settings_dictionary->RemoveWithoutPathExpansion(kSettingPath, NULL);
|
| + settings_dictionary->RemoveWithoutPathExpansion(kLastModifiedPath,
|
| + NULL);
|
| } else {
|
| settings_dictionary->SetWithoutPathExpansion(
|
| kSettingPath, value->DeepCopy());
|
| + settings_dictionary->SetStringWithoutPathExpansion(
|
| + kLastModifiedPath,
|
| + base::Int64ToString(last_modified.ToInternalValue()));
|
| }
|
| }
|
| // Remove the settings dictionary if it is empty.
|
|
|