From cf84a98babe6b59c890ab06a131805e279f4cd0b Mon Sep 17 00:00:00 2001 From: stelar7 Date: Tue, 13 May 2025 10:09:48 +0200 Subject: [PATCH] LibWeb/IDB: Implement IDBCursor::update --- Libraries/LibWeb/IndexedDB/IDBCursor.cpp | 80 ++++++++++++++++++++++++ Libraries/LibWeb/IndexedDB/IDBCursor.h | 3 + Libraries/LibWeb/IndexedDB/IDBCursor.idl | 2 +- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/Libraries/LibWeb/IndexedDB/IDBCursor.cpp b/Libraries/LibWeb/IndexedDB/IDBCursor.cpp index 7193d1b376d..ec52f9de8a0 100644 --- a/Libraries/LibWeb/IndexedDB/IDBCursor.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBCursor.cpp @@ -301,4 +301,84 @@ WebIDL::ExceptionOr IDBCursor::continue_primary_key(JS::Value key_param, J return {}; } +// https://w3c.github.io/IndexedDB/#cursor-effective-object-store +GC::Ref IDBCursor::effective_object_store() const +{ + return m_source_handle.visit( + [&](GC::Ref store) -> GC::Ref { + // If the source of a cursor is an object store, the effective object store of the cursor is that object store. + return store->store(); + }, + [&](GC::Ref index) -> GC::Ref { + // If the source of a cursor is an index, the effective object store of the cursor is that index’s referenced object store. + return index->object_store()->store(); + }); +} + +// https://w3c.github.io/IndexedDB/#dom-idbcursor-update +WebIDL::ExceptionOr> IDBCursor::update(JS::Value value) +{ + auto& realm = this->realm(); + + // 1. Let transaction be this’s transaction. + auto transaction = this->transaction(); + + // 2. 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 updating cursor"_string); + + // 3. If transaction is a read-only transaction, throw a "ReadOnlyError" DOMException. + if (transaction->is_readonly()) + return WebIDL::ReadOnlyError::create(realm, "Transaction is read-only while updating cursor"_string); + + // FIXME: 4. If this’s source or effective object store has been deleted, throw an "InvalidStateError" DOMException. + + // 5. If this’s got value flag is false, indicating that the cursor is being iterated or has iterated past its end, throw an "InvalidStateError" DOMException. + if (!m_got_value) + return WebIDL::InvalidStateError::create(realm, "Cursor is active or EOL while updating"_string); + + // 6. If this’s key only flag is true, throw an "InvalidStateError" DOMException. + if (m_key_only) + return WebIDL::InvalidStateError::create(realm, "Cursor is key-only while updating"_string); + + // 7. Let targetRealm be a user-agent defined Realm. + // NOTE: this is 'realm' above + + // 8. Let clone be a clone of value in targetRealm during transaction. Rethrow any exceptions. + auto clone = TRY(clone_in_realm(realm, value, transaction)); + + // 9. If this’s effective object store uses in-line keys, then: + auto effective_object_store = this->effective_object_store(); + if (effective_object_store->uses_inline_keys()) { + // 1. Let kpk be the result of extracting a key from a value using a key path with clone and the key path of this’s effective object store. Rethrow any exceptions. + auto kpk = TRY(extract_a_key_from_a_value_using_a_key_path(realm, clone, *effective_object_store->key_path())); + + // 2. If kpk is failure, invalid, or not equal to this’s effective key, throw a "DataError" DOMException. + if (kpk.is_error()) + return WebIDL::DataError::create(realm, "Key path is invalid"_string); + + auto kpk_value = kpk.release_value(); + if (kpk_value->is_invalid()) + return WebIDL::DataError::create(realm, "Key path is invalid"_string); + + if (!Key::equals(*kpk_value, *this->effective_key())) + return WebIDL::DataError::create(realm, "Key path is not equal to effective key"_string); + } + + // 10. Let operation be an algorithm to run store a record into an object store with this’s effective object store, clone, this’s effective key, and false. + auto operation = GC::Function()>::create(realm.heap(), [this, &realm, clone] -> WebIDL::ExceptionOr { + auto optional_key = TRY(store_a_record_into_an_object_store(realm, *this->effective_object_store(), clone, this->effective_key(), false)); + + if (!optional_key || optional_key->is_invalid()) + return JS::js_undefined(); + + return convert_a_key_to_a_value(realm, GC::Ref(*optional_key)); + }); + + // 11. Return the result (an IDBRequest) 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 cursor update with uuid {}", request->uuid()); + return request; +} + } diff --git a/Libraries/LibWeb/IndexedDB/IDBCursor.h b/Libraries/LibWeb/IndexedDB/IDBCursor.h index 93ac3561e90..74840171455 100644 --- a/Libraries/LibWeb/IndexedDB/IDBCursor.h +++ b/Libraries/LibWeb/IndexedDB/IDBCursor.h @@ -49,6 +49,8 @@ public: WebIDL::ExceptionOr continue_(JS::Value); WebIDL::ExceptionOr continue_primary_key(JS::Value, JS::Value); + WebIDL::ExceptionOr> update(JS::Value); + [[nodiscard]] JS::Value value() { return m_value.value_or(JS::js_undefined()); } [[nodiscard]] GC::Ref range() { return m_range; } [[nodiscard]] GC::Ptr position() { return m_position; } @@ -58,6 +60,7 @@ public: [[nodiscard]] GC::Ref transaction(); [[nodiscard]] CursorSource internal_source(); [[nodiscard]] GC::Ref effective_key() const; + [[nodiscard]] GC::Ref effective_object_store() const; void set_request(GC::Ptr request) { m_request = request; } void set_position(GC::Ptr position) { m_position = position; } diff --git a/Libraries/LibWeb/IndexedDB/IDBCursor.idl b/Libraries/LibWeb/IndexedDB/IDBCursor.idl index 9d9f02fc3a7..314660e03c0 100644 --- a/Libraries/LibWeb/IndexedDB/IDBCursor.idl +++ b/Libraries/LibWeb/IndexedDB/IDBCursor.idl @@ -10,7 +10,7 @@ interface IDBCursor { undefined advance([EnforceRange] unsigned long count); undefined continue(optional any key); undefined continuePrimaryKey(any key, any primaryKey); - [FIXME, NewObject] IDBRequest update(any value); + [NewObject] IDBRequest update(any value); [FIXME, NewObject] IDBRequest delete(); };