|  | // 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 <stddef.h> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/location.h" | 
|  | #include "base/threading/thread_task_runner_handle.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_consumer.h" | 
|  | #include "components/webdata/common/web_database_backend.h" | 
|  |  | 
|  | using base::Bind; | 
|  | using base::FilePath; | 
|  |  | 
|  | // Receives messages from the backend on the DB sequence, posts them to | 
|  | // WebDatabaseService on the UI sequence. | 
|  | class WebDatabaseService::BackendDelegate | 
|  | : public WebDatabaseBackend::Delegate { | 
|  | public: | 
|  | BackendDelegate(const base::WeakPtr<WebDatabaseService>& web_database_service) | 
|  | : web_database_service_(web_database_service), | 
|  | callback_task_runner_(base::ThreadTaskRunnerHandle::Get()) {} | 
|  |  | 
|  | void DBLoaded(sql::InitStatus status, | 
|  | const std::string& diagnostics) override { | 
|  | callback_task_runner_->PostTask( | 
|  | FROM_HERE, base::BindOnce(&WebDatabaseService::OnDatabaseLoadDone, | 
|  | web_database_service_, status, diagnostics)); | 
|  | } | 
|  | private: | 
|  | const base::WeakPtr<WebDatabaseService> web_database_service_; | 
|  | scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_; | 
|  | }; | 
|  |  | 
|  | WebDatabaseService::WebDatabaseService( | 
|  | const base::FilePath& path, | 
|  | scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, | 
|  | scoped_refptr<base::SingleThreadTaskRunner> db_task_runner) | 
|  | : base::RefCountedDeleteOnSequence<WebDatabaseService>(ui_task_runner), | 
|  | path_(path), | 
|  | db_loaded_(false), | 
|  | db_task_runner_(db_task_runner), | 
|  | weak_ptr_factory_(this) { | 
|  | DCHECK(ui_task_runner->RunsTasksInCurrentSequence()); | 
|  | DCHECK(db_task_runner_); | 
|  | } | 
|  |  | 
|  | WebDatabaseService::~WebDatabaseService() { | 
|  | } | 
|  |  | 
|  | void WebDatabaseService::AddTable(std::unique_ptr<WebDatabaseTable> table) { | 
|  | if (!web_db_backend_) { | 
|  | web_db_backend_ = new WebDatabaseBackend( | 
|  | path_, new BackendDelegate(weak_ptr_factory_.GetWeakPtr()), | 
|  | db_task_runner_); | 
|  | } | 
|  | web_db_backend_->AddTable(std::move(table)); | 
|  | } | 
|  |  | 
|  | void WebDatabaseService::LoadDatabase() { | 
|  | DCHECK(web_db_backend_); | 
|  | db_task_runner_->PostTask( | 
|  | FROM_HERE, BindOnce(&WebDatabaseBackend::InitDatabase, web_db_backend_)); | 
|  | } | 
|  |  | 
|  | void WebDatabaseService::ShutdownDatabase() { | 
|  | db_loaded_ = false; | 
|  | loaded_callbacks_.clear(); | 
|  | error_callbacks_.clear(); | 
|  | weak_ptr_factory_.InvalidateWeakPtrs(); | 
|  | if (!web_db_backend_) | 
|  | return; | 
|  | db_task_runner_->PostTask( | 
|  | FROM_HERE, | 
|  | BindOnce(&WebDatabaseBackend::ShutdownDatabase, web_db_backend_)); | 
|  | } | 
|  |  | 
|  | WebDatabase* WebDatabaseService::GetDatabaseOnDB() const { | 
|  | DCHECK(db_task_runner_->RunsTasksInCurrentSequence()); | 
|  | return web_db_backend_ ? web_db_backend_->database() : nullptr; | 
|  | } | 
|  |  | 
|  | scoped_refptr<WebDatabaseBackend> WebDatabaseService::GetBackend() const { | 
|  | return web_db_backend_; | 
|  | } | 
|  |  | 
|  | void WebDatabaseService::ScheduleDBTask(const base::Location& from_here, | 
|  | const WriteTask& task) { | 
|  | DCHECK(web_db_backend_); | 
|  | std::unique_ptr<WebDataRequest> request = | 
|  | web_db_backend_->request_manager()->NewRequest(nullptr); | 
|  | db_task_runner_->PostTask( | 
|  | from_here, BindOnce(&WebDatabaseBackend::DBWriteTaskWrapper, | 
|  | web_db_backend_, task, std::move(request))); | 
|  | } | 
|  |  | 
|  | WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult( | 
|  | const base::Location& from_here, | 
|  | const ReadTask& task, | 
|  | WebDataServiceConsumer* consumer) { | 
|  | DCHECK(consumer); | 
|  | DCHECK(web_db_backend_); | 
|  | std::unique_ptr<WebDataRequest> request = | 
|  | web_db_backend_->request_manager()->NewRequest(consumer); | 
|  | WebDataServiceBase::Handle handle = request->GetHandle(); | 
|  | db_task_runner_->PostTask( | 
|  | from_here, BindOnce(&WebDatabaseBackend::DBReadTaskWrapper, | 
|  | web_db_backend_, task, std::move(request))); | 
|  | return handle; | 
|  | } | 
|  |  | 
|  | void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) { | 
|  | if (!web_db_backend_) | 
|  | return; | 
|  | web_db_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, | 
|  | const std::string& diagnostics) { | 
|  | // The INIT_OK_WITH_DATA_LOSS status is an initialization success but with | 
|  | // suspected data loss, so we also run the error callbacks. | 
|  | if (status != sql::INIT_OK) { | 
|  | // Notify that the database load failed. | 
|  | while (!error_callbacks_.empty()) { | 
|  | // The profile error callback is a message box that runs in a nested run | 
|  | // loop. While it's being displayed, other OnDatabaseLoadDone() will run | 
|  | // (posted from WebDatabaseBackend::Delegate::DBLoaded()). We need to make | 
|  | // sure that after the callback running the message box returns, it checks | 
|  | // |error_callbacks_| before it accesses it. | 
|  | DBLoadErrorCallback error_callback = error_callbacks_.back(); | 
|  | error_callbacks_.pop_back(); | 
|  | if (!error_callback.is_null()) | 
|  | error_callback.Run(status, diagnostics); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (status == sql::INIT_OK || status == sql::INIT_OK_WITH_DATA_LOSS) { | 
|  | db_loaded_ = true; | 
|  |  | 
|  | while (!loaded_callbacks_.empty()) { | 
|  | DBLoadedCallback loaded_callback = loaded_callbacks_.back(); | 
|  | loaded_callbacks_.pop_back(); | 
|  | if (!loaded_callback.is_null()) | 
|  | loaded_callback.Run(); | 
|  | } | 
|  | } | 
|  | } |