Index: chrome/browser/extensions/extension_pref_store.cc |
diff --git a/chrome/browser/extensions/extension_pref_store.cc b/chrome/browser/extensions/extension_pref_store.cc |
index 27719ed3a69345009a2b73be9c7c7156c619e99e..4d1f81e7550d8e7136aaadc74b0071ae537fe9d4 100644 |
--- a/chrome/browser/extensions/extension_pref_store.cc |
+++ b/chrome/browser/extensions/extension_pref_store.cc |
@@ -7,79 +7,183 @@ |
#include "base/logging.h" |
#include "base/values.h" |
#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/extensions/extensions_service.h" |
#include "chrome/browser/prefs/pref_service.h" |
#include "chrome/browser/profile.h" |
#include "chrome/common/extensions/extension.h" |
-#include "chrome/common/notification_service.h" |
+#include "chrome/common/notification_details.h" |
+#include "chrome/common/notification_source.h" |
+#include "chrome/common/notification_type.h" |
+ |
+const char ExtensionPrefStore::kExtensionPreferencesKey[] = |
+ "extensions.prefvalues"; |
+ |
+// ExtensionPrefs implementation: |
+ |
+const char ExtensionPrefStore::ExtensionPrefs::kIdKey[] = "id"; |
+const char ExtensionPrefStore::ExtensionPrefs::kPreferencesKey[] = |
+ "preferences"; |
+ |
+ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(DictionaryValue* dict) |
+ : dict_(dict) { |
+ DCHECK(dict_->HasKey("id")); |
+ DCHECK(dict_->HasKey("preferences")); |
+ DCHECK_EQ(2u, dict_->size()); |
+} |
+ |
+ExtensionPrefStore::ExtensionPrefs::~ExtensionPrefs() {} |
+ |
+DictionaryValue* ExtensionPrefStore::ExtensionPrefs::Create( |
+ const std::string& extension_id) { |
+ scoped_ptr<DictionaryValue> dict(new DictionaryValue()); |
+ dict->Set(kIdKey, Value::CreateStringValue(extension_id)); |
+ dict->Set(kPreferencesKey, new DictionaryValue()); |
+ return dict.release(); |
+} |
+ |
+std::string ExtensionPrefStore::ExtensionPrefs::extension_id() const { |
+ std::string string_value; |
+ DCHECK(dict_->GetString(kIdKey, &string_value)); |
+ return string_value; |
+} |
+ |
+DictionaryValue* ExtensionPrefStore::ExtensionPrefs::pref_values() { |
+ DictionaryValue* value = NULL; |
+ CHECK(dict_->GetDictionary(kPreferencesKey, &value)); |
+ return value; |
+} |
+ |
+void ExtensionPrefStore::ExtensionPrefs::Set(const std::string& key, |
+ Value *value) { |
+ if (value == NULL) |
+ pref_values()->RemoveWithoutPathExpansion(key, NULL); |
+ else |
+ pref_values()->SetWithoutPathExpansion(key, value); |
+} |
+ |
+Value* ExtensionPrefStore::ExtensionPrefs::Get(const std::string& key) { |
+ Value* value = NULL; |
+ pref_values()->GetWithoutPathExpansion(key, &value); |
+ return value; |
+} |
+ |
+ |
+// ExtensionStack implementation: |
+ |
+ExtensionPrefStore::ExtensionStack::ExtensionStack() |
+: list_(NULL) {} |
+ |
+ExtensionPrefStore::ExtensionStack::~ExtensionStack() {} |
+ |
+void ExtensionPrefStore::ExtensionStack::Init(ListValue* list) { |
+ list_ = list; |
+} |
+ |
+bool ExtensionPrefStore::ExtensionStack::IsInitialized() const { |
+ return list_ != NULL; |
+} |
+ |
+size_t ExtensionPrefStore::ExtensionStack::Size() const { |
+ DCHECK(IsInitialized()); |
+ return list_->GetSize(); |
+} |
+ |
+ExtensionPrefStore::ExtensionPrefs |
+ ExtensionPrefStore::ExtensionStack::Get(int index) { |
+ DCHECK(IsInitialized()); |
+ DictionaryValue* dict; |
+ CHECK(list_->GetDictionary(index, &dict)); |
+ return ExtensionPrefs(dict); |
+} |
+ |
+void ExtensionPrefStore::ExtensionStack::Remove(int index) { |
+ DCHECK(IsInitialized()); |
+ CHECK(list_->Remove(index, NULL)); |
+} |
+ |
+ExtensionPrefStore::ExtensionPrefs |
+ ExtensionPrefStore::ExtensionStack::CreateEntry( |
+ const std::string& extension_id) { |
+ DCHECK(IsInitialized()); |
+ list_->Insert(0, ExtensionPrefs::Create(extension_id)); |
+ return Get(0); |
+} |
+ |
+ |
+// ExtensionPrefStore implementation: |
ExtensionPrefStore::ExtensionPrefStore(Profile* profile, |
PrefNotifier::PrefStoreType type) |
: prefs_(new DictionaryValue()), |
profile_(profile), |
+ dummy_prefs(profile ? NULL : new ListValue), |
type_(type) { |
RegisterObservers(); |
} |
ExtensionPrefStore::~ExtensionPrefStore() { |
- STLDeleteElements(&extension_stack_); |
notification_registrar_.RemoveAll(); |
} |
void ExtensionPrefStore::InstallExtensionPref(const Extension* extension, |
const char* new_pref_path, |
Value* new_pref_value) { |
- ExtensionStack::iterator i; |
- for (i = extension_stack_.begin(); i != extension_stack_.end(); ++i) { |
- if ((*i)->extension == extension) |
+ if (!extension_stack_.IsInitialized()) |
+ LazyInit(); |
+ |
+ int nr_extensions = extension_stack_.Size(); |
+ int i; |
+ for (i = 0; i < nr_extensions; ++i) { |
+ if (extension_stack_.Get(i).extension_id() == extension->id()) |
break; |
} |
// If this extension is not already in the stack, add it. Otherwise, update |
// or add the value of this preference, but don't change the extension's |
- // position in the stack. We store the extension even if this preference |
- // isn't registered with our PrefService, so that the ordering of extensions |
- // is consistent among all local-state and user ExtensionPrefStores. |
- PrefService* pref_service = GetPrefService(); |
- // The pref_service may be NULL in unit testing. |
- bool is_registered_pref = (pref_service == NULL || |
- pref_service->FindPreference(new_pref_path) != NULL); |
- PrefValueMap* pref_values; |
- if (i == extension_stack_.end()) { |
- pref_values = new PrefValueMap(); |
- if (is_registered_pref) |
- (*pref_values)[new_pref_path] = new_pref_value; |
- |
- ExtensionPrefs* extension_prefs = new ExtensionPrefs(extension, |
- pref_values); |
- extension_stack_.push_front(extension_prefs); |
- } else if (is_registered_pref) { |
- pref_values = (*i)->pref_values; |
- delete (*pref_values)[new_pref_path]; |
- (*pref_values)[new_pref_path] = new_pref_value; |
+ // position in the stack. |
+ if (i == nr_extensions) { |
+ ExtensionPrefs extension_prefs = |
+ extension_stack_.CreateEntry(extension->id()); |
+ extension_prefs.Set(new_pref_path, new_pref_value); |
+ } else { |
+ ExtensionPrefs extension_prefs = extension_stack_.Get(i); |
+ extension_prefs.Set(new_pref_path, new_pref_value); |
} |
// Apply the preference to our local |prefs_| store. |
UpdateOnePref(new_pref_path); |
+ SchedulePersist(); |
} |
void ExtensionPrefStore::UninstallExtension(const Extension* extension) { |
+ if (!extension_stack_.IsInitialized()) |
+ LazyInit(); |
+ |
// Remove this extension from the stack. |
- for (ExtensionStack::iterator i = extension_stack_.begin(); |
- i != extension_stack_.end(); ++i) { |
- if ((*i)->extension == extension) { |
- scoped_ptr<ExtensionPrefs> to_be_deleted(*i); |
- extension_stack_.erase(i); |
- UpdatePrefs(to_be_deleted->pref_values); |
+ for (int i = 0, n = extension_stack_.Size(); i < n; ++i) { |
+ ExtensionPrefs extension_prefs = extension_stack_.Get(i); |
+ if (extension_prefs.extension_id() == extension->id()) { |
+ scoped_ptr<DictionaryValue> dict_values( |
+ extension_prefs.pref_values()->DeepCopyWithoutEmptyChildren()); |
+ extension_stack_.Remove(i); |
+ UpdatePrefs(dict_values.get()); |
+ SchedulePersist(); |
return; |
} |
} |
} |
+DictionaryValue* ExtensionPrefStore::prefs() const { |
+ if (!extension_stack_.IsInitialized()) |
+ LazyInit(); |
+ return prefs_.get(); |
+} |
+ |
void ExtensionPrefStore::GetExtensionIDs(std::vector<std::string>* result) { |
- for (ExtensionStack::iterator i = extension_stack_.begin(); |
- i != extension_stack_.end(); ++i) { |
- (*result).push_back((*i)->extension->id()); |
- } |
+ if (!extension_stack_.IsInitialized()) |
+ LazyInit(); |
+ for (int i = 0, n = extension_stack_.Size(); i < n; ++i) |
+ result->push_back(extension_stack_.Get(i).extension_id()); |
} |
// This could be sped up by keeping track of which extension currently controls |
@@ -87,16 +191,6 @@ void ExtensionPrefStore::GetExtensionIDs(std::vector<std::string>* result) { |
// installed extensions will be trying to control any preferences, so stick |
// with this simpler algorithm until it causes a problem. |
void ExtensionPrefStore::UpdateOnePref(const char* path) { |
- PrefService* pref_service = GetPrefService(); |
- |
- // There are at least two PrefServices, one for local state and one for |
- // user prefs. (See browser_main.cc.) Different preferences are registered |
- // in each; if this one doesn't have the desired pref registered, we ignore |
- // it and let the other one handle it. |
- // The pref_service may be NULL in unit testing. |
- if (pref_service && !pref_service->FindPreference(path)) |
- return; |
- |
// Save the old value before removing it from the local cache. |
Value* my_old_value_ptr = NULL; |
prefs_->Get(path, &my_old_value_ptr); |
@@ -110,16 +204,19 @@ void ExtensionPrefStore::UpdateOnePref(const char* path) { |
// Find the first extension that wants to set this pref and use its value. |
Value* my_new_value = NULL; |
- for (ExtensionStack::iterator ext_iter = extension_stack_.begin(); |
- ext_iter != extension_stack_.end(); ++ext_iter) { |
- PrefValueMap::iterator value_iter = (*ext_iter)->pref_values->find(path); |
- if (value_iter != (*ext_iter)->pref_values->end()) { |
- prefs_->Set(path, (*value_iter).second->DeepCopy()); |
- my_new_value = (*value_iter).second; |
+ for (int i = 0, n = extension_stack_.Size(); i < n; ++i) { |
+ DictionaryValue* pref_values = extension_stack_.Get(i).pref_values(); |
+ bool has_pref = pref_values->GetWithoutPathExpansion(path, &my_new_value); |
+ if (has_pref) { |
+ prefs_->Set(path, my_new_value->DeepCopy()); |
break; |
} |
} |
+ // If any extension contained a value for |path| it is stored in my_new_value |
+ // now. |
+ // May be null in unit tests. |
+ PrefService* pref_service = GetPrefService(); |
if (pref_service) { |
bool value_changed = true; |
if (!my_old_value.get() && !my_new_value) { |
@@ -135,30 +232,41 @@ void ExtensionPrefStore::UpdateOnePref(const char* path) { |
} |
} |
-void ExtensionPrefStore::UpdatePrefs(const PrefValueMap* pref_values) { |
+void ExtensionPrefStore::UpdatePrefs(const DictionaryValue* pref_values) { |
if (!pref_values) |
return; |
- for (PrefValueMap::const_iterator i = pref_values->begin(); |
- i != pref_values->end(); ++i) { |
- UpdateOnePref(i->first); |
+ for (DictionaryValue::key_iterator i = pref_values->begin_keys(); |
+ i != pref_values->end_keys(); ++i) { |
+ UpdateOnePref((*i).c_str()); |
} |
} |
-PrefService* ExtensionPrefStore::GetPrefService() { |
- if (profile_) |
- return profile_->GetPrefs(); |
- return g_browser_process->local_state(); |
+PrefService* ExtensionPrefStore::GetPrefService() const { |
+ // In case of local state, there is no profile nor pref service. |
+ if (!profile_) |
+ return NULL; |
+ return profile_->GetPrefs(); |
+} |
+ |
+void ExtensionPrefStore::SchedulePersist() { |
+ PrefService* prefService = GetPrefService(); |
+ if (prefService) |
+ prefService->ScheduleSavePersistentPrefs(); |
} |
void ExtensionPrefStore::RegisterObservers() { |
+ // If profile_ is NULL, this ExtensionPrefStore is for local-state. |
+ if (!profile_) |
+ return; |
+ |
notification_registrar_.Add(this, |
NotificationType::EXTENSION_PREF_CHANGED, |
- NotificationService::AllSources()); |
+ Source<Profile>(profile_)); |
notification_registrar_.Add(this, |
NotificationType::EXTENSION_UNLOADED, |
- NotificationService::AllSources()); |
+ Source<Profile>(profile_)); |
} |
void ExtensionPrefStore::Observe(NotificationType type, |
@@ -166,22 +274,15 @@ void ExtensionPrefStore::Observe(NotificationType type, |
const NotificationDetails& details) { |
switch (type.value) { |
case NotificationType::EXTENSION_PREF_CHANGED: { |
- Profile* extension_profile = Source<Profile>(source).ptr(); |
- // The ExtensionPrefStore for the local state watches all profiles. |
- if (!profile_ || profile_ == extension_profile) { |
- ExtensionPrefStore::ExtensionPrefDetails* data = |
- Details<ExtensionPrefStore::ExtensionPrefDetails>(details).ptr(); |
- InstallExtensionPref(data->first, data->second.first, |
- data->second.second); |
- } |
+ ExtensionPrefStore::ExtensionPrefDetails* data = |
+ Details<ExtensionPrefStore::ExtensionPrefDetails>(details).ptr(); |
+ InstallExtensionPref(data->first, data->second.first, |
+ data->second.second); |
break; |
} |
case NotificationType::EXTENSION_UNLOADED: { |
- Profile* extension_profile = Source<Profile>(source).ptr(); |
const Extension* extension = Details<const Extension>(details).ptr(); |
- // The ExtensionPrefStore for the local state watches all profiles. |
- if (profile_ == NULL || profile_ == extension_profile) |
- UninstallExtension(extension); |
+ UninstallExtension(extension); |
break; |
} |
default: { |
@@ -190,10 +291,33 @@ void ExtensionPrefStore::Observe(NotificationType type, |
} |
} |
-ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(const Extension* extension, |
- PrefValueMap* values) : extension(extension), pref_values(values) {} |
+void ExtensionPrefStore::LazyInit() const { |
+ // If profile_==NULL, this ExtensionPrefStore is for local-state. |
+ DCHECK(!extension_stack_.IsInitialized()) |
+ << "LazyInit called even though extension_stack_ is already initialized"; |
+ |
+ PrefService* pref_service = GetPrefService(); |
+ if (!pref_service) { |
+ extension_stack_.Init(dummy_prefs.get()); |
+ return; |
+ } |
+ pref_service->RegisterListPref(kExtensionPreferencesKey); |
+ |
+ scoped_ptr<ListValue> dict( |
+ pref_service->GetMutableList(kExtensionPreferencesKey)); |
+ |
+ extension_stack_.Init(dict.release()); |
-ExtensionPrefStore::ExtensionPrefs::~ExtensionPrefs() { |
- STLDeleteValues(pref_values); |
- delete pref_values; |
+ for (int i = 0, n = extension_stack_.Size(); i < n; ++i) { |
+ DictionaryValue* pref_values = extension_stack_.Get(i).pref_values(); |
+ for (DictionaryValue::key_iterator iter = pref_values->begin_keys(); |
+ iter != pref_values->end_keys(); ++iter) { |
+ const std::string& key = *iter; |
+ if (!prefs_->HasKey(key)) { |
+ Value *value; |
+ if (pref_values->GetWithoutPathExpansion(key, &value)) |
+ prefs_->Set(key, value->DeepCopy()); |
+ } |
+ } |
+ } |
} |