no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / dom / base / DOMException.cpp
blob8bb60054a5112d9a310d3d2673e631cdb5835a5f
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"
13 #include "nsCOMPtr.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 {
28 /* DOM4 errors from
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,
54 EncodingError = 0,
56 /* IndexedDB errors
57 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,
65 /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
66 NotReadableError = 0,
68 /* FileHandle API errors */
69 FileHandleInactiveError = 0,
71 /* WebCrypto errors
72 https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError
74 OperationError = 0,
76 /* Push API errors */
77 NotAllowedError = 0,
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 {
86 nsresult mNSResult;
87 uint16_t mCode;
88 const char* mName;
89 const char* mMessage;
90 } sDOMErrorMsgMap[] = {
91 #include "domerr.msg"
94 #undef DOM4_MSG_DEF
95 #undef DOM_MSG_DEF
97 static void NSResultToNameAndMessage(nsresult aNSResult, nsCString& aName,
98 nsCString& aMessage, uint16_t* aCode) {
99 aName.Truncate();
100 aMessage.Truncate();
101 *aCode = 0;
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;
109 return;
113 NS_WARNING("Huh, someone is throwing non-DOM errors using the DOM module!");
116 nsresult NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult,
117 nsACString& aName,
118 nsACString& aMessage,
119 uint16_t* aCode) {
120 nsCString name;
121 nsCString message;
122 uint16_t code = 0;
123 NSResultToNameAndMessage(aNSResult, name, message, &code);
125 if (!name.IsEmpty() && !message.IsEmpty()) {
126 aName = name;
127 aMessage = message;
128 if (aCode) {
129 *aCode = code;
131 return NS_OK;
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)
144 NS_INTERFACE_MAP_END
146 NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception)
147 NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception)
149 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(Exception,
150 (mLocation, mData),
151 (mThrownJSVal))
153 Exception::Exception(const nsACString& aMessage, nsresult aResult,
154 const nsACString& aName, nsIStackFrame* aLocation,
155 nsISupports* aData)
156 : mMessage(aMessage),
157 mResult(aResult),
158 mName(aName),
159 mData(aData),
160 mHoldingJSVal(false) {
161 if (aLocation) {
162 mLocation = aLocation;
163 } else {
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
168 // component loader.
172 Exception::Exception(nsCString&& aMessage, nsresult aResult, nsCString&& aName)
173 : mMessage(std::move(aMessage)),
174 mResult(aResult),
175 mName(std::move(aName)),
176 mHoldingJSVal(false) {}
178 Exception::~Exception() {
179 if (mHoldingJSVal) {
180 MOZ_ASSERT(NS_IsMainThread());
182 mozilla::DropJSObjects(this);
186 bool Exception::StealJSVal(JS::Value* aVp) {
187 MOZ_ASSERT(NS_IsMainThread());
189 if (mHoldingJSVal) {
190 *aVp = mThrownJSVal;
192 mozilla::DropJSObjects(this);
193 mHoldingJSVal = false;
194 return true;
197 return false;
200 void Exception::StowJSVal(JS::Value& aVp) {
201 MOZ_ASSERT(NS_IsMainThread());
203 mThrownJSVal = aVp;
204 if (!mHoldingJSVal) {
205 mozilla::HoldJSObjects(this);
206 mHoldingJSVal = true;
210 void Exception::GetName(nsAString& aName) {
211 if (!mName.IsEmpty()) {
212 CopyUTF8toUTF16(mName, aName);
213 } else {
214 aName.Truncate();
216 const char* name = nullptr;
217 nsXPCException::NameAndFormatForNSResult(mResult, &name, nullptr);
219 if (name) {
220 CopyUTF8toUTF16(mozilla::MakeStringSpan(name), aName);
225 void Exception::GetFilename(JSContext* aCx, nsACString& aFilename) {
226 if (mLocation) {
227 mLocation->GetFilename(aCx, aFilename);
228 return;
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]";
240 nsCString location;
242 if (mLocation) {
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)) {
256 if (!msg) {
257 msg = defaultMsg;
259 resultName = "<unknown>";
261 const char* data = mData ? "yes" : "no";
263 _retval.Truncate();
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 {
280 if (mLocation) {
281 return mLocation->GetSourceId(aCx);
284 return 0;
287 uint32_t Exception::LineNumber(JSContext* aCx) const {
288 if (mLocation) {
289 return mLocation->GetLineNumber(aCx);
292 return 0;
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 {
305 if (mLocation) {
306 mLocation->GetFormattedStack(aCx, aStack);
310 void Exception::Stringify(JSContext* aCx, nsString& retval) {
311 nsCString str;
312 ToString(aCx, str);
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) {
325 aReturn.Truncate();
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;
362 break;
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);
377 /* static */
378 already_AddRefed<DOMException> DOMException::Create(nsresult aRv) {
379 nsCString name;
380 nsCString message;
381 uint16_t code;
382 NSResultToNameAndMessage(aRv, name, message, &code);
383 RefPtr<DOMException> inst = new DOMException(aRv, message, name, code);
384 return inst.forget();
387 /* static */
388 already_AddRefed<DOMException> DOMException::Create(
389 nsresult aRv, const nsACString& aMessage) {
390 nsCString name;
391 nsCString message;
392 uint16_t code;
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)) {
402 return false;
404 return AssignJSString(aCx, aString, jsMessage);
407 already_AddRefed<DOMException> DOMException::ReadStructuredClone(
408 JSContext* aCx, nsIGlobalObject* aGlobal,
409 JSStructuredCloneReader* aReader) {
410 uint32_t reserved;
411 nsresult rv;
412 nsCString message;
413 nsCString name;
414 uint16_t code;
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)) {
419 return nullptr;
422 return do_AddRef(
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)) {
432 return false;
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