mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-23 13:05:12 +00:00
LibWeb/IDB: Implement store_a_record_into_an_object_store
This commit is contained in:
parent
432e2cf1e1
commit
80c36a90ee
6 changed files with 147 additions and 0 deletions
|
@ -1312,4 +1312,117 @@ void delete_records_from_an_object_store(GC::Ref<ObjectStore> store, GC::Ref<IDB
|
|||
// 3. Return undefined.
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#store-a-record-into-an-object-store
|
||||
WebIDL::ExceptionOr<GC::Ptr<Key>> store_a_record_into_an_object_store(JS::Realm& realm, GC::Ref<ObjectStore> store, JS::Value value, GC::Ptr<Key> key, bool no_overwrite)
|
||||
{
|
||||
// 1. If store uses a key generator, then:
|
||||
if (store->key_generator().has_value()) {
|
||||
// 1. If key is undefined, then:
|
||||
if (key == nullptr) {
|
||||
// 1. Let key be the result of generating a key for store.
|
||||
auto maybe_key = generate_a_key(store);
|
||||
|
||||
// 2. If key is failure, then this operation failed with a "ConstraintError" DOMException. Abort this algorithm without taking any further steps.
|
||||
if (maybe_key.is_error())
|
||||
return WebIDL::ConstraintError::create(realm, String::from_utf8_without_validation(maybe_key.error().string_literal().bytes()));
|
||||
|
||||
key = Key::create_number(realm, static_cast<double>(maybe_key.value()));
|
||||
|
||||
// 3. If store also uses in-line keys, then run inject a key into a value using a key path with value, key and store’s key path.
|
||||
if (store->uses_inline_keys())
|
||||
inject_a_key_into_a_value_using_a_key_path(realm, value, GC::Ref(*key), store->key_path().value());
|
||||
}
|
||||
|
||||
// 2. Otherwise, run possibly update the key generator for store with key.
|
||||
else {
|
||||
possibly_update_the_key_generator(store, GC::Ref(*key));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If the no-overwrite flag was given to these steps and is true, and a record already exists in store with its key equal to key,
|
||||
// then this operation failed with a "ConstraintError" DOMException. Abort this algorithm without taking any further steps.
|
||||
auto has_record = store->has_record_with_key(*key);
|
||||
if (no_overwrite && has_record)
|
||||
return WebIDL::ConstraintError::create(realm, "Record already exists"_string);
|
||||
|
||||
// 3. If a record already exists in store with its key equal to key, then remove the record from store using delete records from an object store.
|
||||
if (has_record) {
|
||||
auto key_range = IDBKeyRange::create(realm, key, key, false, false);
|
||||
delete_records_from_an_object_store(store, key_range);
|
||||
}
|
||||
|
||||
// 4. Store a record in store containing key as its key and ! StructuredSerializeForStorage(value) as its value.
|
||||
// The record is stored in the object store’s list of records such that the list is sorted according to the key of the records in ascending order.
|
||||
Record record = {
|
||||
.key = *key,
|
||||
.value = MUST(HTML::structured_serialize_for_storage(realm.vm(), value)),
|
||||
};
|
||||
store->store_a_record(record);
|
||||
|
||||
// 5. For each index which references store:
|
||||
for (auto const& [name, index] : store->index_set()) {
|
||||
// 1. Let index key be the result of extracting a key from a value using a key path with value, index’s key path, and index’s multiEntry flag.
|
||||
auto index_key = TRY(extract_a_key_from_a_value_using_a_key_path(realm, value, index->key_path(), index->multi_entry()));
|
||||
|
||||
// 2. If index key is an exception, or invalid, or failure, take no further actions for index, and continue these steps for the next index.
|
||||
if (index_key.is_error())
|
||||
continue;
|
||||
|
||||
auto index_key_value = index_key.value();
|
||||
auto index_multi_entry = index->multi_entry();
|
||||
auto index_key_is_array = index_key_value->type() == Key::KeyType::Array;
|
||||
auto index_is_unique = index->unique();
|
||||
|
||||
// 3. If index’s multiEntry flag is false, or if index key is not an array key,
|
||||
// and if index already contains a record with key equal to index key,
|
||||
// and index’s unique flag is true,
|
||||
// then this operation failed with a "ConstraintError" DOMException.
|
||||
// Abort this algorithm without taking any further steps.
|
||||
if ((!index_multi_entry || !index_key_is_array) && index_is_unique && index->has_record_with_key(index_key_value))
|
||||
return WebIDL::ConstraintError::create(realm, "Record already exists in index"_string);
|
||||
|
||||
// 4. If index’s multiEntry flag is true and index key is an array key,
|
||||
// and if index already contains a record with key equal to any of the subkeys of index key,
|
||||
// and index’s unique flag is true,
|
||||
// then this operation failed with a "ConstraintError" DOMException.
|
||||
// Abort this algorithm without taking any further steps.
|
||||
if (index_multi_entry && index_key_is_array && index_is_unique) {
|
||||
for (auto const& subkey : index_key_value->subkeys()) {
|
||||
if (index->has_record_with_key(*subkey))
|
||||
return WebIDL::ConstraintError::create(realm, "Record already exists in index"_string);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. If index’s multiEntry flag is false, or if index key is not an array key
|
||||
// then store a record in index containing index key as its key and key as its value.
|
||||
// The record is stored in index’s list of records such that the list is sorted primarily on the records keys,
|
||||
// and secondarily on the records values, in ascending order.
|
||||
if (!index_multi_entry || !index_key_is_array) {
|
||||
// FIXME:
|
||||
// Record index_record = {
|
||||
// .key = index_key_value,
|
||||
// .value = MUST(HTML::structured_serialize_for_storage(realm.vm(), key)),
|
||||
// };
|
||||
// index->store_a_record(index_record);
|
||||
}
|
||||
|
||||
// 6. If index’s multiEntry flag is true and index key is an array key,
|
||||
// then for each subkey of the subkeys of index key store a record in index containing subkey as its key and key as its value.
|
||||
if (index_multi_entry && index_key_is_array) {
|
||||
for (auto const& subkey : index_key_value->subkeys()) {
|
||||
(void)subkey;
|
||||
// FIXME:
|
||||
// Record index_record = {
|
||||
// .key = *subkey,
|
||||
// .value = MUST(HTML::structured_serialize_for_storage(realm.vm(), key)),
|
||||
// };
|
||||
// index->store_a_record(index_record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Return key.
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,5 +40,6 @@ ErrorOr<u64> generate_a_key(GC::Ref<ObjectStore>);
|
|||
void possibly_update_the_key_generator(GC::Ref<ObjectStore>, GC::Ref<Key>);
|
||||
void inject_a_key_into_a_value_using_a_key_path(JS::Realm&, JS::Value, GC::Ref<Key>, KeyPath const&);
|
||||
void delete_records_from_an_object_store(GC::Ref<ObjectStore>, GC::Ref<IDBKeyRange>);
|
||||
WebIDL::ExceptionOr<GC::Ptr<Key>> store_a_record_into_an_object_store(JS::Realm&, GC::Ref<ObjectStore>, JS::Value, GC::Ptr<Key>, bool);
|
||||
|
||||
}
|
||||
|
|
|
@ -48,4 +48,13 @@ void Index::set_name(String name)
|
|||
m_name = move(name);
|
||||
}
|
||||
|
||||
bool Index::has_record_with_key(GC::Ref<Key> key)
|
||||
{
|
||||
auto index = m_records.find_if([&key](auto const& record) {
|
||||
return record.key == key;
|
||||
});
|
||||
|
||||
return index != m_records.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
[[nodiscard]] AK::ReadonlySpan<IndexRecord> records() const { return m_records; }
|
||||
[[nodiscard]] KeyPath const& key_path() const { return m_key_path; }
|
||||
|
||||
[[nodiscard]] bool has_record_with_key(GC::Ref<Key> key);
|
||||
|
||||
protected:
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibWeb/IndexedDB/IDBKeyRange.h>
|
||||
#include <LibWeb/IndexedDB/Internal/ObjectStore.h>
|
||||
|
||||
|
@ -47,4 +48,23 @@ void ObjectStore::remove_records_in_range(GC::Ref<IDBKeyRange> range)
|
|||
});
|
||||
}
|
||||
|
||||
bool ObjectStore::has_record_with_key(GC::Ref<Key> key)
|
||||
{
|
||||
auto index = m_records.find_if([&key](auto const& record) {
|
||||
return record.key == key;
|
||||
});
|
||||
|
||||
return index != m_records.end();
|
||||
}
|
||||
|
||||
void ObjectStore::store_a_record(Record const& record)
|
||||
{
|
||||
m_records.append(record);
|
||||
|
||||
// NOTE: The record is stored in the object store’s list of records such that the list is sorted according to the key of the records in ascending order.
|
||||
AK::quick_sort(m_records, [](auto const& a, auto const& b) {
|
||||
return Key::compare_two_keys(a.key, b.key) < 0;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ public:
|
|||
GC::Ref<Database> database() const { return m_database; }
|
||||
|
||||
void remove_records_in_range(GC::Ref<IDBKeyRange> range);
|
||||
bool has_record_with_key(GC::Ref<Key> key);
|
||||
void store_a_record(Record const& record);
|
||||
|
||||
protected:
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
|
Loading…
Add table
Reference in a new issue