tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }