#include "config.h"
#include "IDBIndex.h"
#if ENABLE(INDEXED_DATABASE)
#include "IDBBindingUtilities.h"
#include "IDBCursor.h"
#include "IDBDatabase.h"
#include "IDBDatabaseException.h"
#include "IDBKeyRangeData.h"
#include "IDBObjectStore.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
#include "Logging.h"
using namespace JSC;
namespace WebCore {
IDBIndex::IDBIndex(ScriptExecutionContext& context, const IDBIndexInfo& info, IDBObjectStore& objectStore)
: ActiveDOMObject(&context)
, m_info(info)
, m_objectStore(objectStore)
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
suspendIfNeeded();
}
IDBIndex::~IDBIndex()
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
}
const char* IDBIndex::activeDOMObjectName() const
{
return "IDBIndex";
}
bool IDBIndex::canSuspendForDocumentSuspension() const
{
return false;
}
bool IDBIndex::hasPendingActivity() const
{
return !m_objectStore.modernTransaction().isFinished();
}
const String& IDBIndex::name() const
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
return m_info.name();
}
RefPtr<IDBObjectStore> IDBIndex::objectStore()
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
return &m_objectStore;
}
const IDBKeyPath& IDBIndex::keyPath() const
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
return m_info.keyPath();
}
bool IDBIndex::unique() const
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
return m_info.unique();
}
bool IDBIndex::multiEntry() const
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
return m_info.multiEntry();
}
RefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext& context, IDBKeyRange* range, const String& directionString, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::openCursor");
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
if (m_deleted || m_objectStore.isDeleted()) {
ec.code = IDBDatabaseException::InvalidStateError;
ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The index or its object store has been deleted.");
return nullptr;
}
if (!m_objectStore.modernTransaction().isActive()) {
ec.code = IDBDatabaseException::TransactionInactiveError;
ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The transaction is inactive or finished.");
return nullptr;
}
IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec.code);
if (ec.code) {
ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The direction provided ('invalid-direction') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
return nullptr;
}
IDBKeyRangeData rangeData = range;
if (rangeData.lowerKey.isNull())
rangeData.lowerKey = IDBKeyData::minimum();
if (rangeData.upperKey.isNull())
rangeData.upperKey = IDBKeyData::maximum();
auto info = IDBCursorInfo::indexCursor(m_objectStore.modernTransaction(), m_objectStore.info().identifier(), m_info.identifier(), rangeData, direction, IndexedDB::CursorType::KeyAndValue);
return m_objectStore.modernTransaction().requestOpenCursor(context, *this, info);
}
RefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext& context, JSValue key, const String& direction, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::openCursor");
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec.code);
if (ec.code) {
ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBIndex': The parameter is not a valid key.");
return nullptr;
}
return openCursor(context, keyRange.get(), direction, ec);
}
RefPtr<IDBRequest> IDBIndex::count(ScriptExecutionContext& context, IDBKeyRange* range, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::count");
return doCount(context, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys(), ec);
}
RefPtr<IDBRequest> IDBIndex::count(ScriptExecutionContext& context, JSValue key, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::count");
auto exec = context.execState();
if (!exec) {
ec.code = IDBDatabaseException::UnknownError;
ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBIndex': Script execution context does not have an execution state.");
return 0;
}
Ref<IDBKey> idbKey = scriptValueToIDBKey(*exec, key);
if (!idbKey->isValid()) {
ec.code = IDBDatabaseException::DataError;
ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The parameter is not a valid key.");
return nullptr;
}
return doCount(context, IDBKeyRangeData(idbKey.ptr()), ec);
}
RefPtr<IDBRequest> IDBIndex::doCount(ScriptExecutionContext& context, const IDBKeyRangeData& range, ExceptionCodeWithMessage& ec)
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
if (m_deleted || m_objectStore.isDeleted()) {
ec.code = IDBDatabaseException::InvalidStateError;
ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The index or its object store has been deleted.");
return nullptr;
}
if (!range.isValid()) {
ec.code = IDBDatabaseException::DataError;
return nullptr;
}
auto& transaction = m_objectStore.modernTransaction();
if (!transaction.isActive()) {
ec.code = IDBDatabaseException::TransactionInactiveError;
ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBIndex': The transaction is inactive or finished.");
return nullptr;
}
return transaction.requestCount(context, *this, range);
}
RefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext& context, IDBKeyRange* range, const String& directionString, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::openKeyCursor");
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
if (m_deleted || m_objectStore.isDeleted()) {
ec.code = IDBDatabaseException::InvalidStateError;
ec.message = ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The index or its object store has been deleted.");
return nullptr;
}
if (!m_objectStore.modernTransaction().isActive()) {
ec.code = IDBDatabaseException::TransactionInactiveError;
ec.message = ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The transaction is inactive or finished.");
return nullptr;
}
IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec.code);
if (ec.code) {
ec.message = ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The direction provided ('invalid-direction') is not one of 'next', 'nextunique', 'prev', or 'prevunique'.");
return nullptr;
}
auto info = IDBCursorInfo::indexCursor(m_objectStore.modernTransaction(), m_objectStore.info().identifier(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyOnly);
return m_objectStore.modernTransaction().requestOpenCursor(context, *this, info);
}
RefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext& context, JSValue key, const String& direction, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::openKeyCursor");
RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec.code);
if (ec.code) {
ec.message = ASCIILiteral("Failed to execute 'openKeyCursor' on 'IDBIndex': The parameter is not a valid key.");
return nullptr;
}
return openKeyCursor(context, keyRange.get(), direction, ec);
}
RefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext& context, IDBKeyRange* range, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::get");
return doGet(context, IDBKeyRangeData(range), ec);
}
RefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext& context, JSValue key, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::get");
auto exec = context.execState();
if (!exec) {
ec.code = IDBDatabaseException::UnknownError;
ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBIndex': Script execution context does not have an execution state.");
return nullptr;
}
Ref<IDBKey> idbKey = scriptValueToIDBKey(*exec, key);
if (!idbKey->isValid()) {
ec.code = IDBDatabaseException::DataError;
ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The parameter is not a valid key.");
return nullptr;
}
return doGet(context, IDBKeyRangeData(idbKey.ptr()), ec);
}
RefPtr<IDBRequest> IDBIndex::doGet(ScriptExecutionContext& context, const IDBKeyRangeData& range, ExceptionCodeWithMessage& ec)
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
if (m_deleted || m_objectStore.isDeleted()) {
ec.code = IDBDatabaseException::InvalidStateError;
ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The index or its object store has been deleted.");
return nullptr;
}
if (range.isNull) {
ec.code = IDBDatabaseException::DataError;
return nullptr;
}
auto& transaction = m_objectStore.modernTransaction();
if (!transaction.isActive()) {
ec.code = IDBDatabaseException::TransactionInactiveError;
ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBIndex': The transaction is inactive or finished.");
return nullptr;
}
return transaction.requestGetValue(context, *this, range);
}
RefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext& context, IDBKeyRange* range, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::getKey");
return doGetKey(context, IDBKeyRangeData(range), ec);
}
RefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext& context, JSValue key, ExceptionCodeWithMessage& ec)
{
LOG(IndexedDB, "IDBIndex::getKey");
auto exec = context.execState();
if (!exec) {
ec.code = IDBDatabaseException::UnknownError;
ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBIndex': Script execution context does not have an execution state.");
return nullptr;
}
Ref<IDBKey> idbKey = scriptValueToIDBKey(*exec, key);
if (!idbKey->isValid()) {
ec.code = IDBDatabaseException::DataError;
ec.message = ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The parameter is not a valid key.");
return nullptr;
}
return doGetKey(context, IDBKeyRangeData(idbKey.ptr()), ec);
}
RefPtr<IDBRequest> IDBIndex::doGetKey(ScriptExecutionContext& context, const IDBKeyRangeData& range, ExceptionCodeWithMessage& ec)
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
if (m_deleted || m_objectStore.isDeleted()) {
ec.code = IDBDatabaseException::InvalidStateError;
ec.message = ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The index or its object store has been deleted.");
return nullptr;
}
if (range.isNull) {
ec.code = IDBDatabaseException::DataError;
return nullptr;
}
auto& transaction = m_objectStore.modernTransaction();
if (!transaction.isActive()) {
ec.code = IDBDatabaseException::TransactionInactiveError;
ec.message = ASCIILiteral("Failed to execute 'getKey' on 'IDBIndex': The transaction is inactive or finished.");
return nullptr;
}
return transaction.requestGetKey(context, *this, range);
}
void IDBIndex::markAsDeleted()
{
ASSERT(currentThread() == m_objectStore.modernTransaction().database().originThreadID());
ASSERT(!m_deleted);
m_deleted = true;
}
void IDBIndex::ref()
{
m_objectStore.ref();
}
void IDBIndex::deref()
{
m_objectStore.deref();
}
}
#endif // ENABLE(INDEXED_DATABASE)