• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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