• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/memory/ref_counted.h"
13 #include "base/values.h"
14 #include "content/browser/browser_thread.h"
15 #include "content/common/json_value_serializer.h"
16 
17 namespace {
18 
19 // Some extensions we'll tack on to copies of the Preferences files.
20 const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
21 
22 // Differentiates file loading between UI and FILE threads.
23 class FileThreadDeserializer
24     : public base::RefCountedThreadSafe<FileThreadDeserializer> {
25  public:
FileThreadDeserializer(JsonPrefStore * delegate)26   explicit FileThreadDeserializer(JsonPrefStore* delegate)
27       : delegate_(delegate) {
28   }
29 
Start(const FilePath & path)30   void Start(const FilePath& path) {
31     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
32     BrowserThread::PostTask(
33         BrowserThread::FILE,
34         FROM_HERE,
35         NewRunnableMethod(this,
36                           &FileThreadDeserializer::ReadFileAndReport,
37                           path));
38   }
39 
40   // Deserializes JSON on the FILE thread.
ReadFileAndReport(const FilePath & path)41   void ReadFileAndReport(const FilePath& path) {
42     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43 
44     int error_code;
45     std::string error_msg;
46     JSONFileValueSerializer serializer(path);
47     value_.reset(serializer.Deserialize(&error_code, &error_msg));
48 
49     HandleErrors(value_.get(), path, error_code, error_msg, &error_);
50 
51     no_dir_ = !file_util::PathExists(path.DirName());
52 
53     BrowserThread::PostTask(
54         BrowserThread::UI,
55         FROM_HERE,
56         NewRunnableMethod(this, &FileThreadDeserializer::ReportOnUIThread));
57   }
58 
59   // Reports deserialization result on the UI thread.
ReportOnUIThread()60   void ReportOnUIThread() {
61     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
62     delegate_->OnFileRead(value_.release(), error_, no_dir_);
63   }
64 
65   static void HandleErrors(const Value* value,
66                            const FilePath& path,
67                            int error_code,
68                            const std::string& error_msg,
69                            PersistentPrefStore::PrefReadError* error);
70 
71  private:
72   friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
73 
74   bool no_dir_;
75   PersistentPrefStore::PrefReadError error_;
76   scoped_ptr<Value> value_;
77   scoped_refptr<JsonPrefStore> delegate_;
78 };
79 
80 // static
HandleErrors(const Value * value,const FilePath & path,int error_code,const std::string & error_msg,PersistentPrefStore::PrefReadError * error)81 void FileThreadDeserializer::HandleErrors(
82     const Value* value,
83     const FilePath& path,
84     int error_code,
85     const std::string& error_msg,
86     PersistentPrefStore::PrefReadError* error) {
87   *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
88   if (!value) {
89     DLOG(ERROR) << "Error while loading JSON file: " << error_msg;
90     switch (error_code) {
91       case JSONFileValueSerializer::JSON_ACCESS_DENIED:
92         *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
93         break;
94       case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
95         *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
96         break;
97       case JSONFileValueSerializer::JSON_FILE_LOCKED:
98         *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
99         break;
100       case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
101         *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
102         break;
103       default:
104         *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
105         // JSON errors indicate file corruption of some sort.
106         // Since the file is corrupt, move it to the side and continue with
107         // empty preferences.  This will result in them losing their settings.
108         // We keep the old file for possible support and debugging assistance
109         // as well as to detect if they're seeing these errors repeatedly.
110         // TODO(erikkay) Instead, use the last known good file.
111         FilePath bad = path.ReplaceExtension(kBadExtension);
112 
113         // If they've ever had a parse error before, put them in another bucket.
114         // TODO(erikkay) if we keep this error checking for very long, we may
115         // want to differentiate between recent and long ago errors.
116         if (file_util::PathExists(bad))
117           *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
118         file_util::Move(path, bad);
119         break;
120     }
121   } else if (!value->IsType(Value::TYPE_DICTIONARY)) {
122     *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
123   }
124 }
125 
126 }  // namespace
127 
JsonPrefStore(const FilePath & filename,base::MessageLoopProxy * file_message_loop_proxy)128 JsonPrefStore::JsonPrefStore(const FilePath& filename,
129                              base::MessageLoopProxy* file_message_loop_proxy)
130     : path_(filename),
131       prefs_(new DictionaryValue()),
132       read_only_(false),
133       writer_(filename, file_message_loop_proxy) {
134 }
135 
~JsonPrefStore()136 JsonPrefStore::~JsonPrefStore() {
137   CommitPendingWrite();
138 }
139 
GetValue(const std::string & key,const Value ** result) const140 PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key,
141                                               const Value** result) const {
142   Value* tmp = NULL;
143   if (prefs_->Get(key, &tmp)) {
144     *result = tmp;
145     return READ_OK;
146   }
147   return READ_NO_VALUE;
148 }
149 
AddObserver(PrefStore::Observer * observer)150 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
151   observers_.AddObserver(observer);
152 }
153 
RemoveObserver(PrefStore::Observer * observer)154 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
155   observers_.RemoveObserver(observer);
156 }
157 
GetMutableValue(const std::string & key,Value ** result)158 PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key,
159                                                      Value** result) {
160   return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE;
161 }
162 
SetValue(const std::string & key,Value * value)163 void JsonPrefStore::SetValue(const std::string& key, Value* value) {
164   DCHECK(value);
165   scoped_ptr<Value> new_value(value);
166   Value* old_value = NULL;
167   prefs_->Get(key, &old_value);
168   if (!old_value || !value->Equals(old_value)) {
169     prefs_->Set(key, new_value.release());
170     FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
171   }
172 }
173 
SetValueSilently(const std::string & key,Value * value)174 void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) {
175   DCHECK(value);
176   scoped_ptr<Value> new_value(value);
177   Value* old_value = NULL;
178   prefs_->Get(key, &old_value);
179   if (!old_value || !value->Equals(old_value))
180     prefs_->Set(key, new_value.release());
181 }
182 
RemoveValue(const std::string & key)183 void JsonPrefStore::RemoveValue(const std::string& key) {
184   if (prefs_->Remove(key, NULL)) {
185     FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
186   }
187 }
188 
ReadOnly() const189 bool JsonPrefStore::ReadOnly() const {
190   return read_only_;
191 }
192 
OnFileRead(Value * value_owned,PersistentPrefStore::PrefReadError error,bool no_dir)193 void JsonPrefStore::OnFileRead(Value* value_owned,
194                                PersistentPrefStore::PrefReadError error,
195                                bool no_dir) {
196   scoped_ptr<Value> value(value_owned);
197   switch (error) {
198     case PREF_READ_ERROR_ACCESS_DENIED:
199     case PREF_READ_ERROR_FILE_OTHER:
200     case PREF_READ_ERROR_FILE_LOCKED:
201     case PREF_READ_ERROR_JSON_TYPE:
202       read_only_ = true;
203       break;
204     case PREF_READ_ERROR_NONE:
205       DCHECK(value.get());
206       prefs_.reset(static_cast<DictionaryValue*>(value.release()));
207       break;
208     case PREF_READ_ERROR_NO_FILE:
209       // If the file just doesn't exist, maybe this is first run.  In any case
210       // there's no harm in writing out default prefs in this case.
211       break;
212     case PREF_READ_ERROR_JSON_PARSE:
213     case PREF_READ_ERROR_JSON_REPEAT:
214       break;
215     default:
216       NOTREACHED() << "Unknown error: " << error;
217   }
218 
219   if (delegate_)
220     delegate_->OnPrefsRead(error, no_dir);
221 }
222 
ReadPrefs(Delegate * delegate)223 void JsonPrefStore::ReadPrefs(Delegate* delegate) {
224   DCHECK(delegate);
225   delegate_ = delegate;
226 
227   if (path_.empty()) {
228     read_only_ = true;
229     delegate_->OnPrefsRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
230     return;
231   }
232 
233   // This guarantees that class will not be deleted while JSON is readed.
234   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
235 
236   // Start async reading of the preferences file. It will delete itself
237   // in the end.
238   scoped_refptr<FileThreadDeserializer> deserializer(
239       new FileThreadDeserializer(this));
240   deserializer->Start(path_);
241 }
242 
ReadPrefs()243 PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
244   delegate_ = NULL;
245 
246   if (path_.empty()) {
247     read_only_ = true;
248     return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
249   }
250 
251   int error_code = 0;
252   std::string error_msg;
253 
254   JSONFileValueSerializer serializer(path_);
255   scoped_ptr<Value> value(serializer.Deserialize(&error_code, &error_msg));
256 
257   PersistentPrefStore::PrefReadError error;
258   FileThreadDeserializer::HandleErrors(value.get(),
259                                        path_,
260                                        error_code,
261                                        error_msg,
262                                        &error);
263 
264   OnFileRead(value.release(), error, false);
265 
266   return error;
267 }
268 
WritePrefs()269 bool JsonPrefStore::WritePrefs() {
270   std::string data;
271   if (!SerializeData(&data))
272     return false;
273 
274   // Lie about our ability to save.
275   if (read_only_)
276     return true;
277 
278   writer_.WriteNow(data);
279   return true;
280 }
281 
ScheduleWritePrefs()282 void JsonPrefStore::ScheduleWritePrefs() {
283   if (read_only_)
284     return;
285 
286   writer_.ScheduleWrite(this);
287 }
288 
CommitPendingWrite()289 void JsonPrefStore::CommitPendingWrite() {
290   if (writer_.HasPendingWrite() && !read_only_)
291     writer_.DoScheduledWrite();
292 }
293 
ReportValueChanged(const std::string & key)294 void JsonPrefStore::ReportValueChanged(const std::string& key) {
295   FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
296 }
297 
SerializeData(std::string * output)298 bool JsonPrefStore::SerializeData(std::string* output) {
299   // TODO(tc): Do we want to prune webkit preferences that match the default
300   // value?
301   JSONStringValueSerializer serializer(output);
302   serializer.set_pretty_print(true);
303   scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren());
304   return serializer.Serialize(*(copy.get()));
305 }
306