diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index 0e7601817a9..c2129c92093 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -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, diff --git a/components/script/dom/crypto.rs b/components/script/dom/crypto.rs index 0550d5920d5..e9f68b3c62d 100644 --- a/components/script/dom/crypto.rs +++ b/components/script/dom/crypto.rs @@ -63,7 +63,10 @@ impl CryptoMethods 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() }; diff --git a/components/script/dom/idbtransaction.rs b/components/script/dom/idbtransaction.rs index deea1e5a5e0..0346b285d74 100644 --- a/components/script/dom/idbtransaction.rs +++ b/components/script/dom/idbtransaction.rs @@ -263,7 +263,10 @@ impl IDBTransactionMethods 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 diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index c4fc8b8f5cf..bc72e651793 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -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; diff --git a/components/script/dom/quotaexceedederror.rs b/components/script/dom/quotaexceedederror.rs new file mode 100644 index 00000000000..0e573cd5bed --- /dev/null +++ b/components/script/dom/quotaexceedederror.rs @@ -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}; + +/// +#[dom_struct] +pub(crate) struct QuotaExceededError { + /// + dom_exception: DOMException, + /// + quota: Option>, + /// + requested: Option>, +} + +impl QuotaExceededError { + fn new_inherited( + message: DOMString, + quota: Option>, + requested: Option>, + ) -> 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>, + requested: Option>, + can_gc: CanGc, + ) -> DomRoot { + reflect_dom_object( + Box::new(Self::new_inherited(message, quota, requested)), + global, + can_gc, + ) + } +} + +impl QuotaExceededErrorMethods for QuotaExceededError { + /// + fn Constructor( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + message: DOMString, + options: &QuotaExceededErrorOptions, + ) -> Result, 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 this’s quota is not null, this’s requested is not null, and this’s requested + // is less than this’s 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, + )) + } + + /// + fn GetQuota(&self) -> Option> { + // The quota getter steps are to return this’s quota. + self.quota + } + + /// + fn GetRequested(&self) -> Option> { + // The requested getter steps are to return this’s requested. + self.requested + } +} diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs index 126394bb351..f0ee34882c1 100644 --- a/components/script/dom/storage.rs +++ b/components/script/dom/storage.rs @@ -127,7 +127,10 @@ impl StorageMethods 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)); diff --git a/components/script_bindings/error.rs b/components/script_bindings/error.rs index ac8b6c3123b..ec6d2d3e28d 100644 --- a/components/script_bindings/error.rs +++ b/components/script_bindings/error.rs @@ -6,6 +6,7 @@ use js::error::throw_type_error; use js::jsapi::JS_IsExceptionPending; use crate::codegen::PrototypeList::proto_id_to_name; +use crate::num::Finite; use crate::script_runtime::JSContext as SafeJSContext; /// DOM exceptions that can be thrown by a native DOM method. @@ -55,7 +56,10 @@ pub enum Error { /// NoModificationAllowedError DOMException NoModificationAllowed, /// QuotaExceededError DOMException - QuotaExceeded, + QuotaExceeded { + quota: Option>, + requested: Option>, + }, /// TypeMismatchError DOMException TypeMismatch, /// InvalidModificationError DOMException diff --git a/components/script_bindings/num.rs b/components/script_bindings/num.rs index 58b12f6cb7f..8d87deef9f6 100644 --- a/components/script_bindings/num.rs +++ b/components/script_bindings/num.rs @@ -11,7 +11,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use num_traits::Float; /// Encapsulates the IDL restricted float type. -#[derive(Clone, Copy, Eq, JSTraceable, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, JSTraceable, PartialEq)] pub struct Finite(T); impl Finite { diff --git a/components/script_bindings/webidls/QuotaExceededError.webidl b/components/script_bindings/webidls/QuotaExceededError.webidl new file mode 100644 index 00000000000..73c8fdcc953 --- /dev/null +++ b/components/script_bindings/webidls/QuotaExceededError.webidl @@ -0,0 +1,21 @@ +/* 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/. */ + +// https://heycam.github.io/webidl/#quotaexceedederror + +[ + Exposed=(Window,Worker,Worklet,DissimilarOriginWindow), + // Serializable +] +interface QuotaExceededError : DOMException { + [Throws] constructor(optional DOMString message = "", optional QuotaExceededErrorOptions options = {}); + + readonly attribute double? quota; + readonly attribute double? requested; +}; + +dictionary QuotaExceededErrorOptions { + double quota; + double requested; +}; diff --git a/tests/wpt/meta/WebCryptoAPI/getRandomValues.any.js.ini b/tests/wpt/meta/WebCryptoAPI/getRandomValues.any.js.ini deleted file mode 100644 index 23dba632f65..00000000000 --- a/tests/wpt/meta/WebCryptoAPI/getRandomValues.any.js.ini +++ /dev/null @@ -1,56 +0,0 @@ -[getRandomValues.any.worker.html] - [Large length: Int8Array] - expected: FAIL - - [Large length: Int16Array] - expected: FAIL - - [Large length: Int32Array] - expected: FAIL - - [Large length: BigInt64Array] - expected: FAIL - - [Large length: Uint8Array] - expected: FAIL - - [Large length: Uint8ClampedArray] - expected: FAIL - - [Large length: Uint16Array] - expected: FAIL - - [Large length: Uint32Array] - expected: FAIL - - [Large length: BigUint64Array] - expected: FAIL - - -[getRandomValues.any.html] - [Large length: Int8Array] - expected: FAIL - - [Large length: Int16Array] - expected: FAIL - - [Large length: Int32Array] - expected: FAIL - - [Large length: BigInt64Array] - expected: FAIL - - [Large length: Uint8Array] - expected: FAIL - - [Large length: Uint8ClampedArray] - expected: FAIL - - [Large length: Uint16Array] - expected: FAIL - - [Large length: Uint32Array] - expected: FAIL - - [Large length: BigUint64Array] - expected: FAIL diff --git a/tests/wpt/meta/webstorage/storage_session_setitem_quotaexceedederr.window.js.ini b/tests/wpt/meta/webstorage/storage_session_setitem_quotaexceedederr.window.js.ini deleted file mode 100644 index e7744a93f19..00000000000 --- a/tests/wpt/meta/webstorage/storage_session_setitem_quotaexceedederr.window.js.ini +++ /dev/null @@ -1,3 +0,0 @@ -[storage_session_setitem_quotaexceedederr.window.html] - [Throws QuotaExceededError when the quota has been exceeded] - expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 4b8d26cdf32..b130875c753 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13725,14 +13725,14 @@ ] ], "interfaces.https.html": [ - "05ead2c3e2c362586506be77c8093c8bc4f0fb53", + "cbdb5bef0bab4f54514913517c214631a71f7d3a", [ null, {} ] ], "interfaces.worker.js": [ - "a02605ff8e63db701c77d734037fcb33d76418d0", + "1955d6f821d8445c813ac09e098b2c66f0e07d26", [ "mozilla/interfaces.worker.html", {} diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 05ead2c3e2c..cbdb5bef0ba 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -288,6 +288,7 @@ test_interfaces([ "ProcessingInstruction", "ProgressEvent", "PromiseRejectionEvent", + "QuotaExceededError", "RadioNodeList", "Range", "ReadableStreamDefaultReader", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index a02605ff8e6..1955d6f821d 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -98,6 +98,7 @@ test_interfaces([ "Permissions", "ProgressEvent", "PromiseRejectionEvent", + "QuotaExceededError", "ReadableStream", "ReadableStreamDefaultReader", "ReadableStreamDefaultController",