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