diff --git a/Libraries/LibWeb/IndexedDB/IDBFactory.cpp b/Libraries/LibWeb/IndexedDB/IDBFactory.cpp index 9690cb2f38c..418dc6d6e76 100644 --- a/Libraries/LibWeb/IndexedDB/IDBFactory.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBFactory.cpp @@ -108,18 +108,18 @@ WebIDL::ExceptionOr IDBFactory::cmp(JS::Value first, JS::Value second) auto a = TRY(convert_a_value_to_a_key(realm(), first)); // 2. If a is invalid, throw a "DataError" DOMException. - if (a.is_error()) + if (a->is_invalid()) return WebIDL::DataError::create(realm(), "Failed to convert a value to a key"_string); // 3. Let b be the result of converting a value to a key with second. Rethrow any exceptions. auto b = TRY(convert_a_value_to_a_key(realm(), second)); // 4. If b is invalid, throw a "DataError" DOMException. - if (b.is_error()) + if (b->is_invalid()) return WebIDL::DataError::create(realm(), "Failed to convert a value to a key"_string); // 5. Return the results of comparing two keys with a and b. - return Key::compare_two_keys(a.release_value(), b.release_value()); + return Key::compare_two_keys(a, b); } // https://w3c.github.io/IndexedDB/#dom-idbfactory-deletedatabase diff --git a/Libraries/LibWeb/IndexedDB/IDBKeyRange.cpp b/Libraries/LibWeb/IndexedDB/IDBKeyRange.cpp index b9c9e5adcb2..8ec54b6465a 100644 --- a/Libraries/LibWeb/IndexedDB/IDBKeyRange.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBKeyRange.cpp @@ -64,14 +64,12 @@ WebIDL::ExceptionOr> IDBKeyRange::only(JS::VM& vm, JS::Valu auto& realm = *vm.current_realm(); // 1. Let key be the result of converting a value to a key with value. Rethrow any exceptions. - auto maybe_key = TRY(convert_a_value_to_a_key(realm, value)); + auto key = TRY(convert_a_value_to_a_key(realm, value)); // 2. If key is invalid, throw a "DataError" DOMException. - if (maybe_key.is_error()) + if (key->is_invalid()) return WebIDL::DataError::create(realm, "Value is invalid"_string); - auto key = maybe_key.release_value(); - // 3. Create and return a new key range containing only key. return IDBKeyRange::create(realm, key, key, false, false); } @@ -82,14 +80,14 @@ WebIDL::ExceptionOr> IDBKeyRange::lower_bound(JS::VM& vm, J auto& realm = *vm.current_realm(); // 1. Let lowerKey be the result of converting a value to a key with lower. Rethrow any exceptions. - auto lower_key = TRY(convert_a_value_to_a_key(realm, lower)); + auto key = TRY(convert_a_value_to_a_key(realm, lower)); // 2. If lowerKey is invalid, throw a "DataError" DOMException. - if (lower_key.is_error()) + if (key->is_invalid()) return WebIDL::DataError::create(realm, "Value is invalid"_string); // 3. Create and return a new key range with lower bound set to lowerKey, lower open flag set to open, upper bound set to null, and upper open flag set to true. - return IDBKeyRange::create(realm, lower_key.release_value(), {}, open, true); + return IDBKeyRange::create(realm, key, {}, open, true); } // https://w3c.github.io/IndexedDB/#dom-idbkeyrange-upperbound @@ -98,14 +96,14 @@ WebIDL::ExceptionOr> IDBKeyRange::upper_bound(JS::VM& vm, J auto& realm = *vm.current_realm(); // 1. Let upperKey be the result of converting a value to a key with upper. Rethrow any exceptions. - auto upper_key = TRY(convert_a_value_to_a_key(realm, upper)); + auto key = TRY(convert_a_value_to_a_key(realm, upper)); // 2. If upperKey is invalid, throw a "DataError" DOMException. - if (upper_key.is_error()) + if (key->is_invalid()) return WebIDL::DataError::create(realm, "Value is invalid"_string); // 3. Create and return a new key range with lower bound set to null, lower open flag set to true, upper bound set to upperKey, and upper open flag set to open. - return IDBKeyRange::create(realm, {}, upper_key.release_value(), true, open); + return IDBKeyRange::create(realm, {}, key, true, open); } // https://w3c.github.io/IndexedDB/#dom-idbkeyrange-bound @@ -117,22 +115,22 @@ WebIDL::ExceptionOr> IDBKeyRange::bound(JS::VM& vm, JS::Val auto lower_key = TRY(convert_a_value_to_a_key(realm, lower)); // 2. If lowerKey is invalid, throw a "DataError" DOMException. - if (lower_key.is_error()) + if (lower_key->is_invalid()) return WebIDL::DataError::create(realm, "Value is invalid"_string); // 3. Let upperKey be the result of converting a value to a key with upper. Rethrow any exceptions. auto upper_key = TRY(convert_a_value_to_a_key(realm, upper)); // 4. If upperKey is invalid, throw a "DataError" DOMException. - if (upper_key.is_error()) + if (upper_key->is_invalid()) return WebIDL::DataError::create(realm, "Value is invalid"_string); // 5. If lowerKey is greater than upperKey, throw a "DataError" DOMException. - if (Key::less_than(upper_key.release_value(), lower_key.release_value())) + if (Key::less_than(upper_key, lower_key)) return WebIDL::DataError::create(realm, "Lower key is greater than upper key"_string); // 6. Create and return a new key range with lower bound set to lowerKey, lower open flag set to lowerOpen, upper bound set to upperKey and upper open flag set to upperOpen. - return IDBKeyRange::create(realm, lower_key.release_value(), upper_key.release_value(), lower_open, upper_open); + return IDBKeyRange::create(realm, lower_key, upper_key, lower_open, upper_open); } // https://w3c.github.io/IndexedDB/#dom-idbkeyrange-includes @@ -144,11 +142,11 @@ WebIDL::ExceptionOr IDBKeyRange::includes(JS::Value key) auto k = TRY(convert_a_value_to_a_key(realm, key)); // 2. If k is invalid, throw a "DataError" DOMException. - if (k.is_error()) + if (k->is_invalid()) return WebIDL::DataError::create(realm, "Value is invalid"_string); // 3. Return true if k is in this range, and false otherwise. - return is_in_range(k.release_value()); + return is_in_range(k); } // https://w3c.github.io/IndexedDB/#dom-idbkeyrange-lower diff --git a/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp b/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp index 2f85a046363..175d41af48d 100644 --- a/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp +++ b/Libraries/LibWeb/IndexedDB/IDBObjectStore.cpp @@ -257,13 +257,14 @@ WebIDL::ExceptionOr> IDBObjectStore::add_or_put(GC::Refis_invalid()) return WebIDL::DataError::create(realm, "Key is invalid"_string); // 3. Let key be r. - key_value = maybe_key.release_value(); + key_value = r; } // 9. Let targetRealm be a user-agent defined Realm. @@ -277,13 +278,14 @@ WebIDL::ExceptionOr> IDBObjectStore::add_or_put(GC::Refis_invalid()) + return WebIDL::DataError::create(realm, "Key path is invalid"_string); } // 4. Otherwise (kpk is failure): @@ -300,12 +302,9 @@ WebIDL::ExceptionOr> IDBObjectStore::add_or_put(GC::Ref()>::create(realm.heap(), [&realm, &store, clone, key_value, no_overwrite] -> WebIDL::ExceptionOr { - auto maybe_key = store_a_record_into_an_object_store(realm, store, clone, key_value, no_overwrite); - if (maybe_key.is_error()) - return maybe_key.release_error(); + auto optional_key = TRY(store_a_record_into_an_object_store(realm, store, clone, key_value, no_overwrite)); - auto optional_key = maybe_key.release_value(); - if (optional_key == nullptr) + if (!optional_key || optional_key->is_invalid()) return JS::js_undefined(); return convert_a_key_to_a_value(realm, GC::Ref(*optional_key)); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index a1012745ccd..0d3b08c4e19 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -207,14 +207,14 @@ bool fire_a_version_change_event(JS::Realm& realm, FlyString const& event_name, } // https://w3c.github.io/IndexedDB/#convert-value-to-key -WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm& realm, JS::Value input, Vector seen) +WebIDL::ExceptionOr> convert_a_value_to_a_key(JS::Realm& realm, JS::Value input, Vector seen) { // 1. If seen was not given, then let seen be a new empty set. // NOTE: This is handled by the caller. // 2. If seen contains input, then return invalid. if (seen.contains_slow(input)) - return Error::from_string_literal("Already seen key"); + return Key::create_invalid(realm, "Already seen key"_string); // 3. Jump to the appropriate step below: @@ -223,7 +223,7 @@ WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm& r // 1. If input is NaN then return invalid. if (input.is_nan()) - return Error::from_string_literal("NaN key"); + return Key::create_invalid(realm, "NaN key"_string); // 2. Otherwise, return a new key with type number and value input. return Key::create_number(realm, input.as_double()); @@ -238,7 +238,7 @@ WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm& r // 2. If ms is NaN then return invalid. if (isnan(ms)) - return Error::from_string_literal("NaN key"); + return Key::create_invalid(realm, "NaN key"_string); // 3. Otherwise, return a new key with type date and value ms. return Key::create_date(realm, ms); @@ -256,10 +256,10 @@ WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm& r // 1. If input is detached then return invalid. if (WebIDL::is_buffer_source_detached(input)) - return Error::from_string_literal("Detached buffer is not supported as key"); + return Key::create_invalid(realm, "Detached buffer is not supported as key"_string); // 2. Let bytes be the result of getting a copy of the bytes held by the buffer source input. - auto data_buffer = TRY(WebIDL::get_buffer_source_copy(input.as_object())); + auto data_buffer = MUST(WebIDL::get_buffer_source_copy(input.as_object())); // 3. Return a new key with type binary and value bytes. return Key::create_binary(realm, data_buffer); @@ -287,15 +287,18 @@ WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm& r // 2. If hop is false, return invalid. if (!hop) - return Error::from_string_literal("Array-like object has no property"); + return Key::create_invalid(realm, "Array-like object has no property"_string); // 3. Let entry be ? Get(input, index). auto entry = TRY(input.as_object().get(index)); // 4. Let key be the result of converting a value to a key with arguments entry and seen. // 5. ReturnIfAbrupt(key). + auto key = TRY(convert_a_value_to_a_key(realm, entry, seen)); + // 6. If key is invalid abort these steps and return invalid. - auto key = TRY(TRY(convert_a_value_to_a_key(realm, entry, seen))); + if (key->is_invalid()) + return key; // 7. Append key to keys. keys.append(key); @@ -309,7 +312,8 @@ WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm& r } // - Otherwise - return Error::from_string_literal("Unknown key type"); + // Return invalid. + return Key::create_invalid(realm, "Unable to convert value to key. Its not of a known type"_string); } // https://w3c.github.io/IndexedDB/#close-a-database-connection @@ -682,6 +686,8 @@ JS::Value convert_a_key_to_a_value(JS::Realm& realm, GC::Ref key) // 6. Return array. return array; } + case Key::KeyType::Invalid: + VERIFY_NOT_REACHED(); } VERIFY_NOT_REACHED(); @@ -812,7 +818,7 @@ WebIDL::ExceptionOr clone_in_realm(JS::Realm& target_realm, JS::Value } // https://w3c.github.io/IndexedDB/#convert-a-value-to-a-multientry-key -WebIDL::ExceptionOr>> convert_a_value_to_a_multi_entry_key(JS::Realm& realm, JS::Value value) +WebIDL::ExceptionOr> convert_a_value_to_a_multi_entry_key(JS::Realm& realm, JS::Value value) { // 1. If input is an Array exotic object, then: if (value.is_object() && is(value.as_object())) { @@ -843,14 +849,10 @@ WebIDL::ExceptionOr>> convert_a_value_to_a_multi_entry_key( // 2. If key is not invalid or an abrupt completion, and there is no item in keys equal to key, then append key to keys. if (!completion_key.is_error()) { - auto maybe_key = completion_key.release_value(); + auto key = completion_key.release_value(); - if (!maybe_key.is_error()) { - auto key = maybe_key.release_value(); - - if (!keys.contains_slow(key)) - keys.append(key); - } + if (!key->is_invalid() && !keys.contains_slow(key)) + keys.append(key); } } @@ -1368,15 +1370,22 @@ WebIDL::ExceptionOr> store_a_record_into_an_object_store(JS::Realm& // 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())); + auto completion_index_key = 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()) + if (completion_index_key.is_error()) + continue; + + auto failure_index_key = completion_index_key.release_value(); + if (failure_index_key.is_error()) + continue; + + auto index_key = failure_index_key.release_value(); + if (index_key->is_invalid()) 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_key_is_array = index_key->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, @@ -1384,7 +1393,7 @@ WebIDL::ExceptionOr> store_a_record_into_an_object_store(JS::Realm& // 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)) + if ((!index_multi_entry || !index_key_is_array) && index_is_unique && index->has_record_with_key(index_key)) 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, @@ -1393,7 +1402,7 @@ WebIDL::ExceptionOr> store_a_record_into_an_object_store(JS::Realm& // 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()) { + for (auto const& subkey : index_key->subkeys()) { if (index->has_record_with_key(*subkey)) return WebIDL::ConstraintError::create(realm, "Record already exists in index"_string); } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index e9c208bd082..53f8d25b92b 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -19,7 +19,7 @@ using KeyPath = Variant>; WebIDL::ExceptionOr> open_a_database_connection(JS::Realm&, StorageAPI::StorageKey, String, Optional, GC::Ref); bool fire_a_version_change_event(JS::Realm&, FlyString const&, GC::Ref, u64, Optional); -WebIDL::ExceptionOr>> convert_a_value_to_a_key(JS::Realm&, JS::Value, Vector = {}); +WebIDL::ExceptionOr> convert_a_value_to_a_key(JS::Realm&, JS::Value, Vector = {}); void close_a_database_connection(GC::Ref, bool forced = false); GC::Ref upgrade_a_database(JS::Realm&, GC::Ref, u64, GC::Ref); WebIDL::ExceptionOr delete_a_database(JS::Realm&, StorageAPI::StorageKey, String, GC::Ref); @@ -29,7 +29,7 @@ bool is_valid_key_path(KeyPath const&); GC::Ref create_a_sorted_name_list(JS::Realm&, Vector); void commit_a_transaction(JS::Realm&, GC::Ref); WebIDL::ExceptionOr clone_in_realm(JS::Realm&, JS::Value, GC::Ref); -WebIDL::ExceptionOr>> convert_a_value_to_a_multi_entry_key(JS::Realm&, JS::Value); +WebIDL::ExceptionOr> convert_a_value_to_a_multi_entry_key(JS::Realm&, JS::Value); WebIDL::ExceptionOr> evaluate_key_path_on_a_value(JS::Realm&, JS::Value, KeyPath const&); WebIDL::ExceptionOr>> extract_a_key_from_a_value_using_a_key_path(JS::Realm&, JS::Value, KeyPath const&, bool = false); bool check_that_a_key_could_be_injected_into_a_value(JS::Realm&, JS::Value, KeyPath const&); diff --git a/Libraries/LibWeb/IndexedDB/Internal/Key.cpp b/Libraries/LibWeb/IndexedDB/Internal/Key.cpp index df054d1c90c..dcb0dc2fb18 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Key.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Key.cpp @@ -73,6 +73,8 @@ i8 Key::compare_two_keys(GC::Ref a, GC::Ref b) // 6. Switch on ta: switch (ta) { + case KeyType::Invalid: + VERIFY_NOT_REACHED(); // number // date case KeyType::Number: diff --git a/Libraries/LibWeb/IndexedDB/Internal/Key.h b/Libraries/LibWeb/IndexedDB/Internal/Key.h index aca300c2c0a..0dccc6dd924 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Key.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Key.h @@ -31,6 +31,7 @@ class Key : public JS::Cell { // A key has an associated type which is one of: number, date, string, binary, or array. enum KeyType { + Invalid, Number, Date, String, @@ -45,6 +46,8 @@ public: [[nodiscard]] KeyType type() { return m_type; } [[nodiscard]] KeyValue value() { return m_value; } + [[nodiscard]] bool is_invalid() { return m_type == Invalid; } + [[nodiscard]] double value_as_double() { return m_value.get(); } [[nodiscard]] AK::String value_as_string() { return m_value.get(); } [[nodiscard]] ByteBuffer value_as_byte_buffer() { return m_value.get(); } @@ -60,6 +63,7 @@ public: [[nodiscard]] static GC::Ref create_string(JS::Realm& realm, AK::String const& value) { return create(realm, String, value); } [[nodiscard]] static GC::Ref create_binary(JS::Realm& realm, ByteBuffer const& value) { return create(realm, Binary, value); } [[nodiscard]] static GC::Ref create_array(JS::Realm& realm, Vector> const& value) { return create(realm, Array, value); } + [[nodiscard]] static GC::Ref create_invalid(JS::Realm& realm, AK::String const& value) { return create(realm, Invalid, value); } [[nodiscard]] static i8 compare_two_keys(GC::Ref a, GC::Ref b); [[nodiscard]] static bool equals(GC::Ref a, GC::Ref b) { return compare_two_keys(a, b) == 0; }