Chromium Code Reviews| Index: chrome/browser/extensions/extension_prefs.cc |
| diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc |
| index 3c789b094fa88fcce2fcb8526a45694e5e428fb3..8a0b45b9e6899e96e26b8f9553ea418db86c55cd 100644 |
| --- a/chrome/browser/extensions/extension_prefs.cc |
| +++ b/chrome/browser/extensions/extension_prefs.cc |
| @@ -8,6 +8,7 @@ |
| #include "base/string_number_conversions.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/common/extensions/extension.h" |
| +#include "chrome/common/in_memory_pref_store.h" |
| #include "chrome/common/pref_names.h" |
| using base::Time; |
| @@ -78,6 +79,13 @@ const char kPrefAppLaunchIndex[] = "app_launcher_index"; |
| // "A preference for storing extra data sent in update checks for an extension. |
| const char kUpdateUrlData[] = "update_url_data"; |
| +// A preference that indicates when an extension was installed. |
| +const char kPrefInstallTime[] = "install_time"; |
| + |
| +// A preference that indicates the last effective preference values of an |
| +// extension. |
|
Mattias Nissler (ping if slow)
2010/11/18 16:30:33
Maybe add "The value is a dictionary mapping prefe
battre (please use the other)
2010/11/18 16:47:33
Done.
|
| +const char kPrefPreferences[] = "preferences"; |
| + |
| } // namespace |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -119,6 +127,8 @@ ExtensionPrefs::ExtensionPrefs(PrefService* prefs, const FilePath& root_dir) |
| CleanupBadExtensionKeys(prefs); |
| MakePathsRelative(); |
| + |
| + InstallPersistedExtensionControlledPrefs(); |
| } |
| ExtensionPrefs::~ExtensionPrefs() {} |
| @@ -524,12 +534,18 @@ void ExtensionPrefs::OnExtensionInstalled( |
| const Extension* extension, Extension::State initial_state, |
| bool initial_incognito_enabled) { |
| const std::string& id = extension->id(); |
| + const base::Time installTime = GetCurrentTime(); |
| UpdateExtensionPref(id, kPrefState, |
| Value::CreateIntegerValue(initial_state)); |
| UpdateExtensionPref(id, kPrefIncognitoEnabled, |
| Value::CreateBooleanValue(initial_incognito_enabled)); |
| UpdateExtensionPref(id, kPrefLocation, |
| Value::CreateIntegerValue(extension->location())); |
| + UpdateExtensionPref(id, kPrefInstallTime, |
| + Value::CreateStringValue( |
| + base::Int64ToString(installTime.ToInternalValue()))); |
| + UpdateExtensionPref(id, kPrefPreferences, new DictionaryValue()); |
| + |
| FilePath::StringType path = MakePathRelative(install_directory_, |
| extension->path(), NULL); |
| UpdateExtensionPref(id, kPrefPath, Value::CreateStringValue(path)); |
| @@ -551,6 +567,10 @@ void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id, |
| // and install the extension anymore (except when |external_uninstall| is |
| // true, which signifies that the registry key was deleted or the pref file |
| // no longer lists the extension). |
| + |
| + std::vector<std::string> prefKeys; |
| + GetExtensionControlledPrefKeys(extension_id, &prefKeys); |
| + |
| if (!external_uninstall && Extension::IsExternalLocation(location)) { |
| UpdateExtensionPref(extension_id, kPrefState, |
| Value::CreateIntegerValue(Extension::KILLBIT)); |
| @@ -558,10 +578,15 @@ void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id, |
| } else { |
| DeleteExtensionPrefs(extension_id); |
| } |
| + |
| + for (std::vector<std::string>::iterator i = prefKeys.begin(); |
| + i != prefKeys.end(); ++i) { |
| + UpdateWinningPref(*i); |
| + } |
| } |
| Extension::State ExtensionPrefs::GetExtensionState( |
| - const std::string& extension_id) { |
| + const std::string& extension_id) const { |
| DictionaryValue* extension = GetExtensionPref(extension_id); |
| // If the extension doesn't have a pref, it's a --load-extension. |
| @@ -582,6 +607,14 @@ void ExtensionPrefs::SetExtensionState(const Extension* extension, |
| Extension::State state) { |
| UpdateExtensionPref(extension->id(), kPrefState, |
| Value::CreateIntegerValue(state)); |
| + |
| + std::vector<std::string> prefKeys; |
| + GetExtensionControlledPrefKeys(extension->id(), &prefKeys); |
| + for (std::vector<std::string>::iterator i = prefKeys.begin(); |
| + i != prefKeys.end(); ++i) { |
| + UpdateWinningPref(*i); |
| + } |
| + |
| SavePrefsAndNotify(); |
| } |
| @@ -661,6 +694,16 @@ DictionaryValue* ExtensionPrefs::GetExtensionPref( |
| return extension; |
| } |
| +DictionaryValue* ExtensionPrefs::GetExtensionControlledPrefs( |
| + const std::string& extension_id) const { |
| + DictionaryValue* extension = GetExtensionPref(extension_id); |
| + if (!extension) |
| + return NULL; |
| + DictionaryValue* preferences = NULL; |
| + extension->GetDictionary(kPrefPreferences, &preferences); |
| + return preferences; |
| +} |
| + |
| // Helper function for GetInstalledExtensionsInfo. |
| static ExtensionInfo* GetInstalledExtensionInfoImpl( |
| DictionaryValue* extension_data, |
| @@ -920,6 +963,189 @@ std::string ExtensionPrefs::GetUpdateUrlData(const std::string& extension_id) { |
| return data; |
| } |
| +base::Time ExtensionPrefs::GetCurrentTime() const { |
| + return base::Time::Now(); |
| +} |
| + |
| +base::Time ExtensionPrefs::GetInstallTime(const DictionaryValue* extension) |
| + const { |
| + DCHECK(extension); |
| + std::string install_time_str("0"); |
| + extension->GetString(kPrefInstallTime, &install_time_str); |
| + int64 install_time_i64 = 0; |
| + base::StringToInt64(install_time_str, &install_time_i64); |
| + LOG_IF(ERROR, install_time_i64 == 0) |
| + << "Error parsing installation time of an extension"; |
| + return base::Time::FromInternalValue(install_time_i64); |
| +} |
| + |
| +void ExtensionPrefs::InstallPersistedExtensionControlledPrefs() { |
| + // When this is called, the PrefService is initialized and provides access |
| + // to the user preferences stored in a JSON file. We take the persisted |
| + // preferences of all extensions, calculate the effective preferences |
| + // (considering that one extension overrides preferences of other extensions) |
| + // and store the effective preferences in the PrefService. |
| + |
| + const DictionaryValue* extensions = |
| + pref_service()->GetDictionary(kExtensionsPref); |
| + |
| + // Collect extensions, sorted by time (latest installed appears last). |
| + std::vector<DictionaryValue*> sorted_extensions; |
| + for (DictionaryValue::key_iterator ext_id = extensions->begin_keys(); |
| + ext_id != extensions->end_keys(); ++ext_id) { |
| + if (GetExtensionState(*ext_id) != Extension::ENABLED) |
| + continue; |
| + |
| + DictionaryValue* extension = GetExtensionPref(*ext_id); |
| + CHECK(extension != NULL); |
| + |
| + if (GetInstallTime(extension) == base::Time::FromInternalValue(0)) { |
| + // Fix old entry that did not get an installation time entry when |
| + // it was installed. |
| + const base::Time installTime = GetCurrentTime(); |
| + extension->Set(kPrefInstallTime, |
| + Value::CreateStringValue( |
| + base::Int64ToString(installTime.ToInternalValue()))); |
| + SavePrefsAndNotify(); |
| + } |
| + DictionaryValue* dummy_prefs; |
| + if (!extension->GetDictionary(kPrefPreferences, &dummy_prefs)) { |
| + extension->Set(kPrefPreferences, new DictionaryValue()); |
| + } |
| + |
| + // Currently we sort only by time, no tie-breaker. |
| + std::vector<DictionaryValue*>::iterator insert_pos = |
| + sorted_extensions.begin(); |
| + while (insert_pos != sorted_extensions.end() && |
| + GetInstallTime(extension) > GetInstallTime(*insert_pos)) { |
| + ++insert_pos; |
| + } |
| + sorted_extensions.insert(insert_pos, extension); |
| + } |
| + |
| + // Collect all effective preferences (later ones override newer ones). |
| + scoped_ptr<DictionaryValue> merged_non_expanded(new DictionaryValue); |
| + for (std::vector<DictionaryValue*>::iterator i = sorted_extensions.begin(); |
| + i != sorted_extensions.end(); ++i) { |
| + DictionaryValue* preferences; |
| + if ((*i)->GetDictionary(kPrefPreferences, &preferences)) |
| + merged_non_expanded->MergeDictionary(preferences); |
| + } |
| + |
| + // Expand all keys. |
| + scoped_ptr<InMemoryPrefStore> extension_prefs(new InMemoryPrefStore); |
| + for (DictionaryValue::key_iterator prefkey = |
| + merged_non_expanded->begin_keys(); |
| + prefkey != merged_non_expanded->end_keys(); |
| + ++prefkey) { |
| + Value* value; |
| + CHECK(merged_non_expanded->GetWithoutPathExpansion(*prefkey, &value)); |
| + extension_prefs->prefs()->Set(*prefkey, value->DeepCopy()); |
| + } |
| + |
| + // Store result in pref service. |
| + pref_service()->pref_value_store()->ReplaceExtensionPrefStore( |
| + extension_prefs.release()); |
| +} |
| + |
| +namespace subtle { |
| +static bool equalValues(const Value* a, const Value* b) { |
| + if ((a == NULL) && (b == NULL)) return true; |
| + if ((a == NULL) ^ (b == NULL)) return false; |
| + return a->Equals(b); |
| +} |
| +} |
| + |
| +const Value* ExtensionPrefs::WinningExtensionControlledPrefValue( |
| + const std::string& key) const { |
| + Value *winner = NULL; |
| + int64 winners_install_time = 0; |
| + |
| + const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref); |
| + for (DictionaryValue::key_iterator ext_iter = extensions->begin_keys(); |
| + ext_iter != extensions->end_keys(); ++ext_iter) { |
| + if (GetExtensionState(*ext_iter) != Extension::ENABLED) |
| + continue; |
| + |
| + DictionaryValue* extension = GetExtensionPref(*ext_iter); |
| + CHECK(extension); |
| + |
| + std::string extension_install_time_s = "0"; |
| + extension->GetString(kPrefInstallTime, &extension_install_time_s); |
| + int64 extension_install_time = 0; |
| + base::StringToInt64(extension_install_time_s, &extension_install_time); |
| + |
| + // We do not need to consider extensions that were installed before the |
| + // most recent extension found that provides the requested preference. |
| + if (extension_install_time < winners_install_time) { |
| + continue; |
| + } |
| + |
| + DictionaryValue* preferences = GetExtensionControlledPrefs(*ext_iter); |
| + CHECK(preferences); |
| + |
| + Value *value = NULL; |
| + if (preferences->GetWithoutPathExpansion(key, &value)) { |
| + // This extension is more recent than the last one providing this pref. |
| + winner = value; |
| + winners_install_time = extension_install_time; |
| + } |
| + } |
| + return winner; |
| +} |
| + |
| +void ExtensionPrefs::UpdateWinningPref(const std::string& pref_key) { |
| + PrefStore* extensionPrefStore = |
| + pref_service()->pref_value_store()->GetExtensionPrefStore(); |
| + const Value* winningPrefValue = WinningExtensionControlledPrefValue(pref_key); |
| + Value* oldValue = NULL; |
| + extensionPrefStore->prefs()->Get(pref_key, &oldValue); |
| + bool changed = !subtle::equalValues(winningPrefValue, oldValue); |
| + |
| + if (winningPrefValue) { |
| + extensionPrefStore->prefs()->Set(pref_key, winningPrefValue->DeepCopy()); |
| + } else { |
| + extensionPrefStore->prefs()->Remove(pref_key, NULL); |
| + } |
| + |
| + if (changed) |
| + pref_service()->pref_notifier()->OnPreferenceSet( |
| + pref_key.c_str(), PrefNotifier::EXTENSION_STORE); |
| +} |
| + |
| +void ExtensionPrefs::SetExtensionControlledPref(const std::string& extension_id, |
| + const std::string& pref_key, |
| + Value* value) { |
| + DictionaryValue* extensionPreferences = |
| + GetExtensionControlledPrefs(extension_id); |
| + |
| + Value* oldValue = NULL; |
| + extensionPreferences->GetWithoutPathExpansion(pref_key, &oldValue); |
| + bool modified = subtle::equalValues(oldValue, value); |
| + |
| + if (value == NULL) |
| + extensionPreferences->RemoveWithoutPathExpansion(pref_key, NULL); |
| + else |
| + extensionPreferences->SetWithoutPathExpansion(pref_key, value); |
| + |
| + if (modified) |
| + pref_service()->ScheduleSavePersistentPrefs(); |
| + |
| + UpdateWinningPref(pref_key); |
| +} |
| + |
| +void ExtensionPrefs::GetExtensionControlledPrefKeys( |
| + const std::string& extension_id, std::vector<std::string> *out) const { |
| + DCHECK(out != NULL); |
| + DictionaryValue* extPrefs = GetExtensionControlledPrefs(extension_id); |
| + if (extPrefs) { |
| + for (DictionaryValue::key_iterator i = extPrefs->begin_keys(); |
| + i != extPrefs->end_keys(); ++i) { |
| + out->push_back(*i); |
| + } |
| + } |
| +} |
| + |
| // static |
| void ExtensionPrefs::RegisterUserPrefs(PrefService* prefs) { |
| prefs->RegisterDictionaryPref(kExtensionsPref); |