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