From 8a59c2cf56029d92c35c75ea8e723dd0ce935038 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 19 Sep 2025 23:33:16 -0700 Subject: [PATCH] Implement indexeddb array conversion (#38288) Implement conversion from js arrays into rust. Testing: WPT Fixes: None --------- Signed-off-by: Ashwin Naren Signed-off-by: Josh Matthews Co-authored-by: Josh Matthews --- components/script/dom/idbobjectstore.rs | 36 +++++-- components/script/indexed_db.rs | 55 +++++++++-- .../idb-partitioned-coverage.sub.html.ini | 1 + ...r-continuePrimaryKey-exceptions.any.js.ini | 3 - .../meta/IndexedDB/idbfactory_cmp.any.js.ini | 6 -- .../IndexedDB/idbkeyrange-includes.any.js.ini | 2 + .../idbkeyrange_incorrect.any.js.ini | 6 -- .../idbobjectstore_getAll.any.js.ini | 6 ++ .../idbobjectstore_openKeyCursor.any.js.ini | 2 + .../key-conversion-exceptions.any.js.ini | 96 ------------------- .../wpt/meta/IndexedDB/key_invalid.any.js.ini | 2 + tests/wpt/meta/IndexedDB/keyorder.any.js.ini | 72 -------------- .../IndexedDB/keypath-exceptions.any.js.ini | 14 ++- 13 files changed, 98 insertions(+), 203 deletions(-) diff --git a/components/script/dom/idbobjectstore.rs b/components/script/dom/idbobjectstore.rs index 7188344b443..5b8ad5b01ec 100644 --- a/components/script/dom/idbobjectstore.rs +++ b/components/script/dom/idbobjectstore.rs @@ -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 store’s 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 store’s 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!(), + }, } } diff --git a/components/script/indexed_db.rs b/components/script/indexed_db.rs index 60d58884b6f..7b75380bb5d 100644 --- a/components/script/indexed_db.rs +++ b/components/script/indexed_db.rs @@ -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>, ) -> Result { // 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))); } } } diff --git a/tests/wpt/meta/IndexedDB/idb-partitioned-coverage.sub.html.ini b/tests/wpt/meta/IndexedDB/idb-partitioned-coverage.sub.html.ini index 44a831c069b..30a8938c7f9 100644 --- a/tests/wpt/meta/IndexedDB/idb-partitioned-coverage.sub.html.ini +++ b/tests/wpt/meta/IndexedDB/idb-partitioned-coverage.sub.html.ini @@ -1,4 +1,5 @@ [idb-partitioned-coverage.sub.html] + expected: CRASH [ - advance] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.js.ini b/tests/wpt/meta/IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.js.ini index a4175b02477..cae6d52a98a 100644 --- a/tests/wpt/meta/IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.js.ini +++ b/tests/wpt/meta/IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.js.ini @@ -1,7 +1,4 @@ [idbcursor-continuePrimaryKey-exceptions.any.worker.html] - [IDBCursor continuePrimaryKey() on object store cursor] - expected: FAIL - [IDBCursor continuePrimaryKey() on "nextunique" cursor] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/idbfactory_cmp.any.js.ini b/tests/wpt/meta/IndexedDB/idbfactory_cmp.any.js.ini index 1a1d9fbb3f2..ddb5326b519 100644 --- a/tests/wpt/meta/IndexedDB/idbfactory_cmp.any.js.ini +++ b/tests/wpt/meta/IndexedDB/idbfactory_cmp.any.js.ini @@ -2,9 +2,6 @@ expected: ERROR [idbfactory_cmp.any.html] - [Array vs. Binary] - expected: FAIL - [Compare in unsigned octet values (in the range [0, 255\])] expected: FAIL @@ -13,9 +10,6 @@ [idbfactory_cmp.any.worker.html] - [Array vs. Binary] - expected: FAIL - [Compare in unsigned octet values (in the range [0, 255\])] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/idbkeyrange-includes.any.js.ini b/tests/wpt/meta/IndexedDB/idbkeyrange-includes.any.js.ini index 0cf69e81580..87b5375dbc6 100644 --- a/tests/wpt/meta/IndexedDB/idbkeyrange-includes.any.js.ini +++ b/tests/wpt/meta/IndexedDB/idbkeyrange-includes.any.js.ini @@ -1,4 +1,5 @@ [idbkeyrange-includes.any.html] + expected: CRASH [IDBKeyRange.includes() with invalid input] expected: FAIL @@ -13,6 +14,7 @@ expected: ERROR [idbkeyrange-includes.any.worker.html] + expected: CRASH [IDBKeyRange.includes() with invalid input] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/idbkeyrange_incorrect.any.js.ini b/tests/wpt/meta/IndexedDB/idbkeyrange_incorrect.any.js.ini index ee3992529ce..46ce56b7867 100644 --- a/tests/wpt/meta/IndexedDB/idbkeyrange_incorrect.any.js.ini +++ b/tests/wpt/meta/IndexedDB/idbkeyrange_incorrect.any.js.ini @@ -1,12 +1,6 @@ [idbkeyrange_incorrect.any.html] - [IDBKeyRange.bound(DOMString/Date/Array, 1) - A DOMString, Date and Array are greater than a float.] - expected: FAIL - [idbkeyrange_incorrect.any.worker.html] - [IDBKeyRange.bound(DOMString/Date/Array, 1) - A DOMString, Date and Array are greater than a float.] - expected: FAIL - [idbkeyrange_incorrect.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/IndexedDB/idbobjectstore_getAll.any.js.ini b/tests/wpt/meta/IndexedDB/idbobjectstore_getAll.any.js.ini index 5eca3119ad7..5ddf56d5b32 100644 --- a/tests/wpt/meta/IndexedDB/idbobjectstore_getAll.any.js.ini +++ b/tests/wpt/meta/IndexedDB/idbobjectstore_getAll.any.js.ini @@ -17,6 +17,9 @@ [Get all values with invalid query keys] expected: FAIL + [Get all values with transaction.commit()] + expected: FAIL + [idbobjectstore_getAll.any.serviceworker.html] expected: ERROR @@ -40,6 +43,9 @@ [Get all values with invalid query keys] expected: FAIL + [Get all values with transaction.commit()] + expected: FAIL + [idbobjectstore_getAll.any.sharedworker.html] expected: ERROR diff --git a/tests/wpt/meta/IndexedDB/idbobjectstore_openKeyCursor.any.js.ini b/tests/wpt/meta/IndexedDB/idbobjectstore_openKeyCursor.any.js.ini index 4db384c17da..3ba5b52b3ef 100644 --- a/tests/wpt/meta/IndexedDB/idbobjectstore_openKeyCursor.any.js.ini +++ b/tests/wpt/meta/IndexedDB/idbobjectstore_openKeyCursor.any.js.ini @@ -2,6 +2,7 @@ expected: ERROR [idbobjectstore_openKeyCursor.any.html] + expected: CRASH [IDBObjectStore.openKeyCursor() - forward iteration] expected: FAIL @@ -22,6 +23,7 @@ expected: ERROR [idbobjectstore_openKeyCursor.any.worker.html] + expected: CRASH [IDBObjectStore.openKeyCursor() - forward iteration] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/key-conversion-exceptions.any.js.ini b/tests/wpt/meta/IndexedDB/key-conversion-exceptions.any.js.ini index 904d00c6f33..adb5aa11410 100644 --- a/tests/wpt/meta/IndexedDB/key-conversion-exceptions.any.js.ini +++ b/tests/wpt/meta/IndexedDB/key-conversion-exceptions.any.js.ini @@ -2,54 +2,12 @@ expected: ERROR [key-conversion-exceptions.any.worker.html] - [IDBFactory cmp() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange only() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange lowerBound() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange upperBound() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange bound() static with throwing/invalid keys] - expected: FAIL - - [IDBCursor continue() method with throwing/invalid keys] - expected: FAIL - [IndexedDB: Exceptions thrown during key conversion] expected: FAIL [IDBCursor update() method with throwing/invalid keys] expected: FAIL - [IDBObjectStore add() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore put() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore delete() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore get() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore getKey() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore count() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore openCursor() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore openKeyCursor() method with throwing/invalid keys] - expected: FAIL - [IDBIndex get() method with throwing/invalid keys] expected: FAIL @@ -65,12 +23,6 @@ [IDBIndex openKeyCursor() method with throwing/invalid keys] expected: FAIL - [IDBObjectStore getAll() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore getAllKeys() method with throwing/invalid keys] - expected: FAIL - [IDBIndex getAll() method with throwing/invalid keys] expected: FAIL @@ -82,54 +34,12 @@ [key-conversion-exceptions.any.html] - [IDBFactory cmp() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange only() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange lowerBound() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange upperBound() static with throwing/invalid keys] - expected: FAIL - - [IDBKeyRange bound() static with throwing/invalid keys] - expected: FAIL - - [IDBCursor continue() method with throwing/invalid keys] - expected: FAIL - [IndexedDB: Exceptions thrown during key conversion] expected: FAIL [IDBCursor update() method with throwing/invalid keys] expected: FAIL - [IDBObjectStore add() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore put() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore delete() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore get() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore getKey() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore count() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore openCursor() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore openKeyCursor() method with throwing/invalid keys] - expected: FAIL - [IDBIndex get() method with throwing/invalid keys] expected: FAIL @@ -145,12 +55,6 @@ [IDBIndex openKeyCursor() method with throwing/invalid keys] expected: FAIL - [IDBObjectStore getAll() method with throwing/invalid keys] - expected: FAIL - - [IDBObjectStore getAllKeys() method with throwing/invalid keys] - expected: FAIL - [IDBIndex getAll() method with throwing/invalid keys] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/key_invalid.any.js.ini b/tests/wpt/meta/IndexedDB/key_invalid.any.js.ini index e5c55a9a7a6..f0a963739a4 100644 --- a/tests/wpt/meta/IndexedDB/key_invalid.any.js.ini +++ b/tests/wpt/meta/IndexedDB/key_invalid.any.js.ini @@ -2,6 +2,7 @@ expected: ERROR [key_invalid.any.worker.html] + expected: CRASH [Invalid key - [{}\]] expected: FAIL @@ -40,6 +41,7 @@ expected: ERROR [key_invalid.any.html] + expected: CRASH [Invalid key - [{}\]] expected: FAIL diff --git a/tests/wpt/meta/IndexedDB/keyorder.any.js.ini b/tests/wpt/meta/IndexedDB/keyorder.any.js.ini index 5bd3336e7aa..892819219e1 100644 --- a/tests/wpt/meta/IndexedDB/keyorder.any.js.ini +++ b/tests/wpt/meta/IndexedDB/keyorder.any.js.ini @@ -5,149 +5,77 @@ [Database readback sort - String < Array] expected: FAIL - [IDBKey.cmp sort - String < Array] - expected: FAIL - [Database readback sort - float < String] expected: FAIL - [IDBKey.cmp sort - float < String] - expected: FAIL - [Database readback sort - float < Date] expected: FAIL - [IDBKey.cmp sort - float < Date] - expected: FAIL - [Database readback sort - float < Date < String < Array] expected: FAIL - [IDBKey.cmp sort - float < Date < String < Array] - expected: FAIL - [Database readback sort - Date(1 sec ago) < Date(now) < Date(1 minute in future)] expected: FAIL - [IDBKey.cmp sort - Date(1 sec ago) < Date(now) < Date(1 minute in future)] - expected: FAIL - [Database readback sort - -1.1 < 1 < 1.01337 < 1.013373 < 2] expected: FAIL - [IDBKey.cmp sort - -1.1 < 1 < 1.01337 < 1.013373 < 2] - expected: FAIL - [Database readback sort - -Infinity < -0.01 < 0 < Infinity] expected: FAIL - [IDBKey.cmp sort - -Infinity < -0.01 < 0 < Infinity] - expected: FAIL - [Database readback sort - "" < "a" < "ab" < "b" < "ba"] expected: FAIL - [IDBKey.cmp sort - "" < "a" < "ab" < "b" < "ba"] - expected: FAIL - [Database readback sort - Arrays] expected: FAIL - [IDBKey.cmp sort - Arrays] - expected: FAIL - [Database readback sort - Array.length: 10,000 < Array.length: 10,001] expected: FAIL - [IDBKey.cmp sort - Array.length: 10,000 < Array.length: 10,001] - expected: FAIL - [Database readback sort - Infinity inside arrays] expected: FAIL - [IDBKey.cmp sort - Infinity inside arrays] - expected: FAIL - [Database readback sort - Test different stuff at once] expected: FAIL - [IDBKey.cmp sort - Test different stuff at once] - expected: FAIL - [keyorder.any.html] [Database readback sort - String < Array] expected: FAIL - [IDBKey.cmp sort - String < Array] - expected: FAIL - [Database readback sort - float < String] expected: FAIL - [IDBKey.cmp sort - float < String] - expected: FAIL - [Database readback sort - float < Date] expected: FAIL - [IDBKey.cmp sort - float < Date] - expected: FAIL - [Database readback sort - float < Date < String < Array] expected: FAIL - [IDBKey.cmp sort - float < Date < String < Array] - expected: FAIL - [Database readback sort - Date(1 sec ago) < Date(now) < Date(1 minute in future)] expected: FAIL - [IDBKey.cmp sort - Date(1 sec ago) < Date(now) < Date(1 minute in future)] - expected: FAIL - [Database readback sort - -1.1 < 1 < 1.01337 < 1.013373 < 2] expected: FAIL - [IDBKey.cmp sort - -1.1 < 1 < 1.01337 < 1.013373 < 2] - expected: FAIL - [Database readback sort - -Infinity < -0.01 < 0 < Infinity] expected: FAIL - [IDBKey.cmp sort - -Infinity < -0.01 < 0 < Infinity] - expected: FAIL - [Database readback sort - "" < "a" < "ab" < "b" < "ba"] expected: FAIL - [IDBKey.cmp sort - "" < "a" < "ab" < "b" < "ba"] - expected: FAIL - [Database readback sort - Arrays] expected: FAIL - [IDBKey.cmp sort - Arrays] - expected: FAIL - [Database readback sort - Array.length: 10,000 < Array.length: 10,001] expected: FAIL - [IDBKey.cmp sort - Array.length: 10,000 < Array.length: 10,001] - expected: FAIL - [Database readback sort - Infinity inside arrays] expected: FAIL - [IDBKey.cmp sort - Infinity inside arrays] - expected: FAIL - [Database readback sort - Test different stuff at once] expected: FAIL - [IDBKey.cmp sort - Test different stuff at once] - expected: FAIL - [keyorder.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/IndexedDB/keypath-exceptions.any.js.ini b/tests/wpt/meta/IndexedDB/keypath-exceptions.any.js.ini index c67cb04c80f..52f4271a550 100644 --- a/tests/wpt/meta/IndexedDB/keypath-exceptions.any.js.ini +++ b/tests/wpt/meta/IndexedDB/keypath-exceptions.any.js.ini @@ -1,8 +1,13 @@ [keypath-exceptions.any.html] - expected: CRASH [Array key conversion should not invoke prototype getters] expected: FAIL + [The last element of keypath is validated] + expected: FAIL + + [Key path evaluation: Exceptions from non-enumerable getters] + expected: FAIL + [keypath-exceptions.any.serviceworker.html] expected: ERROR @@ -11,6 +16,11 @@ expected: ERROR [keypath-exceptions.any.worker.html] - expected: CRASH [Array key conversion should not invoke prototype getters] expected: FAIL + + [The last element of keypath is validated] + expected: FAIL + + [Key path evaluation: Exceptions from non-enumerable getters] + expected: FAIL