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/. */
7 #include "mozilla/dom/DOMException.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/HoldDropJSObjects.h"
11 #include "mozilla/dom/Exceptions.h"
12 #include "nsContentUtils.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsIException.h"
16 #include "xpcprivate.h"
18 #include "mozilla/dom/DOMExceptionBinding.h"
19 #include "mozilla/ErrorResult.h"
21 #include "js/TypeDecls.h"
22 #include "js/StructuredClone.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
27 enum DOM4ErrorTypeCodeMap
{
29 http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */
30 IndexSizeError
= DOMException_Binding::INDEX_SIZE_ERR
,
31 HierarchyRequestError
= DOMException_Binding::HIERARCHY_REQUEST_ERR
,
32 WrongDocumentError
= DOMException_Binding::WRONG_DOCUMENT_ERR
,
33 InvalidCharacterError
= DOMException_Binding::INVALID_CHARACTER_ERR
,
34 NoModificationAllowedError
=
35 DOMException_Binding::NO_MODIFICATION_ALLOWED_ERR
,
36 NotFoundError
= DOMException_Binding::NOT_FOUND_ERR
,
37 NotSupportedError
= DOMException_Binding::NOT_SUPPORTED_ERR
,
38 // Can't remove until setNamedItem is removed
39 InUseAttributeError
= DOMException_Binding::INUSE_ATTRIBUTE_ERR
,
40 InvalidStateError
= DOMException_Binding::INVALID_STATE_ERR
,
41 SyntaxError
= DOMException_Binding::SYNTAX_ERR
,
42 InvalidModificationError
= DOMException_Binding::INVALID_MODIFICATION_ERR
,
43 NamespaceError
= DOMException_Binding::NAMESPACE_ERR
,
44 InvalidAccessError
= DOMException_Binding::INVALID_ACCESS_ERR
,
45 TypeMismatchError
= DOMException_Binding::TYPE_MISMATCH_ERR
,
46 SecurityError
= DOMException_Binding::SECURITY_ERR
,
47 NetworkError
= DOMException_Binding::NETWORK_ERR
,
48 AbortError
= DOMException_Binding::ABORT_ERR
,
49 URLMismatchError
= DOMException_Binding::URL_MISMATCH_ERR
,
50 QuotaExceededError
= DOMException_Binding::QUOTA_EXCEEDED_ERR
,
51 TimeoutError
= DOMException_Binding::TIMEOUT_ERR
,
52 InvalidNodeTypeError
= DOMException_Binding::INVALID_NODE_TYPE_ERR
,
53 DataCloneError
= DOMException_Binding::DATA_CLONE_ERR
,
57 http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#exceptions */
61 TransactionInactiveError
= 0,
65 /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
68 /* FileHandle API errors */
69 FileHandleInactiveError
= 0,
72 https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError
80 #define DOM4_MSG_DEF(name, message, nsresult) \
81 {(nsresult), name, #name, message},
82 #define DOM_MSG_DEF(val, message) \
83 {(val), NS_ERROR_GET_CODE(val), #val, message},
85 static constexpr struct ResultStruct
{
90 } sDOMErrorMsgMap
[] = {
97 static void NSResultToNameAndMessage(nsresult aNSResult
, nsCString
& aName
,
98 nsCString
& aMessage
, uint16_t* aCode
) {
102 for (uint32_t idx
= 0; idx
< ArrayLength(sDOMErrorMsgMap
); idx
++) {
103 if (aNSResult
== sDOMErrorMsgMap
[idx
].mNSResult
) {
104 aName
.Rebind(sDOMErrorMsgMap
[idx
].mName
,
105 strlen(sDOMErrorMsgMap
[idx
].mName
));
106 aMessage
.Rebind(sDOMErrorMsgMap
[idx
].mMessage
,
107 strlen(sDOMErrorMsgMap
[idx
].mMessage
));
108 *aCode
= sDOMErrorMsgMap
[idx
].mCode
;
113 NS_WARNING("Huh, someone is throwing non-DOM errors using the DOM module!");
116 nsresult
NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult
,
118 nsACString
& aMessage
,
123 NSResultToNameAndMessage(aNSResult
, name
, message
, &code
);
125 if (!name
.IsEmpty() && !message
.IsEmpty()) {
134 return NS_ERROR_NOT_AVAILABLE
;
137 namespace mozilla::dom
{
139 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Exception
)
140 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
141 NS_INTERFACE_MAP_ENTRY(Exception
)
142 NS_INTERFACE_MAP_ENTRY(nsIException
)
143 NS_INTERFACE_MAP_ENTRY(nsISupports
)
146 NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception
)
147 NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception
)
149 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(Exception
,
153 Exception::Exception(const nsACString
& aMessage
, nsresult aResult
,
154 const nsACString
& aName
, nsIStackFrame
* aLocation
,
156 : mMessage(aMessage
),
160 mHoldingJSVal(false) {
162 mLocation
= aLocation
;
164 mLocation
= GetCurrentJSStack();
165 // it is legal for there to be no active JS stack, if C++ code
166 // is operating on a JS-implemented interface pointer without
167 // having been called in turn by JS. This happens in the JS
172 Exception::Exception(nsCString
&& aMessage
, nsresult aResult
, nsCString
&& aName
)
173 : mMessage(std::move(aMessage
)),
175 mName(std::move(aName
)),
176 mHoldingJSVal(false) {}
178 Exception::~Exception() {
180 MOZ_ASSERT(NS_IsMainThread());
182 mozilla::DropJSObjects(this);
186 bool Exception::StealJSVal(JS::Value
* aVp
) {
187 MOZ_ASSERT(NS_IsMainThread());
192 mozilla::DropJSObjects(this);
193 mHoldingJSVal
= false;
200 void Exception::StowJSVal(JS::Value
& aVp
) {
201 MOZ_ASSERT(NS_IsMainThread());
204 if (!mHoldingJSVal
) {
205 mozilla::HoldJSObjects(this);
206 mHoldingJSVal
= true;
210 void Exception::GetName(nsAString
& aName
) {
211 if (!mName
.IsEmpty()) {
212 CopyUTF8toUTF16(mName
, aName
);
216 const char* name
= nullptr;
217 nsXPCException::NameAndFormatForNSResult(mResult
, &name
, nullptr);
220 CopyUTF8toUTF16(mozilla::MakeStringSpan(name
), aName
);
225 void Exception::GetFilename(JSContext
* aCx
, nsACString
& aFilename
) {
227 mLocation
->GetFilename(aCx
, aFilename
);
231 aFilename
.Truncate();
234 void Exception::ToString(JSContext
* aCx
, nsACString
& _retval
) {
235 static const char defaultMsg
[] = "<no message>";
236 static const char defaultLocation
[] = "<unknown>";
237 static const char format
[] = "[Exception... \"%s\" nsresult: \"0x%" PRIx32
238 " (%s)\" location: \"%s\" data: %s]";
243 // we need to free this if it does not fail
244 mLocation
->ToString(aCx
, location
);
247 if (location
.IsEmpty()) {
248 location
.Assign(defaultLocation
);
251 const char* msg
= mMessage
.IsEmpty() ? nullptr : mMessage
.get();
253 const char* resultName
= mName
.IsEmpty() ? nullptr : mName
.get();
254 if (!resultName
&& !nsXPCException::NameAndFormatForNSResult(
255 mResult
, &resultName
, (!msg
) ? &msg
: nullptr)) {
259 resultName
= "<unknown>";
261 const char* data
= mData
? "yes" : "no";
264 _retval
.AppendPrintf(format
, msg
, static_cast<uint32_t>(mResult
), resultName
,
265 location
.get(), data
);
268 JSObject
* Exception::WrapObject(JSContext
* cx
,
269 JS::Handle
<JSObject
*> aGivenProto
) {
270 return Exception_Binding::Wrap(cx
, this, aGivenProto
);
273 void Exception::GetMessageMoz(nsString
& retval
) {
274 CopyUTF8toUTF16(mMessage
, retval
);
277 uint32_t Exception::Result() const { return (uint32_t)mResult
; }
279 uint32_t Exception::SourceId(JSContext
* aCx
) const {
281 return mLocation
->GetSourceId(aCx
);
287 uint32_t Exception::LineNumber(JSContext
* aCx
) const {
289 return mLocation
->GetLineNumber(aCx
);
295 uint32_t Exception::ColumnNumber() const { return 0; }
297 already_AddRefed
<nsIStackFrame
> Exception::GetLocation() const {
298 nsCOMPtr
<nsIStackFrame
> location
= mLocation
;
299 return location
.forget();
302 nsISupports
* Exception::GetData() const { return mData
; }
304 void Exception::GetStack(JSContext
* aCx
, nsAString
& aStack
) const {
306 mLocation
->GetFormattedStack(aCx
, aStack
);
310 void Exception::Stringify(JSContext
* aCx
, nsString
& retval
) {
313 CopyUTF8toUTF16(str
, retval
);
316 DOMException::DOMException(nsresult aRv
, const nsACString
& aMessage
,
317 const nsACString
& aName
, uint16_t aCode
,
318 nsIStackFrame
* aLocation
)
319 : Exception(aMessage
, aRv
, aName
, aLocation
, nullptr), mCode(aCode
) {}
320 DOMException::DOMException(nsresult aRv
, nsCString
&& aMessage
,
321 nsCString
&& aName
, uint16_t aCode
)
322 : Exception(std::move(aMessage
), aRv
, std::move(aName
)), mCode(aCode
) {}
324 void DOMException::ToString(JSContext
* aCx
, nsACString
& aReturn
) {
327 static const char defaultMsg
[] = "<no message>";
328 static const char defaultLocation
[] = "<unknown>";
329 static const char defaultName
[] = "<unknown>";
330 static const char format
[] =
331 "[Exception... \"%s\" code: \"%d\" nsresult: \"0x%" PRIx32
332 " (%s)\" location: \"%s\"]";
334 nsAutoCString location
;
336 if (location
.IsEmpty()) {
337 location
= defaultLocation
;
340 const char* msg
= !mMessage
.IsEmpty() ? mMessage
.get() : defaultMsg
;
341 const char* resultName
= !mName
.IsEmpty() ? mName
.get() : defaultName
;
343 aReturn
.AppendPrintf(format
, msg
, mCode
, static_cast<uint32_t>(mResult
),
344 resultName
, location
.get());
347 void DOMException::GetName(nsString
& retval
) { CopyUTF8toUTF16(mName
, retval
); }
349 already_AddRefed
<DOMException
> DOMException::Constructor(
350 GlobalObject
& /* unused */, const nsAString
& aMessage
,
351 const Optional
<nsAString
>& aName
) {
352 nsresult exceptionResult
= NS_OK
;
353 uint16_t exceptionCode
= 0;
354 nsCString
name("Error"_ns
);
356 if (aName
.WasPassed()) {
357 CopyUTF16toUTF8(aName
.Value(), name
);
358 for (uint32_t idx
= 0; idx
< ArrayLength(sDOMErrorMsgMap
); idx
++) {
359 if (name
.EqualsASCII(sDOMErrorMsgMap
[idx
].mName
)) {
360 exceptionResult
= sDOMErrorMsgMap
[idx
].mNSResult
;
361 exceptionCode
= sDOMErrorMsgMap
[idx
].mCode
;
367 RefPtr
<DOMException
> retval
= new DOMException(
368 exceptionResult
, NS_ConvertUTF16toUTF8(aMessage
), name
, exceptionCode
);
369 return retval
.forget();
372 JSObject
* DOMException::WrapObject(JSContext
* aCx
,
373 JS::Handle
<JSObject
*> aGivenProto
) {
374 return DOMException_Binding::Wrap(aCx
, this, aGivenProto
);
378 already_AddRefed
<DOMException
> DOMException::Create(nsresult aRv
) {
382 NSResultToNameAndMessage(aRv
, name
, message
, &code
);
383 RefPtr
<DOMException
> inst
= new DOMException(aRv
, message
, name
, code
);
384 return inst
.forget();
388 already_AddRefed
<DOMException
> DOMException::Create(
389 nsresult aRv
, const nsACString
& aMessage
) {
393 NSResultToNameAndMessage(aRv
, name
, message
, &code
);
394 RefPtr
<DOMException
> inst
= new DOMException(aRv
, aMessage
, name
, code
);
395 return inst
.forget();
398 static bool ReadAsCString(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
399 nsCString
& aString
) {
400 JS::Rooted
<JSString
*> jsMessage(aCx
);
401 if (!JS_ReadString(aReader
, &jsMessage
)) {
404 return AssignJSString(aCx
, aString
, jsMessage
);
407 already_AddRefed
<DOMException
> DOMException::ReadStructuredClone(
408 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
409 JSStructuredCloneReader
* aReader
) {
416 if (!JS_ReadBytes(aReader
, &reserved
, 4) || !JS_ReadBytes(aReader
, &rv
, 4) ||
417 !ReadAsCString(aCx
, aReader
, message
) ||
418 !ReadAsCString(aCx
, aReader
, name
) || !JS_ReadBytes(aReader
, &code
, 2)) {
423 new DOMException(rv
, std::move(message
), std::move(name
), code
));
426 bool DOMException::WriteStructuredClone(
427 JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
) const {
428 JS::Rooted
<JS::Value
> messageValue(aCx
);
429 JS::Rooted
<JS::Value
> nameValue(aCx
);
430 if (!NonVoidByteStringToJsval(aCx
, mMessage
, &messageValue
) ||
431 !NonVoidByteStringToJsval(aCx
, mName
, &nameValue
)) {
435 JS::Rooted
<JSString
*> message(aCx
, messageValue
.toString());
436 JS::Rooted
<JSString
*> name(aCx
, nameValue
.toString());
438 static_assert(sizeof(nsresult
) == 4);
440 // A reserved field. Use this to indicate stack serialization support etc.
441 uint32_t reserved
= 0;
442 return JS_WriteBytes(aWriter
, &reserved
, 4) &&
443 JS_WriteBytes(aWriter
, &mResult
, 4) &&
444 JS_WriteString(aWriter
, message
) && JS_WriteString(aWriter
, name
) &&
445 JS_WriteBytes(aWriter
, &mCode
, 2);
448 } // namespace mozilla::dom