// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/value_store/value_store_frontend.h" #include #include "base/bind.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/macros.h" #include "base/trace_event/trace_event.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/value_store/leveldb_value_store.h" #include "extensions/browser/value_store/value_store_factory.h" using content::BrowserThread; using extensions::ValueStoreFactory; class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe { public: Backend(const scoped_refptr& store_factory, BackendType backend_type) : store_factory_(store_factory), backend_type_(backend_type) {} void Get(const std::string& key, const ValueStoreFrontend::ReadCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); LazyInit(); ValueStore::ReadResult result = storage_->Get(key); // Extract the value from the ReadResult and pass ownership of it to the // callback. scoped_ptr value; if (result->status().ok()) { result->settings().RemoveWithoutPathExpansion(key, &value); } else { LOG(WARNING) << "Reading " << key << " from " << db_path_.value() << " failed: " << result->status().message; } BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&ValueStoreFrontend::Backend::RunCallback, this, callback, base::Passed(&value))); } void Set(const std::string& key, scoped_ptr value) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); LazyInit(); // We don't need the old value, so skip generating changes. ValueStore::WriteResult result = storage_->Set( ValueStore::IGNORE_QUOTA | ValueStore::NO_GENERATE_CHANGES, key, *value.get()); LOG_IF(ERROR, !result->status().ok()) << "Error while writing " << key << " to " << db_path_.value(); } void Remove(const std::string& key) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); LazyInit(); storage_->Remove(key); } private: friend class base::RefCountedThreadSafe; virtual ~Backend() { if (storage_ && !BrowserThread::CurrentlyOn(BrowserThread::FILE)) BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, storage_.release()); } void LazyInit() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); if (storage_) return; TRACE_EVENT0("ValueStoreFrontend::Backend", "LazyInit"); switch (backend_type_) { case BackendType::RULES: storage_ = store_factory_->CreateRulesStore(); break; case BackendType::STATE: storage_ = store_factory_->CreateStateStore(); break; } } void RunCallback(const ValueStoreFrontend::ReadCallback& callback, scoped_ptr value) { DCHECK_CURRENTLY_ON(BrowserThread::UI); callback.Run(std::move(value)); } // The factory which will be used to lazily create the ValueStore when needed. // Used exclusively on the FILE thread. scoped_refptr store_factory_; BackendType backend_type_; // The actual ValueStore that handles persisting the data to disk. Used // exclusively on the FILE thread. scoped_ptr storage_; base::FilePath db_path_; DISALLOW_COPY_AND_ASSIGN(Backend); }; ValueStoreFrontend::ValueStoreFrontend( const scoped_refptr& store_factory, BackendType backend_type) : backend_(new Backend(store_factory, backend_type)) {} ValueStoreFrontend::~ValueStoreFrontend() { DCHECK(CalledOnValidThread()); } void ValueStoreFrontend::Get(const std::string& key, const ReadCallback& callback) { DCHECK(CalledOnValidThread()); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&ValueStoreFrontend::Backend::Get, backend_, key, callback)); } void ValueStoreFrontend::Set(const std::string& key, scoped_ptr value) { DCHECK(CalledOnValidThread()); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&ValueStoreFrontend::Backend::Set, backend_, key, base::Passed(&value))); } void ValueStoreFrontend::Remove(const std::string& key) { DCHECK(CalledOnValidThread()); BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&ValueStoreFrontend::Backend::Remove, backend_, key)); }