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 "nsGlobalWindow.h"
54 #include "mozilla/AutoRestore.h"
55 #include "mozilla/BasePrincipal.h"
56 #include "mozilla/PresShell.h"
57 #include "mozilla/SchedulerGroup.h"
58 #include "mozilla/StaticPrefs_javascript.h"
59 #include "mozilla/StaticPtr.h"
60 #include "mozilla/dom/BrowsingContext.h"
61 #include "mozilla/dom/DOMException.h"
62 #include "mozilla/dom/DOMExceptionBinding.h"
63 #include "mozilla/dom/Element.h"
64 #include "mozilla/dom/ErrorEvent.h"
65 #include "mozilla/dom/FetchUtil.h"
66 #include "mozilla/dom/ScriptSettings.h"
67 #include "mozilla/dom/SerializedStackHolder.h"
68 #include "mozilla/CycleCollectedJSRuntime.h"
69 #include "nsRefreshDriver.h"
70 #include "nsJSPrincipals.h"
71 #include "AccessCheck.h"
72 #include "mozilla/Logging.h"
75 #include "mozilla/Preferences.h"
76 #include "mozilla/Telemetry.h"
77 #include "mozilla/dom/BindingUtils.h"
78 #include "mozilla/Attributes.h"
79 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
80 #include "mozilla/ContentEvents.h"
81 #include "mozilla/CycleCollectedJSContext.h"
82 #include "nsCycleCollectionNoteRootCallback.h"
83 #include "mozilla/IdleTaskRunner.h"
84 #include "nsViewManager.h"
85 #include "mozilla/EventStateManager.h"
86 #include "mozilla/ProfilerLabels.h"
87 #include "mozilla/ProfilerMarkers.h"
89 using namespace mozilla
;
90 using namespace mozilla::dom
;
92 // Thank you Microsoft!
97 static JS::GCSliceCallback sPrevGCSliceCallback
;
99 static bool sIncrementalCC
= false;
101 static bool sIsInitialized
;
102 static bool sShuttingDown
;
104 static CCGCScheduler sScheduler
;
106 struct CycleCollectorStats
{
107 constexpr CycleCollectorStats() = default;
110 void AfterPrepareForCycleCollectionSlice(TimeStamp aDeadline
,
111 TimeStamp aBeginTime
,
112 TimeStamp aMaybeAfterGCTime
);
113 void AfterCycleCollectionSlice();
114 void AfterSyncForgetSkippable(TimeStamp beginTime
);
115 void AfterForgetSkippable(TimeDuration duration
, uint32_t aRemovedPurples
);
116 void AfterCycleCollection();
118 void SendTelemetry(TimeDuration aCCNowDuration
) const;
119 void MaybeLogStats(const CycleCollectorResults
& aResults
,
120 uint32_t aCleanups
) const;
121 void MaybeNotifyStats(const CycleCollectorResults
& aResults
,
122 TimeDuration aCCNowDuration
, uint32_t aCleanups
) const;
124 // Time the current slice began, including any GC finishing.
125 TimeStamp mBeginSliceTime
;
127 // Time the previous slice of the current CC ended.
128 TimeStamp mEndSliceTime
;
130 // Time the current cycle collection began.
131 TimeStamp mBeginTime
;
133 // The longest GC finishing duration for any slice of the current CC.
134 TimeDuration mMaxGCDuration
;
136 // True if we ran sync forget skippable in any slice of the current CC.
137 bool mRanSyncForgetSkippable
= false;
139 // Number of suspected objects at the start of the current CC.
140 uint32_t mSuspected
= 0;
142 // The longest duration spent on sync forget skippable in any slice of the
144 TimeDuration mMaxSkippableDuration
;
146 // The longest pause of any slice in the current CC.
147 TimeDuration mMaxSliceTime
;
149 // The longest slice time since ClearMaxCCSliceTime() was called.
150 TimeDuration mMaxSliceTimeSinceClear
;
152 // The total amount of time spent actually running the current CC.
153 TimeDuration mTotalSliceTime
;
155 // True if we were locked out by the GC in any slice of the current CC.
156 bool mAnyLockedOut
= false;
158 // A file to dump CC activity to; set by MOZ_CCTIMER environment variable.
159 FILE* mFile
= nullptr;
161 // In case CC slice was triggered during idle time, set to the end of the idle
163 TimeStamp mIdleDeadline
;
165 TimeDuration mMinForgetSkippableTime
;
166 TimeDuration mMaxForgetSkippableTime
;
167 TimeDuration mTotalForgetSkippableTime
;
168 uint32_t mForgetSkippableBeforeCC
= 0;
170 uint32_t mRemovedPurples
= 0;
173 static CycleCollectorStats sCCStats
;
175 static const char* ProcessNameForCollectorLog() {
176 return XRE_GetProcessType() == GeckoProcessType_Default
? "default"
182 // This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
183 // Exceptions, and arbitrary values that were associated with a stack by the
184 // JS engine when they were thrown, as specified by exceptionStack.
186 // Note that the returned stackObj and stackGlobal are _not_ wrapped into the
187 // compartment of exceptionValue.
188 void FindExceptionStackForConsoleReport(nsPIDOMWindowInner
* win
,
189 JS::HandleValue exceptionValue
,
190 JS::HandleObject exceptionStack
,
191 JS::MutableHandleObject stackObj
,
192 JS::MutableHandleObject stackGlobal
) {
193 stackObj
.set(nullptr);
194 stackGlobal
.set(nullptr);
196 if (!exceptionValue
.isObject()) {
197 // Use the stack provided by the JS engine, if available. This will not be
199 if (exceptionStack
) {
200 stackObj
.set(exceptionStack
);
201 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
206 if (win
&& win
->AsGlobal()->IsDying()) {
207 // Pretend like we have no stack, so we don't end up keeping the global
208 // alive via the stack.
212 JS::RootingContext
* rcx
= RootingCx();
213 JS::RootedObject
exceptionObject(rcx
, &exceptionValue
.toObject());
214 if (JSObject
* excStack
= JS::ExceptionStackOrNull(exceptionObject
)) {
215 // At this point we know exceptionObject is a possibly-wrapped
216 // js::ErrorObject that has excStack as stack. excStack might also be a CCW,
217 // but excStack must be same-compartment with the unwrapped ErrorObject.
218 // Return the ErrorObject's global as stackGlobal. This matches what we do
219 // in the ErrorObject's |.stack| getter and ensures stackObj and stackGlobal
220 // are same-compartment.
221 JSObject
* unwrappedException
= js::UncheckedUnwrap(exceptionObject
);
222 stackObj
.set(excStack
);
223 stackGlobal
.set(JS::GetNonCCWObjectGlobal(unwrappedException
));
227 // It is not a JS Exception, try DOM Exception.
228 RefPtr
<Exception
> exception
;
229 UNWRAP_OBJECT(DOMException
, exceptionObject
, exception
);
231 // Not a DOM Exception, try XPC Exception.
232 UNWRAP_OBJECT(Exception
, exceptionObject
, exception
);
234 // As above, use the stack provided by the JS engine, if available.
235 if (exceptionStack
) {
236 stackObj
.set(exceptionStack
);
237 stackGlobal
.set(JS::GetNonCCWObjectGlobal(exceptionStack
));
243 nsCOMPtr
<nsIStackFrame
> stack
= exception
->GetLocation();
247 JS::RootedValue
value(rcx
);
248 stack
->GetNativeSavedFrame(&value
);
249 if (value
.isObject()) {
250 stackObj
.set(&value
.toObject());
251 MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj
));
252 stackGlobal
.set(JS::GetNonCCWObjectGlobal(stackObj
));
257 } /* namespace xpc */
259 static TimeDuration
GetCollectionTimeDelta() {
260 static TimeStamp sFirstCollectionTime
;
261 TimeStamp now
= TimeStamp::Now();
262 if (sFirstCollectionTime
) {
263 return now
- sFirstCollectionTime
;
265 sFirstCollectionTime
= now
;
266 return TimeDuration();
269 class nsJSEnvironmentObserver final
: public nsIObserver
{
270 ~nsJSEnvironmentObserver() = default;
277 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver
, nsIObserver
)
280 nsJSEnvironmentObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
281 const char16_t
* aData
) {
282 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) {
283 if (StaticPrefs::javascript_options_gc_on_memory_pressure()) {
285 // Don't GC/CC if we're already shutting down.
288 nsDependentString
data(aData
);
289 if (data
.EqualsLiteral("low-memory-ongoing")) {
290 // Don't GC/CC if we are in an ongoing low-memory state since its very
291 // slow and it likely won't help us anyway.
294 if (data
.EqualsLiteral("heap-minimize")) {
295 // heap-minimize notifiers expect this to run synchronously
296 nsJSContext::DoLowMemoryGC();
299 if (data
.EqualsLiteral("low-memory")) {
300 nsJSContext::SetLowMemoryState(true);
302 // Asynchronously GC.
303 nsJSContext::LowMemoryGC();
305 } else if (!nsCRT::strcmp(aTopic
, "memory-pressure-stop")) {
306 nsJSContext::SetLowMemoryState(false);
307 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-inactive")) {
308 sScheduler
.UserIsInactive();
309 } else if (!nsCRT::strcmp(aTopic
, "user-interaction-active")) {
310 sScheduler
.UserIsActive();
311 } else if (!nsCRT::strcmp(aTopic
, "quit-application") ||
312 !nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) ||
313 !nsCRT::strcmp(aTopic
, "content-child-will-shutdown")) {
314 sShuttingDown
= true;
315 sScheduler
.Shutdown();
321 /****************************************************************
322 ************************** AutoFree ****************************
323 ****************************************************************/
327 explicit AutoFree(void* aPtr
) : mPtr(aPtr
) {}
329 if (mPtr
) free(mPtr
);
331 void Invalidate() { mPtr
= nullptr; }
337 // A utility function for script languages to call. Although it looks small,
338 // the use of nsIDocShell and nsPresContext triggers a huge number of
339 // dependencies that most languages would not otherwise need.
340 // XXXmarkh - This function is mis-placed!
341 bool NS_HandleScriptError(nsIScriptGlobalObject
* aScriptGlobal
,
342 const ErrorEventInit
& aErrorEventInit
,
343 nsEventStatus
* aStatus
) {
345 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryInterface(aScriptGlobal
));
346 nsIDocShell
* docShell
= win
? win
->GetDocShell() : nullptr;
348 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
350 static int32_t errorDepth
; // Recursion prevention
353 if (errorDepth
< 2) {
354 // Dispatch() must be synchronous for the recursion block
355 // (errorDepth) to work.
356 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
357 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, aErrorEventInit
);
358 event
->SetTrusted(true);
360 EventDispatcher::DispatchDOMEvent(win
, nullptr, event
, presContext
,
369 class ScriptErrorEvent
: public Runnable
{
371 ScriptErrorEvent(nsPIDOMWindowInner
* aWindow
, JS::RootingContext
* aRootingCx
,
372 xpc::ErrorReport
* aReport
, JS::Handle
<JS::Value
> aError
,
373 JS::Handle
<JSObject
*> aErrorStack
)
374 : mozilla::Runnable("ScriptErrorEvent"),
377 mError(aRootingCx
, aError
),
378 mErrorStack(aRootingCx
, aErrorStack
) {}
380 NS_IMETHOD
Run() override
{
381 nsEventStatus status
= nsEventStatus_eIgnore
;
382 nsPIDOMWindowInner
* win
= mWindow
;
384 MOZ_ASSERT(NS_IsMainThread());
385 // First, notify the DOM that we have a script error, but only if
386 // our window is still the current inner.
387 JS::RootingContext
* rootingCx
= RootingCx();
388 if (win
->IsCurrentInnerWindow() && win
->GetDocShell() &&
389 !sHandlingScriptError
) {
390 AutoRestore
<bool> recursionGuard(sHandlingScriptError
);
391 sHandlingScriptError
= true;
393 RefPtr
<nsPresContext
> presContext
= win
->GetDocShell()->GetPresContext();
395 RootedDictionary
<ErrorEventInit
> init(rootingCx
);
396 init
.mCancelable
= true;
397 init
.mFilename
= mReport
->mFileName
;
398 init
.mBubbles
= true;
400 constexpr auto xoriginMsg
= u
"Script error."_ns
;
401 if (!mReport
->mIsMuted
) {
402 init
.mMessage
= mReport
->mErrorMsg
;
403 init
.mLineno
= mReport
->mLineNumber
;
404 init
.mColno
= mReport
->mColumn
;
405 init
.mError
= mError
;
407 NS_WARNING("Not same origin error!");
408 init
.mMessage
= xoriginMsg
;
412 RefPtr
<ErrorEvent
> event
= ErrorEvent::Constructor(
413 nsGlobalWindowInner::Cast(win
), u
"error"_ns
, init
);
414 event
->SetTrusted(true);
416 EventDispatcher::DispatchDOMEvent(win
, nullptr, event
, presContext
,
420 if (status
!= nsEventStatus_eConsumeNoDefault
) {
421 JS::Rooted
<JSObject
*> stack(rootingCx
);
422 JS::Rooted
<JSObject
*> stackGlobal(rootingCx
);
423 xpc::FindExceptionStackForConsoleReport(win
, mError
, mErrorStack
, &stack
,
425 JS::Rooted
<Maybe
<JS::Value
>> exception(rootingCx
, Some(mError
));
426 nsGlobalWindowInner
* inner
= nsGlobalWindowInner::Cast(win
);
427 mReport
->LogToConsoleWithStack(inner
, exception
, stack
, stackGlobal
);
434 nsCOMPtr
<nsPIDOMWindowInner
> mWindow
;
435 RefPtr
<xpc::ErrorReport
> mReport
;
436 JS::PersistentRootedValue mError
;
437 JS::PersistentRootedObject mErrorStack
;
439 static bool sHandlingScriptError
;
442 bool ScriptErrorEvent::sHandlingScriptError
= false;
444 // This temporarily lives here to avoid code churn. It will go away entirely
448 void DispatchScriptErrorEvent(nsPIDOMWindowInner
* win
,
449 JS::RootingContext
* rootingCx
,
450 xpc::ErrorReport
* xpcReport
,
451 JS::Handle
<JS::Value
> exception
,
452 JS::Handle
<JSObject
*> exceptionStack
) {
453 nsContentUtils::AddScriptRunner(new ScriptErrorEvent(
454 win
, rootingCx
, xpcReport
, exception
, exceptionStack
));
457 } /* namespace xpc */
460 // A couple of useful functions to call when you're debugging.
461 nsGlobalWindowInner
* JSObject2Win(JSObject
* obj
) {
462 return xpc::WindowOrNull(obj
);
465 template <typename T
>
466 void PrintWinURI(T
* win
) {
468 printf("No window passed in.\n");
472 nsCOMPtr
<Document
> doc
= win
->GetExtantDoc();
474 printf("No document in the window.\n");
478 nsIURI
* uri
= doc
->GetDocumentURI();
480 printf("Document doesn't have a URI.\n");
484 printf("%s\n", uri
->GetSpecOrDefault().get());
487 void PrintWinURIInner(nsGlobalWindowInner
* aWin
) { return PrintWinURI(aWin
); }
489 void PrintWinURIOuter(nsGlobalWindowOuter
* aWin
) { return PrintWinURI(aWin
); }
491 template <typename T
>
492 void PrintWinCodebase(T
* win
) {
494 printf("No window passed in.\n");
498 nsIPrincipal
* prin
= win
->GetPrincipal();
500 printf("Window doesn't have principals.\n");
503 if (prin
->IsSystemPrincipal()) {
504 printf("No URI, it's the system principal.\n");
508 prin
->GetAsciiSpec(spec
);
509 printf("%s\n", spec
.get());
512 void PrintWinCodebaseInner(nsGlobalWindowInner
* aWin
) {
513 return PrintWinCodebase(aWin
);
516 void PrintWinCodebaseOuter(nsGlobalWindowOuter
* aWin
) {
517 return PrintWinCodebase(aWin
);
520 void DumpString(const nsAString
& str
) {
521 printf("%s\n", NS_ConvertUTF16toUTF8(str
).get());
525 nsJSContext::nsJSContext(bool aGCOnDestruction
,
526 nsIScriptGlobalObject
* aGlobalObject
)
527 : mWindowProxy(nullptr),
528 mGCOnDestruction(aGCOnDestruction
),
529 mGlobalObjectRef(aGlobalObject
) {
532 mProcessingScriptTag
= false;
536 nsJSContext::~nsJSContext() {
537 mGlobalObjectRef
= nullptr;
542 void nsJSContext::Destroy() {
543 if (mGCOnDestruction
) {
544 sScheduler
.PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY
, mWindowProxy
);
550 // QueryInterface implementation for nsJSContext
551 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext
)
553 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext
)
554 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy
)
555 NS_IMPL_CYCLE_COLLECTION_TRACE_END
557 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext
)
558 tmp
->mGCOnDestruction
= false;
559 tmp
->mWindowProxy
= nullptr;
561 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef
)
562 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
563 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext
)
564 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef
)
565 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
567 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext
)
568 NS_INTERFACE_MAP_ENTRY(nsIScriptContext
)
569 NS_INTERFACE_MAP_ENTRY(nsISupports
)
572 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext
)
573 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext
)
576 bool AtomIsEventHandlerName(nsAtom
* aName
) {
577 const char16_t
* name
= aName
->GetUTF16String();
581 for (cp
= name
; *cp
!= '\0'; ++cp
) {
583 if ((c
< 'A' || c
> 'Z') && (c
< 'a' || c
> 'z')) return false;
590 nsIScriptGlobalObject
* nsJSContext::GetGlobalObject() {
591 // Note: this could probably be simplified somewhat more; see bug 974327
597 MOZ_ASSERT(mGlobalObjectRef
);
598 return mGlobalObjectRef
;
601 nsresult
nsJSContext::SetProperty(JS::Handle
<JSObject
*> aTarget
,
602 const char* aPropName
, nsISupports
* aArgs
) {
604 if (NS_WARN_IF(!jsapi
.Init(GetGlobalObject()))) {
605 return NS_ERROR_FAILURE
;
607 JSContext
* cx
= jsapi
.cx();
609 JS::RootedVector
<JS::Value
> args(cx
);
611 JS::Rooted
<JSObject
*> global(cx
, GetWindowProxy());
612 nsresult rv
= ConvertSupportsTojsvals(cx
, aArgs
, global
, &args
);
613 NS_ENSURE_SUCCESS(rv
, rv
);
615 // got the arguments, now attach them.
617 for (uint32_t i
= 0; i
< args
.length(); ++i
) {
618 if (!JS_WrapValue(cx
, args
[i
])) {
619 return NS_ERROR_FAILURE
;
623 JS::Rooted
<JSObject
*> array(cx
, JS::NewArrayObject(cx
, args
));
625 return NS_ERROR_FAILURE
;
628 return JS_DefineProperty(cx
, aTarget
, aPropName
, array
, 0) ? NS_OK
632 nsresult
nsJSContext::ConvertSupportsTojsvals(
633 JSContext
* aCx
, nsISupports
* aArgs
, JS::Handle
<JSObject
*> aScope
,
634 JS::MutableHandleVector
<JS::Value
> aArgsOut
) {
637 // If the array implements nsIJSArgArray, copy the contents and return.
638 nsCOMPtr
<nsIJSArgArray
> fastArray
= do_QueryInterface(aArgs
);
642 rv
= fastArray
->GetArgs(&argc
, reinterpret_cast<void**>(&argv
));
643 if (NS_SUCCEEDED(rv
) && !aArgsOut
.append(argv
, argc
)) {
644 rv
= NS_ERROR_OUT_OF_MEMORY
;
649 // Take the slower path converting each item.
650 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
651 // SetProperty('arguments', ...);
653 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
654 NS_ENSURE_TRUE(xpc
, NS_ERROR_UNEXPECTED
);
656 if (!aArgs
) return NS_OK
;
658 // This general purpose function may need to convert an arg array
659 // (window.arguments, event-handler args) and a generic property.
660 nsCOMPtr
<nsIArray
> argsArray(do_QueryInterface(aArgs
));
663 rv
= argsArray
->GetLength(&argCount
);
664 NS_ENSURE_SUCCESS(rv
, rv
);
665 if (argCount
== 0) return NS_OK
;
667 argCount
= 1; // the nsISupports which is not an array
670 // Use the caller's auto guards to release and unroot.
671 if (!aArgsOut
.resize(argCount
)) {
672 return NS_ERROR_OUT_OF_MEMORY
;
676 for (uint32_t argCtr
= 0; argCtr
< argCount
&& NS_SUCCEEDED(rv
); argCtr
++) {
677 nsCOMPtr
<nsISupports
> arg
;
678 JS::MutableHandle
<JS::Value
> thisVal
= aArgsOut
[argCtr
];
679 argsArray
->QueryElementAt(argCtr
, NS_GET_IID(nsISupports
),
680 getter_AddRefs(arg
));
685 nsCOMPtr
<nsIVariant
> variant(do_QueryInterface(arg
));
686 if (variant
!= nullptr) {
687 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, thisVal
);
689 // And finally, support the nsISupportsPrimitives supplied
690 // by the AppShell. It generally will pass only strings, but
691 // as we have code for handling all, we may as well use it.
692 rv
= AddSupportsPrimitiveTojsvals(aCx
, arg
, thisVal
.address());
693 if (rv
== NS_ERROR_NO_INTERFACE
) {
694 // something else - probably an event object or similar -
697 // but first, check its not another nsISupportsPrimitive, as
698 // these are now deprecated for use with script contexts.
699 nsCOMPtr
<nsISupportsPrimitive
> prim(do_QueryInterface(arg
));
700 NS_ASSERTION(prim
== nullptr,
701 "Don't pass nsISupportsPrimitives - use nsIVariant!");
703 JSAutoRealm
ar(aCx
, aScope
);
704 rv
= nsContentUtils::WrapNative(aCx
, arg
, thisVal
);
709 nsCOMPtr
<nsIVariant
> variant
= do_QueryInterface(aArgs
);
711 rv
= xpc
->VariantToJS(aCx
, aScope
, variant
, aArgsOut
[0]);
713 NS_ERROR("Not an array, not an interface?");
714 rv
= NS_ERROR_UNEXPECTED
;
720 // This really should go into xpconnect somewhere...
721 nsresult
nsJSContext::AddSupportsPrimitiveTojsvals(JSContext
* aCx
,
724 MOZ_ASSERT(aArg
, "Empty arg");
726 nsCOMPtr
<nsISupportsPrimitive
> argPrimitive(do_QueryInterface(aArg
));
727 if (!argPrimitive
) return NS_ERROR_NO_INTERFACE
;
730 argPrimitive
->GetType(&type
);
733 case nsISupportsPrimitive::TYPE_CSTRING
: {
734 nsCOMPtr
<nsISupportsCString
> p(do_QueryInterface(argPrimitive
));
735 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
741 JSString
* str
= ::JS_NewStringCopyN(aCx
, data
.get(), data
.Length());
742 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
744 aArgv
->setString(str
);
748 case nsISupportsPrimitive::TYPE_STRING
: {
749 nsCOMPtr
<nsISupportsString
> p(do_QueryInterface(argPrimitive
));
750 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
756 // cast is probably safe since wchar_t and char16_t are expected
757 // to be equivalent; both unsigned 16-bit entities
758 JSString
* str
= ::JS_NewUCStringCopyN(aCx
, data
.get(), data
.Length());
759 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
761 aArgv
->setString(str
);
764 case nsISupportsPrimitive::TYPE_PRBOOL
: {
765 nsCOMPtr
<nsISupportsPRBool
> p(do_QueryInterface(argPrimitive
));
766 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
772 aArgv
->setBoolean(data
);
776 case nsISupportsPrimitive::TYPE_PRUINT8
: {
777 nsCOMPtr
<nsISupportsPRUint8
> p(do_QueryInterface(argPrimitive
));
778 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
784 aArgv
->setInt32(data
);
788 case nsISupportsPrimitive::TYPE_PRUINT16
: {
789 nsCOMPtr
<nsISupportsPRUint16
> p(do_QueryInterface(argPrimitive
));
790 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
796 aArgv
->setInt32(data
);
800 case nsISupportsPrimitive::TYPE_PRUINT32
: {
801 nsCOMPtr
<nsISupportsPRUint32
> p(do_QueryInterface(argPrimitive
));
802 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
808 aArgv
->setInt32(data
);
812 case nsISupportsPrimitive::TYPE_CHAR
: {
813 nsCOMPtr
<nsISupportsChar
> p(do_QueryInterface(argPrimitive
));
814 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
820 JSString
* str
= ::JS_NewStringCopyN(aCx
, &data
, 1);
821 NS_ENSURE_TRUE(str
, NS_ERROR_OUT_OF_MEMORY
);
823 aArgv
->setString(str
);
827 case nsISupportsPrimitive::TYPE_PRINT16
: {
828 nsCOMPtr
<nsISupportsPRInt16
> p(do_QueryInterface(argPrimitive
));
829 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
835 aArgv
->setInt32(data
);
839 case nsISupportsPrimitive::TYPE_PRINT32
: {
840 nsCOMPtr
<nsISupportsPRInt32
> p(do_QueryInterface(argPrimitive
));
841 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
847 aArgv
->setInt32(data
);
851 case nsISupportsPrimitive::TYPE_FLOAT
: {
852 nsCOMPtr
<nsISupportsFloat
> p(do_QueryInterface(argPrimitive
));
853 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
859 *aArgv
= ::JS_NumberValue(data
);
863 case nsISupportsPrimitive::TYPE_DOUBLE
: {
864 nsCOMPtr
<nsISupportsDouble
> p(do_QueryInterface(argPrimitive
));
865 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
871 *aArgv
= ::JS_NumberValue(data
);
875 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER
: {
876 nsCOMPtr
<nsISupportsInterfacePointer
> p(do_QueryInterface(argPrimitive
));
877 NS_ENSURE_TRUE(p
, NS_ERROR_UNEXPECTED
);
879 nsCOMPtr
<nsISupports
> data
;
880 nsIID
* iid
= nullptr;
882 p
->GetData(getter_AddRefs(data
));
884 NS_ENSURE_TRUE(iid
, NS_ERROR_UNEXPECTED
);
886 AutoFree
iidGuard(iid
); // Free iid upon destruction.
888 JS::Rooted
<JSObject
*> scope(aCx
, GetWindowProxy());
889 JS::Rooted
<JS::Value
> v(aCx
);
890 JSAutoRealm
ar(aCx
, scope
);
891 nsresult rv
= nsContentUtils::WrapNative(aCx
, data
, iid
, &v
);
892 NS_ENSURE_SUCCESS(rv
, rv
);
898 case nsISupportsPrimitive::TYPE_ID
:
899 case nsISupportsPrimitive::TYPE_PRUINT64
:
900 case nsISupportsPrimitive::TYPE_PRINT64
:
901 case nsISupportsPrimitive::TYPE_PRTIME
: {
902 NS_WARNING("Unsupported primitive type used");
907 NS_WARNING("Unknown primitive type used");
919 inline bool IsJProfAction(struct sigaction
* action
) {
920 return (action
->sa_sigaction
&&
921 (action
->sa_flags
& (SA_RESTART
| SA_SIGINFO
)) ==
922 (SA_RESTART
| SA_SIGINFO
));
925 void NS_JProfStartProfiling();
926 void NS_JProfStopProfiling();
927 void NS_JProfClearCircular();
929 static bool JProfStartProfilingJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
930 NS_JProfStartProfiling();
934 void NS_JProfStartProfiling() {
935 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
936 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
938 struct sigaction action
;
940 // Must check ALRM before PROF since both are enabled for real-time
941 sigaction(SIGALRM
, nullptr, &action
);
942 // printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
943 if (IsJProfAction(&action
)) {
944 // printf("Beginning real-time jprof profiling.\n");
949 sigaction(SIGPROF
, nullptr, &action
);
950 // printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
951 if (IsJProfAction(&action
)) {
952 // printf("Beginning process-time jprof profiling.\n");
957 sigaction(SIGPOLL
, nullptr, &action
);
958 // printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
959 if (IsJProfAction(&action
)) {
960 // printf("Beginning rtc-based jprof profiling.\n");
965 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
968 static bool JProfStopProfilingJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
969 NS_JProfStopProfiling();
973 void NS_JProfStopProfiling() {
975 // printf("Stopped jprof profiling.\n");
978 static bool JProfClearCircularJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
979 NS_JProfClearCircular();
983 void NS_JProfClearCircular() {
985 // printf("cleared jprof buffer\n");
988 static bool JProfSaveCircularJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
990 NS_JProfStopProfiling();
991 NS_JProfStartProfiling();
995 static const JSFunctionSpec JProfFunctions
[] = {
996 JS_FN("JProfStartProfiling", JProfStartProfilingJS
, 0, 0),
997 JS_FN("JProfStopProfiling", JProfStopProfilingJS
, 0, 0),
998 JS_FN("JProfClearCircular", JProfClearCircularJS
, 0, 0),
999 JS_FN("JProfSaveCircular", JProfSaveCircularJS
, 0, 0), JS_FS_END
};
1001 #endif /* defined(MOZ_JPROF) */
1003 nsresult
nsJSContext::InitClasses(JS::Handle
<JSObject
*> aGlobalObj
) {
1006 JSContext
* cx
= jsapi
.cx();
1007 JSAutoRealm
ar(cx
, aGlobalObj
);
1010 // Attempt to initialize JProf functions
1011 ::JS_DefineFunctions(cx
, aGlobalObj
, JProfFunctions
);
1017 bool nsJSContext::GetProcessingScriptTag() { return mProcessingScriptTag
; }
1019 void nsJSContext::SetProcessingScriptTag(bool aFlag
) {
1020 mProcessingScriptTag
= aFlag
;
1024 void nsJSContext::SetLowMemoryState(bool aState
) {
1025 JSContext
* cx
= danger::GetJSContext();
1026 JS::SetLowMemoryState(cx
, aState
);
1030 void nsJSContext::GarbageCollectNow(JS::GCReason aReason
,
1031 IsIncremental aIncremental
,
1032 IsShrinking aShrinking
,
1033 int64_t aSliceMillis
) {
1034 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
1035 "nsJSContext::GarbageCollectNow", GCCC
, JS::ExplainGCReason(aReason
));
1037 MOZ_ASSERT_IF(aSliceMillis
, aIncremental
== IncrementalGC
);
1039 // We use danger::GetJSContext() since AutoJSAPI will assert if the current
1040 // thread's context is null (such as during shutdown).
1041 JSContext
* cx
= danger::GetJSContext();
1043 if (!nsContentUtils::XPConnect() || !cx
) {
1047 if (sScheduler
.InIncrementalGC() && aIncremental
== IncrementalGC
) {
1048 // We're in the middle of incremental GC. Do another slice.
1049 JS::PrepareForIncrementalGC(cx
);
1050 JS::IncrementalGCSlice(cx
, aReason
, aSliceMillis
);
1054 JS::GCOptions options
=
1055 aShrinking
== ShrinkingGC
? JS::GCOptions::Shrink
: JS::GCOptions::Normal
;
1057 if (aIncremental
== NonIncrementalGC
||
1058 aReason
== JS::GCReason::FULL_GC_TIMER
||
1059 aReason
== JS::GCReason::USER_INACTIVE
) {
1060 sScheduler
.SetNeedsFullGC();
1063 if (sScheduler
.NeedsFullGC()) {
1064 JS::PrepareForFullGC(cx
);
1067 if (aIncremental
== IncrementalGC
) {
1068 // Incremental GC slices will be triggered by the GC Runner. If one doesn't
1069 // already exist, create it in the GC_SLICE_END callback for the first
1070 // slice being executed here.
1071 JS::StartIncrementalGC(cx
, options
, aReason
, aSliceMillis
);
1073 JS::NonIncrementalGC(cx
, options
, aReason
);
1077 static void FinishAnyIncrementalGC() {
1078 AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC
);
1080 if (sScheduler
.InIncrementalGC()) {
1084 // We're in the middle of an incremental GC, so finish it.
1085 JS::PrepareForIncrementalGC(jsapi
.cx());
1086 JS::FinishIncrementalGC(jsapi
.cx(), JS::GCReason::CC_FORCED
);
1090 static void FireForgetSkippable(bool aRemoveChildless
, TimeStamp aDeadline
) {
1091 AUTO_PROFILER_TRACING_MARKER(
1092 "CC", aDeadline
.IsNull() ? "ForgetSkippable" : "IdleForgetSkippable",
1094 TimeStamp startTimeStamp
= TimeStamp::Now();
1095 FinishAnyIncrementalGC();
1097 uint32_t suspectedBefore
= nsCycleCollector_suspectedCount();
1098 js::SliceBudget budget
=
1099 sScheduler
.ComputeForgetSkippableBudget(startTimeStamp
, aDeadline
);
1100 bool earlyForgetSkippable
= sScheduler
.IsEarlyForgetSkippable();
1101 nsCycleCollector_forgetSkippable(budget
, aRemoveChildless
,
1102 earlyForgetSkippable
);
1103 TimeStamp now
= TimeStamp::Now();
1104 uint32_t removedPurples
= sScheduler
.NoteForgetSkippableComplete(
1105 now
, suspectedBefore
, nsCycleCollector_suspectedCount());
1107 TimeDuration duration
= now
- startTimeStamp
;
1109 sCCStats
.AfterForgetSkippable(duration
, removedPurples
);
1111 if (duration
.ToSeconds()) {
1112 TimeDuration idleDuration
;
1113 if (!aDeadline
.IsNull()) {
1114 if (aDeadline
< now
) {
1115 // This slice overflowed the idle period.
1116 if (aDeadline
> startTimeStamp
) {
1117 idleDuration
= aDeadline
- startTimeStamp
;
1120 idleDuration
= duration
;
1125 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
1126 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE
, percent
);
1131 static TimeDuration
TimeBetween(TimeStamp aStart
, TimeStamp aEnd
) {
1132 MOZ_ASSERT(aEnd
>= aStart
);
1133 return aEnd
- aStart
;
1136 static TimeDuration
TimeUntilNow(TimeStamp start
) {
1137 if (start
.IsNull()) {
1138 return TimeDuration();
1140 return TimeBetween(start
, TimeStamp::Now());
1143 void CycleCollectorStats::Init() {
1146 char* env
= getenv("MOZ_CCTIMER");
1150 if (strcmp(env
, "none") == 0) {
1152 } else if (strcmp(env
, "stdout") == 0) {
1154 } else if (strcmp(env
, "stderr") == 0) {
1157 mFile
= fopen(env
, "a");
1159 MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
1164 void CycleCollectorStats::Clear() {
1165 if (mFile
&& mFile
!= stdout
&& mFile
!= stderr
) {
1168 *this = CycleCollectorStats();
1171 void CycleCollectorStats::AfterCycleCollectionSlice() {
1172 if (mBeginSliceTime
.IsNull()) {
1173 // We already called this method from EndCycleCollectionCallback for this
1178 mEndSliceTime
= TimeStamp::Now();
1179 TimeDuration duration
= mEndSliceTime
- mBeginSliceTime
;
1181 if (duration
.ToSeconds()) {
1182 TimeDuration idleDuration
;
1183 if (!mIdleDeadline
.IsNull()) {
1184 if (mIdleDeadline
< mEndSliceTime
) {
1185 // This slice overflowed the idle period.
1186 idleDuration
= mIdleDeadline
- mBeginSliceTime
;
1188 idleDuration
= duration
;
1193 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
1194 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE
,
1198 TimeDuration sliceTime
= TimeBetween(mBeginSliceTime
, mEndSliceTime
);
1199 mMaxSliceTime
= std::max(mMaxSliceTime
, sliceTime
);
1200 mMaxSliceTimeSinceClear
= std::max(mMaxSliceTimeSinceClear
, sliceTime
);
1201 mTotalSliceTime
+= sliceTime
;
1202 mBeginSliceTime
= TimeStamp();
1205 void CycleCollectorStats::AfterPrepareForCycleCollectionSlice(
1206 TimeStamp aDeadline
, TimeStamp aBeginTime
, TimeStamp aMaybeAfterGCTime
) {
1207 mBeginSliceTime
= aBeginTime
;
1208 mIdleDeadline
= aDeadline
;
1210 if (!aMaybeAfterGCTime
.IsNull()) {
1211 mAnyLockedOut
= true;
1212 mMaxGCDuration
= std::max(mMaxGCDuration
, aMaybeAfterGCTime
- aBeginTime
);
1216 void CycleCollectorStats::AfterSyncForgetSkippable(TimeStamp beginTime
) {
1217 mMaxSkippableDuration
=
1218 std::max(mMaxSkippableDuration
, TimeUntilNow(beginTime
));
1219 mRanSyncForgetSkippable
= true;
1222 void CycleCollectorStats::AfterForgetSkippable(TimeDuration duration
,
1223 uint32_t aRemovedPurples
) {
1224 if (!mMinForgetSkippableTime
|| mMinForgetSkippableTime
> duration
) {
1225 mMinForgetSkippableTime
= duration
;
1227 if (!mMaxForgetSkippableTime
|| mMaxForgetSkippableTime
< duration
) {
1228 mMaxForgetSkippableTime
= duration
;
1230 mTotalForgetSkippableTime
+= duration
;
1231 ++mForgetSkippableBeforeCC
;
1233 mRemovedPurples
+= aRemovedPurples
;
1236 void CycleCollectorStats::SendTelemetry(TimeDuration aCCNowDuration
) const {
1237 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC
, mAnyLockedOut
);
1238 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE
,
1239 mRanSyncForgetSkippable
);
1240 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL
,
1241 aCCNowDuration
.ToMilliseconds());
1242 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE
,
1243 mMaxSliceTime
.ToMilliseconds());
1245 TimeStamp lastCCEndTime
= sScheduler
.GetLastCCEndTime();
1246 if (!lastCCEndTime
.IsNull()) {
1247 TimeDuration timeBetween
= TimeBetween(lastCCEndTime
, mBeginTime
);
1248 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN
,
1249 timeBetween
.ToSeconds());
1252 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX
,
1253 mMaxForgetSkippableTime
.ToMilliseconds());
1256 void CycleCollectorStats::MaybeLogStats(const CycleCollectorResults
& aResults
,
1257 uint32_t aCleanups
) const {
1258 if (!StaticPrefs::javascript_options_mem_log() && !sCCStats
.mFile
) {
1262 TimeDuration delta
= GetCollectionTimeDelta();
1265 if (aResults
.mMergedZones
) {
1266 mergeMsg
.AssignLiteral(" merged");
1270 if (aResults
.mForcedGC
) {
1271 gcMsg
.AssignLiteral(", forced a GC");
1274 const char16_t
* kFmt
=
1275 u
"CC(T+%.1f)[%s-%i] max pause: %.fms, total time: %.fms, slices: %lu, "
1276 u
"suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu "
1277 u
"RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
1278 u
"ForgetSkippable %lu times before CC, min: %.f ms, max: %.f ms, avg: "
1279 u
"%.f ms, total: %.f ms, max sync: %.f ms, removed: %lu";
1281 nsTextFormatter::ssprintf(
1282 msg
, kFmt
, delta
.ToMicroseconds() / PR_USEC_PER_SEC
,
1283 ProcessNameForCollectorLog(), getpid(), mMaxSliceTime
.ToMilliseconds(),
1284 mTotalSliceTime
.ToMilliseconds(), aResults
.mNumSlices
, mSuspected
,
1285 aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
, mergeMsg
.get(),
1286 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1287 sScheduler
.mCCollectedWaitingForGC
,
1288 sScheduler
.mCCollectedZonesWaitingForGC
,
1289 sScheduler
.mLikelyShortLivingObjectsNeedingGC
, gcMsg
.get(),
1290 mForgetSkippableBeforeCC
, mMinForgetSkippableTime
.ToMilliseconds(),
1291 mMaxForgetSkippableTime
.ToMilliseconds(),
1292 mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1293 mTotalForgetSkippableTime
.ToMilliseconds(),
1294 mMaxSkippableDuration
.ToMilliseconds(), mRemovedPurples
);
1295 if (StaticPrefs::javascript_options_mem_log()) {
1296 nsCOMPtr
<nsIConsoleService
> cs
=
1297 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1299 cs
->LogStringMessage(msg
.get());
1303 fprintf(mFile
, "%s\n", NS_ConvertUTF16toUTF8(msg
).get());
1307 void CycleCollectorStats::MaybeNotifyStats(
1308 const CycleCollectorResults
& aResults
, TimeDuration aCCNowDuration
,
1309 uint32_t aCleanups
) const {
1310 if (!StaticPrefs::javascript_options_mem_notify()) {
1314 const char16_t
* kJSONFmt
=
1315 u
"{ \"timestamp\": %llu, "
1316 u
"\"duration\": %.f, "
1317 u
"\"max_slice_pause\": %.f, "
1318 u
"\"total_slice_pause\": %.f, "
1319 u
"\"max_finish_gc_duration\": %.f, "
1320 u
"\"max_sync_skippable_duration\": %.f, "
1321 u
"\"suspected\": %lu, "
1324 u
"\"GCed\": %lu }, "
1325 u
"\"collected\": { "
1327 u
"\"GCed\": %lu }, "
1328 u
"\"waiting_for_gc\": %lu, "
1329 u
"\"zones_waiting_for_gc\": %lu, "
1330 u
"\"short_living_objects_waiting_for_gc\": %lu, "
1331 u
"\"forced_gc\": %d, "
1332 u
"\"forget_skippable\": { "
1333 u
"\"times_before_cc\": %lu, "
1338 u
"\"removed\": %lu } "
1342 nsTextFormatter::ssprintf(
1343 json
, kJSONFmt
, PR_Now(), aCCNowDuration
.ToMilliseconds(),
1344 mMaxSliceTime
.ToMilliseconds(), mTotalSliceTime
.ToMilliseconds(),
1345 mMaxGCDuration
.ToMilliseconds(), mMaxSkippableDuration
.ToMilliseconds(),
1346 mSuspected
, aResults
.mVisitedRefCounted
, aResults
.mVisitedGCed
,
1347 aResults
.mFreedRefCounted
, aResults
.mFreedGCed
,
1348 sScheduler
.mCCollectedWaitingForGC
,
1349 sScheduler
.mCCollectedZonesWaitingForGC
,
1350 sScheduler
.mLikelyShortLivingObjectsNeedingGC
, aResults
.mForcedGC
,
1351 mForgetSkippableBeforeCC
, mMinForgetSkippableTime
.ToMilliseconds(),
1352 mMaxForgetSkippableTime
.ToMilliseconds(),
1353 mTotalForgetSkippableTime
.ToMilliseconds() / aCleanups
,
1354 mTotalForgetSkippableTime
.ToMilliseconds(), mRemovedPurples
);
1355 nsCOMPtr
<nsIObserverService
> observerService
=
1356 mozilla::services::GetObserverService();
1357 if (observerService
) {
1358 observerService
->NotifyObservers(nullptr, "cycle-collection-statistics",
1364 void nsJSContext::CycleCollectNow(nsICycleCollectorListener
* aListener
) {
1365 if (!NS_IsMainThread()) {
1369 AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC
);
1371 PrepareForCycleCollectionSlice(TimeStamp());
1372 nsCycleCollector_collect(aListener
);
1373 sCCStats
.AfterCycleCollectionSlice();
1377 void nsJSContext::PrepareForCycleCollectionSlice(TimeStamp aDeadline
) {
1378 TimeStamp beginTime
= TimeStamp::Now();
1380 // Before we begin the cycle collection, make sure there is no active GC.
1381 TimeStamp afterGCTime
;
1382 if (sScheduler
.InIncrementalGC()) {
1383 FinishAnyIncrementalGC();
1384 afterGCTime
= TimeStamp::Now();
1386 sCCStats
.AfterPrepareForCycleCollectionSlice(aDeadline
, beginTime
,
1391 void nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline
) {
1392 if (!NS_IsMainThread()) {
1396 AUTO_PROFILER_TRACING_MARKER(
1397 "CC", aDeadline
.IsNull() ? "CCSlice" : "IdleCCSlice", GCCC
);
1399 AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorSlice", GCCC
);
1401 PrepareForCycleCollectionSlice(aDeadline
);
1403 // Decide how long we want to budget for this slice.
1404 if (sIncrementalCC
) {
1405 bool preferShorterSlices
;
1406 js::SliceBudget budget
= sScheduler
.ComputeCCSliceBudget(
1407 aDeadline
, sCCStats
.mBeginTime
, sCCStats
.mEndSliceTime
,
1408 TimeStamp::Now(), &preferShorterSlices
);
1409 nsCycleCollector_collectSlice(budget
, preferShorterSlices
);
1411 js::SliceBudget budget
= js::SliceBudget::unlimited();
1412 nsCycleCollector_collectSlice(budget
, false);
1415 sCCStats
.AfterCycleCollectionSlice();
1419 void nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget
) {
1420 if (!NS_IsMainThread()) {
1424 AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", GCCC
);
1426 PrepareForCycleCollectionSlice(TimeStamp());
1428 js::SliceBudget budget
= js::SliceBudget(js::WorkBudget(aWorkBudget
));
1429 nsCycleCollector_collectSlice(budget
);
1431 sCCStats
.AfterCycleCollectionSlice();
1434 void nsJSContext::ClearMaxCCSliceTime() {
1435 sCCStats
.mMaxSliceTimeSinceClear
= TimeDuration();
1438 uint32_t nsJSContext::GetMaxCCSliceTimeSinceClear() {
1439 return sCCStats
.mMaxSliceTimeSinceClear
.ToMilliseconds();
1443 void nsJSContext::BeginCycleCollectionCallback() {
1444 MOZ_ASSERT(NS_IsMainThread());
1446 TimeStamp startTime
= TimeStamp::Now();
1447 sCCStats
.mBeginTime
=
1448 sCCStats
.mBeginSliceTime
.IsNull() ? startTime
: sCCStats
.mBeginSliceTime
;
1449 sCCStats
.mSuspected
= nsCycleCollector_suspectedCount();
1451 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1452 // is particularly useful if we recently finished a GC.
1453 if (sScheduler
.IsEarlyForgetSkippable()) {
1454 while (sScheduler
.IsEarlyForgetSkippable()) {
1455 FireForgetSkippable(false, TimeStamp());
1457 sCCStats
.AfterSyncForgetSkippable(startTime
);
1460 if (sShuttingDown
) {
1464 sScheduler
.InitCCRunnerStateMachine(
1465 mozilla::CCGCScheduler::CCRunnerState::CycleCollecting
);
1466 sScheduler
.EnsureCCRunner(kICCIntersliceDelay
, kIdleICCSliceBudget
);
1470 void nsJSContext::EndCycleCollectionCallback(CycleCollectorResults
& aResults
) {
1471 MOZ_ASSERT(NS_IsMainThread());
1473 sScheduler
.KillCCRunner();
1475 // Update timing information for the current slice before we log it, if
1476 // we previously called PrepareForCycleCollectionSlice(). During shutdown
1477 // CCs, this won't happen.
1478 sCCStats
.AfterCycleCollectionSlice();
1479 sScheduler
.NoteCycleCollected(aResults
);
1481 TimeStamp endCCTimeStamp
= TimeStamp::Now();
1482 TimeDuration ccNowDuration
= TimeBetween(sCCStats
.mBeginTime
, endCCTimeStamp
);
1484 if (sScheduler
.NeedsGCAfterCC()) {
1486 TimeDuration::FromMilliseconds(
1487 StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration
,
1488 "A max duration ICC shouldn't reduce GC delay to 0");
1490 sScheduler
.PokeGC(JS::GCReason::CC_FINISHED
, nullptr,
1491 TimeDuration::FromMilliseconds(
1492 StaticPrefs::javascript_options_gc_delay()) -
1493 std::min(ccNowDuration
, kMaxICCDuration
));
1496 // Log information about the CC via telemetry, JSON and the console.
1498 sCCStats
.SendTelemetry(ccNowDuration
);
1500 uint32_t cleanups
= std::max(sCCStats
.mForgetSkippableBeforeCC
, 1u);
1502 sCCStats
.MaybeLogStats(aResults
, cleanups
);
1504 sCCStats
.MaybeNotifyStats(aResults
, ccNowDuration
, cleanups
);
1506 // Update global state to indicate we have just run a cycle collection.
1507 sScheduler
.NoteCCEnd(endCCTimeStamp
);
1512 bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline
) {
1513 bool didDoWork
= false;
1515 // The CC/GC scheduler (sScheduler) decides what action(s) to take during
1516 // this invocation of the CC runner.
1518 // This may be zero, one, or multiple actions. (Zero is when CC is blocked by
1519 // incremental GC, or when the scheduler determined that a CC is no longer
1520 // needed.) Loop until the scheduler finishes this invocation by returning
1521 // `Yield` in step.mYield.
1524 step
= sScheduler
.AdvanceCCRunner(aDeadline
, TimeStamp::Now(),
1525 nsCycleCollector_suspectedCount());
1526 switch (step
.mAction
) {
1527 case CCRunnerAction::None
:
1530 case CCRunnerAction::ForgetSkippable
:
1531 // 'Forget skippable' only, then end this invocation.
1532 FireForgetSkippable(bool(step
.mRemoveChildless
), aDeadline
);
1535 case CCRunnerAction::CleanupContentUnbinder
:
1536 // Clear content unbinder before the first actual CC slice.
1537 Element::ClearContentUnbinder();
1540 case CCRunnerAction::CleanupDeferred
:
1541 // and if time still permits, perform deferred deletions.
1542 nsCycleCollector_doDeferredDeletion();
1545 case CCRunnerAction::CycleCollect
:
1546 // Cycle collection slice.
1547 nsJSContext::RunCycleCollectorSlice(aDeadline
);
1550 case CCRunnerAction::StopRunning
:
1551 // End this CC, either because we have run a cycle collection slice, or
1552 // because a CC is no longer needed.
1553 sScheduler
.KillCCRunner();
1557 if (step
.mAction
!= CCRunnerAction::None
) {
1560 } while (step
.mYield
== CCRunnerYield::Continue
);
1566 bool nsJSContext::HasHadCleanupSinceLastGC() {
1567 return sScheduler
.IsEarlyForgetSkippable(1);
1571 void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason
,
1572 mozilla::TimeStamp aDeadline
) {
1573 sScheduler
.RunNextCollectorTimer(aReason
, aDeadline
);
1577 void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell
* aDocShell
,
1578 JS::GCReason aReason
) {
1579 if (!aDocShell
|| !XRE_IsContentProcess()) {
1583 BrowsingContext
* bc
= aDocShell
->GetBrowsingContext();
1588 BrowsingContext
* root
= bc
->Top();
1590 // We don't want to run collectors when loading the top level page.
1594 nsIDocShell
* rootDocShell
= root
->GetDocShell();
1595 if (!rootDocShell
) {
1599 Document
* rootDocument
= rootDocShell
->GetDocument();
1600 if (!rootDocument
||
1601 rootDocument
->GetReadyStateEnum() != Document::READYSTATE_COMPLETE
||
1602 rootDocument
->IsInBackgroundWindow()) {
1606 PresShell
* presShell
= rootDocument
->GetPresShell();
1611 nsViewManager
* vm
= presShell
->GetViewManager();
1616 if (!sScheduler
.IsUserActive()) {
1617 Maybe
<TimeStamp
> next
= nsRefreshDriver::GetNextTickHint();
1618 // Try to not delay the next RefreshDriver tick, so give a reasonable
1619 // deadline for collectors.
1620 if (next
.isSome()) {
1621 sScheduler
.RunNextCollectorTimer(aReason
, next
.value());
1627 void nsJSContext::PokeGC(JS::GCReason aReason
, JSObject
* aObj
,
1628 TimeDuration aDelay
) {
1629 sScheduler
.PokeGC(aReason
, aObj
, aDelay
);
1633 void nsJSContext::DoLowMemoryGC() {
1634 if (sShuttingDown
) {
1637 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1638 nsJSContext::NonIncrementalGC
,
1639 nsJSContext::ShrinkingGC
);
1640 nsJSContext::CycleCollectNow();
1641 if (sScheduler
.NeedsGCAfterCC()) {
1642 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE
,
1643 nsJSContext::NonIncrementalGC
,
1644 nsJSContext::ShrinkingGC
);
1649 void nsJSContext::LowMemoryGC() {
1650 RefPtr
<CCGCScheduler::MayGCPromise
> mbPromise
=
1651 CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE
);
1653 // Normally when the promise is null it means that IPC failed, that probably
1654 // means that something bad happened, don't bother with the GC.
1658 GetMainThreadSerialEventTarget(), __func__
,
1659 [](bool aIgnored
) { DoLowMemoryGC(); },
1660 [](mozilla::ipc::ResponseRejectReason r
) {});
1664 void nsJSContext::MaybePokeCC() {
1665 sScheduler
.MaybePokeCC(TimeStamp::Now(), nsCycleCollector_suspectedCount());
1668 static void DOMGCSliceCallback(JSContext
* aCx
, JS::GCProgress aProgress
,
1669 const JS::GCDescription
& aDesc
) {
1670 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
1672 static TimeStamp sCurrentGCStartTime
;
1674 switch (aProgress
) {
1675 case JS::GC_CYCLE_BEGIN
: {
1676 // Prevent cycle collections and shrinking during incremental GC.
1677 sScheduler
.NoteGCBegin();
1678 sCurrentGCStartTime
= TimeStamp::Now();
1682 case JS::GC_CYCLE_END
: {
1683 TimeDuration delta
= GetCollectionTimeDelta();
1685 if (StaticPrefs::javascript_options_mem_log()) {
1687 gcstats
.Adopt(aDesc
.formatSummaryMessage(aCx
));
1688 nsAutoString prefix
;
1689 nsTextFormatter::ssprintf(prefix
, u
"GC(T+%.1f)[%s-%i] ",
1691 ProcessNameForCollectorLog(), getpid());
1692 nsString msg
= prefix
+ gcstats
;
1693 nsCOMPtr
<nsIConsoleService
> cs
=
1694 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1696 cs
->LogStringMessage(msg
.get());
1700 sScheduler
.NoteGCEnd();
1702 // May need to kill the GC runner
1703 sScheduler
.KillGCRunner();
1705 TimeStamp now
= TimeStamp::Now();
1706 sScheduler
.MaybePokeCC(now
, nsCycleCollector_suspectedCount());
1708 if (aDesc
.isZone_
) {
1709 sScheduler
.PokeFullGC();
1711 sScheduler
.SetNeedsFullGC(false);
1712 sScheduler
.KillFullGCTimer();
1715 if (sScheduler
.IsCCNeeded(now
, nsCycleCollector_suspectedCount())) {
1716 nsCycleCollector_dispatchDeferredDeletion();
1719 Telemetry::Accumulate(Telemetry::GC_IN_PROGRESS_MS
,
1720 TimeUntilNow(sCurrentGCStartTime
).ToMilliseconds());
1724 case JS::GC_SLICE_BEGIN
:
1727 case JS::GC_SLICE_END
:
1728 sScheduler
.NoteGCSliceEnd(aDesc
.lastSliceEnd(aCx
) -
1729 aDesc
.lastSliceStart(aCx
));
1731 if (sShuttingDown
) {
1732 sScheduler
.KillGCRunner();
1734 // If incremental GC wasn't triggered by GCTimerFired, we may not have a
1735 // runner to ensure all the slices are handled. So, create the runner
1737 sScheduler
.EnsureGCRunner(0);
1740 if (sScheduler
.IsCCNeeded(TimeStamp::Now(),
1741 nsCycleCollector_suspectedCount())) {
1742 nsCycleCollector_dispatchDeferredDeletion();
1745 if (StaticPrefs::javascript_options_mem_log()) {
1747 gcstats
.Adopt(aDesc
.formatSliceMessage(aCx
));
1748 nsAutoString prefix
;
1749 nsTextFormatter::ssprintf(prefix
, u
"[%s-%i] ",
1750 ProcessNameForCollectorLog(), getpid());
1751 nsString msg
= prefix
+ gcstats
;
1752 nsCOMPtr
<nsIConsoleService
> cs
=
1753 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
1755 cs
->LogStringMessage(msg
.get());
1762 MOZ_CRASH("Unexpected GCProgress value");
1765 if (sPrevGCSliceCallback
) {
1766 (*sPrevGCSliceCallback
)(aCx
, aProgress
, aDesc
);
1770 void nsJSContext::SetWindowProxy(JS::Handle
<JSObject
*> aWindowProxy
) {
1771 mWindowProxy
= aWindowProxy
;
1774 JSObject
* nsJSContext::GetWindowProxy() { return mWindowProxy
; }
1776 void nsJSContext::LikelyShortLivingObjectCreated() {
1777 ++sScheduler
.mLikelyShortLivingObjectsNeedingGC
;
1780 void mozilla::dom::StartupJSEnvironment() {
1781 // initialize all our statics, so that we can restart XPCOM
1782 sIsInitialized
= false;
1783 sShuttingDown
= false;
1784 new (&sScheduler
) CCGCScheduler(); // Reset the scheduler state.
1788 static void SetGCParameter(JSGCParamKey aParam
, uint32_t aValue
) {
1791 JS_SetGCParameter(jsapi
.cx(), aParam
, aValue
);
1794 static void ResetGCParameter(JSGCParamKey aParam
) {
1797 JS_ResetGCParameter(jsapi
.cx(), aParam
);
1800 static void SetMemoryPrefChangedCallbackMB(const char* aPrefName
,
1802 int32_t prefMB
= Preferences::GetInt(aPrefName
, -1);
1803 // handle overflow and negative pref values
1804 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefMB
) * 1024 * 1024;
1805 if (prefB
.isValid() && prefB
.value() >= 0) {
1806 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1808 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1812 static void SetMemoryNurseryPrefChangedCallback(const char* aPrefName
,
1814 int32_t prefKB
= Preferences::GetInt(aPrefName
, -1);
1815 // handle overflow and negative pref values
1816 CheckedInt
<int32_t> prefB
= CheckedInt
<int32_t>(prefKB
) * 1024;
1817 if (prefB
.isValid() && prefB
.value() >= 0) {
1818 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, prefB
.value());
1820 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1824 static void SetMemoryPrefChangedCallbackInt(const char* aPrefName
,
1826 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1827 // handle overflow and negative pref values
1828 if (pref
>= 0 && pref
< 10000) {
1829 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1831 ResetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
);
1835 static void SetMemoryPrefChangedCallbackBool(const char* aPrefName
,
1837 bool pref
= Preferences::GetBool(aPrefName
);
1838 SetGCParameter((JSGCParamKey
)(uintptr_t)aClosure
, pref
);
1841 static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName
,
1843 int32_t pref
= Preferences::GetInt(aPrefName
, -1);
1844 // handle overflow and negative pref values
1845 if (pref
> 0 && pref
< 100000) {
1846 sScheduler
.SetActiveIntersliceGCBudget(
1847 TimeDuration::FromMilliseconds(pref
));
1848 SetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
, pref
);
1850 ResetGCParameter(JSGC_SLICE_TIME_BUDGET_MS
);
1854 static void SetIncrementalCCPrefChangedCallback(const char* aPrefName
,
1856 bool pref
= Preferences::GetBool(aPrefName
);
1857 sIncrementalCC
= pref
;
1860 class JSDispatchableRunnable final
: public Runnable
{
1861 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable
); }
1864 explicit JSDispatchableRunnable(JS::Dispatchable
* aDispatchable
)
1865 : mozilla::Runnable("JSDispatchableRunnable"),
1866 mDispatchable(aDispatchable
) {
1867 MOZ_ASSERT(mDispatchable
);
1871 NS_IMETHOD
Run() override
{
1872 MOZ_ASSERT(NS_IsMainThread());
1877 JS::Dispatchable::MaybeShuttingDown maybeShuttingDown
=
1878 sShuttingDown
? JS::Dispatchable::ShuttingDown
1879 : JS::Dispatchable::NotShuttingDown
;
1881 mDispatchable
->run(jsapi
.cx(), maybeShuttingDown
);
1882 mDispatchable
= nullptr; // mDispatchable may delete itself
1888 JS::Dispatchable
* mDispatchable
;
1891 static bool DispatchToEventLoop(void* closure
,
1892 JS::Dispatchable
* aDispatchable
) {
1893 MOZ_ASSERT(!closure
);
1895 // This callback may execute either on the main thread or a random JS-internal
1896 // helper thread. This callback can be called during shutdown so we cannot
1897 // simply NS_DispatchToMainThread. Failure during shutdown is expected and
1898 // properly handled by the JS engine.
1900 nsCOMPtr
<nsIEventTarget
> mainTarget
= GetMainThreadEventTarget();
1905 RefPtr
<JSDispatchableRunnable
> r
= new JSDispatchableRunnable(aDispatchable
);
1906 MOZ_ALWAYS_SUCCEEDS(mainTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
1910 static bool ConsumeStream(JSContext
* aCx
, JS::HandleObject aObj
,
1911 JS::MimeType aMimeType
,
1912 JS::StreamConsumer
* aConsumer
) {
1913 return FetchUtil::StreamResponseToJS(aCx
, aObj
, aMimeType
, aConsumer
,
1917 void nsJSContext::EnsureStatics() {
1918 if (sIsInitialized
) {
1919 if (!nsContentUtils::XPConnect()) {
1925 // Let's make sure that our main thread is the same as the xpcom main thread.
1926 MOZ_ASSERT(NS_IsMainThread());
1931 sPrevGCSliceCallback
= JS::SetGCSliceCallback(jsapi
.cx(), DOMGCSliceCallback
);
1933 JS::InitDispatchToEventLoop(jsapi
.cx(), DispatchToEventLoop
, nullptr);
1934 JS::InitConsumeStreamCallback(jsapi
.cx(), ConsumeStream
,
1935 FetchUtil::ReportJSStreamError
);
1937 // Set these global xpconnect options...
1938 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB
,
1939 "javascript.options.mem.max",
1940 (void*)JSGC_MAX_BYTES
);
1941 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
1942 "javascript.options.mem.nursery.min_kb",
1943 (void*)JSGC_MIN_NURSERY_BYTES
);
1944 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback
,
1945 "javascript.options.mem.nursery.max_kb",
1946 (void*)JSGC_MAX_NURSERY_BYTES
);
1948 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1949 "javascript.options.mem.gc_per_zone",
1950 (void*)JSGC_PER_ZONE_GC_ENABLED
);
1952 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1953 "javascript.options.mem.gc_incremental",
1954 (void*)JSGC_INCREMENTAL_GC_ENABLED
);
1956 Preferences::RegisterCallbackAndCall(
1957 SetMemoryGCSliceTimePrefChangedCallback
,
1958 "javascript.options.mem.gc_incremental_slice_ms");
1960 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool
,
1961 "javascript.options.mem.gc_compacting",
1962 (void*)JSGC_COMPACTING_ENABLED
);
1964 Preferences::RegisterCallbackAndCall(
1965 SetMemoryPrefChangedCallbackBool
,
1966 "javascript.options.mem.incremental_weakmap",
1967 (void*)JSGC_INCREMENTAL_WEAKMAP_ENABLED
);
1969 Preferences::RegisterCallbackAndCall(
1970 SetMemoryPrefChangedCallbackInt
,
1971 "javascript.options.mem.gc_high_frequency_time_limit_ms",
1972 (void*)JSGC_HIGH_FREQUENCY_TIME_LIMIT
);
1974 Preferences::RegisterCallbackAndCall(
1975 SetMemoryPrefChangedCallbackInt
,
1976 "javascript.options.mem.gc_low_frequency_heap_growth",
1977 (void*)JSGC_LOW_FREQUENCY_HEAP_GROWTH
);
1979 Preferences::RegisterCallbackAndCall(
1980 SetMemoryPrefChangedCallbackInt
,
1981 "javascript.options.mem.gc_high_frequency_large_heap_growth",
1982 (void*)JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH
);
1984 Preferences::RegisterCallbackAndCall(
1985 SetMemoryPrefChangedCallbackInt
,
1986 "javascript.options.mem.gc_high_frequency_small_heap_growth",
1987 (void*)JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH
);
1989 Preferences::RegisterCallbackAndCall(
1990 SetMemoryPrefChangedCallbackInt
,
1991 "javascript.options.mem.gc_small_heap_size_max_mb",
1992 (void*)JSGC_SMALL_HEAP_SIZE_MAX
);
1994 Preferences::RegisterCallbackAndCall(
1995 SetMemoryPrefChangedCallbackInt
,
1996 "javascript.options.mem.gc_large_heap_size_min_mb",
1997 (void*)JSGC_LARGE_HEAP_SIZE_MIN
);
1999 Preferences::RegisterCallbackAndCall(
2000 SetMemoryPrefChangedCallbackInt
,
2001 "javascript.options.mem.gc_allocation_threshold_mb",
2002 (void*)JSGC_ALLOCATION_THRESHOLD
);
2004 Preferences::RegisterCallbackAndCall(
2005 SetMemoryPrefChangedCallbackInt
,
2006 "javascript.options.mem.gc_malloc_threshold_base_mb",
2007 (void*)JSGC_MALLOC_THRESHOLD_BASE
);
2009 Preferences::RegisterCallbackAndCall(
2010 SetMemoryPrefChangedCallbackInt
,
2011 "javascript.options.mem.gc_small_heap_incremental_limit",
2012 (void*)JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
);
2013 Preferences::RegisterCallbackAndCall(
2014 SetMemoryPrefChangedCallbackInt
,
2015 "javascript.options.mem.gc_large_heap_incremental_limit",
2016 (void*)JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
);
2018 Preferences::RegisterCallbackAndCall(
2019 SetMemoryPrefChangedCallbackInt
,
2020 "javascript.options.mem.gc_urgent_threshold_mb",
2021 (void*)JSGC_URGENT_THRESHOLD_MB
);
2023 Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback
,
2024 "dom.cycle_collector.incremental");
2026 Preferences::RegisterCallbackAndCall(
2027 SetMemoryPrefChangedCallbackInt
,
2028 "javascript.options.mem.gc_min_empty_chunk_count",
2029 (void*)JSGC_MIN_EMPTY_CHUNK_COUNT
);
2031 Preferences::RegisterCallbackAndCall(
2032 SetMemoryPrefChangedCallbackInt
,
2033 "javascript.options.mem.gc_max_empty_chunk_count",
2034 (void*)JSGC_MAX_EMPTY_CHUNK_COUNT
);
2036 Preferences::RegisterCallbackAndCall(
2037 SetMemoryPrefChangedCallbackInt
,
2038 "javascript.options.mem.gc_helper_thread_ratio",
2039 (void*)JSGC_HELPER_THREAD_RATIO
);
2041 Preferences::RegisterCallbackAndCall(
2042 SetMemoryPrefChangedCallbackInt
,
2043 "javascript.options.mem.gc_max_helper_threads",
2044 (void*)JSGC_MAX_HELPER_THREADS
);
2046 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
2051 nsIObserver
* observer
= new nsJSEnvironmentObserver();
2052 obs
->AddObserver(observer
, "memory-pressure", false);
2053 obs
->AddObserver(observer
, "user-interaction-inactive", false);
2054 obs
->AddObserver(observer
, "user-interaction-active", false);
2055 obs
->AddObserver(observer
, "quit-application", false);
2056 obs
->AddObserver(observer
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
2057 obs
->AddObserver(observer
, "content-child-will-shutdown", false);
2059 sIsInitialized
= true;
2062 void mozilla::dom::ShutdownJSEnvironment() {
2063 sShuttingDown
= true;
2064 sScheduler
.Shutdown();
2067 AsyncErrorReporter::AsyncErrorReporter(xpc::ErrorReport
* aReport
)
2068 : Runnable("dom::AsyncErrorReporter"), mReport(aReport
) {}
2070 void AsyncErrorReporter::SerializeStack(JSContext
* aCx
,
2071 JS::Handle
<JSObject
*> aStack
) {
2072 mStackHolder
= MakeUnique
<SerializedStackHolder
>();
2073 mStackHolder
->SerializeMainThreadOrWorkletStack(aCx
, aStack
);
2076 void AsyncErrorReporter::SetException(JSContext
* aCx
,
2077 JS::Handle
<JS::Value
> aException
) {
2078 MOZ_ASSERT(NS_IsMainThread());
2079 mException
.init(aCx
, aException
);
2080 mHasException
= true;
2083 NS_IMETHODIMP
AsyncErrorReporter::Run() {
2085 // We're only using this context to deserialize a stack to report to the
2086 // console, so the scope we use doesn't matter. Stack frame filtering happens
2087 // based on the principal encoded into the frame and the caller compartment,
2088 // not the compartment of the frame object, and the console reporting code
2089 // will not be using our context, and therefore will not care what compartment
2091 DebugOnly
<bool> ok
= jsapi
.Init(xpc::PrivilegedJunkScope());
2092 MOZ_ASSERT(ok
, "Problem with system global?");
2093 JSContext
* cx
= jsapi
.cx();
2094 JS::Rooted
<JSObject
*> stack(cx
);
2095 JS::Rooted
<JSObject
*> stackGlobal(cx
);
2097 stack
= mStackHolder
->ReadStack(cx
);
2099 stackGlobal
= JS::CurrentGlobalOrNull(cx
);
2103 JS::Rooted
<Maybe
<JS::Value
>> exception(cx
, Nothing());
2104 if (mHasException
) {
2105 MOZ_ASSERT(NS_IsMainThread());
2106 exception
= Some(mException
);
2107 // Remove our reference to the exception.
2108 mException
.setUndefined();
2109 mHasException
= false;
2112 mReport
->LogToConsoleWithStack(nullptr, exception
, stack
, stackGlobal
);
2116 // A fast-array class for JS. This class supports both nsIJSScriptArray and
2117 // nsIArray. If it is JS itself providing and consuming this class, all work
2118 // can be done via nsIJSScriptArray, and avoid the conversion of elements
2119 // to/from nsISupports.
2120 // When consumed by non-JS (eg, another script language), conversion is done
2122 class nsJSArgArray final
: public nsIJSArgArray
{
2124 nsJSArgArray(JSContext
* aContext
, uint32_t argc
, const JS::Value
* argv
,
2128 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2129 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray
,
2136 nsresult
GetArgs(uint32_t* argc
, void** argv
) override
;
2138 void ReleaseJSObjects();
2142 JSContext
* mContext
;
2143 JS::Heap
<JS::Value
>* mArgv
;
2147 nsJSArgArray::nsJSArgArray(JSContext
* aContext
, uint32_t argc
,
2148 const JS::Value
* argv
, nsresult
* prv
)
2149 : mContext(aContext
), mArgv(nullptr), mArgc(argc
) {
2150 // copy the array - we don't know its lifetime, and ours is tied to xpcom
2153 mArgv
= new (fallible
) JS::Heap
<JS::Value
>[argc
];
2155 *prv
= NS_ERROR_OUT_OF_MEMORY
;
2160 // Callers are allowed to pass in a null argv even for argc > 0. They can
2161 // then use GetArgs to initialize the values.
2163 for (uint32_t i
= 0; i
< argc
; ++i
) mArgv
[i
] = argv
[i
];
2167 mozilla::HoldJSObjects(this);
2173 nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); }
2175 void nsJSArgArray::ReleaseJSObjects() {
2180 mozilla::DropJSObjects(this);
2184 // QueryInterface implementation for nsJSArgArray
2185 NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(nsJSArgArray
)
2187 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray
)
2188 tmp
->ReleaseJSObjects();
2189 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray
)
2191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2193 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray
)
2195 for (uint32_t i
= 0; i
< tmp
->mArgc
; ++i
) {
2196 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv
[i
])
2199 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2201 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray
)
2202 NS_INTERFACE_MAP_ENTRY(nsIArray
)
2203 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray
)
2204 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIJSArgArray
)
2205 NS_INTERFACE_MAP_END
2207 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray
)
2208 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray
)
2210 nsresult
nsJSArgArray::GetArgs(uint32_t* argc
, void** argv
) {
2211 *argv
= (void*)mArgv
;
2217 NS_IMETHODIMP
nsJSArgArray::GetLength(uint32_t* aLength
) {
2222 NS_IMETHODIMP
nsJSArgArray::QueryElementAt(uint32_t index
, const nsIID
& uuid
,
2225 if (index
>= mArgc
) return NS_ERROR_INVALID_ARG
;
2227 if (uuid
.Equals(NS_GET_IID(nsIVariant
)) ||
2228 uuid
.Equals(NS_GET_IID(nsISupports
))) {
2229 // Have to copy a Heap into a Rooted to work with it.
2230 JS::Rooted
<JS::Value
> val(mContext
, mArgv
[index
]);
2231 return nsContentUtils::XPConnect()->JSToVariant(mContext
, val
,
2232 (nsIVariant
**)result
);
2234 NS_WARNING("nsJSArgArray only handles nsIVariant");
2235 return NS_ERROR_NO_INTERFACE
;
2238 NS_IMETHODIMP
nsJSArgArray::IndexOf(uint32_t startIndex
, nsISupports
* element
,
2239 uint32_t* _retval
) {
2240 return NS_ERROR_NOT_IMPLEMENTED
;
2243 NS_IMETHODIMP
nsJSArgArray::ScriptedEnumerate(const nsIID
& aElemIID
,
2245 nsISimpleEnumerator
** aResult
) {
2246 return NS_ERROR_NOT_IMPLEMENTED
;
2249 NS_IMETHODIMP
nsJSArgArray::EnumerateImpl(const nsID
& aEntryIID
,
2250 nsISimpleEnumerator
** _retval
) {
2251 return NS_ERROR_NOT_IMPLEMENTED
;
2254 // The factory function
2255 nsresult
NS_CreateJSArgv(JSContext
* aContext
, uint32_t argc
,
2256 const JS::Value
* argv
, nsIJSArgArray
** aArray
) {
2258 nsCOMPtr
<nsIJSArgArray
> ret
= new nsJSArgArray(aContext
, argc
, argv
, &rv
);
2259 if (NS_FAILED(rv
)) {