1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/Exceptions.h"
11 #include "mozilla/CycleCollectedJSRuntime.h"
12 #include "mozilla/dom/BindingUtils.h"
13 #include "mozilla/dom/DOMException.h"
14 #include "mozilla/dom/ScriptSettings.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsThreadUtils.h"
18 #include "XPCWrapper.h"
19 #include "WorkerPrivate.h"
20 #include "nsContentUtils.h"
26 ThrowExceptionObject(JSContext
* aCx
, nsIException
* aException
)
28 // See if we really have an Exception.
29 nsCOMPtr
<Exception
> exception
= do_QueryInterface(aException
);
31 return ThrowExceptionObject(aCx
, exception
);
34 // We only have an nsIException (probably an XPCWrappedJS). Fall back on old
36 MOZ_ASSERT(NS_IsMainThread());
38 JS::Rooted
<JSObject
*> glob(aCx
, JS::CurrentGlobalOrNull(aCx
));
40 // XXXbz Can this really be null here?
44 JS::Rooted
<JS::Value
> val(aCx
);
45 if (!WrapObject(aCx
, aException
, &NS_GET_IID(nsIException
), &val
)) {
49 JS_SetPendingException(aCx
, val
);
55 ThrowExceptionObject(JSContext
* aCx
, Exception
* aException
)
57 JS::Rooted
<JS::Value
> thrown(aCx
);
59 // If we stored the original thrown JS value in the exception
60 // (see XPCConvert::ConstructException) and we are in a web context
61 // (i.e., not chrome), rethrow the original value. This only applies to JS
62 // implemented components so we only need to check for this on the main
64 if (NS_IsMainThread() && !nsContentUtils::IsCallerChrome() &&
65 aException
->StealJSVal(thrown
.address())) {
66 if (!JS_WrapValue(aCx
, &thrown
)) {
69 JS_SetPendingException(aCx
, thrown
);
73 JS::Rooted
<JSObject
*> glob(aCx
, JS::CurrentGlobalOrNull(aCx
));
75 // XXXbz Can this actually be null here?
79 if (!GetOrCreateDOMReflector(aCx
, aException
, &thrown
)) {
83 JS_SetPendingException(aCx
, thrown
);
88 Throw(JSContext
* aCx
, nsresult aRv
, const char* aMessage
)
90 if (JS_IsExceptionPending(aCx
)) {
91 // Don't clobber the existing exception.
95 CycleCollectedJSRuntime
* runtime
= CycleCollectedJSRuntime::Get();
96 nsCOMPtr
<nsIException
> existingException
= runtime
->GetPendingException();
97 if (existingException
) {
99 if (NS_SUCCEEDED(existingException
->GetResult(&nr
)) &&
101 // Reuse the existing exception.
103 // Clear pending exception
104 runtime
->SetPendingException(nullptr);
106 if (!ThrowExceptionObject(aCx
, existingException
)) {
107 // If we weren't able to throw an exception we're
108 // most likely out of memory
109 JS_ReportOutOfMemory(aCx
);
115 nsRefPtr
<Exception
> finalException
= CreateException(aCx
, aRv
, aMessage
);
117 MOZ_ASSERT(finalException
);
118 if (!ThrowExceptionObject(aCx
, finalException
)) {
119 // If we weren't able to throw an exception we're
120 // most likely out of memory
121 JS_ReportOutOfMemory(aCx
);
128 ThrowAndReport(nsPIDOMWindow
* aWindow
, nsresult aRv
, const char* aMessage
)
131 if (NS_WARN_IF(!jsapi
.InitWithLegacyErrorReporting(aWindow
))) {
135 Throw(jsapi
.cx(), aRv
, aMessage
);
136 (void) JS_ReportPendingException(jsapi
.cx());
139 already_AddRefed
<Exception
>
140 CreateException(JSContext
* aCx
, nsresult aRv
, const char* aMessage
)
142 // Do we use DOM exceptions for this error code?
143 switch (NS_ERROR_GET_MODULE(aRv
)) {
144 case NS_ERROR_MODULE_DOM
:
145 case NS_ERROR_MODULE_SVG
:
146 case NS_ERROR_MODULE_DOM_XPATH
:
147 case NS_ERROR_MODULE_DOM_INDEXEDDB
:
148 case NS_ERROR_MODULE_DOM_FILEHANDLE
:
149 case NS_ERROR_MODULE_DOM_BLUETOOTH
:
150 return DOMException::Create(aRv
);
155 // If not, use the default.
156 // aMessage can be null, so we can't use nsDependentCString on it.
157 nsRefPtr
<Exception
> exception
=
158 new Exception(nsCString(aMessage
), aRv
,
159 EmptyCString(), nullptr, nullptr);
160 return exception
.forget();
163 already_AddRefed
<nsIStackFrame
>
166 // is there a current context available?
167 JSContext
* cx
= nullptr;
169 if (NS_IsMainThread()) {
170 MOZ_ASSERT(nsContentUtils::XPConnect());
171 cx
= nsContentUtils::GetCurrentJSContext();
173 cx
= workers::GetCurrentThreadJSContext();
180 nsCOMPtr
<nsIStackFrame
> stack
= exceptions::CreateStack(cx
);
185 // Note that CreateStack only returns JS frames, so we're done here.
186 return stack
.forget();
189 namespace exceptions
{
191 class StackFrame
: public nsIStackFrame
194 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
195 NS_DECL_CYCLE_COLLECTION_CLASS(StackFrame
)
196 NS_DECL_NSISTACKFRAME
198 StackFrame(uint32_t aLanguage
,
199 const char* aFilename
,
200 const char* aFunctionName
,
202 nsIStackFrame
* aCaller
);
207 , mLanguage(nsIProgrammingLanguage::UNKNOWN
)
211 static already_AddRefed
<nsIStackFrame
>
212 CreateStackFrameLocation(uint32_t aLanguage
,
213 const char* aFilename
,
214 const char* aFunctionName
,
216 nsIStackFrame
* aCaller
);
218 virtual ~StackFrame();
220 virtual bool IsJSFrame() const
225 virtual nsresult
GetLineno(int32_t* aLineNo
)
231 virtual nsresult
GetColNo(int32_t* aColNo
)
237 nsCOMPtr
<nsIStackFrame
> mCaller
;
245 StackFrame::StackFrame(uint32_t aLanguage
,
246 const char* aFilename
,
247 const char* aFunctionName
,
249 nsIStackFrame
* aCaller
)
251 , mLineno(aLineNumber
)
252 , mLanguage(aLanguage
)
254 CopyUTF8toUTF16(aFilename
, mFilename
);
255 CopyUTF8toUTF16(aFunctionName
, mFunname
);
258 StackFrame::~StackFrame()
262 NS_IMPL_CYCLE_COLLECTION(StackFrame
, mCaller
)
263 NS_IMPL_CYCLE_COLLECTING_ADDREF(StackFrame
)
264 NS_IMPL_CYCLE_COLLECTING_RELEASE(StackFrame
)
266 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StackFrame
)
267 NS_INTERFACE_MAP_ENTRY(nsIStackFrame
)
268 NS_INTERFACE_MAP_ENTRY(nsISupports
)
271 class JSStackFrame
: public StackFrame
274 NS_DECL_ISUPPORTS_INHERITED
275 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSStackFrame
,
278 // aStack must not be null.
279 explicit JSStackFrame(JS::Handle
<JSObject
*> aStack
);
281 static already_AddRefed
<nsIStackFrame
>
282 CreateStack(JSContext
* aCx
, int32_t aMaxDepth
= -1);
284 NS_IMETHOD
GetLanguageName(nsACString
& aLanguageName
) MOZ_OVERRIDE
;
285 NS_IMETHOD
GetFilename(nsAString
& aFilename
) MOZ_OVERRIDE
;
286 NS_IMETHOD
GetName(nsAString
& aFunction
) MOZ_OVERRIDE
;
287 NS_IMETHOD
GetCaller(nsIStackFrame
** aCaller
) MOZ_OVERRIDE
;
288 NS_IMETHOD
GetFormattedStack(nsAString
& aStack
) MOZ_OVERRIDE
;
291 virtual bool IsJSFrame() const MOZ_OVERRIDE
{
295 virtual nsresult
GetLineno(int32_t* aLineNo
) MOZ_OVERRIDE
;
296 virtual nsresult
GetColNo(int32_t* aColNo
) MOZ_OVERRIDE
;
299 virtual ~JSStackFrame();
301 JS::Heap
<JSObject
*> mStack
;
302 nsString mFormattedStack
;
304 bool mFilenameInitialized
;
305 bool mFunnameInitialized
;
306 bool mLinenoInitialized
;
307 bool mColNoInitialized
;
308 bool mCallerInitialized
;
309 bool mFormattedStackInitialized
;
312 JSStackFrame::JSStackFrame(JS::Handle
<JSObject
*> aStack
)
314 , mFilenameInitialized(false)
315 , mFunnameInitialized(false)
316 , mLinenoInitialized(false)
317 , mColNoInitialized(false)
318 , mCallerInitialized(false)
319 , mFormattedStackInitialized(false)
323 mozilla::HoldJSObjects(this);
325 mLanguage
= nsIProgrammingLanguage::JAVASCRIPT
;
328 JSStackFrame::~JSStackFrame()
330 mozilla::DropJSObjects(this);
333 NS_IMPL_CYCLE_COLLECTION_CLASS(JSStackFrame
)
334 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(JSStackFrame
, StackFrame
)
335 tmp
->mStack
= nullptr;
336 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
337 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(JSStackFrame
, StackFrame
)
338 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
339 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
340 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSStackFrame
, StackFrame
)
341 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack
)
342 NS_IMPL_CYCLE_COLLECTION_TRACE_END
344 NS_IMPL_ADDREF_INHERITED(JSStackFrame
, StackFrame
)
345 NS_IMPL_RELEASE_INHERITED(JSStackFrame
, StackFrame
)
347 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(JSStackFrame
)
348 NS_INTERFACE_MAP_END_INHERITING(StackFrame
)
350 /* readonly attribute uint32_t language; */
351 NS_IMETHODIMP
StackFrame::GetLanguage(uint32_t* aLanguage
)
353 *aLanguage
= mLanguage
;
357 /* readonly attribute string languageName; */
358 NS_IMETHODIMP
StackFrame::GetLanguageName(nsACString
& aLanguageName
)
360 aLanguageName
.AssignLiteral("C++");
364 NS_IMETHODIMP
JSStackFrame::GetLanguageName(nsACString
& aLanguageName
)
366 aLanguageName
.AssignLiteral("JavaScript");
370 /* readonly attribute AString filename; */
371 NS_IMETHODIMP
JSStackFrame::GetFilename(nsAString
& aFilename
)
373 // We can get called after unlink; in that case we can't do much
374 // about producing a useful value.
375 if (!mFilenameInitialized
&& mStack
) {
376 ThreadsafeAutoJSContext cx
;
377 JS::Rooted
<JSObject
*> stack(cx
, mStack
);
378 JS::ExposeObjectToActiveJS(mStack
);
379 JSAutoCompartment
ac(cx
, stack
);
380 JS::Rooted
<JS::Value
> filenameVal(cx
);
381 if (!JS_GetProperty(cx
, stack
, "source", &filenameVal
) ||
382 !filenameVal
.isString()) {
383 return NS_ERROR_UNEXPECTED
;
386 if (!str
.init(cx
, filenameVal
.toString())) {
387 return NS_ERROR_OUT_OF_MEMORY
;
390 mFilenameInitialized
= true;
393 return StackFrame::GetFilename(aFilename
);
396 NS_IMETHODIMP
StackFrame::GetFilename(nsAString
& aFilename
)
398 // The filename must be set to null if empty.
399 if (mFilename
.IsEmpty()) {
400 aFilename
.SetIsVoid(true);
402 aFilename
.Assign(mFilename
);
408 /* readonly attribute AString name; */
409 NS_IMETHODIMP
JSStackFrame::GetName(nsAString
& aFunction
)
411 // We can get called after unlink; in that case we can't do much
412 // about producing a useful value.
413 if (!mFunnameInitialized
&& mStack
) {
414 ThreadsafeAutoJSContext cx
;
415 JS::Rooted
<JSObject
*> stack(cx
, mStack
);
416 JS::ExposeObjectToActiveJS(mStack
);
417 JSAutoCompartment
ac(cx
, stack
);
418 JS::Rooted
<JS::Value
> nameVal(cx
);
419 // functionDisplayName can be null
420 if (!JS_GetProperty(cx
, stack
, "functionDisplayName", &nameVal
) ||
421 (!nameVal
.isString() && !nameVal
.isNull())) {
422 return NS_ERROR_UNEXPECTED
;
424 if (nameVal
.isString()) {
426 if (!str
.init(cx
, nameVal
.toString())) {
427 return NS_ERROR_OUT_OF_MEMORY
;
431 mFunnameInitialized
= true;
434 return StackFrame::GetName(aFunction
);
437 NS_IMETHODIMP
StackFrame::GetName(nsAString
& aFunction
)
439 // The function name must be set to null if empty.
440 if (mFunname
.IsEmpty()) {
441 aFunction
.SetIsVoid(true);
443 aFunction
.Assign(mFunname
);
451 JSStackFrame::GetLineno(int32_t* aLineNo
)
453 // We can get called after unlink; in that case we can't do much
454 // about producing a useful value.
455 if (!mLinenoInitialized
&& mStack
) {
456 ThreadsafeAutoJSContext cx
;
457 JS::Rooted
<JSObject
*> stack(cx
, mStack
);
458 JS::ExposeObjectToActiveJS(mStack
);
459 JSAutoCompartment
ac(cx
, stack
);
460 JS::Rooted
<JS::Value
> lineVal(cx
);
461 if (!JS_GetProperty(cx
, stack
, "line", &lineVal
) ||
462 !lineVal
.isNumber()) {
463 return NS_ERROR_UNEXPECTED
;
465 mLineno
= lineVal
.toNumber();
466 mLinenoInitialized
= true;
469 return StackFrame::GetLineno(aLineNo
);
472 /* readonly attribute int32_t lineNumber; */
473 NS_IMETHODIMP
StackFrame::GetLineNumber(int32_t* aLineNumber
)
475 return GetLineno(aLineNumber
);
480 JSStackFrame::GetColNo(int32_t* aColNo
)
482 // We can get called after unlink; in that case we can't do much
483 // about producing a useful value.
484 if (!mColNoInitialized
&& mStack
) {
485 ThreadsafeAutoJSContext cx
;
486 JS::Rooted
<JSObject
*> stack(cx
, mStack
);
487 JS::ExposeObjectToActiveJS(mStack
);
488 JSAutoCompartment
ac(cx
, stack
);
489 JS::Rooted
<JS::Value
> colVal(cx
);
490 if (!JS_GetProperty(cx
, stack
, "column", &colVal
) ||
491 !colVal
.isNumber()) {
492 return NS_ERROR_UNEXPECTED
;
494 mColNo
= colVal
.toNumber();
495 mColNoInitialized
= true;
498 return StackFrame::GetColNo(aColNo
);
501 /* readonly attribute int32_t columnNumber; */
502 NS_IMETHODIMP
StackFrame::GetColumnNumber(int32_t* aColumnNumber
)
504 return GetColNo(aColumnNumber
);
507 /* readonly attribute AUTF8String sourceLine; */
508 NS_IMETHODIMP
StackFrame::GetSourceLine(nsACString
& aSourceLine
)
510 aSourceLine
.Truncate();
514 /* readonly attribute nsIStackFrame caller; */
515 NS_IMETHODIMP
JSStackFrame::GetCaller(nsIStackFrame
** aCaller
)
517 // We can get called after unlink; in that case we can't do much
518 // about producing a useful value.
519 if (!mCallerInitialized
&& mStack
) {
520 ThreadsafeAutoJSContext cx
;
521 JS::Rooted
<JSObject
*> stack(cx
, mStack
);
522 JS::ExposeObjectToActiveJS(mStack
);
523 JSAutoCompartment
ac(cx
, stack
);
524 JS::Rooted
<JS::Value
> callerVal(cx
);
525 if (!JS_GetProperty(cx
, stack
, "parent", &callerVal
) ||
526 !callerVal
.isObjectOrNull()) {
527 return NS_ERROR_UNEXPECTED
;
530 if (callerVal
.isObject()) {
531 JS::Rooted
<JSObject
*> caller(cx
, &callerVal
.toObject());
532 mCaller
= new JSStackFrame(caller
);
534 // Do we really need this dummy frame? If so, we should document why... I
535 // guess for symmetry with the "nothing on the stack" case, which returns
536 // a single dummy frame?
537 mCaller
= new StackFrame();
539 mCallerInitialized
= true;
541 return StackFrame::GetCaller(aCaller
);
544 NS_IMETHODIMP
StackFrame::GetCaller(nsIStackFrame
** aCaller
)
546 NS_IF_ADDREF(*aCaller
= mCaller
);
550 NS_IMETHODIMP
JSStackFrame::GetFormattedStack(nsAString
& aStack
)
552 // We can get called after unlink; in that case we can't do much
553 // about producing a useful value.
554 if (!mFormattedStackInitialized
&& mStack
) {
555 ThreadsafeAutoJSContext cx
;
556 JS::Rooted
<JS::Value
> stack(cx
, JS::ObjectValue(*mStack
));
557 JS::ExposeObjectToActiveJS(mStack
);
558 JSAutoCompartment
ac(cx
, mStack
);
559 JS::Rooted
<JSString
*> formattedStack(cx
, JS::ToString(cx
, stack
));
560 if (!formattedStack
) {
561 return NS_ERROR_UNEXPECTED
;
564 if (!str
.init(cx
, formattedStack
)) {
565 return NS_ERROR_OUT_OF_MEMORY
;
567 mFormattedStack
= str
;
568 mFormattedStackInitialized
= true;
571 aStack
= mFormattedStack
;
575 NS_IMETHODIMP
StackFrame::GetFormattedStack(nsAString
& aStack
)
581 /* AUTF8String toString (); */
582 NS_IMETHODIMP
StackFrame::ToString(nsACString
& _retval
)
586 const char* frametype
= IsJSFrame() ? "JS" : "native";
589 nsresult rv
= GetFilename(filename
);
590 NS_ENSURE_SUCCESS(rv
, rv
);
592 if (filename
.IsEmpty()) {
593 filename
.AssignLiteral("<unknown filename>");
597 rv
= GetName(funname
);
598 NS_ENSURE_SUCCESS(rv
, rv
);
600 if (funname
.IsEmpty()) {
601 funname
.AssignLiteral("<TOP_LEVEL>");
605 rv
= GetLineno(&lineno
);
606 NS_ENSURE_SUCCESS(rv
, rv
);
608 static const char format
[] = "%s frame :: %s :: %s :: line %d";
609 _retval
.AppendPrintf(format
, frametype
,
610 NS_ConvertUTF16toUTF8(filename
).get(),
611 NS_ConvertUTF16toUTF8(funname
).get(),
616 /* static */ already_AddRefed
<nsIStackFrame
>
617 JSStackFrame::CreateStack(JSContext
* aCx
, int32_t aMaxDepth
)
619 static const unsigned MAX_FRAMES
= 100;
621 aMaxDepth
= MAX_FRAMES
;
624 JS::Rooted
<JSObject
*> stack(aCx
);
625 if (!JS::CaptureCurrentStack(aCx
, &stack
, aMaxDepth
)) {
629 nsCOMPtr
<nsIStackFrame
> first
;
631 first
= new StackFrame();
633 first
= new JSStackFrame(stack
);
635 return first
.forget();
638 /* static */ already_AddRefed
<nsIStackFrame
>
639 StackFrame::CreateStackFrameLocation(uint32_t aLanguage
,
640 const char* aFilename
,
641 const char* aFunctionName
,
643 nsIStackFrame
* aCaller
)
645 nsRefPtr
<StackFrame
> self
=
646 new StackFrame(aLanguage
, aFilename
, aFunctionName
, aLineNumber
, aCaller
);
647 return self
.forget();
650 already_AddRefed
<nsIStackFrame
>
651 CreateStack(JSContext
* aCx
, int32_t aMaxDepth
)
653 return JSStackFrame::CreateStack(aCx
, aMaxDepth
);
656 already_AddRefed
<nsIStackFrame
>
657 CreateStackFrameLocation(uint32_t aLanguage
,
658 const char* aFilename
,
659 const char* aFunctionName
,
661 nsIStackFrame
* aCaller
)
663 return StackFrame::CreateStackFrameLocation(aLanguage
, aFilename
,
664 aFunctionName
, aLineNumber
,
668 } // namespace exceptions
670 } // namespace mozilla