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