mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-25 14:05:15 +00:00
LibWeb/IDB: Implement IDBDatabase::createObjectStore
This commit is contained in:
parent
3c5578cc87
commit
1057c88fdd
Notes:
github-actions[bot]
2025-03-27 15:49:41 +00:00
Author: https://github.com/stelar7 Commit: https://github.com/LadybirdBrowser/ladybird/commit/1057c88fdda Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4077 Reviewed-by: https://github.com/AtkinsSJ Reviewed-by: https://github.com/gmta ✅
7 changed files with 102 additions and 10 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
|
* Copyright (c) 2024-2025, stelar7 <dudedbz@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +8,7 @@
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
#include <LibWeb/HTML/EventNames.h>
|
#include <LibWeb/HTML/EventNames.h>
|
||||||
#include <LibWeb/IndexedDB/IDBDatabase.h>
|
#include <LibWeb/IndexedDB/IDBDatabase.h>
|
||||||
|
#include <LibWeb/IndexedDB/IDBObjectStore.h>
|
||||||
#include <LibWeb/IndexedDB/Internal/Algorithms.h>
|
#include <LibWeb/IndexedDB/Internal/Algorithms.h>
|
||||||
|
|
||||||
namespace Web::IndexedDB {
|
namespace Web::IndexedDB {
|
||||||
|
@ -90,4 +91,52 @@ void IDBDatabase::close()
|
||||||
close_a_database_connection(*this);
|
close_a_database_connection(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
|
||||||
|
WebIDL::ExceptionOr<GC::Ref<IDBObjectStore>> IDBDatabase::create_object_store(String const& name, IDBObjectStoreParameters const& options)
|
||||||
|
{
|
||||||
|
auto& realm = this->realm();
|
||||||
|
|
||||||
|
// 1. Let database be this's associated database.
|
||||||
|
auto database = associated_database();
|
||||||
|
|
||||||
|
// 2. Let transaction be database’s upgrade transaction if it is not null, or throw an "InvalidStateError" DOMException otherwise.
|
||||||
|
auto transaction = database->upgrade_transaction();
|
||||||
|
if (!transaction)
|
||||||
|
return WebIDL::InvalidStateError::create(realm, "Upgrade transaction is null"_string);
|
||||||
|
|
||||||
|
// 3. 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"_string);
|
||||||
|
|
||||||
|
// 4. Let keyPath be options’s keyPath member if it is not undefined or null, or null otherwise.
|
||||||
|
auto key_path = options.key_path;
|
||||||
|
|
||||||
|
// 5. If keyPath is not null and is not a valid key path, throw a "SyntaxError" DOMException.
|
||||||
|
if (key_path.has_value() && !is_valid_key_path(key_path.value()))
|
||||||
|
return WebIDL::SyntaxError::create(realm, "Invalid key path"_string);
|
||||||
|
|
||||||
|
// 6. If an object store named name already exists in database throw a "ConstraintError" DOMException.
|
||||||
|
if (database->has_object_store_named(name))
|
||||||
|
return WebIDL::ConstraintError::create(realm, "Object store already exists"_string);
|
||||||
|
|
||||||
|
// 7. Let autoIncrement be options’s autoIncrement member.
|
||||||
|
auto auto_increment = options.auto_increment;
|
||||||
|
|
||||||
|
bool is_empty_key_path_or_sequence = key_path.has_value() && key_path.value().visit([](String const& value) -> bool { return value.is_empty(); }, [](Vector<String> const&) -> bool { return true; });
|
||||||
|
|
||||||
|
// 8. If autoIncrement is true and keyPath is an empty string or any sequence (empty or otherwise), throw an "InvalidAccessError" DOMException.
|
||||||
|
if (auto_increment && is_empty_key_path_or_sequence)
|
||||||
|
return WebIDL::InvalidAccessError::create(realm, "Auto increment is true and key path is empty or sequence"_string);
|
||||||
|
|
||||||
|
// 9. Let store be a new object store in database.
|
||||||
|
// Set the created object store's name to name.
|
||||||
|
// If autoIncrement is true, then the created object store uses a key generator.
|
||||||
|
// If keyPath is not null, set the created object store's key path to keyPath.
|
||||||
|
auto object_store = ObjectStore::create(realm, name, auto_increment, key_path);
|
||||||
|
database->add_object_store(object_store);
|
||||||
|
|
||||||
|
// 10. Return a new object store handle associated with store and transaction.
|
||||||
|
return IDBObjectStore::create(realm, object_store, *transaction);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
|
* Copyright (c) 2024-2025, stelar7 <dudedbz@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -11,10 +11,19 @@
|
||||||
#include <LibWeb/HTML/DOMStringList.h>
|
#include <LibWeb/HTML/DOMStringList.h>
|
||||||
#include <LibWeb/IndexedDB/IDBRequest.h>
|
#include <LibWeb/IndexedDB/IDBRequest.h>
|
||||||
#include <LibWeb/IndexedDB/Internal/Database.h>
|
#include <LibWeb/IndexedDB/Internal/Database.h>
|
||||||
|
#include <LibWeb/IndexedDB/Internal/ObjectStore.h>
|
||||||
#include <LibWeb/StorageAPI/StorageKey.h>
|
#include <LibWeb/StorageAPI/StorageKey.h>
|
||||||
|
|
||||||
namespace Web::IndexedDB {
|
namespace Web::IndexedDB {
|
||||||
|
|
||||||
|
using KeyPath = Variant<String, Vector<String>>;
|
||||||
|
|
||||||
|
// https://w3c.github.io/IndexedDB/#dictdef-idbobjectstoreparameters
|
||||||
|
struct IDBObjectStoreParameters {
|
||||||
|
Optional<KeyPath> key_path;
|
||||||
|
bool auto_increment { false };
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: I'm not sure if this object should do double duty as both the connection and the interface
|
// 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...?
|
// but the spec treats it as such...?
|
||||||
// https://w3c.github.io/IndexedDB/#IDBDatabase-interface
|
// https://w3c.github.io/IndexedDB/#IDBDatabase-interface
|
||||||
|
@ -44,6 +53,8 @@ public:
|
||||||
[[nodiscard]] ConnectionState state() const { return m_state; }
|
[[nodiscard]] ConnectionState state() const { return m_state; }
|
||||||
[[nodiscard]] GC::Ref<Database> associated_database() { return m_associated_database; }
|
[[nodiscard]] GC::Ref<Database> associated_database() { return m_associated_database; }
|
||||||
|
|
||||||
|
WebIDL::ExceptionOr<GC::Ref<IDBObjectStore>> create_object_store(String const&, IDBObjectStoreParameters const&);
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
void set_onabort(WebIDL::CallbackType*);
|
void set_onabort(WebIDL::CallbackType*);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#import <DOM/EventTarget.idl>
|
#import <DOM/EventTarget.idl>
|
||||||
#import <DOM/EventHandler.idl>
|
#import <DOM/EventHandler.idl>
|
||||||
|
#import <IndexedDB/IDBObjectStore.idl>
|
||||||
|
|
||||||
[Exposed=(Window,Worker)]
|
[Exposed=(Window,Worker)]
|
||||||
interface IDBDatabase : EventTarget {
|
interface IDBDatabase : EventTarget {
|
||||||
|
@ -9,7 +10,7 @@ interface IDBDatabase : EventTarget {
|
||||||
|
|
||||||
[FIXME, NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly", optional IDBTransactionOptions options = {});
|
[FIXME, NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly", optional IDBTransactionOptions options = {});
|
||||||
undefined close();
|
undefined close();
|
||||||
[FIXME, NewObject] IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters options = {});
|
[NewObject] IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters options = {});
|
||||||
[FIXME] undefined deleteObjectStore(DOMString name);
|
[FIXME] undefined deleteObjectStore(DOMString name);
|
||||||
|
|
||||||
// Event handlers:
|
// Event handlers:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
|
* Copyright (c) 2024-2025, stelar7 <dudedbz@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -15,14 +15,16 @@ GC_DEFINE_ALLOCATOR(IDBObjectStore);
|
||||||
|
|
||||||
IDBObjectStore::~IDBObjectStore() = default;
|
IDBObjectStore::~IDBObjectStore() = default;
|
||||||
|
|
||||||
IDBObjectStore::IDBObjectStore(JS::Realm& realm)
|
IDBObjectStore::IDBObjectStore(JS::Realm& realm, GC::Ref<ObjectStore> store, GC::Ref<IDBTransaction> transaction)
|
||||||
: PlatformObject(realm)
|
: PlatformObject(realm)
|
||||||
|
, m_store(store)
|
||||||
|
, m_transaction(transaction)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ref<IDBObjectStore> IDBObjectStore::create(JS::Realm& realm)
|
GC::Ref<IDBObjectStore> IDBObjectStore::create(JS::Realm& realm, GC::Ref<ObjectStore> store, GC::Ref<IDBTransaction> transaction)
|
||||||
{
|
{
|
||||||
return realm.create<IDBObjectStore>(realm);
|
return realm.create<IDBObjectStore>(realm, store, transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDBObjectStore::initialize(JS::Realm& realm)
|
void IDBObjectStore::initialize(JS::Realm& realm)
|
||||||
|
@ -31,4 +33,11 @@ void IDBObjectStore::initialize(JS::Realm& realm)
|
||||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(IDBObjectStore);
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(IDBObjectStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IDBObjectStore::visit_edges(Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_store);
|
||||||
|
visitor.visit(m_transaction);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, stelar7 <dudedbz@gmail.com>
|
* Copyright (c) 2024-2025, stelar7 <dudedbz@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -8,21 +8,30 @@
|
||||||
|
|
||||||
#include <LibGC/Heap.h>
|
#include <LibGC/Heap.h>
|
||||||
#include <LibWeb/Bindings/PlatformObject.h>
|
#include <LibWeb/Bindings/PlatformObject.h>
|
||||||
|
#include <LibWeb/IndexedDB/IDBTransaction.h>
|
||||||
|
#include <LibWeb/IndexedDB/Internal/ObjectStore.h>
|
||||||
|
|
||||||
namespace Web::IndexedDB {
|
namespace Web::IndexedDB {
|
||||||
|
|
||||||
// https://w3c.github.io/IndexedDB/#object-store-interface
|
// https://w3c.github.io/IndexedDB/#object-store-interface
|
||||||
|
// https://w3c.github.io/IndexedDB/#object-store-handle-construct
|
||||||
class IDBObjectStore : public Bindings::PlatformObject {
|
class IDBObjectStore : public Bindings::PlatformObject {
|
||||||
WEB_PLATFORM_OBJECT(IDBObjectStore, Bindings::PlatformObject);
|
WEB_PLATFORM_OBJECT(IDBObjectStore, Bindings::PlatformObject);
|
||||||
GC_DECLARE_ALLOCATOR(IDBObjectStore);
|
GC_DECLARE_ALLOCATOR(IDBObjectStore);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~IDBObjectStore() override;
|
virtual ~IDBObjectStore() override;
|
||||||
[[nodiscard]] static GC::Ref<IDBObjectStore> create(JS::Realm&);
|
[[nodiscard]] static GC::Ref<IDBObjectStore> create(JS::Realm&, GC::Ref<ObjectStore>, GC::Ref<IDBTransaction>);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit IDBObjectStore(JS::Realm&);
|
explicit IDBObjectStore(JS::Realm&, GC::Ref<ObjectStore>, GC::Ref<IDBTransaction>);
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
virtual void visit_edges(Visitor& visitor) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// An object store handle has an associated object store and an associated transaction.
|
||||||
|
GC::Ref<ObjectStore> m_store;
|
||||||
|
GC::Ref<IDBTransaction> m_transaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,16 @@ void Database::visit_edges(Visitor& visitor)
|
||||||
visitor.visit(m_object_stores);
|
visitor.visit(m_object_stores);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::has_object_store_named(String const& name) const
|
||||||
|
{
|
||||||
|
for (auto const& object_store : m_object_stores) {
|
||||||
|
if (object_store->name() == name)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Vector<GC::Root<Database>> Database::for_key(StorageAPI::StorageKey const& key)
|
Vector<GC::Root<Database>> Database::for_key(StorageAPI::StorageKey const& key)
|
||||||
{
|
{
|
||||||
Vector<GC::Root<Database>> databases;
|
Vector<GC::Root<Database>> databases;
|
||||||
|
|
|
@ -40,6 +40,9 @@ public:
|
||||||
return connections;
|
return connections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_object_store_named(String const& name) const;
|
||||||
|
void add_object_store(GC::Ref<ObjectStore> object_store) { m_object_stores.append(object_store); }
|
||||||
|
|
||||||
[[nodiscard]] static Vector<GC::Root<Database>> for_key(StorageAPI::StorageKey const&);
|
[[nodiscard]] static Vector<GC::Root<Database>> for_key(StorageAPI::StorageKey const&);
|
||||||
[[nodiscard]] static Optional<GC::Root<Database> const&> for_key_and_name(StorageAPI::StorageKey&, String&);
|
[[nodiscard]] static Optional<GC::Root<Database> const&> for_key_and_name(StorageAPI::StorageKey&, String&);
|
||||||
[[nodiscard]] static ErrorOr<GC::Root<Database>> create_for_key_and_name(JS::Realm&, StorageAPI::StorageKey&, String&);
|
[[nodiscard]] static ErrorOr<GC::Root<Database>> create_for_key_and_name(JS::Realm&, StorageAPI::StorageKey&, String&);
|
||||||
|
|
Loading…
Add table
Reference in a new issue