script: Implement QuotaExceededError WebIDL interface (#38507)

Implements the new WebIDL interface for QuotaExceededError and uses it
in appropriate places.

Testing: WPT tests. Now passing many more in
`tests/wpt/tests/WebCryptoAPI/getRandomValues.any.js` and
`tests/wpt/tests/webstorage/storage_session_setitem_quotaexceedederr.window.js`.
Fixes: #38489

---------

Signed-off-by: Rahul Menon <menonrahul02@gmail.com>
This commit is contained in:
Rahul Menon 2025-08-14 12:58:50 -05:00 committed by GitHub
parent fad247c802
commit b5932e5abf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 173 additions and 67 deletions

View file

@ -18,6 +18,7 @@ use js::rust::{HandleObject, HandleValue, MutableHandleValue};
use libc::c_uint;
use script_bindings::conversions::SafeToJSValConvertible;
pub(crate) use script_bindings::error::*;
use script_bindings::str::DOMString;
#[cfg(feature = "js_backtrace")]
use crate::dom::bindings::cell::DomRefCell;
@ -27,6 +28,7 @@ use crate::dom::bindings::conversions::{
use crate::dom::bindings::str::USVString;
use crate::dom::domexception::{DOMErrorName, DOMException};
use crate::dom::globalscope::GlobalScope;
use crate::dom::types::QuotaExceededError;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
@ -92,7 +94,15 @@ pub(crate) fn throw_dom_exception(
Error::ReadOnly => DOMErrorName::ReadOnlyError,
Error::Version => DOMErrorName::VersionError,
Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
Error::QuotaExceeded { quota, requested } => unsafe {
assert!(!JS_IsExceptionPending(*cx));
let exception =
QuotaExceededError::new(global, DOMString::new(), quota, requested, can_gc);
rooted!(in(*cx) let mut thrown = UndefinedValue());
exception.safe_to_jsval(cx, thrown.handle_mut());
JS_SetPendingException(*cx, thrown.handle(), ExceptionStackBehavior::Capture);
return;
},
Error::TypeMismatch => DOMErrorName::TypeMismatchError,
Error::InvalidModification => DOMErrorName::InvalidModificationError,
Error::NotReadable => DOMErrorName::NotReadableError,

View file

@ -63,7 +63,10 @@ impl CryptoMethods<crate::DomTypeHolder> for Crypto {
} else {
let data = unsafe { input.as_mut_slice() };
if data.len() > 65536 {
return Err(Error::QuotaExceeded);
return Err(Error::QuotaExceeded {
quota: None,
requested: None,
});
}
self.rng.borrow_mut().fill_bytes(data);
let underlying_object = unsafe { input.underlying_object() };

View file

@ -263,7 +263,10 @@ impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
// Step 2
if let Err(_result) = result {
// FIXME:(rasviitanen) also support Unknown error
return Err(Error::QuotaExceeded);
return Err(Error::QuotaExceeded {
quota: None,
requested: None,
});
}
// Step 3

View file

@ -513,6 +513,7 @@ pub(crate) mod progressevent;
pub(crate) mod promise;
pub(crate) mod promisenativehandler;
pub(crate) mod promiserejectionevent;
pub(crate) mod quotaexceedederror;
pub(crate) mod radionodelist;
pub(crate) mod range;
pub(crate) mod raredata;

View file

@ -0,0 +1,118 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use js::gc::HandleObject;
use script_bindings::codegen::GenericBindings::QuotaExceededErrorBinding::{
QuotaExceededErrorMethods, QuotaExceededErrorOptions,
};
use script_bindings::num::Finite;
use script_bindings::root::DomRoot;
use script_bindings::script_runtime::CanGc;
use script_bindings::str::DOMString;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::reflector::{reflect_dom_object, reflect_dom_object_with_proto};
use crate::dom::types::{DOMException, GlobalScope};
/// <https://webidl.spec.whatwg.org/#quotaexceedederror>
#[dom_struct]
pub(crate) struct QuotaExceededError {
/// <https://webidl.spec.whatwg.org/#idl-DOMException>
dom_exception: DOMException,
/// <https://webidl.spec.whatwg.org/#dom-quotaexceedederror-quota>
quota: Option<Finite<f64>>,
/// <https://webidl.spec.whatwg.org/#dom-quotaexceedederror-requested>
requested: Option<Finite<f64>>,
}
impl QuotaExceededError {
fn new_inherited(
message: DOMString,
quota: Option<Finite<f64>>,
requested: Option<Finite<f64>>,
) -> Self {
Self {
dom_exception: DOMException::new_inherited(
message,
DOMString::from_string("QuotaExceededError".to_string()),
),
quota,
requested,
}
}
pub(crate) fn new(
global: &GlobalScope,
message: DOMString,
quota: Option<Finite<f64>>,
requested: Option<Finite<f64>>,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(Self::new_inherited(message, quota, requested)),
global,
can_gc,
)
}
}
impl QuotaExceededErrorMethods<crate::DomTypeHolder> for QuotaExceededError {
/// <https://webidl.spec.whatwg.org/#dom-quotaexceedederror-quotaexceedederror>
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
message: DOMString,
options: &QuotaExceededErrorOptions,
) -> Result<DomRoot<Self>, Error> {
// If options["quota"] is present:
if let Some(quota) = options.quota {
// If options["quota"] is less than 0, then throw a RangeError.
if *quota < 0.0 {
return Err(Error::Range(
"quota must be at least zero if present".to_string(),
));
}
}
// If options["requested"] is present:
if let Some(requested) = options.requested {
// If options["requested"] is less than 0, then throw a RangeError.
if *requested < 0.0 {
return Err(Error::Range(
"requested must be at least zero if present".to_string(),
));
}
}
// If thiss quota is not null, thiss requested is not null, and thiss requested
// is less than thiss quota, then throw a RangeError.
if let (Some(quota), Some(requested)) = (options.quota, options.requested) {
if *requested < *quota {
return Err(Error::Range("requested is less than quota".to_string()));
}
}
Ok(reflect_dom_object_with_proto(
Box::new(QuotaExceededError::new_inherited(
message,
options.quota,
options.requested,
)),
global,
proto,
can_gc,
))
}
/// <https://webidl.spec.whatwg.org/#dom-quotaexceedederror-quota>
fn GetQuota(&self) -> Option<Finite<f64>> {
// The quota getter steps are to return thiss quota.
self.quota
}
/// <https://webidl.spec.whatwg.org/#dom-quotaexceedederror-requested>
fn GetRequested(&self) -> Option<Finite<f64>> {
// The requested getter steps are to return thiss requested.
self.requested
}
}

View file

@ -127,7 +127,10 @@ impl StorageMethods<crate::DomTypeHolder> for Storage {
);
self.get_storage_thread().send(msg).unwrap();
match receiver.recv().unwrap() {
Err(_) => Err(Error::QuotaExceeded),
Err(_) => Err(Error::QuotaExceeded {
quota: None,
requested: None,
}),
Ok((changed, old_value)) => {
if changed {
self.broadcast_change_notification(Some(name), old_value, Some(value));