[IndexedDB] Key ranges implementation (#37684)

Improves the implementation of keys to a point where key ranges can be
implemented as well.

Due to me making branching mistakes I'll have to cherry-pick out the
first commit (it's from #37682)

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
Ashwin Naren 2025-07-10 11:33:23 -07:00 committed by GitHub
parent 32d889f770
commit 6ad57a343e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 19 deletions

View file

@ -13,6 +13,7 @@ use js::jsapi::{
}; };
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::{HandleValue, MutableHandleValue}; use js::rust::{HandleValue, MutableHandleValue};
use log::error;
use net_traits::IpcSend; use net_traits::IpcSend;
use net_traits::indexeddb_thread::{ use net_traits::indexeddb_thread::{
AsyncOperation, IndexedDBKeyType, IndexedDBThreadMsg, SyncOperation, AsyncOperation, IndexedDBKeyType, IndexedDBThreadMsg, SyncOperation,
@ -25,6 +26,7 @@ use crate::dom::bindings::codegen::Bindings::IDBObjectStoreBinding::IDBObjectSto
use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode; use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
// We need to alias this name, otherwise test-tidy complains at &String reference. // We need to alias this name, otherwise test-tidy complains at &String reference.
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence; use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
use crate::dom::bindings::conversions::jsstring_to_str;
use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::root::{DomRoot, MutNullableDom};
@ -150,22 +152,22 @@ impl IDBObjectStore {
let _seen = seen.unwrap_or_default(); let _seen = seen.unwrap_or_default();
// Step 2: If seen contains input, then return invalid. // Step 2: If seen contains input, then return invalid.
// FIXME:(rasviitanen) // FIXME:(arihant2math) implement this
// Check if we have seen this key // Check if we have seen this key
// Does not currently work with HandleValue, // Does not currently work with HandleValue,
// as it does not implement PartialEq // as it does not implement PartialEq
// Step 3 // Step 3
// FIXME:(rasviitanen) Accept buffer, array and date as well // FIXME:(arihant2math) Accept buffer, array and date as well
if input.is_number() { if input.is_number() {
// FIXME:(rasviitanen) check for NaN // FIXME:(arihant2math) check for NaN
let key = structuredclone::write(cx, input, None).expect("Could not serialize key"); return Ok(IndexedDBKeyType::Number(input.to_number()));
return Ok(IndexedDBKeyType::Number(key.serialized));
} }
if input.is_string() { if input.is_string() {
let key = structuredclone::write(cx, input, None).expect("Could not serialize key"); let string_ptr = std::ptr::NonNull::new(input.to_string()).unwrap();
return Ok(IndexedDBKeyType::String(key.serialized)); let key = unsafe { jsstring_to_str(*cx, string_ptr).str().to_string() };
return Ok(IndexedDBKeyType::String(key));
} }
if input.is_object() { if input.is_object() {
@ -185,11 +187,9 @@ impl IDBObjectStore {
} }
if IsArrayBufferObject(*object) || JS_IsArrayBufferViewObject(*object) { if IsArrayBufferObject(*object) || JS_IsArrayBufferViewObject(*object) {
let key = // FIXME:(arihant2math)
structuredclone::write(cx, input, None).expect("Could not serialize key"); error!("Array buffers as keys is currently unsupported");
// FIXME:(arihant2math) Return the correct type here return Err(Error::NotSupported);
// it doesn't really matter at the moment...
return Ok(IndexedDBKeyType::Number(key.serialized.clone()));
} }
if let ESClass::Array = built_in_class { if let ESClass::Array = built_in_class {

View file

@ -15,19 +15,57 @@ pub enum IndexedDBTxnMode {
} }
// https://www.w3.org/TR/IndexedDB-2/#key-type // https://www.w3.org/TR/IndexedDB-2/#key-type
#[derive(Clone, Debug, Deserialize, Serialize)] // FIXME:(arihant2math) Ordering needs to completely be reimplemented as per https://www.w3.org/TR/IndexedDB-2/#compare-two-keys
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
pub enum IndexedDBKeyType { pub enum IndexedDBKeyType {
Number(Vec<u8>), Number(f64),
String(Vec<u8>), String(String),
Binary(Vec<u8>), Binary(Vec<u8>),
// FIXME:(arihant2math) Date should not be stored as a Vec<u8>
Date(Vec<u8>), Date(Vec<u8>),
// FIXME:(arihant2math) implment Array(), Array(Vec<IndexedDBKeyType>),
// FIXME:(arihant2math) implment ArrayBuffer
} }
// https://www.w3.org/TR/IndexedDB-2/#key-range // <https://www.w3.org/TR/IndexedDB-2/#key-range>
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[allow(unused)] #[allow(unused)]
pub enum IndexedDBKeyRange {} pub struct IndexedDBKeyRange {
pub lower: Option<IndexedDBKeyType>,
pub upper: Option<IndexedDBKeyType>,
pub lower_open: bool,
pub upper_open: bool,
}
impl From<IndexedDBKeyType> for IndexedDBKeyRange {
fn from(key: IndexedDBKeyType) -> Self {
IndexedDBKeyRange {
lower: Some(key.clone()),
upper: Some(key),
..Default::default()
}
}
}
impl IndexedDBKeyRange {
// <https://www.w3.org/TR/IndexedDB-2/#in>
pub fn contains(&self, key: &IndexedDBKeyType) -> bool {
// A key is in a key range if both of the following conditions are fulfilled:
// The lower bound is null, or it is less than key,
// or it is both equal to key and the lower open flag is unset.
// The upper bound is null, or it is greater than key,
// or it is both equal to key and the upper open flag is unset
let lower_bound_condition = self
.lower
.as_ref()
.is_none_or(|lower| lower < key || (!self.lower_open && lower == key));
let upper_bound_condition = self
.upper
.as_ref()
.is_none_or(|upper| key < upper || (!self.upper_open && key == upper));
lower_bound_condition && upper_bound_condition
}
}
// Operations that are not executed instantly, but rather added to a // Operations that are not executed instantly, but rather added to a
// queue that is eventually run. // queue that is eventually run.