michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Code for throwing errors into JavaScript. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "XPCWrapper.h" michael@0: #include "jsprf.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/Exceptions.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: bool XPCThrower::sVerbose = true; michael@0: michael@0: // static michael@0: void michael@0: XPCThrower::Throw(nsresult rv, JSContext* cx) michael@0: { michael@0: const char* format; michael@0: if (JS_IsExceptionPending(cx)) michael@0: return; michael@0: if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) michael@0: format = ""; michael@0: dom::Throw(cx, rv, format); michael@0: } michael@0: michael@0: namespace xpc { michael@0: michael@0: bool michael@0: Throw(JSContext *cx, nsresult rv) michael@0: { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: } // namespace xpc michael@0: michael@0: /* michael@0: * If there has already been an exception thrown, see if we're throwing the michael@0: * same sort of exception, and if we are, don't clobber the old one. ccx michael@0: * should be the current call context. michael@0: */ michael@0: // static michael@0: bool michael@0: XPCThrower::CheckForPendingException(nsresult result, JSContext *cx) michael@0: { michael@0: nsCOMPtr e = XPCJSRuntime::Get()->GetPendingException(); michael@0: if (!e) michael@0: return false; michael@0: XPCJSRuntime::Get()->SetPendingException(nullptr); michael@0: michael@0: nsresult e_result; michael@0: if (NS_FAILED(e->GetResult(&e_result)) || e_result != result) michael@0: return false; michael@0: michael@0: if (!ThrowExceptionObject(cx, e)) michael@0: JS_ReportOutOfMemory(cx); michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCThrower::Throw(nsresult rv, XPCCallContext& ccx) michael@0: { michael@0: char* sz; michael@0: const char* format; michael@0: michael@0: if (CheckForPendingException(rv, ccx)) michael@0: return; michael@0: michael@0: if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) michael@0: format = ""; michael@0: michael@0: sz = (char*) format; michael@0: michael@0: if (sz && sVerbose) michael@0: Verbosify(ccx, &sz, false); michael@0: michael@0: dom::Throw(ccx, rv, sz); michael@0: michael@0: if (sz && sz != format) michael@0: JS_smprintf_free(sz); michael@0: } michael@0: michael@0: michael@0: // static michael@0: void michael@0: XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx) michael@0: { michael@0: char* sz; michael@0: const char* format; michael@0: const char* name; michael@0: michael@0: /* michael@0: * If there is a pending exception when the native call returns and michael@0: * it has the same error result as returned by the native call, then michael@0: * the native call may be passing through an error from a previous JS michael@0: * call. So we'll just throw that exception into our JS. michael@0: */ michael@0: michael@0: if (CheckForPendingException(result, ccx)) michael@0: return; michael@0: michael@0: // else... michael@0: michael@0: if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) || !format) michael@0: format = ""; michael@0: michael@0: if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) && name) michael@0: sz = JS_smprintf("%s 0x%x (%s)", format, result, name); michael@0: else michael@0: sz = JS_smprintf("%s 0x%x", format, result); michael@0: michael@0: if (sz && sVerbose) michael@0: Verbosify(ccx, &sz, true); michael@0: michael@0: dom::Throw(ccx, result, sz); michael@0: michael@0: if (sz) michael@0: JS_smprintf_free(sz); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx) michael@0: { michael@0: char* sz; michael@0: const char* format; michael@0: michael@0: if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) michael@0: format = ""; michael@0: michael@0: sz = JS_smprintf("%s arg %d", format, paramNum); michael@0: michael@0: if (sz && sVerbose) michael@0: Verbosify(ccx, &sz, true); michael@0: michael@0: dom::Throw(ccx, rv, sz); michael@0: michael@0: if (sz) michael@0: JS_smprintf_free(sz); michael@0: } michael@0: michael@0: michael@0: // static michael@0: void michael@0: XPCThrower::Verbosify(XPCCallContext& ccx, michael@0: char** psz, bool own) michael@0: { michael@0: char* sz = nullptr; michael@0: michael@0: if (ccx.HasInterfaceAndMember()) { michael@0: XPCNativeInterface* iface = ccx.GetInterface(); michael@0: jsid id = ccx.GetMember()->GetName(); michael@0: JSAutoByteString bytes; michael@0: const char *name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id)); michael@0: if (!name) { michael@0: name = ""; michael@0: } michael@0: sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); michael@0: } michael@0: michael@0: if (sz) { michael@0: if (own) michael@0: JS_smprintf_free(*psz); michael@0: *psz = sz; michael@0: } michael@0: }