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/. */
8 #include "nsJSEnvironment.h"
9 #include "nsIScriptGlobalObject.h"
10 #include "nsIScriptObjectPrincipal.h"
11 #include "nsPIDOMWindow.h"
13 #include "nsIXPConnect.h"
15 #include "nsISupportsPrimitives.h"
16 #include "nsReadableUtils.h"
17 #include "nsDOMJSUtils.h"
18 #include "nsJSUtils.h"
19 #include "nsIDocShell.h"
20 #include "nsIDocShellTreeItem.h"
21 #include "nsPresContext.h"
22 #include "nsIConsoleService.h"
23 #include "nsIInterfaceRequestor.h"
24 #include "nsIInterfaceRequestorUtils.h"
25 #include "nsIObserverService.h"
28 #include "nsContentUtils.h"
29 #include "mozilla/EventDispatcher.h"
30 #include "mozilla/HoldDropJSObjects.h"
31 #include "nsIContent.h"
32 #include "nsCycleCollector.h"
33 #include "nsXPCOMCIDInternal.h"
34 #include "nsServiceManagerUtils.h"
35 #include "nsTextFormatter.h"
38 # define getpid _getpid
40 # include <unistd.h> // for getpid()
42 #include "xpcpublic.h"
45 #include "js/Array.h" // JS::NewArrayObject
46 #include "js/PropertyAndElement.h" // JS_DefineProperty
47 #include "js/PropertySpec.h"
48 #include "js/SliceBudget.h"
49 #include "js/Wrapper.h"
51 #include "CCGCScheduler.h"
52 #include "WrapperFactory.h"
53 #include "nsGlobalWindowInner.h"
54 #include "nsGlobalWindowOuter.h"
55 #include "mozilla/AutoRestore.h"
56 #include "mozilla/BasePrincipal.h"
57 #include "mozilla/CycleCollectorStats.h"
58 #include "mozilla/MainThreadIdlePeriod.h"
59 #include "mozilla/PresShell.h"
60 #include "mozilla/SchedulerGroup.h"
61 #include "mozilla/StaticPrefs_dom.h"
62 #include "mozilla/StaticPrefs_javascript.h"
63 #include "mozilla/StaticPtr.h"
64 #include "mozilla/dom/BrowsingContext.h"
65 #include "mozilla/dom/DOMException.h"
66 #include "mozilla/dom/DOMExceptionBinding.h"
67 #include "mozilla/dom/Element.h"
68 #include "mozilla/dom/ErrorEvent.h"
69 #include "mozilla/dom/FetchUtil.h"
70 #include "mozilla/dom/RootedDictionary.h"
71 #include "mozilla/dom/ScriptSettings.h"
72 #include "mozilla/dom/SerializedStackHolder.h"
73 #include "mozilla/CycleCollectedJSRuntime.h"
74 #include "nsRefreshDriver.h"
75 #include "nsJSPrincipals.h"
76 #include "AccessCheck.h"
77 #include "mozilla/Logging.h"
80 #include "mozilla/Preferences.h"
81 #include "mozilla/Telemetry.h"
82 #include "mozilla/dom/BindingUtils.h"
83 #include "mozilla/Attributes.h"
84 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
85 #include "mozilla/ContentEvents.h"
86 #include "mozilla/CycleCollectedJSContext.h"
87 #include "nsCycleCollectionNoteRootCallback.h"
88 #include "nsViewManager.h"
89 #include "mozilla/EventStateManager.h"
90 #include "mozilla/ProfilerLabels.h"
91 #include "mozilla/ProfilerMarkers.h"
92 #if defined(MOZ_MEMORY)
93 # include "mozmemory.h"
96 using namespace mozilla
;
97 using namespace mozilla::dom
;
99 // Thank you Microsoft!
101 # undef CompareString
104 static JS::GCSliceCallback sPrevGCSliceCallback
;
106 static bool sIncrementalCC
= false;
108 static bool sIsInitialized
;
109 static bool sShuttingDown
;
111 static CCGCScheduler
* sScheduler
= nullptr;
112 static std::aligned_storage_t
<sizeof(*sScheduler
)> sSchedulerStorage
;
114 // Cache a pointer to the main thread's statistics struct.
115 static CycleCollectorStats
* sCCStats
= nullptr;
117 static const char* ProcessNameForCollectorLog() {
118 return XRE_GetProcessType() == GeckoProcessType_Default
? "default"
124 // This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
125 // Exceptions, and arbitrary values that were associated with a stack by the
126 // JS engine when they were thrown, as specified by exceptionStack.
128 // Note that the returned stackObj and stackGlobal are _not_ wrapped into the
129 // compartment of exceptionValue.
130 void FindExceptionStackForConsoleReport(
131 nsPIDOMWindowInner
* win
, JS::Handle
<JS::Value
> exceptionValue
,
132 JS::Handle
<JSObject
*> exceptionStack
, JS::MutableHandle
<JSObject
*> stackObj
,
133 JS::MutableHandle
<JSObject
*> stackGlobal
) {
134 stackObj
.set(nullptr);
135 stackGlobal
.set(nullptr);
137 if (!exceptionValue
.isObject()) {
138 // Use the stack provided by the JS engine, if available. This will not be
140 if (exceptionStack
) {
141 stackObj
.set(exceptionStack
);
142 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
147 if (win
&& win
->AsGlobal()->IsDying()) {
148 // Pretend like we have no stack, so we don't end up keeping the global
149 // alive via the stack.
153 JS::RootingContext
* rcx
= RootingCx();
154 JS::Rooted
<JSObject
*> exceptionObject(rcx
, &exceptionValue
.toObject());
155 if (JSObject
* excStack
= JS::ExceptionStackOrNull(exceptionObject
)) {
156 // At this point we know exceptionObject is a possibly-wrapped
157 // js::ErrorObject that has excStack as stack. excStack might also be a CCW,
158 // but excStack must be same-compartment with the unwrapped ErrorObject.
159 // Return the ErrorObject's global as stackGlobal. This matches what we do
160 // in the ErrorObject's |.stack| getter and ensures stackObj and stackGlobal
161 // are same-compartment.
162 JSObject
* unwrappedException
= js::UncheckedUnwrap(exceptionObject
);
163 stackObj
.set(excStack
);
164 stackGlobal
.set(JS::GetNonCCWObjectGlobal(unwrappedException
));
168 // It is not a JS Exception, try DOM Exception.
169 RefPtr
<Exception
> exception
;
170 UNWRAP_OBJECT(DOMException
, exceptionObject
, exception
);
172 // Not a DOM Exception, try XPC Exception.
173 UNWRAP_OBJECT(Exception
, exceptionObject
, exception
);
175 // As above, use the stack provided by the JS engine, if available.
176 if (exceptionStack
) {
177 stackObj
.set(exceptionStack
);
178 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
184 nsCOMPtr
<nsIStackFrame
> stack
= exception
->GetLocation();
188 JS::Rooted
<JS::Value
> value(rcx
);
189 stack
->GetNativeSavedFrame(&value
);
190 if (value
.isObject()) {
191 stackObj
.set(&value
.toObject());
192 MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj
));
193 stackGlobal
.set(JS::GetNonCCWObjectGlobal(stackObj
));
198 } /* namespace xpc */
200 static TimeDuration
GetCollectionTimeDelta() {
201 static TimeStamp sFirstCollectionTime
;
202 TimeStamp now
= TimeStamp::Now();
203 if (sFirstCollectionTime
) {
204 return now
- sFirstCollectionTime
;
206 sFirstCollectionTime
= now
;
207 return TimeDuration();
210 class nsJSEnvironmentObserver final
: public nsIObserver
{
211 ~nsJSEnvironmentObserver() = default;
218 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver
, nsIObserver
)
221 nsJSEnvironmentObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
222 const char16_t
* aData
) {
223 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) {
224 if (StaticPrefs::javascript_options_gc_on_memory_pressure()) {
226 // Don't GC/CC if we're already shutting down.
229 nsDependentString
data(aData
);
230 if (data
.EqualsLiteral("low-memory-ongoing")) {
231 // Don't GC/CC if we are in an ongoing low-memory state since its very
232 // slow and it likely won't help us anyway.
235 if (data
.EqualsLiteral("heap-minimize")) {
236 // heap-minimize notifiers expect this to run synchronously
237 nsJSContext::DoLowMemoryGC();
240 if (data
.EqualsLiteral("low-memory")) {
241 nsJSContext::SetLowMemoryState(true);
243 // Asynchronously GC.
244 nsJSContext::LowMemoryGC();
246 } else if (!nsCRT::strcmp(aTopic
, "memory-pressure-stop")) {
247 nsJSContext::SetLowMemoryState(false);
248 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-inactive")) {
249 sScheduler
->UserIsInactive();
250 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-active")) {
251 sScheduler
->UserIsActive();
252 } else if (!nsCRT::strcmp(aTopic
, "quit-application") ||
253 !nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) ||
254 !nsCRT::strcmp(aTopic
, "content-child-will-shutdown")) {
255 sShuttingDown
= true;
256 sScheduler
->Shutdown();
262 /****************************************************************
263 ************************** AutoFree ****************************
264 ****************************************************************/
268 explicit AutoFree(void* aPtr
) : mPtr(aPtr
) {}
270 if (mPtr
) free(mPtr
);
272 void Invalidate() { mPtr
= nullptr; }
278 // A utility function for script languages to call. Although it looks small,
279 // the use of nsIDocShell and nsPresContext triggers a huge number of
280 // dependencies that most languages would not otherwise need.
281 // XXXmarkh - This function is mis-placed!
282 bool NS_HandleScriptError(nsIScriptGlobalObject
* aScriptGlobal
,
283 const ErrorEventInit
& aErrorEventInit
,
284 nsEventStatus
* aStatus
) {
286 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryInterface(aScriptGlobal
));
287 nsIDocShell
* docShell
= win
? win
->GetDocShell() : nullptr;
289 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
291 static int32_t errorDepth
; // Recursion prevention
294 if (errorDepth
< 2) {
295 // Dispatch() must be synchronous for the recursion block
296 // (errorDepth) to work.
297 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
298 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, aErrorEventInit
);
299 event
->SetTrusted(true);
301 // MOZ_KnownLive due to bug 1506441
302 EventDispatcher::DispatchDOMEvent(
303 MOZ_KnownLive(nsGlobalWindowInner::Cast(win
)), nullptr, event
,
304 presContext
, aStatus
);
312 class ScriptErrorEvent
: public Runnable
{
314 ScriptErrorEvent(nsPIDOMWindowInner
* aWindow
, JS::RootingContext
* aRootingCx
,
315 xpc::ErrorReport
* aReport
, JS::Handle
<JS::Value
> aError
,
316 JS::Handle
<JSObject
*> aErrorStack
)
317 : mozilla::Runnable("ScriptErrorEvent"),
320 mError(aRootingCx
, aError
),
321 mErrorStack(aRootingCx
, aErrorStack
) {}
323 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
324 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
325 nsEventStatus status
= nsEventStatus_eIgnore
;
326 nsCOMPtr
<nsPIDOMWindowInner
> win
= mWindow
;
328 MOZ_ASSERT(NS_IsMainThread());
329 // First, notify the DOM that we have a script error, but only if
330 // our window is still the current inner.
331 JS::RootingContext
* rootingCx
= RootingCx();
332 if (win
->IsCurrentInnerWindow() && win
->GetDocShell() &&
333 !sHandlingScriptError
) {
334 AutoRestore
<bool> recursionGuard(sHandlingScriptError
);
335 sHandlingScriptError
= true;
337 RefPtr
<nsPresContext
> presContext
= win
->GetDocShell()->GetPresContext();
339 RootedDictionary
<ErrorEventInit
> init(rootingCx
);
340 init
.mCancelable
= true;
341 init
.mFilename
= mReport
->mFileName
;
342 init
.mBubbles
= true;
344 constexpr auto xoriginMsg
= u
"Script error."_ns
;
345 if (!mReport
->mIsMuted
) {
346 init
.mMessage
= mReport
->mErrorMsg
;
347 init
.mLineno
= mReport
->mLineNumber
;
348 init
.mColno
= mReport
->mColumn
;
349 init
.mError
= mError
;
351 NS_WARNING("Not same origin error!");
352 init
.mMessage
= xoriginMsg
;
356 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
357 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, init
);
358 event
->SetTrusted(true);
360 // MOZ_KnownLive due to bug 1506441
361 EventDispatcher::DispatchDOMEvent(
362 MOZ_KnownLive(nsGlobalWindowInner::Cast(win
)), nullptr, event
,
363 presContext
, &status
);
366 if (status
!= nsEventStatus_eConsumeNoDefault
) {
367 JS::Rooted
<JSObject
*> stack(rootingCx
);
368 JS::Rooted
<JSObject
*> stackGlobal(rootingCx
);
369 xpc::FindExceptionStackForConsoleReport(win
, mError
, mErrorStack
, &stack
,
371 JS::Rooted
<Maybe
<JS::Value
>> exception(rootingCx
, Some(mError
));
372 nsGlobalWindowInner
* inner
= nsGlobalWindowInner::Cast(win
);
373 mReport
->LogToConsoleWithStack(inner
, exception
, stack
, stackGlobal
);
380 nsCOMPtr
<nsPIDOMWindowInner
> mWindow
;
381 RefPtr
<xpc::ErrorReport
> mReport
;
382 JS::PersistentRooted
<JS::Value
> mError
;
383 JS::PersistentRooted
<JSObject
*> mErrorStack
;
385 static bool sHandlingScriptError
;
388 bool ScriptErrorEvent::sHandlingScriptError
= false;
390 // This temporarily lives here to avoid code churn. It will go away entirely
394 void DispatchScriptErrorEvent(nsPIDOMWindowInner
* win
,
395 JS::RootingContext
* rootingCx
,
396 xpc::ErrorReport
* xpcReport
,
397 JS::Handle
<JS::Value
> exception
,
398 JS::Handle
<JSObject
*> exceptionStack
) {
399 nsContentUtils::AddScriptRunner(new ScriptErrorEvent(
400 win
, rootingCx
, xpcReport
, exception
, exceptionStack
));
403 } /* namespace xpc */
406 // A couple of useful functions to call when you're debugging.
407 nsGlobalWindowInner
* JSObject2Win(JSObject
* obj
) {
408 return xpc::WindowOrNull(obj
);
411 template <typename T
>
412 void PrintWinURI(T
* win
) {
414 printf("No window passed in.\n");
418 nsCOMPtr
<Document
> doc
= win
->GetExtantDoc();
420 printf("No document in the window.\n");
424 nsIURI
* uri
= doc
->GetDocumentURI();
426 printf("Document doesn't have a URI.\n");
430 printf("%s\n", uri
->GetSpecOrDefault().get());
433 void PrintWinURIInner(nsGlobalWindowInner
* aWin
) { return PrintWinURI(aWin
); }
435 void PrintWinURIOuter(nsGlobalWindowOuter
* aWin
) { return PrintWinURI(aWin
); }
437 template <typename T
>
438 void PrintWinCodebase(T
* win
) {
440 printf("No window passed in.\n");
444 nsIPrincipal
* prin
= win
->GetPrincipal();
446 printf("Window doesn't have principals.\n");
449 if (prin
->IsSystemPrincipal()) {
450 printf("No URI, it's the system principal.\n");
454 prin
->GetAsciiSpec(spec
);
455 printf("%s\n", spec
.get());
458 void PrintWinCodebaseInner(nsGlobalWindowInner
* aWin
) {
459 return PrintWinCodebase(aWin
);
462 void PrintWinCodebaseOuter(nsGlobalWindowOuter
* aWin
) {
463 return PrintWinCodebase(aWin
);
466 void DumpString(const nsAString
& str
) {
467 printf("%s\n", NS_ConvertUTF16toUTF8(str
).get());
471 nsJSContext::nsJSContext(bool aGCOnDestruction
,
472 nsIScriptGlobalObject
* aGlobalObject
)
473 : mWindowProxy(nullptr),
474 mGCOnDestruction(aGCOnDestruction
),
475 mGlobalObjectRef(aGlobalObject
) {
478 mProcessingScriptTag
= false;
482 nsJSContext::~nsJSContext() {
483 mGlobalObjectRef
= nullptr;
488 void nsJSContext::Destroy() {
489 if (mGCOnDestruction
) {
490 sScheduler
->PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY
, mWindowProxy
);
496 // QueryInterface implementation for nsJSContext
497 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext
)
499 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext
)
500 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy
)
501 NS_IMPL_CYCLE_COLLECTION_TRACE_END
503 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext
)
504 tmp
->mGCOnDestruction
= false;
505 tmp
->mWindowProxy
= nullptr;
507 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef
)
508 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
509 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext
)
510 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef
)
511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
513 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext
)
514 NS_INTERFACE_MAP_ENTRY(nsIScriptContext
)
515 NS_INTERFACE_MAP_ENTRY(nsISupports
)
518 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext
)
519 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext
)
522 bool AtomIsEventHandlerName(nsAtom
* aName
) {
523 const char16_t
* name
= aName
->GetUTF16String();
527 for (cp
= name
; *cp
!= '\0'; ++cp
) {
529 if ((c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z')) return false;
536 nsIScriptGlobalObject
* nsJSContext::GetGlobalObject() {
537 // Note: this could probably be simplified somewhat more; see bug 974327
543 MOZ_ASSERT(mGlobalObjectRef
);
544 return mGlobalObjectRef
;
547 nsresult
nsJSContext::SetProperty(JS::Handle
<JSObject
*> aTarget
,
548 const char* aPropName
, nsISupports
* aArgs
) {
550 if (NS_WARN_IF(!jsapi
.Init(GetGlobalObject()))) {
551 return NS_ERROR_FAILURE
;
553 JSContext
* cx
= jsapi
.cx();
555 JS::RootedVector
<JS::Value
> args(cx
);
557 JS::Rooted
<JSObject
*> global(cx
, GetWindowProxy());
558 nsresult rv
= ConvertSupportsTojsvals(cx
, aArgs
, global
, &args
);
559 NS_ENSURE_SUCCESS(rv
, rv
);
561 // got the arguments, now attach them.
563 for (uint32_t i
= 0; i
< args
.length(); ++i
) {
564 if (!JS_WrapValue(cx
, args
[i
])) {
565 return NS_ERROR_FAILURE
;
569 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, args
));
571 return NS_ERROR_FAILURE
;
574 return JS_DefineProperty(cx
, aTarget
, aPropName
, array
, 0) ? NS_OK
578 nsresult
nsJSContext::ConvertSupportsTojsvals(
579 JSContext
* aCx
, nsISupports
* aArgs
, JS::Handle
<JSObject
*> aScope
,
580 JS::MutableHandleVector
<JS::Value
> aArgsOut
) {
583 // If the array implements nsIJSArgArray, copy the contents and return.
584 nsCOMPtr
<nsIJSArgArray
> fastArray
= do_QueryInterface(aArgs
);
588 rv
= fastArray
->GetArgs(&argc
, reinterpret_cast<void**>(&argv
));
589 if (NS_SUCCEEDED(rv
) && !aArgsOut
.append(argv
, argc
)) {
590 rv
= NS_ERROR_OUT_OF_MEMORY
;
595 // Take the slower path converting each item.
596 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
597 // SetProperty('arguments', ...);
599 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
600 NS_ENSURE_TRUE(xpc
, NS_ERROR_UNEXPECTED
);
602 if (!aArgs
) return NS_OK
;
604 // This general purpose function may need to convert an arg array
605 // (window.arguments, event-handler args) and a generic property.
606 nsCOMPtr
<nsIArray
> argsArray(do_QueryInterface(aArgs
));
609 rv
= argsArray
->GetLength(&argCount
);
610 NS_ENSURE_SUCCESS(rv
, rv
);
611 if (argCount
== 0) return NS_OK
;
613 argCount
= 1; // the nsISupports which is not an array
616 // Use the caller's auto guards to release and unroot.
617 if (!aArgsOut
.resize(argCount
)) {
618 return NS_ERROR_OUT_OF_MEMORY
;
622 for (uint32_t argCtr
= 0; argCtr
< argCount
&& NS_SUCCEEDED(rv
); argCtr
++) {
623 nsCOMPtr
<nsISupports
> arg
;
624 JS::MutableHandle
<JS::Value
> thisVal
= aArgsOut
[argCtr
];
625 argsArray
->QueryElementAt(argCtr
, NS_GET_IID(nsISupports
),
626 getter_AddRefs(arg
));
631 nsCOMPtr
<nsIVariant
> variant(do_QueryInterface(arg
));
632 if (variant
!= nullptr) {
633 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, thisVal
);
635 // And finally, support the nsISupportsPrimitives supplied
636 // by the AppShell. It generally will pass only strings, but
637 // as we have code for handling all, we may as well use it.
638 rv
= AddSupportsPrimitiveTojsvals(aCx
, arg
, thisVal
.address());
639 if (rv
== NS_ERROR_NO_INTERFACE
) {
640 // something else - probably an event object or similar -
643 // but first, check its not another nsISupportsPrimitive, as
644 // these are now deprecated for use with script contexts.
645 nsCOMPtr
<nsISupportsPrimitive
> prim(do_QueryInterface(arg
));
646 NS_ASSERTION(prim
== nullptr,
647 "Don't pass nsISupportsPrimitives - use nsIVariant!");
649 JSAutoRealm
ar(aCx
, aScope
);
650 rv
= nsContentUtils::WrapNative(aCx
, arg
, thisVal
);
655 nsCOMPtr
<nsIVariant
> variant
= do_QueryInterface(aArgs
);
657 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, aArgsOut
[0]);
659 NS_ERROR("Not an array, not an interface?");
660 rv
= NS_ERROR_UNEXPECTED
;
666 // This really should go into xpconnect somewhere...
667 nsresult
nsJSContext::AddSupportsPrimitiveTojsvals(JSContext
* aCx
,
670 MOZ_ASSERT(aArg
, "Empty arg");
672 nsCOMPtr
<nsISupportsPrimitive
> argPrimitive(do_QueryInterface(aArg
));
673 if (!argPrimitive
) return NS_ERROR_NO_INTERFACE
;
676 argPrimitive
->GetType(&type
);
679 case nsISupportsPrimitive::TYPE_CSTRING
: {
680 nsCOMPtr
<nsISupportsCString
> p(do_QueryInterface(argPrimitive
));
681 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
687 JSString
* str
= ::JS_NewStringCopyN(aCx
, data
.get(), data
.Length());
688 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
690 aArgv
->setString(str
);
694 case nsISupportsPrimitive::TYPE_STRING
: {
695 nsCOMPtr
<nsISupportsString
> p(do_QueryInterface(argPrimitive
));
696 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
702 // cast is probably safe since wchar_t and char16_t are expected
703 // to be equivalent; both unsigned 16-bit entities
704 JSString
* str
= ::JS_NewUCStringCopyN(aCx
, data
.get(), data
.Length());
705 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
707 aArgv
->setString(str
);
710 case nsISupportsPrimitive::TYPE_PRBOOL
: {
711 nsCOMPtr
<nsISupportsPRBool
> p(do_QueryInterface(argPrimitive
));
712 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
718 aArgv
->setBoolean(data
);
722 case nsISupportsPrimitive::TYPE_PRUINT8
: {
723 nsCOMPtr
<nsISupportsPRUint8
> p(do_QueryInterface(argPrimitive
));
724 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
730 aArgv
->setInt32(data
);
734 case nsISupportsPrimitive::TYPE_PRUINT16
: {
735 nsCOMPtr
<nsISupportsPRUint16
> p(do_QueryInterface(argPrimitive
));
736 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
742 aArgv
->setInt32(data
);
746 case nsISupportsPrimitive::TYPE_PRUINT32
: {
747 nsCOMPtr
<nsISupportsPRUint32
> p(do_QueryInterface(argPrimitive
));
748 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
754 aArgv
->setInt32(data
);
758 case nsISupportsPrimitive::TYPE_CHAR
: {
759 nsCOMPtr
<nsISupportsChar
> p(do_QueryInterface(argPrimitive
));
760 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
766 JSString
* str
= ::JS_NewStringCopyN(aCx
, &data
, 1);
767 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
769 aArgv
->setString(str
);
773 case nsISupportsPrimitive::TYPE_PRINT16
: {
774 nsCOMPtr
<nsISupportsPRInt16
> p(do_QueryInterface(argPrimitive
));
775 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
781 aArgv
->setInt32(data
);
785 case nsISupportsPrimitive::TYPE_PRINT32
: {
786 nsCOMPtr
<nsISupportsPRInt32
> p(do_QueryInterface(argPrimitive
));
787 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
793 aArgv
->setInt32(data
);
797 case nsISupportsPrimitive::TYPE_FLOAT
: {
798 nsCOMPtr
<nsISupportsFloat
> p(do_QueryInterface(argPrimitive
));
799 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
805 *aArgv
= ::JS_NumberValue(data
);
809 case nsISupportsPrimitive::TYPE_DOUBLE
: {
810 nsCOMPtr
<nsISupportsDouble
> p(do_QueryInterface(argPrimitive
));
811 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
817 *aArgv
= ::JS_NumberValue(data
);
821 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER
: {
822 nsCOMPtr
<nsISupportsInterfacePointer
> p(do_QueryInterface(argPrimitive
));
823 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
825 nsCOMPtr
<nsISupports
> data
;
826 nsIID
* iid
= nullptr;
828 p
->GetData(getter_AddRefs(data
));
830 NS_ENSURE_TRUE(iid
, NS_ERROR_UNEXPECTED
);
832 AutoFree
iidGuard(iid
); // Free iid upon destruction.
834 JS::Rooted
<JSObject
*> scope(aCx
, GetWindowProxy());
835 JS::Rooted
<JS::Value
> v(aCx
);
836 JSAutoRealm
ar(aCx
, scope
);
837 nsresult rv
= nsContentUtils::WrapNative(aCx
, data
, iid
, &v
);
838 NS_ENSURE_SUCCESS(rv
, rv
);
844 case nsISupportsPrimitive::TYPE_ID
:
845 case nsISupportsPrimitive::TYPE_PRUINT64
:
846 case nsISupportsPrimitive::TYPE_PRINT64
:
847 case nsISupportsPrimitive::TYPE_PRTIME
: {
848 NS_WARNING("Unsupported primitive type used");
853 NS_WARNING("Unknown primitive type used");
861 nsresult
nsJSContext::InitClasses(JS::Handle
<JSObject
*> aGlobalObj
) {
864 JSContext
* cx
= jsapi
.cx();
865 JSAutoRealm
ar(cx
, aGlobalObj
);
870 bool nsJSContext::GetProcessingScriptTag() { return mProcessingScriptTag
; }
872 void nsJSContext::SetProcessingScriptTag(bool aFlag
) {
873 mProcessingScriptTag
= aFlag
;
877 void nsJSContext::SetLowMemoryState(bool aState
) {
878 JSContext
* cx
= danger::GetJSContext();
879 JS::SetLowMemoryState(cx
, aState
);
882 static void GarbageCollectImpl(JS::GCReason aReason
,
883 nsJSContext::IsShrinking aShrinking
,
884 const JS::SliceBudget
& aBudget
) {
885 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
886 "nsJSContext::GarbageCollectNow", GCCC
, JS::ExplainGCReason(aReason
));
888 bool wantIncremental
= !aBudget
.isUnlimited();
890 // We use danger::GetJSContext() since AutoJSAPI will assert if the current
891 // thread's context is null (such as during shutdown).
892 JSContext
* cx
= danger::GetJSContext();
894 if (!nsContentUtils::XPConnect() || !cx
) {
898 if (sScheduler
->InIncrementalGC() && wantIncremental
) {
899 // We're in the middle of incremental GC. Do another slice.
900 JS::PrepareForIncrementalGC(cx
);
901 JS::IncrementalGCSlice(cx
, aReason
, aBudget
);
905 JS::GCOptions options
= aShrinking
== nsJSContext::ShrinkingGC
906 ? JS::GCOptions::Shrink
907 : JS::GCOptions::Normal
;
909 if (!wantIncremental
|| aReason
== JS::GCReason::FULL_GC_TIMER
) {
910 sScheduler
->SetNeedsFullGC();
913 if (sScheduler
->NeedsFullGC()) {
914 JS::PrepareForFullGC(cx
);
917 if (wantIncremental
) {
918 // Incremental GC slices will be triggered by the GC Runner. If one doesn't
919 // already exist, create it in the GC_SLICE_END callback for the first
920 // slice being executed here.
921 JS::StartIncrementalGC(cx
, options
, aReason
, aBudget
);
923 JS::NonIncrementalGC(cx
, options
, aReason
);
928 void nsJSContext::GarbageCollectNow(JS::GCReason aReason
,
929 IsShrinking aShrinking
) {
930 GarbageCollectImpl(aReason
, aShrinking
, JS::SliceBudget::unlimited());
934 void nsJSContext::RunIncrementalGCSlice(JS::GCReason aReason
,
935 IsShrinking aShrinking
,
936 JS::SliceBudget
& aBudget
) {
937 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental GC", GCCC
);
938 GarbageCollectImpl(aReason
, aShrinking
, aBudget
);
941 static void FinishAnyIncrementalGC() {
942 AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC
);
944 if (sScheduler
->InIncrementalGC()) {
948 // We're in the middle of an incremental GC, so finish it.
949 JS::PrepareForIncrementalGC(jsapi
.cx());
950 JS::FinishIncrementalGC(jsapi
.cx(), JS::GCReason::CC_FORCED
);
954 static void FireForgetSkippable(bool aRemoveChildless
, TimeStamp aDeadline
) {
955 TimeStamp startTimeStamp
= TimeStamp::Now();
956 FinishAnyIncrementalGC();
958 JS::SliceBudget budget
=
959 sScheduler
->ComputeForgetSkippableBudget(startTimeStamp
, aDeadline
);
960 bool earlyForgetSkippable
= sScheduler
->IsEarlyForgetSkippable();
961 nsCycleCollector_forgetSkippable(startTimeStamp
, budget
, !aDeadline
.IsNull(),
962 aRemoveChildless
, earlyForgetSkippable
);
963 TimeStamp now
= TimeStamp::Now();
964 sScheduler
->NoteForgetSkippableComplete(now
,
965 nsCycleCollector_suspectedCount());
967 TimeDuration duration
= now
- startTimeStamp
;
968 if (duration
.ToSeconds()) {
969 TimeDuration idleDuration
;
970 if (!aDeadline
.IsNull()) {
971 if (aDeadline
< now
) {
972 // This slice overflowed the idle period.
973 if (aDeadline
> startTimeStamp
) {
974 idleDuration
= aDeadline
- startTimeStamp
;
977 idleDuration
= duration
;
982 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
983 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE
, percent
);
987 static void MaybeLogStats(const CycleCollectorResults
& aResults
,
988 uint32_t aCleanups
) {
989 if (!StaticPrefs::javascript_options_mem_log() && !sCCStats
->mFile
) {
993 TimeDuration delta
= GetCollectionTimeDelta();
996 if (aResults
.mMergedZones
) {
997 mergeMsg
.AssignLiteral(" merged");
1001 if (aResults
.mForcedGC
) {
1002 gcMsg
.AssignLiteral(", forced a GC");
1005 const char16_t
* kFmt
=
1006 u
"CC(T+%.1f)[%s-%i] max pause: %.fms, total time: %.fms, slices: %lu, "
1007 u
"suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu "
1008 u
"RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
1009 u
"ForgetSkippable %lu times before CC, min: %.f ms, max: %.f ms, avg: "
1010 u
"%.f ms, total: %.f ms, max sync: %.f ms, removed: %lu";
1012 nsTextFormatter::ssprintf(
1013 msg
, kFmt
, delta
.ToMicroseconds() / PR_USEC_PER_SEC
,
1014 ProcessNameForCollectorLog(), getpid(),
1015 sCCStats
->mMaxSliceTime
.ToMilliseconds(),
1016 sCCStats
->mTotalSliceTime
.ToMilliseconds(), aResults
.mNumSlices
,
1017 sCCStats
->mSuspected
, aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
,
1018 mergeMsg
.get(), aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1019 sScheduler
->mCCollectedWaitingForGC
,
1020 sScheduler
->mCCollectedZonesWaitingForGC
,
1021 sScheduler
->mLikelyShortLivingObjectsNeedingGC
, gcMsg
.get(),
1022 sCCStats
->mForgetSkippableBeforeCC
,
1023 sCCStats
->mMinForgetSkippableTime
.ToMilliseconds(),
1024 sCCStats
->mMaxForgetSkippableTime
.ToMilliseconds(),
1025 sCCStats
->mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1026 sCCStats
->mTotalForgetSkippableTime
.ToMilliseconds(),
1027 sCCStats
->mMaxSkippableDuration
.ToMilliseconds(),
1028 sCCStats
->mRemovedPurples
);
1029 if (StaticPrefs::javascript_options_mem_log()) {
1030 nsCOMPtr
<nsIConsoleService
> cs
=
1031 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1033 cs
->LogStringMessage(msg
.get());
1036 if (sCCStats
->mFile
) {
1037 fprintf(sCCStats
->mFile
, "%s\n", NS_ConvertUTF16toUTF8(msg
).get());
1041 static void MaybeNotifyStats(const CycleCollectorResults
& aResults
,
1042 TimeDuration aCCNowDuration
, uint32_t aCleanups
) {
1043 if (!StaticPrefs::javascript_options_mem_notify()) {
1047 const char16_t
* kJSONFmt
=
1048 u
"{ \"timestamp\": %llu, "
1049 u
"\"duration\": %.f, "
1050 u
"\"max_slice_pause\": %.f, "
1051 u
"\"total_slice_pause\": %.f, "
1052 u
"\"max_finish_gc_duration\": %.f, "
1053 u
"\"max_sync_skippable_duration\": %.f, "
1054 u
"\"suspected\": %lu, "
1057 u
"\"GCed\": %lu }, "
1058 u
"\"collected\": { "
1060 u
"\"GCed\": %lu }, "
1061 u
"\"waiting_for_gc\": %lu, "
1062 u
"\"zones_waiting_for_gc\": %lu, "
1063 u
"\"short_living_objects_waiting_for_gc\": %lu, "
1064 u
"\"forced_gc\": %d, "
1065 u
"\"forget_skippable\": { "
1066 u
"\"times_before_cc\": %lu, "
1071 u
"\"removed\": %lu } "
1075 nsTextFormatter::ssprintf(
1076 json
, kJSONFmt
, PR_Now(), aCCNowDuration
.ToMilliseconds(),
1077 sCCStats
->mMaxSliceTime
.ToMilliseconds(),
1078 sCCStats
->mTotalSliceTime
.ToMilliseconds(),
1079 sCCStats
->mMaxGCDuration
.ToMilliseconds(),
1080 sCCStats
->mMaxSkippableDuration
.ToMilliseconds(), sCCStats
->mSuspected
,
1081 aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
,
1082 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1083 sScheduler
->mCCollectedWaitingForGC
,
1084 sScheduler
->mCCollectedZonesWaitingForGC
,
1085 sScheduler
->mLikelyShortLivingObjectsNeedingGC
, aResults
.mForcedGC
,
1086 sCCStats
->mForgetSkippableBeforeCC
,
1087 sCCStats
->mMinForgetSkippableTime
.ToMilliseconds(),
1088 sCCStats
->mMaxForgetSkippableTime
.ToMilliseconds(),
1089 sCCStats
->mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1090 sCCStats
->mTotalForgetSkippableTime
.ToMilliseconds(),
1091 sCCStats
->mRemovedPurples
);
1092 nsCOMPtr
<nsIObserverService
> observerService
=
1093 mozilla::services::GetObserverService();
1094 if (observerService
) {
1095 observerService
->NotifyObservers(nullptr, "cycle-collection-statistics",
1101 void nsJSContext::CycleCollectNow(CCReason aReason
,
1102 nsICycleCollectorListener
* aListener
) {
1103 if (!NS_IsMainThread()) {
1107 AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC
);
1109 PrepareForCycleCollectionSlice(aReason
, TimeStamp());
1110 nsCycleCollector_collect(aReason
, aListener
);
1111 sCCStats
->AfterCycleCollectionSlice();
1115 void nsJSContext::PrepareForCycleCollectionSlice(CCReason aReason
,
1116 TimeStamp aDeadline
) {
1117 TimeStamp beginTime
= TimeStamp::Now();
1119 // Before we begin the cycle collection, make sure there is no active GC.
1120 TimeStamp afterGCTime
;
1121 if (sScheduler
->InIncrementalGC()) {
1122 FinishAnyIncrementalGC();
1123 afterGCTime
= TimeStamp::Now();
1126 if (!sScheduler
->IsCollectingCycles()) {
1127 sCCStats
->PrepareForCycleCollection(beginTime
);
1128 sScheduler
->NoteCCBegin();
1131 sCCStats
->AfterPrepareForCycleCollectionSlice(aDeadline
, beginTime
,
1136 void nsJSContext::RunCycleCollectorSlice(CCReason aReason
,
1137 TimeStamp aDeadline
) {
1138 if (!NS_IsMainThread()) {
1142 PrepareForCycleCollectionSlice(aReason
, aDeadline
);
1144 // Decide how long we want to budget for this slice.
1145 if (sIncrementalCC
) {
1146 bool preferShorterSlices
;
1147 JS::SliceBudget budget
= sScheduler
->ComputeCCSliceBudget(
1148 aDeadline
, sCCStats
->mBeginTime
, sCCStats
->mEndSliceTime
,
1149 TimeStamp::Now(), &preferShorterSlices
);
1150 nsCycleCollector_collectSlice(budget
, aReason
, preferShorterSlices
);
1152 JS::SliceBudget budget
= JS::SliceBudget::unlimited();
1153 nsCycleCollector_collectSlice(budget
, aReason
, false);
1156 sCCStats
->AfterCycleCollectionSlice();
1160 void nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget
) {
1161 if (!NS_IsMainThread()) {
1165 AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", GCCC
);
1167 PrepareForCycleCollectionSlice(CCReason::API
, TimeStamp());
1169 JS::SliceBudget budget
= JS::SliceBudget(JS::WorkBudget(aWorkBudget
));
1170 nsCycleCollector_collectSlice(budget
, CCReason::API
);
1172 sCCStats
->AfterCycleCollectionSlice();
1175 void nsJSContext::ClearMaxCCSliceTime() {
1176 sCCStats
->mMaxSliceTimeSinceClear
= TimeDuration();
1179 uint32_t nsJSContext::GetMaxCCSliceTimeSinceClear() {
1180 return sCCStats
->mMaxSliceTimeSinceClear
.ToMilliseconds();
1184 void nsJSContext::BeginCycleCollectionCallback(CCReason aReason
) {
1185 MOZ_ASSERT(NS_IsMainThread());
1187 TimeStamp startTime
= TimeStamp::Now();
1188 sCCStats
->PrepareForCycleCollection(startTime
);
1190 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1191 // is particularly useful if we recently finished a GC.
1192 if (sScheduler
->IsEarlyForgetSkippable()) {
1193 while (sScheduler
->IsEarlyForgetSkippable()) {
1194 FireForgetSkippable(false, TimeStamp());
1196 sCCStats
->AfterSyncForgetSkippable(startTime
);
1199 if (sShuttingDown
) {
1203 sScheduler
->InitCCRunnerStateMachine(
1204 mozilla::CCGCScheduler::CCRunnerState::CycleCollecting
, aReason
);
1205 sScheduler
->EnsureCCRunner(kICCIntersliceDelay
, kIdleICCSliceBudget
);
1209 void nsJSContext::EndCycleCollectionCallback(
1210 const CycleCollectorResults
& aResults
) {
1211 MOZ_ASSERT(NS_IsMainThread());
1213 sScheduler
->KillCCRunner();
1215 // Update timing information for the current slice before we log it, if
1216 // we previously called PrepareForCycleCollectionSlice(). During shutdown
1217 // CCs, this won't happen.
1218 sCCStats
->AfterCycleCollectionSlice();
1220 TimeStamp endCCTimeStamp
= TimeStamp::Now();
1221 MOZ_ASSERT(endCCTimeStamp
>= sCCStats
->mBeginTime
);
1222 TimeDuration ccNowDuration
= endCCTimeStamp
- sCCStats
->mBeginTime
;
1223 TimeStamp prevCCEnd
= sScheduler
->GetLastCCEndTime();
1225 sScheduler
->NoteCCEnd(aResults
, endCCTimeStamp
);
1227 // Log information about the CC via telemetry, JSON and the console.
1229 sCCStats
->SendTelemetry(ccNowDuration
, prevCCEnd
);
1231 uint32_t cleanups
= std::max(sCCStats
->mForgetSkippableBeforeCC
, 1u);
1233 MaybeLogStats(aResults
, cleanups
);
1235 MaybeNotifyStats(aResults
, ccNowDuration
, cleanups
);
1237 // Update global state to indicate we have just run a cycle collection.
1240 // If we need a GC after this CC (typically because lots of GCed objects or
1241 // zones have been collected in the CC), schedule it.
1243 if (sScheduler
->NeedsGCAfterCC()) {
1245 TimeDuration::FromMilliseconds(
1246 StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration
,
1247 "A max duration ICC shouldn't reduce GC delay to 0");
1250 if (sScheduler
->PreferFasterCollection()) {
1251 // If we collected lots of objects, trigger the next GC sooner so that
1252 // GC can cut JS-to-native edges and native objects can be then deleted.
1253 delay
= TimeDuration::FromMilliseconds(
1254 StaticPrefs::javascript_options_gc_delay_interslice());
1256 delay
= TimeDuration::FromMilliseconds(
1257 StaticPrefs::javascript_options_gc_delay()) -
1258 std::min(ccNowDuration
, kMaxICCDuration
);
1261 sScheduler
->PokeGC(JS::GCReason::CC_FINISHED
, nullptr, delay
);
1263 #if defined(MOZ_MEMORY)
1266 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1267 jemalloc_free_dirty_pages();
1272 bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline
) {
1273 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental CC", GCCC
);
1276 mCurrentCollectionHasSeenNonIdle
= true;
1277 } else if (mPreferFasterCollection
) {
1278 // We found some idle time, try to utilize that a bit more given that
1279 // we're in a mode where idle time is rare.
1280 aDeadline
= aDeadline
+ TimeDuration::FromMilliseconds(5.0);
1283 bool didDoWork
= false;
1285 // The CC/GC scheduler (sScheduler) decides what action(s) to take during
1286 // this invocation of the CC runner.
1288 // This may be zero, one, or multiple actions. (Zero is when CC is blocked by
1289 // incremental GC, or when the scheduler determined that a CC is no longer
1290 // needed.) Loop until the scheduler finishes this invocation by returning
1291 // `Yield` in step.mYield.
1294 step
= sScheduler
->AdvanceCCRunner(aDeadline
, TimeStamp::Now(),
1295 nsCycleCollector_suspectedCount());
1296 switch (step
.mAction
) {
1297 case CCRunnerAction::None
:
1300 case CCRunnerAction::MinorGC
:
1301 JS::MaybeRunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
1302 step
.mParam
.mReason
);
1303 sScheduler
->NoteMinorGCEnd();
1306 case CCRunnerAction::ForgetSkippable
:
1307 // 'Forget skippable' only, then end this invocation.
1308 FireForgetSkippable(bool(step
.mParam
.mRemoveChildless
), aDeadline
);
1311 case CCRunnerAction::CleanupContentUnbinder
:
1312 // Clear content unbinder before the first actual CC slice.
1313 Element::ClearContentUnbinder();
1316 case CCRunnerAction::CleanupDeferred
:
1317 // and if time still permits, perform deferred deletions.
1318 nsCycleCollector_doDeferredDeletion();
1321 case CCRunnerAction::CycleCollect
:
1322 // Cycle collection slice.
1323 nsJSContext::RunCycleCollectorSlice(step
.mParam
.mCCReason
, aDeadline
);
1326 case CCRunnerAction::StopRunning
:
1327 // End this CC, either because we have run a cycle collection slice, or
1328 // because a CC is no longer needed.
1329 sScheduler
->KillCCRunner();
1333 if (step
.mAction
!= CCRunnerAction::None
) {
1336 } while (step
.mYield
== CCRunnerYield::Continue
);
1342 bool nsJSContext::HasHadCleanupSinceLastGC() {
1343 return sScheduler
->IsEarlyForgetSkippable(1);
1347 void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason
,
1348 mozilla::TimeStamp aDeadline
) {
1349 sScheduler
->RunNextCollectorTimer(aReason
, aDeadline
);
1353 void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell
* aDocShell
,
1354 JS::GCReason aReason
) {
1355 if (!aDocShell
|| !XRE_IsContentProcess()) {
1359 BrowsingContext
* bc
= aDocShell
->GetBrowsingContext();
1364 BrowsingContext
* root
= bc
->Top();
1366 // We don't want to run collectors when loading the top level page.
1370 nsIDocShell
* rootDocShell
= root
->GetDocShell();
1371 if (!rootDocShell
) {
1375 Document
* rootDocument
= rootDocShell
->GetDocument();
1376 if (!rootDocument
||
1377 rootDocument
->GetReadyStateEnum() != Document::READYSTATE_COMPLETE
||
1378 rootDocument
->IsInBackgroundWindow()) {
1382 PresShell
* presShell
= rootDocument
->GetPresShell();
1387 nsViewManager
* vm
= presShell
->GetViewManager();
1392 if (!sScheduler
->IsUserActive() &&
1393 (sScheduler
->InIncrementalGC() || sScheduler
->IsCollectingCycles())) {
1394 Maybe
<TimeStamp
> next
= nsRefreshDriver::GetNextTickHint();
1395 if (next
.isSome()) {
1396 // Try to not delay the next RefreshDriver tick, so give a reasonable
1397 // deadline for collectors.
1398 sScheduler
->RunNextCollectorTimer(aReason
, next
.value());
1402 nsCOMPtr
<nsIDocShell
> shell
= aDocShell
;
1403 NS_DispatchToCurrentThreadQueue(
1404 NS_NewRunnableFunction("nsJSContext::MaybeRunNextCollectorSlice",
1406 nsIDocShell::BusyFlags busyFlags
=
1407 nsIDocShell::BUSY_FLAGS_NONE
;
1408 shell
->GetBusyFlags(&busyFlags
);
1409 if (busyFlags
== nsIDocShell::BUSY_FLAGS_NONE
) {
1413 // In order to improve performance on the next
1414 // page, run a minor GC. The 16ms limit ensures
1415 // it isn't called all the time if there are for
1416 // example multiple iframes loading at the same
1418 JS::RunNurseryCollection(
1419 CycleCollectedJSRuntime::Get()->Runtime(),
1420 JS::GCReason::PREPARE_FOR_PAGELOAD
,
1421 mozilla::TimeDuration::FromMilliseconds(16));
1423 EventQueuePriority::Idle
);
1427 void nsJSContext::PokeGC(JS::GCReason aReason
, JSObject
* aObj
,
1428 TimeDuration aDelay
) {
1429 sScheduler
->PokeGC(aReason
, aObj
, aDelay
);
1433 void nsJSContext::MaybePokeGC() {
1434 if (sShuttingDown
) {
1438 JSRuntime
* rt
= CycleCollectedJSRuntime::Get()->Runtime();
1439 JS::GCReason reason
= JS::WantEagerMinorGC(rt
);
1440 if (reason
!= JS::GCReason::NO_REASON
) {
1441 MOZ_ASSERT(reason
== JS::GCReason::EAGER_NURSERY_COLLECTION
);
1442 sScheduler
->PokeMinorGC(reason
);
1445 // Bug 1772638: For now, only do eager minor GCs. Eager major GCs regress some
1446 // benchmarks. Hopefully that will be worked out and this will check for
1447 // whether an eager major GC is needed.
1450 void nsJSContext::DoLowMemoryGC() {
1451 if (sShuttingDown
) {
1454 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1455 nsJSContext::ShrinkingGC
);
1456 nsJSContext::CycleCollectNow(CCReason::MEM_PRESSURE
);
1457 if (sScheduler
->NeedsGCAfterCC()) {
1458 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1459 nsJSContext::ShrinkingGC
);
1464 void nsJSContext::LowMemoryGC() {
1465 RefPtr
<CCGCScheduler::MayGCPromise
> mbPromise
=
1466 CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE
);
1468 // Normally when the promise is null it means that IPC failed, that probably
1469 // means that something bad happened, don't bother with the GC.
1473 GetMainThreadSerialEventTarget(), __func__
,
1474 [](bool aIgnored
) { DoLowMemoryGC(); },
1475 [](mozilla::ipc::ResponseRejectReason r
) {});
1479 void nsJSContext::MaybePokeCC() {
1480 sScheduler
->MaybePokeCC(TimeStamp::NowLoRes(),
1481 nsCycleCollector_suspectedCount());
1484 static void DOMGCSliceCallback(JSContext
* aCx
, JS::GCProgress aProgress
,
1485 const JS::GCDescription
& aDesc
) {
1486 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
1488 static TimeStamp sCurrentGCStartTime
;
1490 switch (aProgress
) {
1491 case JS::GC_CYCLE_BEGIN
: {
1492 // Prevent cycle collections and shrinking during incremental GC.
1493 sScheduler
->NoteGCBegin(aDesc
.reason_
);
1494 sCurrentGCStartTime
= TimeStamp::Now();
1498 case JS::GC_CYCLE_END
: {
1499 TimeDuration delta
= GetCollectionTimeDelta();
1501 if (StaticPrefs::javascript_options_mem_log()) {
1503 gcstats
.Adopt(aDesc
.formatSummaryMessage(aCx
));
1504 nsAutoString prefix
;
1505 nsTextFormatter::ssprintf(prefix
, u
"GC(T+%.1f)[%s-%i] ",
1507 ProcessNameForCollectorLog(), getpid());
1508 nsString msg
= prefix
+ gcstats
;
1509 nsCOMPtr
<nsIConsoleService
> cs
=
1510 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1512 cs
->LogStringMessage(msg
.get());
1516 sScheduler
->NoteGCEnd();
1518 // May need to kill the GC runner
1519 sScheduler
->KillGCRunner();
1521 nsJSContext::MaybePokeCC();
1523 #if defined(MOZ_MEMORY)
1524 bool freeDirty
= false;
1526 if (aDesc
.isZone_
) {
1527 sScheduler
->PokeFullGC();
1529 #if defined(MOZ_MEMORY)
1532 sScheduler
->SetNeedsFullGC(false);
1533 sScheduler
->KillFullGCTimer();
1536 if (sScheduler
->IsCCNeeded(TimeStamp::Now(),
1537 nsCycleCollector_suspectedCount()) !=
1538 CCReason::NO_REASON
) {
1539 #if defined(MOZ_MEMORY)
1540 // We're likely to free the dirty pages after CC.
1543 nsCycleCollector_dispatchDeferredDeletion();
1546 MOZ_ASSERT(sCurrentGCStartTime
);
1547 Telemetry::Accumulate(
1548 Telemetry::GC_IN_PROGRESS_MS
,
1549 (TimeStamp::Now() - sCurrentGCStartTime
).ToMilliseconds());
1551 #if defined(MOZ_MEMORY)
1554 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1555 jemalloc_free_dirty_pages();
1561 case JS::GC_SLICE_BEGIN
:
1564 case JS::GC_SLICE_END
:
1565 sScheduler
->NoteGCSliceEnd(aDesc
.lastSliceStart(aCx
),
1566 aDesc
.lastSliceEnd(aCx
));
1568 if (sShuttingDown
) {
1569 sScheduler
->KillGCRunner();
1571 // If incremental GC wasn't triggered by GCTimerFired, we may not have a
1572 // runner to ensure all the slices are handled. So, create the runner
1574 sScheduler
->EnsureOrResetGCRunner();
1577 if (sScheduler
->IsCCNeeded(TimeStamp::Now(),
1578 nsCycleCollector_suspectedCount()) !=
1579 CCReason::NO_REASON
) {
1580 nsCycleCollector_dispatchDeferredDeletion();
1583 if (StaticPrefs::javascript_options_mem_log()) {
1585 gcstats
.Adopt(aDesc
.formatSliceMessage(aCx
));
1586 nsAutoString prefix
;
1587 nsTextFormatter::ssprintf(prefix
, u
"[%s-%i] ",
1588 ProcessNameForCollectorLog(), getpid());
1589 nsString msg
= prefix
+ gcstats
;
1590 nsCOMPtr
<nsIConsoleService
> cs
=
1591 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1593 cs
->LogStringMessage(msg
.get());
1600 MOZ_CRASH("Unexpected GCProgress value");
1603 if (sPrevGCSliceCallback
) {
1604 (*sPrevGCSliceCallback
)(aCx
, aProgress
, aDesc
);
1608 void nsJSContext::SetWindowProxy(JS::Handle
<JSObject
*> aWindowProxy
) {
1609 mWindowProxy
= aWindowProxy
;
1612 JSObject
* nsJSContext::GetWindowProxy() { return mWindowProxy
; }
1614 void nsJSContext::LikelyShortLivingObjectCreated() {
1615 ++sScheduler
->mLikelyShortLivingObjectsNeedingGC
;
1618 void mozilla::dom::StartupJSEnvironment() {
1619 // initialize all our statics, so that we can restart XPCOM
1620 sIsInitialized
= false;
1621 sShuttingDown
= false;
1622 sCCStats
= CycleCollectorStats::Get();
1625 static void SetGCParameter(JSGCParamKey aParam
, uint32_t aValue
) {
1628 JS_SetGCParameter(jsapi
.cx(), aParam
, aValue
);
1631 static void ResetGCParameter(JSGCParamKey aParam
) {
1634 JS_ResetGCParameter(jsapi
.cx(), aParam
);
1637 static void SetMemoryPrefChangedCallbackMB(const char* aPrefName
,
1639 int32_t prefMB
= Preferences::GetInt(aPrefName
, -1);
1640 // handle overflow and negative pref values
1641 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefMB
) * 1024 * 1024;
1642 if (prefB
.isValid() && prefB
.value() >= 0) {
1643 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1645 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1649 static void SetMemoryNurseryPrefChangedCallback(const char* aPrefName
,
1651 int32_t prefKB
= Preferences::GetInt(aPrefName
, -1);
1652 // handle overflow and negative pref values
1653 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefKB
) * 1024;
1654 if (prefB
.isValid() && prefB
.value() >= 0) {
1655 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1657 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1661 static void SetMemoryPrefChangedCallbackInt(const char* aPrefName
,
1663 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1664 // handle overflow and negative pref values
1665 if (pref
>= 0 && pref
< 10000) {
1666 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1668 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1672 static void SetMemoryPrefChangedCallbackBool(const char* aPrefName
,
1674 bool pref
= Preferences::GetBool(aPrefName
);
1675 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1678 static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName
,
1680 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1681 // handle overflow and negative pref values
1682 if (pref
> 0 && pref
< 100000) {
1683 sScheduler
->SetActiveIntersliceGCBudget(
1684 TimeDuration::FromMilliseconds(pref
));
1685 SetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
, pref
);
1687 ResetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
);
1691 static void SetIncrementalCCPrefChangedCallback(const char* aPrefName
,
1693 bool pref
= Preferences::GetBool(aPrefName
);
1694 sIncrementalCC
= pref
;
1697 class JSDispatchableRunnable final
: public Runnable
{
1698 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable
); }
1701 explicit JSDispatchableRunnable(JS::Dispatchable
* aDispatchable
)
1702 : mozilla::Runnable("JSDispatchableRunnable"),
1703 mDispatchable(aDispatchable
) {
1704 MOZ_ASSERT(mDispatchable
);
1708 NS_IMETHOD
Run() override
{
1709 MOZ_ASSERT(NS_IsMainThread());
1714 JS::Dispatchable::MaybeShuttingDown maybeShuttingDown
=
1715 sShuttingDown
? JS::Dispatchable::ShuttingDown
1716 : JS::Dispatchable::NotShuttingDown
;
1718 mDispatchable
->run(jsapi
.cx(), maybeShuttingDown
);
1719 mDispatchable
= nullptr; // mDispatchable may delete itself
1725 JS::Dispatchable
* mDispatchable
;
1728 static bool DispatchToEventLoop(void* closure
,
1729 JS::Dispatchable
* aDispatchable
) {
1730 MOZ_ASSERT(!closure
);
1732 // This callback may execute either on the main thread or a random JS-internal
1733 // helper thread. This callback can be called during shutdown so we cannot
1734 // simply NS_DispatchToMainThread. Failure during shutdown is expected and
1735 // properly handled by the JS engine.
1737 nsCOMPtr
<nsIEventTarget
> mainTarget
= GetMainThreadSerialEventTarget();
1742 RefPtr
<JSDispatchableRunnable
> r
= new JSDispatchableRunnable(aDispatchable
);
1743 MOZ_ALWAYS_SUCCEEDS(mainTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
1747 static bool ConsumeStream(JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
,
1748 JS::MimeType aMimeType
,
1749 JS::StreamConsumer
* aConsumer
) {
1750 return FetchUtil::StreamResponseToJS(aCx
, aObj
, aMimeType
, aConsumer
,
1754 static JS::SliceBudget
CreateGCSliceBudget(JS::GCReason aReason
,
1756 return sScheduler
->CreateGCSliceBudget(
1757 mozilla::TimeDuration::FromMilliseconds(aMillis
), false, false);
1760 void nsJSContext::EnsureStatics() {
1761 if (sIsInitialized
) {
1762 if (!nsContentUtils::XPConnect()) {
1768 // Let's make sure that our main thread is the same as the xpcom main thread.
1769 MOZ_ASSERT(NS_IsMainThread());
1772 new (&sSchedulerStorage
) CCGCScheduler(); // Reset the scheduler state.
1777 sPrevGCSliceCallback
= JS::SetGCSliceCallback(jsapi
.cx(), DOMGCSliceCallback
);
1779 JS::SetCreateGCSliceBudgetCallback(jsapi
.cx(), CreateGCSliceBudget
);
1781 JS::InitDispatchToEventLoop(jsapi
.cx(), DispatchToEventLoop
, nullptr);
1782 JS::InitConsumeStreamCallback(jsapi
.cx(), ConsumeStream
,
1783 FetchUtil::ReportJSStreamError
);
1785 // Set these global xpconnect options...
1786 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB
,
1787 "javascript.options.mem.max",
1788 (void*)JSGC_MAX_BYTES
);
1789 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
1790 "javascript.options.mem.nursery.min_kb",
1791 (void*)JSGC_MIN_NURSERY_BYTES
);
1792 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
1793 "javascript.options.mem.nursery.max_kb",
1794 (void*)JSGC_MAX_NURSERY_BYTES
);
1796 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1797 "javascript.options.mem.gc_per_zone",
1798 (void*)JSGC_PER_ZONE_GC_ENABLED
);
1800 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1801 "javascript.options.mem.gc_incremental",
1802 (void*)JSGC_INCREMENTAL_GC_ENABLED
);
1804 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1805 "javascript.options.mem.gc_generational",
1806 (void*)JSGC_NURSERY_ENABLED
);
1808 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1809 "javascript.options.mem.gc_compacting",
1810 (void*)JSGC_COMPACTING_ENABLED
);
1812 #ifdef NIGHTLY_BUILD
1813 Preferences::RegisterCallbackAndCall(
1814 SetMemoryPrefChangedCallbackBool
,
1815 "javascript.options.mem.gc_experimental_semispace_nursery",
1816 (void*)JSGC_SEMISPACE_NURSERY_ENABLED
);
1819 Preferences::RegisterCallbackAndCall(
1820 SetMemoryPrefChangedCallbackBool
,
1821 "javascript.options.mem.gc_parallel_marking",
1822 (void*)JSGC_PARALLEL_MARKING_ENABLED
);
1824 Preferences::RegisterCallbackAndCall(
1825 SetMemoryPrefChangedCallbackInt
,
1826 "javascript.options.mem.gc_parallel_marking_threshold_mb",
1827 (void*)JSGC_PARALLEL_MARKING_THRESHOLD_MB
);
1829 Preferences::RegisterCallbackAndCall(
1830 SetMemoryPrefChangedCallbackInt
,
1831 "javascript.options.mem.gc_max_parallel_marking_threads",
1832 (void*)JSGC_MAX_MARKING_THREADS
);
1834 Preferences::RegisterCallbackAndCall(
1835 SetMemoryGCSliceTimePrefChangedCallback
,
1836 "javascript.options.mem.gc_incremental_slice_ms");
1838 Preferences::RegisterCallbackAndCall(
1839 SetMemoryPrefChangedCallbackBool
,
1840 "javascript.options.mem.incremental_weakmap",
1841 (void*)JSGC_INCREMENTAL_WEAKMAP_ENABLED
);
1843 Preferences::RegisterCallbackAndCall(
1844 SetMemoryPrefChangedCallbackInt
,
1845 "javascript.options.mem.gc_high_frequency_time_limit_ms",
1846 (void*)JSGC_HIGH_FREQUENCY_TIME_LIMIT
);
1848 Preferences::RegisterCallbackAndCall(
1849 SetMemoryPrefChangedCallbackInt
,
1850 "javascript.options.mem.gc_low_frequency_heap_growth",
1851 (void*)JSGC_LOW_FREQUENCY_HEAP_GROWTH
);
1853 Preferences::RegisterCallbackAndCall(
1854 SetMemoryPrefChangedCallbackInt
,
1855 "javascript.options.mem.gc_high_frequency_large_heap_growth",
1856 (void*)JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH
);
1858 Preferences::RegisterCallbackAndCall(
1859 SetMemoryPrefChangedCallbackInt
,
1860 "javascript.options.mem.gc_high_frequency_small_heap_growth",
1861 (void*)JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH
);
1863 Preferences::RegisterCallbackAndCall(
1864 SetMemoryPrefChangedCallbackBool
,
1865 "javascript.options.mem.gc_balanced_heap_limits",
1866 (void*)JSGC_BALANCED_HEAP_LIMITS_ENABLED
);
1868 Preferences::RegisterCallbackAndCall(
1869 SetMemoryPrefChangedCallbackInt
,
1870 "javascript.options.mem.gc_heap_growth_factor",
1871 (void*)JSGC_HEAP_GROWTH_FACTOR
);
1873 Preferences::RegisterCallbackAndCall(
1874 SetMemoryPrefChangedCallbackInt
,
1875 "javascript.options.mem.gc_small_heap_size_max_mb",
1876 (void*)JSGC_SMALL_HEAP_SIZE_MAX
);
1878 Preferences::RegisterCallbackAndCall(
1879 SetMemoryPrefChangedCallbackInt
,
1880 "javascript.options.mem.gc_large_heap_size_min_mb",
1881 (void*)JSGC_LARGE_HEAP_SIZE_MIN
);
1883 Preferences::RegisterCallbackAndCall(
1884 SetMemoryPrefChangedCallbackInt
,
1885 "javascript.options.mem.gc_allocation_threshold_mb",
1886 (void*)JSGC_ALLOCATION_THRESHOLD
);
1888 Preferences::RegisterCallbackAndCall(
1889 SetMemoryPrefChangedCallbackInt
,
1890 "javascript.options.mem.gc_malloc_threshold_base_mb",
1891 (void*)JSGC_MALLOC_THRESHOLD_BASE
);
1893 Preferences::RegisterCallbackAndCall(
1894 SetMemoryPrefChangedCallbackInt
,
1895 "javascript.options.mem.gc_small_heap_incremental_limit",
1896 (void*)JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
);
1897 Preferences::RegisterCallbackAndCall(
1898 SetMemoryPrefChangedCallbackInt
,
1899 "javascript.options.mem.gc_large_heap_incremental_limit",
1900 (void*)JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
);
1902 Preferences::RegisterCallbackAndCall(
1903 SetMemoryPrefChangedCallbackInt
,
1904 "javascript.options.mem.gc_urgent_threshold_mb",
1905 (void*)JSGC_URGENT_THRESHOLD_MB
);
1907 Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback
,
1908 "dom.cycle_collector.incremental");
1910 Preferences::RegisterCallbackAndCall(
1911 SetMemoryPrefChangedCallbackInt
,
1912 "javascript.options.mem.gc_min_empty_chunk_count",
1913 (void*)JSGC_MIN_EMPTY_CHUNK_COUNT
);
1915 Preferences::RegisterCallbackAndCall(
1916 SetMemoryPrefChangedCallbackInt
,
1917 "javascript.options.mem.gc_helper_thread_ratio",
1918 (void*)JSGC_HELPER_THREAD_RATIO
);
1920 Preferences::RegisterCallbackAndCall(
1921 SetMemoryPrefChangedCallbackInt
,
1922 "javascript.options.mem.gc_max_helper_threads",
1923 (void*)JSGC_MAX_HELPER_THREADS
);
1925 Preferences::RegisterCallbackAndCall(
1926 SetMemoryPrefChangedCallbackInt
,
1927 "javascript.options.mem.nursery_eager_collection_threshold_kb",
1928 (void*)JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_KB
);
1930 Preferences::RegisterCallbackAndCall(
1931 SetMemoryPrefChangedCallbackInt
,
1932 "javascript.options.mem.nursery_eager_collection_threshold_percent",
1933 (void*)JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_PERCENT
);
1935 Preferences::RegisterCallbackAndCall(
1936 SetMemoryPrefChangedCallbackInt
,
1937 "javascript.options.mem.nursery_eager_collection_timeout_ms",
1938 (void*)JSGC_NURSERY_EAGER_COLLECTION_TIMEOUT_MS
);
1940 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1945 nsIObserver
* observer
= new nsJSEnvironmentObserver();
1946 obs
->AddObserver(observer
, "memory-pressure", false);
1947 obs
->AddObserver(observer
, "user-interaction-inactive", false);
1948 obs
->AddObserver(observer
, "user-interaction-active", false);
1949 obs
->AddObserver(observer
, "quit-application", false);
1950 obs
->AddObserver(observer
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
1951 obs
->AddObserver(observer
, "content-child-will-shutdown", false);
1953 sIsInitialized
= true;
1956 void mozilla::dom::ShutdownJSEnvironment() {
1957 sShuttingDown
= true;
1958 sScheduler
->Shutdown();
1962 AsyncErrorReporter::AsyncErrorReporter(xpc::ErrorReport
* aReport
)
1963 : Runnable("dom::AsyncErrorReporter"), mReport(aReport
) {}
1965 void AsyncErrorReporter::SerializeStack(JSContext
* aCx
,
1966 JS::Handle
<JSObject
*> aStack
) {
1967 mStackHolder
= MakeUnique
<SerializedStackHolder
>();
1968 mStackHolder
->SerializeMainThreadOrWorkletStack(aCx
, aStack
);
1971 void AsyncErrorReporter::SetException(JSContext
* aCx
,
1972 JS::Handle
<JS::Value
> aException
) {
1973 MOZ_ASSERT(NS_IsMainThread());
1974 mException
.init(aCx
, aException
);
1975 mHasException
= true;
1978 NS_IMETHODIMP
AsyncErrorReporter::Run() {
1980 // We're only using this context to deserialize a stack to report to the
1981 // console, so the scope we use doesn't matter. Stack frame filtering happens
1982 // based on the principal encoded into the frame and the caller compartment,
1983 // not the compartment of the frame object, and the console reporting code
1984 // will not be using our context, and therefore will not care what compartment
1986 DebugOnly
<bool> ok
= jsapi
.Init(xpc::PrivilegedJunkScope());
1987 MOZ_ASSERT(ok
, "Problem with system global?");
1988 JSContext
* cx
= jsapi
.cx();
1989 JS::Rooted
<JSObject
*> stack(cx
);
1990 JS::Rooted
<JSObject
*> stackGlobal(cx
);
1992 stack
= mStackHolder
->ReadStack(cx
);
1994 stackGlobal
= JS::CurrentGlobalOrNull(cx
);
1998 JS::Rooted
<Maybe
<JS::Value
>> exception(cx
, Nothing());
1999 if (mHasException
) {
2000 MOZ_ASSERT(NS_IsMainThread());
2001 exception
= Some(mException
);
2002 // Remove our reference to the exception.
2003 mException
.setUndefined();
2004 mHasException
= false;
2007 mReport
->LogToConsoleWithStack(nullptr, exception
, stack
, stackGlobal
);
2011 // A fast-array class for JS. This class supports both nsIJSScriptArray and
2012 // nsIArray. If it is JS itself providing and consuming this class, all work
2013 // can be done via nsIJSScriptArray, and avoid the conversion of elements
2014 // to/from nsISupports.
2015 // When consumed by non-JS (eg, another script language), conversion is done
2017 class nsJSArgArray final
: public nsIJSArgArray
{
2019 nsJSArgArray(JSContext
* aContext
, uint32_t argc
, const JS::Value
* argv
,
2023 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2024 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray
,
2031 nsresult
GetArgs(uint32_t* argc
, void** argv
) override
;
2033 void ReleaseJSObjects();
2037 JSContext
* mContext
;
2038 JS::Heap
<JS::Value
>* mArgv
;
2042 nsJSArgArray::nsJSArgArray(JSContext
* aContext
, uint32_t argc
,
2043 const JS::Value
* argv
, nsresult
* prv
)
2044 : mContext(aContext
), mArgv(nullptr), mArgc(argc
) {
2045 // copy the array - we don't know its lifetime, and ours is tied to xpcom
2048 mArgv
= new (fallible
) JS::Heap
<JS::Value
>[argc
];
2050 *prv
= NS_ERROR_OUT_OF_MEMORY
;
2055 // Callers are allowed to pass in a null argv even for argc > 0. They can
2056 // then use GetArgs to initialize the values.
2058 for (uint32_t i
= 0; i
< argc
; ++i
) mArgv
[i
] = argv
[i
];
2062 mozilla::HoldJSObjects(this);
2068 nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); }
2070 void nsJSArgArray::ReleaseJSObjects() {
2075 mozilla::DropJSObjects(this);
2079 // QueryInterface implementation for nsJSArgArray
2080 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray
)
2082 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray
)
2083 tmp
->ReleaseJSObjects();
2084 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2085 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray
)
2086 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2088 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray
)
2090 for (uint32_t i
= 0; i
< tmp
->mArgc
; ++i
) {
2091 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv
[i
])
2094 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2096 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray
)
2097 NS_INTERFACE_MAP_ENTRY(nsIArray
)
2098 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray
)
2099 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIJSArgArray
)
2100 NS_INTERFACE_MAP_END
2102 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray
)
2103 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray
)
2105 nsresult
nsJSArgArray::GetArgs(uint32_t* argc
, void** argv
) {
2106 *argv
= (void*)mArgv
;
2112 NS_IMETHODIMP
nsJSArgArray::GetLength(uint32_t* aLength
) {
2117 NS_IMETHODIMP
nsJSArgArray::QueryElementAt(uint32_t index
, const nsIID
& uuid
,
2120 if (index
>= mArgc
) return NS_ERROR_INVALID_ARG
;
2122 if (uuid
.Equals(NS_GET_IID(nsIVariant
)) ||
2123 uuid
.Equals(NS_GET_IID(nsISupports
))) {
2124 // Have to copy a Heap into a Rooted to work with it.
2125 JS::Rooted
<JS::Value
> val(mContext
, mArgv
[index
]);
2126 return nsContentUtils::XPConnect()->JSToVariant(mContext
, val
,
2127 (nsIVariant
**)result
);
2129 NS_WARNING("nsJSArgArray only handles nsIVariant");
2130 return NS_ERROR_NO_INTERFACE
;
2133 NS_IMETHODIMP
nsJSArgArray::IndexOf(uint32_t startIndex
, nsISupports
* element
,
2134 uint32_t* _retval
) {
2135 return NS_ERROR_NOT_IMPLEMENTED
;
2138 NS_IMETHODIMP
nsJSArgArray::ScriptedEnumerate(const nsIID
& aElemIID
,
2140 nsISimpleEnumerator
** aResult
) {
2141 return NS_ERROR_NOT_IMPLEMENTED
;
2144 NS_IMETHODIMP
nsJSArgArray::EnumerateImpl(const nsID
& aEntryIID
,
2145 nsISimpleEnumerator
** _retval
) {
2146 return NS_ERROR_NOT_IMPLEMENTED
;
2149 // The factory function
2150 nsresult
NS_CreateJSArgv(JSContext
* aContext
, uint32_t argc
,
2151 const JS::Value
* argv
, nsIJSArgArray
** aArray
) {
2153 nsCOMPtr
<nsIJSArgArray
> ret
= new nsJSArgArray(aContext
, argc
, argv
, &rv
);
2154 if (NS_FAILED(rv
)) {