script: correctly handle indexeddb backend errors (#38740)

Sets the indexeddb request error when the backend errors out. This also
matches statements to the spec.

Testing: Covered by WPT
Fixes: General indexeddb

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Ashwin Naren 2025-08-28 00:49:17 -07:00 committed by GitHub
parent 57f25d0a8a
commit 91b27d98a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 24 deletions

View file

@ -41,7 +41,7 @@ thread_local! {
}
/// Error values that have no equivalent DOMException representation.
enum JsEngineError {
pub(crate) enum JsEngineError {
/// An EMCAScript TypeError.
Type(String),
/// An ECMAScript RangeError.
@ -94,7 +94,7 @@ pub(crate) fn throw_dom_exception(
/// If possible, create a new DOMException representing the provided error.
/// If no such DOMException exists, return a subset of the original error values
/// that may need additional handling.
fn create_dom_exception(
pub(crate) fn create_dom_exception(
global: &GlobalScope,
result: Error,
can_gc: CanGc,

View file

@ -220,7 +220,7 @@ impl IDBOpenDBRequest {
self.idbrequest.set_result(result);
}
pub fn set_error(&self, error: Error, can_gc: CanGc) {
pub fn set_error(&self, error: Option<Error>, can_gc: CanGc) {
self.idbrequest.set_error(error, can_gc);
}
@ -270,7 +270,7 @@ impl IDBOpenDBRequest {
},
Err(dom_exception) => {
request.set_result(HandleValue::undefined());
request.set_error(dom_exception, CanGc::note());
request.set_error(Some(dom_exception), CanGc::note());
let event = Event::new(
&global,
Atom::from("error"),

View file

@ -23,13 +23,13 @@ use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{
IDBRequestMethods, IDBRequestReadyState,
};
use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::error::{Error, Fallible, create_dom_exception};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::structuredclone;
use crate::dom::domexception::{DOMErrorName, DOMException};
use crate::dom::domexception::DOMException;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@ -98,11 +98,14 @@ impl From<u64> for IdbResult {
}
impl RequestListener {
// https://www.w3.org/TR/IndexedDB-2/#async-execute-request
// Implements Step 5.4
fn handle_async_request_finished(&self, result: BackendResult<IdbResult>) {
let request = self.request.root();
let global = request.global();
let cx = GlobalScope::get_cx();
// Substep 1: Set the result of request to result.
request.set_ready_state_done();
let _ac = enter_realm(&*request);
@ -129,15 +132,21 @@ impl RequestListener {
IdbResult::None => {
// no-op
},
IdbResult::Error(_err) => {
request.set_result(answer.handle());
Self::handle_async_request_error(request, &global);
IdbResult::Error(error) => {
// Substep 2
Self::handle_async_request_error(&global, cx, request, error);
return;
},
}
// Substep 3.1: Set the result of request to answer.
request.set_result(answer.handle());
// Substep 3.2: Set the error of request to undefined
request.set_error(None, CanGc::note());
// Substep 3.3: Fire a success event at request.
// TODO: follow spec here
let transaction = request
.transaction
.get()
@ -157,15 +166,29 @@ impl RequestListener {
.fire(request.upcast(), CanGc::note());
transaction.set_active_flag(false);
} else {
request.set_result(answer.handle());
Self::handle_async_request_error(request, &global);
// FIXME:(arihant2math) dispatch correct error
// Substep 2
Self::handle_async_request_error(&global, cx, request, Error::Data);
}
}
fn handle_async_request_error(request: DomRoot<IDBRequest>, global: &GlobalScope) {
// FIXME:(rasviitanen)
// Set the error of request to result
// https://www.w3.org/TR/IndexedDB-2/#async-execute-request
// Implements Step 5.4.2
fn handle_async_request_error(
global: &GlobalScope,
cx: SafeJSContext,
request: DomRoot<IDBRequest>,
error: Error,
) {
// Substep 1: Set the result of request to undefined.
rooted!(in(*cx) let undefined = UndefinedValue());
request.set_result(undefined.handle());
// Substep 2: Set the error of request to result.
request.set_error(Some(error), CanGc::note());
// Substep 3: Fire an error event at request.
// TODO: follow the spec here
let transaction = request
.transaction
.get()
@ -179,6 +202,7 @@ impl RequestListener {
CanGc::note(),
);
// TODO: why does the transaction need to be active?
transaction.set_active_flag(true);
event
.upcast::<Event>()
@ -227,14 +251,13 @@ impl IDBRequest {
self.result.set(result.get());
}
pub fn set_error(&self, error: Error, can_gc: CanGc) {
// FIXME:(rasviitanen) Support all error types
if let Error::Version = error {
self.error.set(Some(&DOMException::new(
&self.global(),
DOMErrorName::VersionError,
can_gc,
)));
pub fn set_error(&self, error: Option<Error>, can_gc: CanGc) {
if let Some(error) = error {
if let Ok(exception) = create_dom_exception(&self.global(), error, can_gc) {
self.error.set(Some(&exception));
}
} else {
self.error.set(None);
}
}

View file

@ -1,11 +1,13 @@
[error-attributes.any.worker.html]
expected: TIMEOUT
[IDBRequest and IDBTransaction error properties should be DOMExceptions]
expected: FAIL
expected: TIMEOUT
[error-attributes.any.html]
expected: TIMEOUT
[IDBRequest and IDBTransaction error properties should be DOMExceptions]
expected: FAIL
expected: TIMEOUT
[error-attributes.any.sharedworker.html]