Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsJSEnvironment.cpp
blob035faa1ee175f136cd3499f869df623f22a0049a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsError.h"
8 #include "nsJSEnvironment.h"
9 #include "nsIScriptGlobalObject.h"
10 #include "nsIScriptObjectPrincipal.h"
11 #include "nsPIDOMWindow.h"
12 #include "nsDOMCID.h"
13 #include "nsIXPConnect.h"
14 #include "nsCOMPtr.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"
26 #include "nsITimer.h"
27 #include "nsAtom.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"
36 #ifdef XP_WIN
37 # include <process.h>
38 # define getpid _getpid
39 #else
40 # include <unistd.h> // for getpid()
41 #endif
42 #include "xpcpublic.h"
44 #include "jsapi.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"
50 #include "nsIArray.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"
76 #include "prthread.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"
92 #endif
94 using namespace mozilla;
95 using namespace mozilla::dom;
97 // Thank you Microsoft!
98 #ifdef CompareString
99 # undef CompareString
100 #endif
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;
114 void Init();
115 void Clear();
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
150 // current CC.
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
169 // period.
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"
184 : "content";
187 namespace xpc {
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
204 // a wrapper.
205 if (exceptionStack) {
206 stackObj.set(exceptionStack);
207 stackGlobal.set(JS::GetNonCCWObjectGlobal(exceptionStack));
209 return;
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.
215 return;
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));
230 return;
233 // It is not a JS Exception, try DOM Exception.
234 RefPtr<Exception> exception;
235 UNWRAP_OBJECT(DOMException, exceptionObject, exception);
236 if (!exception) {
237 // Not a DOM Exception, try XPC Exception.
238 UNWRAP_OBJECT(Exception, exceptionObject, exception);
239 if (!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));
245 return;
249 nsCOMPtr<nsIStackFrame> stack = exception->GetLocation();
250 if (!stack) {
251 return;
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));
259 return;
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;
278 public:
279 NS_DECL_ISUPPORTS
280 NS_DECL_NSIOBSERVER
283 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver, nsIObserver)
285 NS_IMETHODIMP
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()) {
290 if (sShuttingDown) {
291 // Don't GC/CC if we're already shutting down.
292 return NS_OK;
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.
298 return NS_OK;
300 if (data.EqualsLiteral("heap-minimize")) {
301 // heap-minimize notifiers expect this to run synchronously
302 nsJSContext::DoLowMemoryGC();
303 return NS_OK;
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();
324 return NS_OK;
327 /****************************************************************
328 ************************** AutoFree ****************************
329 ****************************************************************/
331 class AutoFree {
332 public:
333 explicit AutoFree(void* aPtr) : mPtr(aPtr) {}
334 ~AutoFree() {
335 if (mPtr) free(mPtr);
337 void Invalidate() { mPtr = nullptr; }
339 private:
340 void* mPtr;
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) {
350 bool called = false;
351 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aScriptGlobal));
352 nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
353 if (docShell) {
354 RefPtr<nsPresContext> presContext = docShell->GetPresContext();
356 static int32_t errorDepth; // Recursion prevention
357 ++errorDepth;
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);
370 called = true;
372 --errorDepth;
374 return called;
377 class ScriptErrorEvent : public Runnable {
378 public:
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"),
383 mWindow(aWindow),
384 mReport(aReport),
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;
392 MOZ_ASSERT(win);
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;
415 } else {
416 NS_WARNING("Not same origin error!");
417 init.mMessage = xoriginMsg;
418 init.mLineno = 0;
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,
435 &stackGlobal);
436 JS::Rooted<Maybe<JS::Value>> exception(rootingCx, Some(mError));
437 nsGlobalWindowInner* inner = nsGlobalWindowInner::Cast(win);
438 mReport->LogToConsoleWithStack(inner, exception, stack, stackGlobal);
441 return NS_OK;
444 private:
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
456 // soon.
457 namespace xpc {
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 */
470 #ifdef DEBUG
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) {
478 if (!win) {
479 printf("No window passed in.\n");
480 return;
483 nsCOMPtr<Document> doc = win->GetExtantDoc();
484 if (!doc) {
485 printf("No document in the window.\n");
486 return;
489 nsIURI* uri = doc->GetDocumentURI();
490 if (!uri) {
491 printf("Document doesn't have a URI.\n");
492 return;
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) {
504 if (!win) {
505 printf("No window passed in.\n");
506 return;
509 nsIPrincipal* prin = win->GetPrincipal();
510 if (!prin) {
511 printf("Window doesn't have principals.\n");
512 return;
514 if (prin->IsSystemPrincipal()) {
515 printf("No URI, it's the system principal.\n");
516 return;
518 nsCString spec;
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());
534 #endif
536 nsJSContext::nsJSContext(bool aGCOnDestruction,
537 nsIScriptGlobalObject* aGlobalObject)
538 : mWindowProxy(nullptr),
539 mGCOnDestruction(aGCOnDestruction),
540 mGlobalObjectRef(aGlobalObject) {
541 EnsureStatics();
543 mProcessingScriptTag = false;
544 HoldJSObjects(this);
547 nsJSContext::~nsJSContext() {
548 mGlobalObjectRef = nullptr;
550 Destroy();
553 void nsJSContext::Destroy() {
554 if (mGCOnDestruction) {
555 sScheduler->PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY, mWindowProxy);
558 DropJSObjects(this);
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;
571 tmp->Destroy();
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)
581 NS_INTERFACE_MAP_END
583 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
584 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
586 #ifdef DEBUG
587 bool AtomIsEventHandlerName(nsAtom* aName) {
588 const char16_t* name = aName->GetUTF16String();
590 const char16_t* cp;
591 char16_t c;
592 for (cp = name; *cp != '\0'; ++cp) {
593 c = *cp;
594 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) return false;
597 return true;
599 #endif
601 nsIScriptGlobalObject* nsJSContext::GetGlobalObject() {
602 // Note: this could probably be simplified somewhat more; see bug 974327
603 // comments 1 and 3.
604 if (!mWindowProxy) {
605 return nullptr;
608 MOZ_ASSERT(mGlobalObjectRef);
609 return mGlobalObjectRef;
612 nsresult nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget,
613 const char* aPropName, nsISupports* aArgs) {
614 AutoJSAPI jsapi;
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));
635 if (!array) {
636 return NS_ERROR_FAILURE;
639 return JS_DefineProperty(cx, aTarget, aPropName, array, 0) ? NS_OK
640 : NS_ERROR_FAILURE;
643 nsresult nsJSContext::ConvertSupportsTojsvals(
644 JSContext* aCx, nsISupports* aArgs, JS::Handle<JSObject*> aScope,
645 JS::MutableHandleVector<JS::Value> aArgsOut) {
646 nsresult rv = NS_OK;
648 // If the array implements nsIJSArgArray, copy the contents and return.
649 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
650 if (fastArray) {
651 uint32_t argc;
652 JS::Value* argv;
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;
657 return rv;
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;
668 uint32_t argCount;
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));
673 if (argsArray) {
674 rv = argsArray->GetLength(&argCount);
675 NS_ENSURE_SUCCESS(rv, rv);
676 if (argCount == 0) return NS_OK;
677 } else {
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;
686 if (argsArray) {
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));
692 if (!arg) {
693 thisVal.setNull();
694 continue;
696 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
697 if (variant != nullptr) {
698 rv = xpc->VariantToJS(aCx, aScope, variant, thisVal);
699 } else {
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 -
706 // just wrap it.
707 #ifdef DEBUG
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!");
713 #endif
714 JSAutoRealm ar(aCx, aScope);
715 rv = nsContentUtils::WrapNative(aCx, arg, thisVal);
719 } else {
720 nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
721 if (variant) {
722 rv = xpc->VariantToJS(aCx, aScope, variant, aArgsOut[0]);
723 } else {
724 NS_ERROR("Not an array, not an interface?");
725 rv = NS_ERROR_UNEXPECTED;
728 return rv;
731 // This really should go into xpconnect somewhere...
732 nsresult nsJSContext::AddSupportsPrimitiveTojsvals(JSContext* aCx,
733 nsISupports* aArg,
734 JS::Value* aArgv) {
735 MOZ_ASSERT(aArg, "Empty arg");
737 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
738 if (!argPrimitive) return NS_ERROR_NO_INTERFACE;
740 uint16_t type;
741 argPrimitive->GetType(&type);
743 switch (type) {
744 case nsISupportsPrimitive::TYPE_CSTRING: {
745 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
746 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
748 nsAutoCString data;
750 p->GetData(data);
752 JSString* str = ::JS_NewStringCopyN(aCx, data.get(), data.Length());
753 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
755 aArgv->setString(str);
757 break;
759 case nsISupportsPrimitive::TYPE_STRING: {
760 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
761 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
763 nsAutoString data;
765 p->GetData(data);
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);
773 break;
775 case nsISupportsPrimitive::TYPE_PRBOOL: {
776 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
777 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
779 bool data;
781 p->GetData(&data);
783 aArgv->setBoolean(data);
785 break;
787 case nsISupportsPrimitive::TYPE_PRUINT8: {
788 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
789 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
791 uint8_t data;
793 p->GetData(&data);
795 aArgv->setInt32(data);
797 break;
799 case nsISupportsPrimitive::TYPE_PRUINT16: {
800 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
801 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
803 uint16_t data;
805 p->GetData(&data);
807 aArgv->setInt32(data);
809 break;
811 case nsISupportsPrimitive::TYPE_PRUINT32: {
812 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
813 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
815 uint32_t data;
817 p->GetData(&data);
819 aArgv->setInt32(data);
821 break;
823 case nsISupportsPrimitive::TYPE_CHAR: {
824 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
825 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
827 char data;
829 p->GetData(&data);
831 JSString* str = ::JS_NewStringCopyN(aCx, &data, 1);
832 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
834 aArgv->setString(str);
836 break;
838 case nsISupportsPrimitive::TYPE_PRINT16: {
839 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
840 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
842 int16_t data;
844 p->GetData(&data);
846 aArgv->setInt32(data);
848 break;
850 case nsISupportsPrimitive::TYPE_PRINT32: {
851 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
852 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
854 int32_t data;
856 p->GetData(&data);
858 aArgv->setInt32(data);
860 break;
862 case nsISupportsPrimitive::TYPE_FLOAT: {
863 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
864 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
866 float data;
868 p->GetData(&data);
870 *aArgv = ::JS_NumberValue(data);
872 break;
874 case nsISupportsPrimitive::TYPE_DOUBLE: {
875 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
876 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
878 double data;
880 p->GetData(&data);
882 *aArgv = ::JS_NumberValue(data);
884 break;
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));
894 p->GetDataIID(&iid);
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);
905 *aArgv = v;
907 break;
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");
914 aArgv->setNull();
915 break;
917 default: {
918 NS_WARNING("Unknown primitive type used");
919 aArgv->setNull();
920 break;
923 return NS_OK;
926 #ifdef MOZ_JPROF
928 # include <signal.h>
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();
942 return true;
945 void NS_JProfStartProfiling() {
946 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
947 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
948 // JP_RTC_HZ)
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");
956 raise(SIGALRM);
957 return;
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");
964 raise(SIGPROF);
965 return;
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");
972 raise(SIGPOLL);
973 return;
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();
981 return true;
984 void NS_JProfStopProfiling() {
985 raise(SIGUSR1);
986 // printf("Stopped jprof profiling.\n");
989 static bool JProfClearCircularJS(JSContext* cx, unsigned argc, JS::Value* vp) {
990 NS_JProfClearCircular();
991 return true;
994 void NS_JProfClearCircular() {
995 raise(SIGUSR2);
996 // printf("cleared jprof buffer\n");
999 static bool JProfSaveCircularJS(JSContext* cx, unsigned argc, JS::Value* vp) {
1000 // Not ideal...
1001 NS_JProfStopProfiling();
1002 NS_JProfStartProfiling();
1003 return true;
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) {
1015 AutoJSAPI jsapi;
1016 jsapi.Init();
1017 JSContext* cx = jsapi.cx();
1018 JSAutoRealm ar(cx, aGlobalObj);
1020 #ifdef MOZ_JPROF
1021 // Attempt to initialize JProf functions
1022 ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
1023 #endif
1025 return NS_OK;
1028 bool nsJSContext::GetProcessingScriptTag() { return mProcessingScriptTag; }
1030 void nsJSContext::SetProcessingScriptTag(bool aFlag) {
1031 mProcessingScriptTag = aFlag;
1034 // static
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) {
1053 return;
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);
1060 return;
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);
1080 } else {
1081 JS::NonIncrementalGC(cx, options, aReason);
1085 // static
1086 void nsJSContext::GarbageCollectNow(JS::GCReason aReason,
1087 IsShrinking aShrinking) {
1088 GarbageCollectImpl(aReason, aShrinking, js::SliceBudget::unlimited());
1091 // static
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()) {
1103 AutoJSAPI jsapi;
1104 jsapi.Init();
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> {
1114 public:
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;
1165 } else {
1166 idleDuration = duration;
1170 uint32_t percent =
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());
1180 MOZ_ALWAYS_INLINE
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() {
1194 Clear();
1196 char* env = getenv("MOZ_CCTIMER");
1197 if (!env) {
1198 return;
1200 if (strcmp(env, "none") == 0) {
1201 mFile = nullptr;
1202 } else if (strcmp(env, "stdout") == 0) {
1203 mFile = stdout;
1204 } else if (strcmp(env, "stderr") == 0) {
1205 mFile = stderr;
1206 } else {
1207 mFile = fopen(env, "a");
1208 if (!mFile) {
1209 MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
1214 void CycleCollectorStats::Clear() {
1215 if (mFile && mFile != stdout && mFile != stderr) {
1216 fclose(mFile);
1218 *this = CycleCollectorStats();
1221 void CycleCollectorStats::AfterCycleCollectionSlice() {
1222 if (mBeginSliceTime.IsNull()) {
1223 // We already called this method from EndCycleCollectionCallback for this
1224 // slice.
1225 return;
1228 mEndSliceTime = TimeStamp::Now();
1229 TimeDuration duration = mEndSliceTime - mBeginSliceTime;
1231 PROFILER_MARKER(
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;
1243 } else {
1244 idleDuration = duration;
1248 uint32_t percent =
1249 uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
1250 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE,
1251 percent);
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) {
1262 mBeginTime = 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) {
1320 return;
1323 TimeDuration delta = GetCollectionTimeDelta();
1325 nsCString mergeMsg;
1326 if (aResults.mMergedZones) {
1327 mergeMsg.AssignLiteral(" merged");
1330 nsCString gcMsg;
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";
1341 nsString msg;
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);
1359 if (cs) {
1360 cs->LogStringMessage(msg.get());
1363 if (mFile) {
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()) {
1372 return;
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, "
1383 u"\"visited\": { "
1384 u"\"RCed\": %lu, "
1385 u"\"GCed\": %lu }, "
1386 u"\"collected\": { "
1387 u"\"RCed\": %lu, "
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, "
1395 u"\"min\": %.f, "
1396 u"\"max\": %.f, "
1397 u"\"avg\": %.f, "
1398 u"\"total\": %.f, "
1399 u"\"removed\": %lu } "
1400 u"}";
1402 nsString json;
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",
1420 json.get());
1424 // static
1425 void nsJSContext::CycleCollectNow(CCReason aReason,
1426 nsICycleCollectorListener* aListener) {
1427 if (!NS_IsMainThread()) {
1428 return;
1431 AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC);
1433 PrepareForCycleCollectionSlice(aReason, TimeStamp());
1434 nsCycleCollector_collect(aReason, aListener);
1435 sCCStats.AfterCycleCollectionSlice();
1438 // static
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,
1458 afterGCTime);
1461 // static
1462 void nsJSContext::RunCycleCollectorSlice(CCReason aReason,
1463 TimeStamp aDeadline) {
1464 if (!NS_IsMainThread()) {
1465 return;
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);
1477 } else {
1478 js::SliceBudget budget = js::SliceBudget::unlimited();
1479 nsCycleCollector_collectSlice(budget, aReason, false);
1482 sCCStats.AfterCycleCollectionSlice();
1485 // static
1486 void nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget) {
1487 if (!NS_IsMainThread()) {
1488 return;
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();
1509 // static
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) {
1526 return;
1529 sScheduler->InitCCRunnerStateMachine(
1530 mozilla::CCGCScheduler::CCRunnerState::CycleCollecting, aReason);
1531 sScheduler->EnsureCCRunner(kICCIntersliceDelay, kIdleICCSliceBudget);
1534 // static
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.
1563 sCCStats.Clear();
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()) {
1569 MOZ_ASSERT(
1570 TimeDuration::FromMilliseconds(
1571 StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration,
1572 "A max duration ICC shouldn't reduce GC delay to 0");
1574 TimeDuration delay;
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());
1580 } else {
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)
1589 else if (
1590 StaticPrefs::
1591 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1592 jemalloc_free_dirty_pages();
1594 #endif
1597 /* static */
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.
1610 CCRunnerStep step;
1611 do {
1612 step = sScheduler->AdvanceCCRunner(aDeadline, TimeStamp::Now(),
1613 nsCycleCollector_suspectedCount());
1614 switch (step.mAction) {
1615 case CCRunnerAction::None:
1616 break;
1618 case CCRunnerAction::MinorGC:
1619 JS::MaybeRunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
1620 step.mParam.mReason);
1621 sScheduler->NoteMinorGCEnd();
1622 break;
1624 case CCRunnerAction::ForgetSkippable:
1625 // 'Forget skippable' only, then end this invocation.
1626 FireForgetSkippable(bool(step.mParam.mRemoveChildless), aDeadline);
1627 break;
1629 case CCRunnerAction::CleanupContentUnbinder:
1630 // Clear content unbinder before the first actual CC slice.
1631 Element::ClearContentUnbinder();
1632 break;
1634 case CCRunnerAction::CleanupDeferred:
1635 // and if time still permits, perform deferred deletions.
1636 nsCycleCollector_doDeferredDeletion();
1637 break;
1639 case CCRunnerAction::CycleCollect:
1640 // Cycle collection slice.
1641 nsJSContext::RunCycleCollectorSlice(step.mParam.mCCReason, aDeadline);
1642 break;
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();
1648 break;
1651 if (step.mAction != CCRunnerAction::None) {
1652 didDoWork = true;
1654 } while (step.mYield == CCRunnerYield::Continue);
1656 return didDoWork;
1659 // static
1660 bool nsJSContext::HasHadCleanupSinceLastGC() {
1661 return sScheduler->IsEarlyForgetSkippable(1);
1664 // static
1665 void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason,
1666 mozilla::TimeStamp aDeadline) {
1667 sScheduler->RunNextCollectorTimer(aReason, aDeadline);
1670 // static
1671 void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
1672 JS::GCReason aReason) {
1673 if (!aDocShell || !XRE_IsContentProcess()) {
1674 return;
1677 BrowsingContext* bc = aDocShell->GetBrowsingContext();
1678 if (!bc) {
1679 return;
1682 BrowsingContext* root = bc->Top();
1683 if (bc == root) {
1684 // We don't want to run collectors when loading the top level page.
1685 return;
1688 nsIDocShell* rootDocShell = root->GetDocShell();
1689 if (!rootDocShell) {
1690 return;
1693 Document* rootDocument = rootDocShell->GetDocument();
1694 if (!rootDocument ||
1695 rootDocument->GetReadyStateEnum() != Document::READYSTATE_COMPLETE ||
1696 rootDocument->IsInBackgroundWindow()) {
1697 return;
1700 PresShell* presShell = rootDocument->GetPresShell();
1701 if (!presShell) {
1702 return;
1705 nsViewManager* vm = presShell->GetViewManager();
1706 if (!vm) {
1707 return;
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",
1723 [shell] {
1724 nsIDocShell::BusyFlags busyFlags =
1725 nsIDocShell::BUSY_FLAGS_NONE;
1726 shell->GetBusyFlags(&busyFlags);
1727 if (busyFlags == nsIDocShell::BUSY_FLAGS_NONE) {
1728 return;
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
1735 // time.
1736 JS::RunNurseryCollection(
1737 CycleCollectedJSRuntime::Get()->Runtime(),
1738 JS::GCReason::PREPARE_FOR_PAGELOAD,
1739 mozilla::TimeDuration::FromMilliseconds(16));
1741 EventQueuePriority::Idle);
1744 // static
1745 void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
1746 TimeDuration aDelay) {
1747 sScheduler->PokeGC(aReason, aObj, aDelay);
1750 // static
1751 void nsJSContext::MaybePokeGC() {
1752 if (sShuttingDown) {
1753 return;
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) {
1770 return;
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);
1781 // static
1782 void nsJSContext::LowMemoryGC() {
1783 RefPtr<CCGCScheduler::MayGCPromise> mbPromise =
1784 CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE);
1785 if (!mbPromise) {
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.
1788 return;
1790 mbPromise->Then(
1791 GetMainThreadSerialEventTarget(), __func__,
1792 [](bool aIgnored) { DoLowMemoryGC(); },
1793 [](mozilla::ipc::ResponseRejectReason r) {});
1796 // static
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();
1813 break;
1816 case JS::GC_CYCLE_END: {
1817 TimeDuration delta = GetCollectionTimeDelta();
1819 if (StaticPrefs::javascript_options_mem_log()) {
1820 nsString gcstats;
1821 gcstats.Adopt(aDesc.formatSummaryMessage(aCx));
1822 nsAutoString prefix;
1823 nsTextFormatter::ssprintf(prefix, u"GC(T+%.1f)[%s-%i] ",
1824 delta.ToSeconds(),
1825 ProcessNameForCollectorLog(), getpid());
1826 nsString msg = prefix + gcstats;
1827 nsCOMPtr<nsIConsoleService> cs =
1828 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1829 if (cs) {
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;
1843 #endif
1844 if (aDesc.isZone_) {
1845 sScheduler->PokeFullGC();
1846 } else {
1847 #if defined(MOZ_MEMORY)
1848 freeDirty = true;
1849 #endif
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.
1859 freeDirty = false;
1860 #endif
1861 nsCycleCollector_dispatchDeferredDeletion();
1864 Telemetry::Accumulate(Telemetry::GC_IN_PROGRESS_MS,
1865 TimeUntilNow(sCurrentGCStartTime).ToMilliseconds());
1867 #if defined(MOZ_MEMORY)
1868 if (freeDirty &&
1869 StaticPrefs::
1870 dom_memory_foreground_content_processes_have_larger_page_cache()) {
1871 jemalloc_free_dirty_pages();
1873 #endif
1874 break;
1877 case JS::GC_SLICE_BEGIN:
1878 break;
1880 case JS::GC_SLICE_END:
1881 sScheduler->NoteGCSliceEnd(aDesc.lastSliceStart(aCx),
1882 aDesc.lastSliceEnd(aCx));
1884 if (sShuttingDown) {
1885 sScheduler->KillGCRunner();
1886 } else {
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
1889 // here.
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()) {
1900 nsString gcstats;
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);
1908 if (cs) {
1909 cs->LogStringMessage(msg.get());
1913 break;
1915 default:
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;
1938 sCCStats.Init();
1941 static void SetGCParameter(JSGCParamKey aParam, uint32_t aValue) {
1942 AutoJSAPI jsapi;
1943 jsapi.Init();
1944 JS_SetGCParameter(jsapi.cx(), aParam, aValue);
1947 static void ResetGCParameter(JSGCParamKey aParam) {
1948 AutoJSAPI jsapi;
1949 jsapi.Init();
1950 JS_ResetGCParameter(jsapi.cx(), aParam);
1953 static void SetMemoryPrefChangedCallbackMB(const char* aPrefName,
1954 void* aClosure) {
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());
1960 } else {
1961 ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
1965 static void SetMemoryNurseryPrefChangedCallback(const char* aPrefName,
1966 void* aClosure) {
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());
1972 } else {
1973 ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
1977 static void SetMemoryPrefChangedCallbackInt(const char* aPrefName,
1978 void* aClosure) {
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);
1983 } else {
1984 ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
1988 static void SetMemoryPrefChangedCallbackBool(const char* aPrefName,
1989 void* aClosure) {
1990 bool pref = Preferences::GetBool(aPrefName);
1991 SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, pref);
1994 static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName,
1995 void* aClosure) {
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);
2002 } else {
2003 ResetGCParameter(JSGC_SLICE_TIME_BUDGET_MS);
2007 static void SetIncrementalCCPrefChangedCallback(const char* aPrefName,
2008 void* aClosure) {
2009 bool pref = Preferences::GetBool(aPrefName);
2010 sIncrementalCC = pref;
2013 class JSDispatchableRunnable final : public Runnable {
2014 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
2016 public:
2017 explicit JSDispatchableRunnable(JS::Dispatchable* aDispatchable)
2018 : mozilla::Runnable("JSDispatchableRunnable"),
2019 mDispatchable(aDispatchable) {
2020 MOZ_ASSERT(mDispatchable);
2023 protected:
2024 NS_IMETHOD Run() override {
2025 MOZ_ASSERT(NS_IsMainThread());
2027 AutoJSAPI jsapi;
2028 jsapi.Init();
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
2037 return NS_OK;
2040 private:
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();
2054 if (!mainTarget) {
2055 return false;
2058 RefPtr<JSDispatchableRunnable> r = new JSDispatchableRunnable(aDispatchable);
2059 MOZ_ALWAYS_SUCCEEDS(mainTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
2060 return true;
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,
2067 nullptr);
2070 static js::SliceBudget CreateGCSliceBudget(JS::GCReason aReason,
2071 int64_t aMillis) {
2072 return sScheduler->CreateGCSliceBudget(
2073 mozilla::TimeDuration::FromMilliseconds(aMillis), false, false);
2076 void nsJSContext::EnsureStatics() {
2077 if (sIsInitialized) {
2078 if (!nsContentUtils::XPConnect()) {
2079 MOZ_CRASH();
2081 return;
2084 // Let's make sure that our main thread is the same as the xpcom main thread.
2085 MOZ_ASSERT(NS_IsMainThread());
2087 sScheduler =
2088 new (&sSchedulerStorage) CCGCScheduler(); // Reset the scheduler state.
2090 AutoJSAPI jsapi;
2091 jsapi.Init();
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);
2129 #endif
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();
2258 if (!obs) {
2259 MOZ_CRASH();
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() {
2295 AutoJSAPI jsapi;
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
2301 // it has entered.
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);
2307 if (mStackHolder) {
2308 stack = mStackHolder->ReadStack(cx);
2309 if (stack) {
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);
2324 return NS_OK;
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
2332 // on-the-fly.
2333 class nsJSArgArray final : public nsIJSArgArray {
2334 public:
2335 nsJSArgArray(JSContext* aContext, uint32_t argc, const JS::Value* argv,
2336 nsresult* prv);
2338 // nsISupports
2339 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2340 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
2341 nsIJSArgArray)
2343 // nsIArray
2344 NS_DECL_NSIARRAY
2346 // nsIJSArgArray
2347 nsresult GetArgs(uint32_t* argc, void** argv) override;
2349 void ReleaseJSObjects();
2351 protected:
2352 ~nsJSArgArray();
2353 JSContext* mContext;
2354 JS::Heap<JS::Value>* mArgv;
2355 uint32_t mArgc;
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
2362 // refcounting.
2363 if (argc) {
2364 mArgv = new (fallible) JS::Heap<JS::Value>[argc];
2365 if (!mArgv) {
2366 *prv = NS_ERROR_OUT_OF_MEMORY;
2367 return;
2371 // Callers are allowed to pass in a null argv even for argc > 0. They can
2372 // then use GetArgs to initialize the values.
2373 if (argv) {
2374 for (uint32_t i = 0; i < argc; ++i) mArgv[i] = argv[i];
2377 if (argc > 0) {
2378 mozilla::HoldJSObjects(this);
2381 *prv = NS_OK;
2384 nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); }
2386 void nsJSArgArray::ReleaseJSObjects() {
2387 delete[] mArgv;
2389 if (mArgc > 0) {
2390 mArgc = 0;
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)
2405 if (tmp->mArgv) {
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;
2423 *argc = mArgc;
2424 return NS_OK;
2427 // nsIArray impl
2428 NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t* aLength) {
2429 *aLength = mArgc;
2430 return NS_OK;
2433 NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID& uuid,
2434 void** result) {
2435 *result = nullptr;
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,
2455 uint8_t aArgc,
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) {
2468 nsresult rv;
2469 nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc, argv, &rv);
2470 if (NS_FAILED(rv)) {
2471 return rv;
2473 ret.forget(aArray);
2474 return NS_OK;