/src/mozilla-central/dom/base/DOMException.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "mozilla/dom/DOMException.h" |
8 | | |
9 | | #include "mozilla/ArrayUtils.h" |
10 | | #include "mozilla/HoldDropJSObjects.h" |
11 | | #include "mozilla/dom/Exceptions.h" |
12 | | #include "nsContentUtils.h" |
13 | | #include "nsCOMPtr.h" |
14 | | #include "nsIDocument.h" |
15 | | #include "nsIException.h" |
16 | | #include "nsMemory.h" |
17 | | #include "xpcprivate.h" |
18 | | |
19 | | #include "mozilla/dom/DOMExceptionBinding.h" |
20 | | #include "mozilla/ErrorResult.h" |
21 | | |
22 | | using namespace mozilla; |
23 | | using namespace mozilla::dom; |
24 | | |
25 | | enum DOM4ErrorTypeCodeMap { |
26 | | /* DOM4 errors from http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */ |
27 | | IndexSizeError = DOMException_Binding::INDEX_SIZE_ERR, |
28 | | HierarchyRequestError = DOMException_Binding::HIERARCHY_REQUEST_ERR, |
29 | | WrongDocumentError = DOMException_Binding::WRONG_DOCUMENT_ERR, |
30 | | InvalidCharacterError = DOMException_Binding::INVALID_CHARACTER_ERR, |
31 | | NoModificationAllowedError = DOMException_Binding::NO_MODIFICATION_ALLOWED_ERR, |
32 | | NotFoundError = DOMException_Binding::NOT_FOUND_ERR, |
33 | | NotSupportedError = DOMException_Binding::NOT_SUPPORTED_ERR, |
34 | | // Can't remove until setNamedItem is removed |
35 | | InUseAttributeError = DOMException_Binding::INUSE_ATTRIBUTE_ERR, |
36 | | InvalidStateError = DOMException_Binding::INVALID_STATE_ERR, |
37 | | SyntaxError = DOMException_Binding::SYNTAX_ERR, |
38 | | InvalidModificationError = DOMException_Binding::INVALID_MODIFICATION_ERR, |
39 | | NamespaceError = DOMException_Binding::NAMESPACE_ERR, |
40 | | InvalidAccessError = DOMException_Binding::INVALID_ACCESS_ERR, |
41 | | TypeMismatchError = DOMException_Binding::TYPE_MISMATCH_ERR, |
42 | | SecurityError = DOMException_Binding::SECURITY_ERR, |
43 | | NetworkError = DOMException_Binding::NETWORK_ERR, |
44 | | AbortError = DOMException_Binding::ABORT_ERR, |
45 | | URLMismatchError = DOMException_Binding::URL_MISMATCH_ERR, |
46 | | QuotaExceededError = DOMException_Binding::QUOTA_EXCEEDED_ERR, |
47 | | TimeoutError = DOMException_Binding::TIMEOUT_ERR, |
48 | | InvalidNodeTypeError = DOMException_Binding::INVALID_NODE_TYPE_ERR, |
49 | | DataCloneError = DOMException_Binding::DATA_CLONE_ERR, |
50 | | InvalidPointerId = 0, |
51 | | EncodingError = 0, |
52 | | |
53 | | /* XXX Should be JavaScript native errors */ |
54 | | TypeError = 0, |
55 | | RangeError = 0, |
56 | | |
57 | | /* IndexedDB errors http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#exceptions */ |
58 | | UnknownError = 0, |
59 | | ConstraintError = 0, |
60 | | DataError = 0, |
61 | | TransactionInactiveError = 0, |
62 | | ReadOnlyError = 0, |
63 | | VersionError = 0, |
64 | | |
65 | | /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */ |
66 | | NotReadableError = 0, |
67 | | |
68 | | /* FileHandle API errors */ |
69 | | FileHandleInactiveError = 0, |
70 | | |
71 | | /* WebCrypto errors https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError */ |
72 | | OperationError = 0, |
73 | | |
74 | | /* Push API errors */ |
75 | | NotAllowedError = 0, |
76 | | }; |
77 | | |
78 | | #define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message}, |
79 | | #define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message}, |
80 | | |
81 | | static constexpr struct ResultStruct |
82 | | { |
83 | | nsresult mNSResult; |
84 | | uint16_t mCode; |
85 | | const char* mName; |
86 | | const char* mMessage; |
87 | | } sDOMErrorMsgMap[] = { |
88 | | #include "domerr.msg" |
89 | | }; |
90 | | |
91 | | #undef DOM4_MSG_DEF |
92 | | #undef DOM_MSG_DEF |
93 | | |
94 | | static void |
95 | | NSResultToNameAndMessage(nsresult aNSResult, |
96 | | nsCString& aName, |
97 | | nsCString& aMessage, |
98 | | uint16_t* aCode) |
99 | 0 | { |
100 | 0 | aName.Truncate(); |
101 | 0 | aMessage.Truncate(); |
102 | 0 | *aCode = 0; |
103 | 0 | for (uint32_t idx = 0; idx < ArrayLength(sDOMErrorMsgMap); idx++) { |
104 | 0 | if (aNSResult == sDOMErrorMsgMap[idx].mNSResult) { |
105 | 0 | aName.Rebind(sDOMErrorMsgMap[idx].mName, |
106 | 0 | strlen(sDOMErrorMsgMap[idx].mName)); |
107 | 0 | aMessage.Rebind(sDOMErrorMsgMap[idx].mMessage, |
108 | 0 | strlen(sDOMErrorMsgMap[idx].mMessage)); |
109 | 0 | *aCode = sDOMErrorMsgMap[idx].mCode; |
110 | 0 | return; |
111 | 0 | } |
112 | 0 | } |
113 | 0 |
|
114 | 0 | NS_WARNING("Huh, someone is throwing non-DOM errors using the DOM module!"); |
115 | 0 | } |
116 | | |
117 | | nsresult |
118 | | NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult, nsACString& aName, |
119 | | nsACString& aMessage, uint16_t* aCode) |
120 | 0 | { |
121 | 0 | nsCString name; |
122 | 0 | nsCString message; |
123 | 0 | uint16_t code = 0; |
124 | 0 | NSResultToNameAndMessage(aNSResult, name, message, &code); |
125 | 0 |
|
126 | 0 | if (!name.IsEmpty() && !message.IsEmpty()) { |
127 | 0 | aName = name; |
128 | 0 | aMessage = message; |
129 | 0 | if (aCode) { |
130 | 0 | *aCode = code; |
131 | 0 | } |
132 | 0 | return NS_OK; |
133 | 0 | } |
134 | 0 |
|
135 | 0 | return NS_ERROR_NOT_AVAILABLE; |
136 | 0 | } |
137 | | |
138 | | namespace mozilla { |
139 | | namespace dom { |
140 | | |
141 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Exception) |
142 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
143 | 0 | NS_INTERFACE_MAP_ENTRY(Exception) |
144 | 0 | NS_INTERFACE_MAP_ENTRY(nsIException) |
145 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
146 | 0 | NS_INTERFACE_MAP_END |
147 | | |
148 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception) |
149 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception) |
150 | | |
151 | | NS_IMPL_CYCLE_COLLECTION_CLASS(Exception) |
152 | | |
153 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception) |
154 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) |
155 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData) |
156 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
157 | | |
158 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception) |
159 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
160 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mThrownJSVal) |
161 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
162 | | |
163 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception) |
164 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) |
165 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mData) |
166 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
167 | 0 | tmp->mThrownJSVal.setNull(); |
168 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
169 | | |
170 | | Exception::Exception(const nsACString& aMessage, |
171 | | nsresult aResult, |
172 | | const nsACString& aName, |
173 | | nsIStackFrame *aLocation, |
174 | | nsISupports *aData) |
175 | | : mMessage(aMessage) |
176 | | , mResult(aResult) |
177 | | , mName(aName) |
178 | | , mData(aData) |
179 | | , mHoldingJSVal(false) |
180 | 0 | { |
181 | 0 | if (aLocation) { |
182 | 0 | mLocation = aLocation; |
183 | 0 | } else { |
184 | 0 | mLocation = GetCurrentJSStack(); |
185 | 0 | // it is legal for there to be no active JS stack, if C++ code |
186 | 0 | // is operating on a JS-implemented interface pointer without |
187 | 0 | // having been called in turn by JS. This happens in the JS |
188 | 0 | // component loader. |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | Exception::~Exception() |
193 | 0 | { |
194 | 0 | if (mHoldingJSVal) { |
195 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
196 | 0 |
|
197 | 0 | mozilla::DropJSObjects(this); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | bool |
202 | | Exception::StealJSVal(JS::Value* aVp) |
203 | 0 | { |
204 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
205 | 0 |
|
206 | 0 | if (mHoldingJSVal) { |
207 | 0 | *aVp = mThrownJSVal; |
208 | 0 | mThrownJSVal.setNull(); |
209 | 0 |
|
210 | 0 | mozilla::DropJSObjects(this); |
211 | 0 | mHoldingJSVal = false; |
212 | 0 | return true; |
213 | 0 | } |
214 | 0 | |
215 | 0 | return false; |
216 | 0 | } |
217 | | |
218 | | void |
219 | | Exception::StowJSVal(JS::Value& aVp) |
220 | 0 | { |
221 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
222 | 0 |
|
223 | 0 | mThrownJSVal = aVp; |
224 | 0 | if (!mHoldingJSVal) { |
225 | 0 | mozilla::HoldJSObjects(this); |
226 | 0 | mHoldingJSVal = true; |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | void |
231 | | Exception::GetName(nsAString& aName) |
232 | 0 | { |
233 | 0 | if (!mName.IsEmpty()) { |
234 | 0 | CopyUTF8toUTF16(mName, aName); |
235 | 0 | } else { |
236 | 0 | aName.Truncate(); |
237 | 0 |
|
238 | 0 | const char* name = nullptr; |
239 | 0 | nsXPCException::NameAndFormatForNSResult(mResult, &name, nullptr); |
240 | 0 |
|
241 | 0 | if (name) { |
242 | 0 | CopyUTF8toUTF16(mozilla::MakeStringSpan(name), aName); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | void |
248 | | Exception::GetFilename(JSContext* aCx, nsAString& aFilename) |
249 | 0 | { |
250 | 0 | if (mLocation) { |
251 | 0 | mLocation->GetFilename(aCx, aFilename); |
252 | 0 | return; |
253 | 0 | } |
254 | 0 | |
255 | 0 | aFilename.Truncate(); |
256 | 0 | } |
257 | | |
258 | | void |
259 | | Exception::ToString(JSContext* aCx, nsACString& _retval) |
260 | 0 | { |
261 | 0 | static const char defaultMsg[] = "<no message>"; |
262 | 0 | static const char defaultLocation[] = "<unknown>"; |
263 | 0 | static const char format[] = |
264 | 0 | "[Exception... \"%s\" nsresult: \"0x%" PRIx32 " (%s)\" location: \"%s\" data: %s]"; |
265 | 0 |
|
266 | 0 | nsCString location; |
267 | 0 |
|
268 | 0 | if (mLocation) { |
269 | 0 | // we need to free this if it does not fail |
270 | 0 | mLocation->ToString(aCx, location); |
271 | 0 | } |
272 | 0 |
|
273 | 0 | if (location.IsEmpty()) { |
274 | 0 | location.Assign(defaultLocation); |
275 | 0 | } |
276 | 0 |
|
277 | 0 | const char* msg = mMessage.IsEmpty() ? nullptr : mMessage.get(); |
278 | 0 |
|
279 | 0 | const char* resultName = mName.IsEmpty() ? nullptr: mName.get(); |
280 | 0 | if (!resultName && |
281 | 0 | !nsXPCException::NameAndFormatForNSResult(mResult, &resultName, |
282 | 0 | (!msg) ? &msg : nullptr)) { |
283 | 0 | if (!msg) { |
284 | 0 | msg = defaultMsg; |
285 | 0 | } |
286 | 0 | resultName = "<unknown>"; |
287 | 0 | } |
288 | 0 | const char* data = mData ? "yes" : "no"; |
289 | 0 |
|
290 | 0 | _retval.Truncate(); |
291 | 0 | _retval.AppendPrintf(format, msg, static_cast<uint32_t>(mResult), resultName, |
292 | 0 | location.get(), data); |
293 | 0 | } |
294 | | |
295 | | JSObject* |
296 | | Exception::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) |
297 | 0 | { |
298 | 0 | return Exception_Binding::Wrap(cx, this, aGivenProto); |
299 | 0 | } |
300 | | |
301 | | void |
302 | | Exception::GetMessageMoz(nsString& retval) |
303 | 0 | { |
304 | 0 | CopyUTF8toUTF16(mMessage, retval); |
305 | 0 | } |
306 | | |
307 | | uint32_t |
308 | | Exception::Result() const |
309 | 0 | { |
310 | 0 | return (uint32_t)mResult; |
311 | 0 | } |
312 | | |
313 | | uint32_t |
314 | | Exception::LineNumber(JSContext* aCx) const |
315 | 0 | { |
316 | 0 | if (mLocation) { |
317 | 0 | return mLocation->GetLineNumber(aCx); |
318 | 0 | } |
319 | 0 | |
320 | 0 | return 0; |
321 | 0 | } |
322 | | |
323 | | uint32_t |
324 | | Exception::ColumnNumber() const |
325 | 0 | { |
326 | 0 | return 0; |
327 | 0 | } |
328 | | |
329 | | already_AddRefed<nsIStackFrame> |
330 | | Exception::GetLocation() const |
331 | 0 | { |
332 | 0 | nsCOMPtr<nsIStackFrame> location = mLocation; |
333 | 0 | return location.forget(); |
334 | 0 | } |
335 | | |
336 | | nsISupports* |
337 | | Exception::GetData() const |
338 | 0 | { |
339 | 0 | return mData; |
340 | 0 | } |
341 | | |
342 | | void |
343 | | Exception::GetStack(JSContext* aCx, nsAString& aStack) const |
344 | 0 | { |
345 | 0 | if (mLocation) { |
346 | 0 | mLocation->GetFormattedStack(aCx, aStack); |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | void |
351 | | Exception::Stringify(JSContext* aCx, nsString& retval) |
352 | 0 | { |
353 | 0 | nsCString str; |
354 | 0 | ToString(aCx, str); |
355 | 0 | CopyUTF8toUTF16(str, retval); |
356 | 0 | } |
357 | | |
358 | | DOMException::DOMException(nsresult aRv, const nsACString& aMessage, |
359 | | const nsACString& aName, uint16_t aCode) |
360 | | : Exception(aMessage, aRv, aName, nullptr, nullptr), |
361 | | mCode(aCode) |
362 | 0 | { |
363 | 0 | } |
364 | | |
365 | | void |
366 | | DOMException::ToString(JSContext* aCx, nsACString& aReturn) |
367 | 0 | { |
368 | 0 | aReturn.Truncate(); |
369 | 0 |
|
370 | 0 | static const char defaultMsg[] = "<no message>"; |
371 | 0 | static const char defaultLocation[] = "<unknown>"; |
372 | 0 | static const char defaultName[] = "<unknown>"; |
373 | 0 | static const char format[] = |
374 | 0 | "[Exception... \"%s\" code: \"%d\" nsresult: \"0x%" PRIx32 " (%s)\" location: \"%s\"]"; |
375 | 0 |
|
376 | 0 | nsAutoCString location; |
377 | 0 |
|
378 | 0 | if (location.IsEmpty()) { |
379 | 0 | location = defaultLocation; |
380 | 0 | } |
381 | 0 |
|
382 | 0 | const char* msg = !mMessage.IsEmpty() ? mMessage.get() : defaultMsg; |
383 | 0 | const char* resultName = !mName.IsEmpty() ? mName.get() : defaultName; |
384 | 0 |
|
385 | 0 | aReturn.AppendPrintf(format, msg, mCode, static_cast<uint32_t>(mResult), resultName, |
386 | 0 | location.get()); |
387 | 0 | } |
388 | | |
389 | | void |
390 | | DOMException::GetName(nsString& retval) |
391 | 0 | { |
392 | 0 | CopyUTF8toUTF16(mName, retval); |
393 | 0 | } |
394 | | |
395 | | already_AddRefed<DOMException> |
396 | | DOMException::Constructor(GlobalObject& /* unused */, |
397 | | const nsAString& aMessage, |
398 | | const Optional<nsAString>& aName, |
399 | | ErrorResult& aError) |
400 | 0 | { |
401 | 0 | nsresult exceptionResult = NS_OK; |
402 | 0 | uint16_t exceptionCode = 0; |
403 | 0 | nsCString name(NS_LITERAL_CSTRING("Error")); |
404 | 0 |
|
405 | 0 | if (aName.WasPassed()) { |
406 | 0 | CopyUTF16toUTF8(aName.Value(), name); |
407 | 0 | for (uint32_t idx = 0; idx < ArrayLength(sDOMErrorMsgMap); idx++) { |
408 | 0 | if (name.EqualsASCII(sDOMErrorMsgMap[idx].mName)) { |
409 | 0 | exceptionResult = sDOMErrorMsgMap[idx].mNSResult; |
410 | 0 | exceptionCode = sDOMErrorMsgMap[idx].mCode; |
411 | 0 | break; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | } |
415 | 0 |
|
416 | 0 | RefPtr<DOMException> retval = |
417 | 0 | new DOMException(exceptionResult, |
418 | 0 | NS_ConvertUTF16toUTF8(aMessage), |
419 | 0 | name, |
420 | 0 | exceptionCode); |
421 | 0 | return retval.forget(); |
422 | 0 | } |
423 | | |
424 | | JSObject* |
425 | | DOMException::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
426 | 0 | { |
427 | 0 | return DOMException_Binding::Wrap(aCx, this, aGivenProto); |
428 | 0 | } |
429 | | |
430 | | /* static */already_AddRefed<DOMException> |
431 | | DOMException::Create(nsresult aRv) |
432 | 0 | { |
433 | 0 | nsCString name; |
434 | 0 | nsCString message; |
435 | 0 | uint16_t code; |
436 | 0 | NSResultToNameAndMessage(aRv, name, message, &code); |
437 | 0 | RefPtr<DOMException> inst = |
438 | 0 | new DOMException(aRv, message, name, code); |
439 | 0 | return inst.forget(); |
440 | 0 | } |
441 | | |
442 | | /* static */already_AddRefed<DOMException> |
443 | | DOMException::Create(nsresult aRv, const nsACString& aMessage) |
444 | 0 | { |
445 | 0 | nsCString name; |
446 | 0 | nsCString message; |
447 | 0 | uint16_t code; |
448 | 0 | NSResultToNameAndMessage(aRv, name, message, &code); |
449 | 0 | RefPtr<DOMException> inst = |
450 | 0 | new DOMException(aRv, aMessage, name, code); |
451 | 0 | return inst.forget(); |
452 | 0 | } |
453 | | |
454 | | } // namespace dom |
455 | | } // namespace mozilla |