1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
8 * nsScriptErrorWithStack implementation.
9 * a main-thread-only, cycle-collected subclass of nsScriptErrorBase
10 * that can store a SavedFrame stack trace object.
13 #include "nsScriptError.h"
14 #include "MainThreadUtils.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/HoldDropJSObjects.h"
17 #include "mozilla/dom/ScriptSettings.h"
18 #include "js/Wrapper.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsGlobalWindowInner.h"
21 #include "nsJSUtils.h"
23 using namespace mozilla::dom
;
27 static nsCString
FormatStackString(JSContext
* cx
, JSPrincipals
* aPrincipals
,
28 JS::Handle
<JSObject
*> aStack
) {
29 JS::Rooted
<JSString
*> formattedStack(cx
);
30 if (!JS::BuildStackString(cx
, aPrincipals
, aStack
, &formattedStack
)) {
34 nsAutoJSString stackJSString
;
35 if (!stackJSString
.init(cx
, formattedStack
)) {
39 return NS_ConvertUTF16toUTF8(stackJSString
.get());
44 NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack
)
46 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack
)
47 tmp
->mException
.setUndefined();
48 tmp
->mStack
= nullptr;
49 tmp
->mStackGlobal
= nullptr;
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack
)
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
55 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack
)
56 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mException
)
57 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack
)
58 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStackGlobal
)
59 NS_IMPL_CYCLE_COLLECTION_TRACE_END
61 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack
)
62 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack
)
64 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack
)
65 NS_INTERFACE_MAP_ENTRY(nsISupports
)
66 NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage
)
67 NS_INTERFACE_MAP_ENTRY(nsIScriptError
)
70 nsScriptErrorWithStack::nsScriptErrorWithStack(
71 JS::Handle
<mozilla::Maybe
<JS::Value
>> aException
,
72 JS::Handle
<JSObject
*> aStack
, JS::Handle
<JSObject
*> aStackGlobal
)
73 : mStack(aStack
), mStackGlobal(aStackGlobal
) {
74 MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers.");
76 if (aException
.isSome()) {
78 mException
.set(*aException
);
80 mHasException
= false;
81 mException
.setUndefined();
85 MOZ_ASSERT(JS_IsGlobalObject(mStackGlobal
));
86 js::AssertSameCompartment(mStack
, mStackGlobal
);
88 MOZ_ASSERT(!mStackGlobal
);
91 mozilla::HoldJSObjects(this);
94 nsScriptErrorWithStack::~nsScriptErrorWithStack() {
95 mozilla::DropJSObjects(this);
99 nsScriptErrorWithStack::GetHasException(bool* aHasException
) {
100 *aHasException
= mHasException
;
105 nsScriptErrorWithStack::GetException(JS::MutableHandle
<JS::Value
> aException
) {
106 aException
.set(mException
);
111 nsScriptErrorWithStack::GetStack(JS::MutableHandle
<JS::Value
> aStack
) {
112 aStack
.setObjectOrNull(mStack
);
117 nsScriptErrorWithStack::GetStackGlobal(
118 JS::MutableHandle
<JS::Value
> aStackGlobal
) {
119 aStackGlobal
.setObjectOrNull(mStackGlobal
);
124 nsScriptErrorWithStack::ToString(nsACString
& /*UTF8*/ aResult
) {
125 MOZ_ASSERT(NS_IsMainThread());
128 nsresult rv
= nsScriptErrorBase::ToString(message
);
129 NS_ENSURE_SUCCESS(rv
, rv
);
132 aResult
.Assign(message
);
137 if (!jsapi
.Init(mStackGlobal
)) {
138 return NS_ERROR_FAILURE
;
141 JSPrincipals
* principals
=
142 JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(mStackGlobal
));
144 JSContext
* cx
= jsapi
.cx();
145 JS::Rooted
<JSObject
*> stack(cx
, mStack
);
146 nsCString stackString
= FormatStackString(cx
, principals
, stack
);
147 nsCString combined
= message
+ "\n"_ns
+ stackString
;
148 aResult
.Assign(combined
);
153 static bool IsObjectGlobalDying(JSObject
* aObj
) {
154 // CCWs are not associated with a single global
155 if (js::IsCrossCompartmentWrapper(aObj
)) {
159 nsGlobalWindowInner
* win
= xpc::WindowGlobalOrNull(aObj
);
160 return win
&& win
->IsDying();
163 already_AddRefed
<nsScriptErrorBase
> CreateScriptError(
164 nsGlobalWindowInner
* win
, JS::Handle
<mozilla::Maybe
<JS::Value
>> aException
,
165 JS::Handle
<JSObject
*> aStack
, JS::Handle
<JSObject
*> aStackGlobal
) {
166 bool createWithStack
= true;
167 if (aException
.isNothing() && !aStack
) {
168 // Neither stack nor exception, do not need nsScriptErrorWithStack.
169 createWithStack
= false;
170 } else if (win
&& (win
->IsDying() || !win
->WindowID())) {
171 // The window is already dying or we don't have a WindowID,
172 // this means nsConsoleService::ClearMessagesForWindowID
173 // would be unable to cleanup this error.
174 createWithStack
= false;
175 } else if ((aStackGlobal
&& IsObjectGlobalDying(aStackGlobal
)) ||
176 (aException
.isSome() && aException
.value().isObject() &&
177 IsObjectGlobalDying(&aException
.value().toObject()))) {
178 // Prevent leaks by not creating references to already dying globals.
179 createWithStack
= false;
182 if (!createWithStack
) {
183 RefPtr
<nsScriptErrorBase
> error
= new nsScriptError();
184 return error
.forget();
187 RefPtr
<nsScriptErrorBase
> error
=
188 new nsScriptErrorWithStack(aException
, aStack
, aStackGlobal
);
189 return error
.forget();