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/PresShell.h"
58 #include "mozilla/SchedulerGroup.h"
59 #include "mozilla/StaticPrefs_dom.h"
60 #include "mozilla/StaticPrefs_javascript.h"
61 #include "mozilla/StaticPtr.h"
62 #include "mozilla/dom/BrowsingContext.h"
63 #include "mozilla/dom/DOMException.h"
64 #include "mozilla/dom/DOMExceptionBinding.h"
65 #include "mozilla/dom/Element.h"
66 #include "mozilla/dom/ErrorEvent.h"
67 #include "mozilla/dom/FetchUtil.h"
68 #include "mozilla/dom/RootedDictionary.h"
69 #include "mozilla/dom/ScriptSettings.h"
70 #include "mozilla/dom/SerializedStackHolder.h"
71 #include "mozilla/CycleCollectedJSRuntime.h"
72 #include "nsRefreshDriver.h"
73 #include "nsJSPrincipals.h"
74 #include "AccessCheck.h"
75 #include "mozilla/Logging.h"
78 #include "mozilla/Preferences.h"
79 #include "mozilla/Telemetry.h"
80 #include "mozilla/dom/BindingUtils.h"
81 #include "mozilla/Attributes.h"
82 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
83 #include "mozilla/ContentEvents.h"
84 #include "mozilla/CycleCollectedJSContext.h"
85 #include "nsCycleCollectionNoteRootCallback.h"
86 #include "nsViewManager.h"
87 #include "mozilla/EventStateManager.h"
88 #include "mozilla/ProfilerLabels.h"
89 #include "mozilla/ProfilerMarkers.h"
90 #if defined(MOZ_MEMORY)
91 # include "mozmemory.h"
94 using namespace mozilla
;
95 using namespace mozilla::dom
;
97 // Thank you Microsoft!
102 static JS::GCSliceCallback sPrevGCSliceCallback
;
104 static bool sIncrementalCC
= false;
106 static bool sIsInitialized
;
107 static bool sShuttingDown
;
109 static CCGCScheduler sScheduler
;
111 struct CycleCollectorStats
{
112 constexpr CycleCollectorStats() = default;
115 void PrepareForCycleCollection(TimeStamp aNow
);
116 void AfterPrepareForCycleCollectionSlice(TimeStamp aDeadline
,
117 TimeStamp aBeginTime
,
118 TimeStamp aMaybeAfterGCTime
);
119 void AfterCycleCollectionSlice();
120 void AfterSyncForgetSkippable(TimeStamp beginTime
);
121 void AfterForgetSkippable(TimeDuration duration
, uint32_t aRemovedPurples
);
122 void AfterCycleCollection();
124 void SendTelemetry(TimeDuration aCCNowDuration
, TimeStamp aPrevCCEnd
) const;
125 void MaybeLogStats(const CycleCollectorResults
& aResults
,
126 uint32_t aCleanups
) const;
127 void MaybeNotifyStats(const CycleCollectorResults
& aResults
,
128 TimeDuration aCCNowDuration
, uint32_t aCleanups
) const;
130 // Time the current slice began, including any GC finishing.
131 TimeStamp mBeginSliceTime
;
133 // Time the previous slice of the current CC ended.
134 TimeStamp mEndSliceTime
;
136 // Time the current cycle collection began.
137 TimeStamp mBeginTime
;
139 // The longest GC finishing duration for any slice of the current CC.
140 TimeDuration mMaxGCDuration
;
142 // True if we ran sync forget skippable in any slice of the current CC.
143 bool mRanSyncForgetSkippable
= false;
145 // Number of suspected objects at the start of the current CC.
146 uint32_t mSuspected
= 0;
148 // The longest duration spent on sync forget skippable in any slice of the
150 TimeDuration mMaxSkippableDuration
;
152 // The longest pause of any slice in the current CC.
153 TimeDuration mMaxSliceTime
;
155 // The longest slice time since ClearMaxCCSliceTime() was called.
156 TimeDuration mMaxSliceTimeSinceClear
;
158 // The total amount of time spent actually running the current CC.
159 TimeDuration mTotalSliceTime
;
161 // True if we were locked out by the GC in any slice of the current CC.
162 bool mAnyLockedOut
= false;
164 // A file to dump CC activity to; set by MOZ_CCTIMER environment variable.
165 FILE* mFile
= nullptr;
167 // In case CC slice was triggered during idle time, set to the end of the idle
169 TimeStamp mIdleDeadline
;
171 TimeDuration mMinForgetSkippableTime
;
172 TimeDuration mMaxForgetSkippableTime
;
173 TimeDuration mTotalForgetSkippableTime
;
174 uint32_t mForgetSkippableBeforeCC
= 0;
176 uint32_t mRemovedPurples
= 0;
179 static CycleCollectorStats sCCStats
;
181 static const char* ProcessNameForCollectorLog() {
182 return XRE_GetProcessType() == GeckoProcessType_Default
? "default"
188 // This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
189 // Exceptions, and arbitrary values that were associated with a stack by the
190 // JS engine when they were thrown, as specified by exceptionStack.
192 // Note that the returned stackObj and stackGlobal are _not_ wrapped into the
193 // compartment of exceptionValue.
194 void FindExceptionStackForConsoleReport(
195 nsPIDOMWindowInner
* win
, JS::Handle
<JS::Value
> exceptionValue
,
196 JS::Handle
<JSObject
*> exceptionStack
, JS::MutableHandle
<JSObject
*> stackObj
,
197 JS::MutableHandle
<JSObject
*> stackGlobal
) {
198 stackObj
.set(nullptr);
199 stackGlobal
.set(nullptr);
201 if (!exceptionValue
.isObject()) {
202 // Use the stack provided by the JS engine, if available. This will not be
204 if (exceptionStack
) {
205 stackObj
.set(exceptionStack
);
206 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
211 if (win
&& win
->AsGlobal()->IsDying()) {
212 // Pretend like we have no stack, so we don't end up keeping the global
213 // alive via the stack.
217 JS::RootingContext
* rcx
= RootingCx();
218 JS::Rooted
<JSObject
*> exceptionObject(rcx
, &exceptionValue
.toObject());
219 if (JSObject
* excStack
= JS::ExceptionStackOrNull(exceptionObject
)) {
220 // At this point we know exceptionObject is a possibly-wrapped
221 // js::ErrorObject that has excStack as stack. excStack might also be a CCW,
222 // but excStack must be same-compartment with the unwrapped ErrorObject.
223 // Return the ErrorObject's global as stackGlobal. This matches what we do
224 // in the ErrorObject's |.stack| getter and ensures stackObj and stackGlobal
225 // are same-compartment.
226 JSObject
* unwrappedException
= js::UncheckedUnwrap(exceptionObject
);
227 stackObj
.set(excStack
);
228 stackGlobal
.set(JS::GetNonCCWObjectGlobal(unwrappedException
));
232 // It is not a JS Exception, try DOM Exception.
233 RefPtr
<Exception
> exception
;
234 UNWRAP_OBJECT(DOMException
, exceptionObject
, exception
);
236 // Not a DOM Exception, try XPC Exception.
237 UNWRAP_OBJECT(Exception
, exceptionObject
, exception
);
239 // As above, use the stack provided by the JS engine, if available.
240 if (exceptionStack
) {
241 stackObj
.set(exceptionStack
);
242 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
248 nsCOMPtr
<nsIStackFrame
> stack
= exception
->GetLocation();
252 JS::Rooted
<JS::Value
> value(rcx
);
253 stack
->GetNativeSavedFrame(&value
);
254 if (value
.isObject()) {
255 stackObj
.set(&value
.toObject());
256 MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj
));
257 stackGlobal
.set(JS::GetNonCCWObjectGlobal(stackObj
));
262 } /* namespace xpc */
264 static TimeDuration
GetCollectionTimeDelta() {
265 static TimeStamp sFirstCollectionTime
;
266 TimeStamp now
= TimeStamp::Now();
267 if (sFirstCollectionTime
) {
268 return now
- sFirstCollectionTime
;
270 sFirstCollectionTime
= now
;
271 return TimeDuration();
274 class nsJSEnvironmentObserver final
: public nsIObserver
{
275 ~nsJSEnvironmentObserver() = default;
282 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver
, nsIObserver
)
285 nsJSEnvironmentObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
286 const char16_t
* aData
) {
287 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) {
288 if (StaticPrefs::javascript_options_gc_on_memory_pressure()) {
290 // Don't GC/CC if we're already shutting down.
293 nsDependentString
data(aData
);
294 if (data
.EqualsLiteral("low-memory-ongoing")) {
295 // Don't GC/CC if we are in an ongoing low-memory state since its very
296 // slow and it likely won't help us anyway.
299 if (data
.EqualsLiteral("heap-minimize")) {
300 // heap-minimize notifiers expect this to run synchronously
301 nsJSContext::DoLowMemoryGC();
304 if (data
.EqualsLiteral("low-memory")) {
305 nsJSContext::SetLowMemoryState(true);
307 // Asynchronously GC.
308 nsJSContext::LowMemoryGC();
310 } else if (!nsCRT::strcmp(aTopic
, "memory-pressure-stop")) {
311 nsJSContext::SetLowMemoryState(false);
312 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-inactive")) {
313 sScheduler
.UserIsInactive();
314 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-active")) {
315 sScheduler
.UserIsActive();
316 } else if (!nsCRT::strcmp(aTopic
, "quit-application") ||
317 !nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) ||
318 !nsCRT::strcmp(aTopic
, "content-child-will-shutdown")) {
319 sShuttingDown
= true;
320 sScheduler
.Shutdown();
326 /****************************************************************
327 ************************** AutoFree ****************************
328 ****************************************************************/
332 explicit AutoFree(void* aPtr
) : mPtr(aPtr
) {}
334 if (mPtr
) free(mPtr
);
336 void Invalidate() { mPtr
= nullptr; }
342 // A utility function for script languages to call. Although it looks small,
343 // the use of nsIDocShell and nsPresContext triggers a huge number of
344 // dependencies that most languages would not otherwise need.
345 // XXXmarkh - This function is mis-placed!
346 bool NS_HandleScriptError(nsIScriptGlobalObject
* aScriptGlobal
,
347 const ErrorEventInit
& aErrorEventInit
,
348 nsEventStatus
* aStatus
) {
350 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryInterface(aScriptGlobal
));
351 nsIDocShell
* docShell
= win
? win
->GetDocShell() : nullptr;
353 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
355 static int32_t errorDepth
; // Recursion prevention
358 if (errorDepth
< 2) {
359 // Dispatch() must be synchronous for the recursion block
360 // (errorDepth) to work.
361 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
362 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, aErrorEventInit
);
363 event
->SetTrusted(true);
365 // MOZ_KnownLive due to bug 1506441
366 EventDispatcher::DispatchDOMEvent(
367 MOZ_KnownLive(nsGlobalWindowInner::Cast(win
)), nullptr, event
,
368 presContext
, aStatus
);
376 class ScriptErrorEvent
: public Runnable
{
378 ScriptErrorEvent(nsPIDOMWindowInner
* aWindow
, JS::RootingContext
* aRootingCx
,
379 xpc::ErrorReport
* aReport
, JS::Handle
<JS::Value
> aError
,
380 JS::Handle
<JSObject
*> aErrorStack
)
381 : mozilla::Runnable("ScriptErrorEvent"),
384 mError(aRootingCx
, aError
),
385 mErrorStack(aRootingCx
, aErrorStack
) {}
387 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
388 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
389 nsEventStatus status
= nsEventStatus_eIgnore
;
390 nsCOMPtr
<nsPIDOMWindowInner
> win
= mWindow
;
392 MOZ_ASSERT(NS_IsMainThread());
393 // First, notify the DOM that we have a script error, but only if
394 // our window is still the current inner.
395 JS::RootingContext
* rootingCx
= RootingCx();
396 if (win
->IsCurrentInnerWindow() && win
->GetDocShell() &&
397 !sHandlingScriptError
) {
398 AutoRestore
<bool> recursionGuard(sHandlingScriptError
);
399 sHandlingScriptError
= true;
401 RefPtr
<nsPresContext
> presContext
= win
->GetDocShell()->GetPresContext();
403 RootedDictionary
<ErrorEventInit
> init(rootingCx
);
404 init
.mCancelable
= true;
405 init
.mFilename
= mReport
->mFileName
;
406 init
.mBubbles
= true;
408 constexpr auto xoriginMsg
= u
"Script error."_ns
;
409 if (!mReport
->mIsMuted
) {
410 init
.mMessage
= mReport
->mErrorMsg
;
411 init
.mLineno
= mReport
->mLineNumber
;
412 init
.mColno
= mReport
->mColumn
;
413 init
.mError
= mError
;
415 NS_WARNING("Not same origin error!");
416 init
.mMessage
= xoriginMsg
;
420 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
421 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, init
);
422 event
->SetTrusted(true);
424 // MOZ_KnownLive due to bug 1506441
425 EventDispatcher::DispatchDOMEvent(
426 MOZ_KnownLive(nsGlobalWindowInner::Cast(win
)), nullptr, event
,
427 presContext
, &status
);
430 if (status
!= nsEventStatus_eConsumeNoDefault
) {
431 JS::Rooted
<JSObject
*> stack(rootingCx
);
432 JS::Rooted
<JSObject
*> stackGlobal(rootingCx
);
433 xpc::FindExceptionStackForConsoleReport(win
, mError
, mErrorStack
, &stack
,
435 JS::Rooted
<Maybe
<JS::Value
>> exception(rootingCx
, Some(mError
));
436 nsGlobalWindowInner
* inner
= nsGlobalWindowInner::Cast(win
);
437 mReport
->LogToConsoleWithStack(inner
, exception
, stack
, stackGlobal
);
444 nsCOMPtr
<nsPIDOMWindowInner
> mWindow
;
445 RefPtr
<xpc::ErrorReport
> mReport
;
446 JS::PersistentRooted
<JS::Value
> mError
;
447 JS::PersistentRooted
<JSObject
*> mErrorStack
;
449 static bool sHandlingScriptError
;
452 bool ScriptErrorEvent::sHandlingScriptError
= false;
454 // This temporarily lives here to avoid code churn. It will go away entirely
458 void DispatchScriptErrorEvent(nsPIDOMWindowInner
* win
,
459 JS::RootingContext
* rootingCx
,
460 xpc::ErrorReport
* xpcReport
,
461 JS::Handle
<JS::Value
> exception
,
462 JS::Handle
<JSObject
*> exceptionStack
) {
463 nsContentUtils::AddScriptRunner(new ScriptErrorEvent(
464 win
, rootingCx
, xpcReport
, exception
, exceptionStack
));
467 } /* namespace xpc */
470 // A couple of useful functions to call when you're debugging.
471 nsGlobalWindowInner
* JSObject2Win(JSObject
* obj
) {
472 return xpc::WindowOrNull(obj
);
475 template <typename T
>
476 void PrintWinURI(T
* win
) {
478 printf("No window passed in.\n");
482 nsCOMPtr
<Document
> doc
= win
->GetExtantDoc();
484 printf("No document in the window.\n");
488 nsIURI
* uri
= doc
->GetDocumentURI();
490 printf("Document doesn't have a URI.\n");
494 printf("%s\n", uri
->GetSpecOrDefault().get());
497 void PrintWinURIInner(nsGlobalWindowInner
* aWin
) { return PrintWinURI(aWin
); }
499 void PrintWinURIOuter(nsGlobalWindowOuter
* aWin
) { return PrintWinURI(aWin
); }
501 template <typename T
>
502 void PrintWinCodebase(T
* win
) {
504 printf("No window passed in.\n");
508 nsIPrincipal
* prin
= win
->GetPrincipal();
510 printf("Window doesn't have principals.\n");
513 if (prin
->IsSystemPrincipal()) {
514 printf("No URI, it's the system principal.\n");
518 prin
->GetAsciiSpec(spec
);
519 printf("%s\n", spec
.get());
522 void PrintWinCodebaseInner(nsGlobalWindowInner
* aWin
) {
523 return PrintWinCodebase(aWin
);
526 void PrintWinCodebaseOuter(nsGlobalWindowOuter
* aWin
) {
527 return PrintWinCodebase(aWin
);
530 void DumpString(const nsAString
& str
) {
531 printf("%s\n", NS_ConvertUTF16toUTF8(str
).get());
535 nsJSContext::nsJSContext(bool aGCOnDestruction
,
536 nsIScriptGlobalObject
* aGlobalObject
)
537 : mWindowProxy(nullptr),
538 mGCOnDestruction(aGCOnDestruction
),
539 mGlobalObjectRef(aGlobalObject
) {
542 mProcessingScriptTag
= false;
546 nsJSContext::~nsJSContext() {
547 mGlobalObjectRef
= nullptr;
552 void nsJSContext::Destroy() {
553 if (mGCOnDestruction
) {
554 sScheduler
.PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY
, mWindowProxy
);
560 // QueryInterface implementation for nsJSContext
561 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext
)
563 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext
)
564 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy
)
565 NS_IMPL_CYCLE_COLLECTION_TRACE_END
567 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext
)
568 tmp
->mGCOnDestruction
= false;
569 tmp
->mWindowProxy
= nullptr;
571 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef
)
572 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
573 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext
)
574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef
)
575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
577 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext
)
578 NS_INTERFACE_MAP_ENTRY(nsIScriptContext
)
579 NS_INTERFACE_MAP_ENTRY(nsISupports
)
582 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext
)
583 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext
)
586 bool AtomIsEventHandlerName(nsAtom
* aName
) {
587 const char16_t
* name
= aName
->GetUTF16String();
591 for (cp
= name
; *cp
!= '\0'; ++cp
) {
593 if ((c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z')) return false;
600 nsIScriptGlobalObject
* nsJSContext::GetGlobalObject() {
601 // Note: this could probably be simplified somewhat more; see bug 974327
607 MOZ_ASSERT(mGlobalObjectRef
);
608 return mGlobalObjectRef
;
611 nsresult
nsJSContext::SetProperty(JS::Handle
<JSObject
*> aTarget
,
612 const char* aPropName
, nsISupports
* aArgs
) {
614 if (NS_WARN_IF(!jsapi
.Init(GetGlobalObject()))) {
615 return NS_ERROR_FAILURE
;
617 JSContext
* cx
= jsapi
.cx();
619 JS::RootedVector
<JS::Value
> args(cx
);
621 JS::Rooted
<JSObject
*> global(cx
, GetWindowProxy());
622 nsresult rv
= ConvertSupportsTojsvals(cx
, aArgs
, global
, &args
);
623 NS_ENSURE_SUCCESS(rv
, rv
);
625 // got the arguments, now attach them.
627 for (uint32_t i
= 0; i
< args
.length(); ++i
) {
628 if (!JS_WrapValue(cx
, args
[i
])) {
629 return NS_ERROR_FAILURE
;
633 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, args
));
635 return NS_ERROR_FAILURE
;
638 return JS_DefineProperty(cx
, aTarget
, aPropName
, array
, 0) ? NS_OK
642 nsresult
nsJSContext::ConvertSupportsTojsvals(
643 JSContext
* aCx
, nsISupports
* aArgs
, JS::Handle
<JSObject
*> aScope
,
644 JS::MutableHandleVector
<JS::Value
> aArgsOut
) {
647 // If the array implements nsIJSArgArray, copy the contents and return.
648 nsCOMPtr
<nsIJSArgArray
> fastArray
= do_QueryInterface(aArgs
);
652 rv
= fastArray
->GetArgs(&argc
, reinterpret_cast<void**>(&argv
));
653 if (NS_SUCCEEDED(rv
) && !aArgsOut
.append(argv
, argc
)) {
654 rv
= NS_ERROR_OUT_OF_MEMORY
;
659 // Take the slower path converting each item.
660 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
661 // SetProperty('arguments', ...);
663 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
664 NS_ENSURE_TRUE(xpc
, NS_ERROR_UNEXPECTED
);
666 if (!aArgs
) return NS_OK
;
668 // This general purpose function may need to convert an arg array
669 // (window.arguments, event-handler args) and a generic property.
670 nsCOMPtr
<nsIArray
> argsArray(do_QueryInterface(aArgs
));
673 rv
= argsArray
->GetLength(&argCount
);
674 NS_ENSURE_SUCCESS(rv
, rv
);
675 if (argCount
== 0) return NS_OK
;
677 argCount
= 1; // the nsISupports which is not an array
680 // Use the caller's auto guards to release and unroot.
681 if (!aArgsOut
.resize(argCount
)) {
682 return NS_ERROR_OUT_OF_MEMORY
;
686 for (uint32_t argCtr
= 0; argCtr
< argCount
&& NS_SUCCEEDED(rv
); argCtr
++) {
687 nsCOMPtr
<nsISupports
> arg
;
688 JS::MutableHandle
<JS::Value
> thisVal
= aArgsOut
[argCtr
];
689 argsArray
->QueryElementAt(argCtr
, NS_GET_IID(nsISupports
),
690 getter_AddRefs(arg
));
695 nsCOMPtr
<nsIVariant
> variant(do_QueryInterface(arg
));
696 if (variant
!= nullptr) {
697 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, thisVal
);
699 // And finally, support the nsISupportsPrimitives supplied
700 // by the AppShell. It generally will pass only strings, but
701 // as we have code for handling all, we may as well use it.
702 rv
= AddSupportsPrimitiveTojsvals(aCx
, arg
, thisVal
.address());
703 if (rv
== NS_ERROR_NO_INTERFACE
) {
704 // something else - probably an event object or similar -
707 // but first, check its not another nsISupportsPrimitive, as
708 // these are now deprecated for use with script contexts.
709 nsCOMPtr
<nsISupportsPrimitive
> prim(do_QueryInterface(arg
));
710 NS_ASSERTION(prim
== nullptr,
711 "Don't pass nsISupportsPrimitives - use nsIVariant!");
713 JSAutoRealm
ar(aCx
, aScope
);
714 rv
= nsContentUtils::WrapNative(aCx
, arg
, thisVal
);
719 nsCOMPtr
<nsIVariant
> variant
= do_QueryInterface(aArgs
);
721 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, aArgsOut
[0]);
723 NS_ERROR("Not an array, not an interface?");
724 rv
= NS_ERROR_UNEXPECTED
;
730 // This really should go into xpconnect somewhere...
731 nsresult
nsJSContext::AddSupportsPrimitiveTojsvals(JSContext
* aCx
,
734 MOZ_ASSERT(aArg
, "Empty arg");
736 nsCOMPtr
<nsISupportsPrimitive
> argPrimitive(do_QueryInterface(aArg
));
737 if (!argPrimitive
) return NS_ERROR_NO_INTERFACE
;
740 argPrimitive
->GetType(&type
);
743 case nsISupportsPrimitive::TYPE_CSTRING
: {
744 nsCOMPtr
<nsISupportsCString
> p(do_QueryInterface(argPrimitive
));
745 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
751 JSString
* str
= ::JS_NewStringCopyN(aCx
, data
.get(), data
.Length());
752 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
754 aArgv
->setString(str
);
758 case nsISupportsPrimitive::TYPE_STRING
: {
759 nsCOMPtr
<nsISupportsString
> p(do_QueryInterface(argPrimitive
));
760 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
766 // cast is probably safe since wchar_t and char16_t are expected
767 // to be equivalent; both unsigned 16-bit entities
768 JSString
* str
= ::JS_NewUCStringCopyN(aCx
, data
.get(), data
.Length());
769 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
771 aArgv
->setString(str
);
774 case nsISupportsPrimitive::TYPE_PRBOOL
: {
775 nsCOMPtr
<nsISupportsPRBool
> p(do_QueryInterface(argPrimitive
));
776 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
782 aArgv
->setBoolean(data
);
786 case nsISupportsPrimitive::TYPE_PRUINT8
: {
787 nsCOMPtr
<nsISupportsPRUint8
> p(do_QueryInterface(argPrimitive
));
788 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
794 aArgv
->setInt32(data
);
798 case nsISupportsPrimitive::TYPE_PRUINT16
: {
799 nsCOMPtr
<nsISupportsPRUint16
> p(do_QueryInterface(argPrimitive
));
800 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
806 aArgv
->setInt32(data
);
810 case nsISupportsPrimitive::TYPE_PRUINT32
: {
811 nsCOMPtr
<nsISupportsPRUint32
> p(do_QueryInterface(argPrimitive
));
812 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
818 aArgv
->setInt32(data
);
822 case nsISupportsPrimitive::TYPE_CHAR
: {
823 nsCOMPtr
<nsISupportsChar
> p(do_QueryInterface(argPrimitive
));
824 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
830 JSString
* str
= ::JS_NewStringCopyN(aCx
, &data
, 1);
831 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
833 aArgv
->setString(str
);
837 case nsISupportsPrimitive::TYPE_PRINT16
: {
838 nsCOMPtr
<nsISupportsPRInt16
> p(do_QueryInterface(argPrimitive
));
839 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
845 aArgv
->setInt32(data
);
849 case nsISupportsPrimitive::TYPE_PRINT32
: {
850 nsCOMPtr
<nsISupportsPRInt32
> p(do_QueryInterface(argPrimitive
));
851 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
857 aArgv
->setInt32(data
);
861 case nsISupportsPrimitive::TYPE_FLOAT
: {
862 nsCOMPtr
<nsISupportsFloat
> p(do_QueryInterface(argPrimitive
));
863 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
869 *aArgv
= ::JS_NumberValue(data
);
873 case nsISupportsPrimitive::TYPE_DOUBLE
: {
874 nsCOMPtr
<nsISupportsDouble
> p(do_QueryInterface(argPrimitive
));
875 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
881 *aArgv
= ::JS_NumberValue(data
);
885 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER
: {
886 nsCOMPtr
<nsISupportsInterfacePointer
> p(do_QueryInterface(argPrimitive
));
887 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
889 nsCOMPtr
<nsISupports
> data
;
890 nsIID
* iid
= nullptr;
892 p
->GetData(getter_AddRefs(data
));
894 NS_ENSURE_TRUE(iid
, NS_ERROR_UNEXPECTED
);
896 AutoFree
iidGuard(iid
); // Free iid upon destruction.
898 JS::Rooted
<JSObject
*> scope(aCx
, GetWindowProxy());
899 JS::Rooted
<JS::Value
> v(aCx
);
900 JSAutoRealm
ar(aCx
, scope
);
901 nsresult rv
= nsContentUtils::WrapNative(aCx
, data
, iid
, &v
);
902 NS_ENSURE_SUCCESS(rv
, rv
);
908 case nsISupportsPrimitive::TYPE_ID
:
909 case nsISupportsPrimitive::TYPE_PRUINT64
:
910 case nsISupportsPrimitive::TYPE_PRINT64
:
911 case nsISupportsPrimitive::TYPE_PRTIME
: {
912 NS_WARNING("Unsupported primitive type used");
917 NS_WARNING("Unknown primitive type used");
929 inline bool IsJProfAction(struct sigaction
* action
) {
930 return (action
->sa_sigaction
&&
931 (action
->sa_flags
& (SA_RESTART
| SA_SIGINFO
)) ==
932 (SA_RESTART
| SA_SIGINFO
));
935 void NS_JProfStartProfiling();
936 void NS_JProfStopProfiling();
937 void NS_JProfClearCircular();
939 static bool JProfStartProfilingJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
940 NS_JProfStartProfiling();
944 void NS_JProfStartProfiling() {
945 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
946 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
948 struct sigaction action
;
950 // Must check ALRM before PROF since both are enabled for real-time
951 sigaction(SIGALRM
, nullptr, &action
);
952 // printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
953 if (IsJProfAction(&action
)) {
954 // printf("Beginning real-time jprof profiling.\n");
959 sigaction(SIGPROF
, nullptr, &action
);
960 // printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
961 if (IsJProfAction(&action
)) {
962 // printf("Beginning process-time jprof profiling.\n");
967 sigaction(SIGPOLL
, nullptr, &action
);
968 // printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
969 if (IsJProfAction(&action
)) {
970 // printf("Beginning rtc-based jprof profiling.\n");
975 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
978 static bool JProfStopProfilingJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
979 NS_JProfStopProfiling();
983 void NS_JProfStopProfiling() {
985 // printf("Stopped jprof profiling.\n");
988 static bool JProfClearCircularJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
989 NS_JProfClearCircular();
993 void NS_JProfClearCircular() {
995 // printf("cleared jprof buffer\n");
998 static bool JProfSaveCircularJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
1000 NS_JProfStopProfiling();
1001 NS_JProfStartProfiling();
1005 static const JSFunctionSpec JProfFunctions
[] = {
1006 JS_FN("JProfStartProfiling", JProfStartProfilingJS
, 0, 0),
1007 JS_FN("JProfStopProfiling", JProfStopProfilingJS
, 0, 0),
1008 JS_FN("JProfClearCircular", JProfClearCircularJS
, 0, 0),
1009 JS_FN("JProfSaveCircular", JProfSaveCircularJS
, 0, 0), JS_FS_END
};
1011 #endif /* defined(MOZ_JPROF) */
1013 nsresult
nsJSContext::InitClasses(JS::Handle
<JSObject
*> aGlobalObj
) {
1016 JSContext
* cx
= jsapi
.cx();
1017 JSAutoRealm
ar(cx
, aGlobalObj
);
1020 // Attempt to initialize JProf functions
1021 ::JS_DefineFunctions(cx
, aGlobalObj
, JProfFunctions
);
1027 bool nsJSContext::GetProcessingScriptTag() { return mProcessingScriptTag
; }
1029 void nsJSContext::SetProcessingScriptTag(bool aFlag
) {
1030 mProcessingScriptTag
= aFlag
;
1034 void nsJSContext::SetLowMemoryState(bool aState
) {
1035 JSContext
* cx
= danger::GetJSContext();
1036 JS::SetLowMemoryState(cx
, aState
);
1039 static void GarbageCollectImpl(JS::GCReason aReason
,
1040 nsJSContext::IsShrinking aShrinking
,
1041 const js::SliceBudget
& aBudget
) {
1042 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
1043 "nsJSContext::GarbageCollectNow", GCCC
, JS::ExplainGCReason(aReason
));
1045 bool wantIncremental
= !aBudget
.isUnlimited();
1047 // We use danger::GetJSContext() since AutoJSAPI will assert if the current
1048 // thread's context is null (such as during shutdown).
1049 JSContext
* cx
= danger::GetJSContext();
1051 if (!nsContentUtils::XPConnect() || !cx
) {
1055 if (sScheduler
.InIncrementalGC() && wantIncremental
) {
1056 // We're in the middle of incremental GC. Do another slice.
1057 JS::PrepareForIncrementalGC(cx
);
1058 JS::IncrementalGCSlice(cx
, aReason
, aBudget
);
1062 JS::GCOptions options
= aShrinking
== nsJSContext::ShrinkingGC
1063 ? JS::GCOptions::Shrink
1064 : JS::GCOptions::Normal
;
1066 if (!wantIncremental
|| aReason
== JS::GCReason::FULL_GC_TIMER
) {
1067 sScheduler
.SetNeedsFullGC();
1070 if (sScheduler
.NeedsFullGC()) {
1071 JS::PrepareForFullGC(cx
);
1074 if (wantIncremental
) {
1075 // Incremental GC slices will be triggered by the GC Runner. If one doesn't
1076 // already exist, create it in the GC_SLICE_END callback for the first
1077 // slice being executed here.
1078 JS::StartIncrementalGC(cx
, options
, aReason
, aBudget
);
1080 JS::NonIncrementalGC(cx
, options
, aReason
);
1085 void nsJSContext::GarbageCollectNow(JS::GCReason aReason
,
1086 IsShrinking aShrinking
) {
1087 GarbageCollectImpl(aReason
, aShrinking
, js::SliceBudget::unlimited());
1091 void nsJSContext::RunIncrementalGCSlice(JS::GCReason aReason
,
1092 IsShrinking aShrinking
,
1093 js::SliceBudget
& aBudget
) {
1094 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental GC", GCCC
);
1095 GarbageCollectImpl(aReason
, aShrinking
, aBudget
);
1098 static void FinishAnyIncrementalGC() {
1099 AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC
);
1101 if (sScheduler
.InIncrementalGC()) {
1105 // We're in the middle of an incremental GC, so finish it.
1106 JS::PrepareForIncrementalGC(jsapi
.cx());
1107 JS::FinishIncrementalGC(jsapi
.cx(), JS::GCReason::CC_FORCED
);
1111 namespace geckoprofiler::markers
{
1112 class CCSliceMarker
{
1114 static constexpr Span
<const char> MarkerTypeName() {
1115 return mozilla::MakeStringSpan("CCSlice");
1117 static void StreamJSONMarkerData(
1118 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1119 bool aIsDuringIdle
) {
1120 aWriter
.BoolProperty("idle", aIsDuringIdle
);
1122 static MarkerSchema
MarkerTypeDisplay() {
1123 using MS
= MarkerSchema
;
1124 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
,
1125 MS::Location::TimelineMemory
};
1126 schema
.SetAllLabels("{marker.name} (idle={marker.data.idle})");
1127 schema
.AddKeyLabelFormat("idle", "Idle", MS::Format::Integer
);
1131 } // namespace geckoprofiler::markers
1133 static void FireForgetSkippable(bool aRemoveChildless
, TimeStamp aDeadline
) {
1134 TimeStamp startTimeStamp
= TimeStamp::Now();
1135 FinishAnyIncrementalGC();
1137 uint32_t suspectedBefore
= nsCycleCollector_suspectedCount();
1138 js::SliceBudget budget
=
1139 sScheduler
.ComputeForgetSkippableBudget(startTimeStamp
, aDeadline
);
1140 bool earlyForgetSkippable
= sScheduler
.IsEarlyForgetSkippable();
1141 nsCycleCollector_forgetSkippable(budget
, aRemoveChildless
,
1142 earlyForgetSkippable
);
1143 TimeStamp now
= TimeStamp::Now();
1144 uint32_t removedPurples
= sScheduler
.NoteForgetSkippableComplete(
1145 now
, suspectedBefore
, nsCycleCollector_suspectedCount());
1147 TimeDuration duration
= now
- startTimeStamp
;
1149 sCCStats
.AfterForgetSkippable(duration
, removedPurples
);
1151 if (duration
.ToSeconds()) {
1152 TimeDuration idleDuration
;
1153 if (!aDeadline
.IsNull()) {
1154 if (aDeadline
< now
) {
1155 // This slice overflowed the idle period.
1156 if (aDeadline
> startTimeStamp
) {
1157 idleDuration
= aDeadline
- startTimeStamp
;
1160 idleDuration
= duration
;
1165 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
1166 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE
, percent
);
1169 PROFILER_MARKER("ForgetSkippable", GCCC
,
1170 MarkerTiming::IntervalUntilNowFrom(startTimeStamp
),
1171 CCSliceMarker
, !aDeadline
.IsNull());
1175 static TimeDuration
TimeBetween(TimeStamp aStart
, TimeStamp aEnd
) {
1176 MOZ_ASSERT(aEnd
>= aStart
);
1177 return aEnd
- aStart
;
1180 static TimeDuration
TimeUntilNow(TimeStamp start
) {
1181 if (start
.IsNull()) {
1182 return TimeDuration();
1184 return TimeBetween(start
, TimeStamp::Now());
1187 void CycleCollectorStats::Init() {
1190 char* env
= getenv("MOZ_CCTIMER");
1194 if (strcmp(env
, "none") == 0) {
1196 } else if (strcmp(env
, "stdout") == 0) {
1198 } else if (strcmp(env
, "stderr") == 0) {
1201 mFile
= fopen(env
, "a");
1203 MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
1208 void CycleCollectorStats::Clear() {
1209 if (mFile
&& mFile
!= stdout
&& mFile
!= stderr
) {
1212 *this = CycleCollectorStats();
1215 void CycleCollectorStats::AfterCycleCollectionSlice() {
1216 if (mBeginSliceTime
.IsNull()) {
1217 // We already called this method from EndCycleCollectionCallback for this
1222 mEndSliceTime
= TimeStamp::Now();
1223 TimeDuration duration
= mEndSliceTime
- mBeginSliceTime
;
1226 "CCSlice", GCCC
, MarkerTiming::Interval(mBeginSliceTime
, mEndSliceTime
),
1227 CCSliceMarker
, !mIdleDeadline
.IsNull() && mIdleDeadline
>= mEndSliceTime
);
1229 if (duration
.ToSeconds()) {
1230 TimeDuration idleDuration
;
1231 if (!mIdleDeadline
.IsNull()) {
1232 if (mIdleDeadline
< mEndSliceTime
) {
1233 // This slice overflowed the idle period.
1234 if (mIdleDeadline
> mBeginSliceTime
) {
1235 idleDuration
= mIdleDeadline
- mBeginSliceTime
;
1238 idleDuration
= duration
;
1243 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
1244 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE
,
1248 TimeDuration sliceTime
= TimeBetween(mBeginSliceTime
, mEndSliceTime
);
1249 mMaxSliceTime
= std::max(mMaxSliceTime
, sliceTime
);
1250 mMaxSliceTimeSinceClear
= std::max(mMaxSliceTimeSinceClear
, sliceTime
);
1251 mTotalSliceTime
+= sliceTime
;
1252 mBeginSliceTime
= TimeStamp();
1255 void CycleCollectorStats::PrepareForCycleCollection(TimeStamp aNow
) {
1257 mSuspected
= nsCycleCollector_suspectedCount();
1260 void CycleCollectorStats::AfterPrepareForCycleCollectionSlice(
1261 TimeStamp aDeadline
, TimeStamp aBeginTime
, TimeStamp aMaybeAfterGCTime
) {
1262 mBeginSliceTime
= aBeginTime
;
1263 mIdleDeadline
= aDeadline
;
1265 if (!aMaybeAfterGCTime
.IsNull()) {
1266 mAnyLockedOut
= true;
1267 mMaxGCDuration
= std::max(mMaxGCDuration
, aMaybeAfterGCTime
- aBeginTime
);
1271 void CycleCollectorStats::AfterSyncForgetSkippable(TimeStamp beginTime
) {
1272 mMaxSkippableDuration
=
1273 std::max(mMaxSkippableDuration
, TimeUntilNow(beginTime
));
1274 mRanSyncForgetSkippable
= true;
1277 void CycleCollectorStats::AfterForgetSkippable(TimeDuration duration
,
1278 uint32_t aRemovedPurples
) {
1279 if (!mMinForgetSkippableTime
|| mMinForgetSkippableTime
> duration
) {
1280 mMinForgetSkippableTime
= duration
;
1282 if (!mMaxForgetSkippableTime
|| mMaxForgetSkippableTime
< duration
) {
1283 mMaxForgetSkippableTime
= duration
;
1285 mTotalForgetSkippableTime
+= duration
;
1286 ++mForgetSkippableBeforeCC
;
1288 mRemovedPurples
+= aRemovedPurples
;
1291 void CycleCollectorStats::SendTelemetry(TimeDuration aCCNowDuration
,
1292 TimeStamp aPrevCCEnd
) const {
1293 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC
, mAnyLockedOut
);
1294 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE
,
1295 mRanSyncForgetSkippable
);
1296 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL
,
1297 aCCNowDuration
.ToMilliseconds());
1298 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE
,
1299 mMaxSliceTime
.ToMilliseconds());
1301 if (!aPrevCCEnd
.IsNull()) {
1302 TimeDuration timeBetween
= TimeBetween(aPrevCCEnd
, mBeginTime
);
1303 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN
,
1304 timeBetween
.ToSeconds());
1307 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX
,
1308 mMaxForgetSkippableTime
.ToMilliseconds());
1311 void CycleCollectorStats::MaybeLogStats(const CycleCollectorResults
& aResults
,
1312 uint32_t aCleanups
) const {
1313 if (!StaticPrefs::javascript_options_mem_log() && !sCCStats
.mFile
) {
1317 TimeDuration delta
= GetCollectionTimeDelta();
1320 if (aResults
.mMergedZones
) {
1321 mergeMsg
.AssignLiteral(" merged");
1325 if (aResults
.mForcedGC
) {
1326 gcMsg
.AssignLiteral(", forced a GC");
1329 const char16_t
* kFmt
=
1330 u
"CC(T+%.1f)[%s-%i] max pause: %.fms, total time: %.fms, slices: %lu, "
1331 u
"suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu "
1332 u
"RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
1333 u
"ForgetSkippable %lu times before CC, min: %.f ms, max: %.f ms, avg: "
1334 u
"%.f ms, total: %.f ms, max sync: %.f ms, removed: %lu";
1336 nsTextFormatter::ssprintf(
1337 msg
, kFmt
, delta
.ToMicroseconds() / PR_USEC_PER_SEC
,
1338 ProcessNameForCollectorLog(), getpid(), mMaxSliceTime
.ToMilliseconds(),
1339 mTotalSliceTime
.ToMilliseconds(), aResults
.mNumSlices
, mSuspected
,
1340 aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
, mergeMsg
.get(),
1341 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1342 sScheduler
.mCCollectedWaitingForGC
,
1343 sScheduler
.mCCollectedZonesWaitingForGC
,
1344 sScheduler
.mLikelyShortLivingObjectsNeedingGC
, gcMsg
.get(),
1345 mForgetSkippableBeforeCC
, mMinForgetSkippableTime
.ToMilliseconds(),
1346 mMaxForgetSkippableTime
.ToMilliseconds(),
1347 mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1348 mTotalForgetSkippableTime
.ToMilliseconds(),
1349 mMaxSkippableDuration
.ToMilliseconds(), mRemovedPurples
);
1350 if (StaticPrefs::javascript_options_mem_log()) {
1351 nsCOMPtr
<nsIConsoleService
> cs
=
1352 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1354 cs
->LogStringMessage(msg
.get());
1358 fprintf(mFile
, "%s\n", NS_ConvertUTF16toUTF8(msg
).get());
1362 void CycleCollectorStats::MaybeNotifyStats(
1363 const CycleCollectorResults
& aResults
, TimeDuration aCCNowDuration
,
1364 uint32_t aCleanups
) const {
1365 if (!StaticPrefs::javascript_options_mem_notify()) {
1369 const char16_t
* kJSONFmt
=
1370 u
"{ \"timestamp\": %llu, "
1371 u
"\"duration\": %.f, "
1372 u
"\"max_slice_pause\": %.f, "
1373 u
"\"total_slice_pause\": %.f, "
1374 u
"\"max_finish_gc_duration\": %.f, "
1375 u
"\"max_sync_skippable_duration\": %.f, "
1376 u
"\"suspected\": %lu, "
1379 u
"\"GCed\": %lu }, "
1380 u
"\"collected\": { "
1382 u
"\"GCed\": %lu }, "
1383 u
"\"waiting_for_gc\": %lu, "
1384 u
"\"zones_waiting_for_gc\": %lu, "
1385 u
"\"short_living_objects_waiting_for_gc\": %lu, "
1386 u
"\"forced_gc\": %d, "
1387 u
"\"forget_skippable\": { "
1388 u
"\"times_before_cc\": %lu, "
1393 u
"\"removed\": %lu } "
1397 nsTextFormatter::ssprintf(
1398 json
, kJSONFmt
, PR_Now(), aCCNowDuration
.ToMilliseconds(),
1399 mMaxSliceTime
.ToMilliseconds(), mTotalSliceTime
.ToMilliseconds(),
1400 mMaxGCDuration
.ToMilliseconds(), mMaxSkippableDuration
.ToMilliseconds(),
1401 mSuspected
, aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
,
1402 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1403 sScheduler
.mCCollectedWaitingForGC
,
1404 sScheduler
.mCCollectedZonesWaitingForGC
,
1405 sScheduler
.mLikelyShortLivingObjectsNeedingGC
, aResults
.mForcedGC
,
1406 mForgetSkippableBeforeCC
, mMinForgetSkippableTime
.ToMilliseconds(),
1407 mMaxForgetSkippableTime
.ToMilliseconds(),
1408 mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1409 mTotalForgetSkippableTime
.ToMilliseconds(), mRemovedPurples
);
1410 nsCOMPtr
<nsIObserverService
> observerService
=
1411 mozilla::services::GetObserverService();
1412 if (observerService
) {
1413 observerService
->NotifyObservers(nullptr, "cycle-collection-statistics",
1419 void nsJSContext::CycleCollectNow(CCReason aReason
,
1420 nsICycleCollectorListener
* aListener
) {
1421 if (!NS_IsMainThread()) {
1425 AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC
);
1427 PrepareForCycleCollectionSlice(aReason
, TimeStamp());
1428 nsCycleCollector_collect(aReason
, aListener
);
1429 sCCStats
.AfterCycleCollectionSlice();
1433 void nsJSContext::PrepareForCycleCollectionSlice(CCReason aReason
,
1434 TimeStamp aDeadline
) {
1435 TimeStamp beginTime
= TimeStamp::Now();
1437 // Before we begin the cycle collection, make sure there is no active GC.
1438 TimeStamp afterGCTime
;
1439 if (sScheduler
.InIncrementalGC()) {
1440 FinishAnyIncrementalGC();
1441 afterGCTime
= TimeStamp::Now();
1444 if (!sScheduler
.IsCollectingCycles()) {
1445 sCCStats
.PrepareForCycleCollection(beginTime
);
1446 sScheduler
.NoteCCBegin(aReason
, beginTime
,
1447 sCCStats
.mForgetSkippableBeforeCC
,
1448 sCCStats
.mSuspected
, sCCStats
.mRemovedPurples
);
1451 sCCStats
.AfterPrepareForCycleCollectionSlice(aDeadline
, beginTime
,
1456 void nsJSContext::RunCycleCollectorSlice(CCReason aReason
,
1457 TimeStamp aDeadline
) {
1458 if (!NS_IsMainThread()) {
1462 PrepareForCycleCollectionSlice(aReason
, aDeadline
);
1464 // Decide how long we want to budget for this slice.
1465 if (sIncrementalCC
) {
1466 bool preferShorterSlices
;
1467 js::SliceBudget budget
= sScheduler
.ComputeCCSliceBudget(
1468 aDeadline
, sCCStats
.mBeginTime
, sCCStats
.mEndSliceTime
,
1469 TimeStamp::Now(), &preferShorterSlices
);
1470 nsCycleCollector_collectSlice(budget
, aReason
, preferShorterSlices
);
1472 js::SliceBudget budget
= js::SliceBudget::unlimited();
1473 nsCycleCollector_collectSlice(budget
, aReason
, false);
1476 sCCStats
.AfterCycleCollectionSlice();
1480 void nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget
) {
1481 if (!NS_IsMainThread()) {
1485 AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", GCCC
);
1487 PrepareForCycleCollectionSlice(CCReason::API
, TimeStamp());
1489 js::SliceBudget budget
= js::SliceBudget(js::WorkBudget(aWorkBudget
));
1490 nsCycleCollector_collectSlice(budget
, CCReason::API
);
1492 sCCStats
.AfterCycleCollectionSlice();
1495 void nsJSContext::ClearMaxCCSliceTime() {
1496 sCCStats
.mMaxSliceTimeSinceClear
= TimeDuration();
1499 uint32_t nsJSContext::GetMaxCCSliceTimeSinceClear() {
1500 return sCCStats
.mMaxSliceTimeSinceClear
.ToMilliseconds();
1504 void nsJSContext::BeginCycleCollectionCallback(CCReason aReason
) {
1505 MOZ_ASSERT(NS_IsMainThread());
1507 TimeStamp startTime
= TimeStamp::Now();
1508 sCCStats
.PrepareForCycleCollection(startTime
);
1510 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1511 // is particularly useful if we recently finished a GC.
1512 if (sScheduler
.IsEarlyForgetSkippable()) {
1513 while (sScheduler
.IsEarlyForgetSkippable()) {
1514 FireForgetSkippable(false, TimeStamp());
1516 sCCStats
.AfterSyncForgetSkippable(startTime
);
1519 if (sShuttingDown
) {
1523 sScheduler
.InitCCRunnerStateMachine(
1524 mozilla::CCGCScheduler::CCRunnerState::CycleCollecting
, aReason
);
1525 sScheduler
.EnsureCCRunner(kICCIntersliceDelay
, kIdleICCSliceBudget
);
1529 void nsJSContext::EndCycleCollectionCallback(
1530 const CycleCollectorResults
& aResults
) {
1531 MOZ_ASSERT(NS_IsMainThread());
1533 sScheduler
.KillCCRunner();
1535 // Update timing information for the current slice before we log it, if
1536 // we previously called PrepareForCycleCollectionSlice(). During shutdown
1537 // CCs, this won't happen.
1538 sCCStats
.AfterCycleCollectionSlice();
1540 TimeStamp endCCTimeStamp
= TimeStamp::Now();
1541 TimeDuration ccNowDuration
= TimeBetween(sCCStats
.mBeginTime
, endCCTimeStamp
);
1542 TimeStamp prevCCEnd
= sScheduler
.GetLastCCEndTime();
1544 sScheduler
.NoteCCEnd(aResults
, endCCTimeStamp
, sCCStats
.mMaxSliceTime
);
1546 // Log information about the CC via telemetry, JSON and the console.
1548 sCCStats
.SendTelemetry(ccNowDuration
, prevCCEnd
);
1550 uint32_t cleanups
= std::max(sCCStats
.mForgetSkippableBeforeCC
, 1u);
1552 sCCStats
.MaybeLogStats(aResults
, cleanups
);
1554 sCCStats
.MaybeNotifyStats(aResults
, ccNowDuration
, cleanups
);
1556 // Update global state to indicate we have just run a cycle collection.
1559 // If we need a GC after this CC (typically because lots of GCed objects or
1560 // zones have been collected in the CC), schedule it.
1562 if (sScheduler
.NeedsGCAfterCC()) {
1564 TimeDuration::FromMilliseconds(
1565 StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration
,
1566 "A max duration ICC shouldn't reduce GC delay to 0");
1568 sScheduler
.PokeGC(JS::GCReason::CC_FINISHED
, nullptr,
1569 TimeDuration::FromMilliseconds(
1570 StaticPrefs::javascript_options_gc_delay()) -
1571 std::min(ccNowDuration
, kMaxICCDuration
));
1573 #if defined(MOZ_MEMORY)
1576 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1577 jemalloc_free_dirty_pages();
1583 bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline
) {
1584 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental CC", GCCC
);
1586 bool didDoWork
= false;
1588 // The CC/GC scheduler (sScheduler) decides what action(s) to take during
1589 // this invocation of the CC runner.
1591 // This may be zero, one, or multiple actions. (Zero is when CC is blocked by
1592 // incremental GC, or when the scheduler determined that a CC is no longer
1593 // needed.) Loop until the scheduler finishes this invocation by returning
1594 // `Yield` in step.mYield.
1597 step
= sScheduler
.AdvanceCCRunner(aDeadline
, TimeStamp::Now(),
1598 nsCycleCollector_suspectedCount());
1599 switch (step
.mAction
) {
1600 case CCRunnerAction::None
:
1603 case CCRunnerAction::MinorGC
:
1604 JS::MaybeRunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
1605 step
.mParam
.mReason
);
1606 sScheduler
.NoteMinorGCEnd();
1609 case CCRunnerAction::ForgetSkippable
:
1610 // 'Forget skippable' only, then end this invocation.
1611 FireForgetSkippable(bool(step
.mParam
.mRemoveChildless
), aDeadline
);
1614 case CCRunnerAction::CleanupContentUnbinder
:
1615 // Clear content unbinder before the first actual CC slice.
1616 Element::ClearContentUnbinder();
1619 case CCRunnerAction::CleanupDeferred
:
1620 // and if time still permits, perform deferred deletions.
1621 nsCycleCollector_doDeferredDeletion();
1624 case CCRunnerAction::CycleCollect
:
1625 // Cycle collection slice.
1626 nsJSContext::RunCycleCollectorSlice(step
.mParam
.mCCReason
, aDeadline
);
1629 case CCRunnerAction::StopRunning
:
1630 // End this CC, either because we have run a cycle collection slice, or
1631 // because a CC is no longer needed.
1632 sScheduler
.KillCCRunner();
1636 if (step
.mAction
!= CCRunnerAction::None
) {
1639 } while (step
.mYield
== CCRunnerYield::Continue
);
1645 bool nsJSContext::HasHadCleanupSinceLastGC() {
1646 return sScheduler
.IsEarlyForgetSkippable(1);
1650 void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason
,
1651 mozilla::TimeStamp aDeadline
) {
1652 sScheduler
.RunNextCollectorTimer(aReason
, aDeadline
);
1656 void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell
* aDocShell
,
1657 JS::GCReason aReason
) {
1658 if (!aDocShell
|| !XRE_IsContentProcess()) {
1662 BrowsingContext
* bc
= aDocShell
->GetBrowsingContext();
1667 BrowsingContext
* root
= bc
->Top();
1669 // We don't want to run collectors when loading the top level page.
1673 nsIDocShell
* rootDocShell
= root
->GetDocShell();
1674 if (!rootDocShell
) {
1678 Document
* rootDocument
= rootDocShell
->GetDocument();
1679 if (!rootDocument
||
1680 rootDocument
->GetReadyStateEnum() != Document::READYSTATE_COMPLETE
||
1681 rootDocument
->IsInBackgroundWindow()) {
1685 PresShell
* presShell
= rootDocument
->GetPresShell();
1690 nsViewManager
* vm
= presShell
->GetViewManager();
1695 if (!sScheduler
.IsUserActive()) {
1696 Maybe
<TimeStamp
> next
= nsRefreshDriver::GetNextTickHint();
1697 // Try to not delay the next RefreshDriver tick, so give a reasonable
1698 // deadline for collectors.
1699 if (next
.isSome()) {
1700 if (sScheduler
.InIncrementalGC() || sScheduler
.IsCollectingCycles()) {
1701 sScheduler
.RunNextCollectorTimer(aReason
, next
.value());
1703 // In order to improve performance on the next page, run a minor GC.
1704 // The 16ms limit ensures it isn't called all the time if there are for
1705 // example multiple iframes loading at the same time.
1706 JS::RunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
1707 JS::GCReason::PREPARE_FOR_PAGELOAD
,
1708 mozilla::TimeDuration::FromMilliseconds(16));
1715 void nsJSContext::PokeGC(JS::GCReason aReason
, JSObject
* aObj
,
1716 TimeDuration aDelay
) {
1717 sScheduler
.PokeGC(aReason
, aObj
, aDelay
);
1721 void nsJSContext::MaybePokeGC() {
1722 if (sShuttingDown
) {
1726 JSRuntime
* rt
= CycleCollectedJSRuntime::Get()->Runtime();
1727 JS::GCReason reason
= JS::WantEagerMinorGC(rt
);
1728 if (reason
!= JS::GCReason::NO_REASON
) {
1729 MOZ_ASSERT(reason
== JS::GCReason::EAGER_NURSERY_COLLECTION
);
1730 sScheduler
.PokeMinorGC(reason
);
1733 // Bug 1772638: For now, only do eager minor GCs. Eager major GCs regress some
1734 // benchmarks. Hopefully that will be worked out and this will check for
1735 // whether an eager major GC is needed.
1738 void nsJSContext::DoLowMemoryGC() {
1739 if (sShuttingDown
) {
1742 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1743 nsJSContext::ShrinkingGC
);
1744 nsJSContext::CycleCollectNow(CCReason::MEM_PRESSURE
);
1745 if (sScheduler
.NeedsGCAfterCC()) {
1746 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1747 nsJSContext::ShrinkingGC
);
1752 void nsJSContext::LowMemoryGC() {
1753 RefPtr
<CCGCScheduler::MayGCPromise
> mbPromise
=
1754 CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE
);
1756 // Normally when the promise is null it means that IPC failed, that probably
1757 // means that something bad happened, don't bother with the GC.
1761 GetMainThreadSerialEventTarget(), __func__
,
1762 [](bool aIgnored
) { DoLowMemoryGC(); },
1763 [](mozilla::ipc::ResponseRejectReason r
) {});
1767 void nsJSContext::MaybePokeCC() {
1768 sScheduler
.MaybePokeCC(TimeStamp::NowLoRes(),
1769 nsCycleCollector_suspectedCount());
1772 static void DOMGCSliceCallback(JSContext
* aCx
, JS::GCProgress aProgress
,
1773 const JS::GCDescription
& aDesc
) {
1774 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
1776 static TimeStamp sCurrentGCStartTime
;
1778 switch (aProgress
) {
1779 case JS::GC_CYCLE_BEGIN
: {
1780 // Prevent cycle collections and shrinking during incremental GC.
1781 sScheduler
.NoteGCBegin(aDesc
.reason_
);
1782 sCurrentGCStartTime
= TimeStamp::Now();
1786 case JS::GC_CYCLE_END
: {
1787 TimeDuration delta
= GetCollectionTimeDelta();
1789 if (StaticPrefs::javascript_options_mem_log()) {
1791 gcstats
.Adopt(aDesc
.formatSummaryMessage(aCx
));
1792 nsAutoString prefix
;
1793 nsTextFormatter::ssprintf(prefix
, u
"GC(T+%.1f)[%s-%i] ",
1795 ProcessNameForCollectorLog(), getpid());
1796 nsString msg
= prefix
+ gcstats
;
1797 nsCOMPtr
<nsIConsoleService
> cs
=
1798 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1800 cs
->LogStringMessage(msg
.get());
1804 sScheduler
.NoteGCEnd();
1806 // May need to kill the GC runner
1807 sScheduler
.KillGCRunner();
1809 nsJSContext::MaybePokeCC();
1811 #if defined(MOZ_MEMORY)
1812 bool freeDirty
= false;
1814 if (aDesc
.isZone_
) {
1815 sScheduler
.PokeFullGC();
1817 #if defined(MOZ_MEMORY)
1820 sScheduler
.SetNeedsFullGC(false);
1821 sScheduler
.KillFullGCTimer();
1824 if (sScheduler
.IsCCNeeded(TimeStamp::Now(),
1825 nsCycleCollector_suspectedCount()) !=
1826 CCReason::NO_REASON
) {
1827 #if defined(MOZ_MEMORY)
1828 // We're likely to free the dirty pages after CC.
1831 nsCycleCollector_dispatchDeferredDeletion();
1834 Telemetry::Accumulate(Telemetry::GC_IN_PROGRESS_MS
,
1835 TimeUntilNow(sCurrentGCStartTime
).ToMilliseconds());
1837 #if defined(MOZ_MEMORY)
1840 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1841 jemalloc_free_dirty_pages();
1847 case JS::GC_SLICE_BEGIN
:
1850 case JS::GC_SLICE_END
:
1851 sScheduler
.NoteGCSliceEnd(aDesc
.lastSliceStart(aCx
),
1852 aDesc
.lastSliceEnd(aCx
));
1854 if (sShuttingDown
) {
1855 sScheduler
.KillGCRunner();
1857 // If incremental GC wasn't triggered by GCTimerFired, we may not have a
1858 // runner to ensure all the slices are handled. So, create the runner
1860 sScheduler
.EnsureGCRunner(0);
1863 if (sScheduler
.IsCCNeeded(TimeStamp::Now(),
1864 nsCycleCollector_suspectedCount()) !=
1865 CCReason::NO_REASON
) {
1866 nsCycleCollector_dispatchDeferredDeletion();
1869 if (StaticPrefs::javascript_options_mem_log()) {
1871 gcstats
.Adopt(aDesc
.formatSliceMessage(aCx
));
1872 nsAutoString prefix
;
1873 nsTextFormatter::ssprintf(prefix
, u
"[%s-%i] ",
1874 ProcessNameForCollectorLog(), getpid());
1875 nsString msg
= prefix
+ gcstats
;
1876 nsCOMPtr
<nsIConsoleService
> cs
=
1877 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1879 cs
->LogStringMessage(msg
.get());
1886 MOZ_CRASH("Unexpected GCProgress value");
1889 if (sPrevGCSliceCallback
) {
1890 (*sPrevGCSliceCallback
)(aCx
, aProgress
, aDesc
);
1894 void nsJSContext::SetWindowProxy(JS::Handle
<JSObject
*> aWindowProxy
) {
1895 mWindowProxy
= aWindowProxy
;
1898 JSObject
* nsJSContext::GetWindowProxy() { return mWindowProxy
; }
1900 void nsJSContext::LikelyShortLivingObjectCreated() {
1901 ++sScheduler
.mLikelyShortLivingObjectsNeedingGC
;
1904 void mozilla::dom::StartupJSEnvironment() {
1905 // initialize all our statics, so that we can restart XPCOM
1906 sIsInitialized
= false;
1907 sShuttingDown
= false;
1908 new (&sScheduler
) CCGCScheduler(); // Reset the scheduler state.
1912 static void SetGCParameter(JSGCParamKey aParam
, uint32_t aValue
) {
1915 JS_SetGCParameter(jsapi
.cx(), aParam
, aValue
);
1918 static void ResetGCParameter(JSGCParamKey aParam
) {
1921 JS_ResetGCParameter(jsapi
.cx(), aParam
);
1924 static void SetMemoryPrefChangedCallbackMB(const char* aPrefName
,
1926 int32_t prefMB
= Preferences::GetInt(aPrefName
, -1);
1927 // handle overflow and negative pref values
1928 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefMB
) * 1024 * 1024;
1929 if (prefB
.isValid() && prefB
.value() >= 0) {
1930 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1932 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1936 static void SetMemoryNurseryPrefChangedCallback(const char* aPrefName
,
1938 int32_t prefKB
= Preferences::GetInt(aPrefName
, -1);
1939 // handle overflow and negative pref values
1940 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefKB
) * 1024;
1941 if (prefB
.isValid() && prefB
.value() >= 0) {
1942 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1944 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1948 static void SetMemoryPrefChangedCallbackInt(const char* aPrefName
,
1950 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1951 // handle overflow and negative pref values
1952 if (pref
>= 0 && pref
< 10000) {
1953 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1955 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1959 static void SetMemoryPrefChangedCallbackBool(const char* aPrefName
,
1961 bool pref
= Preferences::GetBool(aPrefName
);
1962 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1965 static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName
,
1967 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1968 // handle overflow and negative pref values
1969 if (pref
> 0 && pref
< 100000) {
1970 sScheduler
.SetActiveIntersliceGCBudget(
1971 TimeDuration::FromMilliseconds(pref
));
1972 SetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
, pref
);
1974 ResetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
);
1978 static void SetIncrementalCCPrefChangedCallback(const char* aPrefName
,
1980 bool pref
= Preferences::GetBool(aPrefName
);
1981 sIncrementalCC
= pref
;
1984 class JSDispatchableRunnable final
: public Runnable
{
1985 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable
); }
1988 explicit JSDispatchableRunnable(JS::Dispatchable
* aDispatchable
)
1989 : mozilla::Runnable("JSDispatchableRunnable"),
1990 mDispatchable(aDispatchable
) {
1991 MOZ_ASSERT(mDispatchable
);
1995 NS_IMETHOD
Run() override
{
1996 MOZ_ASSERT(NS_IsMainThread());
2001 JS::Dispatchable::MaybeShuttingDown maybeShuttingDown
=
2002 sShuttingDown
? JS::Dispatchable::ShuttingDown
2003 : JS::Dispatchable::NotShuttingDown
;
2005 mDispatchable
->run(jsapi
.cx(), maybeShuttingDown
);
2006 mDispatchable
= nullptr; // mDispatchable may delete itself
2012 JS::Dispatchable
* mDispatchable
;
2015 static bool DispatchToEventLoop(void* closure
,
2016 JS::Dispatchable
* aDispatchable
) {
2017 MOZ_ASSERT(!closure
);
2019 // This callback may execute either on the main thread or a random JS-internal
2020 // helper thread. This callback can be called during shutdown so we cannot
2021 // simply NS_DispatchToMainThread. Failure during shutdown is expected and
2022 // properly handled by the JS engine.
2024 nsCOMPtr
<nsIEventTarget
> mainTarget
= GetMainThreadSerialEventTarget();
2029 RefPtr
<JSDispatchableRunnable
> r
= new JSDispatchableRunnable(aDispatchable
);
2030 MOZ_ALWAYS_SUCCEEDS(mainTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
2034 static bool ConsumeStream(JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
,
2035 JS::MimeType aMimeType
,
2036 JS::StreamConsumer
* aConsumer
) {
2037 return FetchUtil::StreamResponseToJS(aCx
, aObj
, aMimeType
, aConsumer
,
2041 static js::SliceBudget
CreateGCSliceBudget(JS::GCReason aReason
,
2043 return sScheduler
.CreateGCSliceBudget(
2044 mozilla::TimeDuration::FromMilliseconds(aMillis
), false, false);
2047 void nsJSContext::EnsureStatics() {
2048 if (sIsInitialized
) {
2049 if (!nsContentUtils::XPConnect()) {
2055 // Let's make sure that our main thread is the same as the xpcom main thread.
2056 MOZ_ASSERT(NS_IsMainThread());
2061 sPrevGCSliceCallback
= JS::SetGCSliceCallback(jsapi
.cx(), DOMGCSliceCallback
);
2063 JS::SetCreateGCSliceBudgetCallback(jsapi
.cx(), CreateGCSliceBudget
);
2065 JS::InitDispatchToEventLoop(jsapi
.cx(), DispatchToEventLoop
, nullptr);
2066 JS::InitConsumeStreamCallback(jsapi
.cx(), ConsumeStream
,
2067 FetchUtil::ReportJSStreamError
);
2069 // Set these global xpconnect options...
2070 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB
,
2071 "javascript.options.mem.max",
2072 (void*)JSGC_MAX_BYTES
);
2073 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
2074 "javascript.options.mem.nursery.min_kb",
2075 (void*)JSGC_MIN_NURSERY_BYTES
);
2076 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
2077 "javascript.options.mem.nursery.max_kb",
2078 (void*)JSGC_MAX_NURSERY_BYTES
);
2080 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
2081 "javascript.options.mem.gc_per_zone",
2082 (void*)JSGC_PER_ZONE_GC_ENABLED
);
2084 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
2085 "javascript.options.mem.gc_incremental",
2086 (void*)JSGC_INCREMENTAL_GC_ENABLED
);
2088 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
2089 "javascript.options.mem.gc_compacting",
2090 (void*)JSGC_COMPACTING_ENABLED
);
2092 Preferences::RegisterCallbackAndCall(
2093 SetMemoryPrefChangedCallbackBool
,
2094 "javascript.options.mem.gc_parallel_marking",
2095 (void*)JSGC_PARALLEL_MARKING_ENABLED
);
2097 Preferences::RegisterCallbackAndCall(
2098 SetMemoryPrefChangedCallbackInt
,
2099 "javascript.options.mem.gc_parallel_marking_threshold_mb",
2100 (void*)JSGC_PARALLEL_MARKING_THRESHOLD_MB
);
2102 Preferences::RegisterCallbackAndCall(
2103 SetMemoryGCSliceTimePrefChangedCallback
,
2104 "javascript.options.mem.gc_incremental_slice_ms");
2106 Preferences::RegisterCallbackAndCall(
2107 SetMemoryPrefChangedCallbackBool
,
2108 "javascript.options.mem.incremental_weakmap",
2109 (void*)JSGC_INCREMENTAL_WEAKMAP_ENABLED
);
2111 Preferences::RegisterCallbackAndCall(
2112 SetMemoryPrefChangedCallbackInt
,
2113 "javascript.options.mem.gc_high_frequency_time_limit_ms",
2114 (void*)JSGC_HIGH_FREQUENCY_TIME_LIMIT
);
2116 Preferences::RegisterCallbackAndCall(
2117 SetMemoryPrefChangedCallbackInt
,
2118 "javascript.options.mem.gc_low_frequency_heap_growth",
2119 (void*)JSGC_LOW_FREQUENCY_HEAP_GROWTH
);
2121 Preferences::RegisterCallbackAndCall(
2122 SetMemoryPrefChangedCallbackInt
,
2123 "javascript.options.mem.gc_high_frequency_large_heap_growth",
2124 (void*)JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH
);
2126 Preferences::RegisterCallbackAndCall(
2127 SetMemoryPrefChangedCallbackInt
,
2128 "javascript.options.mem.gc_high_frequency_small_heap_growth",
2129 (void*)JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH
);
2131 Preferences::RegisterCallbackAndCall(
2132 SetMemoryPrefChangedCallbackBool
,
2133 "javascript.options.mem.gc_balanced_heap_limits",
2134 (void*)JSGC_BALANCED_HEAP_LIMITS_ENABLED
);
2136 Preferences::RegisterCallbackAndCall(
2137 SetMemoryPrefChangedCallbackInt
,
2138 "javascript.options.mem.gc_heap_growth_factor",
2139 (void*)JSGC_HEAP_GROWTH_FACTOR
);
2141 Preferences::RegisterCallbackAndCall(
2142 SetMemoryPrefChangedCallbackInt
,
2143 "javascript.options.mem.gc_small_heap_size_max_mb",
2144 (void*)JSGC_SMALL_HEAP_SIZE_MAX
);
2146 Preferences::RegisterCallbackAndCall(
2147 SetMemoryPrefChangedCallbackInt
,
2148 "javascript.options.mem.gc_large_heap_size_min_mb",
2149 (void*)JSGC_LARGE_HEAP_SIZE_MIN
);
2151 Preferences::RegisterCallbackAndCall(
2152 SetMemoryPrefChangedCallbackInt
,
2153 "javascript.options.mem.gc_allocation_threshold_mb",
2154 (void*)JSGC_ALLOCATION_THRESHOLD
);
2156 Preferences::RegisterCallbackAndCall(
2157 SetMemoryPrefChangedCallbackInt
,
2158 "javascript.options.mem.gc_malloc_threshold_base_mb",
2159 (void*)JSGC_MALLOC_THRESHOLD_BASE
);
2161 Preferences::RegisterCallbackAndCall(
2162 SetMemoryPrefChangedCallbackInt
,
2163 "javascript.options.mem.gc_small_heap_incremental_limit",
2164 (void*)JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
);
2165 Preferences::RegisterCallbackAndCall(
2166 SetMemoryPrefChangedCallbackInt
,
2167 "javascript.options.mem.gc_large_heap_incremental_limit",
2168 (void*)JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
);
2170 Preferences::RegisterCallbackAndCall(
2171 SetMemoryPrefChangedCallbackInt
,
2172 "javascript.options.mem.gc_urgent_threshold_mb",
2173 (void*)JSGC_URGENT_THRESHOLD_MB
);
2175 Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback
,
2176 "dom.cycle_collector.incremental");
2178 Preferences::RegisterCallbackAndCall(
2179 SetMemoryPrefChangedCallbackInt
,
2180 "javascript.options.mem.gc_min_empty_chunk_count",
2181 (void*)JSGC_MIN_EMPTY_CHUNK_COUNT
);
2183 Preferences::RegisterCallbackAndCall(
2184 SetMemoryPrefChangedCallbackInt
,
2185 "javascript.options.mem.gc_max_empty_chunk_count",
2186 (void*)JSGC_MAX_EMPTY_CHUNK_COUNT
);
2188 Preferences::RegisterCallbackAndCall(
2189 SetMemoryPrefChangedCallbackInt
,
2190 "javascript.options.mem.gc_helper_thread_ratio",
2191 (void*)JSGC_HELPER_THREAD_RATIO
);
2193 Preferences::RegisterCallbackAndCall(
2194 SetMemoryPrefChangedCallbackInt
,
2195 "javascript.options.mem.gc_max_helper_threads",
2196 (void*)JSGC_MAX_HELPER_THREADS
);
2198 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
2203 nsIObserver
* observer
= new nsJSEnvironmentObserver();
2204 obs
->AddObserver(observer
, "memory-pressure", false);
2205 obs
->AddObserver(observer
, "user-interaction-inactive", false);
2206 obs
->AddObserver(observer
, "user-interaction-active", false);
2207 obs
->AddObserver(observer
, "quit-application", false);
2208 obs
->AddObserver(observer
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
2209 obs
->AddObserver(observer
, "content-child-will-shutdown", false);
2211 sIsInitialized
= true;
2214 void mozilla::dom::ShutdownJSEnvironment() {
2215 sShuttingDown
= true;
2216 sScheduler
.Shutdown();
2219 AsyncErrorReporter::AsyncErrorReporter(xpc::ErrorReport
* aReport
)
2220 : Runnable("dom::AsyncErrorReporter"), mReport(aReport
) {}
2222 void AsyncErrorReporter::SerializeStack(JSContext
* aCx
,
2223 JS::Handle
<JSObject
*> aStack
) {
2224 mStackHolder
= MakeUnique
<SerializedStackHolder
>();
2225 mStackHolder
->SerializeMainThreadOrWorkletStack(aCx
, aStack
);
2228 void AsyncErrorReporter::SetException(JSContext
* aCx
,
2229 JS::Handle
<JS::Value
> aException
) {
2230 MOZ_ASSERT(NS_IsMainThread());
2231 mException
.init(aCx
, aException
);
2232 mHasException
= true;
2235 NS_IMETHODIMP
AsyncErrorReporter::Run() {
2237 // We're only using this context to deserialize a stack to report to the
2238 // console, so the scope we use doesn't matter. Stack frame filtering happens
2239 // based on the principal encoded into the frame and the caller compartment,
2240 // not the compartment of the frame object, and the console reporting code
2241 // will not be using our context, and therefore will not care what compartment
2243 DebugOnly
<bool> ok
= jsapi
.Init(xpc::PrivilegedJunkScope());
2244 MOZ_ASSERT(ok
, "Problem with system global?");
2245 JSContext
* cx
= jsapi
.cx();
2246 JS::Rooted
<JSObject
*> stack(cx
);
2247 JS::Rooted
<JSObject
*> stackGlobal(cx
);
2249 stack
= mStackHolder
->ReadStack(cx
);
2251 stackGlobal
= JS::CurrentGlobalOrNull(cx
);
2255 JS::Rooted
<Maybe
<JS::Value
>> exception(cx
, Nothing());
2256 if (mHasException
) {
2257 MOZ_ASSERT(NS_IsMainThread());
2258 exception
= Some(mException
);
2259 // Remove our reference to the exception.
2260 mException
.setUndefined();
2261 mHasException
= false;
2264 mReport
->LogToConsoleWithStack(nullptr, exception
, stack
, stackGlobal
);
2268 // A fast-array class for JS. This class supports both nsIJSScriptArray and
2269 // nsIArray. If it is JS itself providing and consuming this class, all work
2270 // can be done via nsIJSScriptArray, and avoid the conversion of elements
2271 // to/from nsISupports.
2272 // When consumed by non-JS (eg, another script language), conversion is done
2274 class nsJSArgArray final
: public nsIJSArgArray
{
2276 nsJSArgArray(JSContext
* aContext
, uint32_t argc
, const JS::Value
* argv
,
2280 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2281 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray
,
2288 nsresult
GetArgs(uint32_t* argc
, void** argv
) override
;
2290 void ReleaseJSObjects();
2294 JSContext
* mContext
;
2295 JS::Heap
<JS::Value
>* mArgv
;
2299 nsJSArgArray::nsJSArgArray(JSContext
* aContext
, uint32_t argc
,
2300 const JS::Value
* argv
, nsresult
* prv
)
2301 : mContext(aContext
), mArgv(nullptr), mArgc(argc
) {
2302 // copy the array - we don't know its lifetime, and ours is tied to xpcom
2305 mArgv
= new (fallible
) JS::Heap
<JS::Value
>[argc
];
2307 *prv
= NS_ERROR_OUT_OF_MEMORY
;
2312 // Callers are allowed to pass in a null argv even for argc > 0. They can
2313 // then use GetArgs to initialize the values.
2315 for (uint32_t i
= 0; i
< argc
; ++i
) mArgv
[i
] = argv
[i
];
2319 mozilla::HoldJSObjects(this);
2325 nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); }
2327 void nsJSArgArray::ReleaseJSObjects() {
2332 mozilla::DropJSObjects(this);
2336 // QueryInterface implementation for nsJSArgArray
2337 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray
)
2339 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray
)
2340 tmp
->ReleaseJSObjects();
2341 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2342 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray
)
2343 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2345 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray
)
2347 for (uint32_t i
= 0; i
< tmp
->mArgc
; ++i
) {
2348 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv
[i
])
2351 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2353 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray
)
2354 NS_INTERFACE_MAP_ENTRY(nsIArray
)
2355 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray
)
2356 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIJSArgArray
)
2357 NS_INTERFACE_MAP_END
2359 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray
)
2360 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray
)
2362 nsresult
nsJSArgArray::GetArgs(uint32_t* argc
, void** argv
) {
2363 *argv
= (void*)mArgv
;
2369 NS_IMETHODIMP
nsJSArgArray::GetLength(uint32_t* aLength
) {
2374 NS_IMETHODIMP
nsJSArgArray::QueryElementAt(uint32_t index
, const nsIID
& uuid
,
2377 if (index
>= mArgc
) return NS_ERROR_INVALID_ARG
;
2379 if (uuid
.Equals(NS_GET_IID(nsIVariant
)) ||
2380 uuid
.Equals(NS_GET_IID(nsISupports
))) {
2381 // Have to copy a Heap into a Rooted to work with it.
2382 JS::Rooted
<JS::Value
> val(mContext
, mArgv
[index
]);
2383 return nsContentUtils::XPConnect()->JSToVariant(mContext
, val
,
2384 (nsIVariant
**)result
);
2386 NS_WARNING("nsJSArgArray only handles nsIVariant");
2387 return NS_ERROR_NO_INTERFACE
;
2390 NS_IMETHODIMP
nsJSArgArray::IndexOf(uint32_t startIndex
, nsISupports
* element
,
2391 uint32_t* _retval
) {
2392 return NS_ERROR_NOT_IMPLEMENTED
;
2395 NS_IMETHODIMP
nsJSArgArray::ScriptedEnumerate(const nsIID
& aElemIID
,
2397 nsISimpleEnumerator
** aResult
) {
2398 return NS_ERROR_NOT_IMPLEMENTED
;
2401 NS_IMETHODIMP
nsJSArgArray::EnumerateImpl(const nsID
& aEntryIID
,
2402 nsISimpleEnumerator
** _retval
) {
2403 return NS_ERROR_NOT_IMPLEMENTED
;
2406 // The factory function
2407 nsresult
NS_CreateJSArgv(JSContext
* aContext
, uint32_t argc
,
2408 const JS::Value
* argv
, nsIJSArgArray
** aArray
) {
2410 nsCOMPtr
<nsIJSArgArray
> ret
= new nsJSArgArray(aContext
, argc
, argv
, &rv
);
2411 if (NS_FAILED(rv
)) {