Bug 1863873 - Block ability to perform audio decoding outside of Utility on release...
[gecko.git] / dom / bindings / nsScriptErrorWithStack.cpp
blob0ba6ab817fa5ca9704ae99e0f75461cefe3449b6
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/. */
7 /*
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;
25 namespace {
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)) {
31 return nsCString();
34 nsAutoJSString stackJSString;
35 if (!stackJSString.init(cx, formattedStack)) {
36 return nsCString();
39 return NS_ConvertUTF16toUTF8(stackJSString.get());
42 } // namespace
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)
68 NS_INTERFACE_MAP_END
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()) {
77 mHasException = true;
78 mException.set(*aException);
79 } else {
80 mHasException = false;
81 mException.setUndefined();
84 if (mStack) {
85 MOZ_ASSERT(JS_IsGlobalObject(mStackGlobal));
86 js::AssertSameCompartment(mStack, mStackGlobal);
87 } else {
88 MOZ_ASSERT(!mStackGlobal);
91 mozilla::HoldJSObjects(this);
94 nsScriptErrorWithStack::~nsScriptErrorWithStack() {
95 mozilla::DropJSObjects(this);
98 NS_IMETHODIMP
99 nsScriptErrorWithStack::GetHasException(bool* aHasException) {
100 *aHasException = mHasException;
101 return NS_OK;
104 NS_IMETHODIMP
105 nsScriptErrorWithStack::GetException(JS::MutableHandle<JS::Value> aException) {
106 aException.set(mException);
107 return NS_OK;
110 NS_IMETHODIMP
111 nsScriptErrorWithStack::GetStack(JS::MutableHandle<JS::Value> aStack) {
112 aStack.setObjectOrNull(mStack);
113 return NS_OK;
116 NS_IMETHODIMP
117 nsScriptErrorWithStack::GetStackGlobal(
118 JS::MutableHandle<JS::Value> aStackGlobal) {
119 aStackGlobal.setObjectOrNull(mStackGlobal);
120 return NS_OK;
123 NS_IMETHODIMP
124 nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult) {
125 MOZ_ASSERT(NS_IsMainThread());
127 nsCString message;
128 nsresult rv = nsScriptErrorBase::ToString(message);
129 NS_ENSURE_SUCCESS(rv, rv);
131 if (!mStack) {
132 aResult.Assign(message);
133 return NS_OK;
136 AutoJSAPI jsapi;
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);
150 return NS_OK;
153 static bool IsObjectGlobalDying(JSObject* aObj) {
154 // CCWs are not associated with a single global
155 if (js::IsCrossCompartmentWrapper(aObj)) {
156 return false;
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();