diff options
Diffstat (limited to 'dom/base/DOMException.cpp')
-rw-r--r-- | dom/base/DOMException.cpp | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/dom/base/DOMException.cpp b/dom/base/DOMException.cpp new file mode 100644 index 000000000..dfda47316 --- /dev/null +++ b/dom/base/DOMException.cpp @@ -0,0 +1,646 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/DOMException.h" + +#include "jsprf.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/dom/Exceptions.h" +#include "nsContentUtils.h" +#include "nsCOMPtr.h" +#include "nsIClassInfoImpl.h" +#include "nsIDocument.h" +#include "nsIDOMDOMException.h" +#include "nsIException.h" +#include "nsIProgrammingLanguage.h" +#include "nsMemory.h" +#include "prprf.h" +#include "xpcprivate.h" + +#include "mozilla/dom/DOMExceptionBinding.h" +#include "mozilla/ErrorResult.h" + +using namespace mozilla; + +enum DOM4ErrorTypeCodeMap { + /* DOM4 errors from http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */ + IndexSizeError = nsIDOMDOMException::INDEX_SIZE_ERR, + HierarchyRequestError = nsIDOMDOMException::HIERARCHY_REQUEST_ERR, + WrongDocumentError = nsIDOMDOMException::WRONG_DOCUMENT_ERR, + InvalidCharacterError = nsIDOMDOMException::INVALID_CHARACTER_ERR, + NoModificationAllowedError = nsIDOMDOMException::NO_MODIFICATION_ALLOWED_ERR, + NotFoundError = nsIDOMDOMException::NOT_FOUND_ERR, + NotSupportedError = nsIDOMDOMException::NOT_SUPPORTED_ERR, + // Can't remove until setNamedItem is removed + InUseAttributeError = nsIDOMDOMException::INUSE_ATTRIBUTE_ERR, + InvalidStateError = nsIDOMDOMException::INVALID_STATE_ERR, + SyntaxError = nsIDOMDOMException::SYNTAX_ERR, + InvalidModificationError = nsIDOMDOMException::INVALID_MODIFICATION_ERR, + NamespaceError = nsIDOMDOMException::NAMESPACE_ERR, + InvalidAccessError = nsIDOMDOMException::INVALID_ACCESS_ERR, + TypeMismatchError = nsIDOMDOMException::TYPE_MISMATCH_ERR, + SecurityError = nsIDOMDOMException::SECURITY_ERR, + NetworkError = nsIDOMDOMException::NETWORK_ERR, + AbortError = nsIDOMDOMException::ABORT_ERR, + URLMismatchError = nsIDOMDOMException::URL_MISMATCH_ERR, + QuotaExceededError = nsIDOMDOMException::QUOTA_EXCEEDED_ERR, + TimeoutError = nsIDOMDOMException::TIMEOUT_ERR, + InvalidNodeTypeError = nsIDOMDOMException::INVALID_NODE_TYPE_ERR, + DataCloneError = nsIDOMDOMException::DATA_CLONE_ERR, + InvalidPointerId = nsIDOMDOMException::INVALID_POINTER_ERR, + EncodingError = 0, + + /* XXX Should be JavaScript native errors */ + TypeError = 0, + RangeError = 0, + + /* IndexedDB errors http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#exceptions */ + UnknownError = 0, + ConstraintError = 0, + DataError = 0, + TransactionInactiveError = 0, + ReadOnlyError = 0, + VersionError = 0, + + /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */ + NotReadableError = 0, + + /* FileHandle API errors */ + FileHandleInactiveError = 0, + + /* WebCrypto errors https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-DataError */ + OperationError = 0, + + /* Push API errors */ + NotAllowedError = 0, +}; + +#define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message}, +#define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message}, + +static const struct ResultStruct +{ + nsresult mNSResult; + uint16_t mCode; + const char* mName; + const char* mMessage; +} sDOMErrorMsgMap[] = { +#include "domerr.msg" +}; + +#undef DOM4_MSG_DEF +#undef DOM_MSG_DEF + +static void +NSResultToNameAndMessage(nsresult aNSResult, + nsCString& aName, + nsCString& aMessage, + uint16_t* aCode) +{ + aName.Truncate(); + aMessage.Truncate(); + *aCode = 0; + for (uint32_t idx = 0; idx < ArrayLength(sDOMErrorMsgMap); idx++) { + if (aNSResult == sDOMErrorMsgMap[idx].mNSResult) { + aName.Rebind(sDOMErrorMsgMap[idx].mName, + strlen(sDOMErrorMsgMap[idx].mName)); + aMessage.Rebind(sDOMErrorMsgMap[idx].mMessage, + strlen(sDOMErrorMsgMap[idx].mMessage)); + *aCode = sDOMErrorMsgMap[idx].mCode; + return; + } + } + + NS_WARNING("Huh, someone is throwing non-DOM errors using the DOM module!"); + + return; +} + +nsresult +NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult, nsACString& aName, + nsACString& aMessage, uint16_t* aCode) +{ + nsCString name; + nsCString message; + uint16_t code = 0; + NSResultToNameAndMessage(aNSResult, name, message, &code); + + if (!name.IsEmpty() && !message.IsEmpty()) { + aName = name; + aMessage = message; + if (aCode) { + *aCode = code; + } + return NS_OK; + } + + return NS_ERROR_NOT_AVAILABLE; +} + +namespace mozilla { +namespace dom { + +bool Exception::sEverMadeOneFromFactory = false; + +NS_IMPL_CLASSINFO(Exception, nullptr, nsIClassInfo::DOM_OBJECT, + NS_XPCEXCEPTION_CID) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Exception) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(Exception) + NS_INTERFACE_MAP_ENTRY(nsIException) + NS_INTERFACE_MAP_ENTRY(nsIXPCException) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIException) + NS_IMPL_QUERY_CLASSINFO(Exception) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(Exception) +NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception) + +NS_IMPL_CYCLE_COLLECTION_CLASS(Exception) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mThrownJSVal) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mData) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->mThrownJSVal.setNull(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CI_INTERFACE_GETTER(Exception, nsIXPCException) + +Exception::Exception(const nsACString& aMessage, + nsresult aResult, + const nsACString& aName, + nsIStackFrame *aLocation, + nsISupports *aData) +: mResult(NS_OK), + mInitialized(false), + mHoldingJSVal(false) +{ + // A little hack... The nsIGenericModule nsIClassInfo scheme relies on there + // having been at least one instance made via the factory. Otherwise, the + // shared factory/classinsance object never gets created and our QI getter + // for our instance's pointer to our nsIClassInfo will always return null. + // This is bad because it means that wrapped exceptions will never have a + // shared prototype. So... We force one to be created via the factory + // *once* and then go about our business. + if (!sEverMadeOneFromFactory) { + nsCOMPtr<nsIXPCException> e = + do_CreateInstance(XPC_EXCEPTION_CONTRACTID); + sEverMadeOneFromFactory = true; + } + + Initialize(aMessage, aResult, aName, aLocation, aData); +} + +Exception::Exception() + : mResult(NS_OK), + mInitialized(false), + mHoldingJSVal(false) +{ +} + +Exception::~Exception() +{ + if (mHoldingJSVal) { + MOZ_ASSERT(NS_IsMainThread()); + + mozilla::DropJSObjects(this); + } +} + +bool +Exception::StealJSVal(JS::Value* aVp) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mHoldingJSVal) { + *aVp = mThrownJSVal; + mThrownJSVal.setNull(); + + mozilla::DropJSObjects(this); + mHoldingJSVal = false; + return true; + } + + return false; +} + +void +Exception::StowJSVal(JS::Value& aVp) +{ + MOZ_ASSERT(NS_IsMainThread()); + + mThrownJSVal = aVp; + if (!mHoldingJSVal) { + mozilla::HoldJSObjects(this); + mHoldingJSVal = true; + } +} + +NS_IMETHODIMP +Exception::GetMessageMoz(nsACString& aMessage) +{ + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + aMessage.Assign(mMessage); + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetResult(nsresult* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + *aResult = mResult; + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetName(nsACString& aName) +{ + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + if (!mName.IsEmpty()) { + aName.Assign(mName); + } else { + aName.Truncate(); + + const char* name = nullptr; + nsXPCException::NameAndFormatForNSResult(mResult, &name, nullptr); + + if (name) { + aName.Assign(name); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetFilename(JSContext* aCx, nsAString& aFilename) +{ + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + if (mLocation) { + return mLocation->GetFilename(aCx, aFilename); + } + + aFilename.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetLineNumber(JSContext* aCx, uint32_t *aLineNumber) +{ + NS_ENSURE_ARG_POINTER(aLineNumber); + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + if (mLocation) { + int32_t lineno; + nsresult rv = mLocation->GetLineNumber(aCx, &lineno); + *aLineNumber = lineno; + return rv; + } + + *aLineNumber = 0; + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetColumnNumber(uint32_t* aColumnNumber) +{ + NS_ENSURE_ARG_POINTER(aColumnNumber); + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + *aColumnNumber = 0; + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetLocation(nsIStackFrame** aLocation) +{ + NS_ENSURE_ARG_POINTER(aLocation); + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + nsCOMPtr<nsIStackFrame> location = mLocation; + location.forget(aLocation); + return NS_OK; +} + +NS_IMETHODIMP +Exception::GetData(nsISupports** aData) +{ + NS_ENSURE_ARG_POINTER(aData); + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + nsCOMPtr<nsISupports> data = mData; + data.forget(aData); + return NS_OK; +} + +NS_IMETHODIMP +Exception::ToString(JSContext* aCx, nsACString& _retval) +{ + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + + static const char defaultMsg[] = "<no message>"; + static const char defaultLocation[] = "<unknown>"; + static const char format[] = +"[Exception... \"%s\" nsresult: \"0x%x (%s)\" location: \"%s\" data: %s]"; + + nsCString location; + + if (mLocation) { + // we need to free this if it does not fail + nsresult rv = mLocation->ToString(aCx, location); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (location.IsEmpty()) { + location.Assign(defaultLocation); + } + + const char* msg = mMessage.IsEmpty() ? nullptr : mMessage.get(); + + const char* resultName = mName.IsEmpty() ? nullptr: mName.get(); + if (!resultName && + !nsXPCException::NameAndFormatForNSResult(mResult, &resultName, + (!msg) ? &msg : nullptr)) { + if (!msg) { + msg = defaultMsg; + } + resultName = "<unknown>"; + } + const char* data = mData ? "yes" : "no"; + + _retval.Truncate(); + _retval.AppendPrintf(format, msg, mResult, resultName, + location.get(), data); + return NS_OK; +} + +NS_IMETHODIMP +Exception::Initialize(const nsACString& aMessage, nsresult aResult, + const nsACString& aName, nsIStackFrame *aLocation, + nsISupports *aData) +{ + NS_ENSURE_FALSE(mInitialized, NS_ERROR_ALREADY_INITIALIZED); + + mMessage = aMessage; + mName = aName; + mResult = aResult; + + if (aLocation) { + mLocation = aLocation; + } else { + mLocation = GetCurrentJSStack(); + // it is legal for there to be no active JS stack, if C++ code + // is operating on a JS-implemented interface pointer without + // having been called in turn by JS. This happens in the JS + // component loader. + } + + mData = aData; + + mInitialized = true; + return NS_OK; +} + +JSObject* +Exception::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) +{ + return ExceptionBinding::Wrap(cx, this, aGivenProto); +} + +void +Exception::GetMessageMoz(nsString& retval) +{ + nsCString str; +#ifdef DEBUG + DebugOnly<nsresult> rv = +#endif + GetMessageMoz(str); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + CopyUTF8toUTF16(str, retval); +} + +uint32_t +Exception::Result() const +{ + return (uint32_t)mResult; +} + +void +Exception::GetName(nsString& retval) +{ + nsCString str; +#ifdef DEBUG + DebugOnly<nsresult> rv = +#endif + GetName(str); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + CopyUTF8toUTF16(str, retval); +} + +uint32_t +Exception::LineNumber(JSContext* aCx) const +{ + if (mLocation) { + int32_t lineno; + if (NS_SUCCEEDED(mLocation->GetLineNumber(aCx, &lineno))) { + return lineno; + } + return 0; + } + + return 0; +} + +uint32_t +Exception::ColumnNumber() const +{ + return 0; +} + +already_AddRefed<nsIStackFrame> +Exception::GetLocation() const +{ + nsCOMPtr<nsIStackFrame> location = mLocation; + return location.forget(); +} + +already_AddRefed<nsISupports> +Exception::GetData() const +{ + nsCOMPtr<nsISupports> data = mData; + return data.forget(); +} + +void +Exception::GetStack(JSContext* aCx, nsAString& aStack, ErrorResult& aRv) const +{ + if (mLocation) { + aRv = mLocation->GetFormattedStack(aCx, aStack); + } +} + +void +Exception::Stringify(JSContext* aCx, nsString& retval) +{ + nsCString str; +#ifdef DEBUG + DebugOnly<nsresult> rv = +#endif + ToString(aCx, str); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + CopyUTF8toUTF16(str, retval); +} + +NS_IMPL_ADDREF_INHERITED(DOMException, Exception) +NS_IMPL_RELEASE_INHERITED(DOMException, Exception) +NS_INTERFACE_MAP_BEGIN(DOMException) + NS_INTERFACE_MAP_ENTRY(nsIDOMDOMException) +NS_INTERFACE_MAP_END_INHERITING(Exception) + +DOMException::DOMException(nsresult aRv, const nsACString& aMessage, + const nsACString& aName, uint16_t aCode) + : Exception(EmptyCString(), aRv, EmptyCString(), nullptr, nullptr), + mName(aName), + mMessage(aMessage), + mCode(aCode) +{ +} + +NS_IMETHODIMP +DOMException::GetCode(uint16_t* aCode) +{ + NS_ENSURE_ARG_POINTER(aCode); + *aCode = mCode; + + // Warn only when the code was changed (other than DOM Core) + // or the code is useless (zero) + if (NS_ERROR_GET_MODULE(mResult) != NS_ERROR_MODULE_DOM || !mCode) { + nsCOMPtr<nsIDocument> doc = nsContentUtils::GetDocumentFromCaller(); + if (doc) { + doc->WarnOnceAbout(nsIDocument::eDOMExceptionCode); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +DOMException::ToString(JSContext* aCx, nsACString& aReturn) +{ + aReturn.Truncate(); + + static const char defaultMsg[] = "<no message>"; + static const char defaultLocation[] = "<unknown>"; + static const char defaultName[] = "<unknown>"; + static const char format[] = + "[Exception... \"%s\" code: \"%d\" nsresult: \"0x%x (%s)\" location: \"%s\"]"; + + nsAutoCString location; + + if (location.IsEmpty()) { + location = defaultLocation; + } + + const char* msg = !mMessage.IsEmpty() ? mMessage.get() : defaultMsg; + const char* resultName = !mName.IsEmpty() ? mName.get() : defaultName; + + aReturn.AppendPrintf(format, msg, mCode, mResult, resultName, + location.get()); + + return NS_OK; +} + +void +DOMException::GetName(nsString& retval) +{ + CopyUTF8toUTF16(mName, retval); +} + +void +DOMException::GetMessageMoz(nsString& retval) +{ + CopyUTF8toUTF16(mMessage, retval); +} + +already_AddRefed<DOMException> +DOMException::Constructor(GlobalObject& /* unused */, + const nsAString& aMessage, + const Optional<nsAString>& aName, + ErrorResult& aError) +{ + nsresult exceptionResult = NS_OK; + uint16_t exceptionCode = 0; + nsCString name(NS_LITERAL_CSTRING("Error")); + + if (aName.WasPassed()) { + CopyUTF16toUTF8(aName.Value(), name); + for (uint32_t idx = 0; idx < ArrayLength(sDOMErrorMsgMap); idx++) { + if (name.EqualsASCII(sDOMErrorMsgMap[idx].mName)) { + exceptionResult = sDOMErrorMsgMap[idx].mNSResult; + exceptionCode = sDOMErrorMsgMap[idx].mCode; + break; + } + } + } + + RefPtr<DOMException> retval = + new DOMException(exceptionResult, + NS_ConvertUTF16toUTF8(aMessage), + name, + exceptionCode); + return retval.forget(); +} + +JSObject* +DOMException::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return DOMExceptionBinding::Wrap(aCx, this, aGivenProto); +} + +/* static */already_AddRefed<DOMException> +DOMException::Create(nsresult aRv) +{ + nsCString name; + nsCString message; + uint16_t code; + NSResultToNameAndMessage(aRv, name, message, &code); + RefPtr<DOMException> inst = + new DOMException(aRv, message, name, code); + return inst.forget(); +} + +/* static */already_AddRefed<DOMException> +DOMException::Create(nsresult aRv, const nsACString& aMessage) +{ + nsCString name; + nsCString message; + uint16_t code; + NSResultToNameAndMessage(aRv, name, message, &code); + RefPtr<DOMException> inst = + new DOMException(aRv, aMessage, name, code); + return inst.forget(); +} + +} // namespace dom +} // namespace mozilla |