From 852323009f6ecd9596ca0c196ca00700326d47a4 Mon Sep 17 00:00:00 2001 From: stelar7 Date: Tue, 13 May 2025 22:54:08 +0200 Subject: [PATCH] LibWeb/IDB: Implement IDBIndex::count --- Libraries/LibWeb/IndexedDB/IDBIndex.cpp | 31 +++++++++++++++++++ Libraries/LibWeb/IndexedDB/IDBIndex.h | 1 + Libraries/LibWeb/IndexedDB/IDBIndex.idl | 2 +- .../LibWeb/IndexedDB/Internal/Algorithms.cpp | 10 ++++-- .../LibWeb/IndexedDB/Internal/Algorithms.h | 3 +- Libraries/LibWeb/IndexedDB/Internal/Index.cpp | 10 ++++++ Libraries/LibWeb/IndexedDB/Internal/Index.h | 1 + 7 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Libraries/LibWeb/IndexedDB/IDBIndex.cpp b/Libraries/LibWeb/IndexedDB/IDBIndex.cpp index d0212b88c68..647c38bb9b7 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.cpp @@ -265,4 +265,35 @@ WebIDL::ExceptionOr> IDBIndex::get_all_keys(Optional> IDBIndex::count(JS::Value query) +{ + 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 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()>::create(realm.heap(), [index, range] -> WebIDL::ExceptionOr { + 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; +} + } diff --git a/Libraries/LibWeb/IndexedDB/IDBIndex.h b/Libraries/LibWeb/IndexedDB/IDBIndex.h index c1e1585570d..0724b301458 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.h +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.h @@ -33,6 +33,7 @@ public: [[nodiscard]] WebIDL::ExceptionOr> get_key(JS::Value); [[nodiscard]] WebIDL::ExceptionOr> get_all(Optional, Optional); [[nodiscard]] WebIDL::ExceptionOr> get_all_keys(Optional, Optional); + [[nodiscard]] WebIDL::ExceptionOr> count(JS::Value); [[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 38f314f316b..c0b880727b8 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.idl +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.idl @@ -11,7 +11,7 @@ interface IDBIndex { [NewObject] IDBRequest getKey(any query); [NewObject] IDBRequest getAll(optional any query, optional [EnforceRange] unsigned long count); [NewObject] IDBRequest getAllKeys(optional any query, optional [EnforceRange] unsigned long count); - [FIXME, NewObject] IDBRequest count(optional any query); + [NewObject] IDBRequest count(optional any query); [NewObject] IDBRequest openCursor(optional any query, optional IDBCursorDirection direction = "next"); [FIXME, NewObject] IDBRequest openKeyCursor(optional any query, optional IDBCursorDirection direction = "next"); }; diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index 73e24d6f9c3..4037204a7e5 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -1453,10 +1453,16 @@ WebIDL::ExceptionOr> convert_a_value_to_a_key_range(JS::Rea } // https://w3c.github.io/IndexedDB/#count-the-records-in-a-range -JS::Value count_the_records_in_a_range(GC::Ref source, GC::Ref range) +JS::Value count_the_records_in_a_range(RecordSource source, GC::Ref range) { // 1. Let count be the number of records, if any, in source’s list of records with key in range. - auto count = source->count_records_in_range(range); + auto count = source.visit( + [range](GC::Ref object_store) { + return object_store->count_records_in_range(range); + }, + [range](GC::Ref index) { + return index->count_records_in_range(range); + }); // 2. Return count. return JS::Value(count); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index 10b88e91e91..ae2b1c50ae9 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -18,6 +18,7 @@ namespace Web::IndexedDB { using KeyPath = Variant>; +using RecordSource = Variant, GC::Ref>; WebIDL::ExceptionOr> open_a_database_connection(JS::Realm&, StorageAPI::StorageKey, String, Optional, GC::Ref); bool fire_a_version_change_event(JS::Realm&, FlyString const&, GC::Ref, u64, Optional); @@ -44,7 +45,7 @@ void inject_a_key_into_a_value_using_a_key_path(JS::Realm&, JS::Value, GC::Ref, GC::Ref); WebIDL::ExceptionOr> store_a_record_into_an_object_store(JS::Realm&, GC::Ref, JS::Value, GC::Ptr, bool); WebIDL::ExceptionOr> convert_a_value_to_a_key_range(JS::Realm&, Optional, bool = false); -JS::Value count_the_records_in_a_range(GC::Ref, GC::Ref); +JS::Value count_the_records_in_a_range(RecordSource, GC::Ref); WebIDL::ExceptionOr retrieve_a_value_from_an_object_store(JS::Realm&, GC::Ref, GC::Ref); GC::Ptr iterate_a_cursor(JS::Realm&, GC::Ref, GC::Ptr = nullptr, GC::Ptr = nullptr, u64 = 1); JS::Value clear_an_object_store(GC::Ref); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Index.cpp b/Libraries/LibWeb/IndexedDB/Internal/Index.cpp index 71c247bf7fb..ebd280b08d0 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Index.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Index.cpp @@ -97,4 +97,14 @@ GC::ConservativeVector Index::first_n_in_range(GC::Ref return records; } +u64 Index::count_records_in_range(GC::Ref range) +{ + u64 count = 0; + for (auto const& record : m_records) { + if (range->is_in_range(record.key)) + ++count; + } + return count; +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Index.h b/Libraries/LibWeb/IndexedDB/Internal/Index.h index e002583ca3a..730c4ad2ba0 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Index.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Index.h @@ -43,6 +43,7 @@ public: void clear_records(); Optional first_in_range(GC::Ref range); GC::ConservativeVector first_n_in_range(GC::Ref range, Optional count); + u64 count_records_in_range(GC::Ref range); HTML::SerializationRecord referenced_value(IndexRecord const& index_record) const;