| // Copyright 2013 The Chromium Authors |
| // 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/check.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "components/os_crypt/async/browser/os_crypt_async.h" |
| #include "components/os_crypt/async/common/encryptor.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" |
| |
| // Receives messages from the backend on the DB sequence, posts them to |
| // WebDatabaseService on the UI sequence. |
| class WebDatabaseService::BackendDelegate |
| : public WebDatabaseBackend::Delegate { |
| public: |
| explicit BackendDelegate( |
| const base::WeakPtr<WebDatabaseService>& web_database_service) |
| : web_database_service_(web_database_service), |
| callback_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {} |
| |
| 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::SequencedTaskRunner> callback_task_runner_; |
| }; |
| |
| WebDatabaseService::WebDatabaseService( |
| const base::FilePath& path, |
| scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
| scoped_refptr<base::SequencedTaskRunner> db_task_runner) |
| : base::RefCountedDeleteOnSequence<WebDatabaseService>(ui_task_runner), |
| path_(path), |
| db_task_runner_(std::move(db_task_runner)), |
| pending_task_queue_( |
| base::MakeRefCounted<base::DeferredSequencedTaskRunner>( |
| db_task_runner_)) { |
| DCHECK(ui_task_runner->RunsTasksInCurrentSequence()); |
| DCHECK(db_task_runner_); |
| } |
| |
| WebDatabaseService::~WebDatabaseService() = default; |
| |
| void WebDatabaseService::AddTable(std::unique_ptr<WebDatabaseTable> table) { |
| CHECK(!pending_task_queue_->Started()) |
| << "Cannot call AddTable after LoadDatabase."; |
| if (!web_db_backend_) { |
| web_db_backend_ = base::MakeRefCounted<WebDatabaseBackend>( |
| path_, |
| std::make_unique<BackendDelegate>(weak_ptr_factory_.GetWeakPtr()), |
| db_task_runner_); |
| } |
| web_db_backend_->AddTable(std::move(table)); |
| } |
| |
| void WebDatabaseService::CompleteLoadDatabase( |
| os_crypt_async::Encryptor encryptor) { |
| DCHECK(web_db_backend_); |
| // All AddTable calls must have happened by the time LoadDatabase is called. |
| web_db_backend_->MaybeInitEncryptorOnUiSequence(std::move(encryptor)); |
| // This ensures that the InitDatabase task gets executed on the DB task runner |
| // before any database tasks. |
| db_task_runner_->PostTask( |
| FROM_HERE, BindOnce(&WebDatabaseBackend::InitDatabase, web_db_backend_)); |
| pending_task_queue_->Start(); |
| } |
| |
| void WebDatabaseService::LoadDatabase(os_crypt_async::OSCryptAsync* os_crypt) { |
| os_crypt->GetInstance( |
| base::BindOnce(&WebDatabaseService::CompleteLoadDatabase, this)); |
| } |
| |
| void WebDatabaseService::ShutdownDatabase() { |
| 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_; |
| } |
| |
| scoped_refptr<base::SequencedTaskRunner> WebDatabaseService::GetDbSequence() { |
| return pending_task_queue_; |
| } |
| |
| void WebDatabaseService::ScheduleDBTask(const base::Location& from_here, |
| WriteTask task) { |
| DCHECK(web_db_backend_); |
| std::unique_ptr<WebDataRequest> request = |
| web_db_backend_->request_manager()->NewRequest({}); |
| pending_task_queue_->PostTask( |
| from_here, |
| BindOnce(&WebDatabaseBackend::DBWriteTaskWrapper, web_db_backend_, |
| std::move(task), std::move(request))); |
| } |
| |
| WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult( |
| const base::Location& from_here, |
| ReadTask task, |
| WebDataServiceConsumer* consumer) { |
| return ScheduleDBTaskWithResult( |
| from_here, std::move(task), |
| base::BindOnce(&WebDataServiceConsumer::OnWebDataServiceRequestDone, |
| consumer->GetWebDataServiceConsumerWeakPtr())); |
| } |
| |
| WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult( |
| const base::Location& from_here, |
| ReadTask task, |
| WebDataServiceRequestCallback consumer) { |
| DCHECK(consumer); |
| DCHECK(web_db_backend_); |
| std::unique_ptr<WebDataRequest> request = |
| web_db_backend_->request_manager()->NewRequest(std::move(consumer)); |
| WebDataServiceBase::Handle handle = request->GetHandle(); |
| pending_task_queue_->PostTask( |
| from_here, |
| BindOnce(&WebDatabaseBackend::DBReadTaskWrapper, web_db_backend_, |
| std::move(task), std::move(request))); |
| return handle; |
| } |
| |
| void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) { |
| if (web_db_backend_) { |
| web_db_backend_->request_manager()->CancelRequest(h); |
| } |
| } |
| |
| void WebDatabaseService::RegisterDBErrorCallback(DBLoadErrorCallback callback) { |
| error_callbacks_.push_back(std::move(callback)); |
| } |
| |
| bool WebDatabaseService::UsesInMemoryDatabaseForTesting() const { |
| // This mimics what WebDatabase::Init() does internally, as it would require |
| // significant test-only boilerplate to actually fetch the authoritative |
| // boolean from the very underlying `sql::Database::in_memory_`. |
| return path_.value() == WebDatabase::kInMemoryPath; |
| } |
| |
| 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) { |
| return; |
| } |
| |
| // 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 = std::move(error_callbacks_.back()); |
| error_callbacks_.pop_back(); |
| if (!error_callback.is_null()) { |
| std::move(error_callback).Run(status, diagnostics); |
| } |
| } |
| } |