diff --git a/Libraries/LibWeb/IndexedDB/IDBRequest.h b/Libraries/LibWeb/IndexedDB/IDBRequest.h index 10c16405504..7564c6e6253 100644 --- a/Libraries/LibWeb/IndexedDB/IDBRequest.h +++ b/Libraries/LibWeb/IndexedDB/IDBRequest.h @@ -38,7 +38,7 @@ public: void set_error(GC::Ptr error) { m_error = error; } void set_processed(bool processed) { m_processed = processed; } void set_source(IDBRequestSource source) { m_source = source; } - void set_transaction(GC::Ref transaction) { m_transaction = transaction; } + void set_transaction(GC::Ptr transaction) { m_transaction = transaction; } void set_onsuccess(WebIDL::CallbackType*); WebIDL::CallbackType* onsuccess(); diff --git a/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp b/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp index 61408832412..b546c7c8f9a 100644 --- a/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp @@ -36,6 +36,7 @@ void IDBTransaction::visit_edges(Visitor& visitor) Base::visit_edges(visitor); visitor.visit(m_connection); visitor.visit(m_error); + visitor.visit(m_associated_request); } void IDBTransaction::set_onabort(WebIDL::CallbackType* event_handler) diff --git a/Libraries/LibWeb/IndexedDB/IDBTransaction.h b/Libraries/LibWeb/IndexedDB/IDBTransaction.h index 2528c05fe85..1777935abfe 100644 --- a/Libraries/LibWeb/IndexedDB/IDBTransaction.h +++ b/Libraries/LibWeb/IndexedDB/IDBTransaction.h @@ -36,10 +36,12 @@ public: [[nodiscard]] GC::Ptr error() const { return m_error; } [[nodiscard]] GC::Ref connection() const { return m_connection; } [[nodiscard]] Bindings::IDBTransactionDurability durability() const { return m_durability; } + [[nodiscard]] GC::Ptr associated_request() const { return m_associated_request; } void set_mode(Bindings::IDBTransactionMode mode) { m_mode = mode; } void set_state(TransactionState state) { m_state = state; } void set_error(GC::Ptr error) { m_error = error; } + void set_associated_request(GC::Ptr request) { m_associated_request = request; } [[nodiscard]] bool is_upgrade_transaction() const { return m_mode == Bindings::IDBTransactionMode::Versionchange; } [[nodiscard]] bool is_readonly() const { return m_mode == Bindings::IDBTransactionMode::Readonly; } @@ -63,5 +65,7 @@ private: Bindings::IDBTransactionDurability m_durability { Bindings::IDBTransactionDurability::Default }; TransactionState m_state; GC::Ptr m_error; + + GC::Ptr m_associated_request; }; } diff --git a/Libraries/LibWeb/IndexedDB/IDBTransaction.idl b/Libraries/LibWeb/IndexedDB/IDBTransaction.idl index f08d9a38d7a..bba6179e240 100644 --- a/Libraries/LibWeb/IndexedDB/IDBTransaction.idl +++ b/Libraries/LibWeb/IndexedDB/IDBTransaction.idl @@ -11,7 +11,7 @@ interface IDBTransaction : EventTarget { readonly attribute DOMException? error; [FIXME] IDBObjectStore objectStore(DOMString name); [FIXME] undefined commit(); - [FIXME] undefined abort(); + undefined abort(); attribute EventHandler onabort; attribute EventHandler oncomplete; diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index 6c0a66a9043..ffb517fb05f 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -326,7 +326,9 @@ void upgrade_a_database(JS::Realm& realm, GC::Ref connection, u64 v request->set_result(connection); // 2. Set request’s transaction to transaction. + // NOTE: We need to do a two-way binding here. request->set_transaction(transaction); + transaction->set_associated_request(request); // 3. Set request’s done flag to true. request->set_done(true); @@ -422,4 +424,60 @@ WebIDL::ExceptionOr delete_a_database(JS::Realm& realm, StorageAPI::Storage return version; } +// https://w3c.github.io/IndexedDB/#abort-a-transaction +void abort_a_transaction(IDBTransaction& transaction, GC::Ptr error) +{ + // FIXME: 1. All the changes made to the database by the transaction are reverted. + // For upgrade transactions this includes changes to the set of object stores and indexes, as well as the change to the version. + // Any object stores and indexes which were created during the transaction are now considered deleted for the purposes of other algorithms. + + // FIXME: 2. If transaction is an upgrade transaction, run the steps to abort an upgrade transaction with transaction. + // if (transaction.is_upgrade_transaction()) + // abort_an_upgrade_transaction(transaction); + + // 3. Set transaction’s state to finished. + transaction.set_state(IDBTransaction::TransactionState::Finished); + + // 4. If error is not null, set transaction’s error to error. + if (error) + transaction.set_error(error); + + // FIXME: 5. For each request of transaction’s request list, abort the steps to asynchronously execute a request for request, + // set request’s processed flag to true, and queue a task to run these steps: + // FIXME: 1. Set request’s done flag to true. + // FIXME: 2. Set request’s result to undefined. + // FIXME: 3. Set request’s error to a newly created "AbortError" DOMException. + // FIXME: 4. Fire an event named error at request with its bubbles and cancelable attributes initialized to true. + + // 6. Queue a task to run these steps: + HTML::queue_a_task(HTML::Task::Source::DatabaseAccess, nullptr, nullptr, GC::create_function(transaction.realm().vm().heap(), [&transaction]() { + // 1. If transaction is an upgrade transaction, then set transaction’s connection's associated database's upgrade transaction to null. + if (transaction.is_upgrade_transaction()) + transaction.connection()->associated_database()->set_upgrade_transaction(nullptr); + + // 2. Fire an event named abort at transaction with its bubbles attribute initialized to true. + transaction.dispatch_event(DOM::Event::create(transaction.realm(), HTML::EventNames::abort, { .bubbles = true })); + + // 3. If transaction is an upgrade transaction, then: + if (transaction.is_upgrade_transaction()) { + // 1. Let request be the open request associated with transaction. + auto request = transaction.associated_request(); + + // 2. Set request’s transaction to null. + // NOTE: Clear the two-way binding. + request->set_transaction(nullptr); + transaction.set_associated_request(nullptr); + + // 3. Set request’s result to undefined. + request->set_result(JS::js_undefined()); + + // 4. Set request’s processed flag to false. + request->set_processed(false); + + // 5. Set request’s done flag to false. + request->set_done(false); + } + })); +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index 806d1fe12c3..ae235611ed4 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -19,5 +19,6 @@ ErrorOr convert_a_value_to_a_key(JS::Realm&, JS::Value, Vector = void close_a_database_connection(IDBDatabase&, bool forced = false); void upgrade_a_database(JS::Realm&, GC::Ref, u64, GC::Ref); WebIDL::ExceptionOr delete_a_database(JS::Realm&, StorageAPI::StorageKey, String, GC::Ref); +void abort_a_transaction(IDBTransaction&, GC::Ptr); }