script/dom/
domexception.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use base::id::{DomExceptionId, DomExceptionIndex};
6use constellation_traits::DomException;
7use dom_struct::dom_struct;
8use js::rust::HandleObject;
9use rustc_hash::FxHashMap;
10
11use crate::dom::bindings::codegen::Bindings::DOMExceptionBinding::{
12    DOMExceptionConstants, DOMExceptionMethods,
13};
14use crate::dom::bindings::error::Error;
15use crate::dom::bindings::reflector::{
16    Reflector, reflect_dom_object, reflect_dom_object_with_proto,
17};
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::bindings::serializable::Serializable;
20use crate::dom::bindings::str::DOMString;
21use crate::dom::bindings::structuredclone::StructuredData;
22use crate::dom::globalscope::GlobalScope;
23use crate::script_runtime::CanGc;
24
25#[repr(u16)]
26#[allow(clippy::enum_variant_names)]
27#[derive(Clone, Copy, Debug, Eq, JSTraceable, MallocSizeOf, Ord, PartialEq, PartialOrd)]
28pub(crate) enum DOMErrorName {
29    IndexSizeError = DOMExceptionConstants::INDEX_SIZE_ERR,
30    HierarchyRequestError = DOMExceptionConstants::HIERARCHY_REQUEST_ERR,
31    WrongDocumentError = DOMExceptionConstants::WRONG_DOCUMENT_ERR,
32    InvalidCharacterError = DOMExceptionConstants::INVALID_CHARACTER_ERR,
33    NoModificationAllowedError = DOMExceptionConstants::NO_MODIFICATION_ALLOWED_ERR,
34    NotFoundError = DOMExceptionConstants::NOT_FOUND_ERR,
35    NotSupportedError = DOMExceptionConstants::NOT_SUPPORTED_ERR,
36    InUseAttributeError = DOMExceptionConstants::INUSE_ATTRIBUTE_ERR,
37    InvalidStateError = DOMExceptionConstants::INVALID_STATE_ERR,
38    SyntaxError = DOMExceptionConstants::SYNTAX_ERR,
39    InvalidModificationError = DOMExceptionConstants::INVALID_MODIFICATION_ERR,
40    NamespaceError = DOMExceptionConstants::NAMESPACE_ERR,
41    InvalidAccessError = DOMExceptionConstants::INVALID_ACCESS_ERR,
42    SecurityError = DOMExceptionConstants::SECURITY_ERR,
43    NetworkError = DOMExceptionConstants::NETWORK_ERR,
44    AbortError = DOMExceptionConstants::ABORT_ERR,
45    TypeMismatchError = DOMExceptionConstants::TYPE_MISMATCH_ERR,
46    URLMismatchError = DOMExceptionConstants::URL_MISMATCH_ERR,
47    QuotaExceededError = DOMExceptionConstants::QUOTA_EXCEEDED_ERR,
48    TimeoutError = DOMExceptionConstants::TIMEOUT_ERR,
49    InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR,
50    DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR,
51    DataError,
52    TransactionInactiveError,
53    ReadOnlyError,
54    VersionError,
55    EncodingError,
56    NotReadableError,
57    OperationError,
58    NotAllowedError,
59    ConstraintError,
60}
61
62impl DOMErrorName {
63    pub(crate) fn from(s: &DOMString) -> Option<DOMErrorName> {
64        match s.as_ref() {
65            "IndexSizeError" => Some(DOMErrorName::IndexSizeError),
66            "HierarchyRequestError" => Some(DOMErrorName::HierarchyRequestError),
67            "WrongDocumentError" => Some(DOMErrorName::WrongDocumentError),
68            "InvalidCharacterError" => Some(DOMErrorName::InvalidCharacterError),
69            "NoModificationAllowedError" => Some(DOMErrorName::NoModificationAllowedError),
70            "NotFoundError" => Some(DOMErrorName::NotFoundError),
71            "NotSupportedError" => Some(DOMErrorName::NotSupportedError),
72            "InUseAttributeError" => Some(DOMErrorName::InUseAttributeError),
73            "InvalidStateError" => Some(DOMErrorName::InvalidStateError),
74            "SyntaxError" => Some(DOMErrorName::SyntaxError),
75            "InvalidModificationError" => Some(DOMErrorName::InvalidModificationError),
76            "NamespaceError" => Some(DOMErrorName::NamespaceError),
77            "InvalidAccessError" => Some(DOMErrorName::InvalidAccessError),
78            "SecurityError" => Some(DOMErrorName::SecurityError),
79            "NetworkError" => Some(DOMErrorName::NetworkError),
80            "AbortError" => Some(DOMErrorName::AbortError),
81            "TypeMismatchError" => Some(DOMErrorName::TypeMismatchError),
82            "URLMismatchError" => Some(DOMErrorName::URLMismatchError),
83            "QuotaExceededError" => Some(DOMErrorName::QuotaExceededError),
84            "TimeoutError" => Some(DOMErrorName::TimeoutError),
85            "InvalidNodeTypeError" => Some(DOMErrorName::InvalidNodeTypeError),
86            "DataCloneError" => Some(DOMErrorName::DataCloneError),
87            "DataError" => Some(DOMErrorName::DataError),
88            "TransactionInactiveError" => Some(DOMErrorName::TransactionInactiveError),
89            "ReadOnlyError" => Some(DOMErrorName::ReadOnlyError),
90            "VersionError" => Some(DOMErrorName::VersionError),
91            "EncodingError" => Some(DOMErrorName::EncodingError),
92            "NotReadableError" => Some(DOMErrorName::NotReadableError),
93            "OperationError" => Some(DOMErrorName::OperationError),
94            "NotAllowedError" => Some(DOMErrorName::NotAllowedError),
95            "ConstraintError" => Some(DOMErrorName::ConstraintError),
96            _ => None,
97        }
98    }
99}
100
101#[dom_struct]
102pub(crate) struct DOMException {
103    reflector_: Reflector,
104    message: DOMString,
105    name: DOMString,
106}
107
108impl DOMException {
109    fn get_error_data_by_code(code: DOMErrorName) -> (DOMString, DOMString) {
110        let message = match &code {
111            DOMErrorName::IndexSizeError => "The index is not in the allowed range.",
112            DOMErrorName::HierarchyRequestError => {
113                "The operation would yield an incorrect node tree."
114            },
115            DOMErrorName::WrongDocumentError => "The object is in the wrong document.",
116            DOMErrorName::InvalidCharacterError => "The string contains invalid characters.",
117            DOMErrorName::NoModificationAllowedError => "The object can not be modified.",
118            DOMErrorName::NotFoundError => "The object can not be found here.",
119            DOMErrorName::NotSupportedError => "The operation is not supported.",
120            DOMErrorName::InUseAttributeError => "The attribute already in use.",
121            DOMErrorName::InvalidStateError => "The object is in an invalid state.",
122            DOMErrorName::SyntaxError => "The string did not match the expected pattern.",
123            DOMErrorName::InvalidModificationError => "The object can not be modified in this way.",
124            DOMErrorName::NamespaceError => "The operation is not allowed by Namespaces in XML.",
125            DOMErrorName::InvalidAccessError => {
126                "The object does not support the operation or argument."
127            },
128            DOMErrorName::SecurityError => "The operation is insecure.",
129            DOMErrorName::NetworkError => "A network error occurred.",
130            DOMErrorName::AbortError => "The operation was aborted.",
131            DOMErrorName::TypeMismatchError => "The given type does not match any expected type.",
132            DOMErrorName::URLMismatchError => "The given URL does not match another URL.",
133            DOMErrorName::QuotaExceededError => "The quota has been exceeded.",
134            DOMErrorName::TimeoutError => "The operation timed out.",
135            DOMErrorName::InvalidNodeTypeError => {
136                "The supplied node is incorrect or has an incorrect ancestor for this operation."
137            },
138            DOMErrorName::DataCloneError => "The object can not be cloned.",
139            DOMErrorName::DataError => "Provided data is inadequate.",
140            DOMErrorName::TransactionInactiveError => {
141                "A request was placed against a transaction which is currently not active, or which is finished."
142            },
143            DOMErrorName::ReadOnlyError => {
144                "The mutating operation was attempted in a \"readonly\" transaction."
145            },
146            DOMErrorName::VersionError => {
147                "An attempt was made to open a database using a lower version than the existing version."
148            },
149            DOMErrorName::EncodingError => {
150                "The encoding operation (either encoded or decoding) failed."
151            },
152            DOMErrorName::NotReadableError => "The I/O read operation failed.",
153            DOMErrorName::OperationError => {
154                "The operation failed for an operation-specific reason."
155            },
156            DOMErrorName::NotAllowedError => {
157                r#"The request is not allowed by the user agent or the platform in the current context,
158                possibly because the user denied permission."#
159            },
160            DOMErrorName::ConstraintError => {
161                "A mutation operation in a transaction failed because a constraint was not satisfied."
162            },
163        };
164
165        (
166            DOMString::from(message),
167            DOMString::from(format!("{:?}", code)),
168        )
169    }
170
171    pub(crate) fn new_inherited(message: DOMString, name: DOMString) -> DOMException {
172        DOMException {
173            reflector_: Reflector::new(),
174            message,
175            name,
176        }
177    }
178
179    pub(crate) fn new(
180        global: &GlobalScope,
181        code: DOMErrorName,
182        can_gc: CanGc,
183    ) -> DomRoot<DOMException> {
184        let (message, name) = DOMException::get_error_data_by_code(code);
185
186        reflect_dom_object(
187            Box::new(DOMException::new_inherited(message, name)),
188            global,
189            can_gc,
190        )
191    }
192
193    pub(crate) fn new_with_custom_message(
194        global: &GlobalScope,
195        code: DOMErrorName,
196        message: String,
197        can_gc: CanGc,
198    ) -> DomRoot<DOMException> {
199        let (_, name) = DOMException::get_error_data_by_code(code);
200
201        reflect_dom_object(
202            Box::new(DOMException::new_inherited(DOMString::from(message), name)),
203            global,
204            can_gc,
205        )
206    }
207
208    // not an IDL stringifier, used internally
209    pub(crate) fn stringifier(&self) -> DOMString {
210        DOMString::from(format!("{}: {}", self.name, self.message))
211    }
212}
213
214impl DOMExceptionMethods<crate::DomTypeHolder> for DOMException {
215    // https://webidl.spec.whatwg.org/#dom-domexception-domexception
216    fn Constructor(
217        global: &GlobalScope,
218        proto: Option<HandleObject>,
219        can_gc: CanGc,
220        message: DOMString,
221        name: DOMString,
222    ) -> Result<DomRoot<DOMException>, Error> {
223        Ok(reflect_dom_object_with_proto(
224            Box::new(DOMException::new_inherited(message, name)),
225            global,
226            proto,
227            can_gc,
228        ))
229    }
230
231    // https://webidl.spec.whatwg.org/#dom-domexception-code
232    fn Code(&self) -> u16 {
233        match DOMErrorName::from(&self.name) {
234            Some(code) if code <= DOMErrorName::DataCloneError => code as u16,
235            _ => 0,
236        }
237    }
238
239    // https://webidl.spec.whatwg.org/#dom-domexception-name
240    fn Name(&self) -> DOMString {
241        self.name.clone()
242    }
243
244    // https://webidl.spec.whatwg.org/#dom-domexception-message
245    fn Message(&self) -> DOMString {
246        self.message.clone()
247    }
248}
249
250impl Serializable for DOMException {
251    type Index = DomExceptionIndex;
252    type Data = DomException;
253
254    // https://webidl.spec.whatwg.org/#idl-DOMException
255    fn serialize(&self) -> Result<(DomExceptionId, Self::Data), ()> {
256        let serialized = DomException {
257            message: self.message.to_string(),
258            name: self.name.to_string(),
259        };
260        Ok((DomExceptionId::new(), serialized))
261    }
262
263    // https://webidl.spec.whatwg.org/#idl-DOMException
264    fn deserialize(
265        owner: &GlobalScope,
266        serialized: Self::Data,
267        can_gc: CanGc,
268    ) -> Result<DomRoot<Self>, ()>
269    where
270        Self: Sized,
271    {
272        Ok(Self::new_with_custom_message(
273            owner,
274            DOMErrorName::from(&DOMString::from_string(serialized.name)).ok_or(())?,
275            serialized.message,
276            can_gc,
277        ))
278    }
279
280    fn serialized_storage<'a>(
281        data: StructuredData<'a, '_>,
282    ) -> &'a mut Option<FxHashMap<DomExceptionId, Self::Data>> {
283        match data {
284            StructuredData::Reader(reader) => &mut reader.exceptions,
285            StructuredData::Writer(writer) => &mut writer.exceptions,
286        }
287    }
288}