blob: d65b97019e3c8f4fb1ca4869600e5b83a3622dc6 [file] [log] [blame]
// 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