diff --git a/Libraries/LibWeb/IndexedDB/IDBRequest.cpp b/Libraries/LibWeb/IndexedDB/IDBRequest.cpp index 5297512be0a..3dd39c6bfa4 100644 --- a/Libraries/LibWeb/IndexedDB/IDBRequest.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBRequest.cpp @@ -32,6 +32,7 @@ void IDBRequest::visit_edges(Visitor& visitor) Base::visit_edges(visitor); visitor.visit(m_error); visitor.visit(m_result); + visitor.visit(m_transaction); } // https://w3c.github.io/IndexedDB/#dom-idbrequest-onsuccess diff --git a/Libraries/LibWeb/IndexedDB/IDBRequest.h b/Libraries/LibWeb/IndexedDB/IDBRequest.h index 6c3bbc25d3d..10c16405504 100644 --- a/Libraries/LibWeb/IndexedDB/IDBRequest.h +++ b/Libraries/LibWeb/IndexedDB/IDBRequest.h @@ -10,11 +10,13 @@ #include #include +#include namespace Web::IndexedDB { using IDBRequestSource = Variant, GC::Ref, GC::Ref>; +// https://w3c.github.io/IndexedDB/#request-api class IDBRequest : public DOM::EventTarget { WEB_PLATFORM_OBJECT(IDBRequest, DOM::EventTarget); GC_DECLARE_ALLOCATOR(IDBRequest); @@ -25,6 +27,7 @@ public: [[nodiscard]] bool done() const { return m_done; } [[nodiscard]] bool processed() const { return m_processed; } [[nodiscard]] IDBRequestSource source() const { return m_source; } + [[nodiscard]] GC::Ptr transaction() const { return m_transaction; } [[nodiscard]] Bindings::IDBRequestReadyState ready_state() const; [[nodiscard]] WebIDL::ExceptionOr> error() const; @@ -35,6 +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_onsuccess(WebIDL::CallbackType*); WebIDL::CallbackType* onsuccess(); @@ -57,7 +61,8 @@ private: GC::Ptr m_error; // A request has a source object. IDBRequestSource m_source; - // FIXME: A request has a transaction which is initially null. + // A request has a transaction which is initially null. + GC::Ptr m_transaction; }; } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index cf6d812d265..4e51fae300f 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -106,9 +107,8 @@ WebIDL::ExceptionOr> open_a_database_connection(JS::Realm& return true; })); - // FIXME: 6. Run upgrade a database using connection, version and request. - // NOTE: upgrade a database sets this flag, so we set it manually temporarily. - request->set_processed(true); + // 6. Run upgrade a database using connection, version and request. + upgrade_a_database(realm, connection, version, request); // 7. If connection was closed, return a newly created "AbortError" DOMException and abort these steps. if (connection->state() == IDBDatabase::ConnectionState::Closed) { @@ -282,4 +282,57 @@ void close_a_database_connection(IDBDatabase& connection, bool forced) connection.dispatch_event(DOM::Event::create(connection.realm(), HTML::EventNames::close)); } +void upgrade_a_database(JS::Realm& realm, GC::Ref connection, u64 version, GC::Ref request) +{ + // 1. Let db be connection’s database. + auto db = connection->associated_database(); + + // 2. Let transaction be a new upgrade transaction with connection used as connection. + auto transaction = IDBTransaction::create(realm, connection); + + // FIXME: 3. Set transaction’s scope to connection’s object store set. + + // 4. Set db’s upgrade transaction to transaction. + db->set_upgrade_transaction(transaction); + + // 5. Set transaction’s state to inactive. + transaction->set_state(IDBTransaction::TransactionState::Inactive); + + // FIXME: 6. Start transaction. + + // 7. Let old version be db’s version. + auto old_version = db->version(); + + // 8. Set db’s version to version. This change is considered part of the transaction, and so if the transaction is aborted, this change is reverted. + db->set_version(version); + + // 9. Set request’s processed flag to true. + request->set_processed(true); + + // 10. Queue a task to run these steps: + HTML::queue_a_task(HTML::Task::Source::DatabaseAccess, nullptr, nullptr, GC::create_function(realm.vm().heap(), [&realm, request, connection, transaction, old_version, version]() { + // 1. Set request’s result to connection. + request->set_result(connection); + + // 2. Set request’s transaction to transaction. + request->set_transaction(transaction); + + // 3. Set request’s done flag to true. + request->set_done(true); + + // 4. Set transaction’s state to active. + transaction->set_state(IDBTransaction::TransactionState::Active); + + // 5. Let didThrow be the result of firing a version change event named upgradeneeded at request with old version and version. + [[maybe_unused]] auto did_throw = fire_a_version_change_event(realm, HTML::EventNames::upgradeneeded, request, old_version, version); + + // 6. Set transaction’s state to inactive. + transaction->set_state(IDBTransaction::TransactionState::Inactive); + + // FIXME: 7. If didThrow is true, run abort a transaction with transaction and a newly created "AbortError" DOMException. + })); + + // FIXME: 11. Wait for transaction to finish. +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index 175ba0311fb..dddcdbc560b 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -17,5 +17,6 @@ WebIDL::ExceptionOr> open_a_database_connection(JS::Realm&, bool fire_a_version_change_event(JS::Realm&, FlyString const&, GC::Ref, u64, Optional); 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); } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Database.cpp b/Libraries/LibWeb/IndexedDB/Internal/Database.cpp index 456fe770844..d6d94585788 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Database.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Database.cpp @@ -25,6 +25,7 @@ void Database::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_associated_connections); + visitor.visit(m_upgrade_transaction); } ConnectionQueue& ConnectionQueueHandler::for_key_and_name(StorageAPI::StorageKey& key, String& name) diff --git a/Libraries/LibWeb/IndexedDB/Internal/Database.h b/Libraries/LibWeb/IndexedDB/Internal/Database.h index 0da84efaffa..d20de43d20e 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Database.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Database.h @@ -25,6 +25,8 @@ public: u64 version() const { return m_version; } String name() const { return m_name; } + void set_upgrade_transaction(GC::Ptr transaction) { m_upgrade_transaction = transaction; } + void associate(GC::Ref connection) { m_associated_connections.append(connection); } ReadonlySpan> associated_connections() { return m_associated_connections; } Vector> associated_connections_except(IDBDatabase& connection) @@ -66,7 +68,8 @@ private: // A database has a version. When a database is first created, its version is 0 (zero). u64 m_version { 0 }; - // FIXME: A database has at most one associated upgrade transaction, which is either null or an upgrade transaction, and is initially null. + // A database has at most one associated upgrade transaction, which is either null or an upgrade transaction, and is initially null. + GC::Ptr m_upgrade_transaction; }; } diff --git a/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt b/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt index fe2e9c44692..1bb98ac6fb6 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt @@ -9,27 +9,27 @@ Found 24 tests 12 Pass 12 Fail Details -Result Test Name MessageFail Database readback sort - String < Array Cannot access property "transaction" on undefined object "db" +Result Test Name MessageFail Database readback sort - String < Array undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - String < Array -Fail Database readback sort - float < String Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - float < String undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - float < String -Fail Database readback sort - float < Date Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - float < Date undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - float < Date -Fail Database readback sort - float < Date < String < Array Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - float < Date < String < Array undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - float < Date < String < Array -Fail Database readback sort - Date(1 sec ago) < Date(now) < Date(1 minute in future) Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - Date(1 sec ago) < Date(now) < Date(1 minute in future) undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - Date(1 sec ago) < Date(now) < Date(1 minute in future) -Fail Database readback sort - -1.1 < 1 < 1.01337 < 1.013373 < 2 Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - -1.1 < 1 < 1.01337 < 1.013373 < 2 undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - -1.1 < 1 < 1.01337 < 1.013373 < 2 -Fail Database readback sort - -Infinity < -0.01 < 0 < Infinity Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - -Infinity < -0.01 < 0 < Infinity undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - -Infinity < -0.01 < 0 < Infinity -Fail Database readback sort - "" < "a" < "ab" < "b" < "ba" Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - "" < "a" < "ab" < "b" < "ba" undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - "" < "a" < "ab" < "b" < "ba" -Fail Database readback sort - Arrays Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - Arrays undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - Arrays -Fail Database readback sort - Array.length: 10,000 < Array.length: 10,001 Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - Array.length: 10,000 < Array.length: 10,001 undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - Array.length: 10,000 < Array.length: 10,001 -Fail Database readback sort - Infinity inside arrays Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - Infinity inside arrays undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - Infinity inside arrays -Fail Database readback sort - Test different stuff at once Cannot access property "transaction" on undefined object "db" +Fail Database readback sort - Test different stuff at once undefined is not a function (evaluated from 'db.createObjectStore') Pass IDBKey.cmp sorted - Test different stuff at once \ No newline at end of file