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
= nullptr;
110 static std::aligned_storage_t
<sizeof(*sScheduler
)> sSchedulerStorage
;
112 struct CycleCollectorStats
{
113 constexpr CycleCollectorStats() = default;
116 void PrepareForCycleCollection(TimeStamp aNow
);
117 void AfterPrepareForCycleCollectionSlice(TimeStamp aDeadline
,
118 TimeStamp aBeginTime
,
119 TimeStamp aMaybeAfterGCTime
);
120 void AfterCycleCollectionSlice();
121 void AfterSyncForgetSkippable(TimeStamp beginTime
);
122 void AfterForgetSkippable(TimeDuration duration
, uint32_t aRemovedPurples
);
123 void AfterCycleCollection();
125 void SendTelemetry(TimeDuration aCCNowDuration
, TimeStamp aPrevCCEnd
) const;
126 void MaybeLogStats(const CycleCollectorResults
& aResults
,
127 uint32_t aCleanups
) const;
128 void MaybeNotifyStats(const CycleCollectorResults
& aResults
,
129 TimeDuration aCCNowDuration
, uint32_t aCleanups
) const;
131 // Time the current slice began, including any GC finishing.
132 TimeStamp mBeginSliceTime
;
134 // Time the previous slice of the current CC ended.
135 TimeStamp mEndSliceTime
;
137 // Time the current cycle collection began.
138 TimeStamp mBeginTime
;
140 // The longest GC finishing duration for any slice of the current CC.
141 TimeDuration mMaxGCDuration
;
143 // True if we ran sync forget skippable in any slice of the current CC.
144 bool mRanSyncForgetSkippable
= false;
146 // Number of suspected objects at the start of the current CC.
147 uint32_t mSuspected
= 0;
149 // The longest duration spent on sync forget skippable in any slice of the
151 TimeDuration mMaxSkippableDuration
;
153 // The longest pause of any slice in the current CC.
154 TimeDuration mMaxSliceTime
;
156 // The longest slice time since ClearMaxCCSliceTime() was called.
157 TimeDuration mMaxSliceTimeSinceClear
;
159 // The total amount of time spent actually running the current CC.
160 TimeDuration mTotalSliceTime
;
162 // True if we were locked out by the GC in any slice of the current CC.
163 bool mAnyLockedOut
= false;
165 // A file to dump CC activity to; set by MOZ_CCTIMER environment variable.
166 FILE* mFile
= nullptr;
168 // In case CC slice was triggered during idle time, set to the end of the idle
170 TimeStamp mIdleDeadline
;
172 TimeDuration mMinForgetSkippableTime
;
173 TimeDuration mMaxForgetSkippableTime
;
174 TimeDuration mTotalForgetSkippableTime
;
175 uint32_t mForgetSkippableBeforeCC
= 0;
177 uint32_t mRemovedPurples
= 0;
180 static CycleCollectorStats sCCStats
;
182 static const char* ProcessNameForCollectorLog() {
183 return XRE_GetProcessType() == GeckoProcessType_Default
? "default"
189 // This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
190 // Exceptions, and arbitrary values that were associated with a stack by the
191 // JS engine when they were thrown, as specified by exceptionStack.
193 // Note that the returned stackObj and stackGlobal are _not_ wrapped into the
194 // compartment of exceptionValue.
195 void FindExceptionStackForConsoleReport(
196 nsPIDOMWindowInner
* win
, JS::Handle
<JS::Value
> exceptionValue
,
197 JS::Handle
<JSObject
*> exceptionStack
, JS::MutableHandle
<JSObject
*> stackObj
,
198 JS::MutableHandle
<JSObject
*> stackGlobal
) {
199 stackObj
.set(nullptr);
200 stackGlobal
.set(nullptr);
202 if (!exceptionValue
.isObject()) {
203 // Use the stack provided by the JS engine, if available. This will not be
205 if (exceptionStack
) {
206 stackObj
.set(exceptionStack
);
207 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
212 if (win
&& win
->AsGlobal()->IsDying()) {
213 // Pretend like we have no stack, so we don't end up keeping the global
214 // alive via the stack.
218 JS::RootingContext
* rcx
= RootingCx();
219 JS::Rooted
<JSObject
*> exceptionObject(rcx
, &exceptionValue
.toObject());
220 if (JSObject
* excStack
= JS::ExceptionStackOrNull(exceptionObject
)) {
221 // At this point we know exceptionObject is a possibly-wrapped
222 // js::ErrorObject that has excStack as stack. excStack might also be a CCW,
223 // but excStack must be same-compartment with the unwrapped ErrorObject.
224 // Return the ErrorObject's global as stackGlobal. This matches what we do
225 // in the ErrorObject's |.stack| getter and ensures stackObj and stackGlobal
226 // are same-compartment.
227 JSObject
* unwrappedException
= js::UncheckedUnwrap(exceptionObject
);
228 stackObj
.set(excStack
);
229 stackGlobal
.set(JS::GetNonCCWObjectGlobal(unwrappedException
));
233 // It is not a JS Exception, try DOM Exception.
234 RefPtr
<Exception
> exception
;
235 UNWRAP_OBJECT(DOMException
, exceptionObject
, exception
);
237 // Not a DOM Exception, try XPC Exception.
238 UNWRAP_OBJECT(Exception
, exceptionObject
, exception
);
240 // As above, use the stack provided by the JS engine, if available.
241 if (exceptionStack
) {
242 stackObj
.set(exceptionStack
);
243 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
249 nsCOMPtr
<nsIStackFrame
> stack
= exception
->GetLocation();
253 JS::Rooted
<JS::Value
> value(rcx
);
254 stack
->GetNativeSavedFrame(&value
);
255 if (value
.isObject()) {
256 stackObj
.set(&value
.toObject());
257 MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj
));
258 stackGlobal
.set(JS::GetNonCCWObjectGlobal(stackObj
));
263 } /* namespace xpc */
265 static TimeDuration
GetCollectionTimeDelta() {
266 static TimeStamp sFirstCollectionTime
;
267 TimeStamp now
= TimeStamp::Now();
268 if (sFirstCollectionTime
) {
269 return now
- sFirstCollectionTime
;
271 sFirstCollectionTime
= now
;
272 return TimeDuration();
275 class nsJSEnvironmentObserver final
: public nsIObserver
{
276 ~nsJSEnvironmentObserver() = default;
283 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver
, nsIObserver
)
286 nsJSEnvironmentObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
287 const char16_t
* aData
) {
288 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) {
289 if (StaticPrefs::javascript_options_gc_on_memory_pressure()) {
291 // Don't GC/CC if we're already shutting down.
294 nsDependentString
data(aData
);
295 if (data
.EqualsLiteral("low-memory-ongoing")) {
296 // Don't GC/CC if we are in an ongoing low-memory state since its very
297 // slow and it likely won't help us anyway.
300 if (data
.EqualsLiteral("heap-minimize")) {
301 // heap-minimize notifiers expect this to run synchronously
302 nsJSContext::DoLowMemoryGC();
305 if (data
.EqualsLiteral("low-memory")) {
306 nsJSContext::SetLowMemoryState(true);
308 // Asynchronously GC.
309 nsJSContext::LowMemoryGC();
311 } else if (!nsCRT::strcmp(aTopic
, "memory-pressure-stop")) {
312 nsJSContext::SetLowMemoryState(false);
313 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-inactive")) {
314 sScheduler
->UserIsInactive();
315 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-active")) {
316 sScheduler
->UserIsActive();
317 } else if (!nsCRT::strcmp(aTopic
, "quit-application") ||
318 !nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) ||
319 !nsCRT::strcmp(aTopic
, "content-child-will-shutdown")) {
320 sShuttingDown
= true;
321 sScheduler
->Shutdown();
327 /****************************************************************
328 ************************** AutoFree ****************************
329 ****************************************************************/
333 explicit AutoFree(void* aPtr
) : mPtr(aPtr
) {}
335 if (mPtr
) free(mPtr
);
337 void Invalidate() { mPtr
= nullptr; }
343 // A utility function for script languages to call. Although it looks small,
344 // the use of nsIDocShell and nsPresContext triggers a huge number of
345 // dependencies that most languages would not otherwise need.
346 // XXXmarkh - This function is mis-placed!
347 bool NS_HandleScriptError(nsIScriptGlobalObject
* aScriptGlobal
,
348 const ErrorEventInit
& aErrorEventInit
,
349 nsEventStatus
* aStatus
) {
351 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryInterface(aScriptGlobal
));
352 nsIDocShell
* docShell
= win
? win
->GetDocShell() : nullptr;
354 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
356 static int32_t errorDepth
; // Recursion prevention
359 if (errorDepth
< 2) {
360 // Dispatch() must be synchronous for the recursion block
361 // (errorDepth) to work.
362 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
363 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, aErrorEventInit
);
364 event
->SetTrusted(true);
366 // MOZ_KnownLive due to bug 1506441
367 EventDispatcher::DispatchDOMEvent(
368 MOZ_KnownLive(nsGlobalWindowInner::Cast(win
)), nullptr, event
,
369 presContext
, aStatus
);
377 class ScriptErrorEvent
: public Runnable
{
379 ScriptErrorEvent(nsPIDOMWindowInner
* aWindow
, JS::RootingContext
* aRootingCx
,
380 xpc::ErrorReport
* aReport
, JS::Handle
<JS::Value
> aError
,
381 JS::Handle
<JSObject
*> aErrorStack
)
382 : mozilla::Runnable("ScriptErrorEvent"),
385 mError(aRootingCx
, aError
),
386 mErrorStack(aRootingCx
, aErrorStack
) {}
388 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
389 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
390 nsEventStatus status
= nsEventStatus_eIgnore
;
391 nsCOMPtr
<nsPIDOMWindowInner
> win
= mWindow
;
393 MOZ_ASSERT(NS_IsMainThread());
394 // First, notify the DOM that we have a script error, but only if
395 // our window is still the current inner.
396 JS::RootingContext
* rootingCx
= RootingCx();
397 if (win
->IsCurrentInnerWindow() && win
->GetDocShell() &&
398 !sHandlingScriptError
) {
399 AutoRestore
<bool> recursionGuard(sHandlingScriptError
);
400 sHandlingScriptError
= true;
402 RefPtr
<nsPresContext
> presContext
= win
->GetDocShell()->GetPresContext();
404 RootedDictionary
<ErrorEventInit
> init(rootingCx
);
405 init
.mCancelable
= true;
406 init
.mFilename
= mReport
->mFileName
;
407 init
.mBubbles
= true;
409 constexpr auto xoriginMsg
= u
"Script error."_ns
;
410 if (!mReport
->mIsMuted
) {
411 init
.mMessage
= mReport
->mErrorMsg
;
412 init
.mLineno
= mReport
->mLineNumber
;
413 init
.mColno
= mReport
->mColumn
;
414 init
.mError
= mError
;
416 NS_WARNING("Not same origin error!");
417 init
.mMessage
= xoriginMsg
;
421 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
422 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, init
);
423 event
->SetTrusted(true);
425 // MOZ_KnownLive due to bug 1506441
426 EventDispatcher::DispatchDOMEvent(
427 MOZ_KnownLive(nsGlobalWindowInner::Cast(win
)), nullptr, event
,
428 presContext
, &status
);
431 if (status
!= nsEventStatus_eConsumeNoDefault
) {
432 JS::Rooted
<JSObject
*> stack(rootingCx
);
433 JS::Rooted
<JSObject
*> stackGlobal(rootingCx
);
434 xpc::FindExceptionStackForConsoleReport(win
, mError
, mErrorStack
, &stack
,
436 JS::Rooted
<Maybe
<JS::Value
>> exception(rootingCx
, Some(mError
));
437 nsGlobalWindowInner
* inner
= nsGlobalWindowInner::Cast(win
);
438 mReport
->LogToConsoleWithStack(inner
, exception
, stack
, stackGlobal
);
445 nsCOMPtr
<nsPIDOMWindowInner
> mWindow
;
446 RefPtr
<xpc::ErrorReport
> mReport
;
447 JS::PersistentRooted
<JS::Value
> mError
;
448 JS::PersistentRooted
<JSObject
*> mErrorStack
;
450 static bool sHandlingScriptError
;
453 bool ScriptErrorEvent::sHandlingScriptError
= false;
455 // This temporarily lives here to avoid code churn. It will go away entirely
459 void DispatchScriptErrorEvent(nsPIDOMWindowInner
* win
,
460 JS::RootingContext
* rootingCx
,
461 xpc::ErrorReport
* xpcReport
,
462 JS::Handle
<JS::Value
> exception
,
463 JS::Handle
<JSObject
*> exceptionStack
) {
464 nsContentUtils::AddScriptRunner(new ScriptErrorEvent(
465 win
, rootingCx
, xpcReport
, exception
, exceptionStack
));
468 } /* namespace xpc */
471 // A couple of useful functions to call when you're debugging.
472 nsGlobalWindowInner
* JSObject2Win(JSObject
* obj
) {
473 return xpc::WindowOrNull(obj
);
476 template <typename T
>
477 void PrintWinURI(T
* win
) {
479 printf("No window passed in.\n");
483 nsCOMPtr
<Document
> doc
= win
->GetExtantDoc();
485 printf("No document in the window.\n");
489 nsIURI
* uri
= doc
->GetDocumentURI();
491 printf("Document doesn't have a URI.\n");
495 printf("%s\n", uri
->GetSpecOrDefault().get());
498 void PrintWinURIInner(nsGlobalWindowInner
* aWin
) { return PrintWinURI(aWin
); }
500 void PrintWinURIOuter(nsGlobalWindowOuter
* aWin
) { return PrintWinURI(aWin
); }
502 template <typename T
>
503 void PrintWinCodebase(T
* win
) {
505 printf("No window passed in.\n");
509 nsIPrincipal
* prin
= win
->GetPrincipal();
511 printf("Window doesn't have principals.\n");
514 if (prin
->IsSystemPrincipal()) {
515 printf("No URI, it's the system principal.\n");
519 prin
->GetAsciiSpec(spec
);
520 printf("%s\n", spec
.get());
523 void PrintWinCodebaseInner(nsGlobalWindowInner
* aWin
) {
524 return PrintWinCodebase(aWin
);
527 void PrintWinCodebaseOuter(nsGlobalWindowOuter
* aWin
) {
528 return PrintWinCodebase(aWin
);
531 void DumpString(const nsAString
& str
) {
532 printf("%s\n", NS_ConvertUTF16toUTF8(str
).get());
536 nsJSContext::nsJSContext(bool aGCOnDestruction
,
537 nsIScriptGlobalObject
* aGlobalObject
)
538 : mWindowProxy(nullptr),
539 mGCOnDestruction(aGCOnDestruction
),
540 mGlobalObjectRef(aGlobalObject
) {
543 mProcessingScriptTag
= false;
547 nsJSContext::~nsJSContext() {
548 mGlobalObjectRef
= nullptr;
553 void nsJSContext::Destroy() {
554 if (mGCOnDestruction
) {
555 sScheduler
->PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY
, mWindowProxy
);
561 // QueryInterface implementation for nsJSContext
562 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext
)
564 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext
)
565 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy
)
566 NS_IMPL_CYCLE_COLLECTION_TRACE_END
568 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext
)
569 tmp
->mGCOnDestruction
= false;
570 tmp
->mWindowProxy
= nullptr;
572 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef
)
573 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext
)
575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef
)
576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
578 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext
)
579 NS_INTERFACE_MAP_ENTRY(nsIScriptContext
)
580 NS_INTERFACE_MAP_ENTRY(nsISupports
)
583 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext
)
584 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext
)
587 bool AtomIsEventHandlerName(nsAtom
* aName
) {
588 const char16_t
* name
= aName
->GetUTF16String();
592 for (cp
= name
; *cp
!= '\0'; ++cp
) {
594 if ((c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z')) return false;
601 nsIScriptGlobalObject
* nsJSContext::GetGlobalObject() {
602 // Note: this could probably be simplified somewhat more; see bug 974327
608 MOZ_ASSERT(mGlobalObjectRef
);
609 return mGlobalObjectRef
;
612 nsresult
nsJSContext::SetProperty(JS::Handle
<JSObject
*> aTarget
,
613 const char* aPropName
, nsISupports
* aArgs
) {
615 if (NS_WARN_IF(!jsapi
.Init(GetGlobalObject()))) {
616 return NS_ERROR_FAILURE
;
618 JSContext
* cx
= jsapi
.cx();
620 JS::RootedVector
<JS::Value
> args(cx
);
622 JS::Rooted
<JSObject
*> global(cx
, GetWindowProxy());
623 nsresult rv
= ConvertSupportsTojsvals(cx
, aArgs
, global
, &args
);
624 NS_ENSURE_SUCCESS(rv
, rv
);
626 // got the arguments, now attach them.
628 for (uint32_t i
= 0; i
< args
.length(); ++i
) {
629 if (!JS_WrapValue(cx
, args
[i
])) {
630 return NS_ERROR_FAILURE
;
634 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, args
));
636 return NS_ERROR_FAILURE
;
639 return JS_DefineProperty(cx
, aTarget
, aPropName
, array
, 0) ? NS_OK
643 nsresult
nsJSContext::ConvertSupportsTojsvals(
644 JSContext
* aCx
, nsISupports
* aArgs
, JS::Handle
<JSObject
*> aScope
,
645 JS::MutableHandleVector
<JS::Value
> aArgsOut
) {
648 // If the array implements nsIJSArgArray, copy the contents and return.
649 nsCOMPtr
<nsIJSArgArray
> fastArray
= do_QueryInterface(aArgs
);
653 rv
= fastArray
->GetArgs(&argc
, reinterpret_cast<void**>(&argv
));
654 if (NS_SUCCEEDED(rv
) && !aArgsOut
.append(argv
, argc
)) {
655 rv
= NS_ERROR_OUT_OF_MEMORY
;
660 // Take the slower path converting each item.
661 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
662 // SetProperty('arguments', ...);
664 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
665 NS_ENSURE_TRUE(xpc
, NS_ERROR_UNEXPECTED
);
667 if (!aArgs
) return NS_OK
;
669 // This general purpose function may need to convert an arg array
670 // (window.arguments, event-handler args) and a generic property.
671 nsCOMPtr
<nsIArray
> argsArray(do_QueryInterface(aArgs
));
674 rv
= argsArray
->GetLength(&argCount
);
675 NS_ENSURE_SUCCESS(rv
, rv
);
676 if (argCount
== 0) return NS_OK
;
678 argCount
= 1; // the nsISupports which is not an array
681 // Use the caller's auto guards to release and unroot.
682 if (!aArgsOut
.resize(argCount
)) {
683 return NS_ERROR_OUT_OF_MEMORY
;
687 for (uint32_t argCtr
= 0; argCtr
< argCount
&& NS_SUCCEEDED(rv
); argCtr
++) {
688 nsCOMPtr
<nsISupports
> arg
;
689 JS::MutableHandle
<JS::Value
> thisVal
= aArgsOut
[argCtr
];
690 argsArray
->QueryElementAt(argCtr
, NS_GET_IID(nsISupports
),
691 getter_AddRefs(arg
));
696 nsCOMPtr
<nsIVariant
> variant(do_QueryInterface(arg
));
697 if (variant
!= nullptr) {
698 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, thisVal
);
700 // And finally, support the nsISupportsPrimitives supplied
701 // by the AppShell. It generally will pass only strings, but
702 // as we have code for handling all, we may as well use it.
703 rv
= AddSupportsPrimitiveTojsvals(aCx
, arg
, thisVal
.address());
704 if (rv
== NS_ERROR_NO_INTERFACE
) {
705 // something else - probably an event object or similar -
708 // but first, check its not another nsISupportsPrimitive, as
709 // these are now deprecated for use with script contexts.
710 nsCOMPtr
<nsISupportsPrimitive
> prim(do_QueryInterface(arg
));
711 NS_ASSERTION(prim
== nullptr,
712 "Don't pass nsISupportsPrimitives - use nsIVariant!");
714 JSAutoRealm
ar(aCx
, aScope
);
715 rv
= nsContentUtils::WrapNative(aCx
, arg
, thisVal
);
720 nsCOMPtr
<nsIVariant
> variant
= do_QueryInterface(aArgs
);
722 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, aArgsOut
[0]);
724 NS_ERROR("Not an array, not an interface?");
725 rv
= NS_ERROR_UNEXPECTED
;
731 // This really should go into xpconnect somewhere...
732 nsresult
nsJSContext::AddSupportsPrimitiveTojsvals(JSContext
* aCx
,
735 MOZ_ASSERT(aArg
, "Empty arg");
737 nsCOMPtr
<nsISupportsPrimitive
> argPrimitive(do_QueryInterface(aArg
));
738 if (!argPrimitive
) return NS_ERROR_NO_INTERFACE
;
741 argPrimitive
->GetType(&type
);
744 case nsISupportsPrimitive::TYPE_CSTRING
: {
745 nsCOMPtr
<nsISupportsCString
> p(do_QueryInterface(argPrimitive
));
746 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
752 JSString
* str
= ::JS_NewStringCopyN(aCx
, data
.get(), data
.Length());
753 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
755 aArgv
->setString(str
);
759 case nsISupportsPrimitive::TYPE_STRING
: {
760 nsCOMPtr
<nsISupportsString
> p(do_QueryInterface(argPrimitive
));
761 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
767 // cast is probably safe since wchar_t and char16_t are expected
768 // to be equivalent; both unsigned 16-bit entities
769 JSString
* str
= ::JS_NewUCStringCopyN(aCx
, data
.get(), data
.Length());
770 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
772 aArgv
->setString(str
);
775 case nsISupportsPrimitive::TYPE_PRBOOL
: {
776 nsCOMPtr
<nsISupportsPRBool
> p(do_QueryInterface(argPrimitive
));
777 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
783 aArgv
->setBoolean(data
);
787 case nsISupportsPrimitive::TYPE_PRUINT8
: {
788 nsCOMPtr
<nsISupportsPRUint8
> p(do_QueryInterface(argPrimitive
));
789 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
795 aArgv
->setInt32(data
);
799 case nsISupportsPrimitive::TYPE_PRUINT16
: {
800 nsCOMPtr
<nsISupportsPRUint16
> p(do_QueryInterface(argPrimitive
));
801 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
807 aArgv
->setInt32(data
);
811 case nsISupportsPrimitive::TYPE_PRUINT32
: {
812 nsCOMPtr
<nsISupportsPRUint32
> p(do_QueryInterface(argPrimitive
));
813 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
819 aArgv
->setInt32(data
);
823 case nsISupportsPrimitive::TYPE_CHAR
: {
824 nsCOMPtr
<nsISupportsChar
> p(do_QueryInterface(argPrimitive
));
825 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
831 JSString
* str
= ::JS_NewStringCopyN(aCx
, &data
, 1);
832 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
834 aArgv
->setString(str
);
838 case nsISupportsPrimitive::TYPE_PRINT16
: {
839 nsCOMPtr
<nsISupportsPRInt16
> p(do_QueryInterface(argPrimitive
));
840 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
846 aArgv
->setInt32(data
);
850 case nsISupportsPrimitive::TYPE_PRINT32
: {
851 nsCOMPtr
<nsISupportsPRInt32
> p(do_QueryInterface(argPrimitive
));
852 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
858 aArgv
->setInt32(data
);
862 case nsISupportsPrimitive::TYPE_FLOAT
: {
863 nsCOMPtr
<nsISupportsFloat
> p(do_QueryInterface(argPrimitive
));
864 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
870 *aArgv
= ::JS_NumberValue(data
);
874 case nsISupportsPrimitive::TYPE_DOUBLE
: {
875 nsCOMPtr
<nsISupportsDouble
> p(do_QueryInterface(argPrimitive
));
876 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
882 *aArgv
= ::JS_NumberValue(data
);
886 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER
: {
887 nsCOMPtr
<nsISupportsInterfacePointer
> p(do_QueryInterface(argPrimitive
));
888 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
890 nsCOMPtr
<nsISupports
> data
;
891 nsIID
* iid
= nullptr;
893 p
->GetData(getter_AddRefs(data
));
895 NS_ENSURE_TRUE(iid
, NS_ERROR_UNEXPECTED
);
897 AutoFree
iidGuard(iid
); // Free iid upon destruction.
899 JS::Rooted
<JSObject
*> scope(aCx
, GetWindowProxy());
900 JS::Rooted
<JS::Value
> v(aCx
);
901 JSAutoRealm
ar(aCx
, scope
);
902 nsresult rv
= nsContentUtils::WrapNative(aCx
, data
, iid
, &v
);
903 NS_ENSURE_SUCCESS(rv
, rv
);
909 case nsISupportsPrimitive::TYPE_ID
:
910 case nsISupportsPrimitive::TYPE_PRUINT64
:
911 case nsISupportsPrimitive::TYPE_PRINT64
:
912 case nsISupportsPrimitive::TYPE_PRTIME
: {
913 NS_WARNING("Unsupported primitive type used");
918 NS_WARNING("Unknown primitive type used");
930 inline bool IsJProfAction(struct sigaction
* action
) {
931 return (action
->sa_sigaction
&&
932 (action
->sa_flags
& (SA_RESTART
| SA_SIGINFO
)) ==
933 (SA_RESTART
| SA_SIGINFO
));
936 void NS_JProfStartProfiling();
937 void NS_JProfStopProfiling();
938 void NS_JProfClearCircular();
940 static bool JProfStartProfilingJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
941 NS_JProfStartProfiling();
945 void NS_JProfStartProfiling() {
946 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
947 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
949 struct sigaction action
;
951 // Must check ALRM before PROF since both are enabled for real-time
952 sigaction(SIGALRM
, nullptr, &action
);
953 // printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
954 if (IsJProfAction(&action
)) {
955 // printf("Beginning real-time jprof profiling.\n");
960 sigaction(SIGPROF
, nullptr, &action
);
961 // printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
962 if (IsJProfAction(&action
)) {
963 // printf("Beginning process-time jprof profiling.\n");
968 sigaction(SIGPOLL
, nullptr, &action
);
969 // printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
970 if (IsJProfAction(&action
)) {
971 // printf("Beginning rtc-based jprof profiling.\n");
976 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
979 static bool JProfStopProfilingJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
980 NS_JProfStopProfiling();
984 void NS_JProfStopProfiling() {
986 // printf("Stopped jprof profiling.\n");
989 static bool JProfClearCircularJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
990 NS_JProfClearCircular();
994 void NS_JProfClearCircular() {
996 // printf("cleared jprof buffer\n");
999 static bool JProfSaveCircularJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
1001 NS_JProfStopProfiling();
1002 NS_JProfStartProfiling();
1006 static const JSFunctionSpec JProfFunctions
[] = {
1007 JS_FN("JProfStartProfiling", JProfStartProfilingJS
, 0, 0),
1008 JS_FN("JProfStopProfiling", JProfStopProfilingJS
, 0, 0),
1009 JS_FN("JProfClearCircular", JProfClearCircularJS
, 0, 0),
1010 JS_FN("JProfSaveCircular", JProfSaveCircularJS
, 0, 0), JS_FS_END
};
1012 #endif /* defined(MOZ_JPROF) */
1014 nsresult
nsJSContext::InitClasses(JS::Handle
<JSObject
*> aGlobalObj
) {
1017 JSContext
* cx
= jsapi
.cx();
1018 JSAutoRealm
ar(cx
, aGlobalObj
);
1021 // Attempt to initialize JProf functions
1022 ::JS_DefineFunctions(cx
, aGlobalObj
, JProfFunctions
);
1028 bool nsJSContext::GetProcessingScriptTag() { return mProcessingScriptTag
; }
1030 void nsJSContext::SetProcessingScriptTag(bool aFlag
) {
1031 mProcessingScriptTag
= aFlag
;
1035 void nsJSContext::SetLowMemoryState(bool aState
) {
1036 JSContext
* cx
= danger::GetJSContext();
1037 JS::SetLowMemoryState(cx
, aState
);
1040 static void GarbageCollectImpl(JS::GCReason aReason
,
1041 nsJSContext::IsShrinking aShrinking
,
1042 const js::SliceBudget
& aBudget
) {
1043 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
1044 "nsJSContext::GarbageCollectNow", GCCC
, JS::ExplainGCReason(aReason
));
1046 bool wantIncremental
= !aBudget
.isUnlimited();
1048 // We use danger::GetJSContext() since AutoJSAPI will assert if the current
1049 // thread's context is null (such as during shutdown).
1050 JSContext
* cx
= danger::GetJSContext();
1052 if (!nsContentUtils::XPConnect() || !cx
) {
1056 if (sScheduler
->InIncrementalGC() && wantIncremental
) {
1057 // We're in the middle of incremental GC. Do another slice.
1058 JS::PrepareForIncrementalGC(cx
);
1059 JS::IncrementalGCSlice(cx
, aReason
, aBudget
);
1063 JS::GCOptions options
= aShrinking
== nsJSContext::ShrinkingGC
1064 ? JS::GCOptions::Shrink
1065 : JS::GCOptions::Normal
;
1067 if (!wantIncremental
|| aReason
== JS::GCReason::FULL_GC_TIMER
) {
1068 sScheduler
->SetNeedsFullGC();
1071 if (sScheduler
->NeedsFullGC()) {
1072 JS::PrepareForFullGC(cx
);
1075 if (wantIncremental
) {
1076 // Incremental GC slices will be triggered by the GC Runner. If one doesn't
1077 // already exist, create it in the GC_SLICE_END callback for the first
1078 // slice being executed here.
1079 JS::StartIncrementalGC(cx
, options
, aReason
, aBudget
);
1081 JS::NonIncrementalGC(cx
, options
, aReason
);
1086 void nsJSContext::GarbageCollectNow(JS::GCReason aReason
,
1087 IsShrinking aShrinking
) {
1088 GarbageCollectImpl(aReason
, aShrinking
, js::SliceBudget::unlimited());
1092 void nsJSContext::RunIncrementalGCSlice(JS::GCReason aReason
,
1093 IsShrinking aShrinking
,
1094 js::SliceBudget
& aBudget
) {
1095 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental GC", GCCC
);
1096 GarbageCollectImpl(aReason
, aShrinking
, aBudget
);
1099 static void FinishAnyIncrementalGC() {
1100 AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC
);
1102 if (sScheduler
->InIncrementalGC()) {
1106 // We're in the middle of an incremental GC, so finish it.
1107 JS::PrepareForIncrementalGC(jsapi
.cx());
1108 JS::FinishIncrementalGC(jsapi
.cx(), JS::GCReason::CC_FORCED
);
1112 namespace geckoprofiler::markers
{
1113 class CCSliceMarker
: public BaseMarkerType
<CCSliceMarker
> {
1115 static constexpr const char* Name
= "CCSlice";
1116 static constexpr const char* Description
=
1117 "Information for an individual CC slice.";
1119 using MS
= MarkerSchema
;
1120 static constexpr MS::PayloadField PayloadFields
[] = {
1121 {"idle", MS::InputType::Boolean
, "Idle", MS::Format::Integer
}};
1123 static constexpr MS::Location Locations
[] = {MS::Location::MarkerChart
,
1124 MS::Location::MarkerTable
,
1125 MS::Location::TimelineMemory
};
1126 static constexpr const char* AllLabels
=
1127 "{marker.name} (idle={marker.data.idle})";
1129 static constexpr MS::ETWMarkerGroup Group
= MS::ETWMarkerGroup::Memory
;
1131 static void StreamJSONMarkerData(
1132 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
1133 bool aIsDuringIdle
) {
1134 StreamJSONMarkerDataImpl(aWriter
, aIsDuringIdle
);
1137 } // namespace geckoprofiler::markers
1139 static void FireForgetSkippable(bool aRemoveChildless
, TimeStamp aDeadline
) {
1140 TimeStamp startTimeStamp
= TimeStamp::Now();
1141 FinishAnyIncrementalGC();
1143 uint32_t suspectedBefore
= nsCycleCollector_suspectedCount();
1144 js::SliceBudget budget
=
1145 sScheduler
->ComputeForgetSkippableBudget(startTimeStamp
, aDeadline
);
1146 bool earlyForgetSkippable
= sScheduler
->IsEarlyForgetSkippable();
1147 nsCycleCollector_forgetSkippable(budget
, aRemoveChildless
,
1148 earlyForgetSkippable
);
1149 TimeStamp now
= TimeStamp::Now();
1150 uint32_t removedPurples
= sScheduler
->NoteForgetSkippableComplete(
1151 now
, suspectedBefore
, nsCycleCollector_suspectedCount());
1153 TimeDuration duration
= now
- startTimeStamp
;
1155 sCCStats
.AfterForgetSkippable(duration
, removedPurples
);
1157 if (duration
.ToSeconds()) {
1158 TimeDuration idleDuration
;
1159 if (!aDeadline
.IsNull()) {
1160 if (aDeadline
< now
) {
1161 // This slice overflowed the idle period.
1162 if (aDeadline
> startTimeStamp
) {
1163 idleDuration
= aDeadline
- startTimeStamp
;
1166 idleDuration
= duration
;
1171 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
1172 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE
, percent
);
1175 PROFILER_MARKER("ForgetSkippable", GCCC
,
1176 MarkerTiming::IntervalUntilNowFrom(startTimeStamp
),
1177 CCSliceMarker
, !aDeadline
.IsNull());
1181 static TimeDuration
TimeBetween(TimeStamp aStart
, TimeStamp aEnd
) {
1182 MOZ_ASSERT(aEnd
>= aStart
);
1183 return aEnd
- aStart
;
1186 static TimeDuration
TimeUntilNow(TimeStamp start
) {
1187 if (start
.IsNull()) {
1188 return TimeDuration();
1190 return TimeBetween(start
, TimeStamp::Now());
1193 void CycleCollectorStats::Init() {
1196 char* env
= getenv("MOZ_CCTIMER");
1200 if (strcmp(env
, "none") == 0) {
1202 } else if (strcmp(env
, "stdout") == 0) {
1204 } else if (strcmp(env
, "stderr") == 0) {
1207 mFile
= fopen(env
, "a");
1209 MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
1214 void CycleCollectorStats::Clear() {
1215 if (mFile
&& mFile
!= stdout
&& mFile
!= stderr
) {
1218 *this = CycleCollectorStats();
1221 void CycleCollectorStats::AfterCycleCollectionSlice() {
1222 if (mBeginSliceTime
.IsNull()) {
1223 // We already called this method from EndCycleCollectionCallback for this
1228 mEndSliceTime
= TimeStamp::Now();
1229 TimeDuration duration
= mEndSliceTime
- mBeginSliceTime
;
1232 "CCSlice", GCCC
, MarkerTiming::Interval(mBeginSliceTime
, mEndSliceTime
),
1233 CCSliceMarker
, !mIdleDeadline
.IsNull() && mIdleDeadline
>= mEndSliceTime
);
1235 if (duration
.ToSeconds()) {
1236 TimeDuration idleDuration
;
1237 if (!mIdleDeadline
.IsNull()) {
1238 if (mIdleDeadline
< mEndSliceTime
) {
1239 // This slice overflowed the idle period.
1240 if (mIdleDeadline
> mBeginSliceTime
) {
1241 idleDuration
= mIdleDeadline
- mBeginSliceTime
;
1244 idleDuration
= duration
;
1249 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
1250 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE
,
1254 TimeDuration sliceTime
= TimeBetween(mBeginSliceTime
, mEndSliceTime
);
1255 mMaxSliceTime
= std::max(mMaxSliceTime
, sliceTime
);
1256 mMaxSliceTimeSinceClear
= std::max(mMaxSliceTimeSinceClear
, sliceTime
);
1257 mTotalSliceTime
+= sliceTime
;
1258 mBeginSliceTime
= TimeStamp();
1261 void CycleCollectorStats::PrepareForCycleCollection(TimeStamp aNow
) {
1263 mSuspected
= nsCycleCollector_suspectedCount();
1266 void CycleCollectorStats::AfterPrepareForCycleCollectionSlice(
1267 TimeStamp aDeadline
, TimeStamp aBeginTime
, TimeStamp aMaybeAfterGCTime
) {
1268 mBeginSliceTime
= aBeginTime
;
1269 mIdleDeadline
= aDeadline
;
1271 if (!aMaybeAfterGCTime
.IsNull()) {
1272 mAnyLockedOut
= true;
1273 mMaxGCDuration
= std::max(mMaxGCDuration
, aMaybeAfterGCTime
- aBeginTime
);
1277 void CycleCollectorStats::AfterSyncForgetSkippable(TimeStamp beginTime
) {
1278 mMaxSkippableDuration
=
1279 std::max(mMaxSkippableDuration
, TimeUntilNow(beginTime
));
1280 mRanSyncForgetSkippable
= true;
1283 void CycleCollectorStats::AfterForgetSkippable(TimeDuration duration
,
1284 uint32_t aRemovedPurples
) {
1285 if (!mMinForgetSkippableTime
|| mMinForgetSkippableTime
> duration
) {
1286 mMinForgetSkippableTime
= duration
;
1288 if (!mMaxForgetSkippableTime
|| mMaxForgetSkippableTime
< duration
) {
1289 mMaxForgetSkippableTime
= duration
;
1291 mTotalForgetSkippableTime
+= duration
;
1292 ++mForgetSkippableBeforeCC
;
1294 mRemovedPurples
+= aRemovedPurples
;
1297 void CycleCollectorStats::SendTelemetry(TimeDuration aCCNowDuration
,
1298 TimeStamp aPrevCCEnd
) const {
1299 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC
, mAnyLockedOut
);
1300 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE
,
1301 mRanSyncForgetSkippable
);
1302 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL
,
1303 aCCNowDuration
.ToMilliseconds());
1304 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE
,
1305 mMaxSliceTime
.ToMilliseconds());
1307 if (!aPrevCCEnd
.IsNull()) {
1308 TimeDuration timeBetween
= TimeBetween(aPrevCCEnd
, mBeginTime
);
1309 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN
,
1310 timeBetween
.ToSeconds());
1313 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX
,
1314 mMaxForgetSkippableTime
.ToMilliseconds());
1317 void CycleCollectorStats::MaybeLogStats(const CycleCollectorResults
& aResults
,
1318 uint32_t aCleanups
) const {
1319 if (!StaticPrefs::javascript_options_mem_log() && !sCCStats
.mFile
) {
1323 TimeDuration delta
= GetCollectionTimeDelta();
1326 if (aResults
.mMergedZones
) {
1327 mergeMsg
.AssignLiteral(" merged");
1331 if (aResults
.mForcedGC
) {
1332 gcMsg
.AssignLiteral(", forced a GC");
1335 const char16_t
* kFmt
=
1336 u
"CC(T+%.1f)[%s-%i] max pause: %.fms, total time: %.fms, slices: %lu, "
1337 u
"suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu "
1338 u
"RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
1339 u
"ForgetSkippable %lu times before CC, min: %.f ms, max: %.f ms, avg: "
1340 u
"%.f ms, total: %.f ms, max sync: %.f ms, removed: %lu";
1342 nsTextFormatter::ssprintf(
1343 msg
, kFmt
, delta
.ToMicroseconds() / PR_USEC_PER_SEC
,
1344 ProcessNameForCollectorLog(), getpid(), mMaxSliceTime
.ToMilliseconds(),
1345 mTotalSliceTime
.ToMilliseconds(), aResults
.mNumSlices
, mSuspected
,
1346 aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
, mergeMsg
.get(),
1347 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1348 sScheduler
->mCCollectedWaitingForGC
,
1349 sScheduler
->mCCollectedZonesWaitingForGC
,
1350 sScheduler
->mLikelyShortLivingObjectsNeedingGC
, gcMsg
.get(),
1351 mForgetSkippableBeforeCC
, mMinForgetSkippableTime
.ToMilliseconds(),
1352 mMaxForgetSkippableTime
.ToMilliseconds(),
1353 mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1354 mTotalForgetSkippableTime
.ToMilliseconds(),
1355 mMaxSkippableDuration
.ToMilliseconds(), mRemovedPurples
);
1356 if (StaticPrefs::javascript_options_mem_log()) {
1357 nsCOMPtr
<nsIConsoleService
> cs
=
1358 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1360 cs
->LogStringMessage(msg
.get());
1364 fprintf(mFile
, "%s\n", NS_ConvertUTF16toUTF8(msg
).get());
1368 void CycleCollectorStats::MaybeNotifyStats(
1369 const CycleCollectorResults
& aResults
, TimeDuration aCCNowDuration
,
1370 uint32_t aCleanups
) const {
1371 if (!StaticPrefs::javascript_options_mem_notify()) {
1375 const char16_t
* kJSONFmt
=
1376 u
"{ \"timestamp\": %llu, "
1377 u
"\"duration\": %.f, "
1378 u
"\"max_slice_pause\": %.f, "
1379 u
"\"total_slice_pause\": %.f, "
1380 u
"\"max_finish_gc_duration\": %.f, "
1381 u
"\"max_sync_skippable_duration\": %.f, "
1382 u
"\"suspected\": %lu, "
1385 u
"\"GCed\": %lu }, "
1386 u
"\"collected\": { "
1388 u
"\"GCed\": %lu }, "
1389 u
"\"waiting_for_gc\": %lu, "
1390 u
"\"zones_waiting_for_gc\": %lu, "
1391 u
"\"short_living_objects_waiting_for_gc\": %lu, "
1392 u
"\"forced_gc\": %d, "
1393 u
"\"forget_skippable\": { "
1394 u
"\"times_before_cc\": %lu, "
1399 u
"\"removed\": %lu } "
1403 nsTextFormatter::ssprintf(
1404 json
, kJSONFmt
, PR_Now(), aCCNowDuration
.ToMilliseconds(),
1405 mMaxSliceTime
.ToMilliseconds(), mTotalSliceTime
.ToMilliseconds(),
1406 mMaxGCDuration
.ToMilliseconds(), mMaxSkippableDuration
.ToMilliseconds(),
1407 mSuspected
, aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
,
1408 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1409 sScheduler
->mCCollectedWaitingForGC
,
1410 sScheduler
->mCCollectedZonesWaitingForGC
,
1411 sScheduler
->mLikelyShortLivingObjectsNeedingGC
, aResults
.mForcedGC
,
1412 mForgetSkippableBeforeCC
, mMinForgetSkippableTime
.ToMilliseconds(),
1413 mMaxForgetSkippableTime
.ToMilliseconds(),
1414 mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1415 mTotalForgetSkippableTime
.ToMilliseconds(), mRemovedPurples
);
1416 nsCOMPtr
<nsIObserverService
> observerService
=
1417 mozilla::services::GetObserverService();
1418 if (observerService
) {
1419 observerService
->NotifyObservers(nullptr, "cycle-collection-statistics",
1425 void nsJSContext::CycleCollectNow(CCReason aReason
,
1426 nsICycleCollectorListener
* aListener
) {
1427 if (!NS_IsMainThread()) {
1431 AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC
);
1433 PrepareForCycleCollectionSlice(aReason
, TimeStamp());
1434 nsCycleCollector_collect(aReason
, aListener
);
1435 sCCStats
.AfterCycleCollectionSlice();
1439 void nsJSContext::PrepareForCycleCollectionSlice(CCReason aReason
,
1440 TimeStamp aDeadline
) {
1441 TimeStamp beginTime
= TimeStamp::Now();
1443 // Before we begin the cycle collection, make sure there is no active GC.
1444 TimeStamp afterGCTime
;
1445 if (sScheduler
->InIncrementalGC()) {
1446 FinishAnyIncrementalGC();
1447 afterGCTime
= TimeStamp::Now();
1450 if (!sScheduler
->IsCollectingCycles()) {
1451 sCCStats
.PrepareForCycleCollection(beginTime
);
1452 sScheduler
->NoteCCBegin(aReason
, beginTime
,
1453 sCCStats
.mForgetSkippableBeforeCC
,
1454 sCCStats
.mSuspected
, sCCStats
.mRemovedPurples
);
1457 sCCStats
.AfterPrepareForCycleCollectionSlice(aDeadline
, beginTime
,
1462 void nsJSContext::RunCycleCollectorSlice(CCReason aReason
,
1463 TimeStamp aDeadline
) {
1464 if (!NS_IsMainThread()) {
1468 PrepareForCycleCollectionSlice(aReason
, aDeadline
);
1470 // Decide how long we want to budget for this slice.
1471 if (sIncrementalCC
) {
1472 bool preferShorterSlices
;
1473 js::SliceBudget budget
= sScheduler
->ComputeCCSliceBudget(
1474 aDeadline
, sCCStats
.mBeginTime
, sCCStats
.mEndSliceTime
,
1475 TimeStamp::Now(), &preferShorterSlices
);
1476 nsCycleCollector_collectSlice(budget
, aReason
, preferShorterSlices
);
1478 js::SliceBudget budget
= js::SliceBudget::unlimited();
1479 nsCycleCollector_collectSlice(budget
, aReason
, false);
1482 sCCStats
.AfterCycleCollectionSlice();
1486 void nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget
) {
1487 if (!NS_IsMainThread()) {
1491 AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", GCCC
);
1493 PrepareForCycleCollectionSlice(CCReason::API
, TimeStamp());
1495 js::SliceBudget budget
= js::SliceBudget(js::WorkBudget(aWorkBudget
));
1496 nsCycleCollector_collectSlice(budget
, CCReason::API
);
1498 sCCStats
.AfterCycleCollectionSlice();
1501 void nsJSContext::ClearMaxCCSliceTime() {
1502 sCCStats
.mMaxSliceTimeSinceClear
= TimeDuration();
1505 uint32_t nsJSContext::GetMaxCCSliceTimeSinceClear() {
1506 return sCCStats
.mMaxSliceTimeSinceClear
.ToMilliseconds();
1510 void nsJSContext::BeginCycleCollectionCallback(CCReason aReason
) {
1511 MOZ_ASSERT(NS_IsMainThread());
1513 TimeStamp startTime
= TimeStamp::Now();
1514 sCCStats
.PrepareForCycleCollection(startTime
);
1516 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1517 // is particularly useful if we recently finished a GC.
1518 if (sScheduler
->IsEarlyForgetSkippable()) {
1519 while (sScheduler
->IsEarlyForgetSkippable()) {
1520 FireForgetSkippable(false, TimeStamp());
1522 sCCStats
.AfterSyncForgetSkippable(startTime
);
1525 if (sShuttingDown
) {
1529 sScheduler
->InitCCRunnerStateMachine(
1530 mozilla::CCGCScheduler::CCRunnerState::CycleCollecting
, aReason
);
1531 sScheduler
->EnsureCCRunner(kICCIntersliceDelay
, kIdleICCSliceBudget
);
1535 void nsJSContext::EndCycleCollectionCallback(
1536 const CycleCollectorResults
& aResults
) {
1537 MOZ_ASSERT(NS_IsMainThread());
1539 sScheduler
->KillCCRunner();
1541 // Update timing information for the current slice before we log it, if
1542 // we previously called PrepareForCycleCollectionSlice(). During shutdown
1543 // CCs, this won't happen.
1544 sCCStats
.AfterCycleCollectionSlice();
1546 TimeStamp endCCTimeStamp
= TimeStamp::Now();
1547 TimeDuration ccNowDuration
= TimeBetween(sCCStats
.mBeginTime
, endCCTimeStamp
);
1548 TimeStamp prevCCEnd
= sScheduler
->GetLastCCEndTime();
1550 sScheduler
->NoteCCEnd(aResults
, endCCTimeStamp
, sCCStats
.mMaxSliceTime
);
1552 // Log information about the CC via telemetry, JSON and the console.
1554 sCCStats
.SendTelemetry(ccNowDuration
, prevCCEnd
);
1556 uint32_t cleanups
= std::max(sCCStats
.mForgetSkippableBeforeCC
, 1u);
1558 sCCStats
.MaybeLogStats(aResults
, cleanups
);
1560 sCCStats
.MaybeNotifyStats(aResults
, ccNowDuration
, cleanups
);
1562 // Update global state to indicate we have just run a cycle collection.
1565 // If we need a GC after this CC (typically because lots of GCed objects or
1566 // zones have been collected in the CC), schedule it.
1568 if (sScheduler
->NeedsGCAfterCC()) {
1570 TimeDuration::FromMilliseconds(
1571 StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration
,
1572 "A max duration ICC shouldn't reduce GC delay to 0");
1575 if (aResults
.mFreedGCed
> 10000 && aResults
.mFreedRefCounted
> 10000) {
1576 // If we collected lots of objects, trigger the next GC sooner so that
1577 // GC can cut JS-to-native edges and native objects can be then deleted.
1578 delay
= TimeDuration::FromMilliseconds(
1579 StaticPrefs::javascript_options_gc_delay_interslice());
1581 delay
= TimeDuration::FromMilliseconds(
1582 StaticPrefs::javascript_options_gc_delay()) -
1583 std::min(ccNowDuration
, kMaxICCDuration
);
1586 sScheduler
->PokeGC(JS::GCReason::CC_FINISHED
, nullptr, delay
);
1588 #if defined(MOZ_MEMORY)
1591 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1592 jemalloc_free_dirty_pages();
1598 bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline
) {
1599 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental CC", GCCC
);
1601 bool didDoWork
= false;
1603 // The CC/GC scheduler (sScheduler) decides what action(s) to take during
1604 // this invocation of the CC runner.
1606 // This may be zero, one, or multiple actions. (Zero is when CC is blocked by
1607 // incremental GC, or when the scheduler determined that a CC is no longer
1608 // needed.) Loop until the scheduler finishes this invocation by returning
1609 // `Yield` in step.mYield.
1612 step
= sScheduler
->AdvanceCCRunner(aDeadline
, TimeStamp::Now(),
1613 nsCycleCollector_suspectedCount());
1614 switch (step
.mAction
) {
1615 case CCRunnerAction::None
:
1618 case CCRunnerAction::MinorGC
:
1619 JS::MaybeRunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
1620 step
.mParam
.mReason
);
1621 sScheduler
->NoteMinorGCEnd();
1624 case CCRunnerAction::ForgetSkippable
:
1625 // 'Forget skippable' only, then end this invocation.
1626 FireForgetSkippable(bool(step
.mParam
.mRemoveChildless
), aDeadline
);
1629 case CCRunnerAction::CleanupContentUnbinder
:
1630 // Clear content unbinder before the first actual CC slice.
1631 Element::ClearContentUnbinder();
1634 case CCRunnerAction::CleanupDeferred
:
1635 // and if time still permits, perform deferred deletions.
1636 nsCycleCollector_doDeferredDeletion();
1639 case CCRunnerAction::CycleCollect
:
1640 // Cycle collection slice.
1641 nsJSContext::RunCycleCollectorSlice(step
.mParam
.mCCReason
, aDeadline
);
1644 case CCRunnerAction::StopRunning
:
1645 // End this CC, either because we have run a cycle collection slice, or
1646 // because a CC is no longer needed.
1647 sScheduler
->KillCCRunner();
1651 if (step
.mAction
!= CCRunnerAction::None
) {
1654 } while (step
.mYield
== CCRunnerYield::Continue
);
1660 bool nsJSContext::HasHadCleanupSinceLastGC() {
1661 return sScheduler
->IsEarlyForgetSkippable(1);
1665 void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason
,
1666 mozilla::TimeStamp aDeadline
) {
1667 sScheduler
->RunNextCollectorTimer(aReason
, aDeadline
);
1671 void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell
* aDocShell
,
1672 JS::GCReason aReason
) {
1673 if (!aDocShell
|| !XRE_IsContentProcess()) {
1677 BrowsingContext
* bc
= aDocShell
->GetBrowsingContext();
1682 BrowsingContext
* root
= bc
->Top();
1684 // We don't want to run collectors when loading the top level page.
1688 nsIDocShell
* rootDocShell
= root
->GetDocShell();
1689 if (!rootDocShell
) {
1693 Document
* rootDocument
= rootDocShell
->GetDocument();
1694 if (!rootDocument
||
1695 rootDocument
->GetReadyStateEnum() != Document::READYSTATE_COMPLETE
||
1696 rootDocument
->IsInBackgroundWindow()) {
1700 PresShell
* presShell
= rootDocument
->GetPresShell();
1705 nsViewManager
* vm
= presShell
->GetViewManager();
1710 if (!sScheduler
->IsUserActive() &&
1711 (sScheduler
->InIncrementalGC() || sScheduler
->IsCollectingCycles())) {
1712 Maybe
<TimeStamp
> next
= nsRefreshDriver::GetNextTickHint();
1713 if (next
.isSome()) {
1714 // Try to not delay the next RefreshDriver tick, so give a reasonable
1715 // deadline for collectors.
1716 sScheduler
->RunNextCollectorTimer(aReason
, next
.value());
1720 nsCOMPtr
<nsIDocShell
> shell
= aDocShell
;
1721 NS_DispatchToCurrentThreadQueue(
1722 NS_NewRunnableFunction("nsJSContext::MaybeRunNextCollectorSlice",
1724 nsIDocShell::BusyFlags busyFlags
=
1725 nsIDocShell::BUSY_FLAGS_NONE
;
1726 shell
->GetBusyFlags(&busyFlags
);
1727 if (busyFlags
== nsIDocShell::BUSY_FLAGS_NONE
) {
1731 // In order to improve performance on the next
1732 // page, run a minor GC. The 16ms limit ensures
1733 // it isn't called all the time if there are for
1734 // example multiple iframes loading at the same
1736 JS::RunNurseryCollection(
1737 CycleCollectedJSRuntime::Get()->Runtime(),
1738 JS::GCReason::PREPARE_FOR_PAGELOAD
,
1739 mozilla::TimeDuration::FromMilliseconds(16));
1741 EventQueuePriority::Idle
);
1745 void nsJSContext::PokeGC(JS::GCReason aReason
, JSObject
* aObj
,
1746 TimeDuration aDelay
) {
1747 sScheduler
->PokeGC(aReason
, aObj
, aDelay
);
1751 void nsJSContext::MaybePokeGC() {
1752 if (sShuttingDown
) {
1756 JSRuntime
* rt
= CycleCollectedJSRuntime::Get()->Runtime();
1757 JS::GCReason reason
= JS::WantEagerMinorGC(rt
);
1758 if (reason
!= JS::GCReason::NO_REASON
) {
1759 MOZ_ASSERT(reason
== JS::GCReason::EAGER_NURSERY_COLLECTION
);
1760 sScheduler
->PokeMinorGC(reason
);
1763 // Bug 1772638: For now, only do eager minor GCs. Eager major GCs regress some
1764 // benchmarks. Hopefully that will be worked out and this will check for
1765 // whether an eager major GC is needed.
1768 void nsJSContext::DoLowMemoryGC() {
1769 if (sShuttingDown
) {
1772 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1773 nsJSContext::ShrinkingGC
);
1774 nsJSContext::CycleCollectNow(CCReason::MEM_PRESSURE
);
1775 if (sScheduler
->NeedsGCAfterCC()) {
1776 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1777 nsJSContext::ShrinkingGC
);
1782 void nsJSContext::LowMemoryGC() {
1783 RefPtr
<CCGCScheduler::MayGCPromise
> mbPromise
=
1784 CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE
);
1786 // Normally when the promise is null it means that IPC failed, that probably
1787 // means that something bad happened, don't bother with the GC.
1791 GetMainThreadSerialEventTarget(), __func__
,
1792 [](bool aIgnored
) { DoLowMemoryGC(); },
1793 [](mozilla::ipc::ResponseRejectReason r
) {});
1797 void nsJSContext::MaybePokeCC() {
1798 sScheduler
->MaybePokeCC(TimeStamp::NowLoRes(),
1799 nsCycleCollector_suspectedCount());
1802 static void DOMGCSliceCallback(JSContext
* aCx
, JS::GCProgress aProgress
,
1803 const JS::GCDescription
& aDesc
) {
1804 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
1806 static TimeStamp sCurrentGCStartTime
;
1808 switch (aProgress
) {
1809 case JS::GC_CYCLE_BEGIN
: {
1810 // Prevent cycle collections and shrinking during incremental GC.
1811 sScheduler
->NoteGCBegin(aDesc
.reason_
);
1812 sCurrentGCStartTime
= TimeStamp::Now();
1816 case JS::GC_CYCLE_END
: {
1817 TimeDuration delta
= GetCollectionTimeDelta();
1819 if (StaticPrefs::javascript_options_mem_log()) {
1821 gcstats
.Adopt(aDesc
.formatSummaryMessage(aCx
));
1822 nsAutoString prefix
;
1823 nsTextFormatter::ssprintf(prefix
, u
"GC(T+%.1f)[%s-%i] ",
1825 ProcessNameForCollectorLog(), getpid());
1826 nsString msg
= prefix
+ gcstats
;
1827 nsCOMPtr
<nsIConsoleService
> cs
=
1828 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1830 cs
->LogStringMessage(msg
.get());
1834 sScheduler
->NoteGCEnd();
1836 // May need to kill the GC runner
1837 sScheduler
->KillGCRunner();
1839 nsJSContext::MaybePokeCC();
1841 #if defined(MOZ_MEMORY)
1842 bool freeDirty
= false;
1844 if (aDesc
.isZone_
) {
1845 sScheduler
->PokeFullGC();
1847 #if defined(MOZ_MEMORY)
1850 sScheduler
->SetNeedsFullGC(false);
1851 sScheduler
->KillFullGCTimer();
1854 if (sScheduler
->IsCCNeeded(TimeStamp::Now(),
1855 nsCycleCollector_suspectedCount()) !=
1856 CCReason::NO_REASON
) {
1857 #if defined(MOZ_MEMORY)
1858 // We're likely to free the dirty pages after CC.
1861 nsCycleCollector_dispatchDeferredDeletion();
1864 Telemetry::Accumulate(Telemetry::GC_IN_PROGRESS_MS
,
1865 TimeUntilNow(sCurrentGCStartTime
).ToMilliseconds());
1867 #if defined(MOZ_MEMORY)
1870 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1871 jemalloc_free_dirty_pages();
1877 case JS::GC_SLICE_BEGIN
:
1880 case JS::GC_SLICE_END
:
1881 sScheduler
->NoteGCSliceEnd(aDesc
.lastSliceStart(aCx
),
1882 aDesc
.lastSliceEnd(aCx
));
1884 if (sShuttingDown
) {
1885 sScheduler
->KillGCRunner();
1887 // If incremental GC wasn't triggered by GCTimerFired, we may not have a
1888 // runner to ensure all the slices are handled. So, create the runner
1890 sScheduler
->EnsureOrResetGCRunner();
1893 if (sScheduler
->IsCCNeeded(TimeStamp::Now(),
1894 nsCycleCollector_suspectedCount()) !=
1895 CCReason::NO_REASON
) {
1896 nsCycleCollector_dispatchDeferredDeletion();
1899 if (StaticPrefs::javascript_options_mem_log()) {
1901 gcstats
.Adopt(aDesc
.formatSliceMessage(aCx
));
1902 nsAutoString prefix
;
1903 nsTextFormatter::ssprintf(prefix
, u
"[%s-%i] ",
1904 ProcessNameForCollectorLog(), getpid());
1905 nsString msg
= prefix
+ gcstats
;
1906 nsCOMPtr
<nsIConsoleService
> cs
=
1907 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1909 cs
->LogStringMessage(msg
.get());
1916 MOZ_CRASH("Unexpected GCProgress value");
1919 if (sPrevGCSliceCallback
) {
1920 (*sPrevGCSliceCallback
)(aCx
, aProgress
, aDesc
);
1924 void nsJSContext::SetWindowProxy(JS::Handle
<JSObject
*> aWindowProxy
) {
1925 mWindowProxy
= aWindowProxy
;
1928 JSObject
* nsJSContext::GetWindowProxy() { return mWindowProxy
; }
1930 void nsJSContext::LikelyShortLivingObjectCreated() {
1931 ++sScheduler
->mLikelyShortLivingObjectsNeedingGC
;
1934 void mozilla::dom::StartupJSEnvironment() {
1935 // initialize all our statics, so that we can restart XPCOM
1936 sIsInitialized
= false;
1937 sShuttingDown
= false;
1941 static void SetGCParameter(JSGCParamKey aParam
, uint32_t aValue
) {
1944 JS_SetGCParameter(jsapi
.cx(), aParam
, aValue
);
1947 static void ResetGCParameter(JSGCParamKey aParam
) {
1950 JS_ResetGCParameter(jsapi
.cx(), aParam
);
1953 static void SetMemoryPrefChangedCallbackMB(const char* aPrefName
,
1955 int32_t prefMB
= Preferences::GetInt(aPrefName
, -1);
1956 // handle overflow and negative pref values
1957 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefMB
) * 1024 * 1024;
1958 if (prefB
.isValid() && prefB
.value() >= 0) {
1959 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1961 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1965 static void SetMemoryNurseryPrefChangedCallback(const char* aPrefName
,
1967 int32_t prefKB
= Preferences::GetInt(aPrefName
, -1);
1968 // handle overflow and negative pref values
1969 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefKB
) * 1024;
1970 if (prefB
.isValid() && prefB
.value() >= 0) {
1971 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1973 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1977 static void SetMemoryPrefChangedCallbackInt(const char* aPrefName
,
1979 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1980 // handle overflow and negative pref values
1981 if (pref
>= 0 && pref
< 10000) {
1982 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1984 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1988 static void SetMemoryPrefChangedCallbackBool(const char* aPrefName
,
1990 bool pref
= Preferences::GetBool(aPrefName
);
1991 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1994 static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName
,
1996 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1997 // handle overflow and negative pref values
1998 if (pref
> 0 && pref
< 100000) {
1999 sScheduler
->SetActiveIntersliceGCBudget(
2000 TimeDuration::FromMilliseconds(pref
));
2001 SetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
, pref
);
2003 ResetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
);
2007 static void SetIncrementalCCPrefChangedCallback(const char* aPrefName
,
2009 bool pref
= Preferences::GetBool(aPrefName
);
2010 sIncrementalCC
= pref
;
2013 class JSDispatchableRunnable final
: public Runnable
{
2014 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable
); }
2017 explicit JSDispatchableRunnable(JS::Dispatchable
* aDispatchable
)
2018 : mozilla::Runnable("JSDispatchableRunnable"),
2019 mDispatchable(aDispatchable
) {
2020 MOZ_ASSERT(mDispatchable
);
2024 NS_IMETHOD
Run() override
{
2025 MOZ_ASSERT(NS_IsMainThread());
2030 JS::Dispatchable::MaybeShuttingDown maybeShuttingDown
=
2031 sShuttingDown
? JS::Dispatchable::ShuttingDown
2032 : JS::Dispatchable::NotShuttingDown
;
2034 mDispatchable
->run(jsapi
.cx(), maybeShuttingDown
);
2035 mDispatchable
= nullptr; // mDispatchable may delete itself
2041 JS::Dispatchable
* mDispatchable
;
2044 static bool DispatchToEventLoop(void* closure
,
2045 JS::Dispatchable
* aDispatchable
) {
2046 MOZ_ASSERT(!closure
);
2048 // This callback may execute either on the main thread or a random JS-internal
2049 // helper thread. This callback can be called during shutdown so we cannot
2050 // simply NS_DispatchToMainThread. Failure during shutdown is expected and
2051 // properly handled by the JS engine.
2053 nsCOMPtr
<nsIEventTarget
> mainTarget
= GetMainThreadSerialEventTarget();
2058 RefPtr
<JSDispatchableRunnable
> r
= new JSDispatchableRunnable(aDispatchable
);
2059 MOZ_ALWAYS_SUCCEEDS(mainTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
2063 static bool ConsumeStream(JSContext
* aCx
, JS::Handle
<JSObject
*> aObj
,
2064 JS::MimeType aMimeType
,
2065 JS::StreamConsumer
* aConsumer
) {
2066 return FetchUtil::StreamResponseToJS(aCx
, aObj
, aMimeType
, aConsumer
,
2070 static js::SliceBudget
CreateGCSliceBudget(JS::GCReason aReason
,
2072 return sScheduler
->CreateGCSliceBudget(
2073 mozilla::TimeDuration::FromMilliseconds(aMillis
), false, false);
2076 void nsJSContext::EnsureStatics() {
2077 if (sIsInitialized
) {
2078 if (!nsContentUtils::XPConnect()) {
2084 // Let's make sure that our main thread is the same as the xpcom main thread.
2085 MOZ_ASSERT(NS_IsMainThread());
2088 new (&sSchedulerStorage
) CCGCScheduler(); // Reset the scheduler state.
2093 sPrevGCSliceCallback
= JS::SetGCSliceCallback(jsapi
.cx(), DOMGCSliceCallback
);
2095 JS::SetCreateGCSliceBudgetCallback(jsapi
.cx(), CreateGCSliceBudget
);
2097 JS::InitDispatchToEventLoop(jsapi
.cx(), DispatchToEventLoop
, nullptr);
2098 JS::InitConsumeStreamCallback(jsapi
.cx(), ConsumeStream
,
2099 FetchUtil::ReportJSStreamError
);
2101 // Set these global xpconnect options...
2102 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB
,
2103 "javascript.options.mem.max",
2104 (void*)JSGC_MAX_BYTES
);
2105 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
2106 "javascript.options.mem.nursery.min_kb",
2107 (void*)JSGC_MIN_NURSERY_BYTES
);
2108 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
2109 "javascript.options.mem.nursery.max_kb",
2110 (void*)JSGC_MAX_NURSERY_BYTES
);
2112 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
2113 "javascript.options.mem.gc_per_zone",
2114 (void*)JSGC_PER_ZONE_GC_ENABLED
);
2116 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
2117 "javascript.options.mem.gc_incremental",
2118 (void*)JSGC_INCREMENTAL_GC_ENABLED
);
2120 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
2121 "javascript.options.mem.gc_compacting",
2122 (void*)JSGC_COMPACTING_ENABLED
);
2124 #ifdef NIGHTLY_BUILD
2125 Preferences::RegisterCallbackAndCall(
2126 SetMemoryPrefChangedCallbackBool
,
2127 "javascript.options.mem.gc_experimental_semispace_nursery",
2128 (void*)JSGC_SEMISPACE_NURSERY_ENABLED
);
2131 Preferences::RegisterCallbackAndCall(
2132 SetMemoryPrefChangedCallbackBool
,
2133 "javascript.options.mem.gc_parallel_marking",
2134 (void*)JSGC_PARALLEL_MARKING_ENABLED
);
2136 Preferences::RegisterCallbackAndCall(
2137 SetMemoryPrefChangedCallbackInt
,
2138 "javascript.options.mem.gc_parallel_marking_threshold_mb",
2139 (void*)JSGC_PARALLEL_MARKING_THRESHOLD_MB
);
2141 Preferences::RegisterCallbackAndCall(
2142 SetMemoryPrefChangedCallbackInt
,
2143 "javascript.options.mem.gc_max_parallel_marking_threads",
2144 (void*)JSGC_MAX_MARKING_THREADS
);
2146 Preferences::RegisterCallbackAndCall(
2147 SetMemoryGCSliceTimePrefChangedCallback
,
2148 "javascript.options.mem.gc_incremental_slice_ms");
2150 Preferences::RegisterCallbackAndCall(
2151 SetMemoryPrefChangedCallbackBool
,
2152 "javascript.options.mem.incremental_weakmap",
2153 (void*)JSGC_INCREMENTAL_WEAKMAP_ENABLED
);
2155 Preferences::RegisterCallbackAndCall(
2156 SetMemoryPrefChangedCallbackInt
,
2157 "javascript.options.mem.gc_high_frequency_time_limit_ms",
2158 (void*)JSGC_HIGH_FREQUENCY_TIME_LIMIT
);
2160 Preferences::RegisterCallbackAndCall(
2161 SetMemoryPrefChangedCallbackInt
,
2162 "javascript.options.mem.gc_low_frequency_heap_growth",
2163 (void*)JSGC_LOW_FREQUENCY_HEAP_GROWTH
);
2165 Preferences::RegisterCallbackAndCall(
2166 SetMemoryPrefChangedCallbackInt
,
2167 "javascript.options.mem.gc_high_frequency_large_heap_growth",
2168 (void*)JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH
);
2170 Preferences::RegisterCallbackAndCall(
2171 SetMemoryPrefChangedCallbackInt
,
2172 "javascript.options.mem.gc_high_frequency_small_heap_growth",
2173 (void*)JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH
);
2175 Preferences::RegisterCallbackAndCall(
2176 SetMemoryPrefChangedCallbackBool
,
2177 "javascript.options.mem.gc_balanced_heap_limits",
2178 (void*)JSGC_BALANCED_HEAP_LIMITS_ENABLED
);
2180 Preferences::RegisterCallbackAndCall(
2181 SetMemoryPrefChangedCallbackInt
,
2182 "javascript.options.mem.gc_heap_growth_factor",
2183 (void*)JSGC_HEAP_GROWTH_FACTOR
);
2185 Preferences::RegisterCallbackAndCall(
2186 SetMemoryPrefChangedCallbackInt
,
2187 "javascript.options.mem.gc_small_heap_size_max_mb",
2188 (void*)JSGC_SMALL_HEAP_SIZE_MAX
);
2190 Preferences::RegisterCallbackAndCall(
2191 SetMemoryPrefChangedCallbackInt
,
2192 "javascript.options.mem.gc_large_heap_size_min_mb",
2193 (void*)JSGC_LARGE_HEAP_SIZE_MIN
);
2195 Preferences::RegisterCallbackAndCall(
2196 SetMemoryPrefChangedCallbackInt
,
2197 "javascript.options.mem.gc_allocation_threshold_mb",
2198 (void*)JSGC_ALLOCATION_THRESHOLD
);
2200 Preferences::RegisterCallbackAndCall(
2201 SetMemoryPrefChangedCallbackInt
,
2202 "javascript.options.mem.gc_malloc_threshold_base_mb",
2203 (void*)JSGC_MALLOC_THRESHOLD_BASE
);
2205 Preferences::RegisterCallbackAndCall(
2206 SetMemoryPrefChangedCallbackInt
,
2207 "javascript.options.mem.gc_small_heap_incremental_limit",
2208 (void*)JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
);
2209 Preferences::RegisterCallbackAndCall(
2210 SetMemoryPrefChangedCallbackInt
,
2211 "javascript.options.mem.gc_large_heap_incremental_limit",
2212 (void*)JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
);
2214 Preferences::RegisterCallbackAndCall(
2215 SetMemoryPrefChangedCallbackInt
,
2216 "javascript.options.mem.gc_urgent_threshold_mb",
2217 (void*)JSGC_URGENT_THRESHOLD_MB
);
2219 Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback
,
2220 "dom.cycle_collector.incremental");
2222 Preferences::RegisterCallbackAndCall(
2223 SetMemoryPrefChangedCallbackInt
,
2224 "javascript.options.mem.gc_min_empty_chunk_count",
2225 (void*)JSGC_MIN_EMPTY_CHUNK_COUNT
);
2227 Preferences::RegisterCallbackAndCall(
2228 SetMemoryPrefChangedCallbackInt
,
2229 "javascript.options.mem.gc_max_empty_chunk_count",
2230 (void*)JSGC_MAX_EMPTY_CHUNK_COUNT
);
2232 Preferences::RegisterCallbackAndCall(
2233 SetMemoryPrefChangedCallbackInt
,
2234 "javascript.options.mem.gc_helper_thread_ratio",
2235 (void*)JSGC_HELPER_THREAD_RATIO
);
2237 Preferences::RegisterCallbackAndCall(
2238 SetMemoryPrefChangedCallbackInt
,
2239 "javascript.options.mem.gc_max_helper_threads",
2240 (void*)JSGC_MAX_HELPER_THREADS
);
2242 Preferences::RegisterCallbackAndCall(
2243 SetMemoryPrefChangedCallbackInt
,
2244 "javascript.options.mem.nursery_eager_collection_threshold_kb",
2245 (void*)JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_KB
);
2247 Preferences::RegisterCallbackAndCall(
2248 SetMemoryPrefChangedCallbackInt
,
2249 "javascript.options.mem.nursery_eager_collection_threshold_percent",
2250 (void*)JSGC_NURSERY_EAGER_COLLECTION_THRESHOLD_PERCENT
);
2252 Preferences::RegisterCallbackAndCall(
2253 SetMemoryPrefChangedCallbackInt
,
2254 "javascript.options.mem.nursery_eager_collection_timeout_ms",
2255 (void*)JSGC_NURSERY_EAGER_COLLECTION_TIMEOUT_MS
);
2257 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
2262 nsIObserver
* observer
= new nsJSEnvironmentObserver();
2263 obs
->AddObserver(observer
, "memory-pressure", false);
2264 obs
->AddObserver(observer
, "user-interaction-inactive", false);
2265 obs
->AddObserver(observer
, "user-interaction-active", false);
2266 obs
->AddObserver(observer
, "quit-application", false);
2267 obs
->AddObserver(observer
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
2268 obs
->AddObserver(observer
, "content-child-will-shutdown", false);
2270 sIsInitialized
= true;
2273 void mozilla::dom::ShutdownJSEnvironment() {
2274 sShuttingDown
= true;
2275 sScheduler
->Shutdown();
2278 AsyncErrorReporter::AsyncErrorReporter(xpc::ErrorReport
* aReport
)
2279 : Runnable("dom::AsyncErrorReporter"), mReport(aReport
) {}
2281 void AsyncErrorReporter::SerializeStack(JSContext
* aCx
,
2282 JS::Handle
<JSObject
*> aStack
) {
2283 mStackHolder
= MakeUnique
<SerializedStackHolder
>();
2284 mStackHolder
->SerializeMainThreadOrWorkletStack(aCx
, aStack
);
2287 void AsyncErrorReporter::SetException(JSContext
* aCx
,
2288 JS::Handle
<JS::Value
> aException
) {
2289 MOZ_ASSERT(NS_IsMainThread());
2290 mException
.init(aCx
, aException
);
2291 mHasException
= true;
2294 NS_IMETHODIMP
AsyncErrorReporter::Run() {
2296 // We're only using this context to deserialize a stack to report to the
2297 // console, so the scope we use doesn't matter. Stack frame filtering happens
2298 // based on the principal encoded into the frame and the caller compartment,
2299 // not the compartment of the frame object, and the console reporting code
2300 // will not be using our context, and therefore will not care what compartment
2302 DebugOnly
<bool> ok
= jsapi
.Init(xpc::PrivilegedJunkScope());
2303 MOZ_ASSERT(ok
, "Problem with system global?");
2304 JSContext
* cx
= jsapi
.cx();
2305 JS::Rooted
<JSObject
*> stack(cx
);
2306 JS::Rooted
<JSObject
*> stackGlobal(cx
);
2308 stack
= mStackHolder
->ReadStack(cx
);
2310 stackGlobal
= JS::CurrentGlobalOrNull(cx
);
2314 JS::Rooted
<Maybe
<JS::Value
>> exception(cx
, Nothing());
2315 if (mHasException
) {
2316 MOZ_ASSERT(NS_IsMainThread());
2317 exception
= Some(mException
);
2318 // Remove our reference to the exception.
2319 mException
.setUndefined();
2320 mHasException
= false;
2323 mReport
->LogToConsoleWithStack(nullptr, exception
, stack
, stackGlobal
);
2327 // A fast-array class for JS. This class supports both nsIJSScriptArray and
2328 // nsIArray. If it is JS itself providing and consuming this class, all work
2329 // can be done via nsIJSScriptArray, and avoid the conversion of elements
2330 // to/from nsISupports.
2331 // When consumed by non-JS (eg, another script language), conversion is done
2333 class nsJSArgArray final
: public nsIJSArgArray
{
2335 nsJSArgArray(JSContext
* aContext
, uint32_t argc
, const JS::Value
* argv
,
2339 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2340 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray
,
2347 nsresult
GetArgs(uint32_t* argc
, void** argv
) override
;
2349 void ReleaseJSObjects();
2353 JSContext
* mContext
;
2354 JS::Heap
<JS::Value
>* mArgv
;
2358 nsJSArgArray::nsJSArgArray(JSContext
* aContext
, uint32_t argc
,
2359 const JS::Value
* argv
, nsresult
* prv
)
2360 : mContext(aContext
), mArgv(nullptr), mArgc(argc
) {
2361 // copy the array - we don't know its lifetime, and ours is tied to xpcom
2364 mArgv
= new (fallible
) JS::Heap
<JS::Value
>[argc
];
2366 *prv
= NS_ERROR_OUT_OF_MEMORY
;
2371 // Callers are allowed to pass in a null argv even for argc > 0. They can
2372 // then use GetArgs to initialize the values.
2374 for (uint32_t i
= 0; i
< argc
; ++i
) mArgv
[i
] = argv
[i
];
2378 mozilla::HoldJSObjects(this);
2384 nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); }
2386 void nsJSArgArray::ReleaseJSObjects() {
2391 mozilla::DropJSObjects(this);
2395 // QueryInterface implementation for nsJSArgArray
2396 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray
)
2398 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray
)
2399 tmp
->ReleaseJSObjects();
2400 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray
)
2402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2404 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray
)
2406 for (uint32_t i
= 0; i
< tmp
->mArgc
; ++i
) {
2407 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv
[i
])
2410 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2412 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray
)
2413 NS_INTERFACE_MAP_ENTRY(nsIArray
)
2414 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray
)
2415 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIJSArgArray
)
2416 NS_INTERFACE_MAP_END
2418 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray
)
2419 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray
)
2421 nsresult
nsJSArgArray::GetArgs(uint32_t* argc
, void** argv
) {
2422 *argv
= (void*)mArgv
;
2428 NS_IMETHODIMP
nsJSArgArray::GetLength(uint32_t* aLength
) {
2433 NS_IMETHODIMP
nsJSArgArray::QueryElementAt(uint32_t index
, const nsIID
& uuid
,
2436 if (index
>= mArgc
) return NS_ERROR_INVALID_ARG
;
2438 if (uuid
.Equals(NS_GET_IID(nsIVariant
)) ||
2439 uuid
.Equals(NS_GET_IID(nsISupports
))) {
2440 // Have to copy a Heap into a Rooted to work with it.
2441 JS::Rooted
<JS::Value
> val(mContext
, mArgv
[index
]);
2442 return nsContentUtils::XPConnect()->JSToVariant(mContext
, val
,
2443 (nsIVariant
**)result
);
2445 NS_WARNING("nsJSArgArray only handles nsIVariant");
2446 return NS_ERROR_NO_INTERFACE
;
2449 NS_IMETHODIMP
nsJSArgArray::IndexOf(uint32_t startIndex
, nsISupports
* element
,
2450 uint32_t* _retval
) {
2451 return NS_ERROR_NOT_IMPLEMENTED
;
2454 NS_IMETHODIMP
nsJSArgArray::ScriptedEnumerate(const nsIID
& aElemIID
,
2456 nsISimpleEnumerator
** aResult
) {
2457 return NS_ERROR_NOT_IMPLEMENTED
;
2460 NS_IMETHODIMP
nsJSArgArray::EnumerateImpl(const nsID
& aEntryIID
,
2461 nsISimpleEnumerator
** _retval
) {
2462 return NS_ERROR_NOT_IMPLEMENTED
;
2465 // The factory function
2466 nsresult
NS_CreateJSArgv(JSContext
* aContext
, uint32_t argc
,
2467 const JS::Value
* argv
, nsIJSArgArray
** aArray
) {
2469 nsCOMPtr
<nsIJSArgArray
> ret
= new nsJSArgArray(aContext
, argc
, argv
, &rv
);
2470 if (NS_FAILED(rv
)) {