| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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/common/pref_service.h" | 5 #include "chrome/common/pref_service.h" |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" | |
| 8 #include "base/file_util.h" | |
| 9 #include "base/logging.h" | 7 #include "base/logging.h" |
| 10 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 11 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 12 #include "base/task.h" | |
| 13 #include "base/thread.h" | 10 #include "base/thread.h" |
| 14 #include "chrome/common/json_value_serializer.h" | 11 #include "chrome/common/json_value_serializer.h" |
| 15 #include "chrome/common/l10n_util.h" | 12 #include "chrome/common/l10n_util.h" |
| 16 #include "chrome/common/notification_service.h" | 13 #include "chrome/common/notification_service.h" |
| 17 #include "chrome/common/stl_util-inl.h" | 14 #include "chrome/common/stl_util-inl.h" |
| 18 #include "grit/generated_resources.h" | 15 #include "grit/generated_resources.h" |
| 19 | 16 |
| 20 namespace { | 17 namespace { |
| 21 | 18 |
| 22 // The number of milliseconds we'll wait to do a write of chrome prefs to disk. | |
| 23 // This lets us batch together write operations. | |
| 24 static const int kCommitIntervalMs = 10000; | |
| 25 | |
| 26 // Replaces the given file's content with the given data. This allows the | |
| 27 // preferences to be written to disk on a background thread. | |
| 28 class SaveLaterTask : public Task { | |
| 29 public: | |
| 30 SaveLaterTask(const FilePath& file_name, | |
| 31 const std::string& data) | |
| 32 : file_name_(file_name), | |
| 33 data_(data) { | |
| 34 } | |
| 35 | |
| 36 void Run() { | |
| 37 // Write the data to a temp file then rename to avoid data loss if we crash | |
| 38 // while writing the file. | |
| 39 FilePath tmp_file_name(file_name_.value() + FILE_PATH_LITERAL(".tmp")); | |
| 40 int bytes_written = file_util::WriteFile(tmp_file_name, data_.c_str(), | |
| 41 static_cast<int>(data_.length())); | |
| 42 if (bytes_written != -1) { | |
| 43 if (!file_util::Move(tmp_file_name, file_name_)) { | |
| 44 // Rename failed. Try again on the off chance someone has locked either | |
| 45 // file and hope we're successful the second time through. | |
| 46 bool move_result = file_util::Move(tmp_file_name, file_name_); | |
| 47 DCHECK(move_result); | |
| 48 } | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 private: | |
| 53 FilePath file_name_; | |
| 54 std::string data_; | |
| 55 | |
| 56 DISALLOW_COPY_AND_ASSIGN(SaveLaterTask); | |
| 57 }; | |
| 58 | |
| 59 // A helper function for RegisterLocalized*Pref that creates a Value* based on | 19 // A helper function for RegisterLocalized*Pref that creates a Value* based on |
| 60 // the string value in the locale dll. Because we control the values in a | 20 // the string value in the locale dll. Because we control the values in a |
| 61 // locale dll, this should always return a Value of the appropriate type. | 21 // locale dll, this should always return a Value of the appropriate type. |
| 62 Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { | 22 Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { |
| 63 std::wstring resource_string = l10n_util::GetString(message_id); | 23 std::wstring resource_string = l10n_util::GetString(message_id); |
| 64 DCHECK(!resource_string.empty()); | 24 DCHECK(!resource_string.empty()); |
| 65 switch (type) { | 25 switch (type) { |
| 66 case Value::TYPE_BOOLEAN: { | 26 case Value::TYPE_BOOLEAN: { |
| 67 if (L"true" == resource_string) | 27 if (L"true" == resource_string) |
| 68 return Value::CreateBooleanValue(true); | 28 return Value::CreateBooleanValue(true); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 92 NOTREACHED() << | 52 NOTREACHED() << |
| 93 "list and dictionary types can not have default locale values"; | 53 "list and dictionary types can not have default locale values"; |
| 94 } | 54 } |
| 95 } | 55 } |
| 96 NOTREACHED(); | 56 NOTREACHED(); |
| 97 return Value::CreateNullValue(); | 57 return Value::CreateNullValue(); |
| 98 } | 58 } |
| 99 | 59 |
| 100 } // namespace | 60 } // namespace |
| 101 | 61 |
| 102 PrefService::PrefService() | 62 PrefService::PrefService(const FilePath& pref_filename, |
| 63 const base::Thread* backend_thread) |
| 103 : persistent_(new DictionaryValue), | 64 : persistent_(new DictionaryValue), |
| 104 transient_(new DictionaryValue), | 65 transient_(new DictionaryValue), |
| 105 save_preferences_factory_(NULL) { | 66 writer_(pref_filename, backend_thread) { |
| 106 } | 67 ReloadPersistentPrefs(); |
| 107 | |
| 108 PrefService::PrefService(const FilePath& pref_filename) | |
| 109 : persistent_(new DictionaryValue), | |
| 110 transient_(new DictionaryValue), | |
| 111 pref_filename_(pref_filename), | |
| 112 ALLOW_THIS_IN_INITIALIZER_LIST(save_preferences_factory_(this)) { | |
| 113 LoadPersistentPrefs(pref_filename_); | |
| 114 } | 68 } |
| 115 | 69 |
| 116 PrefService::~PrefService() { | 70 PrefService::~PrefService() { |
| 117 DCHECK(CalledOnValidThread()); | 71 DCHECK(CalledOnValidThread()); |
| 118 | 72 |
| 119 // Verify that there are no pref observers when we shut down. | 73 // Verify that there are no pref observers when we shut down. |
| 120 for (PrefObserverMap::iterator it = pref_observers_.begin(); | 74 for (PrefObserverMap::iterator it = pref_observers_.begin(); |
| 121 it != pref_observers_.end(); ++it) { | 75 it != pref_observers_.end(); ++it) { |
| 122 NotificationObserverList::Iterator obs_iterator(*(it->second)); | 76 NotificationObserverList::Iterator obs_iterator(*(it->second)); |
| 123 if (obs_iterator.GetNext()) { | 77 if (obs_iterator.GetNext()) { |
| 124 LOG(WARNING) << "pref observer found at shutdown " << it->first; | 78 LOG(WARNING) << "pref observer found at shutdown " << it->first; |
| 125 } | 79 } |
| 126 } | 80 } |
| 127 | 81 |
| 128 STLDeleteContainerPointers(prefs_.begin(), prefs_.end()); | 82 STLDeleteContainerPointers(prefs_.begin(), prefs_.end()); |
| 129 prefs_.clear(); | 83 prefs_.clear(); |
| 130 STLDeleteContainerPairSecondPointers(pref_observers_.begin(), | 84 STLDeleteContainerPairSecondPointers(pref_observers_.begin(), |
| 131 pref_observers_.end()); | 85 pref_observers_.end()); |
| 132 pref_observers_.clear(); | 86 pref_observers_.clear(); |
| 133 } | 87 } |
| 134 | 88 |
| 135 bool PrefService::LoadPersistentPrefs(const FilePath& file_path) { | 89 bool PrefService::ReloadPersistentPrefs() { |
| 136 DCHECK(!file_path.empty()); | |
| 137 DCHECK(CalledOnValidThread()); | 90 DCHECK(CalledOnValidThread()); |
| 138 | 91 |
| 139 JSONFileValueSerializer serializer(file_path); | 92 JSONFileValueSerializer serializer(writer_.path()); |
| 140 scoped_ptr<Value> root(serializer.Deserialize(NULL)); | 93 scoped_ptr<Value> root(serializer.Deserialize(NULL)); |
| 141 if (!root.get()) | 94 if (!root.get()) |
| 142 return false; | 95 return false; |
| 143 | 96 |
| 144 // Preferences should always have a dictionary root. | 97 // Preferences should always have a dictionary root. |
| 145 if (!root->IsType(Value::TYPE_DICTIONARY)) | 98 if (!root->IsType(Value::TYPE_DICTIONARY)) |
| 146 return false; | 99 return false; |
| 147 | 100 |
| 148 persistent_.reset(static_cast<DictionaryValue*>(root.release())); | 101 persistent_.reset(static_cast<DictionaryValue*>(root.release())); |
| 149 return true; | |
| 150 } | |
| 151 | |
| 152 void PrefService::ReloadPersistentPrefs() { | |
| 153 DCHECK(CalledOnValidThread()); | |
| 154 | |
| 155 JSONFileValueSerializer serializer(pref_filename_); | |
| 156 scoped_ptr<Value> root(serializer.Deserialize(NULL)); | |
| 157 if (!root.get()) | |
| 158 return; | |
| 159 | |
| 160 // Preferences should always have a dictionary root. | |
| 161 if (!root->IsType(Value::TYPE_DICTIONARY)) | |
| 162 return; | |
| 163 | |
| 164 persistent_.reset(static_cast<DictionaryValue*>(root.release())); | |
| 165 for (PreferenceSet::iterator it = prefs_.begin(); | 102 for (PreferenceSet::iterator it = prefs_.begin(); |
| 166 it != prefs_.end(); ++it) { | 103 it != prefs_.end(); ++it) { |
| 167 (*it)->root_pref_ = persistent_.get(); | 104 (*it)->root_pref_ = persistent_.get(); |
| 168 } | 105 } |
| 169 } | |
| 170 | 106 |
| 171 bool PrefService::SavePersistentPrefs(base::Thread* thread) const { | |
| 172 DCHECK(!pref_filename_.empty()); | |
| 173 DCHECK(CalledOnValidThread()); | |
| 174 | |
| 175 // TODO(tc): Do we want to prune webkit preferences that match the default | |
| 176 // value? | |
| 177 std::string data; | |
| 178 JSONStringValueSerializer serializer(&data); | |
| 179 serializer.set_pretty_print(true); | |
| 180 if (!serializer.Serialize(*(persistent_.get()))) | |
| 181 return false; | |
| 182 | |
| 183 SaveLaterTask* task = new SaveLaterTask(pref_filename_, data); | |
| 184 if (thread != NULL) { | |
| 185 // We can use the background thread, it will take ownership of the task. | |
| 186 thread->message_loop()->PostTask(FROM_HERE, task); | |
| 187 } else { | |
| 188 // In unit test mode, we have no background thread, just execute. | |
| 189 task->Run(); | |
| 190 delete task; | |
| 191 } | |
| 192 return true; | 107 return true; |
| 193 } | 108 } |
| 194 | 109 |
| 195 void PrefService::ScheduleSavePersistentPrefs(base::Thread* thread) { | 110 bool PrefService::SavePersistentPrefs() { |
| 196 if (!save_preferences_factory_.empty()) | 111 DCHECK(CalledOnValidThread()); |
| 197 return; | |
| 198 | 112 |
| 199 MessageLoop::current()->PostDelayedTask(FROM_HERE, | 113 std::string data; |
| 200 save_preferences_factory_.NewRunnableMethod( | 114 if (!SerializePrefData(&data)) |
| 201 &PrefService::SavePersistentPrefs, thread), | 115 return false; |
| 202 kCommitIntervalMs); | 116 |
| 117 writer_.WriteNow(data); |
| 118 return true; |
| 119 } |
| 120 |
| 121 bool PrefService::ScheduleSavePersistentPrefs() { |
| 122 DCHECK(CalledOnValidThread()); |
| 123 |
| 124 std::string data; |
| 125 if (!SerializePrefData(&data)) |
| 126 return false; |
| 127 |
| 128 writer_.ScheduleWrite(data); |
| 129 return true; |
| 203 } | 130 } |
| 204 | 131 |
| 205 void PrefService::RegisterBooleanPref(const wchar_t* path, | 132 void PrefService::RegisterBooleanPref(const wchar_t* path, |
| 206 bool default_value) { | 133 bool default_value) { |
| 207 Preference* pref = new Preference(persistent_.get(), path, | 134 Preference* pref = new Preference(persistent_.get(), path, |
| 208 Value::CreateBooleanValue(default_value)); | 135 Value::CreateBooleanValue(default_value)); |
| 209 RegisterPreference(pref); | 136 RegisterPreference(pref); |
| 210 } | 137 } |
| 211 | 138 |
| 212 void PrefService::RegisterIntegerPref(const wchar_t* path, | 139 void PrefService::RegisterIntegerPref(const wchar_t* path, |
| (...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 | 637 |
| 711 NotificationObserverList::Iterator it(*(observer_iterator->second)); | 638 NotificationObserverList::Iterator it(*(observer_iterator->second)); |
| 712 NotificationObserver* observer; | 639 NotificationObserver* observer; |
| 713 while ((observer = it.GetNext()) != NULL) { | 640 while ((observer = it.GetNext()) != NULL) { |
| 714 observer->Observe(NotificationType::PREF_CHANGED, | 641 observer->Observe(NotificationType::PREF_CHANGED, |
| 715 Source<PrefService>(this), | 642 Source<PrefService>(this), |
| 716 Details<std::wstring>(&path_str)); | 643 Details<std::wstring>(&path_str)); |
| 717 } | 644 } |
| 718 } | 645 } |
| 719 | 646 |
| 647 bool PrefService::SerializePrefData(std::string* output) const { |
| 648 // TODO(tc): Do we want to prune webkit preferences that match the default |
| 649 // value? |
| 650 JSONStringValueSerializer serializer(output); |
| 651 serializer.set_pretty_print(true); |
| 652 return serializer.Serialize(*(persistent_.get())); |
| 653 } |
| 654 |
| 720 /////////////////////////////////////////////////////////////////////////////// | 655 /////////////////////////////////////////////////////////////////////////////// |
| 721 // PrefService::Preference | 656 // PrefService::Preference |
| 722 | 657 |
| 723 PrefService::Preference::Preference(DictionaryValue* root_pref, | 658 PrefService::Preference::Preference(DictionaryValue* root_pref, |
| 724 const wchar_t* name, | 659 const wchar_t* name, |
| 725 Value* default_value) | 660 Value* default_value) |
| 726 : type_(Value::TYPE_NULL), | 661 : type_(Value::TYPE_NULL), |
| 727 name_(name), | 662 name_(name), |
| 728 default_value_(default_value), | 663 default_value_(default_value), |
| 729 root_pref_(root_pref) { | 664 root_pref_(root_pref) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 752 } | 687 } |
| 753 | 688 |
| 754 // Pref not found, just return the app default. | 689 // Pref not found, just return the app default. |
| 755 return default_value_.get(); | 690 return default_value_.get(); |
| 756 } | 691 } |
| 757 | 692 |
| 758 bool PrefService::Preference::IsDefaultValue() const { | 693 bool PrefService::Preference::IsDefaultValue() const { |
| 759 DCHECK(default_value_.get()); | 694 DCHECK(default_value_.get()); |
| 760 return default_value_->Equals(GetValue()); | 695 return default_value_->Equals(GetValue()); |
| 761 } | 696 } |
| OLD | NEW |