LibWeb/IDB: Implement IDBTransaction::commit

This commit is contained in:
stelar7 2025-04-09 23:11:12 +02:00 committed by Andrew Kaster
parent b6b00acbd1
commit da56c1b1eb
Notes: github-actions[bot] 2025-04-11 01:14:15 +00:00
5 changed files with 77 additions and 1 deletions

View file

@ -103,4 +103,19 @@ GC::Ref<HTML::DOMStringList> IDBTransaction::object_store_names()
return create_a_sorted_name_list(realm(), names);
}
// https://w3c.github.io/IndexedDB/#dom-idbtransaction-commit
WebIDL::ExceptionOr<void> IDBTransaction::commit()
{
auto& realm = this->realm();
// 1. If this's state is not active, then throw an "InvalidStateError" DOMException.
if (m_state != TransactionState::Active)
return WebIDL::InvalidStateError::create(realm, "Transaction is not active while commiting"_string);
// 2. Run commit a transaction with this.
commit_a_transaction(realm, *this);
return {};
}
}

View file

@ -44,6 +44,7 @@ public:
[[nodiscard]] GC::Ptr<IDBRequest> associated_request() const { return m_associated_request; }
[[nodiscard]] bool aborted() const { return m_aborted; }
[[nodiscard]] GC::Ref<HTML::DOMStringList> object_store_names();
[[nodiscard]] RequestList& request_list() { return m_request_list; }
[[nodiscard]] ReadonlySpan<GC::Ref<ObjectStore>> scope() const { return m_scope; }
[[nodiscard]] String uuid() const { return m_uuid; }
@ -59,6 +60,7 @@ public:
[[nodiscard]] bool is_finished() const { return m_state == TransactionState::Finished; }
WebIDL::ExceptionOr<void> abort();
WebIDL::ExceptionOr<void> commit();
void set_onabort(WebIDL::CallbackType*);
WebIDL::CallbackType* onabort();

View file

@ -10,7 +10,7 @@ interface IDBTransaction : EventTarget {
[SameObject, ImplementedAs=connection] readonly attribute IDBDatabase db;
readonly attribute DOMException? error;
[FIXME] IDBObjectStore objectStore(DOMString name);
[FIXME] undefined commit();
undefined commit();
undefined abort();
attribute EventHandler onabort;

View file

@ -14,6 +14,7 @@
#include <LibJS/Runtime/VM.h>
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/IndexedDB/IDBDatabase.h>
#include <LibWeb/IndexedDB/IDBRequest.h>
#include <LibWeb/IndexedDB/IDBTransaction.h>
@ -22,6 +23,7 @@
#include <LibWeb/IndexedDB/Internal/ConnectionQueueHandler.h>
#include <LibWeb/IndexedDB/Internal/Database.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/StorageAPI/StorageKey.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/Buffers.h>
@ -706,4 +708,60 @@ GC::Ref<HTML::DOMStringList> create_a_sorted_name_list(JS::Realm& realm, Vector<
return HTML::DOMStringList::create(realm, names);
}
// https://w3c.github.io/IndexedDB/#commit-a-transaction
void commit_a_transaction(JS::Realm& realm, GC::Ref<IDBTransaction> transaction)
{
// 1. Set transactions state to committing.
transaction->set_state(IDBTransaction::TransactionState::Committing);
dbgln_if(IDB_DEBUG, "commit_a_transaction: transaction {} is committing", transaction->uuid());
// 2. Run the following steps in parallel:
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, transaction]() {
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
// 1. Wait until every item in transactions request list is processed.
HTML::main_thread_event_loop().spin_until(GC::create_function(realm.vm().heap(), [transaction]() {
if constexpr (IDB_DEBUG) {
dbgln("commit_a_transaction: waiting for step 1");
dbgln("requests in queue:");
for (auto const& request : transaction->request_list()) {
dbgln(" - {} = {}", request->uuid(), request->processed() ? "processed"sv : "not processed"sv);
}
}
return transaction->request_list().all_requests_processed();
}));
// 2. If transactions state is no longer committing, then terminate these steps.
if (transaction->state() != IDBTransaction::TransactionState::Committing)
return;
// FIXME: 3. Attempt to write any outstanding changes made by transaction to the database, considering transactions durability hint.
// FIXME: 4. If an error occurs while writing the changes to the database, then run abort a transaction with transaction and an appropriate type for the error, for example "QuotaExceededError" or "UnknownError" DOMException, and terminate these steps.
// 5. 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 transactions connection's associated database's upgrade transaction to null.
if (transaction->is_upgrade_transaction())
transaction->connection()->associated_database()->set_upgrade_transaction(nullptr);
// 2. Set transactions state to finished.
transaction->set_state(IDBTransaction::TransactionState::Finished);
// 3. Fire an event named complete at transaction.
transaction->dispatch_event(DOM::Event::create(transaction->realm(), HTML::EventNames::complete));
// 4. If transaction is an upgrade transaction, then let request be the request associated with transaction and set requests transaction to null.
if (transaction->is_upgrade_transaction()) {
auto request = transaction->associated_request();
request->set_transaction(nullptr);
// Ad-hoc: Clear the two-way binding.
transaction->set_associated_request(nullptr);
}
}));
}));
}
}

View file

@ -27,5 +27,6 @@ void abort_a_transaction(GC::Ref<IDBTransaction>, GC::Ptr<WebIDL::DOMException>)
JS::Value convert_a_key_to_a_value(JS::Realm&, GC::Ref<Key>);
bool is_valid_key_path(KeyPath const&);
GC::Ref<HTML::DOMStringList> create_a_sorted_name_list(JS::Realm&, Vector<String>);
void commit_a_transaction(JS::Realm&, GC::Ref<IDBTransaction>);
}