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 84154 : nsXPCException::NameAndFormatForNSResult(nsresult rv,
73 : const char** name,
74 : const char** format)
75 : {
76 :
77 3222952 : for (ResultMap* p = map; p->name; p++) {
78 3222668 : if (rv == p->rv) {
79 83870 : if (name) *name = p->name;
80 83870 : if (format) *format = p->format;
81 83870 : return true;
82 : }
83 : }
84 284 : return false;
85 : }
86 :
87 : // static
88 : void*
89 927843 : nsXPCException::IterateNSResults(nsresult* rv,
90 : const char** name,
91 : const char** format,
92 : void** iterp)
93 : {
94 927843 : ResultMap* p = (ResultMap*) *iterp;
95 927843 : if (!p)
96 5763 : p = map;
97 : else
98 922080 : p++;
99 927843 : if (!p->name)
100 5763 : p = nsnull;
101 : else {
102 922080 : if (rv)
103 922080 : *rv = p->rv;
104 922080 : if (name)
105 922080 : *name = p->name;
106 922080 : if (format)
107 0 : *format = p->format;
108 : }
109 927843 : *iterp = p;
110 927843 : 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 419390 : NS_INTERFACE_MAP_BEGIN(nsXPCException)
125 419390 : NS_INTERFACE_MAP_ENTRY(nsIException)
126 290735 : NS_INTERFACE_MAP_ENTRY(nsIXPCException)
127 274894 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIException)
128 236670 : NS_IMPL_QUERY_CLASSINFO(nsXPCException)
129 198446 : NS_INTERFACE_MAP_END_THREADSAFE
130 :
131 274197 : NS_IMPL_THREADSAFE_ADDREF(nsXPCException)
132 274197 : NS_IMPL_THREADSAFE_RELEASE(nsXPCException)
133 :
134 4216 : NS_IMPL_CI_INTERFACE_GETTER1(nsXPCException, nsIXPCException)
135 :
136 45911 : 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 45911 : mInitialized(false)
146 : {
147 45911 : MOZ_COUNT_CTOR(nsXPCException);
148 45911 : }
149 :
150 137733 : nsXPCException::~nsXPCException()
151 : {
152 45911 : MOZ_COUNT_DTOR(nsXPCException);
153 45911 : Reset();
154 183644 : }
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 6371 : nsXPCException::StowJSVal(JSContext* cx, jsval v)
170 : {
171 6371 : if (mThrownJSVal.Hold(cx)) {
172 6371 : mThrownJSVal = v;
173 6371 : return NS_OK;
174 : }
175 0 : return NS_ERROR_FAILURE;
176 : }
177 :
178 : void
179 90809 : nsXPCException::Reset()
180 : {
181 90809 : if (mMessage) {
182 44898 : nsMemory::Free(mMessage);
183 44898 : mMessage = nsnull;
184 : }
185 90809 : if (mName) {
186 0 : nsMemory::Free(mName);
187 0 : mName = nsnull;
188 : }
189 90809 : if (mFilename) {
190 40398 : nsMemory::Free(mFilename);
191 40398 : mFilename = nsnull;
192 : }
193 90809 : mLineNumber = (PRUint32)-1;
194 90809 : NS_IF_RELEASE(mLocation);
195 90809 : NS_IF_RELEASE(mData);
196 90809 : NS_IF_RELEASE(mInner);
197 90809 : }
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 13299 : nsXPCException::GetResult(nsresult *aResult)
211 : {
212 13299 : if (!aResult)
213 0 : return NS_ERROR_NULL_POINTER;
214 13299 : if (!mInitialized)
215 0 : return NS_ERROR_NOT_INITIALIZED;
216 13299 : *aResult = mResult;
217 13299 : 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 116 : nsXPCException::GetLocation(nsIStackFrame * *aLocation)
266 : {
267 116 : if (!aLocation)
268 0 : return NS_ERROR_NULL_POINTER;
269 116 : if (!mInitialized)
270 0 : return NS_ERROR_NOT_INITIALIZED;
271 116 : *aLocation = mLocation;
272 116 : NS_IF_ADDREF(mLocation);
273 116 : return NS_OK;
274 : }
275 :
276 : /* readonly attribute nsISupports data; */
277 : NS_IMETHODIMP
278 91 : nsXPCException::GetData(nsISupports * *aData)
279 : {
280 91 : if (!aData)
281 0 : return NS_ERROR_NULL_POINTER;
282 91 : if (!mInitialized)
283 0 : return NS_ERROR_NOT_INITIALIZED;
284 91 : *aData = mData;
285 91 : NS_IF_ADDREF(mData);
286 91 : 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 44898 : nsXPCException::Initialize(const char *aMessage, nsresult aResult, const char *aName, nsIStackFrame *aLocation, nsISupports *aData, nsIException *aInner)
305 : {
306 44898 : if (mInitialized)
307 0 : return NS_ERROR_ALREADY_INITIALIZED;
308 :
309 44898 : Reset();
310 :
311 44898 : if (aMessage) {
312 89796 : if (!(mMessage = (char*) nsMemory::Clone(aMessage,
313 89796 : sizeof(char)*(strlen(aMessage)+1))))
314 0 : return NS_ERROR_OUT_OF_MEMORY;
315 : }
316 :
317 44898 : 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 44898 : mResult = aResult;
324 :
325 44898 : if (aLocation) {
326 40415 : mLocation = aLocation;
327 40415 : 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 40415 : if (NS_FAILED(rc = aLocation->GetFilename(&mFilename)))
332 0 : return rc;
333 40415 : if (NS_FAILED(rc = aLocation->GetLineNumber(&mLineNumber)))
334 0 : return rc;
335 : } else {
336 : nsresult rv;
337 4483 : nsXPConnect* xpc = nsXPConnect::GetXPConnect();
338 4483 : if (!xpc)
339 0 : return NS_ERROR_FAILURE;
340 4483 : rv = xpc->GetCurrentJSStack(&mLocation);
341 4483 : if (NS_FAILED(rv))
342 0 : return rv;
343 : }
344 :
345 44898 : if (aData) {
346 257 : mData = aData;
347 257 : NS_ADDREF(mData);
348 : }
349 44898 : if (aInner) {
350 0 : mInner = aInner;
351 0 : NS_ADDREF(mInner);
352 : }
353 :
354 44898 : mInitialized = true;
355 44898 : return NS_OK;
356 : }
357 :
358 : /* string toString (); */
359 : NS_IMETHODIMP
360 1339 : nsXPCException::ToString(char **_retval)
361 : {
362 1339 : if (!_retval)
363 0 : return NS_ERROR_NULL_POINTER;
364 1339 : 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 1339 : char* indicatedLocation = nsnull;
373 :
374 1339 : if (mLocation) {
375 : // we need to free this if it does not fail
376 1195 : nsresult rv = mLocation->ToString(&indicatedLocation);
377 1195 : if (NS_FAILED(rv))
378 0 : return rv;
379 : }
380 :
381 1339 : const char* msg = mMessage ? mMessage : nsnull;
382 : const char* location = indicatedLocation ?
383 1339 : indicatedLocation : defaultLocation;
384 1339 : const char* resultName = mName;
385 2678 : if (!resultName && !NameAndFormatForNSResult(mResult, &resultName,
386 1339 : (!msg) ? &msg : nsnull)) {
387 0 : if (!msg)
388 0 : msg = defaultMsg;
389 0 : resultName = "<unknown>";
390 : }
391 1339 : const char* data = mData ? "yes" : "no";
392 :
393 1339 : char* temp = JS_smprintf(format, msg, mResult, resultName, location, data);
394 1339 : if (indicatedLocation)
395 1195 : nsMemory::Free(indicatedLocation);
396 :
397 1339 : char* final = nsnull;
398 1339 : if (temp) {
399 1339 : final = (char*) nsMemory::Clone(temp, sizeof(char)*(strlen(temp)+1));
400 1339 : JS_smprintf_free(temp);
401 : }
402 :
403 1339 : *_retval = final;
404 1339 : return final ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
405 : }
406 :
407 : JSBool nsXPCException::sEverMadeOneFromFactory = false;
408 :
409 : // static
410 : nsresult
411 44898 : 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 44898 : if (!sEverMadeOneFromFactory) {
425 : nsCOMPtr<nsIXPCException> e =
426 2026 : do_CreateInstance(XPC_EXCEPTION_CONTRACTID);
427 1013 : sEverMadeOneFromFactory = true;
428 : }
429 :
430 : nsresult rv;
431 44898 : nsXPCException* e = new nsXPCException();
432 44898 : if (e) {
433 44898 : NS_ADDREF(e);
434 :
435 : nsIStackFrame* location;
436 44898 : if (aLocation) {
437 10 : location = aLocation;
438 10 : NS_ADDREF(location);
439 : } else {
440 44888 : nsXPConnect* xpc = nsXPConnect::GetXPConnect();
441 44888 : if (!xpc) {
442 0 : NS_RELEASE(e);
443 0 : return NS_ERROR_FAILURE;
444 : }
445 44888 : rv = xpc->GetCurrentJSStack(&location);
446 44888 : 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 44898 : if (location)
458 0 : while (1) {
459 : PRUint32 language;
460 : PRInt32 lineNumber;
461 40432 : if (NS_FAILED(location->GetLanguage(&language)) ||
462 : language == nsIProgrammingLanguage::JAVASCRIPT ||
463 17 : NS_FAILED(location->GetLineNumber(&lineNumber)) ||
464 : lineNumber) {
465 40398 : break;
466 : }
467 34 : nsCOMPtr<nsIStackFrame> caller;
468 17 : if (NS_FAILED(location->GetCaller(getter_AddRefs(caller))) || !caller)
469 : break;
470 0 : NS_RELEASE(location);
471 17 : 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 44898 : rv = e->Initialize(aMessage, aResult, nsnull, location, aData, nsnull);
476 44898 : NS_IF_RELEASE(location);
477 44898 : if (NS_FAILED(rv))
478 0 : NS_RELEASE(e);
479 : }
480 :
481 44898 : if (!e)
482 0 : return NS_ERROR_FAILURE;
483 :
484 44898 : *exceptn = static_cast<nsIXPCException*>(e);
485 44898 : return NS_OK;
486 : }
|