| OLD | NEW |
| (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 "components/filesystem/public/cpp/prefs/filesystem_json_pref_store.h" |
| 6 |
| 7 #include <stddef.h> |
| 8 |
| 9 #include <algorithm> |
| 10 #include <utility> |
| 11 |
| 12 #include "base/bind.h" |
| 13 #include "base/callback.h" |
| 14 #include "base/files/file_path.h" |
| 15 #include "base/files/file_util.h" |
| 16 #include "base/json/json_string_value_serializer.h" |
| 17 #include "base/logging.h" |
| 18 #include "base/macros.h" |
| 19 #include "base/memory/ref_counted.h" |
| 20 #include "base/prefs/pref_filter.h" |
| 21 #include "base/strings/string_number_conversions.h" |
| 22 #include "base/strings/string_util.h" |
| 23 #include "base/task_runner_util.h" |
| 24 #include "base/time/default_clock.h" |
| 25 #include "base/values.h" |
| 26 #include "mojo/common/common_type_converters.h" |
| 27 |
| 28 namespace filesystem { |
| 29 |
| 30 // Result returned from internal read tasks. |
| 31 struct FilesystemJsonPrefStore::ReadResult { |
| 32 public: |
| 33 ReadResult(); |
| 34 ~ReadResult(); |
| 35 |
| 36 scoped_ptr<base::Value> value; |
| 37 PrefReadError error; |
| 38 |
| 39 private: |
| 40 DISALLOW_COPY_AND_ASSIGN(ReadResult); |
| 41 }; |
| 42 |
| 43 FilesystemJsonPrefStore::ReadResult::ReadResult() |
| 44 : error(PersistentPrefStore::PREF_READ_ERROR_NONE) {} |
| 45 |
| 46 FilesystemJsonPrefStore::ReadResult::~ReadResult() {} |
| 47 |
| 48 namespace { |
| 49 |
| 50 PersistentPrefStore::PrefReadError HandleReadErrors(const base::Value* value) { |
| 51 if (!value->IsType(base::Value::TYPE_DICTIONARY)) |
| 52 return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE; |
| 53 return PersistentPrefStore::PREF_READ_ERROR_NONE; |
| 54 } |
| 55 |
| 56 } // namespace |
| 57 |
| 58 FilesystemJsonPrefStore::FilesystemJsonPrefStore( |
| 59 const std::string& pref_filename, |
| 60 filesystem::FileSystemPtr filesystem, |
| 61 scoped_ptr<PrefFilter> pref_filter) |
| 62 : path_(pref_filename), |
| 63 binding_(this), |
| 64 filesystem_(std::move(filesystem)), |
| 65 prefs_(new base::DictionaryValue()), |
| 66 read_only_(false), |
| 67 pref_filter_(std::move(pref_filter)), |
| 68 initialized_(false), |
| 69 filtering_in_progress_(false), |
| 70 pending_lossy_write_(false), |
| 71 read_error_(PREF_READ_ERROR_NONE) { |
| 72 DCHECK(!path_.empty()); |
| 73 } |
| 74 |
| 75 bool FilesystemJsonPrefStore::GetValue(const std::string& key, |
| 76 const base::Value** result) const { |
| 77 DCHECK(CalledOnValidThread()); |
| 78 |
| 79 base::Value* tmp = nullptr; |
| 80 if (!prefs_->Get(key, &tmp)) |
| 81 return false; |
| 82 |
| 83 if (result) |
| 84 *result = tmp; |
| 85 return true; |
| 86 } |
| 87 |
| 88 void FilesystemJsonPrefStore::AddObserver(PrefStore::Observer* observer) { |
| 89 DCHECK(CalledOnValidThread()); |
| 90 |
| 91 observers_.AddObserver(observer); |
| 92 } |
| 93 |
| 94 void FilesystemJsonPrefStore::RemoveObserver(PrefStore::Observer* observer) { |
| 95 DCHECK(CalledOnValidThread()); |
| 96 |
| 97 observers_.RemoveObserver(observer); |
| 98 } |
| 99 |
| 100 bool FilesystemJsonPrefStore::HasObservers() const { |
| 101 DCHECK(CalledOnValidThread()); |
| 102 |
| 103 return observers_.might_have_observers(); |
| 104 } |
| 105 |
| 106 bool FilesystemJsonPrefStore::IsInitializationComplete() const { |
| 107 DCHECK(CalledOnValidThread()); |
| 108 |
| 109 return initialized_; |
| 110 } |
| 111 |
| 112 bool FilesystemJsonPrefStore::GetMutableValue(const std::string& key, |
| 113 base::Value** result) { |
| 114 DCHECK(CalledOnValidThread()); |
| 115 |
| 116 return prefs_->Get(key, result); |
| 117 } |
| 118 |
| 119 void FilesystemJsonPrefStore::SetValue(const std::string& key, |
| 120 scoped_ptr<base::Value> value, |
| 121 uint32_t flags) { |
| 122 DCHECK(CalledOnValidThread()); |
| 123 |
| 124 DCHECK(value); |
| 125 base::Value* old_value = nullptr; |
| 126 prefs_->Get(key, &old_value); |
| 127 if (!old_value || !value->Equals(old_value)) { |
| 128 prefs_->Set(key, std::move(value)); |
| 129 ReportValueChanged(key, flags); |
| 130 } |
| 131 } |
| 132 |
| 133 void FilesystemJsonPrefStore::SetValueSilently(const std::string& key, |
| 134 scoped_ptr<base::Value> value, |
| 135 uint32_t flags) { |
| 136 DCHECK(CalledOnValidThread()); |
| 137 |
| 138 DCHECK(value); |
| 139 base::Value* old_value = nullptr; |
| 140 prefs_->Get(key, &old_value); |
| 141 if (!old_value || !value->Equals(old_value)) { |
| 142 prefs_->Set(key, std::move(value)); |
| 143 ScheduleWrite(flags); |
| 144 } |
| 145 } |
| 146 |
| 147 void FilesystemJsonPrefStore::RemoveValue(const std::string& key, |
| 148 uint32_t flags) { |
| 149 DCHECK(CalledOnValidThread()); |
| 150 |
| 151 if (prefs_->RemovePath(key, nullptr)) |
| 152 ReportValueChanged(key, flags); |
| 153 } |
| 154 |
| 155 void FilesystemJsonPrefStore::RemoveValueSilently(const std::string& key, |
| 156 uint32_t flags) { |
| 157 DCHECK(CalledOnValidThread()); |
| 158 |
| 159 prefs_->RemovePath(key, nullptr); |
| 160 ScheduleWrite(flags); |
| 161 } |
| 162 |
| 163 bool FilesystemJsonPrefStore::ReadOnly() const { |
| 164 DCHECK(CalledOnValidThread()); |
| 165 |
| 166 return read_only_; |
| 167 } |
| 168 |
| 169 PersistentPrefStore::PrefReadError FilesystemJsonPrefStore::GetReadError() |
| 170 const { |
| 171 DCHECK(CalledOnValidThread()); |
| 172 |
| 173 return read_error_; |
| 174 } |
| 175 |
| 176 PersistentPrefStore::PrefReadError FilesystemJsonPrefStore::ReadPrefs() { |
| 177 NOTREACHED(); |
| 178 // TODO(erg): Synchronously reading files makes no sense in a mojo world and |
| 179 // should be removed from the API. |
| 180 return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE; |
| 181 } |
| 182 |
| 183 void FilesystemJsonPrefStore::ReadPrefsAsync( |
| 184 ReadErrorDelegate* error_delegate) { |
| 185 DCHECK(CalledOnValidThread()); |
| 186 |
| 187 initialized_ = false; |
| 188 error_delegate_.reset(error_delegate); |
| 189 |
| 190 if (!directory_) { |
| 191 OpenFilesystem( |
| 192 Bind(&FilesystemJsonPrefStore::OnPreferencesReadStart, AsWeakPtr())); |
| 193 } else { |
| 194 OnPreferencesReadStart(); |
| 195 } |
| 196 } |
| 197 |
| 198 void FilesystemJsonPrefStore::CommitPendingWrite() { |
| 199 DCHECK(CalledOnValidThread()); |
| 200 |
| 201 // TODO(erg): This is another one of those cases where we have problems |
| 202 // because of mismatch between the models used in the pref service versus |
| 203 // here. Most of the time, CommitPendingWrite() is called from |
| 204 // PrefService. However, in JSONPrefStore, we also call this method on |
| 205 // shutdown and thus need to synchronously write. But in mojo:filesystem, |
| 206 // everything is done asynchronously. So we're sort of stuck until we can |
| 207 // change the interface, which we'll do in the longer term. |
| 208 |
| 209 SchedulePendingLossyWrites(); |
| 210 } |
| 211 |
| 212 void FilesystemJsonPrefStore::SchedulePendingLossyWrites() { |
| 213 // This method is misnamed for the sake of the interface. Given that writing |
| 214 // is already asynchronous in this new world, "sheduleing" a pending write |
| 215 // just starts the asynchronous process. |
| 216 if (pending_lossy_write_) |
| 217 PerformWrite(); |
| 218 } |
| 219 |
| 220 void FilesystemJsonPrefStore::ReportValueChanged(const std::string& key, |
| 221 uint32_t flags) { |
| 222 DCHECK(CalledOnValidThread()); |
| 223 |
| 224 if (pref_filter_) |
| 225 pref_filter_->FilterUpdate(key); |
| 226 |
| 227 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); |
| 228 |
| 229 ScheduleWrite(flags); |
| 230 } |
| 231 |
| 232 void FilesystemJsonPrefStore::OnFileSystemShutdown() {} |
| 233 |
| 234 void FilesystemJsonPrefStore::OnFileRead(scoped_ptr<ReadResult> read_result) { |
| 235 DCHECK(CalledOnValidThread()); |
| 236 |
| 237 DCHECK(read_result); |
| 238 |
| 239 scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue); |
| 240 |
| 241 read_error_ = read_result->error; |
| 242 |
| 243 switch (read_error_) { |
| 244 case PREF_READ_ERROR_ACCESS_DENIED: |
| 245 case PREF_READ_ERROR_FILE_OTHER: |
| 246 case PREF_READ_ERROR_FILE_LOCKED: |
| 247 case PREF_READ_ERROR_JSON_TYPE: |
| 248 case PREF_READ_ERROR_FILE_NOT_SPECIFIED: |
| 249 read_only_ = true; |
| 250 break; |
| 251 case PREF_READ_ERROR_NONE: |
| 252 DCHECK(read_result->value.get()); |
| 253 unfiltered_prefs.reset( |
| 254 static_cast<base::DictionaryValue*>(read_result->value.release())); |
| 255 break; |
| 256 case PREF_READ_ERROR_NO_FILE: |
| 257 // If the file just doesn't exist, maybe this is first run. In any case |
| 258 // there's no harm in writing out default prefs in this case. |
| 259 case PREF_READ_ERROR_JSON_PARSE: |
| 260 case PREF_READ_ERROR_JSON_REPEAT: |
| 261 break; |
| 262 case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE: |
| 263 // This is a special error code to be returned by ReadPrefs when it |
| 264 // can't complete synchronously, it should never be returned by the read |
| 265 // operation itself. |
| 266 case PREF_READ_ERROR_MAX_ENUM: |
| 267 NOTREACHED(); |
| 268 break; |
| 269 } |
| 270 |
| 271 if (pref_filter_) { |
| 272 filtering_in_progress_ = true; |
| 273 const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback( |
| 274 base::Bind(&FilesystemJsonPrefStore::FinalizeFileRead, AsWeakPtr())); |
| 275 pref_filter_->FilterOnLoad(post_filter_on_load_callback, |
| 276 std::move(unfiltered_prefs)); |
| 277 } else { |
| 278 FinalizeFileRead(std::move(unfiltered_prefs), false); |
| 279 } |
| 280 } |
| 281 |
| 282 FilesystemJsonPrefStore::~FilesystemJsonPrefStore() { |
| 283 // TODO(erg): Now that writing is asynchronous, we can't really async write |
| 284 // prefs at shutdown. See comment in CommitPendingWrite(). |
| 285 } |
| 286 |
| 287 void FilesystemJsonPrefStore::FinalizeFileRead( |
| 288 scoped_ptr<base::DictionaryValue> prefs, |
| 289 bool schedule_write) { |
| 290 DCHECK(CalledOnValidThread()); |
| 291 |
| 292 filtering_in_progress_ = false; |
| 293 |
| 294 prefs_ = std::move(prefs); |
| 295 |
| 296 initialized_ = true; |
| 297 |
| 298 if (schedule_write) |
| 299 ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS); |
| 300 |
| 301 if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) |
| 302 error_delegate_->OnError(read_error_); |
| 303 |
| 304 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, |
| 305 OnInitializationCompleted(true)); |
| 306 |
| 307 return; |
| 308 } |
| 309 |
| 310 void FilesystemJsonPrefStore::ScheduleWrite(uint32_t flags) { |
| 311 if (read_only_) |
| 312 return; |
| 313 |
| 314 if (flags & LOSSY_PREF_WRITE_FLAG) |
| 315 pending_lossy_write_ = true; |
| 316 else |
| 317 PerformWrite(); |
| 318 } |
| 319 |
| 320 void FilesystemJsonPrefStore::PerformWrite() { |
| 321 if (!directory_) { |
| 322 OpenFilesystem( |
| 323 Bind(&FilesystemJsonPrefStore::OnTempFileWriteStart, AsWeakPtr())); |
| 324 } else { |
| 325 OnTempFileWriteStart(); |
| 326 } |
| 327 } |
| 328 |
| 329 void FilesystemJsonPrefStore::OpenFilesystem(base::Closure callback) { |
| 330 filesystem::FileSystemClientPtr client; |
| 331 binding_.Bind(GetProxy(&client)); |
| 332 |
| 333 filesystem_->OpenFileSystem( |
| 334 "origin", GetProxy(&directory_), std::move(client), |
| 335 base::Bind(&FilesystemJsonPrefStore::OnOpenFilesystem, AsWeakPtr(), |
| 336 callback)); |
| 337 } |
| 338 |
| 339 void FilesystemJsonPrefStore::OnOpenFilesystem(base::Closure callback, |
| 340 FileError err) { |
| 341 if (err != FileError::OK) { |
| 342 // Do real error checking. |
| 343 NOTIMPLEMENTED(); |
| 344 return; |
| 345 } |
| 346 |
| 347 callback.Run(); |
| 348 } |
| 349 |
| 350 void FilesystemJsonPrefStore::OnTempFileWriteStart() { |
| 351 // Open up a temporary file and truncate it. |
| 352 directory_->OpenFile( |
| 353 "tmp", GetProxy(&temporary_file_), kFlagWrite | kFlagCreate, |
| 354 Bind(&FilesystemJsonPrefStore::OnTempFileOpened, AsWeakPtr())); |
| 355 } |
| 356 |
| 357 void FilesystemJsonPrefStore::OnTempFileOpened(FileError err) { |
| 358 // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the |
| 359 // file can never fail. |
| 360 CHECK_EQ(FileError::OK, err); |
| 361 |
| 362 // Calculate what we want to write, and then write to the temporary file. |
| 363 pending_lossy_write_ = false; |
| 364 |
| 365 if (pref_filter_) |
| 366 pref_filter_->FilterSerializeData(prefs_.get()); |
| 367 |
| 368 std::string output; |
| 369 JSONStringValueSerializer serializer(&output); |
| 370 serializer.set_pretty_print(false); |
| 371 serializer.Serialize(*prefs_); |
| 372 |
| 373 temporary_file_->Write( |
| 374 mojo::Array<uint8_t>::From(output), 0, Whence::FROM_CURRENT, |
| 375 Bind(&FilesystemJsonPrefStore::OnTempFileWrite, AsWeakPtr())); |
| 376 } |
| 377 |
| 378 void FilesystemJsonPrefStore::OnTempFileWrite(FileError err, |
| 379 uint32_t num_bytes_written) { |
| 380 // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the |
| 381 // file can never fail. |
| 382 CHECK_EQ(FileError::OK, err); |
| 383 |
| 384 // Now that we've written the file, close it. |
| 385 temporary_file_->Close( |
| 386 Bind(&FilesystemJsonPrefStore::OnTempFileClosed, AsWeakPtr())); |
| 387 } |
| 388 |
| 389 void FilesystemJsonPrefStore::OnTempFileClosed(FileError err) { |
| 390 // TODO(erg): Error handling. The JsonPrefStore code assumes that writing the |
| 391 // file can never fail. |
| 392 CHECK_EQ(FileError::OK, err); |
| 393 |
| 394 temporary_file_.reset(); |
| 395 directory_->Rename( |
| 396 "tmp", path_, |
| 397 Bind(&FilesystemJsonPrefStore::OnTempFileRenamed, AsWeakPtr())); |
| 398 } |
| 399 |
| 400 void FilesystemJsonPrefStore::OnTempFileRenamed(FileError err) {} |
| 401 |
| 402 void FilesystemJsonPrefStore::OnPreferencesReadStart() { |
| 403 // TODO(erg): implement me. |
| 404 directory_->OpenFile( |
| 405 path_, GetProxy(&preferences_file_), kFlagRead | kFlagOpen, |
| 406 Bind(&FilesystemJsonPrefStore::OnPreferencesFileOpened, AsWeakPtr())); |
| 407 } |
| 408 |
| 409 void FilesystemJsonPrefStore::OnPreferencesFileOpened(FileError err) { |
| 410 // TODO(erg): Error handling. |
| 411 if (err == FileError::OK) { |
| 412 preferences_file_->ReadEntireFile( |
| 413 Bind(&FilesystemJsonPrefStore::OnPreferencesFileRead, AsWeakPtr())); |
| 414 } else { |
| 415 OnPreferencesFileRead(err, mojo::Array<uint8_t>()); |
| 416 } |
| 417 } |
| 418 |
| 419 void FilesystemJsonPrefStore::OnPreferencesFileRead( |
| 420 FileError err, |
| 421 mojo::Array<uint8_t> contents) { |
| 422 scoped_ptr<FilesystemJsonPrefStore::ReadResult> read_result( |
| 423 new FilesystemJsonPrefStore::ReadResult); |
| 424 // TODO(erg): Needs even better error handling. |
| 425 switch (err) { |
| 426 case FileError::IN_USE: |
| 427 case FileError::ACCESS_DENIED: { |
| 428 read_only_ = true; |
| 429 break; |
| 430 } |
| 431 case FileError::NOT_FOUND: { |
| 432 // If the file just doesn't exist, maybe this is the first run. Just |
| 433 // don't pass a value. |
| 434 read_result->error = PREF_READ_ERROR_NO_FILE; |
| 435 break; |
| 436 } |
| 437 default: { |
| 438 int error_code; |
| 439 std::string error_msg; |
| 440 JSONStringValueDeserializer deserializer(base::StringPiece( |
| 441 reinterpret_cast<char*>(&contents.front()), contents.size())); |
| 442 read_result->value = deserializer.Deserialize(&error_code, &error_msg); |
| 443 read_result->error = HandleReadErrors(read_result->value.get()); |
| 444 } |
| 445 } |
| 446 |
| 447 preferences_file_.reset(); |
| 448 |
| 449 OnFileRead(std::move(read_result)); |
| 450 } |
| 451 |
| 452 } // namespace filesystem |
| OLD | NEW |