diff --git a/Libraries/LibWeb/IndexedDB/IDBIndex.cpp b/Libraries/LibWeb/IndexedDB/IDBIndex.cpp index a62ea6e5e31..86cb1b9be33 100644 --- a/Libraries/LibWeb/IndexedDB/IDBIndex.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBIndex.cpp @@ -71,14 +71,16 @@ WebIDL::ExceptionOr IDBIndex::set_name(String const& value) return {}; // 8. If an index named name already exists in index’s object store, throw a "ConstraintError" DOMException. - for (auto const& existing_index : m_object_store_handle->index_set()) { - if (existing_index->name() == name) - return WebIDL::ConstraintError::create(realm, "An index with the given name already exists"_string); - } + if (index->object_store()->index_set().contains(name)) + return WebIDL::ConstraintError::create(realm, "An index with the given name already exists"_string); // 9. Set index’s name to name. index->set_name(name); + // NOTE: Update the key in the map so it still matches the name + auto old_value = m_object_store_handle->index_set().take(m_name).release_value(); + m_object_store_handle->index_set().set(name, old_value); + // 10. Set this’s name to name. m_name = name; diff --git a/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp b/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp index fbbcbe1c205..35f7ea24f46 100644 --- a/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Web::IndexedDB { @@ -41,6 +42,7 @@ void IDBObjectStore::visit_edges(Visitor& visitor) Base::visit_edges(visitor); visitor.visit(m_store); visitor.visit(m_transaction); + visitor.visit(m_indexes); } // https://w3c.github.io/IndexedDB/#dom-idbobjectstore-keypath @@ -101,4 +103,54 @@ WebIDL::ExceptionOr IDBObjectStore::set_name(String const& value) return {}; } +// https://w3c.github.io/IndexedDB/#dom-idbobjectstore-createindex +WebIDL::ExceptionOr> IDBObjectStore::create_index(String const& name, KeyPath key_path, IDBIndexParameters options) +{ + auto& realm = this->realm(); + + // 1. Let transaction be this's transaction. + auto transaction = this->transaction(); + + // 2. Let store be this's object store. + auto store = this->store(); + + // 3. If transaction is not an upgrade transaction, throw an "InvalidStateError" DOMException. + if (transaction->mode() != Bindings::IDBTransactionMode::Versionchange) + return WebIDL::InvalidStateError::create(realm, "Transaction is not an upgrade transaction"_string); + + // FIXME: 4. If store has been deleted, throw an "InvalidStateError" DOMException. + + // 5. If transaction’s state is not active, then throw a "TransactionInactiveError" DOMException. + if (transaction->state() != IDBTransaction::TransactionState::Active) + return WebIDL::TransactionInactiveError::create(realm, "Transaction is not active while creating index"_string); + + // 6. If an index named name already exists in store, throw a "ConstraintError" DOMException. + if (store->index_set().contains(name)) + return WebIDL::ConstraintError::create(realm, "An index with the given name already exists"_string); + + // 7. If keyPath is not a valid key path, throw a "SyntaxError" DOMException. + if (!is_valid_key_path(key_path)) + return WebIDL::SyntaxError::create(realm, "Key path is not valid"_string); + + // 8. Let unique be options’s unique member. + auto unique = options.unique; + + // 9. Let multiEntry be options’s multiEntry member. + auto multi_entry = options.multi_entry; + + // 10. If keyPath is a sequence and multiEntry is true, throw an "InvalidAccessError" DOMException. + if (key_path.has>() && multi_entry) + return WebIDL::InvalidAccessError::create(realm, "Key path is a sequence and multiEntry is true"_string); + + // 11. Let index be a new index in store. + // Set index’s name to name, key path to keyPath, unique flag to unique, and multiEntry flag to multiEntry. + auto index = Index::create(realm, store, name, key_path, unique, multi_entry); + + // 12. Add index to this's index set. + this->index_set().set(name, index); + + // 13. Return a new index handle associated with index and this. + return IDBIndex::create(realm, index, *this); +} + } diff --git a/Libraries/LibWeb/IndexedDB/IDBObjectStore.h b/Libraries/LibWeb/IndexedDB/IDBObjectStore.h index c22fd5081ee..df6e0f03efb 100644 --- a/Libraries/LibWeb/IndexedDB/IDBObjectStore.h +++ b/Libraries/LibWeb/IndexedDB/IDBObjectStore.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -13,6 +14,11 @@ namespace Web::IndexedDB { +struct IDBIndexParameters { + bool unique { false }; + bool multi_entry { false }; +}; + // https://w3c.github.io/IndexedDB/#object-store-interface // https://w3c.github.io/IndexedDB/#object-store-handle-construct class IDBObjectStore : public Bindings::PlatformObject { @@ -23,14 +29,17 @@ public: virtual ~IDBObjectStore() override; [[nodiscard]] static GC::Ref create(JS::Realm&, GC::Ref, GC::Ref); - JS::Value key_path() const; - GC::Ref transaction() const { return m_transaction; } - // https://w3c.github.io/IndexedDB/#dom-idbobjectstore-autoincrement // The autoIncrement getter steps are to return true if this’s object store has a key generator, and false otherwise. bool auto_increment() const { return m_store->key_generator().has_value(); } + JS::Value key_path() const; String name() const { return m_name; } WebIDL::ExceptionOr set_name(String const& value); + GC::Ref transaction() const { return m_transaction; } + GC::Ref store() const { return m_store; } + AK::HashMap>& index_set() { return m_indexes; } + + WebIDL::ExceptionOr> create_index(String const&, KeyPath, IDBIndexParameters options); protected: explicit IDBObjectStore(JS::Realm&, GC::Ref, GC::Ref); @@ -44,6 +53,9 @@ private: // An object store handle has a name, which is initialized to the name of the associated object store when the object store handle is created. String m_name; + + // An object store handle has an index set + AK::HashMap> m_indexes; }; } diff --git a/Libraries/LibWeb/IndexedDB/IDBObjectStore.idl b/Libraries/LibWeb/IndexedDB/IDBObjectStore.idl index 66dd3ef722d..4af455b4102 100644 --- a/Libraries/LibWeb/IndexedDB/IDBObjectStore.idl +++ b/Libraries/LibWeb/IndexedDB/IDBObjectStore.idl @@ -22,7 +22,7 @@ interface IDBObjectStore { [FIXME, NewObject] IDBRequest openCursor(optional any query, optional IDBCursorDirection direction = "next"); [FIXME, NewObject] IDBRequest openKeyCursor(optional any query, optional IDBCursorDirection direction = "next"); [FIXME] IDBIndex index(DOMString name); - [FIXME, NewObject] IDBIndex createIndex(DOMString name, (DOMString or sequence) keyPath, optional IDBIndexParameters options = {}); + [NewObject] IDBIndex createIndex(DOMString name, (DOMString or sequence) keyPath, optional IDBIndexParameters options = {}); [FIXME] undefined deleteIndex(DOMString name); }; diff --git a/Libraries/LibWeb/IndexedDB/Internal/Index.cpp b/Libraries/LibWeb/IndexedDB/Internal/Index.cpp index 38ea6e1e27e..d347f1dd49f 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Index.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Index.cpp @@ -25,7 +25,7 @@ Index::Index(GC::Ref store, String name, KeyPath const& key_path, b , m_multi_entry(multi_entry) , m_key_path(key_path) { - store->add_index(*this); + store->index_set().set(name, *this); } void Index::visit_edges(Visitor& visitor) @@ -39,4 +39,13 @@ void Index::visit_edges(Visitor& visitor) } } +void Index::set_name(String name) +{ + // NOTE: Update the key in the map so it still matches the name + auto old_value = m_object_store->index_set().take(m_name).release_value(); + m_object_store->index_set().set(name, old_value); + + m_name = move(name); +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Index.h b/Libraries/LibWeb/IndexedDB/Internal/Index.h index e984ffbc1b4..6296c8b805c 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Index.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Index.h @@ -31,7 +31,7 @@ public: [[nodiscard]] static GC::Ref create(JS::Realm&, GC::Ref, String, KeyPath const&, bool, bool); virtual ~Index(); - void set_name(String name) { m_name = move(name); } + void set_name(String name); [[nodiscard]] String name() const { return m_name; } [[nodiscard]] bool unique() const { return m_unique; } [[nodiscard]] bool multi_entry() const { return m_multi_entry; } diff --git a/Libraries/LibWeb/IndexedDB/Internal/ObjectStore.h b/Libraries/LibWeb/IndexedDB/Internal/ObjectStore.h index d7f400e37bc..810c33eb8fb 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/ObjectStore.h +++ b/Libraries/LibWeb/IndexedDB/Internal/ObjectStore.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -37,12 +38,10 @@ public: bool uses_inline_keys() const { return m_key_path.has_value(); } bool uses_out_of_line_keys() const { return !m_key_path.has_value(); } Optional key_generator() const { return m_key_generator; } + AK::HashMap>& index_set() { return m_indexes; } GC::Ref database() const { return m_database; } - void add_index(GC::Ref index) { m_indexes.append(index); } - ReadonlySpan> index_set() const { return m_indexes; } - protected: virtual void visit_edges(Visitor&) override; @@ -53,7 +52,7 @@ private: GC::Ref m_database; // AD-HOC: An Index has referenced ObjectStores, we also need the reverse mapping - Vector> m_indexes; + AK::HashMap> m_indexes; // An object store has a name, which is a name. At any one time, the name is unique within the database to which it belongs. String m_name;