summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/XPCThrower.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/src/XPCThrower.cpp')
-rw-r--r--js/xpconnect/src/XPCThrower.cpp188
1 files changed, 188 insertions, 0 deletions
diff --git a/js/xpconnect/src/XPCThrower.cpp b/js/xpconnect/src/XPCThrower.cpp
new file mode 100644
index 0000000000..2e53cbda81
--- /dev/null
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -0,0 +1,188 @@
+/* -*- 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/. */
+
+/* Code for throwing errors into JavaScript. */
+
+#include "xpcprivate.h"
+#include "XPCWrapper.h"
+#include "js/CharacterEncoding.h"
+#include "js/Printf.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/Exceptions.h"
+#include "nsString.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+bool XPCThrower::sVerbose = true;
+
+// static
+void XPCThrower::Throw(nsresult rv, JSContext* cx) {
+ const char* format;
+ if (JS_IsExceptionPending(cx)) {
+ return;
+ }
+ if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) {
+ format = "";
+ }
+ dom::Throw(cx, rv, nsDependentCString(format));
+}
+
+namespace xpc {
+
+bool Throw(JSContext* cx, nsresult rv) {
+ XPCThrower::Throw(rv, cx);
+ return false;
+}
+
+} // namespace xpc
+
+/*
+ * If there has already been an exception thrown, see if we're throwing the
+ * same sort of exception, and if we are, don't clobber the old one. ccx
+ * should be the current call context.
+ */
+// static
+bool XPCThrower::CheckForPendingException(nsresult result, JSContext* cx) {
+ RefPtr<Exception> e = XPCJSContext::Get()->GetPendingException();
+ if (!e) {
+ return false;
+ }
+ XPCJSContext::Get()->SetPendingException(nullptr);
+
+ if (e->GetResult() != result) {
+ return false;
+ }
+
+ ThrowExceptionObject(cx, e);
+ return true;
+}
+
+// static
+void XPCThrower::Throw(nsresult rv, XPCCallContext& ccx) {
+ char* sz;
+ const char* format;
+
+ if (CheckForPendingException(rv, ccx)) {
+ return;
+ }
+
+ if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) {
+ format = "";
+ }
+
+ sz = (char*)format;
+ NS_ENSURE_TRUE_VOID(sz);
+
+ if (sz && sVerbose) {
+ Verbosify(ccx, &sz, false);
+ }
+
+ dom::Throw(ccx, rv, nsDependentCString(sz));
+
+ if (sz && sz != format) {
+ js_free(sz);
+ }
+}
+
+// static
+void XPCThrower::ThrowBadResult(nsresult rv, nsresult result,
+ XPCCallContext& ccx) {
+ char* sz;
+ const char* format;
+ const char* name;
+
+ /*
+ * If there is a pending exception when the native call returns and
+ * it has the same error result as returned by the native call, then
+ * the native call may be passing through an error from a previous JS
+ * call. So we'll just throw that exception into our JS. Note that
+ * we don't need to worry about NS_ERROR_UNCATCHABLE_EXCEPTION,
+ * because presumably there would be no pending exception for that
+ * nsresult!
+ */
+
+ if (CheckForPendingException(result, ccx)) {
+ return;
+ }
+
+ // else...
+
+ if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) ||
+ !format) {
+ format = "";
+ }
+
+ if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) &&
+ name) {
+ sz = JS_smprintf("%s 0x%x (%s)", format, (unsigned)result, name).release();
+ } else {
+ sz = JS_smprintf("%s 0x%x", format, (unsigned)result).release();
+ }
+ NS_ENSURE_TRUE_VOID(sz);
+
+ if (sz && sVerbose) {
+ Verbosify(ccx, &sz, true);
+ }
+
+ dom::Throw(ccx, result, nsDependentCString(sz));
+
+ if (sz) {
+ js_free(sz);
+ }
+}
+
+// static
+void XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum,
+ XPCCallContext& ccx) {
+ char* sz;
+ const char* format;
+
+ if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) {
+ format = "";
+ }
+
+ sz = JS_smprintf("%s arg %d", format, paramNum).release();
+ NS_ENSURE_TRUE_VOID(sz);
+
+ if (sz && sVerbose) {
+ Verbosify(ccx, &sz, true);
+ }
+
+ dom::Throw(ccx, rv, nsDependentCString(sz));
+
+ if (sz) {
+ js_free(sz);
+ }
+}
+
+// static
+void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, bool own) {
+ char* sz = nullptr;
+
+ if (ccx.HasInterfaceAndMember()) {
+ XPCNativeInterface* iface = ccx.GetInterface();
+ jsid id = ccx.GetMember()->GetName();
+ const char* name;
+ JS::UniqueChars bytes;
+ if (!JSID_IS_VOID(id)) {
+ bytes = JS_EncodeStringToLatin1(ccx, JSID_TO_STRING(id));
+ name = bytes ? bytes.get() : "";
+ } else {
+ name = "Unknown";
+ }
+ sz =
+ JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release();
+ }
+
+ if (sz) {
+ if (own) {
+ js_free(*psz);
+ }
+ *psz = sz;
+ }
+}