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 "base/prefs/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/message_loop_proxy.h"
16 #include "base/prefs/pref_filter.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/values.h"
20
21 namespace {
22
23 // Some extensions we'll tack on to copies of the Preferences files.
24 const base::FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
25
26 // Differentiates file loading between origin thread and passed
27 // (aka file) thread.
28 class FileThreadDeserializer
29 : public base::RefCountedThreadSafe<FileThreadDeserializer> {
30 public:
FileThreadDeserializer(JsonPrefStore * delegate,base::SequencedTaskRunner * sequenced_task_runner)31 FileThreadDeserializer(JsonPrefStore* delegate,
32 base::SequencedTaskRunner* sequenced_task_runner)
33 : no_dir_(false),
34 error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
35 delegate_(delegate),
36 sequenced_task_runner_(sequenced_task_runner),
37 origin_loop_proxy_(base::MessageLoopProxy::current()) {
38 }
39
Start(const base::FilePath & path,const base::FilePath & alternate_path)40 void Start(const base::FilePath& path,
41 const base::FilePath& alternate_path) {
42 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
43 // TODO(gab): This should use PostTaskAndReplyWithResult instead of using
44 // the |error_| member to pass data across tasks.
45 sequenced_task_runner_->PostTask(
46 FROM_HERE,
47 base::Bind(&FileThreadDeserializer::ReadFileAndReport,
48 this, path, alternate_path));
49 }
50
51 // Deserializes JSON on the sequenced task runner.
ReadFileAndReport(const base::FilePath & path,const base::FilePath & alternate_path)52 void ReadFileAndReport(const base::FilePath& path,
53 const base::FilePath& alternate_path) {
54 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
55
56 value_.reset(DoReading(path, alternate_path, &error_, &no_dir_));
57
58 origin_loop_proxy_->PostTask(
59 FROM_HERE,
60 base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this));
61 }
62
63 // Reports deserialization result on the origin thread.
ReportOnOriginThread()64 void ReportOnOriginThread() {
65 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
66 delegate_->OnFileRead(value_.Pass(), error_, no_dir_);
67 }
68
DoReading(const base::FilePath & path,const base::FilePath & alternate_path,PersistentPrefStore::PrefReadError * error,bool * no_dir)69 static base::Value* DoReading(const base::FilePath& path,
70 const base::FilePath& alternate_path,
71 PersistentPrefStore::PrefReadError* error,
72 bool* no_dir) {
73 if (!base::PathExists(path) && !alternate_path.empty() &&
74 base::PathExists(alternate_path)) {
75 base::Move(alternate_path, path);
76 }
77
78 int error_code;
79 std::string error_msg;
80 JSONFileValueSerializer serializer(path);
81 base::Value* value = serializer.Deserialize(&error_code, &error_msg);
82 HandleErrors(value, path, error_code, error_msg, error);
83 *no_dir = !base::PathExists(path.DirName());
84 return value;
85 }
86
87 static void HandleErrors(const base::Value* value,
88 const base::FilePath& path,
89 int error_code,
90 const std::string& error_msg,
91 PersistentPrefStore::PrefReadError* error);
92
93 private:
94 friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
~FileThreadDeserializer()95 ~FileThreadDeserializer() {}
96
97 bool no_dir_;
98 PersistentPrefStore::PrefReadError error_;
99 scoped_ptr<base::Value> value_;
100 const scoped_refptr<JsonPrefStore> delegate_;
101 const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
102 const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
103 };
104
105 // static
HandleErrors(const base::Value * value,const base::FilePath & path,int error_code,const std::string & error_msg,PersistentPrefStore::PrefReadError * error)106 void FileThreadDeserializer::HandleErrors(
107 const base::Value* value,
108 const base::FilePath& path,
109 int error_code,
110 const std::string& error_msg,
111 PersistentPrefStore::PrefReadError* error) {
112 *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
113 if (!value) {
114 DVLOG(1) << "Error while loading JSON file: " << error_msg
115 << ", file: " << path.value();
116 switch (error_code) {
117 case JSONFileValueSerializer::JSON_ACCESS_DENIED:
118 *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
119 break;
120 case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
121 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
122 break;
123 case JSONFileValueSerializer::JSON_FILE_LOCKED:
124 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
125 break;
126 case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
127 *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
128 break;
129 default:
130 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
131 // JSON errors indicate file corruption of some sort.
132 // Since the file is corrupt, move it to the side and continue with
133 // empty preferences. This will result in them losing their settings.
134 // We keep the old file for possible support and debugging assistance
135 // as well as to detect if they're seeing these errors repeatedly.
136 // TODO(erikkay) Instead, use the last known good file.
137 base::FilePath bad = path.ReplaceExtension(kBadExtension);
138
139 // If they've ever had a parse error before, put them in another bucket.
140 // TODO(erikkay) if we keep this error checking for very long, we may
141 // want to differentiate between recent and long ago errors.
142 if (base::PathExists(bad))
143 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
144 base::Move(path, bad);
145 break;
146 }
147 } else if (!value->IsType(base::Value::TYPE_DICTIONARY)) {
148 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
149 }
150 }
151
152 } // namespace
153
GetTaskRunnerForFile(const base::FilePath & filename,base::SequencedWorkerPool * worker_pool)154 scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
155 const base::FilePath& filename,
156 base::SequencedWorkerPool* worker_pool) {
157 std::string token("json_pref_store-");
158 token.append(filename.AsUTF8Unsafe());
159 return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
160 worker_pool->GetNamedSequenceToken(token),
161 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
162 }
163
JsonPrefStore(const base::FilePath & filename,base::SequencedTaskRunner * sequenced_task_runner,scoped_ptr<PrefFilter> pref_filter)164 JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
165 base::SequencedTaskRunner* sequenced_task_runner,
166 scoped_ptr<PrefFilter> pref_filter)
167 : path_(filename),
168 sequenced_task_runner_(sequenced_task_runner),
169 prefs_(new base::DictionaryValue()),
170 read_only_(false),
171 writer_(filename, sequenced_task_runner),
172 pref_filter_(pref_filter.Pass()),
173 initialized_(false),
174 filtering_in_progress_(false),
175 read_error_(PREF_READ_ERROR_NONE) {
176 }
177
JsonPrefStore(const base::FilePath & filename,const base::FilePath & alternate_filename,base::SequencedTaskRunner * sequenced_task_runner,scoped_ptr<PrefFilter> pref_filter)178 JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
179 const base::FilePath& alternate_filename,
180 base::SequencedTaskRunner* sequenced_task_runner,
181 scoped_ptr<PrefFilter> pref_filter)
182 : path_(filename),
183 alternate_path_(alternate_filename),
184 sequenced_task_runner_(sequenced_task_runner),
185 prefs_(new base::DictionaryValue()),
186 read_only_(false),
187 writer_(filename, sequenced_task_runner),
188 pref_filter_(pref_filter.Pass()),
189 initialized_(false),
190 filtering_in_progress_(false),
191 read_error_(PREF_READ_ERROR_NONE) {
192 }
193
GetValue(const std::string & key,const base::Value ** result) const194 bool JsonPrefStore::GetValue(const std::string& key,
195 const base::Value** result) const {
196 base::Value* tmp = NULL;
197 if (!prefs_->Get(key, &tmp))
198 return false;
199
200 if (result)
201 *result = tmp;
202 return true;
203 }
204
AddObserver(PrefStore::Observer * observer)205 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
206 observers_.AddObserver(observer);
207 }
208
RemoveObserver(PrefStore::Observer * observer)209 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
210 observers_.RemoveObserver(observer);
211 }
212
HasObservers() const213 bool JsonPrefStore::HasObservers() const {
214 return observers_.might_have_observers();
215 }
216
IsInitializationComplete() const217 bool JsonPrefStore::IsInitializationComplete() const {
218 return initialized_;
219 }
220
GetMutableValue(const std::string & key,base::Value ** result)221 bool JsonPrefStore::GetMutableValue(const std::string& key,
222 base::Value** result) {
223 return prefs_->Get(key, result);
224 }
225
SetValue(const std::string & key,base::Value * value)226 void JsonPrefStore::SetValue(const std::string& key, base::Value* value) {
227 DCHECK(value);
228 scoped_ptr<base::Value> new_value(value);
229 base::Value* old_value = NULL;
230 prefs_->Get(key, &old_value);
231 if (!old_value || !value->Equals(old_value)) {
232 prefs_->Set(key, new_value.release());
233 ReportValueChanged(key);
234 }
235 }
236
SetValueSilently(const std::string & key,base::Value * value)237 void JsonPrefStore::SetValueSilently(const std::string& key,
238 base::Value* value) {
239 DCHECK(value);
240 scoped_ptr<base::Value> new_value(value);
241 base::Value* old_value = NULL;
242 prefs_->Get(key, &old_value);
243 if (!old_value || !value->Equals(old_value)) {
244 prefs_->Set(key, new_value.release());
245 if (!read_only_)
246 writer_.ScheduleWrite(this);
247 }
248 }
249
RemoveValue(const std::string & key)250 void JsonPrefStore::RemoveValue(const std::string& key) {
251 if (prefs_->RemovePath(key, NULL))
252 ReportValueChanged(key);
253 }
254
RemoveValueSilently(const std::string & key)255 void JsonPrefStore::RemoveValueSilently(const std::string& key) {
256 prefs_->RemovePath(key, NULL);
257 if (!read_only_)
258 writer_.ScheduleWrite(this);
259 }
260
ReadOnly() const261 bool JsonPrefStore::ReadOnly() const {
262 return read_only_;
263 }
264
GetReadError() const265 PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
266 return read_error_;
267 }
268
ReadPrefs()269 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
270 if (path_.empty()) {
271 OnFileRead(
272 scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
273 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
274 }
275
276 PrefReadError error;
277 bool no_dir;
278 scoped_ptr<base::Value> value(
279 FileThreadDeserializer::DoReading(path_, alternate_path_, &error,
280 &no_dir));
281 OnFileRead(value.Pass(), error, no_dir);
282 return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE :
283 error;
284 }
285
ReadPrefsAsync(ReadErrorDelegate * error_delegate)286 void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
287 initialized_ = false;
288 error_delegate_.reset(error_delegate);
289 if (path_.empty()) {
290 OnFileRead(
291 scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
292 return;
293 }
294
295 // Start async reading of the preferences file. It will delete itself
296 // in the end.
297 scoped_refptr<FileThreadDeserializer> deserializer(
298 new FileThreadDeserializer(this, sequenced_task_runner_.get()));
299 deserializer->Start(path_, alternate_path_);
300 }
301
CommitPendingWrite()302 void JsonPrefStore::CommitPendingWrite() {
303 if (writer_.HasPendingWrite() && !read_only_)
304 writer_.DoScheduledWrite();
305 }
306
ReportValueChanged(const std::string & key)307 void JsonPrefStore::ReportValueChanged(const std::string& key) {
308 if (pref_filter_)
309 pref_filter_->FilterUpdate(key);
310
311 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
312
313 if (!read_only_)
314 writer_.ScheduleWrite(this);
315 }
316
RegisterOnNextSuccessfulWriteCallback(const base::Closure & on_next_successful_write)317 void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback(
318 const base::Closure& on_next_successful_write) {
319 writer_.RegisterOnNextSuccessfulWriteCallback(on_next_successful_write);
320 }
321
OnFileRead(scoped_ptr<base::Value> value,PersistentPrefStore::PrefReadError error,bool no_dir)322 void JsonPrefStore::OnFileRead(scoped_ptr<base::Value> value,
323 PersistentPrefStore::PrefReadError error,
324 bool no_dir) {
325 scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue);
326
327 read_error_ = error;
328
329 bool initialization_successful = !no_dir;
330
331 if (initialization_successful) {
332 switch (read_error_) {
333 case PREF_READ_ERROR_ACCESS_DENIED:
334 case PREF_READ_ERROR_FILE_OTHER:
335 case PREF_READ_ERROR_FILE_LOCKED:
336 case PREF_READ_ERROR_JSON_TYPE:
337 case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
338 read_only_ = true;
339 break;
340 case PREF_READ_ERROR_NONE:
341 DCHECK(value.get());
342 unfiltered_prefs.reset(
343 static_cast<base::DictionaryValue*>(value.release()));
344 break;
345 case PREF_READ_ERROR_NO_FILE:
346 // If the file just doesn't exist, maybe this is first run. In any case
347 // there's no harm in writing out default prefs in this case.
348 break;
349 case PREF_READ_ERROR_JSON_PARSE:
350 case PREF_READ_ERROR_JSON_REPEAT:
351 break;
352 case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
353 // This is a special error code to be returned by ReadPrefs when it
354 // can't complete synchronously, it should never be returned by the read
355 // operation itself.
356 NOTREACHED();
357 break;
358 case PREF_READ_ERROR_LEVELDB_IO:
359 case PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY:
360 case PREF_READ_ERROR_LEVELDB_CORRUPTION:
361 // These are specific to LevelDBPrefStore.
362 NOTREACHED();
363 case PREF_READ_ERROR_MAX_ENUM:
364 NOTREACHED();
365 break;
366 }
367 }
368
369 if (pref_filter_) {
370 filtering_in_progress_ = true;
371 const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
372 base::Bind(
373 &JsonPrefStore::FinalizeFileRead, this, initialization_successful));
374 pref_filter_->FilterOnLoad(post_filter_on_load_callback,
375 unfiltered_prefs.Pass());
376 } else {
377 FinalizeFileRead(initialization_successful, unfiltered_prefs.Pass(), false);
378 }
379 }
380
~JsonPrefStore()381 JsonPrefStore::~JsonPrefStore() {
382 CommitPendingWrite();
383 }
384
SerializeData(std::string * output)385 bool JsonPrefStore::SerializeData(std::string* output) {
386 if (pref_filter_)
387 pref_filter_->FilterSerializeData(prefs_.get());
388
389 JSONStringValueSerializer serializer(output);
390 serializer.set_pretty_print(true);
391 return serializer.Serialize(*prefs_);
392 }
393
FinalizeFileRead(bool initialization_successful,scoped_ptr<base::DictionaryValue> prefs,bool schedule_write)394 void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
395 scoped_ptr<base::DictionaryValue> prefs,
396 bool schedule_write) {
397 filtering_in_progress_ = false;
398
399 if (!initialization_successful) {
400 FOR_EACH_OBSERVER(PrefStore::Observer,
401 observers_,
402 OnInitializationCompleted(false));
403 return;
404 }
405
406 prefs_ = prefs.Pass();
407
408 initialized_ = true;
409
410 if (schedule_write && !read_only_)
411 writer_.ScheduleWrite(this);
412
413 if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
414 error_delegate_->OnError(read_error_);
415
416 FOR_EACH_OBSERVER(PrefStore::Observer,
417 observers_,
418 OnInitializationCompleted(true));
419
420 return;
421 }
422