| // Copyright 2016 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 "content/browser/indexed_db/database_impl.h" |
| |
| #include <set> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/numerics/safe_math.h" |
| #include "base/sequence_checker.h" |
| #include "base/sequenced_task_runner.h" |
| #include "content/browser/bad_message.h" |
| #include "content/browser/indexed_db/indexed_db_connection.h" |
| #include "content/browser/indexed_db/indexed_db_context_impl.h" |
| #include "content/browser/indexed_db/indexed_db_dispatcher_host.h" |
| #include "content/browser/indexed_db/indexed_db_transaction.h" |
| #include "content/browser/indexed_db/transaction_impl.h" |
| #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h" |
| |
| using blink::IndexedDBIndexKeys; |
| using blink::IndexedDBKey; |
| using blink::IndexedDBKeyPath; |
| using blink::IndexedDBKeyRange; |
| using std::swap; |
| |
| namespace blink { |
| class IndexedDBKeyRange; |
| } |
| |
| namespace content { |
| namespace { |
| |
| const char kTransactionAlreadyExists[] = "Transaction already exists"; |
| |
| } // namespace |
| |
| DatabaseImpl::DatabaseImpl(std::unique_ptr<IndexedDBConnection> connection, |
| const url::Origin& origin, |
| IndexedDBDispatcherHost* dispatcher_host, |
| scoped_refptr<base::SequencedTaskRunner> idb_runner) |
| : dispatcher_host_(dispatcher_host), |
| indexed_db_context_(dispatcher_host->context()), |
| connection_(std::move(connection)), |
| origin_(origin), |
| idb_runner_(std::move(idb_runner)) { |
| DCHECK(idb_runner_->RunsTasksInCurrentSequence()); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(connection_); |
| indexed_db_context_->ConnectionOpened(origin_, connection_.get()); |
| } |
| |
| DatabaseImpl::~DatabaseImpl() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (connection_->IsConnected()) |
| connection_->Close(); |
| indexed_db_context_->ConnectionClosed(origin_, connection_.get()); |
| } |
| |
| void DatabaseImpl::RenameObjectStore(int64_t transaction_id, |
| int64_t object_store_id, |
| const base::string16& new_name) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange) { |
| mojo::ReportBadMessage( |
| "RenameObjectStore must be called from a version change transaction."); |
| } |
| |
| // Note: This doesn't schedule a task on the transaction because version |
| // change transactions pre-start in the OpenRequest inside IndexedDBDatabase. |
| connection_->database()->RenameObjectStore(transaction, object_store_id, |
| new_name); |
| } |
| |
| void DatabaseImpl::CreateTransaction( |
| blink::mojom::IDBTransactionAssociatedRequest transaction_request, |
| int64_t transaction_id, |
| const std::vector<int64_t>& object_store_ids, |
| blink::mojom::IDBTransactionMode mode) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| if (connection_->GetTransaction(transaction_id)) { |
| mojo::ReportBadMessage(kTransactionAlreadyExists); |
| return; |
| } |
| |
| IndexedDBTransaction* transaction = connection_->CreateTransaction( |
| transaction_id, |
| std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode, |
| new IndexedDBBackingStore::Transaction( |
| connection_->database()->backing_store())); |
| connection_->database()->RegisterAndScheduleTransaction(transaction); |
| |
| dispatcher_host_->CreateAndBindTransactionImpl( |
| std::move(transaction_request), origin_, transaction->AsWeakPtr()); |
| } |
| |
| void DatabaseImpl::Close() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| connection_->Close(); |
| } |
| |
| void DatabaseImpl::VersionChangeIgnored() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| connection_->VersionChangeIgnored(); |
| } |
| |
| void DatabaseImpl::AddObserver(int64_t transaction_id, |
| int32_t observer_id, |
| bool include_transaction, |
| bool no_records, |
| bool values, |
| uint32_t operation_types) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| IndexedDBObserver::Options options(include_transaction, no_records, values, |
| operation_types); |
| connection_->database()->AddPendingObserver(transaction, observer_id, |
| options); |
| } |
| |
| void DatabaseImpl::RemoveObservers(const std::vector<int32_t>& observers) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| connection_->RemoveObservers(observers); |
| } |
| |
| void DatabaseImpl::Get( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id, |
| const IndexedDBKeyRange& key_range, |
| bool key_only, |
| blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| scoped_refptr<IndexedDBCallbacks> callbacks( |
| new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, |
| std::move(callbacks_info), idb_runner_)); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| connection_->database()->Get(transaction, object_store_id, index_id, |
| std::make_unique<IndexedDBKeyRange>(key_range), |
| key_only, callbacks); |
| } |
| |
| void DatabaseImpl::GetAll(int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id, |
| const IndexedDBKeyRange& key_range, |
| bool key_only, |
| int64_t max_count, |
| blink::mojom::IDBDatabase::GetAllCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) { |
| IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError, |
| "Unknown error"); |
| std::move(callback).Run( |
| blink::mojom::IDBDatabaseGetAllResult::NewErrorResult( |
| blink::mojom::IDBError::New(error.code(), error.message()))); |
| return; |
| } |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) { |
| IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError, |
| "Unknown error"); |
| std::move(callback).Run( |
| blink::mojom::IDBDatabaseGetAllResult::NewErrorResult( |
| blink::mojom::IDBError::New(error.code(), error.message()))); |
| return; |
| } |
| |
| connection_->database()->GetAll( |
| dispatcher_host_->AsWeakPtr(), transaction, object_store_id, index_id, |
| std::make_unique<IndexedDBKeyRange>(key_range), key_only, max_count, |
| std::move(callback)); |
| } |
| |
| void DatabaseImpl::SetIndexKeys( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| const IndexedDBKey& primary_key, |
| const std::vector<IndexedDBIndexKeys>& index_keys) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange) { |
| mojo::ReportBadMessage( |
| "SetIndexKeys must be called from a version change transaction."); |
| } |
| |
| connection_->database()->SetIndexKeys( |
| transaction, object_store_id, std::make_unique<IndexedDBKey>(primary_key), |
| index_keys); |
| } |
| |
| void DatabaseImpl::SetIndexesReady(int64_t transaction_id, |
| int64_t object_store_id, |
| const std::vector<int64_t>& index_ids) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange) { |
| mojo::ReportBadMessage( |
| "SetIndexesReady must be called from a version change transaction."); |
| } |
| |
| connection_->database()->SetIndexesReady(transaction, object_store_id, |
| index_ids); |
| } |
| |
| void DatabaseImpl::OpenCursor( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id, |
| const IndexedDBKeyRange& key_range, |
| blink::mojom::IDBCursorDirection direction, |
| bool key_only, |
| blink::mojom::IDBTaskType task_type, |
| blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| scoped_refptr<IndexedDBCallbacks> callbacks( |
| new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, |
| std::move(callbacks_info), idb_runner_)); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange && |
| task_type == blink::mojom::IDBTaskType::Preemptive) { |
| mojo::ReportBadMessage( |
| "OpenCursor with |Preemptive| task type must be called from a version " |
| "change transaction."); |
| } |
| |
| connection_->database()->OpenCursor( |
| transaction, object_store_id, index_id, |
| std::make_unique<IndexedDBKeyRange>(key_range), direction, key_only, |
| task_type, std::move(callbacks)); |
| } |
| |
| void DatabaseImpl::Count( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id, |
| const IndexedDBKeyRange& key_range, |
| blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| scoped_refptr<IndexedDBCallbacks> callbacks( |
| new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, |
| std::move(callbacks_info), idb_runner_)); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| connection_->database()->Count(transaction, object_store_id, index_id, |
| std::make_unique<IndexedDBKeyRange>(key_range), |
| std::move(callbacks)); |
| } |
| |
| void DatabaseImpl::DeleteRange( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| const IndexedDBKeyRange& key_range, |
| blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| scoped_refptr<IndexedDBCallbacks> callbacks( |
| new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, |
| std::move(callbacks_info), idb_runner_)); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| connection_->database()->DeleteRange( |
| transaction, object_store_id, |
| std::make_unique<IndexedDBKeyRange>(key_range), std::move(callbacks)); |
| } |
| |
| void DatabaseImpl::GetKeyGeneratorCurrentNumber( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| scoped_refptr<IndexedDBCallbacks> callbacks( |
| new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, |
| std::move(callbacks_info), idb_runner_)); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| connection_->database()->GetKeyGeneratorCurrentNumber( |
| transaction, object_store_id, std::move(callbacks)); |
| } |
| |
| void DatabaseImpl::Clear( |
| int64_t transaction_id, |
| int64_t object_store_id, |
| blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| scoped_refptr<IndexedDBCallbacks> callbacks( |
| new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, |
| std::move(callbacks_info), idb_runner_)); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| connection_->database()->Clear(transaction, object_store_id, callbacks); |
| } |
| |
| void DatabaseImpl::CreateIndex(int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id, |
| const base::string16& name, |
| const IndexedDBKeyPath& key_path, |
| bool unique, |
| bool multi_entry) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange) { |
| mojo::ReportBadMessage( |
| "CreateIndex must be called from a version change transaction."); |
| } |
| |
| connection_->database()->CreateIndex(transaction, object_store_id, index_id, |
| name, key_path, unique, multi_entry); |
| } |
| |
| void DatabaseImpl::DeleteIndex(int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange) { |
| mojo::ReportBadMessage( |
| "DeleteIndex must be called from a version change transaction."); |
| } |
| |
| connection_->database()->DeleteIndex(transaction, object_store_id, index_id); |
| } |
| |
| void DatabaseImpl::RenameIndex(int64_t transaction_id, |
| int64_t object_store_id, |
| int64_t index_id, |
| const base::string16& new_name) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| if (transaction->mode() != blink::mojom::IDBTransactionMode::VersionChange) { |
| mojo::ReportBadMessage( |
| "RenameIndex must be called from a version change transaction."); |
| } |
| |
| connection_->database()->RenameIndex(transaction, object_store_id, index_id, |
| new_name); |
| } |
| |
| void DatabaseImpl::Abort(int64_t transaction_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!connection_->IsConnected()) |
| return; |
| |
| IndexedDBTransaction* transaction = |
| connection_->GetTransaction(transaction_id); |
| if (!transaction) |
| return; |
| |
| connection_->AbortTransaction( |
| transaction, |
| IndexedDBDatabaseError(blink::kWebIDBDatabaseExceptionAbortError, |
| "Transaction aborted by user.")); |
| } |
| |
| } // namespace content |