From 61384473ca1aafea7801bfc323831f3cb33e11b7 Mon Sep 17 00:00:00 2001 From: stelar7 Date: Fri, 25 Apr 2025 18:08:40 +0200 Subject: [PATCH] LibWeb/IDB: Implement IDBDatabase::transaction() --- Libraries/LibWeb/IndexedDB/IDBDatabase.cpp | 49 +++++++++++++++++++ Libraries/LibWeb/IndexedDB/IDBDatabase.h | 9 ++++ Libraries/LibWeb/IndexedDB/IDBDatabase.idl | 3 +- Libraries/LibWeb/IndexedDB/IDBTransaction.cpp | 1 + Libraries/LibWeb/IndexedDB/IDBTransaction.h | 1 + 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/Libraries/LibWeb/IndexedDB/IDBDatabase.cpp b/Libraries/LibWeb/IndexedDB/IDBDatabase.cpp index 3194648286b..8c1b7c52512 100644 --- a/Libraries/LibWeb/IndexedDB/IDBDatabase.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBDatabase.cpp @@ -189,4 +189,53 @@ WebIDL::ExceptionOr IDBDatabase::delete_object_store(String const& name) return {}; } +WebIDL::ExceptionOr> IDBDatabase::transaction(Variant> store_names, Bindings::IDBTransactionMode mode, IDBTransactionOptions options) +{ + // 1. If a live upgrade transaction is associated with the connection, throw an "InvalidStateError" DOMException. + auto database = associated_database(); + if (database->upgrade_transaction()) + return WebIDL::InvalidStateError::create(realm(), "Upgrade transaction is live"_string); + + // 2. If this's close pending flag is true, then throw an "InvalidStateError" DOMException. + if (close_pending()) + return WebIDL::InvalidStateError::create(realm(), "Close pending"_string); + + // 3. Let scope be the set of unique strings in storeNames if it is a sequence, or a set containing one string equal to storeNames otherwise. + Vector scope; + if (store_names.has>()) { + scope = store_names.get>(); + } else { + scope.append(store_names.get()); + } + + // 4. If any string in scope is not the name of an object store in the connected database, throw a "NotFoundError" DOMException. + for (auto const& store_name : scope) { + if (!database->object_store_with_name(store_name)) + return WebIDL::NotFoundError::create(realm(), "Object store not found"_string); + } + + // 5. If scope is empty, throw an "InvalidAccessError" DOMException. + if (scope.is_empty()) + return WebIDL::InvalidAccessError::create(realm(), "Scope is empty"_string); + + // 6. If mode is not "readonly" or "readwrite", throw a TypeError. + if (mode != Bindings::IDBTransactionMode::Readonly && mode != Bindings::IDBTransactionMode::Readwrite) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid transaction mode"_string }; + + // 7. Let transaction be a newly created transaction with this connection, mode, options’ durability member, and the set of object stores named in scope. + Vector> scope_stores; + for (auto const& store_name : scope) { + auto store = database->object_store_with_name(store_name); + scope_stores.append(*store); + } + + auto transaction = IDBTransaction::create(realm(), *this, mode, options.durability, scope_stores); + + // 8. Set transaction’s cleanup event loop to the current event loop. + transaction->set_cleanup_event_loop(HTML::main_thread_event_loop()); + + // 9. Return an IDBTransaction object representing transaction. + return transaction; +} + } diff --git a/Libraries/LibWeb/IndexedDB/IDBDatabase.h b/Libraries/LibWeb/IndexedDB/IDBDatabase.h index 73578505ca3..fa25b082dd4 100644 --- a/Libraries/LibWeb/IndexedDB/IDBDatabase.h +++ b/Libraries/LibWeb/IndexedDB/IDBDatabase.h @@ -7,9 +7,11 @@ #pragma once #include +#include #include #include #include +#include #include #include #include @@ -24,6 +26,11 @@ struct IDBObjectStoreParameters { bool auto_increment { false }; }; +// https://w3c.github.io/IndexedDB/#dictdef-idbtransactionoptions +struct IDBTransactionOptions { + Bindings::IDBTransactionDurability durability = Bindings::IDBTransactionDurability::Default; +}; + // FIXME: I'm not sure if this object should do double duty as both the connection and the interface // but the spec treats it as such...? // https://w3c.github.io/IndexedDB/#IDBDatabase-interface @@ -66,6 +73,8 @@ public: WebIDL::ExceptionOr> create_object_store(String const&, IDBObjectStoreParameters const&); WebIDL::ExceptionOr delete_object_store(String const&); + WebIDL::ExceptionOr> transaction(Variant>, Bindings::IDBTransactionMode = Bindings::IDBTransactionMode::Readonly, IDBTransactionOptions = { .durability = Bindings::IDBTransactionDurability::Default }); + void close(); void set_onabort(WebIDL::CallbackType*); diff --git a/Libraries/LibWeb/IndexedDB/IDBDatabase.idl b/Libraries/LibWeb/IndexedDB/IDBDatabase.idl index af526a21863..c19fae35f0d 100644 --- a/Libraries/LibWeb/IndexedDB/IDBDatabase.idl +++ b/Libraries/LibWeb/IndexedDB/IDBDatabase.idl @@ -1,5 +1,6 @@ #import #import +#import #import [Exposed=(Window,Worker)] @@ -8,7 +9,7 @@ interface IDBDatabase : EventTarget { readonly attribute unsigned long long version; readonly attribute DOMStringList objectStoreNames; - [FIXME, NewObject] IDBTransaction transaction((DOMString or sequence) storeNames, optional IDBTransactionMode mode = "readonly", optional IDBTransactionOptions options = {}); + [NewObject] IDBTransaction transaction((DOMString or sequence) storeNames, optional IDBTransactionMode mode = "readonly", optional IDBTransactionOptions options = {}); undefined close(); [NewObject] IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters options = {}); undefined deleteObjectStore(DOMString name); diff --git a/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp b/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp index 4224f334e05..7d66f003191 100644 --- a/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include diff --git a/Libraries/LibWeb/IndexedDB/IDBTransaction.h b/Libraries/LibWeb/IndexedDB/IDBTransaction.h index 1894771363a..440e430e834 100644 --- a/Libraries/LibWeb/IndexedDB/IDBTransaction.h +++ b/Libraries/LibWeb/IndexedDB/IDBTransaction.h @@ -52,6 +52,7 @@ public: void set_error(GC::Ptr error) { m_error = error; } void set_associated_request(GC::Ptr request) { m_associated_request = request; } void set_aborted(bool aborted) { m_aborted = aborted; } + void set_cleanup_event_loop(GC::Ptr event_loop) { m_cleanup_event_loop = event_loop; } void set_state(TransactionState state) { m_state = state; } [[nodiscard]] bool is_upgrade_transaction() const { return m_mode == Bindings::IDBTransactionMode::Versionchange; }