diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 16afd614a5e..7e3d2c48fe8 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -501,6 +501,7 @@ set(SOURCES Infra/ByteSequences.cpp Infra/JSON.cpp Infra/Strings.cpp + IndexedDB/Internal/Algorithms.cpp IndexedDB/Internal/Database.cpp IndexedDB/IDBFactory.cpp IndexedDB/IDBOpenDBRequest.cpp diff --git a/Userland/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Userland/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp new file mode 100644 index 00000000000..8973d3345b2 --- /dev/null +++ b/Userland/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024, stelar7 + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::IndexedDB { + +// https://w3c.github.io/IndexedDB/#open-a-database-connection +WebIDL::ExceptionOr> open_a_database_connection(JS::Realm& realm, StorageAPI::StorageKey storage_key, String name, Optional maybe_version, JS::NonnullGCPtr request) +{ + // 1. Let queue be the connection queue for storageKey and name. + auto& queue = ConnectionQueueHandler::for_key_and_name(storage_key, name); + + // 2. Add request to queue. + queue.append(request); + + // 3. Wait until all previous requests in queue have been processed. + HTML::main_thread_event_loop().spin_until(JS::create_heap_function(realm.vm().heap(), [queue, request]() { + return queue.all_previous_requests_processed(request); + })); + + // 4. Let db be the database named name in storageKey, or null otherwise. + auto maybe_db = Database::for_key_and_name(storage_key, name); + JS::GCPtr db; + + // 5. If version is undefined, let version be 1 if db is null, or db’s version otherwise. + auto version = maybe_version.value_or(maybe_db.has_value() ? maybe_db.value()->version() : 1); + + // 6. If db is null, let db be a new database with name name, version 0 (zero), and with no object stores. + // If this fails for any reason, return an appropriate error (e.g. a "QuotaExceededError" or "UnknownError" DOMException). + if (!maybe_db.has_value()) { + auto maybe_database = Database::create_for_key_and_name(realm, storage_key, name); + + if (maybe_database.is_error()) { + return WebIDL::OperationError::create(realm, "Unable to create a new database"_string); + } + + db = maybe_database.release_value(); + } + + // 7. If db’s version is greater than version, return a newly created "VersionError" DOMException and abort these steps. + if (db->version() > version) { + return WebIDL::VersionError::create(realm, "Database version is greater than the requested version"_string); + } + + // 8. Let connection be a new connection to db. + auto connection = IDBDatabase::create(realm, *db); + + // 9. Set connection’s version to version. + connection->set_version(version); + + // 10. If db’s version is less than version, then: + if (db->version() < version) { + // 1. Let openConnections be the set of all connections, except connection, associated with db. + auto open_connections = db->associated_connections_except(connection); + + // FIXME: 2. For each entry of openConnections that does not have its close pending flag set to true, + // queue a task to fire a version change event named versionchange at entry with db’s version and version. + for (auto& entry : open_connections) { + if (!entry->close_pending()) { + } + } + + // FIXME: 3. Wait for all of the events to be fired. + + // FIXME: 4. If any of the connections in openConnections are still not closed, + // queue a task to fire a version change event named blocked at request with db’s version and version. + for (auto& entry : open_connections) { + if (entry->state() != IDBDatabase::ConnectionState::Closed) { + } + } + + // 5. Wait until all connections in openConnections are closed. + HTML::main_thread_event_loop().spin_until(JS::create_heap_function(realm.vm().heap(), [open_connections]() { + for (auto const& entry : open_connections) { + if (entry->state() != IDBDatabase::ConnectionState::Closed) { + return false; + } + } + + 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); + + // 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); + } + + // FIXME: 8. If the upgrade transaction was aborted, run the steps to close a database connection with connection, + // return a newly created "AbortError" DOMException and abort these steps. + } + + // 11. Return connection. + return connection; +} + +} diff --git a/Userland/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Userland/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h new file mode 100644 index 00000000000..721cd1a1f7a --- /dev/null +++ b/Userland/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024, stelar7 + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::IndexedDB { + +WebIDL::ExceptionOr> open_a_database_connection(JS::Realm&, StorageAPI::StorageKey, String, Optional, JS::NonnullGCPtr); + +} diff --git a/Userland/Libraries/LibWeb/IndexedDB/Internal/ConnectionQueueHandler.h b/Userland/Libraries/LibWeb/IndexedDB/Internal/ConnectionQueueHandler.h index 76f2caf6ea5..9a4bd9ff9f1 100644 --- a/Userland/Libraries/LibWeb/IndexedDB/Internal/ConnectionQueueHandler.h +++ b/Userland/Libraries/LibWeb/IndexedDB/Internal/ConnectionQueueHandler.h @@ -14,7 +14,21 @@ namespace Web::IndexedDB { -using ConnectionQueue = AK::Vector>; +class ConnectionQueue : public AK::Vector> { +public: + bool all_previous_requests_processed(JS::NonnullGCPtr const& request) const + { + for (auto const& entry : *this) { + if (entry == request) + return true; + if (!entry->processed()) + return false; + } + + return true; + } +}; + using ConnectionMap = HashMap>; // https://w3c.github.io/IndexedDB/#connection-queues