blob: 17d455bdd64efd9bedbcdfd6d1a8ae766fc16d38 [file] [log] [blame]
initial.commit09911bf2008-07-26 23:55:291// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "chrome/common/pref_service.h"
31
32#include "base/file_util.h"
33#include "base/logging.h"
34#include "base/message_loop.h"
35#include "base/string_util.h"
36#include "base/task.h"
37#include "base/thread.h"
38#include "chrome/common/json_value_serializer.h"
39#include "chrome/common/l10n_util.h"
40#include "chrome/common/notification_service.h"
41#include "chrome/common/stl_util-inl.h"
42
43#include "generated_resources.h"
44
45namespace {
46
47// The number of milliseconds we'll wait to do a write of chrome prefs to disk.
48// This lets us batch together write operations.
49static const int kCommitIntervalMs = 10000;
50
51// Replaces the given file's content with the given data. This allows the
52// preferences to be written to disk on a background thread.
53class SaveLaterTask : public Task {
54 public:
55 SaveLaterTask(const std::wstring& file_name,
56 const std::string& data)
57 : file_name_(file_name),
58 data_(data) {
59 }
60
61 void Run() {
62 // Write the data to a temp file then rename to avoid data loss if we crash
63 // while writing the file.
64 std::wstring tmp_file_name = file_name_ + L".tmp";
65 int bytes_written = file_util::WriteFile(tmp_file_name, data_.c_str(),
66 static_cast<int>(data_.length()));
67 if (bytes_written != -1) {
68 MoveFileEx(tmp_file_name.c_str(), file_name_.c_str(),
69 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
70 }
71 }
72
73 private:
74 std::wstring file_name_;
75 std::string data_;
76
77 DISALLOW_EVIL_CONSTRUCTORS(SaveLaterTask);
78};
79
80// A helper function for RegisterLocalized*Pref that creates a Value* based on
81// the string value in the locale dll. Because we control the values in a
82// locale dll, this should always return a Value of the appropriate type.
83Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) {
84 std::wstring resource_string = l10n_util::GetString(message_id);
85 DCHECK(!resource_string.empty());
86 switch (type) {
87 case Value::TYPE_BOOLEAN: {
88 if (L"true" == resource_string)
89 return Value::CreateBooleanValue(true);
90 if (L"false" == resource_string)
91 return Value::CreateBooleanValue(false);
92 break;
93 }
94
95 case Value::TYPE_INTEGER: {
96 int num_int = 0;
97 int parsed_values = swscanf_s(resource_string.c_str(), L"%d", &num_int);
98 // This is a trusted value (comes from our locale dll), so it should
99 // successfully parse.
100 DCHECK(parsed_values == 1);
101 return Value::CreateIntegerValue(num_int);
102 break;
103 }
104
105 case Value::TYPE_REAL: {
106 double num_double = 0.0;
107 int parsed_values = swscanf_s(resource_string.c_str(), L"%lf",
108 &num_double);
109 // This is a trusted value (comes from our locale dll), so it should
110 // successfully parse.
111 DCHECK(parsed_values == 1);
112 return Value::CreateRealValue(num_double);
113 break;
114 }
115
116 case Value::TYPE_STRING: {
117 return Value::CreateStringValue(resource_string);
118 break;
119 }
120
121 default: {
122 DCHECK(false) <<
123 "list and dictionary types can not have default locale values";
124 }
125 }
126 NOTREACHED();
127 return Value::CreateNullValue();
128}
129
130} // namespace
131
132PrefService::PrefService()
133 : persistent_(new DictionaryValue),
134 transient_(new DictionaryValue),
135 save_preferences_factory_(NULL) {
136}
137
138PrefService::PrefService(const std::wstring& pref_filename)
139 : persistent_(new DictionaryValue),
140 transient_(new DictionaryValue),
141 pref_filename_(pref_filename),
142#pragma warning(suppress: 4355) // Okay to pass "this" here.
143 save_preferences_factory_(this) {
144 LoadPersistentPrefs(pref_filename_);
145}
146
147PrefService::~PrefService() {
148 DCHECK(CalledOnValidThread());
149
150 // Verify that there are no pref observers when we shut down.
151 for (PrefObserverMap::iterator it = pref_observers_.begin();
152 it != pref_observers_.end(); ++it) {
153 NotificationObserverList::Iterator obs_iterator(*(it->second));
154 if (obs_iterator.GetNext()) {
155 LOG(WARNING) << "pref observer found at shutdown " << it->first;
156 }
157 }
158
159 STLDeleteContainerPointers(prefs_.begin(), prefs_.end());
160 prefs_.clear();
161 STLDeleteContainerPairSecondPointers(pref_observers_.begin(),
162 pref_observers_.end());
163 pref_observers_.clear();
164}
165
166bool PrefService::LoadPersistentPrefs(const std::wstring& file_path) {
167 DCHECK(!file_path.empty());
168 DCHECK(CalledOnValidThread());
169
170 JSONFileValueSerializer serializer(file_path);
171 Value* root = NULL;
172 if (serializer.Deserialize(&root)) {
173 // Preferences should always have a dictionary root.
174 if (!root->IsType(Value::TYPE_DICTIONARY)) {
175 delete root;
176 return false;
177 }
178
179 persistent_.reset(static_cast<DictionaryValue*>(root));
180 return true;
181 }
182
183 return false;
184}
185
186void PrefService::ReloadPersistentPrefs() {
187 DCHECK(CalledOnValidThread());
188
189 JSONFileValueSerializer serializer(pref_filename_);
190 Value* root;
191 if (serializer.Deserialize(&root)) {
192 // Preferences should always have a dictionary root.
193 if (!root->IsType(Value::TYPE_DICTIONARY)) {
194 delete root;
195 return;
196 }
197
198 persistent_.reset(static_cast<DictionaryValue*>(root));
199 for (PreferenceSet::iterator it = prefs_.begin();
200 it != prefs_.end(); ++it) {
201 (*it)->root_pref_ = persistent_.get();
202 }
203 }
204}
205
206bool PrefService::SavePersistentPrefs(Thread* thread) const {
207 DCHECK(!pref_filename_.empty());
208 DCHECK(CalledOnValidThread());
209
210 // TODO(tc): Do we want to prune webkit preferences that match the default
211 // value?
212 std::string data;
213 JSONStringValueSerializer serializer(&data);
214 serializer.set_pretty_print(true);
215 if (!serializer.Serialize(*(persistent_.get())))
216 return false;
217
218 SaveLaterTask* task = new SaveLaterTask(pref_filename_, data);
219 if (thread != NULL) {
220 // We can use the background thread, it will take ownership of the task.
221 thread->message_loop()->PostTask(FROM_HERE, task);
222 } else {
223 // In unit test mode, we have no background thread, just execute.
224 task->Run();
225 delete task;
226 }
227 return true;
228}
229
230void PrefService::ScheduleSavePersistentPrefs(Thread* thread) {
231 if (!save_preferences_factory_.empty())
232 return;
233
234 MessageLoop::current()->PostDelayedTask(FROM_HERE,
235 save_preferences_factory_.NewRunnableMethod(
236 &PrefService::SavePersistentPrefs, thread),
237 kCommitIntervalMs);
238}
239
240void PrefService::RegisterBooleanPref(const wchar_t* path,
241 bool default_value) {
242 Preference* pref = new Preference(persistent_.get(), path,
243 Value::CreateBooleanValue(default_value));
244 RegisterPreference(pref);
245}
246
247void PrefService::RegisterIntegerPref(const wchar_t* path,
248 int default_value) {
249 Preference* pref = new Preference(persistent_.get(), path,
250 Value::CreateIntegerValue(default_value));
251 RegisterPreference(pref);
252}
253
254void PrefService::RegisterRealPref(const wchar_t* path,
255 double default_value) {
256 Preference* pref = new Preference(persistent_.get(), path,
257 Value::CreateRealValue(default_value));
258 RegisterPreference(pref);
259}
260
261void PrefService::RegisterStringPref(const wchar_t* path,
262 const std::wstring& default_value) {
263 Preference* pref = new Preference(persistent_.get(), path,
264 Value::CreateStringValue(default_value));
265 RegisterPreference(pref);
266}
267
268void PrefService::RegisterListPref(const wchar_t* path) {
269 Preference* pref = new Preference(persistent_.get(), path,
270 new ListValue);
271 RegisterPreference(pref);
272}
273
274void PrefService::RegisterDictionaryPref(const wchar_t* path) {
275 Preference* pref = new Preference(persistent_.get(), path,
276 new DictionaryValue());
277 RegisterPreference(pref);
278}
279
280void PrefService::RegisterLocalizedBooleanPref(const wchar_t* path,
281 int locale_default_message_id) {
282 Preference* pref = new Preference(persistent_.get(), path,
283 CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id));
284 RegisterPreference(pref);
285}
286
287void PrefService::RegisterLocalizedIntegerPref(const wchar_t* path,
288 int locale_default_message_id) {
289 Preference* pref = new Preference(persistent_.get(), path,
290 CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id));
291 RegisterPreference(pref);
292}
293
294void PrefService::RegisterLocalizedRealPref(const wchar_t* path,
295 int locale_default_message_id) {
296 Preference* pref = new Preference(persistent_.get(), path,
297 CreateLocaleDefaultValue(Value::TYPE_REAL, locale_default_message_id));
298 RegisterPreference(pref);
299}
300
301void PrefService::RegisterLocalizedStringPref(const wchar_t* path,
302 int locale_default_message_id) {
303 Preference* pref = new Preference(persistent_.get(), path,
304 CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id));
305 RegisterPreference(pref);
306}
307
308bool PrefService::IsPrefRegistered(const wchar_t* path) {
309 DCHECK(CalledOnValidThread());
310 // TODO(tc): We can remove this method and just use FindPreference.
311 return FindPreference(path) ? true : false;
312}
313
314bool PrefService::GetBoolean(const wchar_t* path) const {
315 DCHECK(CalledOnValidThread());
316
317 bool result = false;
318 if (transient_->GetBoolean(path, &result))
319 return result;
320
321 const Preference* pref = FindPreference(path);
322 if (!pref) {
323 DCHECK(false) << "Trying to read an unregistered pref: " << path;
324 return result;
325 }
326 bool rv = pref->GetValue()->GetAsBoolean(&result);
327 DCHECK(rv);
328 return result;
329}
330
331int PrefService::GetInteger(const wchar_t* path) const {
332 DCHECK(CalledOnValidThread());
333
334 int result = 0;
335 if (transient_->GetInteger(path, &result))
336 return result;
337
338 const Preference* pref = FindPreference(path);
339 if (!pref) {
340 DCHECK(false) << "Trying to read an unregistered pref: " << path;
341 return result;
342 }
343 bool rv = pref->GetValue()->GetAsInteger(&result);
344 DCHECK(rv);
345 return result;
346}
347
348double PrefService::GetReal(const wchar_t* path) const {
349 DCHECK(CalledOnValidThread());
350
351 double result = 0.0;
352 if (transient_->GetReal(path, &result))
353 return result;
354
355 const Preference* pref = FindPreference(path);
356 if (!pref) {
357 DCHECK(false) << "Trying to read an unregistered pref: " << path;
358 return result;
359 }
360 bool rv = pref->GetValue()->GetAsReal(&result);
361 DCHECK(rv);
362 return result;
363}
364
365std::wstring PrefService::GetString(const wchar_t* path) const {
366 DCHECK(CalledOnValidThread());
367
368 std::wstring result;
369 if (transient_->GetString(path, &result))
370 return result;
371
372 const Preference* pref = FindPreference(path);
373 if (!pref) {
374 DCHECK(false) << "Trying to read an unregistered pref: " << path;
375 return result;
376 }
377 bool rv = pref->GetValue()->GetAsString(&result);
378 DCHECK(rv);
379 return result;
380}
381
382bool PrefService::HasPrefPath(const wchar_t* path) const {
383 Value* value = NULL;
384 return (transient_->Get(path, &value) || persistent_->Get(path, &value));
385}
386
387const PrefService::Preference* PrefService::FindPreference(
388 const wchar_t* pref_name) const {
389 DCHECK(CalledOnValidThread());
390 Preference p(NULL, pref_name, NULL);
391 PreferenceSet::const_iterator it = prefs_.find(&p);
392 return it == prefs_.end() ? NULL : *it;
393}
394
395const DictionaryValue* PrefService::GetDictionary(const wchar_t* path) const {
396 DCHECK(CalledOnValidThread());
397
398 DictionaryValue* result = NULL;
399 if (transient_->GetDictionary(path, &result))
400 return result;
401
402 const Preference* pref = FindPreference(path);
403 if (!pref) {
404 DCHECK(false) << "Trying to read an unregistered pref: " << path;
405 return NULL;
406 }
407 const Value* value = pref->GetValue();
408 if (value->GetType() == Value::TYPE_NULL)
409 return NULL;
410 return static_cast<const DictionaryValue*>(value);
411}
412
413const ListValue* PrefService::GetList(const wchar_t* path) const {
414 DCHECK(CalledOnValidThread());
415
416 ListValue* result = NULL;
417 if (transient_->GetList(path, &result))
418 return result;
419
420 const Preference* pref = FindPreference(path);
421 if (!pref) {
422 DCHECK(false) << "Trying to read an unregistered pref: " << path;
423 return NULL;
424 }
425 const Value* value = pref->GetValue();
426 if (value->GetType() == Value::TYPE_NULL)
427 return NULL;
428 return static_cast<const ListValue*>(value);
429}
430
431void PrefService::AddPrefObserver(const wchar_t* path,
432 NotificationObserver* obs) {
433 DCHECK(CalledOnValidThread());
434
435 const Preference* pref = FindPreference(path);
436 if (!pref) {
437 DCHECK(false) << "Trying to add an observer for an unregistered pref: "
438 << path;
439 return;
440 }
441
442 // Get the pref observer list associated with the path.
443 NotificationObserverList* observer_list = NULL;
444 PrefObserverMap::iterator observer_iterator = pref_observers_.find(path);
445 if (observer_iterator == pref_observers_.end()) {
446 observer_list = new NotificationObserverList;
447 pref_observers_[path] = observer_list;
448 } else {
449 observer_list = observer_iterator->second;
450 }
451
452 // Verify that this observer doesn't already exist.
453 NotificationObserverList::Iterator it(*observer_list);
454 NotificationObserver* existing_obs;
455 while (existing_obs = it.GetNext()) {
456 DCHECK(existing_obs != obs) << path << " observer already registered";
457 if (existing_obs == obs)
458 return;
459 }
460
461 // Ok, safe to add the pref observer.
462 observer_list->AddObserver(obs);
463}
464
465void PrefService::RemovePrefObserver(const wchar_t* path,
466 NotificationObserver* obs) {
467 DCHECK(CalledOnValidThread());
468
469 PrefObserverMap::iterator observer_iterator = pref_observers_.find(path);
470 if (observer_iterator == pref_observers_.end()) {
471 return;
472 }
473
474 NotificationObserverList* observer_list = observer_iterator->second;
475 observer_list->RemoveObserver(obs);
476}
477
478void PrefService::RegisterPreference(Preference* pref) {
479 DCHECK(CalledOnValidThread());
480
481 if (FindPreference(pref->name().c_str())) {
482 DCHECK(false) << "Tried to register duplicate pref " << pref->name();
483 delete pref;
484 return;
485 }
486 prefs_.insert(pref);
487}
488
489void PrefService::ClearPref(const wchar_t* path) {
490 DCHECK(CalledOnValidThread());
491
492 const Preference* pref = FindPreference(path);
493 if (!pref) {
494 DCHECK(false) << "Trying to clear an unregistered pref: " << path;
495 return;
496 }
497
498 transient_->Remove(path, NULL);
499 Value* value;
500 bool has_old_value = persistent_->Get(path, &value);
501 persistent_->Remove(path, NULL);
502
503 if (has_old_value)
504 FireObservers(path);
505}
506
507void PrefService::SetBoolean(const wchar_t* path, bool value) {
508 DCHECK(CalledOnValidThread());
509
510 const Preference* pref = FindPreference(path);
511 if (!pref) {
512 DCHECK(false) << "Trying to write an unregistered pref: " << path;
513 return;
514 }
515 if (pref->type() != Value::TYPE_BOOLEAN) {
516 DCHECK(false) << "Wrong type for SetBoolean: " << path;
517 return;
518 }
519
520 scoped_ptr<Value> old_value(GetPrefCopy(path));
521 bool rv = persistent_->SetBoolean(path, value);
522 DCHECK(rv);
523
524 FireObserversIfChanged(path, old_value.get());
525}
526
527void PrefService::SetInteger(const wchar_t* path, int value) {
528 DCHECK(CalledOnValidThread());
529
530 const Preference* pref = FindPreference(path);
531 if (!pref) {
532 DCHECK(false) << "Trying to write an unregistered pref: " << path;
533 return;
534 }
535 if (pref->type() != Value::TYPE_INTEGER) {
536 DCHECK(false) << "Wrong type for SetInteger: " << path;
537 return;
538 }
539
540 scoped_ptr<Value> old_value(GetPrefCopy(path));
541 bool rv = persistent_->SetInteger(path, value);
542 DCHECK(rv);
543
544 FireObserversIfChanged(path, old_value.get());
545}
546
547void PrefService::SetReal(const wchar_t* path, double value) {
548 DCHECK(CalledOnValidThread());
549
550 const Preference* pref = FindPreference(path);
551 if (!pref) {
552 DCHECK(false) << "Trying to write an unregistered pref: " << path;
553 return;
554 }
555 if (pref->type() != Value::TYPE_REAL) {
556 DCHECK(false) << "Wrong type for SetReal: " << path;
557 return;
558 }
559
560 scoped_ptr<Value> old_value(GetPrefCopy(path));
561 bool rv = persistent_->SetReal(path, value);
562 DCHECK(rv);
563
564 FireObserversIfChanged(path, old_value.get());
565}
566
567void PrefService::SetString(const wchar_t* path, const std::wstring& value) {
568 DCHECK(CalledOnValidThread());
569
570 const Preference* pref = FindPreference(path);
571 if (!pref) {
572 DCHECK(false) << "Trying to write an unregistered pref: " << path;
573 return;
574 }
575 if (pref->type() != Value::TYPE_STRING) {
576 DCHECK(false) << "Wrong type for SetString: " << path;
577 return;
578 }
579
580 scoped_ptr<Value> old_value(GetPrefCopy(path));
581 bool rv = persistent_->SetString(path, value);
582 DCHECK(rv);
583
584 FireObserversIfChanged(path, old_value.get());
585}
586
587DictionaryValue* PrefService::GetMutableDictionary(const wchar_t* path) {
588 DCHECK(CalledOnValidThread());
589
590 const Preference* pref = FindPreference(path);
591 if (!pref) {
592 DCHECK(false) << "Trying to get an unregistered pref: " << path;
593 return NULL;
594 }
595 if (pref->type() != Value::TYPE_DICTIONARY) {
596 DCHECK(false) << "Wrong type for GetMutableDictionary: " << path;
597 return NULL;
598 }
599
600 DictionaryValue* dict = NULL;
601 bool rv = persistent_->GetDictionary(path, &dict);
602 if (!rv) {
603 dict = new DictionaryValue;
604 rv = persistent_->Set(path, dict);
605 DCHECK(rv);
606 }
607 return dict;
608}
609
610ListValue* PrefService::GetMutableList(const wchar_t* path) {
611 DCHECK(CalledOnValidThread());
612
613 const Preference* pref = FindPreference(path);
614 if (!pref) {
615 DCHECK(false) << "Trying to get an unregistered pref: " << path;
616 return NULL;
617 }
618 if (pref->type() != Value::TYPE_LIST) {
619 DCHECK(false) << "Wrong type for GetMutableList: " << path;
620 return NULL;
621 }
622
623 ListValue* list = NULL;
624 bool rv = persistent_->GetList(path, &list);
625 if (!rv) {
626 list = new ListValue;
627 rv = persistent_->Set(path, list);
628 DCHECK(rv);
629 }
630 return list;
631}
632
633Value* PrefService::GetPrefCopy(const wchar_t* path) {
634 DCHECK(CalledOnValidThread());
635
636 const Preference* pref = FindPreference(path);
637 DCHECK(pref);
638 return pref->GetValue()->DeepCopy();
639}
640
641void PrefService::FireObserversIfChanged(const wchar_t* path,
642 const Value* old_value) {
643 Value* new_value = NULL;
644 persistent_->Get(path, &new_value);
645 if (!old_value->Equals(new_value))
646 FireObservers(path);
647}
648
649void PrefService::FireObservers(const wchar_t* path) {
650 DCHECK(CalledOnValidThread());
651
652 // Convert path to a std::wstring because the Details constructor requires a
653 // class.
654 std::wstring path_str(path);
655 PrefObserverMap::iterator observer_iterator = pref_observers_.find(path_str);
656 if (observer_iterator == pref_observers_.end())
657 return;
658
659 NotificationObserverList::Iterator it(*(observer_iterator->second));
660 NotificationObserver* observer;
661 while (observer = it.GetNext()) {
662 observer->Observe(NOTIFY_PREF_CHANGED,
663 Source<PrefService>(this),
664 Details<std::wstring>(&path_str));
665 }
666}
667
668///////////////////////////////////////////////////////////////////////////////
669// PrefService::Preference
670
671PrefService::Preference::Preference(DictionaryValue* root_pref,
672 const wchar_t* name,
673 Value* default_value)
674 : root_pref_(root_pref),
675 name_(name),
676 default_value_(default_value),
677 type_(Value::TYPE_NULL) {
678 DCHECK(name);
679
680 if (default_value) {
681 type_ = default_value->GetType();
682 DCHECK(type_ != Value::TYPE_NULL && type_ != Value::TYPE_BINARY) <<
683 "invalid preference type: " << type_;
684 }
685
686 // We set the default value of lists and dictionaries to be null so it's
687 // easier for callers to check for empty list/dict prefs.
688 if (Value::TYPE_LIST == type_ || Value::TYPE_DICTIONARY == type_)
689 default_value_.reset(Value::CreateNullValue());
690}
691
692const Value* PrefService::Preference::GetValue() const {
693 DCHECK(NULL != root_pref_) <<
694 "Must register pref before getting its value";
695
696 Value* temp_value = NULL;
697 if (root_pref_->Get(name_.c_str(), &temp_value) &&
698 temp_value->GetType() == type_) {
699 return temp_value;
700 }
701
702 // Pref not found, just return the app default.
703 return default_value_.get();
704}
705
706bool PrefService::Preference::IsDefaultValue() const {
707 DCHECK(default_value_.get());
708 return default_value_->Equals(GetValue());
709}