tor-browser

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

ScriptSettings.cpp (24001B)


      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 #include "mozilla/dom/ScriptSettings.h"
      8 
      9 #include "MainThreadUtils.h"
     10 #include "js/CharacterEncoding.h"
     11 #include "js/CompilationAndEvaluation.h"
     12 #include "js/Conversions.h"
     13 #include "js/ErrorReport.h"
     14 #include "js/Exception.h"
     15 #include "js/GCAPI.h"
     16 #include "js/PropertyAndElement.h"  // JS_GetProperty
     17 #include "js/TypeDecls.h"
     18 #include "js/Value.h"
     19 #include "js/Warnings.h"
     20 #include "js/Wrapper.h"
     21 #include "js/friend/ErrorMessages.h"
     22 #include "js/loader/LoadedScript.h"
     23 #include "js/loader/ScriptLoadRequest.h"
     24 #include "jsapi.h"
     25 #include "mozilla/Assertions.h"
     26 #include "mozilla/BasePrincipal.h"
     27 #include "mozilla/CycleCollectedJSContext.h"
     28 #include "mozilla/DebugOnly.h"
     29 #include "mozilla/RefPtr.h"
     30 #include "mozilla/ThreadLocal.h"
     31 #include "mozilla/dom/AutoEntryScript.h"
     32 #include "mozilla/dom/BindingUtils.h"
     33 #include "mozilla/dom/Document.h"
     34 #include "mozilla/dom/Element.h"
     35 #include "mozilla/dom/WorkerCommon.h"
     36 #include "nsContentUtils.h"
     37 #include "nsDebug.h"
     38 #include "nsGlobalWindowInner.h"
     39 #include "nsIGlobalObject.h"
     40 #include "nsINode.h"
     41 #include "nsIPrincipal.h"
     42 #include "nsISupports.h"
     43 #include "nsJSUtils.h"
     44 #include "nsPIDOMWindow.h"
     45 #include "nsString.h"
     46 #include "nscore.h"
     47 #include "xpcpublic.h"
     48 
     49 namespace mozilla {
     50 namespace dom {
     51 
     52 static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS;
     53 
     54 class ScriptSettingsStack {
     55 public:
     56  static ScriptSettingsStackEntry* Top() { return sScriptSettingsTLS.get(); }
     57 
     58  static void Push(ScriptSettingsStackEntry* aEntry) {
     59    MOZ_ASSERT(!aEntry->mOlder);
     60    // Whenever JSAPI use is disabled, the next stack entry pushed must
     61    // not be an AutoIncumbentScript.
     62    MOZ_ASSERT_IF(!Top() || Top()->NoJSAPI(), !aEntry->IsIncumbentScript());
     63    // Whenever the top entry is not an incumbent canidate, the next stack entry
     64    // pushed must not be an AutoIncumbentScript.
     65    MOZ_ASSERT_IF(Top() && !Top()->IsIncumbentCandidate(),
     66                  !aEntry->IsIncumbentScript());
     67 
     68    aEntry->mOlder = Top();
     69    sScriptSettingsTLS.set(aEntry);
     70  }
     71 
     72  static void Pop(ScriptSettingsStackEntry* aEntry) {
     73    MOZ_ASSERT(aEntry == Top());
     74    sScriptSettingsTLS.set(aEntry->mOlder);
     75  }
     76 
     77  static nsIGlobalObject* IncumbentGlobal() {
     78    ScriptSettingsStackEntry* entry = Top();
     79    while (entry) {
     80      if (entry->IsIncumbentCandidate()) {
     81        return entry->mGlobalObject;
     82      }
     83      entry = entry->mOlder;
     84    }
     85    return nullptr;
     86  }
     87 
     88  static ScriptSettingsStackEntry* EntryPoint() {
     89    ScriptSettingsStackEntry* entry = Top();
     90    while (entry) {
     91      if (entry->IsEntryCandidate()) {
     92        return entry;
     93      }
     94      entry = entry->mOlder;
     95    }
     96    return nullptr;
     97  }
     98 
     99  static nsIGlobalObject* EntryGlobal() {
    100    ScriptSettingsStackEntry* entry = EntryPoint();
    101    if (!entry) {
    102      return nullptr;
    103    }
    104    return entry->mGlobalObject;
    105  }
    106 
    107 #ifdef DEBUG
    108  static ScriptSettingsStackEntry* TopNonIncumbentScript() {
    109    ScriptSettingsStackEntry* entry = Top();
    110    while (entry) {
    111      if (!entry->IsIncumbentScript()) {
    112        return entry;
    113      }
    114      entry = entry->mOlder;
    115    }
    116    return nullptr;
    117  }
    118 #endif  // DEBUG
    119 };
    120 
    121 void InitScriptSettings() {
    122  bool success = sScriptSettingsTLS.init();
    123  if (!success) {
    124    MOZ_CRASH();
    125  }
    126 
    127  sScriptSettingsTLS.set(nullptr);
    128 }
    129 
    130 void DestroyScriptSettings() {
    131  MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr);
    132 }
    133 
    134 ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject* aGlobal,
    135                                                   Type aType)
    136    : mGlobalObject(aGlobal), mType(aType), mOlder(nullptr) {
    137  MOZ_ASSERT_IF(IsIncumbentCandidate() && !NoJSAPI(), mGlobalObject);
    138  MOZ_ASSERT(!mGlobalObject || mGlobalObject->HasJSGlobal(),
    139             "Must have an actual JS global for the duration on the stack");
    140  MOZ_ASSERT(
    141      !mGlobalObject ||
    142          JS_IsGlobalObject(mGlobalObject->GetGlobalJSObjectPreserveColor()),
    143      "No outer windows allowed");
    144 }
    145 
    146 ScriptSettingsStackEntry::~ScriptSettingsStackEntry() {
    147  // We must have an actual JS global for the entire time this is on the stack.
    148  MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->HasJSGlobal());
    149 }
    150 
    151 // If the entry or incumbent global ends up being something that the subject
    152 // principal doesn't subsume, we don't want to use it. This never happens on
    153 // the web, but can happen with asymmetric privilege relationships (i.e.
    154 // ExpandedPrincipal and System Principal).
    155 //
    156 // The most correct thing to use instead would be the topmost global on the
    157 // callstack whose principal is subsumed by the subject principal. But that's
    158 // hard to compute, so we just substitute the global of the current
    159 // compartment. In practice, this is fine.
    160 //
    161 // Note that in particular things like:
    162 //
    163 // |SpecialPowers.wrap(crossOriginWindow).eval(open())|
    164 //
    165 // trigger this case. Although both the entry global and the current global
    166 // have normal principals, the use of Gecko-specific System-Principaled JS
    167 // puts the code from two different origins on the callstack at once, which
    168 // doesn't happen normally on the web.
    169 static nsIGlobalObject* ClampToSubject(nsIGlobalObject* aGlobalOrNull) {
    170  if (!aGlobalOrNull || !NS_IsMainThread()) {
    171    return aGlobalOrNull;
    172  }
    173 
    174  nsIPrincipal* globalPrin = aGlobalOrNull->PrincipalOrNull();
    175  NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal());
    176  if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()
    177           ->SubsumesConsideringDomain(globalPrin)) {
    178    return GetCurrentGlobal();
    179  }
    180 
    181  return aGlobalOrNull;
    182 }
    183 
    184 nsIGlobalObject* GetEntryGlobal() {
    185  return ClampToSubject(ScriptSettingsStack::EntryGlobal());
    186 }
    187 
    188 Document* GetEntryDocument() {
    189  nsIGlobalObject* global = GetEntryGlobal();
    190  nsCOMPtr<nsPIDOMWindowInner> entryWin = do_QueryInterface(global);
    191 
    192  return entryWin ? entryWin->GetExtantDoc() : nullptr;
    193 }
    194 
    195 nsIGlobalObject* GetIncumbentGlobal() {
    196  // We need the current JSContext in order to check the JS for
    197  // scripted frames that may have appeared since anyone last
    198  // manipulated the stack. If it's null, that means that there
    199  // must be no entry global on the stack, and therefore no incumbent
    200  // global either.
    201  JSContext* cx = nsContentUtils::GetCurrentJSContext();
    202  if (!cx) {
    203    MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
    204    return nullptr;
    205  }
    206 
    207  // See what the JS engine has to say. If we've got a scripted caller
    208  // override in place, the JS engine will lie to us and pretend that
    209  // there's nothing on the JS stack, which will cause us to check the
    210  // incumbent script stack below.
    211  if (JSObject* global = JS::GetScriptedCallerGlobal(cx)) {
    212    return ClampToSubject(xpc::NativeGlobal(global));
    213  }
    214 
    215  // Ok, nothing from the JS engine. Let's use whatever's on the
    216  // explicit stack.
    217  return ClampToSubject(ScriptSettingsStack::IncumbentGlobal());
    218 }
    219 
    220 nsIGlobalObject* GetCurrentGlobal() {
    221  JSContext* cx = nsContentUtils::GetCurrentJSContext();
    222  if (!cx) {
    223    return nullptr;
    224  }
    225 
    226  JSObject* global = JS::CurrentGlobalOrNull(cx);
    227  if (!global) {
    228    return nullptr;
    229  }
    230 
    231  return xpc::NativeGlobal(global);
    232 }
    233 
    234 WebTaskSchedulingState* GetWebTaskSchedulingState() {
    235  if (const nsIGlobalObject* global = GetEntryGlobal()) {
    236    return global->GetWebTaskSchedulingState();
    237  }
    238  return nullptr;
    239 }
    240 
    241 nsIPrincipal* GetWebIDLCallerPrincipal() {
    242  MOZ_ASSERT(NS_IsMainThread());
    243  ScriptSettingsStackEntry* entry = ScriptSettingsStack::EntryPoint();
    244 
    245  // If we have an entry point that is not NoJSAPI, we know it must be an
    246  // AutoEntryScript.
    247  if (!entry || entry->NoJSAPI()) {
    248    return nullptr;
    249  }
    250  AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry);
    251 
    252  return aes->mWebIDLCallerPrincipal;
    253 }
    254 
    255 bool IsJSAPIActive() {
    256  ScriptSettingsStackEntry* topEntry = ScriptSettingsStack::Top();
    257  return topEntry && !topEntry->NoJSAPI();
    258 }
    259 
    260 namespace danger {
    261 JSContext* GetJSContext() { return CycleCollectedJSContext::Get()->Context(); }
    262 }  // namespace danger
    263 
    264 JS::RootingContext* RootingCx() {
    265  return CycleCollectedJSContext::Get()->RootingCx();
    266 }
    267 
    268 AutoJSAPI::AutoJSAPI()
    269    : ScriptSettingsStackEntry(nullptr, eJSAPI),
    270      mCx(nullptr),
    271      mIsMainThread(false)  // For lack of anything better
    272 {}
    273 
    274 AutoJSAPI::~AutoJSAPI() {
    275  if (!mCx) {
    276    // No need to do anything here: we never managed to Init, so can't have an
    277    // exception on our (nonexistent) JSContext.  We also don't need to restore
    278    // any state on it.  Finally, we never made it to pushing ourselves onto the
    279    // ScriptSettingsStack, so shouldn't pop.
    280    MOZ_ASSERT(ScriptSettingsStack::Top() != this);
    281    return;
    282  }
    283 
    284  ReportException();
    285 
    286  if (mOldWarningReporter.isSome()) {
    287    JS::SetWarningReporter(cx(), mOldWarningReporter.value());
    288  }
    289 
    290  ScriptSettingsStack::Pop(this);
    291 }
    292 
    293 void WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep);
    294 
    295 void AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
    296                             JSContext* aCx, bool aIsMainThread) {
    297  MOZ_ASSERT(aCx);
    298  MOZ_ASSERT(aCx == danger::GetJSContext());
    299  MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
    300  MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal));
    301  MOZ_ASSERT_IF(aGlobalObject,
    302                aGlobalObject->GetGlobalJSObjectPreserveColor() == aGlobal);
    303 #ifdef DEBUG
    304  bool haveException = JS_IsExceptionPending(aCx);
    305 #endif  // DEBUG
    306 
    307  mCx = aCx;
    308  mIsMainThread = aIsMainThread;
    309  if (aGlobal) {
    310    JS::AssertObjectIsNotGray(aGlobal);
    311  }
    312  mAutoNullableRealm.emplace(mCx, aGlobal);
    313  mGlobalObject = aGlobalObject;
    314 
    315  ScriptSettingsStack::Push(this);
    316 
    317  mOldWarningReporter.emplace(JS::GetWarningReporter(aCx));
    318 
    319  JS::SetWarningReporter(aCx, WarningOnlyErrorReporter);
    320 
    321 #ifdef DEBUG
    322  if (haveException) {
    323    JS::Rooted<JS::Value> exn(aCx);
    324    JS_GetPendingException(aCx, &exn);
    325 
    326    JS_ClearPendingException(aCx);
    327    if (exn.isObject()) {
    328      JS::Rooted<JSObject*> exnObj(aCx, &exn.toObject());
    329 
    330      // Make sure we can actually read things from it.  This UncheckedUwrap is
    331      // safe because we're only getting data for a debug printf.  In
    332      // particular, we do not expose this data to anyone, which is very
    333      // important; otherwise it could be a cross-origin information leak.
    334      exnObj = js::UncheckedUnwrap(exnObj);
    335      JSAutoRealm ar(aCx, exnObj);
    336 
    337      nsAutoJSString stack, filename, name, message;
    338      int32_t line;
    339 
    340      JS::Rooted<JS::Value> tmp(aCx);
    341      if (!JS_GetProperty(aCx, exnObj, "filename", &tmp)) {
    342        JS_ClearPendingException(aCx);
    343      }
    344      if (tmp.isUndefined()) {
    345        if (!JS_GetProperty(aCx, exnObj, "fileName", &tmp)) {
    346          JS_ClearPendingException(aCx);
    347        }
    348      }
    349 
    350      if (!filename.init(aCx, tmp)) {
    351        JS_ClearPendingException(aCx);
    352      }
    353 
    354      if (!JS_GetProperty(aCx, exnObj, "stack", &tmp) ||
    355          !stack.init(aCx, tmp)) {
    356        JS_ClearPendingException(aCx);
    357      }
    358 
    359      if (!JS_GetProperty(aCx, exnObj, "name", &tmp) || !name.init(aCx, tmp)) {
    360        JS_ClearPendingException(aCx);
    361      }
    362 
    363      if (!JS_GetProperty(aCx, exnObj, "message", &tmp) ||
    364          !message.init(aCx, tmp)) {
    365        JS_ClearPendingException(aCx);
    366      }
    367 
    368      if (!JS_GetProperty(aCx, exnObj, "lineNumber", &tmp) ||
    369          !JS::ToInt32(aCx, tmp, &line)) {
    370        JS_ClearPendingException(aCx);
    371        line = 0;
    372      }
    373 
    374      printf_stderr("PREEXISTING EXCEPTION OBJECT: '%s: %s'\n%s:%d\n%s\n",
    375                    NS_ConvertUTF16toUTF8(name).get(),
    376                    NS_ConvertUTF16toUTF8(message).get(),
    377                    NS_ConvertUTF16toUTF8(filename).get(), line,
    378                    NS_ConvertUTF16toUTF8(stack).get());
    379    } else {
    380      // It's a primitive... not much we can do other than stringify it.
    381      nsAutoJSString exnStr;
    382      if (!exnStr.init(aCx, exn)) {
    383        JS_ClearPendingException(aCx);
    384      }
    385 
    386      printf_stderr("PREEXISTING EXCEPTION PRIMITIVE: %s\n",
    387                    NS_ConvertUTF16toUTF8(exnStr).get());
    388    }
    389    MOZ_ASSERT(false, "We had an exception; we should not have");
    390  }
    391 #endif  // DEBUG
    392 }
    393 
    394 AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread,
    395                     Type aType)
    396    : ScriptSettingsStackEntry(aGlobalObject, aType),
    397      mIsMainThread(aIsMainThread) {
    398  MOZ_ASSERT(aGlobalObject);
    399  MOZ_ASSERT(aGlobalObject->HasJSGlobal(), "Must have a JS global");
    400  MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
    401 
    402  InitInternal(aGlobalObject, aGlobalObject->GetGlobalJSObject(),
    403               danger::GetJSContext(), aIsMainThread);
    404 }
    405 
    406 void AutoJSAPI::Init() {
    407  MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
    408 
    409  InitInternal(/* aGlobalObject */ nullptr, /* aGlobal */ nullptr,
    410               danger::GetJSContext(), NS_IsMainThread());
    411 }
    412 
    413 bool AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx) {
    414  MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
    415  MOZ_ASSERT(aCx);
    416 
    417  if (NS_WARN_IF(!aGlobalObject)) {
    418    return false;
    419  }
    420 
    421  JSObject* global = aGlobalObject->GetGlobalJSObject();
    422  if (NS_WARN_IF(!global)) {
    423    return false;
    424  }
    425 
    426  InitInternal(aGlobalObject, global, aCx, NS_IsMainThread());
    427  return true;
    428 }
    429 
    430 bool AutoJSAPI::Init(nsIGlobalObject* aGlobalObject) {
    431  return Init(aGlobalObject, danger::GetJSContext());
    432 }
    433 
    434 bool AutoJSAPI::Init(JSObject* aObject) {
    435  MOZ_ASSERT(!js::IsCrossCompartmentWrapper(aObject));
    436  return Init(xpc::NativeGlobal(aObject));
    437 }
    438 
    439 bool AutoJSAPI::Init(nsPIDOMWindowInner* aWindow, JSContext* aCx) {
    440  return Init(nsGlobalWindowInner::Cast(aWindow), aCx);
    441 }
    442 
    443 bool AutoJSAPI::Init(nsPIDOMWindowInner* aWindow) {
    444  return Init(nsGlobalWindowInner::Cast(aWindow));
    445 }
    446 
    447 bool AutoJSAPI::Init(nsGlobalWindowInner* aWindow, JSContext* aCx) {
    448  return Init(static_cast<nsIGlobalObject*>(aWindow), aCx);
    449 }
    450 
    451 bool AutoJSAPI::Init(nsGlobalWindowInner* aWindow) {
    452  return Init(static_cast<nsIGlobalObject*>(aWindow));
    453 }
    454 
    455 // Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
    456 // reports to the JSErrorReporter as soon as they are generated. These go
    457 // directly to the console, so we can handle them easily here.
    458 //
    459 // Eventually, SpiderMonkey will have a special-purpose callback for warnings
    460 // only.
    461 void WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep) {
    462  MOZ_ASSERT(aRep->isWarning());
    463  if (!NS_IsMainThread()) {
    464    // Reporting a warning on workers is a bit complicated because we have to
    465    // climb our parent chain until we get to the main thread.  So go ahead and
    466    // just go through the worker or worklet ReportError codepath here.
    467    //
    468    // That said, it feels like we should be able to short-circuit things a bit
    469    // here by posting an appropriate runnable to the main thread directly...
    470    // Worth looking into sometime.
    471    CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::GetFor(aCx);
    472    MOZ_ASSERT(ccjscx);
    473 
    474    ccjscx->ReportError(aRep, JS::ConstUTF8CharsZ());
    475    return;
    476  }
    477 
    478  RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
    479  nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(aCx);
    480  xpcReport->Init(aRep, nullptr, nsContentUtils::IsSystemCaller(aCx),
    481                  win ? win->WindowID() : 0);
    482  xpcReport->LogToConsole();
    483 }
    484 
    485 void AutoJSAPI::ReportException() {
    486  if (!HasException()) {
    487    return;
    488  }
    489 
    490  // AutoJSAPI uses a JSAutoNullableRealm, and may be in a null realm
    491  // when the destructor is called. However, the JS engine requires us
    492  // to be in a realm when we fetch the pending exception. In this case,
    493  // we enter the privileged junk scope and don't dispatch any error events.
    494  JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
    495  if (!errorGlobal) {
    496    if (mIsMainThread) {
    497      errorGlobal = xpc::PrivilegedJunkScope();
    498    } else {
    499      errorGlobal = GetCurrentThreadWorkerGlobal();
    500      if (!errorGlobal) {
    501        // We might be reporting an error in debugger code that ran before the
    502        // worker's global was created. Use the debugger global instead.
    503        errorGlobal = GetCurrentThreadWorkerDebuggerGlobal();
    504        if (NS_WARN_IF(!errorGlobal)) {
    505          // An exception may have been thrown on attempt to create a global
    506          // and now there is no realm from which to fetch the exception.
    507          // Give up.
    508          ClearException();
    509          return;
    510        }
    511      }
    512    }
    513  }
    514  MOZ_ASSERT(JS_IsGlobalObject(errorGlobal));
    515  JSAutoRealm ar(cx(), errorGlobal);
    516  JS::ExceptionStack exnStack(cx());
    517  JS::ErrorReportBuilder jsReport(cx());
    518  if (StealExceptionAndStack(&exnStack) &&
    519      jsReport.init(cx(), exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
    520    if (mIsMainThread) {
    521      RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
    522 
    523      RefPtr<nsGlobalWindowInner> inner = xpc::WindowOrNull(errorGlobal);
    524 
    525      // For WebExtension content script, `WindowOrNull` method will return
    526      // null, whereas we would still like to flag the exception with the
    527      // related WindowGlobal the content script executed against. So we only
    528      // update the `innerWindowID` and not `inner` as we don't want to dispatch
    529      // exceptions caused by the content script to the webpage.
    530      uint64_t innerWindowID = 0;
    531      if (inner) {
    532        innerWindowID = inner->WindowID();
    533      } else if (nsGlobalWindowInner* win = xpc::SandboxWindowOrNull(
    534                     JS::GetNonCCWObjectGlobal(errorGlobal), cx())) {
    535        innerWindowID = win->WindowID();
    536      }
    537 
    538      bool isChrome =
    539          nsContentUtils::ObjectPrincipal(errorGlobal)->IsSystemPrincipal();
    540      xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
    541                      isChrome, innerWindowID);
    542      if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
    543        JS::RootingContext* rcx = JS::RootingContext::get(cx());
    544        DispatchScriptErrorEvent(inner, rcx, xpcReport, exnStack.exception(),
    545                                 exnStack.stack());
    546      } else {
    547        JS::Rooted<JSObject*> stack(cx());
    548        JS::Rooted<JSObject*> stackGlobal(cx());
    549        xpc::FindExceptionStackForConsoleReport(inner, exnStack.exception(),
    550                                                exnStack.stack(), &stack,
    551                                                &stackGlobal);
    552        // This error is not associated with a specific window,
    553        // so omit the exception value to mitigate potential leaks.
    554        xpcReport->LogToConsoleWithStack(inner, JS::NothingHandleValue, stack,
    555                                         stackGlobal);
    556      }
    557    } else {
    558      // On a worker or worklet, we just use the error reporting mechanism and
    559      // don't bother with xpc::ErrorReport.  This will ensure that all the
    560      // right worker events (which are a lot more complicated than in the
    561      // window case) get fired.
    562      CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::GetFor(cx());
    563      MOZ_ASSERT(ccjscx);
    564      // Before invoking ReportError, put the exception back on the context,
    565      // because it may want to put it in its error events and has no other way
    566      // to get hold of it.  After we invoke ReportError, clear the exception on
    567      // cx(), just in case ReportError didn't.
    568      JS::SetPendingExceptionStack(cx(), exnStack);
    569      ccjscx->ReportError(jsReport.report(), jsReport.toStringResult());
    570      ClearException();
    571    }
    572  } else {
    573    NS_WARNING("OOMed while acquiring uncaught exception from JSAPI");
    574    ClearException();
    575  }
    576 }
    577 
    578 bool AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal) {
    579  MOZ_ASSERT_IF(mIsMainThread, IsStackTop());
    580  MOZ_ASSERT(HasException());
    581  MOZ_ASSERT(js::GetContextRealm(cx()));
    582  return JS_GetPendingException(cx(), aVal);
    583 }
    584 
    585 bool AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal) {
    586  JS::ExceptionStack exnStack(cx());
    587  if (!StealExceptionAndStack(&exnStack)) {
    588    return false;
    589  }
    590  aVal.set(exnStack.exception());
    591  return true;
    592 }
    593 
    594 bool AutoJSAPI::StealExceptionAndStack(JS::ExceptionStack* aExnStack) {
    595  MOZ_ASSERT_IF(mIsMainThread, IsStackTop());
    596  MOZ_ASSERT(HasException());
    597  MOZ_ASSERT(js::GetContextRealm(cx()));
    598 
    599  return JS::StealPendingExceptionStack(cx(), aExnStack);
    600 }
    601 
    602 #ifdef DEBUG
    603 bool AutoJSAPI::IsStackTop() const {
    604  return ScriptSettingsStack::TopNonIncumbentScript() == this;
    605 }
    606 #endif  // DEBUG
    607 
    608 AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
    609    : ScriptSettingsStackEntry(aGlobalObject, eIncumbentScript),
    610      mCallerOverride(nsContentUtils::GetCurrentJSContext()) {
    611  ScriptSettingsStack::Push(this);
    612 }
    613 
    614 AutoIncumbentScript::~AutoIncumbentScript() { ScriptSettingsStack::Pop(this); }
    615 
    616 AutoNoJSAPI::AutoNoJSAPI(JSContext* aCx)
    617    : ScriptSettingsStackEntry(nullptr, eNoJSAPI),
    618      JSAutoNullableRealm(aCx, nullptr),
    619      mCx(aCx) {
    620  // Make sure we don't seem to have an incumbent global due to
    621  // whatever script is running right now.
    622  JS::HideScriptedCaller(aCx);
    623 
    624  // Make sure the fallback GetIncumbentGlobal() behavior and
    625  // GetEntryGlobal() both return null.
    626  ScriptSettingsStack::Push(this);
    627 }
    628 
    629 AutoNoJSAPI::~AutoNoJSAPI() {
    630  ScriptSettingsStack::Pop(this);
    631  JS::UnhideScriptedCaller(mCx);
    632 }
    633 
    634 }  // namespace dom
    635 
    636 AutoJSContext::AutoJSContext() : mCx(nullptr) {
    637  JS::AutoSuppressGCAnalysis nogc;
    638  MOZ_ASSERT(!mCx, "mCx should not be initialized!");
    639  MOZ_ASSERT(NS_IsMainThread());
    640 
    641  if (dom::IsJSAPIActive()) {
    642    mCx = dom::danger::GetJSContext();
    643  } else {
    644    mJSAPI.Init();
    645    mCx = mJSAPI.cx();
    646  }
    647 }
    648 
    649 AutoJSContext::operator JSContext*() const { return mCx; }
    650 
    651 AutoSafeJSContext::AutoSafeJSContext() {
    652  MOZ_ASSERT(NS_IsMainThread());
    653 
    654  DebugOnly<bool> ok = Init(xpc::UnprivilegedJunkScope());
    655  MOZ_ASSERT(ok,
    656             "This is quite odd.  We should have crashed in the "
    657             "xpc::NativeGlobal() call if xpc::UnprivilegedJunkScope() "
    658             "returned null, and inited correctly otherwise!");
    659 }
    660 
    661 AutoSlowOperation::AutoSlowOperation() : mIsMainThread(NS_IsMainThread()) {
    662  if (mIsMainThread) {
    663    mScriptActivity.emplace(true);
    664  }
    665 }
    666 
    667 void AutoSlowOperation::CheckForInterrupt() {
    668  // For now we support only main thread!
    669  if (mIsMainThread) {
    670    // JS_CheckForInterrupt expects us to be in a realm, so we use a junk scope.
    671    // In principle, it doesn't matter which one we use, since we aren't really
    672    // running scripts here, and none of our interrupt callbacks can stop
    673    // scripts in a junk scope anyway. In practice, though, the privileged junk
    674    // scope is the same as the JSM global, and therefore always exists, while
    675    // the unprivileged junk scope is created lazily, and may not exist until we
    676    // try to use it. So we use the former for the sake of efficiency.
    677    dom::AutoJSAPI jsapi;
    678    MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
    679    JS_CheckForInterrupt(jsapi.cx());
    680  }
    681 }
    682 
    683 AutoAllowLegacyScriptExecution::AutoAllowLegacyScriptExecution() {
    684 #ifdef DEBUG
    685  // no need to do that dance if we are off the main thread,
    686  // because we only assert if we are on the main thread!
    687  if (!NS_IsMainThread()) {
    688    return;
    689  }
    690  sAutoAllowLegacyScriptExecution++;
    691 #endif
    692 }
    693 
    694 AutoAllowLegacyScriptExecution::~AutoAllowLegacyScriptExecution() {
    695 #ifdef DEBUG
    696  // no need to do that dance if we are off the main thread,
    697  // because we only assert if we are on the main thread!
    698  if (!NS_IsMainThread()) {
    699    return;
    700  }
    701  sAutoAllowLegacyScriptExecution--;
    702  MOZ_ASSERT(sAutoAllowLegacyScriptExecution >= 0,
    703             "how can the stack guard produce a value less than 0?");
    704 #endif
    705 }
    706 
    707 int AutoAllowLegacyScriptExecution::sAutoAllowLegacyScriptExecution = 0;
    708 
    709 /*static*/
    710 bool AutoAllowLegacyScriptExecution::IsAllowed() {
    711  return sAutoAllowLegacyScriptExecution > 0;
    712 }
    713 
    714 }  // namespace mozilla