From 3fa1d1299c7f46bf9f2f2cbfa3e74340cd31741a Mon Sep 17 00:00:00 2001 From: stelar7 Date: Tue, 13 May 2025 22:41:18 +0200 Subject: [PATCH] LibWeb/IDB: Implement IDBIndex::getAll --- Libraries/LibWeb/IndexedDB/IDBIndex.cpp | 31 +++++++++++++++++++ Libraries/LibWeb/IndexedDB/IDBIndex.h | 1 + Libraries/LibWeb/IndexedDB/IDBIndex.idl | 2 +- .../LibWeb/IndexedDB/Internal/Algorithms.cpp | 31 +++++++++++++++++++ .../LibWeb/IndexedDB/Internal/Algorithms.h | 1 + Libraries/LibWeb/IndexedDB/Internal/Index.cpp | 14 +++++++++ Libraries/LibWeb/IndexedDB/Internal/Index.h | 1 + 7 files changed, 80 insertions(+), 1 deletion(-) diff --git a/Libraries/LibWeb/IndexedDB/IDBIndex.cpp b/Libraries/LibWeb/IndexedDB/IDBIndex.cpp index e823e4a6a0b..de71ac37e61 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.cpp @@ -203,4 +203,35 @@ WebIDL::ExceptionOr> IDBIndex::get_key(JS::Value query) return result; } +// https://w3c.github.io/IndexedDB/#dom-idbindex-getall +WebIDL::ExceptionOr> IDBIndex::get_all(Optional query, Optional count) +{ + auto& realm = this->realm(); + + // 1. Let transaction be this’s transaction. + auto transaction = this->transaction(); + + // 2. Let index be this’s index. + auto index = this->index(); + + // FIXME: 3. If index or index’s object store has been deleted, throw an "InvalidStateError" DOMException. + + // 4. If transaction’s 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()>::create(realm.heap(), [&realm, index, range, count] -> WebIDL::ExceptionOr { + 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; +} + } diff --git a/Libraries/LibWeb/IndexedDB/IDBIndex.h b/Libraries/LibWeb/IndexedDB/IDBIndex.h index bb52115f31d..90bdf696828 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.h +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.h @@ -31,6 +31,7 @@ public: [[nodiscard]] WebIDL::ExceptionOr> get(JS::Value); [[nodiscard]] WebIDL::ExceptionOr> get_key(JS::Value); + [[nodiscard]] WebIDL::ExceptionOr> get_all(Optional, Optional); [[nodiscard]] WebIDL::ExceptionOr> open_cursor(JS::Value, Bindings::IDBCursorDirection = Bindings::IDBCursorDirection::Next); // The transaction of an index handle is the transaction of its associated object store handle. diff --git a/Libraries/LibWeb/IndexedDB/IDBIndex.idl b/Libraries/LibWeb/IndexedDB/IDBIndex.idl index 23126d9a6b5..b904ac1b236 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.idl +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.idl @@ -9,7 +9,7 @@ interface IDBIndex { readonly attribute boolean unique; [NewObject] IDBRequest get(any query); [NewObject] IDBRequest getKey(any query); - [FIXME, NewObject] IDBRequest getAll(optional any query, optional [EnforceRange] unsigned long count); + [NewObject] IDBRequest getAll(optional any query, optional [EnforceRange] unsigned long count); [FIXME, NewObject] IDBRequest getAllKeys(optional any query, optional [EnforceRange] unsigned long count); [FIXME, NewObject] IDBRequest count(optional any query); [NewObject] IDBRequest openCursor(optional any query, optional IDBCursorDirection direction = "next"); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index 84dfbbb0921..db2027a73d3 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -1960,4 +1960,35 @@ JS::Value retrieve_a_value_from_an_index(JS::Realm& realm, GC::Ref index, return convert_a_key_to_a_value(realm, record->value); } +// https://w3c.github.io/IndexedDB/#retrieve-multiple-referenced-values-from-an-index +GC::Ref retrieve_multiple_referenced_values_from_an_index(JS::Realm& realm, GC::Ref index, GC::Ref range, Optional count) +{ + // 1. If count is not given or is 0 (zero), let count be infinity. + if (count.has_value() && *count == 0) + count = OptionalNone(); + + // 2. Let records be a list containing the first count records in index’s list of records whose key is in range. + auto records = index->first_n_in_range(range, count); + + // 3. Let list be an empty list. + auto list = MUST(JS::Array::create(realm, records.size())); + + // 4. For each record of records: + for (u32 i = 0; i < records.size(); ++i) { + auto& record = records[i]; + + // 1. Let serialized be record’s referenced value. + auto serialized = index->referenced_value(record); + + // 2. Let entry be ! StructuredDeserialize(serialized, targetRealm). + auto entry = MUST(HTML::structured_deserialize(realm.vm(), serialized, realm)); + + // 3. Append entry to list. + MUST(list->create_data_property_or_throw(i, entry)); + } + + // 5. Return list converted to a sequence. + return list; +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index 31403641941..d32cd13aaea 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -53,5 +53,6 @@ GC::Ref retrieve_multiple_values_from_an_object_store(JS::Realm&, GC: GC::Ref retrieve_multiple_keys_from_an_object_store(JS::Realm&, GC::Ref, GC::Ref, Optional); JS::Value retrieve_a_referenced_value_from_an_index(JS::Realm&, GC::Ref, GC::Ref); JS::Value retrieve_a_value_from_an_index(JS::Realm&, GC::Ref, GC::Ref); +GC::Ref retrieve_multiple_referenced_values_from_an_index(JS::Realm&, GC::Ref, GC::Ref, Optional); } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Index.cpp b/Libraries/LibWeb/IndexedDB/Internal/Index.cpp index 1689fec1336..71c247bf7fb 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Index.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Index.cpp @@ -83,4 +83,18 @@ Optional Index::first_in_range(GC::Ref range) }); } +GC::ConservativeVector Index::first_n_in_range(GC::Ref range, Optional count) +{ + GC::ConservativeVector records(range->heap()); + for (auto const& record : m_records) { + if (range->is_in_range(record.key)) + records.append(record); + + if (count.has_value() && records.size() >= *count) + break; + } + + return records; +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Index.h b/Libraries/LibWeb/IndexedDB/Internal/Index.h index a71dfeb040d..e002583ca3a 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Index.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Index.h @@ -42,6 +42,7 @@ public: [[nodiscard]] bool has_record_with_key(GC::Ref key); void clear_records(); Optional first_in_range(GC::Ref range); + GC::ConservativeVector first_n_in_range(GC::Ref range, Optional count); HTML::SerializationRecord referenced_value(IndexRecord const& index_record) const;