From 91b27d98a2b484068fe61271bfc58b27b67e4097 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Thu, 28 Aug 2025 00:49:17 -0700 Subject: [PATCH] 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 Co-authored-by: Josh Matthews --- components/script/dom/bindings/error.rs | 4 +- components/script/dom/idbopendbrequest.rs | 4 +- components/script/dom/idbrequest.rs | 59 +++++++++++++------ .../IndexedDB/error-attributes.any.js.ini | 6 +- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index 2962562fbc6..2fea095a024 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -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, diff --git a/components/script/dom/idbopendbrequest.rs b/components/script/dom/idbopendbrequest.rs index e1c22ab8e9b..5373546f86c 100644 --- a/components/script/dom/idbopendbrequest.rs +++ b/components/script/dom/idbopendbrequest.rs @@ -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, 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"), diff --git a/components/script/dom/idbrequest.rs b/components/script/dom/idbrequest.rs index f5b74d35a25..42e75151b64 100644 --- a/components/script/dom/idbrequest.rs +++ b/components/script/dom/idbrequest.rs @@ -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 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) { 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, 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, + 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::() @@ -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, 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); } } diff --git a/tests/wpt/meta/IndexedDB/error-attributes.any.js.ini b/tests/wpt/meta/IndexedDB/error-attributes.any.js.ini index b77e1132690..17c04fc4ce3 100644 --- a/tests/wpt/meta/IndexedDB/error-attributes.any.js.ini +++ b/tests/wpt/meta/IndexedDB/error-attributes.any.js.ini @@ -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]