Implement indexeddb array conversion (#38288)

Implement conversion from js arrays into rust.

Testing: WPT
Fixes: None

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Ashwin Naren 2025-09-19 23:33:16 -07:00 committed by GitHub
parent 8590c4edcf
commit 8a59c2cf56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 98 additions and 203 deletions

View file

@ -218,17 +218,35 @@ impl IDBObjectStore {
serialized_key = Some(convert_value_to_key(cx, key, None)?.into_result()?);
} else {
// Step 11: We should use in-line keys instead
if let Some(Ok(ExtractionResult::Key(kpk))) = self
// Step 11.1: Let kpk be the result of running the steps to extract a
// key from a value using a key path with clone and stores key path.
let extraction_result = self
.key_path
.as_ref()
.map(|p| extract_key(cx, value, p, None))
{
serialized_key = Some(kpk);
} else {
if !self.has_key_generator() {
return Err(Error::Data);
}
serialized_key = None;
.map(|p| extract_key(cx, value, p, None));
match extraction_result {
Some(Ok(ExtractionResult::Failure)) | None => {
// Step 11.4. Otherwise:
// Step 11.4.1. If store does not have a key generator, throw
// a "DataError" DOMException.
if !self.has_key_generator() {
return Err(Error::Data);
}
// Stept 11.4.2. Otherwise, if the steps to check that a key could
// be injected into a value with clone and stores key path return
// false, throw a "DataError" DOMException.
// TODO
serialized_key = None;
},
// Step 11.1. Rethrow any exceptions.
Some(extraction_result) => match extraction_result? {
// Step 11.2. If kpk is invalid, throw a "DataError" DOMException.
ExtractionResult::Invalid => return Err(Error::Data),
// Step 11.3. If kpk is not failure, let key be kpk.
ExtractionResult::Key(kpk) => serialized_key = Some(kpk),
ExtractionResult::Failure => unreachable!(),
},
}
}

View file

@ -11,12 +11,13 @@ use itertools::Itertools;
use js::conversions::jsstr_to_string;
use js::gc::MutableHandle;
use js::jsapi::{
ClippedTime, ESClass, GetBuiltinClass, IsArrayBufferObject, JS_GetStringLength,
JS_IsArrayBufferViewObject, JS_NewObject, NewDateObject,
ClippedTime, ESClass, GetArrayLength, GetBuiltinClass, IsArrayBufferObject, JS_GetStringLength,
JS_HasOwnPropertyById, JS_IndexToId, JS_IsArrayBufferViewObject, JS_NewObject, NewDateObject,
PropertyKey,
};
use js::jsval::{DoubleValue, UndefinedValue};
use js::rust::wrappers::{IsArrayObject, JS_GetProperty, JS_HasOwnProperty, JS_IsIdentifier};
use js::rust::{HandleValue, MutableHandleValue};
use js::rust::{HandleValue, IntoHandle, IntoMutableHandle, MutableHandleValue};
use net_traits::indexeddb_thread::{BackendResult, IndexedDBKeyRange, IndexedDBKeyType};
use profile_traits::ipc;
use profile_traits::ipc::IpcReceiver;
@ -158,7 +159,7 @@ pub fn convert_value_to_key(
seen: Option<Vec<HandleValue>>,
) -> Result<ConversionResult, Error> {
// Step 1: If seen was not given, then let seen be a new empty set.
let _seen = seen.unwrap_or_default();
let mut seen = seen.unwrap_or_default();
// Step 2: If seen contains input, then return invalid.
// FIXME:(arihant2math) implement this
@ -189,13 +190,13 @@ pub fn convert_value_to_key(
let mut built_in_class = ESClass::Other;
if !GetBuiltinClass(*cx, object.handle().into(), &mut built_in_class) {
return Err(Error::Data);
return Err(Error::JSFailed);
}
if let ESClass::Date = built_in_class {
let mut f = f64::NAN;
if !js::jsapi::DateGetMsecSinceEpoch(*cx, object.handle().into(), &mut f) {
return Err(Error::Data);
return Err(Error::JSFailed);
}
if f.is_nan() {
return Err(Error::Data);
@ -212,9 +213,45 @@ pub fn convert_value_to_key(
}
if let ESClass::Array = built_in_class {
// FIXME:(arihant2math)
error!("Arrays as keys is currently unsupported");
return Err(Error::NotSupported);
let mut len = 0;
if !GetArrayLength(*cx, object.handle().into_handle(), &mut len) {
return Err(Error::JSFailed);
}
seen.push(input);
let mut values = vec![];
for i in 0..len {
rooted!(in(*cx) let mut id: PropertyKey);
if !JS_IndexToId(*cx, i, js::jsapi::MutableHandleId::from(id.handle_mut())) {
return Err(Error::JSFailed);
}
let mut has_own = false;
if !JS_HasOwnPropertyById(
*cx,
object.handle().into_handle(),
id.handle().into_handle(),
&mut has_own,
) {
return Err(Error::JSFailed);
}
if !has_own {
return Ok(ConversionResult::Invalid);
}
rooted!(in(*cx) let mut item = UndefinedValue());
if !js::jsapi::JS_GetPropertyById(
*cx,
object.handle().into_handle(),
id.handle().into_handle(),
item.handle_mut().into_handle_mut(),
) {
return Err(Error::JSFailed);
}
let key = match convert_value_to_key(cx, item.handle(), Some(seen.clone()))? {
ConversionResult::Valid(key) => key,
ConversionResult::Invalid => return Ok(ConversionResult::Invalid),
};
values.push(key);
}
return Ok(ConversionResult::Valid(IndexedDBKeyType::Array(values)));
}
}
}