Indexeddb: Cache IDBCursor.key conversion to preserve object identity (#44161)

Testing: Covered by idbcursor-key.any.html test.
part of https://github.com/servo/servo/issues/40983

---------

Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com>
This commit is contained in:
Taym Haddadi
2026-04-13 20:14:26 +02:00
committed by GitHub
parent 8918b6f53a
commit a7ef47a46e
2 changed files with 55 additions and 12 deletions

View File

@@ -56,6 +56,10 @@ pub(crate) struct IDBCursor {
/// <https://www.w3.org/TR/IndexedDB-3/#cursor-key>
#[no_trace]
key: DomRefCell<Option<IndexedDBKeyType>>,
#[ignore_malloc_size_of = "mozjs"]
cached_key: DomRefCell<Option<Heap<JSVal>>>,
#[ignore_malloc_size_of = "mozjs"]
cached_primary_key: DomRefCell<Option<Heap<JSVal>>>,
/// <https://www.w3.org/TR/IndexedDB-3/#cursor-value>
#[ignore_malloc_size_of = "mozjs"]
value: Heap<JSVal>,
@@ -89,6 +93,8 @@ impl IDBCursor {
direction,
position: DomRefCell::new(None),
key: DomRefCell::new(None),
cached_key: DomRefCell::new(None),
cached_primary_key: DomRefCell::new(None),
value: Heap::default(),
got_value: Cell::new(got_value),
object_store_position: DomRefCell::new(None),
@@ -124,15 +130,30 @@ impl IDBCursor {
}
fn set_position(&self, position: Option<IndexedDBKeyType>) {
let changed = *self.position.borrow() != position;
*self.position.borrow_mut() = position;
if changed {
*self.cached_primary_key.borrow_mut() = None;
}
}
fn set_key(&self, key: Option<IndexedDBKeyType>) {
let key_changed = {
let current_key = self.key.borrow();
current_key.as_ref() != key.as_ref()
};
*self.key.borrow_mut() = key;
if key_changed {
*self.cached_key.borrow_mut() = None;
}
}
fn set_object_store_position(&self, object_store_position: Option<IndexedDBKeyType>) {
let changed = *self.object_store_position.borrow() != object_store_position;
*self.object_store_position.borrow_mut() = object_store_position;
if changed {
*self.cached_primary_key.borrow_mut() = None;
}
}
pub(crate) fn set_request(&self, request: &IDBRequest) {
@@ -172,18 +193,50 @@ impl IDBCursorMethods<crate::DomTypeHolder> for IDBCursor {
/// <https://www.w3.org/TR/IndexedDB-3/#dom-idbcursor-key>
fn Key(&self, cx: &mut JSContext, mut value: MutableHandleValue) {
// The key getter steps are to return the result of converting a key to a value with the cursors current key.
//
// NOTE: If key returns an object (e.g. a Date or Array), it returns the
// same object instance every time it is inspected, until the cursors key is changed.
// This means that if the object is modified, those modifications will be seen by
// anyone inspecting the value of the cursor. However modifying such an object does not
// modify the contents of the database.
if let Some(cached) = &*self.cached_key.borrow() {
value.set(cached.get());
return;
}
match self.key.borrow().as_ref() {
Some(key) => key_type_to_jsval(cx, key, value),
Some(key) => key_type_to_jsval(cx, key, value.reborrow()),
None => value.set(UndefinedValue()),
}
*self.cached_key.borrow_mut() = Some(Heap::default());
self.cached_key.borrow().as_ref().unwrap().set(value.get());
}
/// <https://www.w3.org/TR/IndexedDB-3/#dom-idbcursor-primarykey>
fn PrimaryKey(&self, cx: &mut JSContext, mut value: MutableHandleValue) {
// NOTE: If primaryKey returns an object (e.g. a Date or Array),
// it returns the same object instance every time it is inspected,
// until the cursors effective key is changed. This means that if the object is modified,
// those modifications will be seen by anyone inspecting the value of the cursor.
// However modifying such an object does not modify the contents of the database.
if let Some(cached) = &*self.cached_primary_key.borrow() {
value.set(cached.get());
return;
}
match self.effective_key() {
Some(effective_key) => key_type_to_jsval(cx, &effective_key, value),
Some(effective_key) => key_type_to_jsval(cx, &effective_key, value.reborrow()),
None => value.set(UndefinedValue()),
}
*self.cached_primary_key.borrow_mut() = Some(Heap::default());
self.cached_primary_key
.borrow()
.as_ref()
.unwrap()
.set(value.get());
}
/// <https://w3c.github.io/IndexedDB/#dom-idbcursor-request>

View File

@@ -1,15 +1,5 @@
[idbcursor-key.any.sharedworker.html]
expected: ERROR
[idbcursor-key.any.worker.html]
[IDBCursor.key 2]
expected: FAIL
[idbcursor-key.any.html]
[IDBCursor.key 2]
expected: FAIL
[idbcursor-key.any.serviceworker.html]
expected: ERROR