/* * Copyright (c) 2024-2025, stelar7 * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include namespace Web::IndexedDB { using KeyPath = Variant>; // https://w3c.github.io/IndexedDB/#dictdef-idbobjectstoreparameters struct IDBObjectStoreParameters { Optional key_path; bool auto_increment { false }; }; // https://w3c.github.io/IndexedDB/#dictdef-idbtransactionoptions struct IDBTransactionOptions { Bindings::IDBTransactionDurability durability = Bindings::IDBTransactionDurability::Default; }; // FIXME: I'm not sure if this object should do double duty as both the connection and the interface // but the spec treats it as such...? // https://w3c.github.io/IndexedDB/#IDBDatabase-interface // https://www.w3.org/TR/IndexedDB/#database-connection class IDBDatabase : public DOM::EventTarget { WEB_PLATFORM_OBJECT(IDBDatabase, DOM::EventTarget); GC_DECLARE_ALLOCATOR(IDBDatabase); enum ConnectionState { Open, Closed, }; public: virtual ~IDBDatabase() override; [[nodiscard]] static GC::Ref create(JS::Realm&, Database&); void set_version(u64 version) { m_version = version; } void set_close_pending(bool close_pending) { m_close_pending = close_pending; } void set_state(ConnectionState state) { m_state = state; } [[nodiscard]] String uuid() const { return m_uuid; } [[nodiscard]] String name() const { return m_name; } [[nodiscard]] u64 version() const { return m_version; } [[nodiscard]] bool close_pending() const { return m_close_pending; } [[nodiscard]] ConnectionState state() const { return m_state; } [[nodiscard]] GC::Ref associated_database() { return m_associated_database; } [[nodiscard]] ReadonlySpan> object_store_set() { return m_object_store_set; } void add_to_object_store_set(GC::Ref object_store) { m_object_store_set.append(object_store); } void remove_from_object_store_set(GC::Ref object_store) { m_object_store_set.remove_first_matching([&](auto& entry) { return entry == object_store; }); } [[nodiscard]] ReadonlySpan> transactions() { return m_transactions; } void add_transaction(GC::Ref transaction) { m_transactions.append(transaction); } [[nodiscard]] GC::Ref object_store_names(); WebIDL::ExceptionOr> create_object_store(String const&, IDBObjectStoreParameters const&); WebIDL::ExceptionOr delete_object_store(String const&); WebIDL::ExceptionOr> transaction(Variant>, Bindings::IDBTransactionMode = Bindings::IDBTransactionMode::Readonly, IDBTransactionOptions = { .durability = Bindings::IDBTransactionDurability::Default }); void close(); void set_onabort(WebIDL::CallbackType*); WebIDL::CallbackType* onabort(); void set_onclose(WebIDL::CallbackType*); WebIDL::CallbackType* onclose(); void set_onerror(WebIDL::CallbackType*); WebIDL::CallbackType* onerror(); void set_onversionchange(WebIDL::CallbackType*); WebIDL::CallbackType* onversionchange(); protected: explicit IDBDatabase(JS::Realm&, Database&); virtual void initialize(JS::Realm&) override; virtual void visit_edges(Visitor& visitor) override; private: u64 m_version { 0 }; String m_name; // Each connection has a close pending flag which is initially false. bool m_close_pending { false }; // When a connection is initially created it is in an opened state. ConnectionState m_state { ConnectionState::Open }; // A connection has an object store set, which is initialized to the set of object stores in the associated database when the connection is created. // The contents of the set will remain constant except when an upgrade transaction is live. Vector> m_object_store_set; // NOTE: There is an associated database in the spec, but there is no mention where it is assigned, nor where its from // So we stash the one we have when opening a connection. GC::Ref m_associated_database; // NOTE: We need to keep track of what transactions were created by this connection Vector> m_transactions; // NOTE: Used for debug purposes String m_uuid; }; }