Bug 1660051 [wpt PR 25111] - Origin isolation: expand getter test coverage, a=testonly
[gecko.git] / dom / base / DOMException.cpp
blob5f5cf6a4b5aebb1e49638b19f5a3e375706ea491
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 "nsMemory.h"
17 #include "xpcprivate.h"
19 #include "mozilla/dom/DOMExceptionBinding.h"
20 #include "mozilla/ErrorResult.h"
22 using namespace mozilla;
23 using namespace mozilla::dom;
25 enum DOM4ErrorTypeCodeMap {
26 /* DOM4 errors from
27 http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */
28 IndexSizeError = DOMException_Binding::INDEX_SIZE_ERR,
29 HierarchyRequestError = DOMException_Binding::HIERARCHY_REQUEST_ERR,
30 WrongDocumentError = DOMException_Binding::WRONG_DOCUMENT_ERR,
31 InvalidCharacterError = DOMException_Binding::INVALID_CHARACTER_ERR,
32 NoModificationAllowedError =
33 DOMException_Binding::NO_MODIFICATION_ALLOWED_ERR,
34 NotFoundError = DOMException_Binding::NOT_FOUND_ERR,
35 NotSupportedError = DOMException_Binding::NOT_SUPPORTED_ERR,
36 // Can't remove until setNamedItem is removed
37 InUseAttributeError = DOMException_Binding::INUSE_ATTRIBUTE_ERR,
38 InvalidStateError = DOMException_Binding::INVALID_STATE_ERR,
39 SyntaxError = DOMException_Binding::SYNTAX_ERR,
40 InvalidModificationError = DOMException_Binding::INVALID_MODIFICATION_ERR,
41 NamespaceError = DOMException_Binding::NAMESPACE_ERR,
42 InvalidAccessError = DOMException_Binding::INVALID_ACCESS_ERR,
43 TypeMismatchError = DOMException_Binding::TYPE_MISMATCH_ERR,
44 SecurityError = DOMException_Binding::SECURITY_ERR,
45 NetworkError = DOMException_Binding::NETWORK_ERR,
46 AbortError = DOMException_Binding::ABORT_ERR,
47 URLMismatchError = DOMException_Binding::URL_MISMATCH_ERR,
48 QuotaExceededError = DOMException_Binding::QUOTA_EXCEEDED_ERR,
49 TimeoutError = DOMException_Binding::TIMEOUT_ERR,
50 InvalidNodeTypeError = DOMException_Binding::INVALID_NODE_TYPE_ERR,
51 DataCloneError = DOMException_Binding::DATA_CLONE_ERR,
52 InvalidPointerId = 0,
53 EncodingError = 0,
55 /* XXX Should be JavaScript native errors */
56 TypeError = 0,
57 RangeError = 0,
59 /* IndexedDB errors
60 http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#exceptions */
61 UnknownError = 0,
62 ConstraintError = 0,
63 DataError = 0,
64 TransactionInactiveError = 0,
65 ReadOnlyError = 0,
66 VersionError = 0,
68 /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
69 NotReadableError = 0,
71 /* FileHandle API errors */
72 FileHandleInactiveError = 0,
74 /* WebCrypto errors
75 https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError
77 OperationError = 0,
79 /* Push API errors */
80 NotAllowedError = 0,
83 #define DOM4_MSG_DEF(name, message, nsresult) \
84 {(nsresult), name, #name, message},
85 #define DOM_MSG_DEF(val, message) \
86 {(val), NS_ERROR_GET_CODE(val), #val, message},
88 static constexpr struct ResultStruct {
89 nsresult mNSResult;
90 uint16_t mCode;
91 const char* mName;
92 const char* mMessage;
93 } sDOMErrorMsgMap[] = {
94 #include "domerr.msg"
97 #undef DOM4_MSG_DEF
98 #undef DOM_MSG_DEF
100 static void NSResultToNameAndMessage(nsresult aNSResult, nsCString& aName,
101 nsCString& aMessage, uint16_t* aCode) {
102 aName.Truncate();
103 aMessage.Truncate();
104 *aCode = 0;
105 for (uint32_t idx = 0; idx < ArrayLength(sDOMErrorMsgMap); idx++) {
106 if (aNSResult == sDOMErrorMsgMap[idx].mNSResult) {
107 aName.Rebind(sDOMErrorMsgMap[idx].mName,
108 strlen(sDOMErrorMsgMap[idx].mName));
109 aMessage.Rebind(sDOMErrorMsgMap[idx].mMessage,
110 strlen(sDOMErrorMsgMap[idx].mMessage));
111 *aCode = sDOMErrorMsgMap[idx].mCode;
112 return;
116 NS_WARNING("Huh, someone is throwing non-DOM errors using the DOM module!");
119 nsresult NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult,
120 nsACString& aName,
121 nsACString& aMessage,
122 uint16_t* aCode) {
123 nsCString name;
124 nsCString message;
125 uint16_t code = 0;
126 NSResultToNameAndMessage(aNSResult, name, message, &code);
128 if (!name.IsEmpty() && !message.IsEmpty()) {
129 aName = name;
130 aMessage = message;
131 if (aCode) {
132 *aCode = code;
134 return NS_OK;
137 return NS_ERROR_NOT_AVAILABLE;
140 namespace mozilla {
141 namespace dom {
143 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Exception)
144 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
145 NS_INTERFACE_MAP_ENTRY(Exception)
146 NS_INTERFACE_MAP_ENTRY(nsIException)
147 NS_INTERFACE_MAP_ENTRY(nsISupports)
148 NS_INTERFACE_MAP_END
150 NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception)
151 NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception)
153 NS_IMPL_CYCLE_COLLECTION_CLASS(Exception)
155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception)
156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
160 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception)
161 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
162 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mThrownJSVal)
163 NS_IMPL_CYCLE_COLLECTION_TRACE_END
165 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception)
166 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
167 NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
168 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
169 tmp->mThrownJSVal.setNull();
170 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
172 Exception::Exception(const nsACString& aMessage, nsresult aResult,
173 const nsACString& aName, nsIStackFrame* aLocation,
174 nsISupports* aData)
175 : mMessage(aMessage),
176 mResult(aResult),
177 mName(aName),
178 mData(aData),
179 mHoldingJSVal(false) {
180 if (aLocation) {
181 mLocation = aLocation;
182 } else {
183 mLocation = GetCurrentJSStack();
184 // it is legal for there to be no active JS stack, if C++ code
185 // is operating on a JS-implemented interface pointer without
186 // having been called in turn by JS. This happens in the JS
187 // component loader.
191 Exception::~Exception() {
192 if (mHoldingJSVal) {
193 MOZ_ASSERT(NS_IsMainThread());
195 mozilla::DropJSObjects(this);
199 bool Exception::StealJSVal(JS::Value* aVp) {
200 MOZ_ASSERT(NS_IsMainThread());
202 if (mHoldingJSVal) {
203 *aVp = mThrownJSVal;
204 mThrownJSVal.setNull();
206 mozilla::DropJSObjects(this);
207 mHoldingJSVal = false;
208 return true;
211 return false;
214 void Exception::StowJSVal(JS::Value& aVp) {
215 MOZ_ASSERT(NS_IsMainThread());
217 mThrownJSVal = aVp;
218 if (!mHoldingJSVal) {
219 mozilla::HoldJSObjects(this);
220 mHoldingJSVal = true;
224 void Exception::GetName(nsAString& aName) {
225 if (!mName.IsEmpty()) {
226 CopyUTF8toUTF16(mName, aName);
227 } else {
228 aName.Truncate();
230 const char* name = nullptr;
231 nsXPCException::NameAndFormatForNSResult(mResult, &name, nullptr);
233 if (name) {
234 CopyUTF8toUTF16(mozilla::MakeStringSpan(name), aName);
239 void Exception::GetFilename(JSContext* aCx, nsAString& aFilename) {
240 if (mLocation) {
241 mLocation->GetFilename(aCx, aFilename);
242 return;
245 aFilename.Truncate();
248 void Exception::ToString(JSContext* aCx, nsACString& _retval) {
249 static const char defaultMsg[] = "<no message>";
250 static const char defaultLocation[] = "<unknown>";
251 static const char format[] = "[Exception... \"%s\" nsresult: \"0x%" PRIx32
252 " (%s)\" location: \"%s\" data: %s]";
254 nsCString location;
256 if (mLocation) {
257 // we need to free this if it does not fail
258 mLocation->ToString(aCx, location);
261 if (location.IsEmpty()) {
262 location.Assign(defaultLocation);
265 const char* msg = mMessage.IsEmpty() ? nullptr : mMessage.get();
267 const char* resultName = mName.IsEmpty() ? nullptr : mName.get();
268 if (!resultName && !nsXPCException::NameAndFormatForNSResult(
269 mResult, &resultName, (!msg) ? &msg : nullptr)) {
270 if (!msg) {
271 msg = defaultMsg;
273 resultName = "<unknown>";
275 const char* data = mData ? "yes" : "no";
277 _retval.Truncate();
278 _retval.AppendPrintf(format, msg, static_cast<uint32_t>(mResult), resultName,
279 location.get(), data);
282 JSObject* Exception::WrapObject(JSContext* cx,
283 JS::Handle<JSObject*> aGivenProto) {
284 return Exception_Binding::Wrap(cx, this, aGivenProto);
287 void Exception::GetMessageMoz(nsString& retval) {
288 CopyUTF8toUTF16(mMessage, retval);
291 uint32_t Exception::Result() const { return (uint32_t)mResult; }
293 uint32_t Exception::SourceId(JSContext* aCx) const {
294 if (mLocation) {
295 return mLocation->GetSourceId(aCx);
298 return 0;
301 uint32_t Exception::LineNumber(JSContext* aCx) const {
302 if (mLocation) {
303 return mLocation->GetLineNumber(aCx);
306 return 0;
309 uint32_t Exception::ColumnNumber() const { return 0; }
311 already_AddRefed<nsIStackFrame> Exception::GetLocation() const {
312 nsCOMPtr<nsIStackFrame> location = mLocation;
313 return location.forget();
316 nsISupports* Exception::GetData() const { return mData; }
318 void Exception::GetStack(JSContext* aCx, nsAString& aStack) const {
319 if (mLocation) {
320 mLocation->GetFormattedStack(aCx, aStack);
324 void Exception::Stringify(JSContext* aCx, nsString& retval) {
325 nsCString str;
326 ToString(aCx, str);
327 CopyUTF8toUTF16(str, retval);
330 DOMException::DOMException(nsresult aRv, const nsACString& aMessage,
331 const nsACString& aName, uint16_t aCode,
332 nsIStackFrame* aLocation)
333 : Exception(aMessage, aRv, aName, aLocation, nullptr), mCode(aCode) {}
335 void DOMException::ToString(JSContext* aCx, nsACString& aReturn) {
336 aReturn.Truncate();
338 static const char defaultMsg[] = "<no message>";
339 static const char defaultLocation[] = "<unknown>";
340 static const char defaultName[] = "<unknown>";
341 static const char format[] =
342 "[Exception... \"%s\" code: \"%d\" nsresult: \"0x%" PRIx32
343 " (%s)\" location: \"%s\"]";
345 nsAutoCString location;
347 if (location.IsEmpty()) {
348 location = defaultLocation;
351 const char* msg = !mMessage.IsEmpty() ? mMessage.get() : defaultMsg;
352 const char* resultName = !mName.IsEmpty() ? mName.get() : defaultName;
354 aReturn.AppendPrintf(format, msg, mCode, static_cast<uint32_t>(mResult),
355 resultName, location.get());
358 void DOMException::GetName(nsString& retval) { CopyUTF8toUTF16(mName, retval); }
360 already_AddRefed<DOMException> DOMException::Constructor(
361 GlobalObject& /* unused */, const nsAString& aMessage,
362 const Optional<nsAString>& aName) {
363 nsresult exceptionResult = NS_OK;
364 uint16_t exceptionCode = 0;
365 nsCString name("Error"_ns);
367 if (aName.WasPassed()) {
368 CopyUTF16toUTF8(aName.Value(), name);
369 for (uint32_t idx = 0; idx < ArrayLength(sDOMErrorMsgMap); idx++) {
370 if (name.EqualsASCII(sDOMErrorMsgMap[idx].mName)) {
371 exceptionResult = sDOMErrorMsgMap[idx].mNSResult;
372 exceptionCode = sDOMErrorMsgMap[idx].mCode;
373 break;
378 RefPtr<DOMException> retval = new DOMException(
379 exceptionResult, NS_ConvertUTF16toUTF8(aMessage), name, exceptionCode);
380 return retval.forget();
383 JSObject* DOMException::WrapObject(JSContext* aCx,
384 JS::Handle<JSObject*> aGivenProto) {
385 return DOMException_Binding::Wrap(aCx, this, aGivenProto);
388 /* static */
389 already_AddRefed<DOMException> DOMException::Create(nsresult aRv) {
390 nsCString name;
391 nsCString message;
392 uint16_t code;
393 NSResultToNameAndMessage(aRv, name, message, &code);
394 RefPtr<DOMException> inst = new DOMException(aRv, message, name, code);
395 return inst.forget();
398 /* static */
399 already_AddRefed<DOMException> DOMException::Create(
400 nsresult aRv, const nsACString& aMessage) {
401 nsCString name;
402 nsCString message;
403 uint16_t code;
404 NSResultToNameAndMessage(aRv, name, message, &code);
405 RefPtr<DOMException> inst = new DOMException(aRv, aMessage, name, code);
406 return inst.forget();
409 } // namespace dom
410 } // namespace mozilla