// Copyright 2013 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 "components/webdata/common/web_database_service.h"
#include "base/bind.h"
#include "base/location.h"
#include "components/webdata/common/web_data_request_manager.h"
#include "components/webdata/common/web_data_results.h"
#include "components/webdata/common/web_data_service_backend.h"
#include "components/webdata/common/web_data_service_consumer.h"
using base::Bind;
using base::FilePath;
// Receives messages from the backend on the DB thread, posts them to
// WebDatabaseService on the UI thread.
class WebDatabaseService::BackendDelegate :
public WebDataServiceBackend::Delegate {
public:
BackendDelegate(
const base::WeakPtr<WebDatabaseService>& web_database_service)
: web_database_service_(web_database_service),
callback_thread_(base::MessageLoopProxy::current()) {
}
virtual void DBLoaded(sql::InitStatus status) OVERRIDE {
callback_thread_->PostTask(
FROM_HERE,
base::Bind(&WebDatabaseService::OnDatabaseLoadDone,
web_database_service_,
status));
}
private:
const base::WeakPtr<WebDatabaseService> web_database_service_;
scoped_refptr<base::MessageLoopProxy> callback_thread_;
};
WebDatabaseService::WebDatabaseService(
const base::FilePath& path,
const scoped_refptr<base::MessageLoopProxy>& ui_thread,
const scoped_refptr<base::MessageLoopProxy>& db_thread)
: base::RefCountedDeleteOnMessageLoop<WebDatabaseService>(ui_thread),
path_(path),
weak_ptr_factory_(this),
db_loaded_(false),
db_thread_(db_thread) {
// WebDatabaseService should be instantiated on UI thread.
DCHECK(ui_thread->BelongsToCurrentThread());
// WebDatabaseService requires DB thread if instantiated.
DCHECK(db_thread.get());
}
WebDatabaseService::~WebDatabaseService() {
}
void WebDatabaseService::AddTable(scoped_ptr<WebDatabaseTable> table) {
if (!wds_backend_.get()) {
wds_backend_ = new WebDataServiceBackend(
path_, new BackendDelegate(weak_ptr_factory_.GetWeakPtr()),
db_thread_);
}
wds_backend_->AddTable(table.Pass());
}
void WebDatabaseService::LoadDatabase() {
DCHECK(wds_backend_.get());
db_thread_->PostTask(
FROM_HERE,
Bind(&WebDataServiceBackend::InitDatabase, wds_backend_));
}
void WebDatabaseService::UnloadDatabase() {
db_loaded_ = false;
if (!wds_backend_.get())
return;
db_thread_->PostTask(FROM_HERE,
Bind(&WebDataServiceBackend::ShutdownDatabase,
wds_backend_, true));
}
void WebDatabaseService::ShutdownDatabase() {
db_loaded_ = false;
weak_ptr_factory_.InvalidateWeakPtrs();
loaded_callbacks_.clear();
error_callbacks_.clear();
if (!wds_backend_.get())
return;
db_thread_->PostTask(FROM_HERE,
Bind(&WebDataServiceBackend::ShutdownDatabase,
wds_backend_, false));
}
WebDatabase* WebDatabaseService::GetDatabaseOnDB() const {
DCHECK(db_thread_->BelongsToCurrentThread());
if (!wds_backend_.get())
return NULL;
return wds_backend_->database();
}
scoped_refptr<WebDataServiceBackend> WebDatabaseService::GetBackend() const {
return wds_backend_;
}
void WebDatabaseService::ScheduleDBTask(
const tracked_objects::Location& from_here,
const WriteTask& task) {
if (!wds_backend_.get()) {
NOTREACHED() << "Task scheduled after Shutdown()";
return;
}
scoped_ptr<WebDataRequest> request(
new WebDataRequest(NULL, wds_backend_->request_manager().get()));
db_thread_->PostTask(from_here,
Bind(&WebDataServiceBackend::DBWriteTaskWrapper, wds_backend_,
task, base::Passed(&request)));
}
WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
const tracked_objects::Location& from_here,
const ReadTask& task,
WebDataServiceConsumer* consumer) {
DCHECK(consumer);
WebDataServiceBase::Handle handle = 0;
if (!wds_backend_.get()) {
NOTREACHED() << "Task scheduled after Shutdown()";
return handle;
}
scoped_ptr<WebDataRequest> request(
new WebDataRequest(consumer, wds_backend_->request_manager().get()));
handle = request->GetHandle();
db_thread_->PostTask(from_here,
Bind(&WebDataServiceBackend::DBReadTaskWrapper, wds_backend_,
task, base::Passed(&request)));
return handle;
}
void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) {
if (!wds_backend_.get())
return;
wds_backend_->request_manager()->CancelRequest(h);
}
void WebDatabaseService::RegisterDBLoadedCallback(
const DBLoadedCallback& callback) {
loaded_callbacks_.push_back(callback);
}
void WebDatabaseService::RegisterDBErrorCallback(
const DBLoadErrorCallback& callback) {
error_callbacks_.push_back(callback);
}
void WebDatabaseService::OnDatabaseLoadDone(sql::InitStatus status) {
if (status == sql::INIT_OK) {
db_loaded_ = true;
for (size_t i = 0; i < loaded_callbacks_.size(); i++) {
if (!loaded_callbacks_[i].is_null())
loaded_callbacks_[i].Run();
}
loaded_callbacks_.clear();
} else {
// Notify that the database load failed.
for (size_t i = 0; i < error_callbacks_.size(); i++) {
if (!error_callbacks_[i].is_null())
error_callbacks_[i].Run(status);
}
error_callbacks_.clear();
}
}