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/. */
6
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"
43
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 "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"
73 #include "prthread.h"
74
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 "nsViewManager.h"
84 #include "mozilla/EventStateManager.h"
85 #include "mozilla/ProfilerLabels.h"
86 #include "mozilla/ProfilerMarkers.h"
87
88 using namespace mozilla;
89 using namespace mozilla::dom;
90
91 // Thank you Microsoft!
92 #ifdef CompareString
93 # undef CompareString
94 #endif
95
96 static JS::GCSliceCallback sPrevGCSliceCallback;
97
98 static bool sIncrementalCC = false;
99
100 static bool sIsInitialized;
101 static bool sShuttingDown;
102
103 static CCGCScheduler sScheduler;
104
105 struct CycleCollectorStats {
106 constexpr CycleCollectorStats() = default;
107 void Init();
108 void Clear();
109 void AfterPrepareForCycleCollectionSlice(TimeStamp aDeadline,
110 TimeStamp aBeginTime,
111 TimeStamp aMaybeAfterGCTime);
112 void AfterCycleCollectionSlice();
113 void AfterSyncForgetSkippable(TimeStamp beginTime);
114 void AfterForgetSkippable(TimeDuration duration, uint32_t aRemovedPurples);
115 void AfterCycleCollection();
116
117 void SendTelemetry(TimeDuration aCCNowDuration) const;
118 void MaybeLogStats(const CycleCollectorResults& aResults,
119 uint32_t aCleanups) const;
120 void MaybeNotifyStats(const CycleCollectorResults& aResults,
121 TimeDuration aCCNowDuration, uint32_t aCleanups) const;
122
123 // Time the current slice began, including any GC finishing.
124 TimeStamp mBeginSliceTime;
125
126 // Time the previous slice of the current CC ended.
127 TimeStamp mEndSliceTime;
128
129 // Time the current cycle collection began.
130 TimeStamp mBeginTime;
131
132 // The longest GC finishing duration for any slice of the current CC.
133 TimeDuration mMaxGCDuration;
134
135 // True if we ran sync forget skippable in any slice of the current CC.
136 bool mRanSyncForgetSkippable = false;
137
138 // Number of suspected objects at the start of the current CC.
139 uint32_t mSuspected = 0;
140
141 // The longest duration spent on sync forget skippable in any slice of the
142 // current CC.
143 TimeDuration mMaxSkippableDuration;
144
145 // The longest pause of any slice in the current CC.
146 TimeDuration mMaxSliceTime;
147
148 // The longest slice time since ClearMaxCCSliceTime() was called.
149 TimeDuration mMaxSliceTimeSinceClear;
150
151 // The total amount of time spent actually running the current CC.
152 TimeDuration mTotalSliceTime;
153
154 // True if we were locked out by the GC in any slice of the current CC.
155 bool mAnyLockedOut = false;
156
157 // A file to dump CC activity to; set by MOZ_CCTIMER environment variable.
158 FILE* mFile = nullptr;
159
160 // In case CC slice was triggered during idle time, set to the end of the idle
161 // period.
162 TimeStamp mIdleDeadline;
163
164 TimeDuration mMinForgetSkippableTime;
165 TimeDuration mMaxForgetSkippableTime;
166 TimeDuration mTotalForgetSkippableTime;
167 uint32_t mForgetSkippableBeforeCC = 0;
168
169 uint32_t mRemovedPurples = 0;
170 };
171
172 static CycleCollectorStats sCCStats;
173
ProcessNameForCollectorLog()174 static const char* ProcessNameForCollectorLog() {
175 return XRE_GetProcessType() == GeckoProcessType_Default ? "default"
176 : "content";
177 }
178
179 namespace xpc {
180
181 // This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
182 // Exceptions, and arbitrary values that were associated with a stack by the
183 // JS engine when they were thrown, as specified by exceptionStack.
184 //
185 // Note that the returned stackObj and stackGlobal are _not_ wrapped into the
186 // compartment of exceptionValue.
FindExceptionStackForConsoleReport(nsPIDOMWindowInner * win,JS::HandleValue exceptionValue,JS::HandleObject exceptionStack,JS::MutableHandleObject stackObj,JS::MutableHandleObject stackGlobal)187 void FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
188 JS::HandleValue exceptionValue,
189 JS::HandleObject exceptionStack,
190 JS::MutableHandleObject stackObj,
191 JS::MutableHandleObject stackGlobal) {
192 stackObj.set(nullptr);
193 stackGlobal.set(nullptr);
194
195 if (!exceptionValue.isObject()) {
196 // Use the stack provided by the JS engine, if available. This will not be
197 // a wrapper.
198 if (exceptionStack) {
199 stackObj.set(exceptionStack);
200 stackGlobal.set(JS::GetNonCCWObjectGlobal(exceptionStack));
201 }
202 return;
203 }
204
205 if (win && win->AsGlobal()->IsDying()) {
206 // Pretend like we have no stack, so we don't end up keeping the global
207 // alive via the stack.
208 return;
209 }
210
211 JS::RootingContext* rcx = RootingCx();
212 JS::RootedObject exceptionObject(rcx, &exceptionValue.toObject());
213 if (JSObject* excStack = JS::ExceptionStackOrNull(exceptionObject)) {
214 // At this point we know exceptionObject is a possibly-wrapped
215 // js::ErrorObject that has excStack as stack. excStack might also be a CCW,
216 // but excStack must be same-compartment with the unwrapped ErrorObject.
217 // Return the ErrorObject's global as stackGlobal. This matches what we do
218 // in the ErrorObject's |.stack| getter and ensures stackObj and stackGlobal
219 // are same-compartment.
220 JSObject* unwrappedException = js::UncheckedUnwrap(exceptionObject);
221 stackObj.set(excStack);
222 stackGlobal.set(JS::GetNonCCWObjectGlobal(unwrappedException));
223 return;
224 }
225
226 // It is not a JS Exception, try DOM Exception.
227 RefPtr<Exception> exception;
228 UNWRAP_OBJECT(DOMException, exceptionObject, exception);
229 if (!exception) {
230 // Not a DOM Exception, try XPC Exception.
231 UNWRAP_OBJECT(Exception, exceptionObject, exception);
232 if (!exception) {
233 // As above, use the stack provided by the JS engine, if available.
234 if (exceptionStack) {
235 stackObj.set(exceptionStack);
236 stackGlobal.set(JS::GetNonCCWObjectGlobal(exceptionStack));
237 }
238 return;
239 }
240 }
241
242 nsCOMPtr<nsIStackFrame> stack = exception->GetLocation();
243 if (!stack) {
244 return;
245 }
246 JS::RootedValue value(rcx);
247 stack->GetNativeSavedFrame(&value);
248 if (value.isObject()) {
249 stackObj.set(&value.toObject());
250 MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj));
251 stackGlobal.set(JS::GetNonCCWObjectGlobal(stackObj));
252 return;
253 }
254 }
255
256 } /* namespace xpc */
257
GetCollectionTimeDelta()258 static TimeDuration GetCollectionTimeDelta() {
259 static TimeStamp sFirstCollectionTime;
260 TimeStamp now = TimeStamp::Now();
261 if (sFirstCollectionTime) {
262 return now - sFirstCollectionTime;
263 }
264 sFirstCollectionTime = now;
265 return TimeDuration();
266 }
267
268 class nsJSEnvironmentObserver final : public nsIObserver {
269 ~nsJSEnvironmentObserver() = default;
270
271 public:
272 NS_DECL_ISUPPORTS
273 NS_DECL_NSIOBSERVER
274 };
275
NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver,nsIObserver)276 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver, nsIObserver)
277
278 NS_IMETHODIMP
279 nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
280 const char16_t* aData) {
281 if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
282 if (StaticPrefs::javascript_options_gc_on_memory_pressure()) {
283 if (sShuttingDown) {
284 // Don't GC/CC if we're already shutting down.
285 return NS_OK;
286 }
287 nsDependentString data(aData);
288 if (data.EqualsLiteral("low-memory-ongoing")) {
289 // Don't GC/CC if we are in an ongoing low-memory state since its very
290 // slow and it likely won't help us anyway.
291 return NS_OK;
292 }
293 if (data.EqualsLiteral("heap-minimize")) {
294 // heap-minimize notifiers expect this to run synchronously
295 nsJSContext::DoLowMemoryGC();
296 return NS_OK;
297 }
298 if (data.EqualsLiteral("low-memory")) {
299 nsJSContext::SetLowMemoryState(true);
300 }
301 // Asynchronously GC.
302 nsJSContext::LowMemoryGC();
303 }
304 } else if (!nsCRT::strcmp(aTopic, "memory-pressure-stop")) {
305 nsJSContext::SetLowMemoryState(false);
306 } else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
307 sScheduler.UserIsInactive();
308 } else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
309 sScheduler.UserIsActive();
310 } else if (!nsCRT::strcmp(aTopic, "quit-application") ||
311 !nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) ||
312 !nsCRT::strcmp(aTopic, "content-child-will-shutdown")) {
313 sShuttingDown = true;
314 sScheduler.Shutdown();
315 }
316
317 return NS_OK;
318 }
319
320 /****************************************************************
321 ************************** AutoFree ****************************
322 ****************************************************************/
323
324 class AutoFree {
325 public:
AutoFree(void * aPtr)326 explicit AutoFree(void* aPtr) : mPtr(aPtr) {}
~AutoFree()327 ~AutoFree() {
328 if (mPtr) free(mPtr);
329 }
Invalidate()330 void Invalidate() { mPtr = nullptr; }
331
332 private:
333 void* mPtr;
334 };
335
336 // A utility function for script languages to call. Although it looks small,
337 // the use of nsIDocShell and nsPresContext triggers a huge number of
338 // dependencies that most languages would not otherwise need.
339 // XXXmarkh - This function is mis-placed!
NS_HandleScriptError(nsIScriptGlobalObject * aScriptGlobal,const ErrorEventInit & aErrorEventInit,nsEventStatus * aStatus)340 bool NS_HandleScriptError(nsIScriptGlobalObject* aScriptGlobal,
341 const ErrorEventInit& aErrorEventInit,
342 nsEventStatus* aStatus) {
343 bool called = false;
344 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aScriptGlobal));
345 nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
346 if (docShell) {
347 RefPtr<nsPresContext> presContext = docShell->GetPresContext();
348
349 static int32_t errorDepth; // Recursion prevention
350 ++errorDepth;
351
352 if (errorDepth < 2) {
353 // Dispatch() must be synchronous for the recursion block
354 // (errorDepth) to work.
355 RefPtr<ErrorEvent> event = ErrorEvent::Constructor(
356 nsGlobalWindowInner::Cast(win), u"error"_ns, aErrorEventInit);
357 event->SetTrusted(true);
358
359 EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
360 aStatus);
361 called = true;
362 }
363 --errorDepth;
364 }
365 return called;
366 }
367
368 class ScriptErrorEvent : public Runnable {
369 public:
ScriptErrorEvent(nsPIDOMWindowInner * aWindow,JS::RootingContext * aRootingCx,xpc::ErrorReport * aReport,JS::Handle<JS::Value> aError,JS::Handle<JSObject * > aErrorStack)370 ScriptErrorEvent(nsPIDOMWindowInner* aWindow, JS::RootingContext* aRootingCx,
371 xpc::ErrorReport* aReport, JS::Handle<JS::Value> aError,
372 JS::Handle<JSObject*> aErrorStack)
373 : mozilla::Runnable("ScriptErrorEvent"),
374 mWindow(aWindow),
375 mReport(aReport),
376 mError(aRootingCx, aError),
377 mErrorStack(aRootingCx, aErrorStack) {}
378
379 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
Run()380 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
381 nsEventStatus status = nsEventStatus_eIgnore;
382 nsCOMPtr<nsPIDOMWindowInner> win = mWindow;
383 MOZ_ASSERT(win);
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;
392
393 RefPtr<nsPresContext> presContext = win->GetDocShell()->GetPresContext();
394
395 RootedDictionary<ErrorEventInit> init(rootingCx);
396 init.mCancelable = true;
397 init.mFilename = mReport->mFileName;
398 init.mBubbles = true;
399
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;
406 } else {
407 NS_WARNING("Not same origin error!");
408 init.mMessage = xoriginMsg;
409 init.mLineno = 0;
410 }
411
412 RefPtr<ErrorEvent> event = ErrorEvent::Constructor(
413 nsGlobalWindowInner::Cast(win), u"error"_ns, init);
414 event->SetTrusted(true);
415
416 EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
417 &status);
418 }
419
420 if (status != nsEventStatus_eConsumeNoDefault) {
421 JS::Rooted<JSObject*> stack(rootingCx);
422 JS::Rooted<JSObject*> stackGlobal(rootingCx);
423 xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack, &stack,
424 &stackGlobal);
425 JS::Rooted<Maybe<JS::Value>> exception(rootingCx, Some(mError));
426 nsGlobalWindowInner* inner = nsGlobalWindowInner::Cast(win);
427 mReport->LogToConsoleWithStack(inner, exception, stack, stackGlobal);
428 }
429
430 return NS_OK;
431 }
432
433 private:
434 nsCOMPtr<nsPIDOMWindowInner> mWindow;
435 RefPtr<xpc::ErrorReport> mReport;
436 JS::PersistentRootedValue mError;
437 JS::PersistentRootedObject mErrorStack;
438
439 static bool sHandlingScriptError;
440 };
441
442 bool ScriptErrorEvent::sHandlingScriptError = false;
443
444 // This temporarily lives here to avoid code churn. It will go away entirely
445 // soon.
446 namespace xpc {
447
DispatchScriptErrorEvent(nsPIDOMWindowInner * win,JS::RootingContext * rootingCx,xpc::ErrorReport * xpcReport,JS::Handle<JS::Value> exception,JS::Handle<JSObject * > exceptionStack)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));
455 }
456
457 } /* namespace xpc */
458
459 #ifdef DEBUG
460 // A couple of useful functions to call when you're debugging.
JSObject2Win(JSObject * obj)461 nsGlobalWindowInner* JSObject2Win(JSObject* obj) {
462 return xpc::WindowOrNull(obj);
463 }
464
465 template <typename T>
PrintWinURI(T * win)466 void PrintWinURI(T* win) {
467 if (!win) {
468 printf("No window passed in.\n");
469 return;
470 }
471
472 nsCOMPtr<Document> doc = win->GetExtantDoc();
473 if (!doc) {
474 printf("No document in the window.\n");
475 return;
476 }
477
478 nsIURI* uri = doc->GetDocumentURI();
479 if (!uri) {
480 printf("Document doesn't have a URI.\n");
481 return;
482 }
483
484 printf("%s\n", uri->GetSpecOrDefault().get());
485 }
486
PrintWinURIInner(nsGlobalWindowInner * aWin)487 void PrintWinURIInner(nsGlobalWindowInner* aWin) { return PrintWinURI(aWin); }
488
PrintWinURIOuter(nsGlobalWindowOuter * aWin)489 void PrintWinURIOuter(nsGlobalWindowOuter* aWin) { return PrintWinURI(aWin); }
490
491 template <typename T>
PrintWinCodebase(T * win)492 void PrintWinCodebase(T* win) {
493 if (!win) {
494 printf("No window passed in.\n");
495 return;
496 }
497
498 nsIPrincipal* prin = win->GetPrincipal();
499 if (!prin) {
500 printf("Window doesn't have principals.\n");
501 return;
502 }
503 if (prin->IsSystemPrincipal()) {
504 printf("No URI, it's the system principal.\n");
505 return;
506 }
507 nsCString spec;
508 prin->GetAsciiSpec(spec);
509 printf("%s\n", spec.get());
510 }
511
PrintWinCodebaseInner(nsGlobalWindowInner * aWin)512 void PrintWinCodebaseInner(nsGlobalWindowInner* aWin) {
513 return PrintWinCodebase(aWin);
514 }
515
PrintWinCodebaseOuter(nsGlobalWindowOuter * aWin)516 void PrintWinCodebaseOuter(nsGlobalWindowOuter* aWin) {
517 return PrintWinCodebase(aWin);
518 }
519
DumpString(const nsAString & str)520 void DumpString(const nsAString& str) {
521 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
522 }
523 #endif
524
nsJSContext(bool aGCOnDestruction,nsIScriptGlobalObject * aGlobalObject)525 nsJSContext::nsJSContext(bool aGCOnDestruction,
526 nsIScriptGlobalObject* aGlobalObject)
527 : mWindowProxy(nullptr),
528 mGCOnDestruction(aGCOnDestruction),
529 mGlobalObjectRef(aGlobalObject) {
530 EnsureStatics();
531
532 mProcessingScriptTag = false;
533 HoldJSObjects(this);
534 }
535
~nsJSContext()536 nsJSContext::~nsJSContext() {
537 mGlobalObjectRef = nullptr;
538
539 Destroy();
540 }
541
Destroy()542 void nsJSContext::Destroy() {
543 if (mGCOnDestruction) {
544 sScheduler.PokeGC(JS::GCReason::NSJSCONTEXT_DESTROY, mWindowProxy);
545 }
546
547 DropJSObjects(this);
548 }
549
550 // QueryInterface implementation for nsJSContext
551 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
552
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
556
557 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
558 tmp->mGCOnDestruction = false;
559 tmp->mWindowProxy = nullptr;
560 tmp->Destroy();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)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
566
567 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
568 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
569 NS_INTERFACE_MAP_ENTRY(nsISupports)
570 NS_INTERFACE_MAP_END
571
572 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
573 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
574
575 #ifdef DEBUG
576 bool AtomIsEventHandlerName(nsAtom* aName) {
577 const char16_t* name = aName->GetUTF16String();
578
579 const char16_t* cp;
580 char16_t c;
581 for (cp = name; *cp != '\0'; ++cp) {
582 c = *cp;
583 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) return false;
584 }
585
586 return true;
587 }
588 #endif
589
GetGlobalObject()590 nsIScriptGlobalObject* nsJSContext::GetGlobalObject() {
591 // Note: this could probably be simplified somewhat more; see bug 974327
592 // comments 1 and 3.
593 if (!mWindowProxy) {
594 return nullptr;
595 }
596
597 MOZ_ASSERT(mGlobalObjectRef);
598 return mGlobalObjectRef;
599 }
600
SetProperty(JS::Handle<JSObject * > aTarget,const char * aPropName,nsISupports * aArgs)601 nsresult nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget,
602 const char* aPropName, nsISupports* aArgs) {
603 AutoJSAPI jsapi;
604 if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
605 return NS_ERROR_FAILURE;
606 }
607 JSContext* cx = jsapi.cx();
608
609 JS::RootedVector<JS::Value> args(cx);
610
611 JS::Rooted<JSObject*> global(cx, GetWindowProxy());
612 nsresult rv = ConvertSupportsTojsvals(cx, aArgs, global, &args);
613 NS_ENSURE_SUCCESS(rv, rv);
614
615 // got the arguments, now attach them.
616
617 for (uint32_t i = 0; i < args.length(); ++i) {
618 if (!JS_WrapValue(cx, args[i])) {
619 return NS_ERROR_FAILURE;
620 }
621 }
622
623 JS::Rooted<JSObject*> array(cx, JS::NewArrayObject(cx, args));
624 if (!array) {
625 return NS_ERROR_FAILURE;
626 }
627
628 return JS_DefineProperty(cx, aTarget, aPropName, array, 0) ? NS_OK
629 : NS_ERROR_FAILURE;
630 }
631
ConvertSupportsTojsvals(JSContext * aCx,nsISupports * aArgs,JS::Handle<JSObject * > aScope,JS::MutableHandleVector<JS::Value> aArgsOut)632 nsresult nsJSContext::ConvertSupportsTojsvals(
633 JSContext* aCx, nsISupports* aArgs, JS::Handle<JSObject*> aScope,
634 JS::MutableHandleVector<JS::Value> aArgsOut) {
635 nsresult rv = NS_OK;
636
637 // If the array implements nsIJSArgArray, copy the contents and return.
638 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
639 if (fastArray) {
640 uint32_t argc;
641 JS::Value* argv;
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;
645 }
646 return rv;
647 }
648
649 // Take the slower path converting each item.
650 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
651 // SetProperty('arguments', ...);
652
653 nsIXPConnect* xpc = nsContentUtils::XPConnect();
654 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
655
656 if (!aArgs) return NS_OK;
657 uint32_t argCount;
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));
661
662 if (argsArray) {
663 rv = argsArray->GetLength(&argCount);
664 NS_ENSURE_SUCCESS(rv, rv);
665 if (argCount == 0) return NS_OK;
666 } else {
667 argCount = 1; // the nsISupports which is not an array
668 }
669
670 // Use the caller's auto guards to release and unroot.
671 if (!aArgsOut.resize(argCount)) {
672 return NS_ERROR_OUT_OF_MEMORY;
673 }
674
675 if (argsArray) {
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));
681 if (!arg) {
682 thisVal.setNull();
683 continue;
684 }
685 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
686 if (variant != nullptr) {
687 rv = xpc->VariantToJS(aCx, aScope, variant, thisVal);
688 } else {
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 -
695 // just wrap it.
696 #ifdef DEBUG
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!");
702 #endif
703 JSAutoRealm ar(aCx, aScope);
704 rv = nsContentUtils::WrapNative(aCx, arg, thisVal);
705 }
706 }
707 }
708 } else {
709 nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
710 if (variant) {
711 rv = xpc->VariantToJS(aCx, aScope, variant, aArgsOut[0]);
712 } else {
713 NS_ERROR("Not an array, not an interface?");
714 rv = NS_ERROR_UNEXPECTED;
715 }
716 }
717 return rv;
718 }
719
720 // This really should go into xpconnect somewhere...
AddSupportsPrimitiveTojsvals(JSContext * aCx,nsISupports * aArg,JS::Value * aArgv)721 nsresult nsJSContext::AddSupportsPrimitiveTojsvals(JSContext* aCx,
722 nsISupports* aArg,
723 JS::Value* aArgv) {
724 MOZ_ASSERT(aArg, "Empty arg");
725
726 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
727 if (!argPrimitive) return NS_ERROR_NO_INTERFACE;
728
729 uint16_t type;
730 argPrimitive->GetType(&type);
731
732 switch (type) {
733 case nsISupportsPrimitive::TYPE_CSTRING: {
734 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
735 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
736
737 nsAutoCString data;
738
739 p->GetData(data);
740
741 JSString* str = ::JS_NewStringCopyN(aCx, data.get(), data.Length());
742 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
743
744 aArgv->setString(str);
745
746 break;
747 }
748 case nsISupportsPrimitive::TYPE_STRING: {
749 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
750 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
751
752 nsAutoString data;
753
754 p->GetData(data);
755
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);
760
761 aArgv->setString(str);
762 break;
763 }
764 case nsISupportsPrimitive::TYPE_PRBOOL: {
765 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
766 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
767
768 bool data;
769
770 p->GetData(&data);
771
772 aArgv->setBoolean(data);
773
774 break;
775 }
776 case nsISupportsPrimitive::TYPE_PRUINT8: {
777 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
778 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
779
780 uint8_t data;
781
782 p->GetData(&data);
783
784 aArgv->setInt32(data);
785
786 break;
787 }
788 case nsISupportsPrimitive::TYPE_PRUINT16: {
789 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
790 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
791
792 uint16_t data;
793
794 p->GetData(&data);
795
796 aArgv->setInt32(data);
797
798 break;
799 }
800 case nsISupportsPrimitive::TYPE_PRUINT32: {
801 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
802 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
803
804 uint32_t data;
805
806 p->GetData(&data);
807
808 aArgv->setInt32(data);
809
810 break;
811 }
812 case nsISupportsPrimitive::TYPE_CHAR: {
813 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
814 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
815
816 char data;
817
818 p->GetData(&data);
819
820 JSString* str = ::JS_NewStringCopyN(aCx, &data, 1);
821 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
822
823 aArgv->setString(str);
824
825 break;
826 }
827 case nsISupportsPrimitive::TYPE_PRINT16: {
828 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
829 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
830
831 int16_t data;
832
833 p->GetData(&data);
834
835 aArgv->setInt32(data);
836
837 break;
838 }
839 case nsISupportsPrimitive::TYPE_PRINT32: {
840 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
841 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
842
843 int32_t data;
844
845 p->GetData(&data);
846
847 aArgv->setInt32(data);
848
849 break;
850 }
851 case nsISupportsPrimitive::TYPE_FLOAT: {
852 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
853 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
854
855 float data;
856
857 p->GetData(&data);
858
859 *aArgv = ::JS_NumberValue(data);
860
861 break;
862 }
863 case nsISupportsPrimitive::TYPE_DOUBLE: {
864 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
865 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
866
867 double data;
868
869 p->GetData(&data);
870
871 *aArgv = ::JS_NumberValue(data);
872
873 break;
874 }
875 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER: {
876 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
877 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
878
879 nsCOMPtr<nsISupports> data;
880 nsIID* iid = nullptr;
881
882 p->GetData(getter_AddRefs(data));
883 p->GetDataIID(&iid);
884 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
885
886 AutoFree iidGuard(iid); // Free iid upon destruction.
887
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);
893
894 *aArgv = v;
895
896 break;
897 }
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");
903 aArgv->setNull();
904 break;
905 }
906 default: {
907 NS_WARNING("Unknown primitive type used");
908 aArgv->setNull();
909 break;
910 }
911 }
912 return NS_OK;
913 }
914
915 #ifdef MOZ_JPROF
916
917 # include <signal.h>
918
IsJProfAction(struct sigaction * action)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));
923 }
924
925 void NS_JProfStartProfiling();
926 void NS_JProfStopProfiling();
927 void NS_JProfClearCircular();
928
JProfStartProfilingJS(JSContext * cx,unsigned argc,JS::Value * vp)929 static bool JProfStartProfilingJS(JSContext* cx, unsigned argc, JS::Value* vp) {
930 NS_JProfStartProfiling();
931 return true;
932 }
933
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
937 // JP_RTC_HZ)
938 struct sigaction action;
939
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");
945 raise(SIGALRM);
946 return;
947 }
948
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");
953 raise(SIGPROF);
954 return;
955 }
956
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");
961 raise(SIGPOLL);
962 return;
963 }
964
965 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
966 }
967
JProfStopProfilingJS(JSContext * cx,unsigned argc,JS::Value * vp)968 static bool JProfStopProfilingJS(JSContext* cx, unsigned argc, JS::Value* vp) {
969 NS_JProfStopProfiling();
970 return true;
971 }
972
NS_JProfStopProfiling()973 void NS_JProfStopProfiling() {
974 raise(SIGUSR1);
975 // printf("Stopped jprof profiling.\n");
976 }
977
JProfClearCircularJS(JSContext * cx,unsigned argc,JS::Value * vp)978 static bool JProfClearCircularJS(JSContext* cx, unsigned argc, JS::Value* vp) {
979 NS_JProfClearCircular();
980 return true;
981 }
982
NS_JProfClearCircular()983 void NS_JProfClearCircular() {
984 raise(SIGUSR2);
985 // printf("cleared jprof buffer\n");
986 }
987
JProfSaveCircularJS(JSContext * cx,unsigned argc,JS::Value * vp)988 static bool JProfSaveCircularJS(JSContext* cx, unsigned argc, JS::Value* vp) {
989 // Not ideal...
990 NS_JProfStopProfiling();
991 NS_JProfStartProfiling();
992 return true;
993 }
994
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};
1000
1001 #endif /* defined(MOZ_JPROF) */
1002
InitClasses(JS::Handle<JSObject * > aGlobalObj)1003 nsresult nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj) {
1004 AutoJSAPI jsapi;
1005 jsapi.Init();
1006 JSContext* cx = jsapi.cx();
1007 JSAutoRealm ar(cx, aGlobalObj);
1008
1009 #ifdef MOZ_JPROF
1010 // Attempt to initialize JProf functions
1011 ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
1012 #endif
1013
1014 return NS_OK;
1015 }
1016
GetProcessingScriptTag()1017 bool nsJSContext::GetProcessingScriptTag() { return mProcessingScriptTag; }
1018
SetProcessingScriptTag(bool aFlag)1019 void nsJSContext::SetProcessingScriptTag(bool aFlag) {
1020 mProcessingScriptTag = aFlag;
1021 }
1022
1023 // static
SetLowMemoryState(bool aState)1024 void nsJSContext::SetLowMemoryState(bool aState) {
1025 JSContext* cx = danger::GetJSContext();
1026 JS::SetLowMemoryState(cx, aState);
1027 }
1028
GarbageCollectImpl(JS::GCReason aReason,nsJSContext::IsShrinking aShrinking,const js::SliceBudget & aBudget)1029 static void GarbageCollectImpl(JS::GCReason aReason,
1030 nsJSContext::IsShrinking aShrinking,
1031 const js::SliceBudget& aBudget) {
1032 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
1033 "nsJSContext::GarbageCollectNow", GCCC, JS::ExplainGCReason(aReason));
1034
1035 bool wantIncremental = !aBudget.isUnlimited();
1036
1037 // We use danger::GetJSContext() since AutoJSAPI will assert if the current
1038 // thread's context is null (such as during shutdown).
1039 JSContext* cx = danger::GetJSContext();
1040
1041 if (!nsContentUtils::XPConnect() || !cx) {
1042 return;
1043 }
1044
1045 if (sScheduler.InIncrementalGC() && wantIncremental) {
1046 // We're in the middle of incremental GC. Do another slice.
1047 JS::PrepareForIncrementalGC(cx);
1048 JS::IncrementalGCSlice(cx, aReason, aBudget);
1049 return;
1050 }
1051
1052 JS::GCOptions options = aShrinking == nsJSContext::ShrinkingGC
1053 ? JS::GCOptions::Shrink
1054 : JS::GCOptions::Normal;
1055
1056 if (!wantIncremental || aReason == JS::GCReason::FULL_GC_TIMER) {
1057 sScheduler.SetNeedsFullGC();
1058 }
1059
1060 if (sScheduler.NeedsFullGC()) {
1061 JS::PrepareForFullGC(cx);
1062 }
1063
1064 if (wantIncremental) {
1065 // Incremental GC slices will be triggered by the GC Runner. If one doesn't
1066 // already exist, create it in the GC_SLICE_END callback for the first
1067 // slice being executed here.
1068 JS::StartIncrementalGC(cx, options, aReason, aBudget);
1069 } else {
1070 JS::NonIncrementalGC(cx, options, aReason);
1071 }
1072 }
1073
1074 // static
GarbageCollectNow(JS::GCReason aReason,IsShrinking aShrinking)1075 void nsJSContext::GarbageCollectNow(JS::GCReason aReason,
1076 IsShrinking aShrinking) {
1077 GarbageCollectImpl(aReason, aShrinking, js::SliceBudget::unlimited());
1078 }
1079
1080 // static
RunIncrementalGCSlice(JS::GCReason aReason,IsShrinking aShrinking,js::SliceBudget & aBudget)1081 void nsJSContext::RunIncrementalGCSlice(JS::GCReason aReason,
1082 IsShrinking aShrinking,
1083 js::SliceBudget& aBudget) {
1084 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental GC", GCCC);
1085 GarbageCollectImpl(aReason, aShrinking, aBudget);
1086 }
1087
FinishAnyIncrementalGC()1088 static void FinishAnyIncrementalGC() {
1089 AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC);
1090
1091 if (sScheduler.InIncrementalGC()) {
1092 AutoJSAPI jsapi;
1093 jsapi.Init();
1094
1095 // We're in the middle of an incremental GC, so finish it.
1096 JS::PrepareForIncrementalGC(jsapi.cx());
1097 JS::FinishIncrementalGC(jsapi.cx(), JS::GCReason::CC_FORCED);
1098 }
1099 }
1100
FireForgetSkippable(bool aRemoveChildless,TimeStamp aDeadline)1101 static void FireForgetSkippable(bool aRemoveChildless, TimeStamp aDeadline) {
1102 AUTO_PROFILER_MARKER_TEXT("ForgetSkippable", GCCC, {},
1103 aDeadline.IsNull() ? ""_ns : "(idle)"_ns);
1104 TimeStamp startTimeStamp = TimeStamp::Now();
1105 FinishAnyIncrementalGC();
1106
1107 uint32_t suspectedBefore = nsCycleCollector_suspectedCount();
1108 js::SliceBudget budget =
1109 sScheduler.ComputeForgetSkippableBudget(startTimeStamp, aDeadline);
1110 bool earlyForgetSkippable = sScheduler.IsEarlyForgetSkippable();
1111 nsCycleCollector_forgetSkippable(budget, aRemoveChildless,
1112 earlyForgetSkippable);
1113 TimeStamp now = TimeStamp::Now();
1114 uint32_t removedPurples = sScheduler.NoteForgetSkippableComplete(
1115 now, suspectedBefore, nsCycleCollector_suspectedCount());
1116
1117 TimeDuration duration = now - startTimeStamp;
1118
1119 sCCStats.AfterForgetSkippable(duration, removedPurples);
1120
1121 if (duration.ToSeconds()) {
1122 TimeDuration idleDuration;
1123 if (!aDeadline.IsNull()) {
1124 if (aDeadline < now) {
1125 // This slice overflowed the idle period.
1126 if (aDeadline > startTimeStamp) {
1127 idleDuration = aDeadline - startTimeStamp;
1128 }
1129 } else {
1130 idleDuration = duration;
1131 }
1132 }
1133
1134 uint32_t percent =
1135 uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
1136 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE, percent);
1137 }
1138 }
1139
1140 MOZ_ALWAYS_INLINE
TimeBetween(TimeStamp aStart,TimeStamp aEnd)1141 static TimeDuration TimeBetween(TimeStamp aStart, TimeStamp aEnd) {
1142 MOZ_ASSERT(aEnd >= aStart);
1143 return aEnd - aStart;
1144 }
1145
TimeUntilNow(TimeStamp start)1146 static TimeDuration TimeUntilNow(TimeStamp start) {
1147 if (start.IsNull()) {
1148 return TimeDuration();
1149 }
1150 return TimeBetween(start, TimeStamp::Now());
1151 }
1152
Init()1153 void CycleCollectorStats::Init() {
1154 Clear();
1155
1156 char* env = getenv("MOZ_CCTIMER");
1157 if (!env) {
1158 return;
1159 }
1160 if (strcmp(env, "none") == 0) {
1161 mFile = nullptr;
1162 } else if (strcmp(env, "stdout") == 0) {
1163 mFile = stdout;
1164 } else if (strcmp(env, "stderr") == 0) {
1165 mFile = stderr;
1166 } else {
1167 mFile = fopen(env, "a");
1168 if (!mFile) {
1169 MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
1170 }
1171 }
1172 }
1173
Clear()1174 void CycleCollectorStats::Clear() {
1175 if (mFile && mFile != stdout && mFile != stderr) {
1176 fclose(mFile);
1177 }
1178 *this = CycleCollectorStats();
1179 }
1180
AfterCycleCollectionSlice()1181 void CycleCollectorStats::AfterCycleCollectionSlice() {
1182 if (mBeginSliceTime.IsNull()) {
1183 // We already called this method from EndCycleCollectionCallback for this
1184 // slice.
1185 return;
1186 }
1187
1188 mEndSliceTime = TimeStamp::Now();
1189 TimeDuration duration = mEndSliceTime - mBeginSliceTime;
1190
1191 if (duration.ToSeconds()) {
1192 TimeDuration idleDuration;
1193 if (!mIdleDeadline.IsNull()) {
1194 if (mIdleDeadline < mEndSliceTime) {
1195 // This slice overflowed the idle period.
1196 if (mIdleDeadline > mBeginSliceTime) {
1197 idleDuration = mIdleDeadline - mBeginSliceTime;
1198 }
1199 } else {
1200 idleDuration = duration;
1201 }
1202 }
1203
1204 uint32_t percent =
1205 uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
1206 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE,
1207 percent);
1208 }
1209
1210 TimeDuration sliceTime = TimeBetween(mBeginSliceTime, mEndSliceTime);
1211 mMaxSliceTime = std::max(mMaxSliceTime, sliceTime);
1212 mMaxSliceTimeSinceClear = std::max(mMaxSliceTimeSinceClear, sliceTime);
1213 mTotalSliceTime += sliceTime;
1214 mBeginSliceTime = TimeStamp();
1215 }
1216
AfterPrepareForCycleCollectionSlice(TimeStamp aDeadline,TimeStamp aBeginTime,TimeStamp aMaybeAfterGCTime)1217 void CycleCollectorStats::AfterPrepareForCycleCollectionSlice(
1218 TimeStamp aDeadline, TimeStamp aBeginTime, TimeStamp aMaybeAfterGCTime) {
1219 mBeginSliceTime = aBeginTime;
1220 mIdleDeadline = aDeadline;
1221
1222 if (!aMaybeAfterGCTime.IsNull()) {
1223 mAnyLockedOut = true;
1224 mMaxGCDuration = std::max(mMaxGCDuration, aMaybeAfterGCTime - aBeginTime);
1225 }
1226 }
1227
AfterSyncForgetSkippable(TimeStamp beginTime)1228 void CycleCollectorStats::AfterSyncForgetSkippable(TimeStamp beginTime) {
1229 mMaxSkippableDuration =
1230 std::max(mMaxSkippableDuration, TimeUntilNow(beginTime));
1231 mRanSyncForgetSkippable = true;
1232 }
1233
AfterForgetSkippable(TimeDuration duration,uint32_t aRemovedPurples)1234 void CycleCollectorStats::AfterForgetSkippable(TimeDuration duration,
1235 uint32_t aRemovedPurples) {
1236 if (!mMinForgetSkippableTime || mMinForgetSkippableTime > duration) {
1237 mMinForgetSkippableTime = duration;
1238 }
1239 if (!mMaxForgetSkippableTime || mMaxForgetSkippableTime < duration) {
1240 mMaxForgetSkippableTime = duration;
1241 }
1242 mTotalForgetSkippableTime += duration;
1243 ++mForgetSkippableBeforeCC;
1244
1245 mRemovedPurples += aRemovedPurples;
1246 }
1247
SendTelemetry(TimeDuration aCCNowDuration) const1248 void CycleCollectorStats::SendTelemetry(TimeDuration aCCNowDuration) const {
1249 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, mAnyLockedOut);
1250 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE,
1251 mRanSyncForgetSkippable);
1252 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL,
1253 aCCNowDuration.ToMilliseconds());
1254 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE,
1255 mMaxSliceTime.ToMilliseconds());
1256
1257 TimeStamp lastCCEndTime = sScheduler.GetLastCCEndTime();
1258 if (!lastCCEndTime.IsNull()) {
1259 TimeDuration timeBetween = TimeBetween(lastCCEndTime, mBeginTime);
1260 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN,
1261 timeBetween.ToSeconds());
1262 }
1263
1264 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
1265 mMaxForgetSkippableTime.ToMilliseconds());
1266 }
1267
MaybeLogStats(const CycleCollectorResults & aResults,uint32_t aCleanups) const1268 void CycleCollectorStats::MaybeLogStats(const CycleCollectorResults& aResults,
1269 uint32_t aCleanups) const {
1270 if (!StaticPrefs::javascript_options_mem_log() && !sCCStats.mFile) {
1271 return;
1272 }
1273
1274 TimeDuration delta = GetCollectionTimeDelta();
1275
1276 nsCString mergeMsg;
1277 if (aResults.mMergedZones) {
1278 mergeMsg.AssignLiteral(" merged");
1279 }
1280
1281 nsCString gcMsg;
1282 if (aResults.mForcedGC) {
1283 gcMsg.AssignLiteral(", forced a GC");
1284 }
1285
1286 const char16_t* kFmt =
1287 u"CC(T+%.1f)[%s-%i] max pause: %.fms, total time: %.fms, slices: %lu, "
1288 u"suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu "
1289 u"RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
1290 u"ForgetSkippable %lu times before CC, min: %.f ms, max: %.f ms, avg: "
1291 u"%.f ms, total: %.f ms, max sync: %.f ms, removed: %lu";
1292 nsString msg;
1293 nsTextFormatter::ssprintf(
1294 msg, kFmt, delta.ToMicroseconds() / PR_USEC_PER_SEC,
1295 ProcessNameForCollectorLog(), getpid(), mMaxSliceTime.ToMilliseconds(),
1296 mTotalSliceTime.ToMilliseconds(), aResults.mNumSlices, mSuspected,
1297 aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
1298 aResults.mFreedRefCounted, aResults.mFreedGCed,
1299 sScheduler.mCCollectedWaitingForGC,
1300 sScheduler.mCCollectedZonesWaitingForGC,
1301 sScheduler.mLikelyShortLivingObjectsNeedingGC, gcMsg.get(),
1302 mForgetSkippableBeforeCC, mMinForgetSkippableTime.ToMilliseconds(),
1303 mMaxForgetSkippableTime.ToMilliseconds(),
1304 mTotalForgetSkippableTime.ToMilliseconds() / aCleanups,
1305 mTotalForgetSkippableTime.ToMilliseconds(),
1306 mMaxSkippableDuration.ToMilliseconds(), mRemovedPurples);
1307 if (StaticPrefs::javascript_options_mem_log()) {
1308 nsCOMPtr<nsIConsoleService> cs =
1309 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1310 if (cs) {
1311 cs->LogStringMessage(msg.get());
1312 }
1313 }
1314 if (mFile) {
1315 fprintf(mFile, "%s\n", NS_ConvertUTF16toUTF8(msg).get());
1316 }
1317 }
1318
MaybeNotifyStats(const CycleCollectorResults & aResults,TimeDuration aCCNowDuration,uint32_t aCleanups) const1319 void CycleCollectorStats::MaybeNotifyStats(
1320 const CycleCollectorResults& aResults, TimeDuration aCCNowDuration,
1321 uint32_t aCleanups) const {
1322 if (!StaticPrefs::javascript_options_mem_notify()) {
1323 return;
1324 }
1325
1326 const char16_t* kJSONFmt =
1327 u"{ \"timestamp\": %llu, "
1328 u"\"duration\": %.f, "
1329 u"\"max_slice_pause\": %.f, "
1330 u"\"total_slice_pause\": %.f, "
1331 u"\"max_finish_gc_duration\": %.f, "
1332 u"\"max_sync_skippable_duration\": %.f, "
1333 u"\"suspected\": %lu, "
1334 u"\"visited\": { "
1335 u"\"RCed\": %lu, "
1336 u"\"GCed\": %lu }, "
1337 u"\"collected\": { "
1338 u"\"RCed\": %lu, "
1339 u"\"GCed\": %lu }, "
1340 u"\"waiting_for_gc\": %lu, "
1341 u"\"zones_waiting_for_gc\": %lu, "
1342 u"\"short_living_objects_waiting_for_gc\": %lu, "
1343 u"\"forced_gc\": %d, "
1344 u"\"forget_skippable\": { "
1345 u"\"times_before_cc\": %lu, "
1346 u"\"min\": %.f, "
1347 u"\"max\": %.f, "
1348 u"\"avg\": %.f, "
1349 u"\"total\": %.f, "
1350 u"\"removed\": %lu } "
1351 u"}";
1352
1353 nsString json;
1354 nsTextFormatter::ssprintf(
1355 json, kJSONFmt, PR_Now(), aCCNowDuration.ToMilliseconds(),
1356 mMaxSliceTime.ToMilliseconds(), mTotalSliceTime.ToMilliseconds(),
1357 mMaxGCDuration.ToMilliseconds(), mMaxSkippableDuration.ToMilliseconds(),
1358 mSuspected, aResults.mVisitedRefCounted, aResults.mVisitedGCed,
1359 aResults.mFreedRefCounted, aResults.mFreedGCed,
1360 sScheduler.mCCollectedWaitingForGC,
1361 sScheduler.mCCollectedZonesWaitingForGC,
1362 sScheduler.mLikelyShortLivingObjectsNeedingGC, aResults.mForcedGC,
1363 mForgetSkippableBeforeCC, mMinForgetSkippableTime.ToMilliseconds(),
1364 mMaxForgetSkippableTime.ToMilliseconds(),
1365 mTotalForgetSkippableTime.ToMilliseconds() / aCleanups,
1366 mTotalForgetSkippableTime.ToMilliseconds(), mRemovedPurples);
1367 nsCOMPtr<nsIObserverService> observerService =
1368 mozilla::services::GetObserverService();
1369 if (observerService) {
1370 observerService->NotifyObservers(nullptr, "cycle-collection-statistics",
1371 json.get());
1372 }
1373 }
1374
1375 // static
CycleCollectNow(CCReason aReason,nsICycleCollectorListener * aListener)1376 void nsJSContext::CycleCollectNow(CCReason aReason,
1377 nsICycleCollectorListener* aListener) {
1378 if (!NS_IsMainThread()) {
1379 return;
1380 }
1381
1382 AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC);
1383
1384 PrepareForCycleCollectionSlice(aReason, TimeStamp());
1385 nsCycleCollector_collect(aReason, aListener);
1386 sCCStats.AfterCycleCollectionSlice();
1387 }
1388
1389 // static
PrepareForCycleCollectionSlice(CCReason aReason,TimeStamp aDeadline)1390 void nsJSContext::PrepareForCycleCollectionSlice(CCReason aReason,
1391 TimeStamp aDeadline) {
1392 TimeStamp beginTime = TimeStamp::Now();
1393
1394 // Before we begin the cycle collection, make sure there is no active GC.
1395 TimeStamp afterGCTime;
1396 if (sScheduler.InIncrementalGC()) {
1397 FinishAnyIncrementalGC();
1398 afterGCTime = TimeStamp::Now();
1399 }
1400
1401 if (!sScheduler.IsCollectingCycles()) {
1402 sScheduler.NoteCCBegin(aReason, beginTime);
1403 }
1404
1405 sCCStats.AfterPrepareForCycleCollectionSlice(aDeadline, beginTime,
1406 afterGCTime);
1407 }
1408
1409 // static
RunCycleCollectorSlice(CCReason aReason,TimeStamp aDeadline)1410 void nsJSContext::RunCycleCollectorSlice(CCReason aReason,
1411 TimeStamp aDeadline) {
1412 if (!NS_IsMainThread()) {
1413 return;
1414 }
1415
1416 AUTO_PROFILER_MARKER_TEXT("CCSlice", GCCC, {},
1417 aDeadline.IsNull() ? ""_ns : "(idle)"_ns);
1418
1419 PrepareForCycleCollectionSlice(aReason, aDeadline);
1420
1421 // Decide how long we want to budget for this slice.
1422 if (sIncrementalCC) {
1423 bool preferShorterSlices;
1424 js::SliceBudget budget = sScheduler.ComputeCCSliceBudget(
1425 aDeadline, sCCStats.mBeginTime, sCCStats.mEndSliceTime,
1426 TimeStamp::Now(), &preferShorterSlices);
1427 nsCycleCollector_collectSlice(budget, aReason, preferShorterSlices);
1428 } else {
1429 js::SliceBudget budget = js::SliceBudget::unlimited();
1430 nsCycleCollector_collectSlice(budget, aReason, false);
1431 }
1432
1433 sCCStats.AfterCycleCollectionSlice();
1434 }
1435
1436 // static
RunCycleCollectorWorkSlice(int64_t aWorkBudget)1437 void nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget) {
1438 if (!NS_IsMainThread()) {
1439 return;
1440 }
1441
1442 AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", GCCC);
1443
1444 PrepareForCycleCollectionSlice(CCReason::API, TimeStamp());
1445
1446 js::SliceBudget budget = js::SliceBudget(js::WorkBudget(aWorkBudget));
1447 nsCycleCollector_collectSlice(budget, CCReason::API);
1448
1449 sCCStats.AfterCycleCollectionSlice();
1450 }
1451
ClearMaxCCSliceTime()1452 void nsJSContext::ClearMaxCCSliceTime() {
1453 sCCStats.mMaxSliceTimeSinceClear = TimeDuration();
1454 }
1455
GetMaxCCSliceTimeSinceClear()1456 uint32_t nsJSContext::GetMaxCCSliceTimeSinceClear() {
1457 return sCCStats.mMaxSliceTimeSinceClear.ToMilliseconds();
1458 }
1459
1460 // static
BeginCycleCollectionCallback(CCReason aReason)1461 void nsJSContext::BeginCycleCollectionCallback(CCReason aReason) {
1462 MOZ_ASSERT(NS_IsMainThread());
1463
1464 TimeStamp startTime = TimeStamp::Now();
1465 sCCStats.mBeginTime =
1466 sCCStats.mBeginSliceTime.IsNull() ? startTime : sCCStats.mBeginSliceTime;
1467 sCCStats.mSuspected = nsCycleCollector_suspectedCount();
1468
1469 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1470 // is particularly useful if we recently finished a GC.
1471 if (sScheduler.IsEarlyForgetSkippable()) {
1472 while (sScheduler.IsEarlyForgetSkippable()) {
1473 FireForgetSkippable(false, TimeStamp());
1474 }
1475 sCCStats.AfterSyncForgetSkippable(startTime);
1476 }
1477
1478 if (sShuttingDown) {
1479 return;
1480 }
1481
1482 sScheduler.InitCCRunnerStateMachine(
1483 mozilla::CCGCScheduler::CCRunnerState::CycleCollecting, aReason);
1484 sScheduler.EnsureCCRunner(kICCIntersliceDelay, kIdleICCSliceBudget);
1485 }
1486
1487 // static
EndCycleCollectionCallback(CycleCollectorResults & aResults)1488 void nsJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults) {
1489 MOZ_ASSERT(NS_IsMainThread());
1490
1491 sScheduler.KillCCRunner();
1492
1493 // Update timing information for the current slice before we log it, if
1494 // we previously called PrepareForCycleCollectionSlice(). During shutdown
1495 // CCs, this won't happen.
1496 sCCStats.AfterCycleCollectionSlice();
1497 sScheduler.NoteCycleCollected(aResults);
1498
1499 TimeStamp endCCTimeStamp = TimeStamp::Now();
1500 TimeDuration ccNowDuration = TimeBetween(sCCStats.mBeginTime, endCCTimeStamp);
1501
1502 if (sScheduler.NeedsGCAfterCC()) {
1503 MOZ_ASSERT(
1504 TimeDuration::FromMilliseconds(
1505 StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration,
1506 "A max duration ICC shouldn't reduce GC delay to 0");
1507
1508 sScheduler.PokeGC(JS::GCReason::CC_FINISHED, nullptr,
1509 TimeDuration::FromMilliseconds(
1510 StaticPrefs::javascript_options_gc_delay()) -
1511 std::min(ccNowDuration, kMaxICCDuration));
1512 }
1513
1514 // Log information about the CC via telemetry, JSON and the console.
1515
1516 sCCStats.SendTelemetry(ccNowDuration);
1517
1518 uint32_t cleanups = std::max(sCCStats.mForgetSkippableBeforeCC, 1u);
1519
1520 sCCStats.MaybeLogStats(aResults, cleanups);
1521
1522 sCCStats.MaybeNotifyStats(aResults, ccNowDuration, cleanups);
1523
1524 // Update global state to indicate we have just run a cycle collection.
1525 sScheduler.NoteCCEnd(endCCTimeStamp);
1526 sCCStats.Clear();
1527 }
1528
1529 /* static */
CCRunnerFired(TimeStamp aDeadline)1530 bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline) {
1531 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental CC", GCCC);
1532
1533 bool didDoWork = false;
1534
1535 // The CC/GC scheduler (sScheduler) decides what action(s) to take during
1536 // this invocation of the CC runner.
1537 //
1538 // This may be zero, one, or multiple actions. (Zero is when CC is blocked by
1539 // incremental GC, or when the scheduler determined that a CC is no longer
1540 // needed.) Loop until the scheduler finishes this invocation by returning
1541 // `Yield` in step.mYield.
1542 CCRunnerStep step;
1543 do {
1544 step = sScheduler.AdvanceCCRunner(aDeadline, TimeStamp::Now(),
1545 nsCycleCollector_suspectedCount());
1546 switch (step.mAction) {
1547 case CCRunnerAction::None:
1548 break;
1549
1550 case CCRunnerAction::ForgetSkippable:
1551 // 'Forget skippable' only, then end this invocation.
1552 FireForgetSkippable(bool(step.mRemoveChildless), aDeadline);
1553 break;
1554
1555 case CCRunnerAction::CleanupContentUnbinder:
1556 // Clear content unbinder before the first actual CC slice.
1557 Element::ClearContentUnbinder();
1558 break;
1559
1560 case CCRunnerAction::CleanupDeferred:
1561 // and if time still permits, perform deferred deletions.
1562 nsCycleCollector_doDeferredDeletion();
1563 break;
1564
1565 case CCRunnerAction::CycleCollect:
1566 // Cycle collection slice.
1567 nsJSContext::RunCycleCollectorSlice(step.mCCReason, aDeadline);
1568 break;
1569
1570 case CCRunnerAction::StopRunning:
1571 // End this CC, either because we have run a cycle collection slice, or
1572 // because a CC is no longer needed.
1573 sScheduler.KillCCRunner();
1574 break;
1575 }
1576
1577 if (step.mAction != CCRunnerAction::None) {
1578 didDoWork = true;
1579 }
1580 } while (step.mYield == CCRunnerYield::Continue);
1581
1582 return didDoWork;
1583 }
1584
1585 // static
HasHadCleanupSinceLastGC()1586 bool nsJSContext::HasHadCleanupSinceLastGC() {
1587 return sScheduler.IsEarlyForgetSkippable(1);
1588 }
1589
1590 // static
RunNextCollectorTimer(JS::GCReason aReason,mozilla::TimeStamp aDeadline)1591 void nsJSContext::RunNextCollectorTimer(JS::GCReason aReason,
1592 mozilla::TimeStamp aDeadline) {
1593 sScheduler.RunNextCollectorTimer(aReason, aDeadline);
1594 }
1595
1596 // static
MaybeRunNextCollectorSlice(nsIDocShell * aDocShell,JS::GCReason aReason)1597 void nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
1598 JS::GCReason aReason) {
1599 if (!aDocShell || !XRE_IsContentProcess()) {
1600 return;
1601 }
1602
1603 BrowsingContext* bc = aDocShell->GetBrowsingContext();
1604 if (!bc) {
1605 return;
1606 }
1607
1608 BrowsingContext* root = bc->Top();
1609 if (bc == root) {
1610 // We don't want to run collectors when loading the top level page.
1611 return;
1612 }
1613
1614 nsIDocShell* rootDocShell = root->GetDocShell();
1615 if (!rootDocShell) {
1616 return;
1617 }
1618
1619 Document* rootDocument = rootDocShell->GetDocument();
1620 if (!rootDocument ||
1621 rootDocument->GetReadyStateEnum() != Document::READYSTATE_COMPLETE ||
1622 rootDocument->IsInBackgroundWindow()) {
1623 return;
1624 }
1625
1626 PresShell* presShell = rootDocument->GetPresShell();
1627 if (!presShell) {
1628 return;
1629 }
1630
1631 nsViewManager* vm = presShell->GetViewManager();
1632 if (!vm) {
1633 return;
1634 }
1635
1636 if (!sScheduler.IsUserActive()) {
1637 Maybe<TimeStamp> next = nsRefreshDriver::GetNextTickHint();
1638 // Try to not delay the next RefreshDriver tick, so give a reasonable
1639 // deadline for collectors.
1640 if (next.isSome()) {
1641 sScheduler.RunNextCollectorTimer(aReason, next.value());
1642 }
1643 }
1644 }
1645
1646 // static
PokeGC(JS::GCReason aReason,JSObject * aObj,TimeDuration aDelay)1647 void nsJSContext::PokeGC(JS::GCReason aReason, JSObject* aObj,
1648 TimeDuration aDelay) {
1649 sScheduler.PokeGC(aReason, aObj, aDelay);
1650 }
1651
1652 // static
DoLowMemoryGC()1653 void nsJSContext::DoLowMemoryGC() {
1654 if (sShuttingDown) {
1655 return;
1656 }
1657 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE,
1658 nsJSContext::ShrinkingGC);
1659 nsJSContext::CycleCollectNow(CCReason::MEM_PRESSURE);
1660 if (sScheduler.NeedsGCAfterCC()) {
1661 nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE,
1662 nsJSContext::ShrinkingGC);
1663 }
1664 }
1665
1666 // static
LowMemoryGC()1667 void nsJSContext::LowMemoryGC() {
1668 RefPtr<CCGCScheduler::MayGCPromise> mbPromise =
1669 CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE);
1670 if (!mbPromise) {
1671 // Normally when the promise is null it means that IPC failed, that probably
1672 // means that something bad happened, don't bother with the GC.
1673 return;
1674 }
1675 mbPromise->Then(
1676 GetMainThreadSerialEventTarget(), __func__,
1677 [](bool aIgnored) { DoLowMemoryGC(); },
1678 [](mozilla::ipc::ResponseRejectReason r) {});
1679 }
1680
1681 // static
MaybePokeCC()1682 void nsJSContext::MaybePokeCC() {
1683 sScheduler.MaybePokeCC(TimeStamp::Now(), nsCycleCollector_suspectedCount());
1684 }
1685
DOMGCSliceCallback(JSContext * aCx,JS::GCProgress aProgress,const JS::GCDescription & aDesc)1686 static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
1687 const JS::GCDescription& aDesc) {
1688 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
1689
1690 static TimeStamp sCurrentGCStartTime;
1691
1692 switch (aProgress) {
1693 case JS::GC_CYCLE_BEGIN: {
1694 // Prevent cycle collections and shrinking during incremental GC.
1695 sScheduler.NoteGCBegin();
1696 sCurrentGCStartTime = TimeStamp::Now();
1697 break;
1698 }
1699
1700 case JS::GC_CYCLE_END: {
1701 TimeDuration delta = GetCollectionTimeDelta();
1702
1703 if (StaticPrefs::javascript_options_mem_log()) {
1704 nsString gcstats;
1705 gcstats.Adopt(aDesc.formatSummaryMessage(aCx));
1706 nsAutoString prefix;
1707 nsTextFormatter::ssprintf(prefix, u"GC(T+%.1f)[%s-%i] ",
1708 delta.ToSeconds(),
1709 ProcessNameForCollectorLog(), getpid());
1710 nsString msg = prefix + gcstats;
1711 nsCOMPtr<nsIConsoleService> cs =
1712 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1713 if (cs) {
1714 cs->LogStringMessage(msg.get());
1715 }
1716 }
1717
1718 sScheduler.NoteGCEnd();
1719
1720 // May need to kill the GC runner
1721 sScheduler.KillGCRunner();
1722
1723 TimeStamp now = TimeStamp::Now();
1724 sScheduler.MaybePokeCC(now, nsCycleCollector_suspectedCount());
1725
1726 if (aDesc.isZone_) {
1727 sScheduler.PokeFullGC();
1728 } else {
1729 sScheduler.SetNeedsFullGC(false);
1730 sScheduler.KillFullGCTimer();
1731 }
1732
1733 if (sScheduler.IsCCNeeded(now, nsCycleCollector_suspectedCount()) !=
1734 CCReason::NO_REASON) {
1735 nsCycleCollector_dispatchDeferredDeletion();
1736 }
1737
1738 Telemetry::Accumulate(Telemetry::GC_IN_PROGRESS_MS,
1739 TimeUntilNow(sCurrentGCStartTime).ToMilliseconds());
1740 break;
1741 }
1742
1743 case JS::GC_SLICE_BEGIN:
1744 break;
1745
1746 case JS::GC_SLICE_END:
1747 sScheduler.NoteGCSliceEnd(aDesc.lastSliceEnd(aCx) -
1748 aDesc.lastSliceStart(aCx));
1749
1750 if (sShuttingDown) {
1751 sScheduler.KillGCRunner();
1752 } else {
1753 // If incremental GC wasn't triggered by GCTimerFired, we may not have a
1754 // runner to ensure all the slices are handled. So, create the runner
1755 // here.
1756 sScheduler.EnsureGCRunner(0);
1757 }
1758
1759 if (sScheduler.IsCCNeeded(TimeStamp::Now(),
1760 nsCycleCollector_suspectedCount()) !=
1761 CCReason::NO_REASON) {
1762 nsCycleCollector_dispatchDeferredDeletion();
1763 }
1764
1765 if (StaticPrefs::javascript_options_mem_log()) {
1766 nsString gcstats;
1767 gcstats.Adopt(aDesc.formatSliceMessage(aCx));
1768 nsAutoString prefix;
1769 nsTextFormatter::ssprintf(prefix, u"[%s-%i] ",
1770 ProcessNameForCollectorLog(), getpid());
1771 nsString msg = prefix + gcstats;
1772 nsCOMPtr<nsIConsoleService> cs =
1773 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1774 if (cs) {
1775 cs->LogStringMessage(msg.get());
1776 }
1777 }
1778
1779 break;
1780
1781 default:
1782 MOZ_CRASH("Unexpected GCProgress value");
1783 }
1784
1785 if (sPrevGCSliceCallback) {
1786 (*sPrevGCSliceCallback)(aCx, aProgress, aDesc);
1787 }
1788 }
1789
SetWindowProxy(JS::Handle<JSObject * > aWindowProxy)1790 void nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
1791 mWindowProxy = aWindowProxy;
1792 }
1793
GetWindowProxy()1794 JSObject* nsJSContext::GetWindowProxy() { return mWindowProxy; }
1795
LikelyShortLivingObjectCreated()1796 void nsJSContext::LikelyShortLivingObjectCreated() {
1797 ++sScheduler.mLikelyShortLivingObjectsNeedingGC;
1798 }
1799
StartupJSEnvironment()1800 void mozilla::dom::StartupJSEnvironment() {
1801 // initialize all our statics, so that we can restart XPCOM
1802 sIsInitialized = false;
1803 sShuttingDown = false;
1804 new (&sScheduler) CCGCScheduler(); // Reset the scheduler state.
1805 sCCStats.Init();
1806 }
1807
SetGCParameter(JSGCParamKey aParam,uint32_t aValue)1808 static void SetGCParameter(JSGCParamKey aParam, uint32_t aValue) {
1809 AutoJSAPI jsapi;
1810 jsapi.Init();
1811 JS_SetGCParameter(jsapi.cx(), aParam, aValue);
1812 }
1813
ResetGCParameter(JSGCParamKey aParam)1814 static void ResetGCParameter(JSGCParamKey aParam) {
1815 AutoJSAPI jsapi;
1816 jsapi.Init();
1817 JS_ResetGCParameter(jsapi.cx(), aParam);
1818 }
1819
SetMemoryPrefChangedCallbackMB(const char * aPrefName,void * aClosure)1820 static void SetMemoryPrefChangedCallbackMB(const char* aPrefName,
1821 void* aClosure) {
1822 int32_t prefMB = Preferences::GetInt(aPrefName, -1);
1823 // handle overflow and negative pref values
1824 CheckedInt<int32_t> prefB = CheckedInt<int32_t>(prefMB) * 1024 * 1024;
1825 if (prefB.isValid() && prefB.value() >= 0) {
1826 SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, prefB.value());
1827 } else {
1828 ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
1829 }
1830 }
1831
SetMemoryNurseryPrefChangedCallback(const char * aPrefName,void * aClosure)1832 static void SetMemoryNurseryPrefChangedCallback(const char* aPrefName,
1833 void* aClosure) {
1834 int32_t prefKB = Preferences::GetInt(aPrefName, -1);
1835 // handle overflow and negative pref values
1836 CheckedInt<int32_t> prefB = CheckedInt<int32_t>(prefKB) * 1024;
1837 if (prefB.isValid() && prefB.value() >= 0) {
1838 SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, prefB.value());
1839 } else {
1840 ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
1841 }
1842 }
1843
SetMemoryPrefChangedCallbackInt(const char * aPrefName,void * aClosure)1844 static void SetMemoryPrefChangedCallbackInt(const char* aPrefName,
1845 void* aClosure) {
1846 int32_t pref = Preferences::GetInt(aPrefName, -1);
1847 // handle overflow and negative pref values
1848 if (pref >= 0 && pref < 10000) {
1849 SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, pref);
1850 } else {
1851 ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
1852 }
1853 }
1854
SetMemoryPrefChangedCallbackBool(const char * aPrefName,void * aClosure)1855 static void SetMemoryPrefChangedCallbackBool(const char* aPrefName,
1856 void* aClosure) {
1857 bool pref = Preferences::GetBool(aPrefName);
1858 SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, pref);
1859 }
1860
SetMemoryGCSliceTimePrefChangedCallback(const char * aPrefName,void * aClosure)1861 static void SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName,
1862 void* aClosure) {
1863 int32_t pref = Preferences::GetInt(aPrefName, -1);
1864 // handle overflow and negative pref values
1865 if (pref > 0 && pref < 100000) {
1866 sScheduler.SetActiveIntersliceGCBudget(
1867 TimeDuration::FromMilliseconds(pref));
1868 SetGCParameter(JSGC_SLICE_TIME_BUDGET_MS, pref);
1869 } else {
1870 ResetGCParameter(JSGC_SLICE_TIME_BUDGET_MS);
1871 }
1872 }
1873
SetIncrementalCCPrefChangedCallback(const char * aPrefName,void * aClosure)1874 static void SetIncrementalCCPrefChangedCallback(const char* aPrefName,
1875 void* aClosure) {
1876 bool pref = Preferences::GetBool(aPrefName);
1877 sIncrementalCC = pref;
1878 }
1879
1880 class JSDispatchableRunnable final : public Runnable {
~JSDispatchableRunnable()1881 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
1882
1883 public:
JSDispatchableRunnable(JS::Dispatchable * aDispatchable)1884 explicit JSDispatchableRunnable(JS::Dispatchable* aDispatchable)
1885 : mozilla::Runnable("JSDispatchableRunnable"),
1886 mDispatchable(aDispatchable) {
1887 MOZ_ASSERT(mDispatchable);
1888 }
1889
1890 protected:
Run()1891 NS_IMETHOD Run() override {
1892 MOZ_ASSERT(NS_IsMainThread());
1893
1894 AutoJSAPI jsapi;
1895 jsapi.Init();
1896
1897 JS::Dispatchable::MaybeShuttingDown maybeShuttingDown =
1898 sShuttingDown ? JS::Dispatchable::ShuttingDown
1899 : JS::Dispatchable::NotShuttingDown;
1900
1901 mDispatchable->run(jsapi.cx(), maybeShuttingDown);
1902 mDispatchable = nullptr; // mDispatchable may delete itself
1903
1904 return NS_OK;
1905 }
1906
1907 private:
1908 JS::Dispatchable* mDispatchable;
1909 };
1910
DispatchToEventLoop(void * closure,JS::Dispatchable * aDispatchable)1911 static bool DispatchToEventLoop(void* closure,
1912 JS::Dispatchable* aDispatchable) {
1913 MOZ_ASSERT(!closure);
1914
1915 // This callback may execute either on the main thread or a random JS-internal
1916 // helper thread. This callback can be called during shutdown so we cannot
1917 // simply NS_DispatchToMainThread. Failure during shutdown is expected and
1918 // properly handled by the JS engine.
1919
1920 nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
1921 if (!mainTarget) {
1922 return false;
1923 }
1924
1925 RefPtr<JSDispatchableRunnable> r = new JSDispatchableRunnable(aDispatchable);
1926 MOZ_ALWAYS_SUCCEEDS(mainTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
1927 return true;
1928 }
1929
ConsumeStream(JSContext * aCx,JS::HandleObject aObj,JS::MimeType aMimeType,JS::StreamConsumer * aConsumer)1930 static bool ConsumeStream(JSContext* aCx, JS::HandleObject aObj,
1931 JS::MimeType aMimeType,
1932 JS::StreamConsumer* aConsumer) {
1933 return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer,
1934 nullptr);
1935 }
1936
CreateGCSliceBudget(JS::GCReason aReason,int64_t aMillis)1937 static js::SliceBudget CreateGCSliceBudget(JS::GCReason aReason,
1938 int64_t aMillis) {
1939 return sScheduler.CreateGCSliceBudget(
1940 mozilla::TimeDuration::FromMilliseconds(aMillis), false, false);
1941 }
1942
EnsureStatics()1943 void nsJSContext::EnsureStatics() {
1944 if (sIsInitialized) {
1945 if (!nsContentUtils::XPConnect()) {
1946 MOZ_CRASH();
1947 }
1948 return;
1949 }
1950
1951 // Let's make sure that our main thread is the same as the xpcom main thread.
1952 MOZ_ASSERT(NS_IsMainThread());
1953
1954 AutoJSAPI jsapi;
1955 jsapi.Init();
1956
1957 sPrevGCSliceCallback = JS::SetGCSliceCallback(jsapi.cx(), DOMGCSliceCallback);
1958
1959 JS::SetCreateGCSliceBudgetCallback(jsapi.cx(), CreateGCSliceBudget);
1960
1961 JS::InitDispatchToEventLoop(jsapi.cx(), DispatchToEventLoop, nullptr);
1962 JS::InitConsumeStreamCallback(jsapi.cx(), ConsumeStream,
1963 FetchUtil::ReportJSStreamError);
1964
1965 // Set these global xpconnect options...
1966 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB,
1967 "javascript.options.mem.max",
1968 (void*)JSGC_MAX_BYTES);
1969 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback,
1970 "javascript.options.mem.nursery.min_kb",
1971 (void*)JSGC_MIN_NURSERY_BYTES);
1972 Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback,
1973 "javascript.options.mem.nursery.max_kb",
1974 (void*)JSGC_MAX_NURSERY_BYTES);
1975
1976 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool,
1977 "javascript.options.mem.gc_per_zone",
1978 (void*)JSGC_PER_ZONE_GC_ENABLED);
1979
1980 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool,
1981 "javascript.options.mem.gc_incremental",
1982 (void*)JSGC_INCREMENTAL_GC_ENABLED);
1983
1984 Preferences::RegisterCallbackAndCall(
1985 SetMemoryGCSliceTimePrefChangedCallback,
1986 "javascript.options.mem.gc_incremental_slice_ms");
1987
1988 Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool,
1989 "javascript.options.mem.gc_compacting",
1990 (void*)JSGC_COMPACTING_ENABLED);
1991
1992 Preferences::RegisterCallbackAndCall(
1993 SetMemoryPrefChangedCallbackBool,
1994 "javascript.options.mem.incremental_weakmap",
1995 (void*)JSGC_INCREMENTAL_WEAKMAP_ENABLED);
1996
1997 Preferences::RegisterCallbackAndCall(
1998 SetMemoryPrefChangedCallbackInt,
1999 "javascript.options.mem.gc_high_frequency_time_limit_ms",
2000 (void*)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
2001
2002 Preferences::RegisterCallbackAndCall(
2003 SetMemoryPrefChangedCallbackInt,
2004 "javascript.options.mem.gc_low_frequency_heap_growth",
2005 (void*)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
2006
2007 Preferences::RegisterCallbackAndCall(
2008 SetMemoryPrefChangedCallbackInt,
2009 "javascript.options.mem.gc_high_frequency_large_heap_growth",
2010 (void*)JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH);
2011
2012 Preferences::RegisterCallbackAndCall(
2013 SetMemoryPrefChangedCallbackInt,
2014 "javascript.options.mem.gc_high_frequency_small_heap_growth",
2015 (void*)JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH);
2016
2017 Preferences::RegisterCallbackAndCall(
2018 SetMemoryPrefChangedCallbackInt,
2019 "javascript.options.mem.gc_small_heap_size_max_mb",
2020 (void*)JSGC_SMALL_HEAP_SIZE_MAX);
2021
2022 Preferences::RegisterCallbackAndCall(
2023 SetMemoryPrefChangedCallbackInt,
2024 "javascript.options.mem.gc_large_heap_size_min_mb",
2025 (void*)JSGC_LARGE_HEAP_SIZE_MIN);
2026
2027 Preferences::RegisterCallbackAndCall(
2028 SetMemoryPrefChangedCallbackInt,
2029 "javascript.options.mem.gc_allocation_threshold_mb",
2030 (void*)JSGC_ALLOCATION_THRESHOLD);
2031
2032 Preferences::RegisterCallbackAndCall(
2033 SetMemoryPrefChangedCallbackInt,
2034 "javascript.options.mem.gc_malloc_threshold_base_mb",
2035 (void*)JSGC_MALLOC_THRESHOLD_BASE);
2036
2037 Preferences::RegisterCallbackAndCall(
2038 SetMemoryPrefChangedCallbackInt,
2039 "javascript.options.mem.gc_small_heap_incremental_limit",
2040 (void*)JSGC_SMALL_HEAP_INCREMENTAL_LIMIT);
2041 Preferences::RegisterCallbackAndCall(
2042 SetMemoryPrefChangedCallbackInt,
2043 "javascript.options.mem.gc_large_heap_incremental_limit",
2044 (void*)JSGC_LARGE_HEAP_INCREMENTAL_LIMIT);
2045
2046 Preferences::RegisterCallbackAndCall(
2047 SetMemoryPrefChangedCallbackInt,
2048 "javascript.options.mem.gc_urgent_threshold_mb",
2049 (void*)JSGC_URGENT_THRESHOLD_MB);
2050
2051 Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback,
2052 "dom.cycle_collector.incremental");
2053
2054 Preferences::RegisterCallbackAndCall(
2055 SetMemoryPrefChangedCallbackInt,
2056 "javascript.options.mem.gc_min_empty_chunk_count",
2057 (void*)JSGC_MIN_EMPTY_CHUNK_COUNT);
2058
2059 Preferences::RegisterCallbackAndCall(
2060 SetMemoryPrefChangedCallbackInt,
2061 "javascript.options.mem.gc_max_empty_chunk_count",
2062 (void*)JSGC_MAX_EMPTY_CHUNK_COUNT);
2063
2064 Preferences::RegisterCallbackAndCall(
2065 SetMemoryPrefChangedCallbackInt,
2066 "javascript.options.mem.gc_helper_thread_ratio",
2067 (void*)JSGC_HELPER_THREAD_RATIO);
2068
2069 Preferences::RegisterCallbackAndCall(
2070 SetMemoryPrefChangedCallbackInt,
2071 "javascript.options.mem.gc_max_helper_threads",
2072 (void*)JSGC_MAX_HELPER_THREADS);
2073
2074 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
2075 if (!obs) {
2076 MOZ_CRASH();
2077 }
2078
2079 nsIObserver* observer = new nsJSEnvironmentObserver();
2080 obs->AddObserver(observer, "memory-pressure", false);
2081 obs->AddObserver(observer, "user-interaction-inactive", false);
2082 obs->AddObserver(observer, "user-interaction-active", false);
2083 obs->AddObserver(observer, "quit-application", false);
2084 obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
2085 obs->AddObserver(observer, "content-child-will-shutdown", false);
2086
2087 sIsInitialized = true;
2088 }
2089
ShutdownJSEnvironment()2090 void mozilla::dom::ShutdownJSEnvironment() {
2091 sShuttingDown = true;
2092 sScheduler.Shutdown();
2093 }
2094
AsyncErrorReporter(xpc::ErrorReport * aReport)2095 AsyncErrorReporter::AsyncErrorReporter(xpc::ErrorReport* aReport)
2096 : Runnable("dom::AsyncErrorReporter"), mReport(aReport) {}
2097
SerializeStack(JSContext * aCx,JS::Handle<JSObject * > aStack)2098 void AsyncErrorReporter::SerializeStack(JSContext* aCx,
2099 JS::Handle<JSObject*> aStack) {
2100 mStackHolder = MakeUnique<SerializedStackHolder>();
2101 mStackHolder->SerializeMainThreadOrWorkletStack(aCx, aStack);
2102 }
2103
SetException(JSContext * aCx,JS::Handle<JS::Value> aException)2104 void AsyncErrorReporter::SetException(JSContext* aCx,
2105 JS::Handle<JS::Value> aException) {
2106 MOZ_ASSERT(NS_IsMainThread());
2107 mException.init(aCx, aException);
2108 mHasException = true;
2109 }
2110
Run()2111 NS_IMETHODIMP AsyncErrorReporter::Run() {
2112 AutoJSAPI jsapi;
2113 // We're only using this context to deserialize a stack to report to the
2114 // console, so the scope we use doesn't matter. Stack frame filtering happens
2115 // based on the principal encoded into the frame and the caller compartment,
2116 // not the compartment of the frame object, and the console reporting code
2117 // will not be using our context, and therefore will not care what compartment
2118 // it has entered.
2119 DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
2120 MOZ_ASSERT(ok, "Problem with system global?");
2121 JSContext* cx = jsapi.cx();
2122 JS::Rooted<JSObject*> stack(cx);
2123 JS::Rooted<JSObject*> stackGlobal(cx);
2124 if (mStackHolder) {
2125 stack = mStackHolder->ReadStack(cx);
2126 if (stack) {
2127 stackGlobal = JS::CurrentGlobalOrNull(cx);
2128 }
2129 }
2130
2131 JS::Rooted<Maybe<JS::Value>> exception(cx, Nothing());
2132 if (mHasException) {
2133 MOZ_ASSERT(NS_IsMainThread());
2134 exception = Some(mException);
2135 // Remove our reference to the exception.
2136 mException.setUndefined();
2137 mHasException = false;
2138 }
2139
2140 mReport->LogToConsoleWithStack(nullptr, exception, stack, stackGlobal);
2141 return NS_OK;
2142 }
2143
2144 // A fast-array class for JS. This class supports both nsIJSScriptArray and
2145 // nsIArray. If it is JS itself providing and consuming this class, all work
2146 // can be done via nsIJSScriptArray, and avoid the conversion of elements
2147 // to/from nsISupports.
2148 // When consumed by non-JS (eg, another script language), conversion is done
2149 // on-the-fly.
2150 class nsJSArgArray final : public nsIJSArgArray {
2151 public:
2152 nsJSArgArray(JSContext* aContext, uint32_t argc, const JS::Value* argv,
2153 nsresult* prv);
2154
2155 // nsISupports
2156 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2157 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
2158 nsIJSArgArray)
2159
2160 // nsIArray
2161 NS_DECL_NSIARRAY
2162
2163 // nsIJSArgArray
2164 nsresult GetArgs(uint32_t* argc, void** argv) override;
2165
2166 void ReleaseJSObjects();
2167
2168 protected:
2169 ~nsJSArgArray();
2170 JSContext* mContext;
2171 JS::Heap<JS::Value>* mArgv;
2172 uint32_t mArgc;
2173 };
2174
nsJSArgArray(JSContext * aContext,uint32_t argc,const JS::Value * argv,nsresult * prv)2175 nsJSArgArray::nsJSArgArray(JSContext* aContext, uint32_t argc,
2176 const JS::Value* argv, nsresult* prv)
2177 : mContext(aContext), mArgv(nullptr), mArgc(argc) {
2178 // copy the array - we don't know its lifetime, and ours is tied to xpcom
2179 // refcounting.
2180 if (argc) {
2181 mArgv = new (fallible) JS::Heap<JS::Value>[argc];
2182 if (!mArgv) {
2183 *prv = NS_ERROR_OUT_OF_MEMORY;
2184 return;
2185 }
2186 }
2187
2188 // Callers are allowed to pass in a null argv even for argc > 0. They can
2189 // then use GetArgs to initialize the values.
2190 if (argv) {
2191 for (uint32_t i = 0; i < argc; ++i) mArgv[i] = argv[i];
2192 }
2193
2194 if (argc > 0) {
2195 mozilla::HoldJSObjects(this);
2196 }
2197
2198 *prv = NS_OK;
2199 }
2200
~nsJSArgArray()2201 nsJSArgArray::~nsJSArgArray() { ReleaseJSObjects(); }
2202
ReleaseJSObjects()2203 void nsJSArgArray::ReleaseJSObjects() {
2204 delete[] mArgv;
2205
2206 if (mArgc > 0) {
2207 mArgc = 0;
2208 mozilla::DropJSObjects(this);
2209 }
2210 }
2211
2212 // QueryInterface implementation for nsJSArgArray
2213 NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(nsJSArgArray)
2214
2215 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
2216 tmp->ReleaseJSObjects();
2217 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2218 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
2219 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2220
2221 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
2222 if (tmp->mArgv) {
2223 for (uint32_t i = 0; i < tmp->mArgc; ++i) {
2224 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv[i])
2225 }
2226 }
2227 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2228
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)2229 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
2230 NS_INTERFACE_MAP_ENTRY(nsIArray)
2231 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
2232 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
2233 NS_INTERFACE_MAP_END
2234
2235 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
2236 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
2237
2238 nsresult nsJSArgArray::GetArgs(uint32_t* argc, void** argv) {
2239 *argv = (void*)mArgv;
2240 *argc = mArgc;
2241 return NS_OK;
2242 }
2243
2244 // nsIArray impl
GetLength(uint32_t * aLength)2245 NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t* aLength) {
2246 *aLength = mArgc;
2247 return NS_OK;
2248 }
2249
QueryElementAt(uint32_t index,const nsIID & uuid,void ** result)2250 NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID& uuid,
2251 void** result) {
2252 *result = nullptr;
2253 if (index >= mArgc) return NS_ERROR_INVALID_ARG;
2254
2255 if (uuid.Equals(NS_GET_IID(nsIVariant)) ||
2256 uuid.Equals(NS_GET_IID(nsISupports))) {
2257 // Have to copy a Heap into a Rooted to work with it.
2258 JS::Rooted<JS::Value> val(mContext, mArgv[index]);
2259 return nsContentUtils::XPConnect()->JSToVariant(mContext, val,
2260 (nsIVariant**)result);
2261 }
2262 NS_WARNING("nsJSArgArray only handles nsIVariant");
2263 return NS_ERROR_NO_INTERFACE;
2264 }
2265
IndexOf(uint32_t startIndex,nsISupports * element,uint32_t * _retval)2266 NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports* element,
2267 uint32_t* _retval) {
2268 return NS_ERROR_NOT_IMPLEMENTED;
2269 }
2270
ScriptedEnumerate(const nsIID & aElemIID,uint8_t aArgc,nsISimpleEnumerator ** aResult)2271 NS_IMETHODIMP nsJSArgArray::ScriptedEnumerate(const nsIID& aElemIID,
2272 uint8_t aArgc,
2273 nsISimpleEnumerator** aResult) {
2274 return NS_ERROR_NOT_IMPLEMENTED;
2275 }
2276
EnumerateImpl(const nsID & aEntryIID,nsISimpleEnumerator ** _retval)2277 NS_IMETHODIMP nsJSArgArray::EnumerateImpl(const nsID& aEntryIID,
2278 nsISimpleEnumerator** _retval) {
2279 return NS_ERROR_NOT_IMPLEMENTED;
2280 }
2281
2282 // The factory function
NS_CreateJSArgv(JSContext * aContext,uint32_t argc,const JS::Value * argv,nsIJSArgArray ** aArray)2283 nsresult NS_CreateJSArgv(JSContext* aContext, uint32_t argc,
2284 const JS::Value* argv, nsIJSArgArray** aArray) {
2285 nsresult rv;
2286 nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc, argv, &rv);
2287 if (NS_FAILED(rv)) {
2288 return rv;
2289 }
2290 ret.forget(aArray);
2291 return NS_OK;
2292 }
2293