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

Side by Side Diff: chrome/common/json_pref_store.cc

Issue 11243002: Move the bits of Prefs where production code has only trivially easy (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments, fix gypi problem Created 8 years, 2 months 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
« no previous file with comments | « chrome/common/json_pref_store.h ('k') | chrome/common/json_pref_store_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/common/json_pref_store.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/file_util.h"
12 #include "base/json/json_file_value_serializer.h"
13 #include "base/json/json_string_value_serializer.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop_proxy.h"
16 #include "base/values.h"
17
18 namespace {
19
20 // Some extensions we'll tack on to copies of the Preferences files.
21 const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
22
23 // Differentiates file loading between origin thread and passed
24 // (aka file) thread.
25 class FileThreadDeserializer
26 : public base::RefCountedThreadSafe<FileThreadDeserializer> {
27 public:
28 FileThreadDeserializer(JsonPrefStore* delegate,
29 base::MessageLoopProxy* file_loop_proxy)
30 : no_dir_(false),
31 error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
32 delegate_(delegate),
33 file_loop_proxy_(file_loop_proxy),
34 origin_loop_proxy_(base::MessageLoopProxy::current()) {
35 }
36
37 void Start(const FilePath& path) {
38 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
39 file_loop_proxy_->PostTask(
40 FROM_HERE,
41 base::Bind(&FileThreadDeserializer::ReadFileAndReport,
42 this, path));
43 }
44
45 // Deserializes JSON on the file thread.
46 void ReadFileAndReport(const FilePath& path) {
47 DCHECK(file_loop_proxy_->BelongsToCurrentThread());
48
49 value_.reset(DoReading(path, &error_, &no_dir_));
50
51 origin_loop_proxy_->PostTask(
52 FROM_HERE,
53 base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this));
54 }
55
56 // Reports deserialization result on the origin thread.
57 void ReportOnOriginThread() {
58 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
59 delegate_->OnFileRead(value_.release(), error_, no_dir_);
60 }
61
62 static Value* DoReading(const FilePath& path,
63 PersistentPrefStore::PrefReadError* error,
64 bool* no_dir) {
65 int error_code;
66 std::string error_msg;
67 JSONFileValueSerializer serializer(path);
68 Value* value = serializer.Deserialize(&error_code, &error_msg);
69 HandleErrors(value, path, error_code, error_msg, error);
70 *no_dir = !file_util::PathExists(path.DirName());
71 return value;
72 }
73
74 static void HandleErrors(const Value* value,
75 const FilePath& path,
76 int error_code,
77 const std::string& error_msg,
78 PersistentPrefStore::PrefReadError* error);
79
80 private:
81 friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
82 ~FileThreadDeserializer() {}
83
84 bool no_dir_;
85 PersistentPrefStore::PrefReadError error_;
86 scoped_ptr<Value> value_;
87 scoped_refptr<JsonPrefStore> delegate_;
88 scoped_refptr<base::MessageLoopProxy> file_loop_proxy_;
89 scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
90 };
91
92 // static
93 void FileThreadDeserializer::HandleErrors(
94 const Value* value,
95 const FilePath& path,
96 int error_code,
97 const std::string& error_msg,
98 PersistentPrefStore::PrefReadError* error) {
99 *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
100 if (!value) {
101 DVLOG(1) << "Error while loading JSON file: " << error_msg;
102 switch (error_code) {
103 case JSONFileValueSerializer::JSON_ACCESS_DENIED:
104 *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
105 break;
106 case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
107 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
108 break;
109 case JSONFileValueSerializer::JSON_FILE_LOCKED:
110 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
111 break;
112 case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
113 *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
114 break;
115 default:
116 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
117 // JSON errors indicate file corruption of some sort.
118 // Since the file is corrupt, move it to the side and continue with
119 // empty preferences. This will result in them losing their settings.
120 // We keep the old file for possible support and debugging assistance
121 // as well as to detect if they're seeing these errors repeatedly.
122 // TODO(erikkay) Instead, use the last known good file.
123 FilePath bad = path.ReplaceExtension(kBadExtension);
124
125 // If they've ever had a parse error before, put them in another bucket.
126 // TODO(erikkay) if we keep this error checking for very long, we may
127 // want to differentiate between recent and long ago errors.
128 if (file_util::PathExists(bad))
129 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
130 file_util::Move(path, bad);
131 break;
132 }
133 } else if (!value->IsType(Value::TYPE_DICTIONARY)) {
134 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
135 }
136 }
137
138 } // namespace
139
140 JsonPrefStore::JsonPrefStore(const FilePath& filename,
141 base::MessageLoopProxy* file_message_loop_proxy)
142 : path_(filename),
143 file_message_loop_proxy_(file_message_loop_proxy),
144 prefs_(new DictionaryValue()),
145 read_only_(false),
146 writer_(filename, file_message_loop_proxy),
147 error_delegate_(NULL),
148 initialized_(false),
149 read_error_(PREF_READ_ERROR_OTHER) {
150 }
151
152 PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key,
153 const Value** result) const {
154 Value* tmp = NULL;
155 if (prefs_->Get(key, &tmp)) {
156 if (result)
157 *result = tmp;
158 return READ_OK;
159 }
160 return READ_NO_VALUE;
161 }
162
163 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
164 observers_.AddObserver(observer);
165 }
166
167 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
168 observers_.RemoveObserver(observer);
169 }
170
171 size_t JsonPrefStore::NumberOfObservers() const {
172 return observers_.size();
173 }
174
175 bool JsonPrefStore::IsInitializationComplete() const {
176 return initialized_;
177 }
178
179 PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key,
180 Value** result) {
181 return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE;
182 }
183
184 void JsonPrefStore::SetValue(const std::string& key, Value* value) {
185 DCHECK(value);
186 scoped_ptr<Value> new_value(value);
187 Value* old_value = NULL;
188 prefs_->Get(key, &old_value);
189 if (!old_value || !value->Equals(old_value)) {
190 prefs_->Set(key, new_value.release());
191 ReportValueChanged(key);
192 }
193 }
194
195 void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) {
196 DCHECK(value);
197 scoped_ptr<Value> new_value(value);
198 Value* old_value = NULL;
199 prefs_->Get(key, &old_value);
200 if (!old_value || !value->Equals(old_value)) {
201 prefs_->Set(key, new_value.release());
202 if (!read_only_)
203 writer_.ScheduleWrite(this);
204 }
205 }
206
207 void JsonPrefStore::RemoveValue(const std::string& key) {
208 if (prefs_->Remove(key, NULL))
209 ReportValueChanged(key);
210 }
211
212 void JsonPrefStore::MarkNeedsEmptyValue(const std::string& key) {
213 keys_need_empty_value_.insert(key);
214 }
215
216 bool JsonPrefStore::ReadOnly() const {
217 return read_only_;
218 }
219
220 PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
221 return read_error_;
222 }
223
224 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
225 if (path_.empty()) {
226 OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
227 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
228 }
229
230 PrefReadError error;
231 bool no_dir;
232 Value* value = FileThreadDeserializer::DoReading(path_, &error, &no_dir);
233 OnFileRead(value, error, no_dir);
234 return error;
235 }
236
237 void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) {
238 initialized_ = false;
239 error_delegate_.reset(error_delegate);
240 if (path_.empty()) {
241 OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
242 return;
243 }
244
245 // Start async reading of the preferences file. It will delete itself
246 // in the end.
247 scoped_refptr<FileThreadDeserializer> deserializer(
248 new FileThreadDeserializer(this, file_message_loop_proxy_.get()));
249 deserializer->Start(path_);
250 }
251
252 void JsonPrefStore::CommitPendingWrite() {
253 if (writer_.HasPendingWrite() && !read_only_)
254 writer_.DoScheduledWrite();
255 }
256
257 void JsonPrefStore::ReportValueChanged(const std::string& key) {
258 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
259 if (!read_only_)
260 writer_.ScheduleWrite(this);
261 }
262
263 void JsonPrefStore::OnFileRead(Value* value_owned,
264 PersistentPrefStore::PrefReadError error,
265 bool no_dir) {
266 scoped_ptr<Value> value(value_owned);
267 read_error_ = error;
268
269 if (no_dir) {
270 FOR_EACH_OBSERVER(PrefStore::Observer,
271 observers_,
272 OnInitializationCompleted(false));
273 return;
274 }
275
276 initialized_ = true;
277
278 switch (error) {
279 case PREF_READ_ERROR_ACCESS_DENIED:
280 case PREF_READ_ERROR_FILE_OTHER:
281 case PREF_READ_ERROR_FILE_LOCKED:
282 case PREF_READ_ERROR_JSON_TYPE:
283 case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
284 read_only_ = true;
285 break;
286 case PREF_READ_ERROR_NONE:
287 DCHECK(value.get());
288 prefs_.reset(static_cast<DictionaryValue*>(value.release()));
289 break;
290 case PREF_READ_ERROR_NO_FILE:
291 // If the file just doesn't exist, maybe this is first run. In any case
292 // there's no harm in writing out default prefs in this case.
293 break;
294 case PREF_READ_ERROR_JSON_PARSE:
295 case PREF_READ_ERROR_JSON_REPEAT:
296 break;
297 default:
298 NOTREACHED() << "Unknown error: " << error;
299 }
300
301 if (error_delegate_.get() && error != PREF_READ_ERROR_NONE)
302 error_delegate_->OnError(error);
303
304 FOR_EACH_OBSERVER(PrefStore::Observer,
305 observers_,
306 OnInitializationCompleted(true));
307 }
308
309 JsonPrefStore::~JsonPrefStore() {
310 CommitPendingWrite();
311 }
312
313 bool JsonPrefStore::SerializeData(std::string* output) {
314 // TODO(tc): Do we want to prune webkit preferences that match the default
315 // value?
316 JSONStringValueSerializer serializer(output);
317 serializer.set_pretty_print(true);
318 scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren());
319
320 // Iterates |keys_need_empty_value_| and if the key exists in |prefs_|,
321 // ensure its empty ListValue or DictonaryValue is preserved.
322 for (std::set<std::string>::const_iterator
323 it = keys_need_empty_value_.begin();
324 it != keys_need_empty_value_.end();
325 ++it) {
326 const std::string& key = *it;
327
328 base::Value* value = NULL;
329 if (!prefs_->Get(key, &value))
330 continue;
331
332 if (value->IsType(base::Value::TYPE_LIST)) {
333 const base::ListValue* list = NULL;
334 if (value->GetAsList(&list) && list->empty())
335 copy->Set(key, new base::ListValue);
336 } else if (value->IsType(base::Value::TYPE_DICTIONARY)) {
337 const base::DictionaryValue* dict = NULL;
338 if (value->GetAsDictionary(&dict) && dict->empty())
339 copy->Set(key, new base::DictionaryValue);
340 }
341 }
342
343 return serializer.Serialize(*(copy.get()));
344 }
OLDNEW
« no previous file with comments | « chrome/common/json_pref_store.h ('k') | chrome/common/json_pref_store_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698