diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp index 19236e774f6..097a822eb05 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.cpp @@ -4,6 +4,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include +#include +#include +#include +#include #include #include #include @@ -13,6 +19,7 @@ #include #include #include +#include namespace Web::IndexedDB { @@ -141,4 +148,122 @@ bool fire_a_version_change_event(JS::Realm& realm, FlyString const& event_name, return legacy_output_did_listeners_throw_flag; } +// https://w3c.github.io/IndexedDB/#convert-value-to-key +ErrorOr 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"); + + // 3. Jump to the appropriate step below: + + // - If Type(input) is Number + if (input.is_number()) { + + // 1. If input is NaN then return invalid. + if (input.is_nan()) + return Error::from_string_literal("NaN key"); + + // 2. Otherwise, return a new key with type number and value input. + return Key::create_number(input.as_double()); + } + + // - If input is a Date (has a [[DateValue]] internal slot) + if (input.is_object() && is(input.as_object())) { + + // 1. Let ms be the value of input’s [[DateValue]] internal slot. + auto& date = static_cast(input.as_object()); + auto ms = date.date_value(); + + // 2. If ms is NaN then return invalid. + if (isnan(ms)) + return Error::from_string_literal("NaN key"); + + // 3. Otherwise, return a new key with type date and value ms. + return Key::create_date(ms); + } + + // - If Type(input) is String + if (input.is_string()) { + + // 1. Return a new key with type string and value input. + return Key::create_string(input.as_string().utf8_string()); + } + + // - If input is a buffer source type + if (input.is_object() && (is(input.as_object()) || is(input.as_object()) || is(input.as_object()))) { + + // 1. Let bytes be the result of getting a copy of the bytes held by the buffer source input. Rethrow any exceptions. + auto data_buffer = TRY(WebIDL::get_buffer_source_copy(input.as_object())); + + // 2. Return a new key with type binary and value bytes. + return Key::create_binary(data_buffer); + } + + // - If input is an Array exotic object + if (input.is_object() && is(input.as_object())) { + + // 1. Let len be ? ToLength( ? Get(input, "length")). + auto maybe_length = length_of_array_like(realm.vm(), input.as_object()); + if (maybe_length.is_error()) + return Error::from_string_literal("Failed to get length of array-like object"); + + auto length = maybe_length.release_value(); + + // 2. Append input to seen. + seen.append(input); + + // 3. Let keys be a new empty list. + Vector keys; + + // 4. Let index be 0. + u64 index = 0; + + // 5. While index is less than len: + while (index < length) { + // 1. Let hop be ? HasOwnProperty(input, index). + auto maybe_hop = input.as_object().has_own_property(index); + if (maybe_hop.is_error()) + return Error::from_string_literal("Failed to check if array-like object has property"); + + auto hop = maybe_hop.release_value(); + + // 2. If hop is false, return invalid. + if (!hop) + return Error::from_string_literal("Array-like object has no property"); + + // 3. Let entry be ? Get(input, index). + auto maybe_entry = input.as_object().get(index); + if (maybe_entry.is_error()) + return Error::from_string_literal("Failed to get property of array-like object"); + + // 4. Let key be the result of converting a value to a key with arguments entry and seen. + auto maybe_key = convert_a_value_to_a_key(realm, maybe_entry.release_value(), seen); + + // 5. ReturnIfAbrupt(key). + // 6. If key is invalid abort these steps and return invalid. + if (maybe_key.is_error()) + return maybe_key.release_error(); + + auto key = maybe_key.release_value(); + + // 7. Append key to keys. + keys.append(key); + + // 8. Increase index by 1. + index++; + } + + // 6. Return a new array key with value keys. + return Key::create_array(keys); + } + + // - Otherwise + // 1. Return invalid. + return Error::from_string_literal("Unknown key type"); +} + } diff --git a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h index b81d76af823..22562eafa84 100644 --- a/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h +++ b/Libraries/LibWeb/IndexedDB/Internal/Algorithms.h @@ -8,11 +8,13 @@ #include #include +#include #include namespace Web::IndexedDB { 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); +ErrorOr convert_a_value_to_a_key(JS::Realm&, JS::Value, Vector = {}); }