1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
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/. */
6
7 /* Code for throwing errors into JavaScript. */
8
9 #include "xpcprivate.h"
10 #include "XPCWrapper.h"
11 #include "js/Printf.h"
12 #include "mozilla/dom/BindingUtils.h"
13 #include "mozilla/dom/DOMException.h"
14 #include "mozilla/dom/Exceptions.h"
15 #include "nsString.h"
16
17 using namespace mozilla;
18 using namespace mozilla::dom;
19
20 bool XPCThrower::sVerbose = true;
21
22 // static
Throw(nsresult rv,JSContext * cx)23 void XPCThrower::Throw(nsresult rv, JSContext* cx) {
24 const char* format;
25 if (JS_IsExceptionPending(cx)) return;
26 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
27 format = "";
28 dom::Throw(cx, rv, nsDependentCString(format));
29 }
30
31 namespace xpc {
32
Throw(JSContext * cx,nsresult rv)33 bool Throw(JSContext* cx, nsresult rv) {
34 XPCThrower::Throw(rv, cx);
35 return false;
36 }
37
38 } // namespace xpc
39
40 /*
41 * If there has already been an exception thrown, see if we're throwing the
42 * same sort of exception, and if we are, don't clobber the old one. ccx
43 * should be the current call context.
44 */
45 // static
CheckForPendingException(nsresult result,JSContext * cx)46 bool XPCThrower::CheckForPendingException(nsresult result, JSContext* cx) {
47 RefPtr<Exception> e = XPCJSContext::Get()->GetPendingException();
48 if (!e) return false;
49 XPCJSContext::Get()->SetPendingException(nullptr);
50
51 if (e->GetResult() != result) return false;
52
53 ThrowExceptionObject(cx, e);
54 return true;
55 }
56
57 // static
Throw(nsresult rv,XPCCallContext & ccx)58 void XPCThrower::Throw(nsresult rv, XPCCallContext& ccx) {
59 char* sz;
60 const char* format;
61
62 if (CheckForPendingException(rv, ccx)) return;
63
64 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
65 format = "";
66
67 sz = (char*)format;
68 NS_ENSURE_TRUE_VOID(sz);
69
70 if (sz && sVerbose) Verbosify(ccx, &sz, false);
71
72 dom::Throw(ccx, rv, nsDependentCString(sz));
73
74 if (sz && sz != format) js_free(sz);
75 }
76
77 // static
ThrowBadResult(nsresult rv,nsresult result,XPCCallContext & ccx)78 void XPCThrower::ThrowBadResult(nsresult rv, nsresult result,
79 XPCCallContext& ccx) {
80 char* sz;
81 const char* format;
82 const char* name;
83
84 /*
85 * If there is a pending exception when the native call returns and
86 * it has the same error result as returned by the native call, then
87 * the native call may be passing through an error from a previous JS
88 * call. So we'll just throw that exception into our JS. Note that
89 * we don't need to worry about NS_ERROR_UNCATCHABLE_EXCEPTION,
90 * because presumably there would be no pending exception for that
91 * nsresult!
92 */
93
94 if (CheckForPendingException(result, ccx)) return;
95
96 // else...
97
98 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) ||
99 !format)
100 format = "";
101
102 if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) && name)
103 sz = JS_smprintf("%s 0x%x (%s)", format, (unsigned)result, name).release();
104 else
105 sz = JS_smprintf("%s 0x%x", format, (unsigned)result).release();
106 NS_ENSURE_TRUE_VOID(sz);
107
108 if (sz && sVerbose) Verbosify(ccx, &sz, true);
109
110 dom::Throw(ccx, result, nsDependentCString(sz));
111
112 if (sz) js_free(sz);
113 }
114
115 // static
ThrowBadParam(nsresult rv,unsigned paramNum,XPCCallContext & ccx)116 void XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum,
117 XPCCallContext& ccx) {
118 char* sz;
119 const char* format;
120
121 if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
122 format = "";
123
124 sz = JS_smprintf("%s arg %d", format, paramNum).release();
125 NS_ENSURE_TRUE_VOID(sz);
126
127 if (sz && sVerbose) Verbosify(ccx, &sz, true);
128
129 dom::Throw(ccx, rv, nsDependentCString(sz));
130
131 if (sz) js_free(sz);
132 }
133
134 // static
Verbosify(XPCCallContext & ccx,char ** psz,bool own)135 void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, bool own) {
136 char* sz = nullptr;
137
138 if (ccx.HasInterfaceAndMember()) {
139 XPCNativeInterface* iface = ccx.GetInterface();
140 jsid id = ccx.GetMember()->GetName();
141 JSAutoByteString bytes;
142 const char* name = JSID_IS_VOID(id)
143 ? "Unknown"
144 : bytes.encodeLatin1(ccx, JSID_TO_STRING(id));
145 if (!name) {
146 name = "";
147 }
148 sz =
149 JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release();
150 }
151
152 if (sz) {
153 if (own) js_free(*psz);
154 *psz = sz;
155 }
156 }
157