blob: 342bb96fe9ceeb5812585c1a54f1270af691fa75 [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/modules/indexeddb/idb_index.h"
#include <memory>
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h"
#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
#include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index_or_idb_cursor.h"
#include "third_party/blink/renderer/bindings/modules/v8/to_v8_for_modules.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_database.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_object_store.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
using blink::WebIDBCallbacks;
using blink::WebIDBCursor;
using blink::WebIDBDatabase;
namespace blink {
IDBIndex::IDBIndex(scoped_refptr<IDBIndexMetadata> metadata,
IDBObjectStore* object_store,
IDBTransaction* transaction)
: metadata_(std::move(metadata)),
object_store_(object_store),
transaction_(transaction) {
DCHECK(object_store_);
DCHECK(transaction_);
DCHECK(metadata_.get());
DCHECK_NE(Id(), IDBIndexMetadata::kInvalidId);
}
IDBIndex::~IDBIndex() = default;
void IDBIndex::Trace(blink::Visitor* visitor) {
visitor->Trace(object_store_);
visitor->Trace(transaction_);
ScriptWrappable::Trace(visitor);
}
void IDBIndex::setName(const String& name, ExceptionState& exception_state) {
IDB_TRACE("IDBIndex::setName");
if (!transaction_->IsVersionChange()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
IDBDatabase::kNotVersionChangeTransactionErrorMessage);
return;
}
if (IsDeleted()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kIndexDeletedErrorMessage);
return;
}
if (!transaction_->IsActive()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kTransactionInactiveError,
transaction_->InactiveErrorMessage());
return;
}
if (this->name() == name)
return;
if (object_store_->ContainsIndex(name)) {
exception_state.ThrowDOMException(DOMExceptionCode::kConstraintError,
IDBDatabase::kIndexNameTakenErrorMessage);
return;
}
if (!BackendDB()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kDatabaseClosedErrorMessage);
return;
}
object_store_->RenameIndex(Id(), name);
}
ScriptValue IDBIndex::keyPath(ScriptState* script_state) const {
return ScriptValue::From(script_state, Metadata().key_path);
}
void IDBIndex::RevertMetadata(scoped_refptr<IDBIndexMetadata> old_metadata) {
metadata_ = std::move(old_metadata);
// An index's metadata will only get reverted if the index was in the
// database when the versionchange transaction started.
deleted_ = false;
}
IDBRequest* IDBIndex::openCursor(ScriptState* script_state,
const ScriptValue& range,
const String& direction_string,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::openCursorRequestSetup", "index_name",
metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::openCursor");
if (IsDeleted()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kIndexDeletedErrorMessage);
return nullptr;
}
if (!transaction_->IsActive()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kTransactionInactiveError,
transaction_->InactiveErrorMessage());
return nullptr;
}
WebIDBCursorDirection direction =
IDBCursor::StringToDirection(direction_string);
IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
ExecutionContext::From(script_state), range, exception_state);
if (exception_state.HadException())
return nullptr;
if (!BackendDB()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kDatabaseClosedErrorMessage);
return nullptr;
}
return openCursor(script_state, key_range, direction, std::move(metrics));
}
IDBRequest* IDBIndex::openCursor(ScriptState* script_state,
IDBKeyRange* key_range,
WebIDBCursorDirection direction,
IDBRequest::AsyncTraceState metrics) {
IDBRequest* request = IDBRequest::Create(
script_state, this, transaction_.Get(), std::move(metrics));
request->SetCursorDetails(indexed_db::kCursorKeyAndValue, direction);
BackendDB()->OpenCursor(transaction_->Id(), object_store_->Id(), Id(),
key_range, direction, false, kWebIDBTaskTypeNormal,
request->CreateWebCallbacks().release());
return request;
}
IDBRequest* IDBIndex::count(ScriptState* script_state,
const ScriptValue& range,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::countRequestSetup", "index_name",
metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::count");
if (IsDeleted()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kIndexDeletedErrorMessage);
return nullptr;
}
if (!transaction_->IsActive()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kTransactionInactiveError,
transaction_->InactiveErrorMessage());
return nullptr;
}
IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
ExecutionContext::From(script_state), range, exception_state);
if (exception_state.HadException())
return nullptr;
if (!BackendDB()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kDatabaseClosedErrorMessage);
return nullptr;
}
IDBRequest* request = IDBRequest::Create(
script_state, this, transaction_.Get(), std::move(metrics));
BackendDB()->Count(transaction_->Id(), object_store_->Id(), Id(), key_range,
request->CreateWebCallbacks().release());
return request;
}
IDBRequest* IDBIndex::openKeyCursor(ScriptState* script_state,
const ScriptValue& range,
const String& direction_string,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::openKeyCursorRequestSetup", "index_name",
metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::openKeyCursor");
if (IsDeleted()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kIndexDeletedErrorMessage);
return nullptr;
}
if (!transaction_->IsActive()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kTransactionInactiveError,
transaction_->InactiveErrorMessage());
return nullptr;
}
WebIDBCursorDirection direction =
IDBCursor::StringToDirection(direction_string);
IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
ExecutionContext::From(script_state), range, exception_state);
if (exception_state.HadException())
return nullptr;
if (!BackendDB()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kDatabaseClosedErrorMessage);
return nullptr;
}
IDBRequest* request = IDBRequest::Create(
script_state, this, transaction_.Get(), std::move(metrics));
request->SetCursorDetails(indexed_db::kCursorKeyOnly, direction);
BackendDB()->OpenCursor(transaction_->Id(), object_store_->Id(), Id(),
key_range, direction, true, kWebIDBTaskTypeNormal,
request->CreateWebCallbacks().release());
return request;
}
IDBRequest* IDBIndex::get(ScriptState* script_state,
const ScriptValue& key,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::getRequestSetup", "index_name", metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::get");
return GetInternal(script_state, key, exception_state, false,
std::move(metrics));
}
IDBRequest* IDBIndex::getAll(ScriptState* script_state,
const ScriptValue& range,
ExceptionState& exception_state) {
return getAll(script_state, range, std::numeric_limits<uint32_t>::max(),
exception_state);
}
IDBRequest* IDBIndex::getAll(ScriptState* script_state,
const ScriptValue& range,
unsigned long max_count,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::getAllRequestSetup", "index_name",
metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::getAll");
return GetAllInternal(script_state, range, max_count, exception_state, false,
std::move(metrics));
}
IDBRequest* IDBIndex::getAllKeys(ScriptState* script_state,
const ScriptValue& range,
ExceptionState& exception_state) {
return getAllKeys(script_state, range, std::numeric_limits<uint32_t>::max(),
exception_state);
}
IDBRequest* IDBIndex::getAllKeys(ScriptState* script_state,
const ScriptValue& range,
uint32_t max_count,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::getAllKeysRequestSetup", "index_name",
metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::getAllKeys");
return GetAllInternal(script_state, range, max_count, exception_state,
/*key_only=*/true, std::move(metrics));
}
IDBRequest* IDBIndex::getKey(ScriptState* script_state,
const ScriptValue& key,
ExceptionState& exception_state) {
IDB_TRACE1("IDBIndex::getKeyRequestSetup", "index_name",
metadata_->name.Utf8());
IDBRequest::AsyncTraceState metrics("IDBIndex::getKey");
return GetInternal(script_state, key, exception_state, true,
std::move(metrics));
}
IDBRequest* IDBIndex::GetInternal(ScriptState* script_state,
const ScriptValue& key,
ExceptionState& exception_state,
bool key_only,
IDBRequest::AsyncTraceState metrics) {
if (IsDeleted()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kIndexDeletedErrorMessage);
return nullptr;
}
if (!transaction_->IsActive()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kTransactionInactiveError,
transaction_->InactiveErrorMessage());
return nullptr;
}
IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
ExecutionContext::From(script_state), key, exception_state);
if (exception_state.HadException())
return nullptr;
if (!key_range) {
exception_state.ThrowDOMException(
DOMExceptionCode::kDataError,
IDBDatabase::kNoKeyOrKeyRangeErrorMessage);
return nullptr;
}
if (!BackendDB()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kDatabaseClosedErrorMessage);
return nullptr;
}
IDBRequest* request = IDBRequest::Create(
script_state, this, transaction_.Get(), std::move(metrics));
BackendDB()->Get(transaction_->Id(), object_store_->Id(), Id(), key_range,
key_only, request->CreateWebCallbacks().release());
return request;
}
IDBRequest* IDBIndex::GetAllInternal(ScriptState* script_state,
const ScriptValue& range,
unsigned long max_count,
ExceptionState& exception_state,
bool key_only,
IDBRequest::AsyncTraceState metrics) {
if (!max_count)
max_count = std::numeric_limits<uint32_t>::max();
if (IsDeleted()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kIndexDeletedErrorMessage);
return nullptr;
}
if (!transaction_->IsActive()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kTransactionInactiveError,
transaction_->InactiveErrorMessage());
return nullptr;
}
IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
ExecutionContext::From(script_state), range, exception_state);
if (exception_state.HadException())
return nullptr;
if (!BackendDB()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
IDBDatabase::kDatabaseClosedErrorMessage);
return nullptr;
}
IDBRequest* request = IDBRequest::Create(
script_state, this, transaction_.Get(), std::move(metrics));
BackendDB()->GetAll(transaction_->Id(), object_store_->Id(), Id(), key_range,
max_count, key_only,
request->CreateWebCallbacks().release());
return request;
}
WebIDBDatabase* IDBIndex::BackendDB() const {
return transaction_->BackendDB();
}
} // namespace blink