1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00
ladybird/Libraries/LibWeb/IndexedDB/IDBIndex.cpp

338 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Array.h>
#include <LibWeb/Bindings/IDBIndexPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/IndexedDB/IDBCursor.h>
#include <LibWeb/IndexedDB/IDBCursorWithValue.h>
#include <LibWeb/IndexedDB/IDBIndex.h>
namespace Web::IndexedDB {
GC_DEFINE_ALLOCATOR(IDBIndex);
IDBIndex::~IDBIndex() = default;
IDBIndex::IDBIndex(JS::Realm& realm, GC::Ref<Index> index, GC::Ref<IDBObjectStore> object_store)
: PlatformObject(realm)
, m_index(index)
, m_object_store_handle(object_store)
, m_name(index->name())
{
}
GC::Ref<IDBIndex> IDBIndex::create(JS::Realm& realm, GC::Ref<Index> index, GC::Ref<IDBObjectStore> object_store)
{
return realm.create<IDBIndex>(realm, index, object_store);
}
void IDBIndex::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(IDBIndex);
Base::initialize(realm);
}
void IDBIndex::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_index);
visitor.visit(m_object_store_handle);
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-name
WebIDL::ExceptionOr<void> IDBIndex::set_name(String const& value)
{
auto& realm = this->realm();
// 1. Let name be the given value.
auto const& name = value;
// 2. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 3. Let index be thiss index.
auto index = this->index();
// 4. If transaction is not an upgrade transaction, throw an "InvalidStateError" DOMException.
if (!transaction->is_upgrade_transaction())
return WebIDL::InvalidStateError::create(realm, "Transaction is not an upgrade transaction"_string);
// 5. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while updating index name"_string);
// FIXME: 6. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 7. If indexs name is equal to name, terminate these steps.
if (index->name() == name)
return {};
// 8. If an index named name already exists in indexs object store, throw a "ConstraintError" DOMException.
if (index->object_store()->index_set().contains(name))
return WebIDL::ConstraintError::create(realm, "An index with the given name already exists"_string);
// 9. Set indexs name to name.
index->set_name(name);
// NOTE: Update the key in the map so it still matches the name
auto old_value = m_object_store_handle->index_set().take(m_name).release_value();
m_object_store_handle->index_set().set(name, old_value);
// 10. Set thiss name to name.
m_name = name;
return {};
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-keypath
JS::Value IDBIndex::key_path() const
{
return m_index->key_path().visit(
[&](String const& value) -> JS::Value {
return JS::PrimitiveString::create(realm().vm(), value);
},
[&](Vector<String> const& value) -> JS::Value {
return JS::Array::create_from<String>(realm(), value.span(), [&](auto const& entry) -> JS::Value {
return JS::PrimitiveString::create(realm().vm(), entry);
});
});
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-opencursor
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::open_cursor(JS::Value query, Bindings::IDBCursorDirection direction)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
[[maybe_unused]] auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while opening cursor"_string);
// 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query));
// 6. Let cursor be a new cursor with its source handle set to this, undefined position, direction set to direction, got value flag set to false, undefined key and value, range set to range, and key only flag set to false.
auto cursor = IDBCursor::create(realm, GC::Ref(*this), {}, direction, IDBCursor::GotValue::No, {}, {}, range, IDBCursor::KeyOnly::No);
// 7. Let operation be an algorithm to run iterate a cursor with the current Realm record and cursor.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [&realm, cursor] -> WebIDL::ExceptionOr<JS::Value> {
return WebIDL::ExceptionOr<JS::Value>(iterate_a_cursor(realm, cursor));
});
// 8. Let request be the result of running asynchronously execute a request with this and operation.
auto request = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
// 9. Set cursors request to request.
cursor->set_request(request);
// 10. Return request.
return request;
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-get
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::get(JS::Value query)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while getting"_string);
// 5. Let range be the result of converting a value to a key range with query and true. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query, true));
// 6. Let operation be an algorithm to run retrieve a referenced value from an index with the current Realm record, index, and range.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [&realm, index, range] -> WebIDL::ExceptionOr<JS::Value> {
return retrieve_a_referenced_value_from_an_index(realm, index, range);
});
// 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
auto result = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
dbgln_if(IDB_DEBUG, "Executing request for get with uuid {}", result->uuid());
return result;
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-getkey
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::get_key(JS::Value query)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while getting key"_string);
// 5. Let range be the result of converting a value to a key range with query and true. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query, true));
// 6. Let operation be an algorithm to run retrieve a value from an index with index and range.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [&realm, index, range] -> WebIDL::ExceptionOr<JS::Value> {
return retrieve_a_value_from_an_index(realm, index, range);
});
// 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
auto result = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
dbgln_if(IDB_DEBUG, "Executing request for get key with uuid {}", result->uuid());
return result;
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-getall
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::get_all(Optional<JS::Value> query, Optional<WebIDL::UnsignedLong> count)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while getting all"_string);
// 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query));
// 6. Let operation be an algorithm to run retrieve multiple referenced values from an index with the current Realm record, index, range, and count if given.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [&realm, index, range, count] -> WebIDL::ExceptionOr<JS::Value> {
return retrieve_multiple_referenced_values_from_an_index(realm, index, range, count);
});
// 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
auto result = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
dbgln_if(IDB_DEBUG, "Executing request for get all with uuid {}", result->uuid());
return result;
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-getallkeys
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::get_all_keys(Optional<JS::Value> query, Optional<WebIDL::UnsignedLong> count)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while getting all keys"_string);
// 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query));
// 6. Let operation be an algorithm to run retrieve multiple values from an index with index, range, and count if given.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [&realm, index, range, count] -> WebIDL::ExceptionOr<JS::Value> {
return retrieve_multiple_values_from_an_index(realm, index, range, count);
});
// 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
auto result = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
dbgln_if(IDB_DEBUG, "Executing request for get all keys with uuid {}", result->uuid());
return result;
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-count
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::count(JS::Value query)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while counting"_string);
// 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query));
// 6. Let operation be an algorithm to run count the records in a range with index and range.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [index, range] -> WebIDL::ExceptionOr<JS::Value> {
return count_the_records_in_a_range(index, range);
});
// 7. Return the result (an IDBRequest) of running asynchronously execute a request with this and operation.
auto result = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
dbgln_if(IDB_DEBUG, "Executing request for count with uuid {}", result->uuid());
return result;
}
// https://w3c.github.io/IndexedDB/#dom-idbindex-openkeycursor
WebIDL::ExceptionOr<GC::Ref<IDBRequest>> IDBIndex::open_key_cursor(JS::Value query, Bindings::IDBCursorDirection direction)
{
auto& realm = this->realm();
// 1. Let transaction be thiss transaction.
auto transaction = this->transaction();
// 2. Let index be thiss index.
[[maybe_unused]] auto index = this->index();
// FIXME: 3. If index or indexs object store has been deleted, throw an "InvalidStateError" DOMException.
// 4. If transactions state is not active, then throw a "TransactionInactiveError" DOMException.
if (transaction->state() != IDBTransaction::TransactionState::Active)
return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while opening key cursor"_string);
// 5. Let range be the result of converting a value to a key range with query. Rethrow any exceptions.
auto range = TRY(convert_a_value_to_a_key_range(realm, query));
// 6. Let cursor be a new cursor with its source handle set to this, undefined position, direction set to direction, got value flag set to false, undefined key and value, range set to range, and key only flag set to true.
auto cursor = IDBCursor::create(realm, GC::Ref(*this), {}, direction, IDBCursor::GotValue::No, {}, {}, range, IDBCursor::KeyOnly::Yes);
// 7. Let operation be an algorithm to run iterate a cursor with the current Realm record and cursor.
auto operation = GC::Function<WebIDL::ExceptionOr<JS::Value>()>::create(realm.heap(), [&realm, cursor] -> WebIDL::ExceptionOr<JS::Value> {
return WebIDL::ExceptionOr<JS::Value>(iterate_a_cursor(realm, cursor));
});
// 8. Let request be the result of running asynchronously execute a request with this and operation.
auto request = asynchronously_execute_a_request(realm, GC::Ref(*this), operation);
dbgln_if(IDB_DEBUG, "Executing request for open key cursor with uuid {}", request->uuid());
// 9. Set cursors request to request.
cursor->set_request(request);
// 10. Return request.
return request;
}
}