diff --git a/Libraries/LibWeb/IndexedDB/IDBFactory.cpp b/Libraries/LibWeb/IndexedDB/IDBFactory.cpp index 6dfa8321d27..1f81deacd75 100644 --- a/Libraries/LibWeb/IndexedDB/IDBFactory.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBFactory.cpp @@ -1,10 +1,13 @@ /* * Copyright (c) 2024, Shannon Booth - * Copyright (c) 2024, stelar7 + * Copyright (c) 2024-2025, stelar7 * * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include #include #include #include @@ -16,6 +19,7 @@ #include #include #include +#include namespace Web::IndexedDB { @@ -173,4 +177,57 @@ WebIDL::ExceptionOr> IDBFactory::delete_database(Strin return request; } +// https://w3c.github.io/IndexedDB/#dom-idbfactory-databases +GC::Ref IDBFactory::databases() +{ + auto& realm = this->realm(); + + // 1. Let environment be this's relevant settings object. + auto& environment = HTML::relevant_settings_object(*this); + + // 2. Let storageKey be the result of running obtain a storage key given environment. + // If failure is returned, then return a promise rejected with a "SecurityError" DOMException + auto maybe_storage_key = StorageAPI::obtain_a_storage_key(environment); + if (!maybe_storage_key.has_value()) + return WebIDL::create_rejected_promise_from_exception(realm, WebIDL::SecurityError::create(realm, "Failed to obtain a storage key"_string)); + + auto storage_key = maybe_storage_key.release_value(); + + // 3. Let p be a new promise. + auto p = WebIDL::create_promise(realm); + + // 4. Run these steps in parallel: + Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, storage_key, p]() { + HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); + + // 1. Let databases be the set of databases in storageKey. + // If this cannot be determined for any reason, then reject p with an appropriate error (e.g. an "UnknownError" DOMException) and terminate these steps. + auto databases = Database::for_key(storage_key); + + // 2. Let result be a new list. + auto result = MUST(JS::Array::create(realm, databases.size())); + + // 3. For each db of databases: + for (u32 i = 0; i < databases.size(); ++i) { + auto& db = databases[i]; + + // 1. Let info be a new IDBDatabaseInfo dictionary. + // 2. Set info’s name dictionary member to db’s name. + // 3. Set info’s version dictionary member to db’s version. + auto info = JS::Object::create(realm, realm.intrinsics().object_prototype()); + MUST(info->create_data_property("name", JS::PrimitiveString::create(realm.vm(), db->name()))); + MUST(info->create_data_property("version", JS::Value(db->version()))); + + // 4. Append info to result. + MUST(result->create_data_property_or_throw(i, info)); + } + + // 4. Resolve p with result. + WebIDL::resolve_promise(realm, p, result); + })); + + // 5. Return p. + return p; +} + } diff --git a/Libraries/LibWeb/IndexedDB/IDBFactory.h b/Libraries/LibWeb/IndexedDB/IDBFactory.h index ab7841b6e3a..b6dcd1db190 100644 --- a/Libraries/LibWeb/IndexedDB/IDBFactory.h +++ b/Libraries/LibWeb/IndexedDB/IDBFactory.h @@ -1,12 +1,13 @@ /* * Copyright (c) 2024, Shannon Booth - * Copyright (c) 2024, stelar7 + * Copyright (c) 2024-2025, stelar7 * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include #include @@ -22,6 +23,7 @@ public: WebIDL::ExceptionOr> open(String const& name, Optional version); WebIDL::ExceptionOr> delete_database(String const& name); + GC::Ref databases(); WebIDL::ExceptionOr cmp(JS::Value first, JS::Value second); diff --git a/Libraries/LibWeb/IndexedDB/IDBFactory.idl b/Libraries/LibWeb/IndexedDB/IDBFactory.idl index 7edba81121d..7382abb09cb 100644 --- a/Libraries/LibWeb/IndexedDB/IDBFactory.idl +++ b/Libraries/LibWeb/IndexedDB/IDBFactory.idl @@ -4,7 +4,7 @@ interface IDBFactory { [NewObject] IDBOpenDBRequest open(DOMString name, optional [EnforceRange] unsigned long long version); [NewObject] IDBOpenDBRequest deleteDatabase(DOMString name); - [FIXME] Promise> databases(); + Promise> databases(); short cmp(any first, any second); }; diff --git a/Libraries/LibWeb/IndexedDB/Internal/Database.cpp b/Libraries/LibWeb/IndexedDB/Internal/Database.cpp index 8fdf3ba3f36..948976cfa8a 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Database.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Database.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, stelar7 + * Copyright (c) 2024-2025, stelar7 * * SPDX-License-Identifier: BSD-2-Clause */ @@ -28,6 +28,16 @@ void Database::visit_edges(Visitor& visitor) visitor.visit(m_upgrade_transaction); } +Vector> Database::for_key(StorageAPI::StorageKey const& key) +{ + Vector> databases; + for (auto const& database_mapping : m_databases.get(key).value_or({})) { + databases.append(database_mapping.value); + } + + return databases; +} + RequestList& ConnectionQueueHandler::for_key_and_name(StorageAPI::StorageKey& key, String& name) { return ConnectionQueueHandler::the().m_open_requests.ensure(key, [] { diff --git a/Libraries/LibWeb/IndexedDB/Internal/Database.h b/Libraries/LibWeb/IndexedDB/Internal/Database.h index d5a150129ae..b980e9c87a6 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Database.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Database.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, stelar7 + * Copyright (c) 2024-2025, stelar7 * * SPDX-License-Identifier: BSD-2-Clause */ @@ -39,6 +39,7 @@ public: return connections; } + [[nodiscard]] static Vector> for_key(StorageAPI::StorageKey const&); [[nodiscard]] static Optional const&> for_key_and_name(StorageAPI::StorageKey&, String&); [[nodiscard]] static ErrorOr> create_for_key_and_name(JS::Realm&, StorageAPI::StorageKey&, String&); [[nodiscard]] static ErrorOr delete_for_key_and_name(StorageAPI::StorageKey&, String&);