diff --git a/Libraries/LibWeb/IndexedDB/IDBRequest.cpp b/Libraries/LibWeb/IndexedDB/IDBRequest.cpp index 44eb24ab1268..bd1f9aff4b1c 100644 --- a/Libraries/LibWeb/IndexedDB/IDBRequest.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBRequest.cpp @@ -39,9 +39,11 @@ GC::Ref IDBRequest::create(JS::Realm& realm, IDBRequestSource source void IDBRequest::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); - visitor.visit(m_error); visitor.visit(m_result); visitor.visit(m_transaction); + + if (m_error.has_value()) + visitor.visit(*m_error); } // https://w3c.github.io/IndexedDB/#dom-idbrequest-onsuccess @@ -83,7 +85,7 @@ WebIDL::CallbackType* IDBRequest::onerror() return WebIDL::InvalidStateError::create(realm(), "The request is not done"_string); // 2. Otherwise, return this's error, or null if no error occurred. - return m_error; + return m_error.value_or(nullptr); } // https://w3c.github.io/IndexedDB/#dom-idbrequest-result @@ -94,7 +96,7 @@ WebIDL::CallbackType* IDBRequest::onerror() return WebIDL::InvalidStateError::create(realm(), "The request is not done"_string); // 2. Otherwise, return this's result, or undefined if the request resulted in an error. - if (m_error) + if (m_error.has_value()) return JS::js_undefined(); return m_result; diff --git a/Libraries/LibWeb/IndexedDB/IDBRequest.h b/Libraries/LibWeb/IndexedDB/IDBRequest.h index 5ba29ca15a98..ff1181b5bd06 100644 --- a/Libraries/LibWeb/IndexedDB/IDBRequest.h +++ b/Libraries/LibWeb/IndexedDB/IDBRequest.h @@ -38,11 +38,13 @@ class IDBRequest : public DOM::EventTarget { void set_done(bool done) { m_done = done; } void set_result(JS::Value result) { m_result = result; } - void set_error(GC::Ptr error) { m_error = error; } + void set_error(Optional> error) { m_error = error; } void set_processed(bool processed) { m_processed = processed; } void set_source(IDBRequestSource source) { m_source = source; } void set_transaction(GC::Ptr transaction) { m_transaction = transaction; } + bool has_error() const { return m_error.has_value(); } + void set_onsuccess(WebIDL::CallbackType*); WebIDL::CallbackType* onsuccess(); void set_onerror(WebIDL::CallbackType*); @@ -63,7 +65,7 @@ class IDBRequest : public DOM::EventTarget { // A request has a result and an error JS::Value m_result; - GC::Ptr m_error; + Optional> m_error; // A request has a source object. IDBRequestSource m_source; diff --git a/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp b/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp index ea24626dae17..3f1decd82129 100644 --- a/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBTransaction.cpp @@ -87,8 +87,7 @@ WebIDL::ExceptionOr IDBTransaction::abort() if (m_state == TransactionState::Committing || m_state == TransactionState::Finished) return WebIDL::InvalidStateError::create(realm(), "Transaction is ending"_string); - // 2. Set this's state to inactive and run abort a transaction with this and null. - m_state = TransactionState::Inactive; + // 2. Run abort a transaction with this and null. abort_a_transaction(*this, nullptr); return {}; } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index fc3410279b68..f32d03c4891d 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -164,16 +164,15 @@ WebIDL::ExceptionOr> open_a_database_connection(JS::Realm& })); // 6. Run upgrade a database using connection, version and request. - // AD-HOC: https://github.com/w3c/IndexedDB/issues/433#issuecomment-2512330086 - auto upgrade_transaction = upgrade_a_database(realm, connection, version, 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) return WebIDL::AbortError::create(realm, "Connection was closed"_string); - // 8. If the upgrade transaction was aborted, run the steps to close a database connection with connection, + // 8. If request's error is set, run the steps to close a database connection with connection, // return a newly created "AbortError" DOMException and abort these steps. - if (upgrade_transaction->aborted()) { + if (request->has_error()) { close_a_database_connection(*connection); return WebIDL::AbortError::create(realm, "Upgrade transaction was aborted"_string); } @@ -360,7 +359,7 @@ void close_a_database_connection(GC::Ref connection, bool forced) } // https://w3c.github.io/IndexedDB/#upgrade-a-database -GC::Ref upgrade_a_database(JS::Realm& realm, GC::Ref connection, u64 version, GC::Ref request) +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(); @@ -431,8 +430,6 @@ GC::Ref upgrade_a_database(JS::Realm& realm, GC::Refis_finished(); })); - - return transaction; } // https://w3c.github.io/IndexedDB/#deleting-a-database @@ -540,22 +537,25 @@ void abort_a_transaction(GC::Ref transaction, GC::Ptrset_aborted(true); dbgln_if(IDB_DEBUG, "abort_a_transaction: transaction {} is aborting", transaction->uuid()); - // FIXME: 1. All the changes made to the database by the transaction are reverted. + // 1. If transaction is finished, abort these steps. + if (transaction->is_finished()) + return; + + // FIXME: 2. 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. + // FIXME: 3. 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. + // 4. 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); + // 5. Set transaction’s error to error. + transaction->set_error(error); - // 5. For each request of transaction’s request list, + // 6. For each request of transaction’s request list, for (auto const& request : transaction->request_list()) { // FIXME: abort the steps to asynchronously execute a request for request, @@ -578,7 +578,7 @@ void abort_a_transaction(GC::Ref transaction, GC::Ptrrealm().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()) @@ -1209,7 +1209,7 @@ GC::Ref asynchronously_execute_a_request(JS::Realm& realm, IDBReques request->set_result(result.release_value()); // 2. Set request’s error to undefined. - request->set_error(nullptr); + request->set_error(Optional> {}); // 3. Fire a success event at request. fire_a_success_event(realm, request); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index ce04060a78c1..1d85ae9ebbb0 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -24,7 +24,7 @@ WebIDL::ExceptionOr> open_a_database_connection(JS::Realm&, bool fire_a_version_change_event(JS::Realm&, FlyString const&, GC::Ref, u64, Optional); WebIDL::ExceptionOr> convert_a_value_to_a_key(JS::Realm&, JS::Value, Vector = {}); void close_a_database_connection(GC::Ref, bool forced = false); -GC::Ref upgrade_a_database(JS::Realm&, GC::Ref, u64, GC::Ref); +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(GC::Ref, GC::Ptr); JS::Value convert_a_key_to_a_value(JS::Realm&, GC::Ref); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Key.cpp b/Libraries/LibWeb/IndexedDB/Internal/Key.cpp index dcb0dc2fb183..d32bae6562d5 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Key.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Key.cpp @@ -165,4 +165,25 @@ i8 Key::compare_two_keys(GC::Ref a, GC::Ref b) VERIFY_NOT_REACHED(); } +String Key::dump() const +{ + return m_value.visit( + [](Vector> const& value) { + StringBuilder sb; + sb.append("["sv); + for (auto const& key : value) { + sb.append(key->dump()); + sb.append(", "sv); + } + sb.append("]"sv); + return MUST(sb.to_string()); + }, + [](ByteBuffer const& value) { + return MUST(String::formatted("{}", value.span())); + }, + [](auto const& value) { + return MUST(String::formatted("{}", value)); + }); +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Key.h b/Libraries/LibWeb/IndexedDB/Internal/Key.h index 0dccc6dd9241..c313f2632664 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Key.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Key.h @@ -70,6 +70,8 @@ class Key : public JS::Cell { [[nodiscard]] static bool less_than(GC::Ref a, GC::Ref b) { return compare_two_keys(a, b) < 0; } [[nodiscard]] static bool greater_than(GC::Ref a, GC::Ref b) { return compare_two_keys(a, b) > 0; } + AK::String dump() const; + private: Key(KeyType type, KeyValue value) : m_type(type) diff --git a/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt b/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.any.txt similarity index 53% rename from Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt rename to Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.any.txt index 6361358f8ab6..4bfd58638ca8 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/IndexedDB/keyorder.any.txt @@ -4,26 +4,26 @@ Found 24 tests 24 Pass Pass Database readback sort - String < Array -Pass IDBKey.cmp sorted - String < Array +Pass IDBKey.cmp sort - String < Array Pass Database readback sort - float < String -Pass IDBKey.cmp sorted - float < String +Pass IDBKey.cmp sort - float < String Pass Database readback sort - float < Date -Pass IDBKey.cmp sorted - float < Date +Pass IDBKey.cmp sort - float < Date Pass Database readback sort - float < Date < String < Array -Pass IDBKey.cmp sorted - float < Date < String < Array +Pass IDBKey.cmp sort - float < Date < String < Array Pass Database readback sort - Date(1 sec ago) < Date(now) < Date(1 minute in future) -Pass IDBKey.cmp sorted - Date(1 sec ago) < Date(now) < Date(1 minute in future) +Pass IDBKey.cmp sort - Date(1 sec ago) < Date(now) < Date(1 minute in future) Pass Database readback sort - -1.1 < 1 < 1.01337 < 1.013373 < 2 -Pass IDBKey.cmp sorted - -1.1 < 1 < 1.01337 < 1.013373 < 2 +Pass IDBKey.cmp sort - -1.1 < 1 < 1.01337 < 1.013373 < 2 Pass Database readback sort - -Infinity < -0.01 < 0 < Infinity -Pass IDBKey.cmp sorted - -Infinity < -0.01 < 0 < Infinity +Pass IDBKey.cmp sort - -Infinity < -0.01 < 0 < Infinity Pass Database readback sort - "" < "a" < "ab" < "b" < "ba" -Pass IDBKey.cmp sorted - "" < "a" < "ab" < "b" < "ba" +Pass IDBKey.cmp sort - "" < "a" < "ab" < "b" < "ba" Pass Database readback sort - Arrays -Pass IDBKey.cmp sorted - Arrays +Pass IDBKey.cmp sort - Arrays Pass Database readback sort - Array.length: 10,000 < Array.length: 10,001 -Pass IDBKey.cmp sorted - Array.length: 10,000 < Array.length: 10,001 +Pass IDBKey.cmp sort - Array.length: 10,000 < Array.length: 10,001 Pass Database readback sort - Infinity inside arrays -Pass IDBKey.cmp sorted - Infinity inside arrays +Pass IDBKey.cmp sort - Infinity inside arrays Pass Database readback sort - Test different stuff at once -Pass IDBKey.cmp sorted - Test different stuff at once \ No newline at end of file +Pass IDBKey.cmp sort - Test different stuff at once \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.any.html b/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.any.html new file mode 100644 index 000000000000..b6fabacfd400 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.any.html @@ -0,0 +1,15 @@ + + +Key sort order + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.any.js b/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.any.js new file mode 100644 index 000000000000..fe3eb11fa92b --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.any.js @@ -0,0 +1,169 @@ +// META: global=window,worker +// META: title=Key sort order +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#key-construct + +'use strict'; + +const global_db = createdb_for_multiple_tests(); + +const keysort = (desc, unsorted, expected) => { + async_test(t => { + const store_name = 'store-' + Date.now() + Math.random(); + + // The database test + const open_rq = global_db.setTest(t); + open_rq.onupgradeneeded = t.step_func(e => { + const db = e.target.result; + const objStore = db.createObjectStore(store_name); + + for (let i = 0; i < unsorted.length; i++) + objStore.add('value', unsorted[i]); + }); + + open_rq.onsuccess = t.step_func(e => { + const db = e.target.result; + const actual_keys = []; + const rq = + db.transaction(store_name).objectStore(store_name).openCursor(); + + rq.onsuccess = t.step_func(e => { + const cursor = e.target.result; + + if (cursor) { + actual_keys.push(cursor.key); + cursor.continue(); + } else { + assert_key_equals(actual_keys, expected, 'keyorder array'); + assert_equals(actual_keys.length, expected.length, 'array length'); + + t.done(); + } + }); + }); + }, `Database readback sort - ${desc}`); + + // The IDBKey.cmp test + test(() => { + const sorted = unsorted.slice(0).sort((a, b) => indexedDB.cmp(a, b)); + assert_key_equals(sorted, expected, 'sorted array'); + }, `IDBKey.cmp sort - ${desc}`); +}; + +const now = new Date(); +const one_sec_ago = new Date(now - 1000); +const one_min_future = new Date(now.getTime() + 1000 * 60); + +keysort('String < Array', [[0], 'yo', '', []], ['', 'yo', [], [0]]); + +keysort( + 'float < String', [Infinity, 'yo', 0, '', 100], + [0, 100, Infinity, '', 'yo']); + +keysort( + 'float < Date', [now, 0, 9999999999999, -0.22], + [-0.22, 0, 9999999999999, now]); + +keysort( + 'float < Date < String < Array', [[], '', now, [0], '-1', 0, 9999999999999], + [0, 9999999999999, now, '', '-1', [], [0]]); + +keysort( + 'Date(1 sec ago) < Date(now) < Date(1 minute in future)', + [now, one_sec_ago, one_min_future], [one_sec_ago, now, one_min_future]); + +keysort( + '-1.1 < 1 < 1.01337 < 1.013373 < 2', [1.013373, 2, 1.01337, -1.1, 1], + [-1.1, 1, 1.01337, 1.013373, 2]); + +keysort( + '-Infinity < -0.01 < 0 < Infinity', [0, -0.01, -Infinity, Infinity], + [-Infinity, -0.01, 0, Infinity]); + +keysort( + '"" < "a" < "ab" < "b" < "ba"', ['a', 'ba', '', 'b', 'ab'], + ['', 'a', 'ab', 'b', 'ba']); + +keysort( + 'Arrays', [[[0]], [0], [], [0, 0], [0, [0]]], + [[], [0], [0, 0], [0, [0]], [[0]]]); + +const big_array = []; +const bigger_array = []; +for (let i = 0; i < 10000; i++) { + big_array.push(i); + bigger_array.push(i); +} +bigger_array.push(0); + +keysort( + 'Array.length: 10,000 < Array.length: 10,001', + [bigger_array, [0, 2, 3], [0], [9], big_array], + [[0], big_array, bigger_array, [0, 2, 3], [9]]); + +keysort( + 'Infinity inside arrays', + [ + [Infinity, 1], + [Infinity, Infinity], + [1, 1], + [1, Infinity], + [1, -Infinity], + [-Infinity, Infinity], + ], + [ + [-Infinity, Infinity], + [1, -Infinity], + [1, 1], + [1, Infinity], + [Infinity, 1], + [Infinity, Infinity], + ]); + +keysort( + 'Test different stuff at once', + [ + now, + [0, []], + 'test', + 1, + ['a', [1, [-1]]], + ['b', 'a'], + [0, 2, 'c'], + ['a', [1, 2]], + [], + [0, [], 3], + ['a', 'b'], + [1, 2], + ['a', 'b', 'c'], + one_sec_ago, + [0, 'b', 'c'], + Infinity, + -Infinity, + 2.55, + [0, now], + [1], + ], + [ + -Infinity, + 1, + 2.55, + Infinity, + one_sec_ago, + now, + 'test', + [], + [0, 2, 'c'], + [0, now], + [0, 'b', 'c'], + [0, []], + [0, [], 3], + [1], + [1, 2], + ['a', 'b'], + ['a', 'b', 'c'], + ['a', [1, 2]], + ['a', [1, [-1]]], + ['b', 'a'], + ]); diff --git a/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.htm b/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.htm deleted file mode 100644 index 660facbb69f2..000000000000 --- a/Tests/LibWeb/Text/input/wpt-import/IndexedDB/keyorder.htm +++ /dev/null @@ -1,175 +0,0 @@ - - - -Key sort order - - - - - - - - -