Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Side by Side Diff: chrome/browser/extensions/extension_pref_store.cc

Issue 4852002: Fix for Bug 50726 "Save extension list and "winning" prefs from extensions" (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Fixed whitespaces Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698