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) 1999
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * John Bandhauer <jband@netscape.com> (original author)
26 : * Mark Hammond <MarkH@ActiveState.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : /* An implementaion of nsIException. */
43 :
44 : #include "xpcprivate.h"
45 : #include "nsNetError.h"
46 : #include "mozStorage.h"
47 : #include "nsPluginError.h"
48 : #include "nsIUnicodeDecoder.h"
49 :
50 : /***************************************************************************/
51 : /* Quick and dirty mapping of well known result codes to strings. We only
52 : * call this when building an exception object, so iterating the short array
53 : * is not too bad.
54 : *
55 : * It sure would be nice to have exceptions declared in idl and available
56 : * in some more global way at runtime.
57 : */
58 :
59 : static struct ResultMap
60 : {nsresult rv; const char* name; const char* format;} map[] = {
61 : #define XPC_MSG_DEF(val, format) \
62 : {(val), #val, format},
63 : #include "xpc.msg"
64 : #undef XPC_MSG_DEF
65 : {0,0,0} // sentinel to mark end of array
66 : };
67 :
68 : #define RESULT_COUNT ((sizeof(map) / sizeof(map[0]))-1)
69 :
70 : // static
71 : JSBool
72 45477 : nsXPCException::NameAndFormatForNSResult(nsresult rv,
73 : const char** name,
74 : const char** format)
75 : {
76 :
77 1934865 : for (ResultMap* p = map; p->name; p++) {
78 1934581 : if (rv == p->rv) {
79 45193 : if (name) *name = p->name;
80 45193 : if (format) *format = p->format;
81 45193 : return true;
82 : }
83 : }
84 284 : return false;
85 : }
86 :
87 : // static
88 : void*
89 921725 : nsXPCException::IterateNSResults(nsresult* rv,
90 : const char** name,
91 : const char** format,
92 : void** iterp)
93 : {
94 921725 : ResultMap* p = (ResultMap*) *iterp;
95 921725 : if (!p)
96 5725 : p = map;
97 : else
98 916000 : p++;
99 921725 : if (!p->name)
100 5725 : p = nsnull;
101 : else {
102 916000 : if (rv)
103 916000 : *rv = p->rv;
104 916000 : if (name)
105 916000 : *name = p->name;
106 916000 : if (format)
107 0 : *format = p->format;
108 : }
109 921725 : *iterp = p;
110 921725 : return p;
111 : }
112 :
113 : // static
114 : PRUint32
115 0 : nsXPCException::GetNSResultCount()
116 : {
117 0 : return RESULT_COUNT;
118 : }
119 :
120 : /***************************************************************************/
121 :
122 : NS_IMPL_CLASSINFO(nsXPCException, NULL, nsIClassInfo::DOM_OBJECT,
123 : NS_XPCEXCEPTION_CID)
124 226256 : NS_INTERFACE_MAP_BEGIN(nsXPCException)
125 226256 : NS_INTERFACE_MAP_ENTRY(nsIException)
126 155656 : NS_INTERFACE_MAP_ENTRY(nsIXPCException)
127 139936 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIException)
128 120993 : NS_IMPL_QUERY_CLASSINFO(nsXPCException)
129 102050 : NS_INTERFACE_MAP_END_THREADSAFE
130 :
131 157966 : NS_IMPL_THREADSAFE_ADDREF(nsXPCException)
132 157966 : NS_IMPL_THREADSAFE_RELEASE(nsXPCException)
133 :
134 4234 : NS_IMPL_CI_INTERFACE_GETTER1(nsXPCException, nsIXPCException)
135 :
136 26521 : nsXPCException::nsXPCException()
137 : : mMessage(nsnull),
138 : mResult(0),
139 : mName(nsnull),
140 : mLocation(nsnull),
141 : mData(nsnull),
142 : mFilename(nsnull),
143 : mLineNumber(0),
144 : mInner(nsnull),
145 26521 : mInitialized(false)
146 : {
147 26521 : MOZ_COUNT_CTOR(nsXPCException);
148 26521 : }
149 :
150 79563 : nsXPCException::~nsXPCException()
151 : {
152 26521 : MOZ_COUNT_DTOR(nsXPCException);
153 26521 : Reset();
154 106084 : }
155 :
156 : /* [noscript] xpcexJSVal stealJSVal (); */
157 : NS_IMETHODIMP
158 0 : nsXPCException::StealJSVal(jsval *vp NS_OUTPARAM)
159 : {
160 0 : if (mThrownJSVal.IsHeld()) {
161 0 : *vp = mThrownJSVal.Release();
162 0 : return NS_OK;
163 : }
164 0 : return NS_ERROR_FAILURE;
165 : }
166 :
167 : /* [noscript] void stowJSVal (in xpcexJSContextPtr cx, in xpcexJSVal val); */
168 : NS_IMETHODIMP
169 6249 : nsXPCException::StowJSVal(JSContext* cx, jsval v)
170 : {
171 6249 : if (mThrownJSVal.Hold(cx)) {
172 6249 : mThrownJSVal = v;
173 6249 : return NS_OK;
174 : }
175 0 : return NS_ERROR_FAILURE;
176 : }
177 :
178 : void
179 52033 : nsXPCException::Reset()
180 : {
181 52033 : if (mMessage) {
182 25512 : nsMemory::Free(mMessage);
183 25512 : mMessage = nsnull;
184 : }
185 52033 : if (mName) {
186 0 : nsMemory::Free(mName);
187 0 : mName = nsnull;
188 : }
189 52033 : if (mFilename) {
190 21117 : nsMemory::Free(mFilename);
191 21117 : mFilename = nsnull;
192 : }
193 52033 : mLineNumber = (PRUint32)-1;
194 52033 : NS_IF_RELEASE(mLocation);
195 52033 : NS_IF_RELEASE(mData);
196 52033 : NS_IF_RELEASE(mInner);
197 52033 : }
198 :
199 : /* readonly attribute string message; */
200 : NS_IMETHODIMP
201 81 : nsXPCException::GetMessageMoz(char * *aMessage)
202 : {
203 81 : if (!mInitialized)
204 0 : return NS_ERROR_NOT_INITIALIZED;
205 81 : XPC_STRING_GETTER_BODY(aMessage, mMessage);
206 : }
207 :
208 : /* readonly attribute nsresult result; */
209 : NS_IMETHODIMP
210 13209 : nsXPCException::GetResult(nsresult *aResult)
211 : {
212 13209 : if (!aResult)
213 0 : return NS_ERROR_NULL_POINTER;
214 13209 : if (!mInitialized)
215 0 : return NS_ERROR_NOT_INITIALIZED;
216 13209 : *aResult = mResult;
217 13209 : return NS_OK;
218 : }
219 :
220 : /* readonly attribute string name; */
221 : NS_IMETHODIMP
222 332 : nsXPCException::GetName(char * *aName)
223 : {
224 332 : if (!mInitialized)
225 0 : return NS_ERROR_NOT_INITIALIZED;
226 :
227 332 : const char* name = mName;
228 332 : if (!name)
229 332 : NameAndFormatForNSResult(mResult, &name, nsnull);
230 :
231 332 : XPC_STRING_GETTER_BODY(aName, name);
232 : }
233 :
234 : /* readonly attribute string filename; */
235 24 : NS_IMETHODIMP nsXPCException::GetFilename(char * *aFilename)
236 : {
237 24 : if (!mInitialized)
238 0 : return NS_ERROR_NOT_INITIALIZED;
239 24 : XPC_STRING_GETTER_BODY(aFilename, mFilename);
240 : }
241 :
242 : /* readonly attribute PRUint32 lineNumber; */
243 24 : NS_IMETHODIMP nsXPCException::GetLineNumber(PRUint32 *aLineNumber)
244 : {
245 24 : if (!aLineNumber)
246 0 : return NS_ERROR_NULL_POINTER;
247 24 : if (!mInitialized)
248 0 : return NS_ERROR_NOT_INITIALIZED;
249 24 : *aLineNumber = mLineNumber;
250 24 : return NS_OK;
251 : }
252 :
253 : /* readonly attribute PRUint32 columnNumber; */
254 0 : NS_IMETHODIMP nsXPCException::GetColumnNumber(PRUint32 *aColumnNumber)
255 : {
256 0 : NS_ENSURE_ARG_POINTER(aColumnNumber);
257 0 : if (!mInitialized)
258 0 : return NS_ERROR_NOT_INITIALIZED;
259 0 : *aColumnNumber = 0;
260 0 : return NS_OK;
261 : }
262 :
263 : /* readonly attribute nsIStackFrame location; */
264 : NS_IMETHODIMP
265 120 : nsXPCException::GetLocation(nsIStackFrame * *aLocation)
266 : {
267 120 : if (!aLocation)
268 0 : return NS_ERROR_NULL_POINTER;
269 120 : if (!mInitialized)
270 0 : return NS_ERROR_NOT_INITIALIZED;
271 120 : *aLocation = mLocation;
272 120 : NS_IF_ADDREF(mLocation);
273 120 : return NS_OK;
274 : }
275 :
276 : /* readonly attribute nsISupports data; */
277 : NS_IMETHODIMP
278 95 : nsXPCException::GetData(nsISupports * *aData)
279 : {
280 95 : if (!aData)
281 0 : return NS_ERROR_NULL_POINTER;
282 95 : if (!mInitialized)
283 0 : return NS_ERROR_NOT_INITIALIZED;
284 95 : *aData = mData;
285 95 : NS_IF_ADDREF(mData);
286 95 : return NS_OK;
287 : }
288 :
289 : /* readonly attribute nsIException inner; */
290 : NS_IMETHODIMP
291 0 : nsXPCException::GetInner(nsIException* *aException)
292 : {
293 0 : if (!aException)
294 0 : return NS_ERROR_NULL_POINTER;
295 0 : if (!mInitialized)
296 0 : return NS_ERROR_NOT_INITIALIZED;
297 0 : *aException = mInner;
298 0 : NS_IF_ADDREF(mInner);
299 0 : return NS_OK;
300 : }
301 :
302 : /* void initialize (in string aMessage, in nsresult aResult, in string aName, in nsIStackFrame aLocation, in nsISupports aData, in nsIException aInner); */
303 : NS_IMETHODIMP
304 25512 : nsXPCException::Initialize(const char *aMessage, nsresult aResult, const char *aName, nsIStackFrame *aLocation, nsISupports *aData, nsIException *aInner)
305 : {
306 25512 : if (mInitialized)
307 0 : return NS_ERROR_ALREADY_INITIALIZED;
308 :
309 25512 : Reset();
310 :
311 25512 : if (aMessage) {
312 51024 : if (!(mMessage = (char*) nsMemory::Clone(aMessage,
313 51024 : sizeof(char)*(strlen(aMessage)+1))))
314 0 : return NS_ERROR_OUT_OF_MEMORY;
315 : }
316 :
317 25512 : if (aName) {
318 0 : if (!(mName = (char*) nsMemory::Clone(aName,
319 0 : sizeof(char)*(strlen(aName)+1))))
320 0 : return NS_ERROR_OUT_OF_MEMORY;
321 : }
322 :
323 25512 : mResult = aResult;
324 :
325 25512 : if (aLocation) {
326 21136 : mLocation = aLocation;
327 21136 : NS_ADDREF(mLocation);
328 : // For now, fill in our location details from our stack frame.
329 : // Later we may allow other locations?
330 : nsresult rc;
331 21136 : if (NS_FAILED(rc = aLocation->GetFilename(&mFilename)))
332 0 : return rc;
333 21136 : if (NS_FAILED(rc = aLocation->GetLineNumber(&mLineNumber)))
334 0 : return rc;
335 : } else {
336 : nsresult rv;
337 4376 : nsXPConnect* xpc = nsXPConnect::GetXPConnect();
338 4376 : if (!xpc)
339 0 : return NS_ERROR_FAILURE;
340 4376 : rv = xpc->GetCurrentJSStack(&mLocation);
341 4376 : if (NS_FAILED(rv))
342 0 : return rv;
343 : }
344 :
345 25512 : if (aData) {
346 270 : mData = aData;
347 270 : NS_ADDREF(mData);
348 : }
349 25512 : if (aInner) {
350 0 : mInner = aInner;
351 0 : NS_ADDREF(mInner);
352 : }
353 :
354 25512 : mInitialized = true;
355 25512 : return NS_OK;
356 : }
357 :
358 : /* string toString (); */
359 : NS_IMETHODIMP
360 1341 : nsXPCException::ToString(char **_retval)
361 : {
362 1341 : if (!_retval)
363 0 : return NS_ERROR_NULL_POINTER;
364 1341 : if (!mInitialized)
365 0 : return NS_ERROR_NOT_INITIALIZED;
366 :
367 : static const char defaultMsg[] = "<no message>";
368 : static const char defaultLocation[] = "<unknown>";
369 : static const char format[] =
370 : "[Exception... \"%s\" nsresult: \"0x%x (%s)\" location: \"%s\" data: %s]";
371 :
372 1341 : char* indicatedLocation = nsnull;
373 :
374 1341 : if (mLocation) {
375 : // we need to free this if it does not fail
376 1191 : nsresult rv = mLocation->ToString(&indicatedLocation);
377 1191 : if (NS_FAILED(rv))
378 0 : return rv;
379 : }
380 :
381 1341 : const char* msg = mMessage ? mMessage : nsnull;
382 : const char* location = indicatedLocation ?
383 1341 : indicatedLocation : defaultLocation;
384 1341 : const char* resultName = mName;
385 2682 : if (!resultName && !NameAndFormatForNSResult(mResult, &resultName,
386 1341 : (!msg) ? &msg : nsnull)) {
387 0 : if (!msg)
388 0 : msg = defaultMsg;
389 0 : resultName = "<unknown>";
390 : }
391 1341 : const char* data = mData ? "yes" : "no";
392 :
393 1341 : char* temp = JS_smprintf(format, msg, mResult, resultName, location, data);
394 1341 : if (indicatedLocation)
395 1191 : nsMemory::Free(indicatedLocation);
396 :
397 1341 : char* final = nsnull;
398 1341 : if (temp) {
399 1341 : final = (char*) nsMemory::Clone(temp, sizeof(char)*(strlen(temp)+1));
400 1341 : JS_smprintf_free(temp);
401 : }
402 :
403 1341 : *_retval = final;
404 1341 : return final ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
405 : }
406 :
407 : JSBool nsXPCException::sEverMadeOneFromFactory = false;
408 :
409 : // static
410 : nsresult
411 25512 : nsXPCException::NewException(const char *aMessage,
412 : nsresult aResult,
413 : nsIStackFrame *aLocation,
414 : nsISupports *aData,
415 : nsIException** exceptn)
416 : {
417 : // A little hack... The nsIGenericModule nsIClassInfo scheme relies on there
418 : // having been at least one instance made via the factory. Otherwise, the
419 : // shared factory/classinsance object never gets created and our QI getter
420 : // for our instance's pointer to our nsIClassInfo will always return null.
421 : // This is bad because it means that wrapped exceptions will never have a
422 : // shared prototype. So... We force one to be created via the factory
423 : // *once* and then go about our business.
424 25512 : if (!sEverMadeOneFromFactory) {
425 : nsCOMPtr<nsIXPCException> e =
426 2018 : do_CreateInstance(XPC_EXCEPTION_CONTRACTID);
427 1009 : sEverMadeOneFromFactory = true;
428 : }
429 :
430 : nsresult rv;
431 25512 : nsXPCException* e = new nsXPCException();
432 25512 : if (e) {
433 25512 : NS_ADDREF(e);
434 :
435 : nsIStackFrame* location;
436 25512 : if (aLocation) {
437 10 : location = aLocation;
438 10 : NS_ADDREF(location);
439 : } else {
440 25502 : nsXPConnect* xpc = nsXPConnect::GetXPConnect();
441 25502 : if (!xpc) {
442 0 : NS_RELEASE(e);
443 0 : return NS_ERROR_FAILURE;
444 : }
445 25502 : rv = xpc->GetCurrentJSStack(&location);
446 25502 : if (NS_FAILED(rv)) {
447 0 : NS_RELEASE(e);
448 0 : return NS_ERROR_FAILURE;
449 : }
450 : // it is legal for there to be no active JS stack, if C++ code
451 : // is operating on a JS-implemented interface pointer without
452 : // having been called in turn by JS. This happens in the JS
453 : // component loader, and will become more common as additional
454 : // components are implemented in JS.
455 : }
456 : // We want to trim off any leading native 'dataless' frames
457 25512 : if (location)
458 0 : while (1) {
459 : PRUint32 language;
460 : PRInt32 lineNumber;
461 21155 : if (NS_FAILED(location->GetLanguage(&language)) ||
462 : language == nsIProgrammingLanguage::JAVASCRIPT ||
463 19 : NS_FAILED(location->GetLineNumber(&lineNumber)) ||
464 : lineNumber) {
465 21117 : break;
466 : }
467 38 : nsCOMPtr<nsIStackFrame> caller;
468 19 : if (NS_FAILED(location->GetCaller(getter_AddRefs(caller))) || !caller)
469 : break;
470 0 : NS_RELEASE(location);
471 19 : caller->QueryInterface(NS_GET_IID(nsIStackFrame), (void **)&location);
472 : }
473 : // at this point we have non-null location with one extra addref,
474 : // or no location at all
475 25512 : rv = e->Initialize(aMessage, aResult, nsnull, location, aData, nsnull);
476 25512 : NS_IF_RELEASE(location);
477 25512 : if (NS_FAILED(rv))
478 0 : NS_RELEASE(e);
479 : }
480 :
481 25512 : if (!e)
482 0 : return NS_ERROR_FAILURE;
483 :
484 25512 : *exceptn = static_cast<nsIXPCException*>(e);
485 25512 : return NS_OK;
486 : }
|