nsScriptErrorWithStack.cpp (5777B)
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/. */ 6 7 /* 8 * nsScriptErrorWithStack implementation. 9 * a main-thread-only, cycle-collected subclass of nsScriptErrorBase 10 * that can store a SavedFrame stack trace object. 11 */ 12 13 #include "MainThreadUtils.h" 14 #include "js/Wrapper.h" 15 #include "mozilla/Assertions.h" 16 #include "mozilla/HoldDropJSObjects.h" 17 #include "mozilla/dom/ScriptSettings.h" 18 #include "nsCycleCollectionParticipant.h" 19 #include "nsGlobalWindowInner.h" 20 #include "nsJSUtils.h" 21 #include "nsScriptError.h" 22 23 using namespace mozilla::dom; 24 25 namespace { 26 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(); 32 } 33 34 nsAutoJSString stackJSString; 35 if (!stackJSString.init(cx, formattedStack)) { 36 return nsCString(); 37 } 38 39 return NS_ConvertUTF16toUTF8(stackJSString.get()); 40 } 41 42 } // namespace 43 44 NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack) 45 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 51 52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack) 53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 54 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 60 61 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack) 62 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack) 63 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 69 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."); 75 76 if (aException.isSome()) { 77 mHasException = true; 78 mException.set(*aException); 79 } else { 80 mHasException = false; 81 mException.setUndefined(); 82 } 83 84 if (mStack) { 85 MOZ_ASSERT(JS_IsGlobalObject(mStackGlobal)); 86 js::AssertSameCompartment(mStack, mStackGlobal); 87 } else { 88 MOZ_ASSERT(!mStackGlobal); 89 } 90 91 mozilla::HoldJSObjects(this); 92 } 93 94 nsScriptErrorWithStack::~nsScriptErrorWithStack() { 95 mozilla::DropJSObjects(this); 96 } 97 98 NS_IMETHODIMP 99 nsScriptErrorWithStack::GetHasException(bool* aHasException) { 100 *aHasException = mHasException; 101 return NS_OK; 102 } 103 104 NS_IMETHODIMP 105 nsScriptErrorWithStack::GetException(JS::MutableHandle<JS::Value> aException) { 106 aException.set(mException); 107 return NS_OK; 108 } 109 110 NS_IMETHODIMP 111 nsScriptErrorWithStack::GetStack(JS::MutableHandle<JS::Value> aStack) { 112 aStack.setObjectOrNull(mStack); 113 return NS_OK; 114 } 115 116 NS_IMETHODIMP 117 nsScriptErrorWithStack::GetStackGlobal( 118 JS::MutableHandle<JS::Value> aStackGlobal) { 119 aStackGlobal.setObjectOrNull(mStackGlobal); 120 return NS_OK; 121 } 122 123 NS_IMETHODIMP 124 nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult) { 125 MOZ_ASSERT(NS_IsMainThread()); 126 127 nsCString message; 128 nsresult rv = nsScriptErrorBase::ToString(message); 129 NS_ENSURE_SUCCESS(rv, rv); 130 131 if (!mStack) { 132 aResult.Assign(message); 133 return NS_OK; 134 } 135 136 AutoJSAPI jsapi; 137 if (!jsapi.Init(mStackGlobal)) { 138 return NS_ERROR_FAILURE; 139 } 140 141 JSPrincipals* principals = 142 JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(mStackGlobal)); 143 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); 149 150 return NS_OK; 151 } 152 153 static bool IsObjectGlobalDying(JSObject* aObj) { 154 // CCWs are not associated with a single global 155 if (js::IsCrossCompartmentWrapper(aObj)) { 156 return false; 157 } 158 159 nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(aObj); 160 return win && win->IsDying(); 161 } 162 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; 180 } 181 182 if (!createWithStack) { 183 RefPtr<nsScriptErrorBase> error = new nsScriptError(); 184 return error.forget(); 185 } 186 187 RefPtr<nsScriptErrorBase> error = 188 new nsScriptErrorWithStack(aException, aStack, aStackGlobal); 189 return error.forget(); 190 }