| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "base/prefs/json_pref_store.h" | 5 #include "base/prefs/json_pref_store.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| 11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 12 #include "base/json/json_file_value_serializer.h" | 12 #include "base/json/json_file_value_serializer.h" |
| 13 #include "base/json/json_string_value_serializer.h" | 13 #include "base/json/json_string_value_serializer.h" |
| 14 #include "base/memory/ref_counted.h" | 14 #include "base/memory/ref_counted.h" |
| 15 #include "base/message_loop_proxy.h" | 15 #include "base/message_loop_proxy.h" |
| 16 #include "base/sequenced_task_runner.h" | 16 #include "base/sequenced_task_runner.h" |
| 17 #include "base/threading/sequenced_worker_pool.h" | 17 #include "base/threading/sequenced_worker_pool.h" |
| 18 #include "base/values.h" | 18 #include "base/values.h" |
| 19 | 19 |
| 20 namespace { | 20 namespace { |
| 21 | 21 |
| 22 // Some extensions we'll tack on to copies of the Preferences files. | 22 // Some extensions we'll tack on to copies of the Preferences files. |
| 23 const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad"); | 23 const base::FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad"); |
| 24 | 24 |
| 25 // Differentiates file loading between origin thread and passed | 25 // Differentiates file loading between origin thread and passed |
| 26 // (aka file) thread. | 26 // (aka file) thread. |
| 27 class FileThreadDeserializer | 27 class FileThreadDeserializer |
| 28 : public base::RefCountedThreadSafe<FileThreadDeserializer> { | 28 : public base::RefCountedThreadSafe<FileThreadDeserializer> { |
| 29 public: | 29 public: |
| 30 FileThreadDeserializer(JsonPrefStore* delegate, | 30 FileThreadDeserializer(JsonPrefStore* delegate, |
| 31 base::SequencedTaskRunner* sequenced_task_runner) | 31 base::SequencedTaskRunner* sequenced_task_runner) |
| 32 : no_dir_(false), | 32 : no_dir_(false), |
| 33 error_(PersistentPrefStore::PREF_READ_ERROR_NONE), | 33 error_(PersistentPrefStore::PREF_READ_ERROR_NONE), |
| 34 delegate_(delegate), | 34 delegate_(delegate), |
| 35 sequenced_task_runner_(sequenced_task_runner), | 35 sequenced_task_runner_(sequenced_task_runner), |
| 36 origin_loop_proxy_(base::MessageLoopProxy::current()) { | 36 origin_loop_proxy_(base::MessageLoopProxy::current()) { |
| 37 } | 37 } |
| 38 | 38 |
| 39 void Start(const FilePath& path) { | 39 void Start(const base::FilePath& path) { |
| 40 DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); | 40 DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); |
| 41 sequenced_task_runner_->PostTask( | 41 sequenced_task_runner_->PostTask( |
| 42 FROM_HERE, | 42 FROM_HERE, |
| 43 base::Bind(&FileThreadDeserializer::ReadFileAndReport, | 43 base::Bind(&FileThreadDeserializer::ReadFileAndReport, |
| 44 this, path)); | 44 this, path)); |
| 45 } | 45 } |
| 46 | 46 |
| 47 // Deserializes JSON on the sequenced task runner. | 47 // Deserializes JSON on the sequenced task runner. |
| 48 void ReadFileAndReport(const FilePath& path) { | 48 void ReadFileAndReport(const base::FilePath& path) { |
| 49 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread()); | 49 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread()); |
| 50 | 50 |
| 51 value_.reset(DoReading(path, &error_, &no_dir_)); | 51 value_.reset(DoReading(path, &error_, &no_dir_)); |
| 52 | 52 |
| 53 origin_loop_proxy_->PostTask( | 53 origin_loop_proxy_->PostTask( |
| 54 FROM_HERE, | 54 FROM_HERE, |
| 55 base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this)); | 55 base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this)); |
| 56 } | 56 } |
| 57 | 57 |
| 58 // Reports deserialization result on the origin thread. | 58 // Reports deserialization result on the origin thread. |
| 59 void ReportOnOriginThread() { | 59 void ReportOnOriginThread() { |
| 60 DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); | 60 DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); |
| 61 delegate_->OnFileRead(value_.release(), error_, no_dir_); | 61 delegate_->OnFileRead(value_.release(), error_, no_dir_); |
| 62 } | 62 } |
| 63 | 63 |
| 64 static Value* DoReading(const FilePath& path, | 64 static Value* DoReading(const base::FilePath& path, |
| 65 PersistentPrefStore::PrefReadError* error, | 65 PersistentPrefStore::PrefReadError* error, |
| 66 bool* no_dir) { | 66 bool* no_dir) { |
| 67 int error_code; | 67 int error_code; |
| 68 std::string error_msg; | 68 std::string error_msg; |
| 69 JSONFileValueSerializer serializer(path); | 69 JSONFileValueSerializer serializer(path); |
| 70 Value* value = serializer.Deserialize(&error_code, &error_msg); | 70 Value* value = serializer.Deserialize(&error_code, &error_msg); |
| 71 HandleErrors(value, path, error_code, error_msg, error); | 71 HandleErrors(value, path, error_code, error_msg, error); |
| 72 *no_dir = !file_util::PathExists(path.DirName()); | 72 *no_dir = !file_util::PathExists(path.DirName()); |
| 73 return value; | 73 return value; |
| 74 } | 74 } |
| 75 | 75 |
| 76 static void HandleErrors(const Value* value, | 76 static void HandleErrors(const Value* value, |
| 77 const FilePath& path, | 77 const base::FilePath& path, |
| 78 int error_code, | 78 int error_code, |
| 79 const std::string& error_msg, | 79 const std::string& error_msg, |
| 80 PersistentPrefStore::PrefReadError* error); | 80 PersistentPrefStore::PrefReadError* error); |
| 81 | 81 |
| 82 private: | 82 private: |
| 83 friend class base::RefCountedThreadSafe<FileThreadDeserializer>; | 83 friend class base::RefCountedThreadSafe<FileThreadDeserializer>; |
| 84 ~FileThreadDeserializer() {} | 84 ~FileThreadDeserializer() {} |
| 85 | 85 |
| 86 bool no_dir_; | 86 bool no_dir_; |
| 87 PersistentPrefStore::PrefReadError error_; | 87 PersistentPrefStore::PrefReadError error_; |
| 88 scoped_ptr<Value> value_; | 88 scoped_ptr<Value> value_; |
| 89 const scoped_refptr<JsonPrefStore> delegate_; | 89 const scoped_refptr<JsonPrefStore> delegate_; |
| 90 const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; | 90 const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; |
| 91 const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; | 91 const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; |
| 92 }; | 92 }; |
| 93 | 93 |
| 94 // static | 94 // static |
| 95 void FileThreadDeserializer::HandleErrors( | 95 void FileThreadDeserializer::HandleErrors( |
| 96 const Value* value, | 96 const Value* value, |
| 97 const FilePath& path, | 97 const base::FilePath& path, |
| 98 int error_code, | 98 int error_code, |
| 99 const std::string& error_msg, | 99 const std::string& error_msg, |
| 100 PersistentPrefStore::PrefReadError* error) { | 100 PersistentPrefStore::PrefReadError* error) { |
| 101 *error = PersistentPrefStore::PREF_READ_ERROR_NONE; | 101 *error = PersistentPrefStore::PREF_READ_ERROR_NONE; |
| 102 if (!value) { | 102 if (!value) { |
| 103 DVLOG(1) << "Error while loading JSON file: " << error_msg | 103 DVLOG(1) << "Error while loading JSON file: " << error_msg |
| 104 << ", file: " << path.value(); | 104 << ", file: " << path.value(); |
| 105 switch (error_code) { | 105 switch (error_code) { |
| 106 case JSONFileValueSerializer::JSON_ACCESS_DENIED: | 106 case JSONFileValueSerializer::JSON_ACCESS_DENIED: |
| 107 *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; | 107 *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; |
| 108 break; | 108 break; |
| 109 case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: | 109 case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: |
| 110 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER; | 110 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER; |
| 111 break; | 111 break; |
| 112 case JSONFileValueSerializer::JSON_FILE_LOCKED: | 112 case JSONFileValueSerializer::JSON_FILE_LOCKED: |
| 113 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED; | 113 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED; |
| 114 break; | 114 break; |
| 115 case JSONFileValueSerializer::JSON_NO_SUCH_FILE: | 115 case JSONFileValueSerializer::JSON_NO_SUCH_FILE: |
| 116 *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE; | 116 *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE; |
| 117 break; | 117 break; |
| 118 default: | 118 default: |
| 119 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE; | 119 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE; |
| 120 // JSON errors indicate file corruption of some sort. | 120 // JSON errors indicate file corruption of some sort. |
| 121 // Since the file is corrupt, move it to the side and continue with | 121 // Since the file is corrupt, move it to the side and continue with |
| 122 // empty preferences. This will result in them losing their settings. | 122 // empty preferences. This will result in them losing their settings. |
| 123 // We keep the old file for possible support and debugging assistance | 123 // We keep the old file for possible support and debugging assistance |
| 124 // as well as to detect if they're seeing these errors repeatedly. | 124 // as well as to detect if they're seeing these errors repeatedly. |
| 125 // TODO(erikkay) Instead, use the last known good file. | 125 // TODO(erikkay) Instead, use the last known good file. |
| 126 FilePath bad = path.ReplaceExtension(kBadExtension); | 126 base::FilePath bad = path.ReplaceExtension(kBadExtension); |
| 127 | 127 |
| 128 // If they've ever had a parse error before, put them in another bucket. | 128 // If they've ever had a parse error before, put them in another bucket. |
| 129 // TODO(erikkay) if we keep this error checking for very long, we may | 129 // TODO(erikkay) if we keep this error checking for very long, we may |
| 130 // want to differentiate between recent and long ago errors. | 130 // want to differentiate between recent and long ago errors. |
| 131 if (file_util::PathExists(bad)) | 131 if (file_util::PathExists(bad)) |
| 132 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT; | 132 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT; |
| 133 file_util::Move(path, bad); | 133 file_util::Move(path, bad); |
| 134 break; | 134 break; |
| 135 } | 135 } |
| 136 } else if (!value->IsType(Value::TYPE_DICTIONARY)) { | 136 } else if (!value->IsType(Value::TYPE_DICTIONARY)) { |
| 137 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE; | 137 *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE; |
| 138 } | 138 } |
| 139 } | 139 } |
| 140 | 140 |
| 141 } // namespace | 141 } // namespace |
| 142 | 142 |
| 143 scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile( | 143 scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile( |
| 144 const FilePath& filename, | 144 const base::FilePath& filename, |
| 145 base::SequencedWorkerPool* worker_pool) { | 145 base::SequencedWorkerPool* worker_pool) { |
| 146 std::string token("json_pref_store-"); | 146 std::string token("json_pref_store-"); |
| 147 token.append(filename.AsUTF8Unsafe()); | 147 token.append(filename.AsUTF8Unsafe()); |
| 148 return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( | 148 return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( |
| 149 worker_pool->GetNamedSequenceToken(token), | 149 worker_pool->GetNamedSequenceToken(token), |
| 150 base::SequencedWorkerPool::BLOCK_SHUTDOWN); | 150 base::SequencedWorkerPool::BLOCK_SHUTDOWN); |
| 151 } | 151 } |
| 152 | 152 |
| 153 JsonPrefStore::JsonPrefStore(const FilePath& filename, | 153 JsonPrefStore::JsonPrefStore(const base::FilePath& filename, |
| 154 base::SequencedTaskRunner* sequenced_task_runner) | 154 base::SequencedTaskRunner* sequenced_task_runner) |
| 155 : path_(filename), | 155 : path_(filename), |
| 156 sequenced_task_runner_(sequenced_task_runner), | 156 sequenced_task_runner_(sequenced_task_runner), |
| 157 prefs_(new DictionaryValue()), | 157 prefs_(new DictionaryValue()), |
| 158 read_only_(false), | 158 read_only_(false), |
| 159 writer_(filename, sequenced_task_runner), | 159 writer_(filename, sequenced_task_runner), |
| 160 error_delegate_(NULL), | 160 error_delegate_(NULL), |
| 161 initialized_(false), | 161 initialized_(false), |
| 162 read_error_(PREF_READ_ERROR_OTHER) { | 162 read_error_(PREF_READ_ERROR_OTHER) { |
| 163 } | 163 } |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 copy->Set(key, new base::ListValue); | 348 copy->Set(key, new base::ListValue); |
| 349 } else if (value->IsType(base::Value::TYPE_DICTIONARY)) { | 349 } else if (value->IsType(base::Value::TYPE_DICTIONARY)) { |
| 350 const base::DictionaryValue* dict = NULL; | 350 const base::DictionaryValue* dict = NULL; |
| 351 if (value->GetAsDictionary(&dict) && dict->empty()) | 351 if (value->GetAsDictionary(&dict) && dict->empty()) |
| 352 copy->Set(key, new base::DictionaryValue); | 352 copy->Set(key, new base::DictionaryValue); |
| 353 } | 353 } |
| 354 } | 354 } |
| 355 | 355 |
| 356 return serializer.Serialize(*(copy.get())); | 356 return serializer.Serialize(*(copy.get())); |
| 357 } | 357 } |
| OLD | NEW |