1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * John Bandhauer <jband@netscape.com> (original author)
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /* Code for throwing errors into JavaScript. */
42 :
43 : #include "xpcprivate.h"
44 : #include "XPCWrapper.h"
45 :
46 : JSBool XPCThrower::sVerbose = true;
47 :
48 : // static
49 : void
50 17 : XPCThrower::Throw(nsresult rv, JSContext* cx)
51 : {
52 : const char* format;
53 17 : if (JS_IsExceptionPending(cx))
54 0 : return;
55 17 : if (!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
56 0 : format = "";
57 17 : BuildAndThrowException(cx, rv, format);
58 : }
59 :
60 : /*
61 : * If there has already been an exception thrown, see if we're throwing the
62 : * same sort of exception, and if we are, don't clobber the old one. ccx
63 : * should be the current call context.
64 : */
65 : // static
66 : JSBool
67 38278 : XPCThrower::CheckForPendingException(nsresult result, JSContext *cx)
68 : {
69 38278 : nsXPConnect* xpc = nsXPConnect::GetXPConnect();
70 38278 : if (!xpc)
71 0 : return false;
72 :
73 76556 : nsCOMPtr<nsIException> e;
74 38278 : xpc->GetPendingException(getter_AddRefs(e));
75 38278 : if (!e)
76 38017 : return false;
77 261 : xpc->SetPendingException(nsnull);
78 :
79 : nsresult e_result;
80 261 : if (NS_FAILED(e->GetResult(&e_result)) || e_result != result)
81 15 : return false;
82 :
83 246 : if (!ThrowExceptionObject(cx, e))
84 0 : JS_ReportOutOfMemory(cx);
85 246 : return true;
86 : }
87 :
88 : // static
89 : void
90 3 : XPCThrower::Throw(nsresult rv, XPCCallContext& ccx)
91 : {
92 : char* sz;
93 : const char* format;
94 :
95 3 : if (CheckForPendingException(rv, ccx))
96 0 : return;
97 :
98 3 : if (!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
99 0 : format = "";
100 :
101 3 : sz = (char*) format;
102 :
103 3 : if (sz && sVerbose)
104 3 : Verbosify(ccx, &sz, false);
105 :
106 3 : BuildAndThrowException(ccx, rv, sz);
107 :
108 3 : if (sz && sz != format)
109 3 : JS_smprintf_free(sz);
110 : }
111 :
112 :
113 : // static
114 : void
115 38046 : XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx)
116 : {
117 : char* sz;
118 : const char* format;
119 : const char* name;
120 :
121 : /*
122 : * If there is a pending exception when the native call returns and
123 : * it has the same error result as returned by the native call, then
124 : * the native call may be passing through an error from a previous JS
125 : * call. So we'll just throw that exception into our JS.
126 : */
127 :
128 38046 : if (CheckForPendingException(result, ccx))
129 246 : return;
130 :
131 : // else...
132 :
133 37800 : if (!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format) || !format)
134 0 : format = "";
135 :
136 37800 : if (nsXPCException::NameAndFormatForNSResult(result, &name, nsnull) && name)
137 37742 : sz = JS_smprintf("%s 0x%x (%s)", format, result, name);
138 : else
139 58 : sz = JS_smprintf("%s 0x%x", format, result);
140 :
141 37800 : if (sz && sVerbose)
142 37800 : Verbosify(ccx, &sz, true);
143 :
144 37800 : BuildAndThrowException(ccx, result, sz);
145 :
146 37800 : if (sz)
147 37800 : JS_smprintf_free(sz);
148 : }
149 :
150 : // static
151 : void
152 25 : XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx)
153 : {
154 : char* sz;
155 : const char* format;
156 :
157 25 : if (!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
158 0 : format = "";
159 :
160 25 : sz = JS_smprintf("%s arg %d", format, paramNum);
161 :
162 25 : if (sz && sVerbose)
163 25 : Verbosify(ccx, &sz, true);
164 :
165 25 : BuildAndThrowException(ccx, rv, sz);
166 :
167 25 : if (sz)
168 25 : JS_smprintf_free(sz);
169 25 : }
170 :
171 :
172 : // static
173 : void
174 37828 : XPCThrower::Verbosify(XPCCallContext& ccx,
175 : char** psz, bool own)
176 : {
177 37828 : char* sz = nsnull;
178 :
179 37828 : if (ccx.HasInterfaceAndMember()) {
180 37828 : XPCNativeInterface* iface = ccx.GetInterface();
181 37828 : jsid id = ccx.GetMember()->GetName();
182 75656 : JSAutoByteString bytes;
183 37828 : const char *name = JSID_IS_VOID(id) ? "Unknown" : bytes.encode(ccx, JSID_TO_STRING(id));
184 37828 : if (!name) {
185 0 : name = "";
186 : }
187 37828 : sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name);
188 : }
189 :
190 37828 : if (sz) {
191 37828 : if (own)
192 37825 : JS_smprintf_free(*psz);
193 37828 : *psz = sz;
194 : }
195 37828 : }
196 :
197 : // static
198 : void
199 38074 : XPCThrower::BuildAndThrowException(JSContext* cx, nsresult rv, const char* sz)
200 : {
201 38074 : JSBool success = false;
202 :
203 : /* no need to set an expection if the security manager already has */
204 38074 : if (rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO && JS_IsExceptionPending(cx))
205 0 : return;
206 76148 : nsCOMPtr<nsIException> finalException;
207 76148 : nsCOMPtr<nsIException> defaultException;
208 38074 : nsXPCException::NewException(sz, rv, nsnull, nsnull, getter_AddRefs(defaultException));
209 38074 : XPCPerThreadData* tls = XPCPerThreadData::GetData(cx);
210 38074 : if (tls) {
211 38074 : nsIExceptionManager * exceptionManager = tls->GetExceptionManager();
212 38074 : if (exceptionManager) {
213 : // Ask the provider for the exception, if there is no provider
214 : // we expect it to set e to null
215 : exceptionManager->GetExceptionFromProvider(rv,
216 : defaultException,
217 38074 : getter_AddRefs(finalException));
218 : // We should get at least the defaultException back,
219 : // but just in case
220 38074 : if (finalException == nsnull) {
221 9 : finalException = defaultException;
222 : }
223 : }
224 : }
225 : // XXX Should we put the following test and call to JS_ReportOutOfMemory
226 : // inside this test?
227 38074 : if (finalException)
228 38074 : success = ThrowExceptionObject(cx, finalException);
229 : // If we weren't able to build or throw an exception we're
230 : // most likely out of memory
231 38074 : if (!success)
232 0 : JS_ReportOutOfMemory(cx);
233 : }
234 :
235 : static bool
236 38320 : IsCallerChrome(JSContext* cx)
237 : {
238 : nsresult rv;
239 :
240 76640 : nsCOMPtr<nsIScriptSecurityManager> secMan;
241 38320 : if (XPCPerThreadData::IsMainThread(cx)) {
242 38320 : secMan = XPCWrapper::GetSecurityManager();
243 : } else {
244 0 : nsXPConnect* xpc = nsXPConnect::GetXPConnect();
245 0 : if (!xpc)
246 0 : return false;
247 :
248 0 : nsCOMPtr<nsIXPCSecurityManager> xpcSecMan;
249 0 : PRUint16 flags = 0;
250 0 : rv = xpc->GetSecurityManagerForJSContext(cx, getter_AddRefs(xpcSecMan),
251 0 : &flags);
252 0 : if (NS_FAILED(rv) || !xpcSecMan)
253 0 : return false;
254 :
255 0 : secMan = do_QueryInterface(xpcSecMan);
256 : }
257 :
258 38320 : if (!secMan)
259 0 : return false;
260 :
261 : bool isChrome;
262 38320 : rv = secMan->SubjectPrincipalIsSystem(&isChrome);
263 38320 : return NS_SUCCEEDED(rv) && isChrome;
264 : }
265 :
266 : // static
267 : JSBool
268 38320 : XPCThrower::ThrowExceptionObject(JSContext* cx, nsIException* e)
269 : {
270 38320 : JSBool success = false;
271 38320 : if (e) {
272 76640 : nsCOMPtr<nsIXPCException> xpcEx;
273 : jsval thrown;
274 : nsXPConnect* xpc;
275 :
276 : // If we stored the original thrown JS value in the exception
277 : // (see XPCConvert::ConstructException) and we are in a web
278 : // context (i.e., not chrome), rethrow the original value.
279 38320 : if (!IsCallerChrome(cx) &&
280 0 : (xpcEx = do_QueryInterface(e)) &&
281 0 : NS_SUCCEEDED(xpcEx->StealJSVal(&thrown))) {
282 0 : if (!JS_WrapValue(cx, &thrown))
283 0 : return false;
284 0 : JS_SetPendingException(cx, thrown);
285 0 : success = true;
286 38320 : } else if ((xpc = nsXPConnect::GetXPConnect())) {
287 38320 : JSObject* glob = JS_GetGlobalForScopeChain(cx);
288 38320 : if (!glob)
289 0 : return false;
290 :
291 76640 : nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
292 : nsresult rv = xpc->WrapNative(cx, glob, e,
293 : NS_GET_IID(nsIException),
294 38320 : getter_AddRefs(holder));
295 38320 : if (NS_SUCCEEDED(rv) && holder) {
296 : JSObject* obj;
297 38320 : if (NS_SUCCEEDED(holder->GetJSObject(&obj))) {
298 38320 : JS_SetPendingException(cx, OBJECT_TO_JSVAL(obj));
299 38320 : success = true;
300 : }
301 : }
302 : }
303 : }
304 38320 : return success;
305 : }
|