Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/extension_pref_store.h" | 5 #include "chrome/browser/extensions/extension_pref_store.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/values.h" | 8 #include "base/values.h" |
| 9 #include "chrome/browser/browser_process.h" | 9 #include "chrome/browser/browser_process.h" |
| 10 #include "chrome/browser/extensions/extensions_service.h" | |
| 10 #include "chrome/browser/prefs/pref_service.h" | 11 #include "chrome/browser/prefs/pref_service.h" |
| 11 #include "chrome/browser/profile.h" | 12 #include "chrome/browser/profile.h" |
| 12 #include "chrome/common/extensions/extension.h" | 13 #include "chrome/common/extensions/extension.h" |
| 13 #include "chrome/common/notification_service.h" | 14 #include "chrome/common/notification_details.h" |
| 15 #include "chrome/common/notification_source.h" | |
| 16 #include "chrome/common/notification_type.h" | |
| 17 | |
| 18 const char ExtensionPrefStore::kExtensionPreferencesKey[] = | |
| 19 "extension_preferences"; | |
| 20 | |
| 21 // ExtensionPrefs implementation: | |
| 22 | |
| 23 const char ExtensionPrefStore::ExtensionPrefs::kIdKey[] = "id"; | |
| 24 const char ExtensionPrefStore::ExtensionPrefs::kPreferencesKey[] = | |
| 25 "preferences"; | |
| 26 | |
| 27 ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(DictionaryValue* dict) | |
| 28 : dict_(dict) { | |
| 29 DCHECK(dict_->HasKey("id")); | |
| 30 DCHECK(dict_->HasKey("preferences")); | |
| 31 DCHECK(dict_->size()==2); | |
|
Bernhard Bauer
2010/11/12 18:16:57
Use DCHECK_EQ(2, dict->size()) for better error me
battre (please use the other)
2010/11/12 19:07:09
Done.
| |
| 32 } | |
| 33 | |
| 34 ExtensionPrefStore::ExtensionPrefs::~ExtensionPrefs() | |
| 35 {} | |
|
Bernhard Bauer
2010/11/12 18:16:57
Nit: Either both braces on the previous line, or t
battre (please use the other)
2010/11/12 19:07:09
Done.
| |
| 36 | |
| 37 DictionaryValue* ExtensionPrefStore::ExtensionPrefs::Create( | |
| 38 const std::string& extension_id) { | |
| 39 scoped_ptr<DictionaryValue> dict(new DictionaryValue()); | |
| 40 dict->Set(kIdKey, Value::CreateStringValue(extension_id)); | |
| 41 dict->Set(kPreferencesKey, new DictionaryValue()); | |
| 42 return dict.release(); | |
| 43 } | |
| 44 | |
| 45 std::string ExtensionPrefStore::ExtensionPrefs::extension_id() const { | |
| 46 std::string string_value; | |
| 47 CHECK(dict_->GetString(kIdKey, &string_value)); | |
|
Bernhard Bauer
2010/11/12 18:16:57
I think in release builds we try to only crash to
battre (please use the other)
2010/11/12 19:07:09
Done. If this fails, there won't be a crash but th
| |
| 48 return string_value; | |
| 49 } | |
| 50 | |
| 51 DictionaryValue* ExtensionPrefStore::ExtensionPrefs::pref_values() { | |
| 52 DictionaryValue* value = NULL; | |
| 53 CHECK(dict_->GetDictionary(kPreferencesKey, &value)); | |
| 54 return value; | |
| 55 } | |
| 56 | |
| 57 void ExtensionPrefStore::ExtensionPrefs::set(const std::string& key, | |
| 58 Value *value) { | |
| 59 if (value == NULL) | |
| 60 pref_values()->RemoveWithoutPathExpansion(key, NULL); | |
| 61 else | |
| 62 pref_values()->SetWithoutPathExpansion(key, value); | |
| 63 } | |
| 64 | |
| 65 Value* ExtensionPrefStore::ExtensionPrefs::get(const std::string& key) { | |
| 66 Value* value = NULL; | |
| 67 pref_values()->GetWithoutPathExpansion(key, &value); | |
| 68 return value; // NULL if Get failed. | |
|
Bernhard Bauer
2010/11/12 18:16:57
Nit: That comment is unnecessary.
battre (please use the other)
2010/11/12 19:07:09
Done.
| |
| 69 } | |
| 70 | |
| 71 | |
| 72 // ExtensionStack implementation: | |
| 73 | |
| 74 ExtensionPrefStore::ExtensionStack::ExtensionStack() | |
| 75 : list_(NULL) {} | |
| 76 | |
| 77 ExtensionPrefStore::ExtensionStack::~ExtensionStack() | |
| 78 {} | |
| 79 | |
| 80 void ExtensionPrefStore::ExtensionStack::init(ListValue* list) { | |
| 81 list_ = list; | |
| 82 } | |
| 83 | |
| 84 bool ExtensionPrefStore::ExtensionStack::is_initialized() const { | |
| 85 return list_ != NULL; | |
| 86 } | |
| 87 | |
| 88 size_t ExtensionPrefStore::ExtensionStack::size() const { | |
| 89 DCHECK(is_initialized()); | |
| 90 return list_->GetSize(); | |
| 91 } | |
| 92 | |
| 93 ExtensionPrefStore::ExtensionPrefs | |
| 94 ExtensionPrefStore::ExtensionStack::get(int index) { | |
| 95 DCHECK(is_initialized()); | |
| 96 DictionaryValue* dict; | |
| 97 CHECK(list_->GetDictionary(index, &dict)); | |
| 98 return ExtensionPrefs(dict); | |
| 99 } | |
| 100 | |
| 101 void ExtensionPrefStore::ExtensionStack::remove(int index) { | |
| 102 DCHECK(is_initialized()); | |
| 103 CHECK(list_->Remove(index, NULL)); | |
| 104 } | |
| 105 | |
| 106 ExtensionPrefStore::ExtensionPrefs | |
| 107 ExtensionPrefStore::ExtensionStack::CreateEntry( | |
| 108 const std::string& extension_id) { | |
| 109 DCHECK(is_initialized()); | |
| 110 list_->Insert(0, ExtensionPrefs::Create(extension_id)); | |
| 111 return get(0); | |
| 112 } | |
| 113 | |
| 114 | |
| 115 // ExtensionPrefStore implementation: | |
| 14 | 116 |
| 15 ExtensionPrefStore::ExtensionPrefStore(Profile* profile, | 117 ExtensionPrefStore::ExtensionPrefStore(Profile* profile, |
| 16 PrefNotifier::PrefStoreType type) | 118 PrefNotifier::PrefStoreType type) |
| 17 : prefs_(new DictionaryValue()), | 119 : prefs_(new DictionaryValue()), |
| 18 profile_(profile), | 120 profile_(profile), |
| 121 dummy_prefs(profile ? NULL : new ListValue), | |
| 19 type_(type) { | 122 type_(type) { |
| 20 RegisterObservers(); | 123 RegisterObservers(); |
| 21 } | 124 } |
| 22 | 125 |
| 23 ExtensionPrefStore::~ExtensionPrefStore() { | 126 ExtensionPrefStore::~ExtensionPrefStore() { |
| 24 STLDeleteElements(&extension_stack_); | |
| 25 notification_registrar_.RemoveAll(); | 127 notification_registrar_.RemoveAll(); |
| 26 } | 128 } |
| 27 | 129 |
| 28 void ExtensionPrefStore::InstallExtensionPref(const Extension* extension, | 130 void ExtensionPrefStore::InstallExtensionPref(const Extension* extension, |
| 29 const char* new_pref_path, | 131 const char* new_pref_path, |
| 30 Value* new_pref_value) { | 132 Value* new_pref_value) { |
| 31 ExtensionStack::iterator i; | 133 if (!extension_stack_.is_initialized()) |
| 32 for (i = extension_stack_.begin(); i != extension_stack_.end(); ++i) { | 134 LazyInit(); |
| 33 if ((*i)->extension == extension) | 135 |
| 136 int nr_extensions = extension_stack_.size(); | |
| 137 int i; | |
| 138 for (i = 0; i < nr_extensions; ++i) { | |
| 139 if (extension_stack_.get(i).extension_id() == extension->id()) | |
| 34 break; | 140 break; |
| 35 } | 141 } |
| 36 | 142 |
| 37 // If this extension is not already in the stack, add it. Otherwise, update | 143 // If this extension is not already in the stack, add it. Otherwise, update |
| 38 // or add the value of this preference, but don't change the extension's | 144 // or add the value of this preference, but don't change the extension's |
| 39 // position in the stack. We store the extension even if this preference | 145 // position in the stack. |
| 40 // isn't registered with our PrefService, so that the ordering of extensions | 146 if (i == nr_extensions) { |
| 41 // is consistent among all local-state and user ExtensionPrefStores. | 147 ExtensionPrefs extension_prefs = |
| 42 PrefService* pref_service = GetPrefService(); | 148 extension_stack_.CreateEntry(extension->id()); |
| 43 // The pref_service may be NULL in unit testing. | 149 extension_prefs.set(new_pref_path, new_pref_value); |
| 44 bool is_registered_pref = (pref_service == NULL || | 150 } else { |
| 45 pref_service->FindPreference(new_pref_path) != NULL); | 151 ExtensionPrefs extension_prefs = extension_stack_.get(i); |
| 46 PrefValueMap* pref_values; | 152 extension_prefs.set(new_pref_path, new_pref_value); |
| 47 if (i == extension_stack_.end()) { | |
| 48 pref_values = new PrefValueMap(); | |
| 49 if (is_registered_pref) | |
| 50 (*pref_values)[new_pref_path] = new_pref_value; | |
| 51 | |
| 52 ExtensionPrefs* extension_prefs = new ExtensionPrefs(extension, | |
| 53 pref_values); | |
| 54 extension_stack_.push_front(extension_prefs); | |
| 55 } else if (is_registered_pref) { | |
| 56 pref_values = (*i)->pref_values; | |
| 57 delete (*pref_values)[new_pref_path]; | |
| 58 (*pref_values)[new_pref_path] = new_pref_value; | |
| 59 } | 153 } |
| 60 | 154 |
| 61 // Apply the preference to our local |prefs_| store. | 155 // Apply the preference to our local |prefs_| store. |
| 62 UpdateOnePref(new_pref_path); | 156 UpdateOnePref(new_pref_path); |
| 157 SchedulePersist(); | |
| 63 } | 158 } |
| 64 | 159 |
| 65 void ExtensionPrefStore::UninstallExtension(const Extension* extension) { | 160 void ExtensionPrefStore::UninstallExtension(const Extension* extension) { |
| 161 if (!extension_stack_.is_initialized()) | |
| 162 LazyInit(); | |
| 163 | |
| 66 // Remove this extension from the stack. | 164 // Remove this extension from the stack. |
| 67 for (ExtensionStack::iterator i = extension_stack_.begin(); | 165 for (int i = 0, n = extension_stack_.size(); i < n; ++i) { |
| 68 i != extension_stack_.end(); ++i) { | 166 ExtensionPrefs extension_prefs = extension_stack_.get(i); |
| 69 if ((*i)->extension == extension) { | 167 if (extension_prefs.extension_id() == extension->id()) { |
| 70 scoped_ptr<ExtensionPrefs> to_be_deleted(*i); | 168 scoped_ptr<DictionaryValue> dict_values( |
| 71 extension_stack_.erase(i); | 169 extension_prefs.pref_values()->DeepCopyWithoutEmptyChildren()); |
| 72 UpdatePrefs(to_be_deleted->pref_values); | 170 extension_stack_.remove(i); |
| 171 UpdatePrefs(dict_values.get()); | |
| 172 SchedulePersist(); | |
| 73 return; | 173 return; |
| 74 } | 174 } |
| 75 } | 175 } |
| 76 } | 176 } |
| 77 | 177 |
| 178 DictionaryValue* ExtensionPrefStore::prefs() const { | |
| 179 if (!extension_stack_.is_initialized()) | |
| 180 LazyInit(); | |
| 181 return prefs_.get(); | |
| 182 } | |
| 183 | |
| 78 void ExtensionPrefStore::GetExtensionIDs(std::vector<std::string>* result) { | 184 void ExtensionPrefStore::GetExtensionIDs(std::vector<std::string>* result) { |
| 79 for (ExtensionStack::iterator i = extension_stack_.begin(); | 185 if (!extension_stack_.is_initialized()) |
| 80 i != extension_stack_.end(); ++i) { | 186 LazyInit(); |
| 81 (*result).push_back((*i)->extension->id()); | 187 for (int i = 0, n = extension_stack_.size(); i < n; ++i) |
| 82 } | 188 (*result).push_back(extension_stack_.get(i).extension_id()); |
|
Bernhard Bauer
2010/11/12 18:16:57
Nit: result->push_back
battre (please use the other)
2010/11/12 19:07:09
Done.
| |
| 83 } | 189 } |
| 84 | 190 |
| 85 // This could be sped up by keeping track of which extension currently controls | 191 // This could be sped up by keeping track of which extension currently controls |
| 86 // a given preference, among other optimizations. But probably fewer than 10 | 192 // a given preference, among other optimizations. But probably fewer than 10 |
| 87 // installed extensions will be trying to control any preferences, so stick | 193 // installed extensions will be trying to control any preferences, so stick |
| 88 // with this simpler algorithm until it causes a problem. | 194 // with this simpler algorithm until it causes a problem. |
| 89 void ExtensionPrefStore::UpdateOnePref(const char* path) { | 195 void ExtensionPrefStore::UpdateOnePref(const char* path) { |
| 90 PrefService* pref_service = GetPrefService(); | |
| 91 | |
| 92 // There are at least two PrefServices, one for local state and one for | |
| 93 // user prefs. (See browser_main.cc.) Different preferences are registered | |
| 94 // in each; if this one doesn't have the desired pref registered, we ignore | |
| 95 // it and let the other one handle it. | |
| 96 // The pref_service may be NULL in unit testing. | |
| 97 if (pref_service && !pref_service->FindPreference(path)) | |
| 98 return; | |
| 99 | |
| 100 // Save the old value before removing it from the local cache. | 196 // Save the old value before removing it from the local cache. |
| 101 Value* my_old_value_ptr = NULL; | 197 Value* my_old_value_ptr = NULL; |
| 102 prefs_->Get(path, &my_old_value_ptr); | 198 prefs_->Get(path, &my_old_value_ptr); |
| 103 scoped_ptr<Value> my_old_value; | 199 scoped_ptr<Value> my_old_value; |
| 104 if (my_old_value_ptr) | 200 if (my_old_value_ptr) |
| 105 my_old_value.reset(my_old_value_ptr->DeepCopy()); | 201 my_old_value.reset(my_old_value_ptr->DeepCopy()); |
| 106 | 202 |
| 107 // DictionaryValue::Set complains if a key is overwritten with the same | 203 // DictionaryValue::Set complains if a key is overwritten with the same |
| 108 // value, so remove it first. | 204 // value, so remove it first. |
| 109 prefs_->Remove(path, NULL); | 205 prefs_->Remove(path, NULL); |
| 110 | 206 |
| 111 // Find the first extension that wants to set this pref and use its value. | 207 // Find the first extension that wants to set this pref and use its value. |
| 112 Value* my_new_value = NULL; | 208 Value* my_new_value = NULL; |
| 113 for (ExtensionStack::iterator ext_iter = extension_stack_.begin(); | 209 for (int i = 0, n = extension_stack_.size(); i < n; ++i) { |
| 114 ext_iter != extension_stack_.end(); ++ext_iter) { | 210 DictionaryValue* pref_values = extension_stack_.get(i).pref_values(); |
| 115 PrefValueMap::iterator value_iter = (*ext_iter)->pref_values->find(path); | 211 bool has_pref = pref_values->GetWithoutPathExpansion(path, &my_new_value); |
| 116 if (value_iter != (*ext_iter)->pref_values->end()) { | 212 if (has_pref) { |
|
Bernhard Bauer
2010/11/12 18:16:57
Nit: You could inline |has_pref|.
battre (please use the other)
2010/11/12 19:07:09
Had it inlined before and found this easier to rea
| |
| 117 prefs_->Set(path, (*value_iter).second->DeepCopy()); | 213 prefs_->Set(path, my_new_value->DeepCopy()); |
| 118 my_new_value = (*value_iter).second; | |
| 119 break; | 214 break; |
| 120 } | 215 } |
| 121 } | 216 } |
| 217 // If any extension contained a value for |path| it is stored in my_new_value | |
| 218 // now. | |
| 122 | 219 |
| 220 // May be null in unit tests. | |
| 221 PrefService* pref_service = GetPrefService(); | |
| 123 if (pref_service) { | 222 if (pref_service) { |
| 124 bool value_changed = true; | 223 bool value_changed = true; |
| 125 if (!my_old_value.get() && !my_new_value) { | 224 if (!my_old_value.get() && !my_new_value) { |
| 126 value_changed = false; | 225 value_changed = false; |
| 127 } else if (my_old_value.get() && | 226 } else if (my_old_value.get() && |
| 128 my_new_value && | 227 my_new_value && |
| 129 my_old_value->Equals(my_new_value)) { | 228 my_old_value->Equals(my_new_value)) { |
| 130 value_changed = false; | 229 value_changed = false; |
| 131 } | 230 } |
| 132 | 231 |
| 133 if (value_changed) | 232 if (value_changed) |
| 134 pref_service->pref_notifier()->OnPreferenceSet(path, type_); | 233 pref_service->pref_notifier()->OnPreferenceSet(path, type_); |
| 135 } | 234 } |
| 136 } | 235 } |
| 137 | 236 |
| 138 void ExtensionPrefStore::UpdatePrefs(const PrefValueMap* pref_values) { | 237 void ExtensionPrefStore::UpdatePrefs(const DictionaryValue* pref_values) { |
| 139 if (!pref_values) | 238 if (!pref_values) |
| 140 return; | 239 return; |
| 141 | 240 |
| 142 for (PrefValueMap::const_iterator i = pref_values->begin(); | 241 for (DictionaryValue::key_iterator i = pref_values->begin_keys(); |
| 143 i != pref_values->end(); ++i) { | 242 i != pref_values->end_keys(); ++i) { |
| 144 UpdateOnePref(i->first); | 243 UpdateOnePref((*i).c_str()); |
| 145 } | 244 } |
| 146 } | 245 } |
| 147 | 246 |
| 148 PrefService* ExtensionPrefStore::GetPrefService() { | 247 PrefService* ExtensionPrefStore::GetPrefService() const { |
| 149 if (profile_) | 248 // In case of local state, there is no profile nor pref service. |
| 150 return profile_->GetPrefs(); | 249 if (!profile_) |
| 151 return g_browser_process->local_state(); | 250 return NULL; |
| 251 return profile_->GetPrefs(); | |
| 252 } | |
| 253 | |
| 254 void ExtensionPrefStore::SchedulePersist() { | |
| 255 PrefService* prefService = GetPrefService(); | |
| 256 if (prefService) | |
| 257 prefService->ScheduleSavePersistentPrefs(); | |
| 152 } | 258 } |
| 153 | 259 |
| 154 void ExtensionPrefStore::RegisterObservers() { | 260 void ExtensionPrefStore::RegisterObservers() { |
| 261 // If profile_==NULL, this ExtensionPrefStore is for local-state. | |
|
Bernhard Bauer
2010/11/12 18:16:57
Nit: "If |profile_| is NULL"? In whole sentences,
battre (please use the other)
2010/11/12 19:07:09
Done.
| |
| 262 if (!profile_) | |
| 263 return; | |
| 264 | |
| 155 notification_registrar_.Add(this, | 265 notification_registrar_.Add(this, |
| 156 NotificationType::EXTENSION_PREF_CHANGED, | 266 NotificationType::EXTENSION_PREF_CHANGED, |
| 157 NotificationService::AllSources()); | 267 Source<Profile>(profile_)); |
| 158 | 268 |
| 159 notification_registrar_.Add(this, | 269 notification_registrar_.Add(this, |
| 160 NotificationType::EXTENSION_UNLOADED, | 270 NotificationType::EXTENSION_UNLOADED, |
| 161 NotificationService::AllSources()); | 271 Source<Profile>(profile_)); |
| 162 } | 272 } |
| 163 | 273 |
| 164 void ExtensionPrefStore::Observe(NotificationType type, | 274 void ExtensionPrefStore::Observe(NotificationType type, |
| 165 const NotificationSource& source, | 275 const NotificationSource& source, |
| 166 const NotificationDetails& details) { | 276 const NotificationDetails& details) { |
| 167 switch (type.value) { | 277 switch (type.value) { |
| 168 case NotificationType::EXTENSION_PREF_CHANGED: { | 278 case NotificationType::EXTENSION_PREF_CHANGED: { |
| 169 Profile* extension_profile = Source<Profile>(source).ptr(); | 279 ExtensionPrefStore::ExtensionPrefDetails* data = |
| 170 // The ExtensionPrefStore for the local state watches all profiles. | 280 Details<ExtensionPrefStore::ExtensionPrefDetails>(details).ptr(); |
| 171 if (!profile_ || profile_ == extension_profile) { | 281 InstallExtensionPref(data->first, data->second.first, |
| 172 ExtensionPrefStore::ExtensionPrefDetails* data = | 282 data->second.second); |
| 173 Details<ExtensionPrefStore::ExtensionPrefDetails>(details).ptr(); | |
| 174 InstallExtensionPref(data->first, data->second.first, | |
| 175 data->second.second); | |
| 176 } | |
| 177 break; | 283 break; |
| 178 } | 284 } |
| 179 case NotificationType::EXTENSION_UNLOADED: { | 285 case NotificationType::EXTENSION_UNLOADED: { |
| 180 Profile* extension_profile = Source<Profile>(source).ptr(); | |
| 181 const Extension* extension = Details<const Extension>(details).ptr(); | 286 const Extension* extension = Details<const Extension>(details).ptr(); |
| 182 // The ExtensionPrefStore for the local state watches all profiles. | 287 UninstallExtension(extension); |
| 183 if (profile_ == NULL || profile_ == extension_profile) | |
| 184 UninstallExtension(extension); | |
| 185 break; | 288 break; |
| 186 } | 289 } |
| 187 default: { | 290 default: { |
| 188 NOTREACHED(); | 291 NOTREACHED(); |
| 189 } | 292 } |
| 190 } | 293 } |
| 191 } | 294 } |
| 192 | 295 |
| 193 ExtensionPrefStore::ExtensionPrefs::ExtensionPrefs(const Extension* extension, | 296 void ExtensionPrefStore::LazyInit() const { |
| 194 PrefValueMap* values) : extension(extension), pref_values(values) {} | 297 // If profile_==NULL, this ExtensionPrefStore is for local-state. |
| 298 DCHECK(!extension_stack_.is_initialized()) | |
| 299 << "LazyInit called even though extension_stack_ is already initialized"; | |
| 195 | 300 |
| 196 ExtensionPrefStore::ExtensionPrefs::~ExtensionPrefs() { | 301 PrefService* pref_service = GetPrefService(); |
| 197 STLDeleteValues(pref_values); | 302 if (!pref_service) { |
| 198 delete pref_values; | 303 extension_stack_.init(dummy_prefs.get()); |
| 304 return; | |
| 305 } | |
| 306 pref_service->RegisterListPref(kExtensionPreferencesKey); | |
| 307 | |
| 308 scoped_ptr<ListValue> dict( | |
| 309 pref_service->GetMutableList(kExtensionPreferencesKey)); | |
| 310 | |
| 311 extension_stack_.init(dict.release()); | |
| 312 | |
| 313 for (int i = 0, n = extension_stack_.size(); i < n; ++i) { | |
| 314 DictionaryValue* pref_values = extension_stack_.get(i).pref_values(); | |
| 315 for (DictionaryValue::key_iterator iter = pref_values->begin_keys(); | |
| 316 iter != pref_values->end_keys(); ++iter) { | |
| 317 const std::string& key = *iter; | |
| 318 if (!prefs_->HasKey(key)) { | |
| 319 Value *value; | |
| 320 if (pref_values->GetWithoutPathExpansion(key, &value)) | |
| 321 prefs_->Set(key, value->DeepCopy()); | |
| 322 } | |
| 323 } | |
| 324 } | |
| 199 } | 325 } |
| OLD | NEW |