Line data Source code
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 "nsIDOMChromeWindow.h"
12 : #include "nsPIDOMWindow.h"
13 : #include "nsIScriptSecurityManager.h"
14 : #include "nsDOMCID.h"
15 : #include "nsIServiceManager.h"
16 : #include "nsIXPConnect.h"
17 : #include "nsCOMPtr.h"
18 : #include "nsISupportsPrimitives.h"
19 : #include "nsReadableUtils.h"
20 : #include "nsDOMJSUtils.h"
21 : #include "nsJSUtils.h"
22 : #include "nsIDocShell.h"
23 : #include "nsIDocShellTreeItem.h"
24 : #include "nsPresContext.h"
25 : #include "nsIConsoleService.h"
26 : #include "nsIScriptError.h"
27 : #include "nsIInterfaceRequestor.h"
28 : #include "nsIInterfaceRequestorUtils.h"
29 : #include "nsIPrompt.h"
30 : #include "nsIObserverService.h"
31 : #include "nsITimer.h"
32 : #include "nsIAtom.h"
33 : #include "nsContentUtils.h"
34 : #include "mozilla/EventDispatcher.h"
35 : #include "nsIContent.h"
36 : #include "nsCycleCollector.h"
37 : #include "nsXPCOMCIDInternal.h"
38 : #include "nsIXULRuntime.h"
39 : #include "nsTextFormatter.h"
40 : #ifdef XP_WIN
41 : #include <process.h>
42 : #define getpid _getpid
43 : #else
44 : #include <unistd.h> // for getpid()
45 : #endif
46 : #include "xpcpublic.h"
47 :
48 : #include "jsapi.h"
49 : #include "jswrapper.h"
50 : #include "js/SliceBudget.h"
51 : #include "nsIArray.h"
52 : #include "nsIObjectInputStream.h"
53 : #include "nsIObjectOutputStream.h"
54 : #include "WrapperFactory.h"
55 : #include "nsGlobalWindow.h"
56 : #include "nsScriptNameSpaceManager.h"
57 : #include "mozilla/AutoRestore.h"
58 : #include "mozilla/MainThreadIdlePeriod.h"
59 : #include "mozilla/StaticPtr.h"
60 : #include "mozilla/dom/DOMException.h"
61 : #include "mozilla/dom/DOMExceptionBinding.h"
62 : #include "mozilla/dom/Element.h"
63 : #include "mozilla/dom/ErrorEvent.h"
64 : #include "mozilla/dom/ScriptSettings.h"
65 : #include "nsAXPCNativeCallContext.h"
66 : #include "mozilla/CycleCollectedJSRuntime.h"
67 : #include "mozilla/SystemGroup.h"
68 : #include "nsRefreshDriver.h"
69 : #include "nsJSPrincipals.h"
70 :
71 : #ifdef XP_MACOSX
72 : // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
73 : #undef check
74 : #endif
75 : #include "AccessCheck.h"
76 :
77 : #include "mozilla/Logging.h"
78 : #include "prthread.h"
79 :
80 : #include "mozilla/Preferences.h"
81 : #include "mozilla/Telemetry.h"
82 : #include "mozilla/dom/BindingUtils.h"
83 : #include "mozilla/Attributes.h"
84 : #include "mozilla/dom/asmjscache/AsmJSCache.h"
85 : #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
86 : #include "mozilla/ContentEvents.h"
87 :
88 : #include "nsCycleCollectionNoteRootCallback.h"
89 : #include "GeckoProfiler.h"
90 :
91 : using namespace mozilla;
92 : using namespace mozilla::dom;
93 :
94 : const size_t gStackSize = 8192;
95 :
96 : // Thank you Microsoft!
97 : #ifdef CompareString
98 : #undef CompareString
99 : #endif
100 :
101 : #define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms
102 :
103 : // The amount of time we wait from the first request to GC to actually
104 : // doing the first GC.
105 : #define NS_FIRST_GC_DELAY 10000 // ms
106 :
107 : #define NS_FULL_GC_DELAY 60000 // ms
108 :
109 : // The default amount of time to wait from the user being idle to starting a
110 : // shrinking GC.
111 : #define NS_DEAULT_INACTIVE_GC_DELAY 300000 // ms
112 :
113 : // Maximum amount of time that should elapse between incremental GC slices
114 : #define NS_INTERSLICE_GC_DELAY 100 // ms
115 :
116 : // The amount of time we wait between a request to CC (after GC ran)
117 : // and doing the actual CC.
118 : #define NS_CC_DELAY 6000 // ms
119 :
120 : #define NS_CC_SKIPPABLE_DELAY 250 // ms
121 :
122 : // ForgetSkippable is usually fast, so we can use small budgets.
123 : // This isn't a real budget but a hint to CollectorRunner whether there
124 : // is enough time to call ForgetSkippable.
125 : static const int64_t kForgetSkippableSliceDuration = 2;
126 :
127 : // Maximum amount of time that should elapse between incremental CC slices
128 : static const int64_t kICCIntersliceDelay = 64; // ms
129 :
130 : // Time budget for an incremental CC slice when using timer to run it.
131 : static const int64_t kICCSliceBudget = 3; // ms
132 : // Minimum budget for an incremental CC slice when using idle time to run it.
133 : static const int64_t kIdleICCSliceBudget = 2; // ms
134 :
135 : // Maximum total duration for an ICC
136 : static const uint32_t kMaxICCDuration = 2000; // ms
137 :
138 : // Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT
139 : // objects in the purple buffer.
140 : #define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min
141 : #define NS_CC_FORCED_PURPLE_LIMIT 10
142 :
143 : // Don't allow an incremental GC to lock out the CC for too long.
144 : #define NS_MAX_CC_LOCKEDOUT_TIME (30 * PR_USEC_PER_SEC) // 30 seconds
145 :
146 : // Trigger a CC if the purple buffer exceeds this size when we check it.
147 : #define NS_CC_PURPLE_LIMIT 200
148 :
149 : // Large value used to specify that a script should run essentially forever
150 : #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
151 :
152 : class CollectorRunner;
153 :
154 : // if you add statics here, add them to the list in StartupJSEnvironment
155 :
156 : static nsITimer *sGCTimer;
157 : static nsITimer *sShrinkingGCTimer;
158 3 : static StaticRefPtr<CollectorRunner> sCCRunner;
159 3 : static StaticRefPtr<CollectorRunner> sICCRunner;
160 : static nsITimer *sFullGCTimer;
161 3 : static StaticRefPtr<CollectorRunner> sInterSliceGCRunner;
162 :
163 : static TimeStamp sLastCCEndTime;
164 :
165 : static bool sCCLockedOut;
166 : static PRTime sCCLockedOutTime;
167 :
168 : static JS::GCSliceCallback sPrevGCSliceCallback;
169 :
170 : static bool sHasRunGC;
171 :
172 : // The number of currently pending document loads. This count isn't
173 : // guaranteed to always reflect reality and can't easily as we don't
174 : // have an easy place to know when a load ends or is interrupted in
175 : // all cases. This counter also gets reset if we end up GC'ing while
176 : // we're waiting for a slow page to load. IOW, this count may be 0
177 : // even when there are pending loads.
178 : static uint32_t sPendingLoadCount;
179 : static bool sLoadingInProgress;
180 :
181 : static uint32_t sCCollectedWaitingForGC;
182 : static uint32_t sCCollectedZonesWaitingForGC;
183 : static uint32_t sLikelyShortLivingObjectsNeedingGC;
184 : static bool sPostGCEventsToConsole;
185 : static bool sPostGCEventsToObserver;
186 : static int32_t sCCRunnerFireCount = 0;
187 : static uint32_t sMinForgetSkippableTime = UINT32_MAX;
188 : static uint32_t sMaxForgetSkippableTime = 0;
189 : static uint32_t sTotalForgetSkippableTime = 0;
190 : static uint32_t sRemovedPurples = 0;
191 : static uint32_t sForgetSkippableBeforeCC = 0;
192 : static uint32_t sPreviousSuspectedCount = 0;
193 : static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
194 : static bool sNeedsFullCC = false;
195 : static bool sNeedsFullGC = false;
196 : static bool sNeedsGCAfterCC = false;
197 : static bool sIncrementalCC = false;
198 : static int32_t sActiveIntersliceGCBudget = 5; // ms;
199 : static nsScriptNameSpaceManager *gNameSpaceManager;
200 :
201 : static PRTime sFirstCollectionTime;
202 :
203 : static bool sIsInitialized;
204 : static bool sDidShutdown;
205 : static bool sShuttingDown;
206 : static int32_t sContextCount;
207 :
208 : static nsIScriptSecurityManager *sSecurityManager;
209 :
210 : // nsJSEnvironmentObserver observes the memory-pressure notifications
211 : // and forces a garbage collection and cycle collection when it happens, if
212 : // the appropriate pref is set.
213 :
214 : static bool sGCOnMemoryPressure;
215 :
216 : // nsJSEnvironmentObserver observes the user-interaction-inactive notifications
217 : // and triggers a shrinking a garbage collection if the user is still inactive
218 : // after NS_SHRINKING_GC_DELAY ms later, if the appropriate pref is set.
219 :
220 : static bool sCompactOnUserInactive;
221 : static uint32_t sCompactOnUserInactiveDelay = NS_DEAULT_INACTIVE_GC_DELAY;
222 : static bool sIsCompactingOnUserInactive = false;
223 :
224 : // In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
225 : // aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
226 : // us from triggering expensive full collections too frequently.
227 : static int32_t sExpensiveCollectorPokes = 0;
228 : static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
229 :
230 : static TimeDuration sGCUnnotifiedTotalTime;
231 :
232 : // Return true if some meaningful work was done.
233 : typedef bool (*CollectorRunnerCallback) (TimeStamp aDeadline, void* aData);
234 :
235 : // Repeating callback runner for CC and GC.
236 : class CollectorRunner final : public IdleRunnable
237 : {
238 : public:
239 : static already_AddRefed<CollectorRunner>
240 4 : Create(CollectorRunnerCallback aCallback, uint32_t aDelay,
241 : int64_t aBudget, bool aRepeating, void* aData = nullptr)
242 : {
243 4 : if (sShuttingDown) {
244 0 : return nullptr;
245 : }
246 :
247 : RefPtr<CollectorRunner> runner =
248 8 : new CollectorRunner(aCallback, aDelay, aBudget, aRepeating, aData);
249 4 : runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
250 4 : return runner.forget();
251 : }
252 :
253 3 : NS_IMETHOD Run() override
254 : {
255 3 : if (!mCallback) {
256 0 : return NS_OK;
257 : }
258 :
259 : // Deadline is null when called from timer.
260 3 : bool deadLineWasNull = mDeadline.IsNull();
261 3 : bool didRun = false;
262 3 : if (deadLineWasNull || ((TimeStamp::Now() + mBudget) < mDeadline)) {
263 3 : CancelTimer();
264 3 : didRun = mCallback(mDeadline, mData);
265 : }
266 :
267 3 : if (mCallback && (mRepeating || !didRun)) {
268 : // If we didn't do meaningful work, don't schedule using immediate
269 : // idle dispatch, since that could lead to a loop until the idle
270 : // period ends.
271 0 : Schedule(didRun);
272 : }
273 :
274 3 : return NS_OK;
275 : }
276 :
277 : static void
278 3 : TimedOut(nsITimer* aTimer, void* aClosure)
279 : {
280 6 : RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
281 3 : runnable->Run();
282 3 : }
283 :
284 0 : void SetDeadline(mozilla::TimeStamp aDeadline) override
285 : {
286 0 : mDeadline = aDeadline;
287 0 : };
288 :
289 7 : void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override
290 : {
291 7 : if (mTimerActive) {
292 3 : return;
293 : }
294 :
295 4 : mTarget = aTarget;
296 4 : if (!mTimer) {
297 4 : mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
298 : } else {
299 0 : mTimer->Cancel();
300 : }
301 :
302 4 : if (mTimer) {
303 4 : mTimer->SetTarget(mTarget);
304 4 : mTimer->InitWithNamedFuncCallback(TimedOut, this, aDelay,
305 : nsITimer::TYPE_ONE_SHOT,
306 4 : "CollectorRunner");
307 4 : mTimerActive = true;
308 : }
309 : }
310 :
311 3 : nsresult Cancel() override
312 : {
313 3 : CancelTimer();
314 3 : mTimer = nullptr;
315 3 : mScheduleTimer = nullptr;
316 3 : mCallback = nullptr;
317 3 : return NS_OK;
318 : }
319 :
320 : static void
321 2 : ScheduleTimedOut(nsITimer* aTimer, void* aClosure)
322 : {
323 4 : RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
324 2 : runnable->Schedule(true);
325 2 : }
326 :
327 6 : void Schedule(bool aAllowIdleDispatch)
328 : {
329 6 : if (!mCallback) {
330 0 : return;
331 : }
332 :
333 6 : if (sShuttingDown) {
334 0 : Cancel();
335 0 : return;
336 : }
337 :
338 6 : mDeadline = TimeStamp();
339 6 : TimeStamp now = TimeStamp::Now();
340 6 : TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
341 6 : if (hint != now) {
342 : // RefreshDriver is ticking, let it schedule the idle dispatch.
343 3 : nsRefreshDriver::DispatchIdleRunnableAfterTick(this, mDelay);
344 : // Ensure we get called at some point, even if RefreshDriver is stopped.
345 3 : SetTimer(mDelay, mTarget);
346 : } else {
347 : // RefreshDriver doesn't seem to be running.
348 3 : if (aAllowIdleDispatch) {
349 2 : nsCOMPtr<nsIRunnable> runnable = this;
350 1 : NS_IdleDispatchToCurrentThread(runnable.forget(), mDelay);
351 1 : SetTimer(mDelay, mTarget);
352 : } else {
353 2 : if (!mScheduleTimer) {
354 2 : mScheduleTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
355 2 : if (!mScheduleTimer) {
356 0 : return;
357 : }
358 : } else {
359 0 : mScheduleTimer->Cancel();
360 : }
361 :
362 : // We weren't allowed to do idle dispatch immediately, do it after a
363 : // short timeout.
364 2 : mScheduleTimer->InitWithNamedFuncCallback(ScheduleTimedOut, this, 16,
365 : nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
366 2 : "CollectorRunner");
367 : }
368 : }
369 : }
370 :
371 : private:
372 4 : explicit CollectorRunner(CollectorRunnerCallback aCallback,
373 : uint32_t aDelay, int64_t aBudget,
374 : bool aRepeating, void* aData)
375 4 : : mCallback(aCallback), mDelay(aDelay)
376 4 : , mBudget(TimeDuration::FromMilliseconds(aBudget))
377 8 : , mRepeating(aRepeating), mTimerActive(false), mData(aData)
378 : {
379 4 : }
380 :
381 0 : ~CollectorRunner()
382 0 : {
383 0 : CancelTimer();
384 0 : }
385 :
386 6 : void CancelTimer()
387 : {
388 6 : nsRefreshDriver::CancelIdleRunnable(this);
389 6 : if (mTimer) {
390 6 : mTimer->Cancel();
391 : }
392 6 : if (mScheduleTimer) {
393 4 : mScheduleTimer->Cancel();
394 : }
395 6 : mTimerActive = false;
396 6 : }
397 :
398 : nsCOMPtr<nsITimer> mTimer;
399 : nsCOMPtr<nsITimer> mScheduleTimer;
400 : nsCOMPtr<nsIEventTarget> mTarget;
401 : CollectorRunnerCallback mCallback;
402 : uint32_t mDelay;
403 : TimeStamp mDeadline;
404 : TimeDuration mBudget;
405 : bool mRepeating;
406 : bool mTimerActive;
407 : void* mData;
408 : };
409 :
410 : static const char*
411 0 : ProcessNameForCollectorLog()
412 : {
413 0 : return XRE_GetProcessType() == GeckoProcessType_Default ?
414 0 : "default" : "content";
415 : }
416 :
417 : namespace xpc {
418 :
419 : // This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC
420 : // Exceptions.
421 : //
422 : // Note that the returned object is _not_ wrapped into the compartment of
423 : // exceptionValue.
424 : JSObject*
425 0 : FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
426 : JS::HandleValue exceptionValue)
427 : {
428 0 : if (!exceptionValue.isObject()) {
429 0 : return nullptr;
430 : }
431 :
432 0 : if (win && win->InnerObjectsFreed()) {
433 : // Pretend like we have no stack, so we don't end up keeping the global
434 : // alive via the stack.
435 0 : return nullptr;
436 : }
437 :
438 0 : JS::RootingContext* rcx = RootingCx();
439 0 : JS::RootedObject exceptionObject(rcx, &exceptionValue.toObject());
440 0 : JSObject* stackObject = ExceptionStackOrNull(exceptionObject);
441 0 : if (stackObject) {
442 0 : return stackObject;
443 : }
444 :
445 : // It is not a JS Exception, try DOM Exception.
446 0 : RefPtr<Exception> exception;
447 0 : UNWRAP_OBJECT(DOMException, exceptionObject, exception);
448 0 : if (!exception) {
449 : // Not a DOM Exception, try XPC Exception.
450 0 : UNWRAP_OBJECT(Exception, exceptionObject, exception);
451 0 : if (!exception) {
452 0 : return nullptr;
453 : }
454 : }
455 :
456 0 : nsCOMPtr<nsIStackFrame> stack = exception->GetLocation();
457 0 : if (!stack) {
458 0 : return nullptr;
459 : }
460 0 : JS::RootedValue value(rcx);
461 0 : stack->GetNativeSavedFrame(&value);
462 0 : if (value.isObject()) {
463 0 : return &value.toObject();
464 : }
465 0 : return nullptr;
466 : }
467 :
468 : } /* namespace xpc */
469 :
470 : static PRTime
471 0 : GetCollectionTimeDelta()
472 : {
473 0 : PRTime now = PR_Now();
474 0 : if (sFirstCollectionTime) {
475 0 : return now - sFirstCollectionTime;
476 : }
477 0 : sFirstCollectionTime = now;
478 0 : return 0;
479 : }
480 :
481 : static void
482 0 : KillTimers()
483 : {
484 0 : nsJSContext::KillGCTimer();
485 0 : nsJSContext::KillShrinkingGCTimer();
486 0 : nsJSContext::KillCCRunner();
487 0 : nsJSContext::KillICCRunner();
488 0 : nsJSContext::KillFullGCTimer();
489 0 : nsJSContext::KillInterSliceGCRunner();
490 0 : }
491 :
492 : // If we collected a substantial amount of cycles, poke the GC since more objects
493 : // might be unreachable now.
494 : static bool
495 0 : NeedsGCAfterCC()
496 : {
497 0 : return sCCollectedWaitingForGC > 250 ||
498 0 : sCCollectedZonesWaitingForGC > 0 ||
499 0 : sLikelyShortLivingObjectsNeedingGC > 2500 ||
500 0 : sNeedsGCAfterCC;
501 : }
502 :
503 2 : class nsJSEnvironmentObserver final : public nsIObserver
504 : {
505 0 : ~nsJSEnvironmentObserver() {}
506 : public:
507 : NS_DECL_ISUPPORTS
508 : NS_DECL_NSIOBSERVER
509 : };
510 :
511 22 : NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver, nsIObserver)
512 :
513 : NS_IMETHODIMP
514 3 : nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
515 : const char16_t* aData)
516 : {
517 3 : if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
518 0 : if (sGCOnMemoryPressure) {
519 0 : if(StringBeginsWith(nsDependentString(aData),
520 0 : NS_LITERAL_STRING("low-memory-ongoing"))) {
521 : // Don't GC/CC if we are in an ongoing low-memory state since its very
522 : // slow and it likely won't help us anyway.
523 0 : return NS_OK;
524 : }
525 : nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
526 : nsJSContext::NonIncrementalGC,
527 0 : nsJSContext::ShrinkingGC);
528 0 : nsJSContext::CycleCollectNow();
529 0 : if (NeedsGCAfterCC()) {
530 : nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
531 : nsJSContext::NonIncrementalGC,
532 0 : nsJSContext::ShrinkingGC);
533 : }
534 : }
535 3 : } else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
536 2 : if (sCompactOnUserInactive) {
537 2 : nsJSContext::PokeShrinkingGC();
538 : }
539 1 : } else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
540 1 : nsJSContext::KillShrinkingGCTimer();
541 1 : if (sIsCompactingOnUserInactive) {
542 0 : AutoJSAPI jsapi;
543 0 : jsapi.Init();
544 0 : JS::AbortIncrementalGC(jsapi.cx());
545 : }
546 1 : MOZ_ASSERT(!sIsCompactingOnUserInactive);
547 0 : } else if (!nsCRT::strcmp(aTopic, "quit-application") ||
548 0 : !nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
549 0 : sShuttingDown = true;
550 0 : KillTimers();
551 : }
552 :
553 3 : return NS_OK;
554 : }
555 :
556 : /****************************************************************
557 : ************************** AutoFree ****************************
558 : ****************************************************************/
559 :
560 : class AutoFree {
561 : public:
562 0 : explicit AutoFree(void* aPtr) : mPtr(aPtr) {
563 0 : }
564 0 : ~AutoFree() {
565 0 : if (mPtr)
566 0 : free(mPtr);
567 0 : }
568 : void Invalidate() {
569 : mPtr = 0;
570 : }
571 : private:
572 : void *mPtr;
573 : };
574 :
575 : // A utility function for script languages to call. Although it looks small,
576 : // the use of nsIDocShell and nsPresContext triggers a huge number of
577 : // dependencies that most languages would not otherwise need.
578 : // XXXmarkh - This function is mis-placed!
579 : bool
580 0 : NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
581 : const ErrorEventInit &aErrorEventInit,
582 : nsEventStatus *aStatus)
583 : {
584 0 : bool called = false;
585 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aScriptGlobal));
586 0 : nsIDocShell *docShell = win ? win->GetDocShell() : nullptr;
587 0 : if (docShell) {
588 0 : RefPtr<nsPresContext> presContext;
589 0 : docShell->GetPresContext(getter_AddRefs(presContext));
590 :
591 : static int32_t errorDepth; // Recursion prevention
592 0 : ++errorDepth;
593 :
594 0 : if (errorDepth < 2) {
595 : // Dispatch() must be synchronous for the recursion block
596 : // (errorDepth) to work.
597 : RefPtr<ErrorEvent> event =
598 0 : ErrorEvent::Constructor(nsGlobalWindow::Cast(win),
599 0 : NS_LITERAL_STRING("error"),
600 0 : aErrorEventInit);
601 0 : event->SetTrusted(true);
602 :
603 0 : EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
604 0 : aStatus);
605 0 : called = true;
606 : }
607 0 : --errorDepth;
608 : }
609 0 : return called;
610 : }
611 :
612 0 : class ScriptErrorEvent : public Runnable
613 : {
614 : public:
615 0 : ScriptErrorEvent(nsPIDOMWindowInner* aWindow,
616 : JS::RootingContext* aRootingCx,
617 : xpc::ErrorReport* aReport,
618 : JS::Handle<JS::Value> aError)
619 0 : : mozilla::Runnable("ScriptErrorEvent")
620 : , mWindow(aWindow)
621 : , mReport(aReport)
622 0 : , mError(aRootingCx, aError)
623 0 : {}
624 :
625 0 : NS_IMETHOD Run() override
626 : {
627 0 : nsEventStatus status = nsEventStatus_eIgnore;
628 0 : nsPIDOMWindowInner* win = mWindow;
629 0 : MOZ_ASSERT(win);
630 0 : MOZ_ASSERT(NS_IsMainThread());
631 : // First, notify the DOM that we have a script error, but only if
632 : // our window is still the current inner.
633 0 : JS::RootingContext* rootingCx = RootingCx();
634 0 : if (win->IsCurrentInnerWindow() && win->GetDocShell() && !sHandlingScriptError) {
635 0 : AutoRestore<bool> recursionGuard(sHandlingScriptError);
636 0 : sHandlingScriptError = true;
637 :
638 0 : RefPtr<nsPresContext> presContext;
639 0 : win->GetDocShell()->GetPresContext(getter_AddRefs(presContext));
640 :
641 0 : RootedDictionary<ErrorEventInit> init(rootingCx);
642 0 : init.mCancelable = true;
643 0 : init.mFilename = mReport->mFileName;
644 0 : init.mBubbles = true;
645 :
646 0 : NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
647 0 : if (!mReport->mIsMuted) {
648 0 : init.mMessage = mReport->mErrorMsg;
649 0 : init.mLineno = mReport->mLineNumber;
650 0 : init.mColno = mReport->mColumn;
651 0 : init.mError = mError;
652 : } else {
653 0 : NS_WARNING("Not same origin error!");
654 0 : init.mMessage = xoriginMsg;
655 0 : init.mLineno = 0;
656 : }
657 :
658 : RefPtr<ErrorEvent> event =
659 0 : ErrorEvent::Constructor(nsGlobalWindow::Cast(win),
660 0 : NS_LITERAL_STRING("error"), init);
661 0 : event->SetTrusted(true);
662 :
663 0 : EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
664 0 : &status);
665 : }
666 :
667 0 : if (status != nsEventStatus_eConsumeNoDefault) {
668 : JS::Rooted<JSObject*> stack(rootingCx,
669 0 : xpc::FindExceptionStackForConsoleReport(win, mError));
670 0 : mReport->LogToConsoleWithStack(stack);
671 : }
672 :
673 0 : return NS_OK;
674 : }
675 :
676 : private:
677 : nsCOMPtr<nsPIDOMWindowInner> mWindow;
678 : RefPtr<xpc::ErrorReport> mReport;
679 : JS::PersistentRootedValue mError;
680 :
681 : static bool sHandlingScriptError;
682 : };
683 :
684 : bool ScriptErrorEvent::sHandlingScriptError = false;
685 :
686 : // This temporarily lives here to avoid code churn. It will go away entirely
687 : // soon.
688 : namespace xpc {
689 :
690 : void
691 0 : DispatchScriptErrorEvent(nsPIDOMWindowInner *win, JS::RootingContext* rootingCx,
692 : xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception)
693 : {
694 0 : nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception));
695 0 : }
696 :
697 : } /* namespace xpc */
698 :
699 : #ifdef DEBUG
700 : // A couple of useful functions to call when you're debugging.
701 : nsGlobalWindow *
702 0 : JSObject2Win(JSObject *obj)
703 : {
704 0 : return xpc::WindowOrNull(obj);
705 : }
706 :
707 : void
708 0 : PrintWinURI(nsGlobalWindow *win)
709 : {
710 0 : if (!win) {
711 0 : printf("No window passed in.\n");
712 0 : return;
713 : }
714 :
715 0 : nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
716 0 : if (!doc) {
717 0 : printf("No document in the window.\n");
718 0 : return;
719 : }
720 :
721 0 : nsIURI *uri = doc->GetDocumentURI();
722 0 : if (!uri) {
723 0 : printf("Document doesn't have a URI.\n");
724 0 : return;
725 : }
726 :
727 0 : printf("%s\n", uri->GetSpecOrDefault().get());
728 : }
729 :
730 : void
731 0 : PrintWinCodebase(nsGlobalWindow *win)
732 : {
733 0 : if (!win) {
734 0 : printf("No window passed in.\n");
735 0 : return;
736 : }
737 :
738 0 : nsIPrincipal *prin = win->GetPrincipal();
739 0 : if (!prin) {
740 0 : printf("Window doesn't have principals.\n");
741 0 : return;
742 : }
743 :
744 0 : nsCOMPtr<nsIURI> uri;
745 0 : prin->GetURI(getter_AddRefs(uri));
746 0 : if (!uri) {
747 0 : printf("No URI, maybe the system principal.\n");
748 0 : return;
749 : }
750 :
751 0 : printf("%s\n", uri->GetSpecOrDefault().get());
752 : }
753 :
754 : void
755 0 : DumpString(const nsAString &str)
756 : {
757 0 : printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
758 0 : }
759 : #endif
760 :
761 : #define JS_OPTIONS_DOT_STR "javascript.options."
762 :
763 : static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
764 :
765 5 : nsJSContext::nsJSContext(bool aGCOnDestruction,
766 5 : nsIScriptGlobalObject* aGlobalObject)
767 : : mWindowProxy(nullptr)
768 : , mGCOnDestruction(aGCOnDestruction)
769 5 : , mGlobalObjectRef(aGlobalObject)
770 : {
771 5 : EnsureStatics();
772 :
773 5 : ++sContextCount;
774 :
775 5 : mIsInitialized = false;
776 5 : mProcessingScriptTag = false;
777 5 : HoldJSObjects(this);
778 5 : }
779 :
780 3 : nsJSContext::~nsJSContext()
781 : {
782 1 : mGlobalObjectRef = nullptr;
783 :
784 1 : Destroy();
785 :
786 1 : --sContextCount;
787 :
788 1 : if (!sContextCount && sDidShutdown) {
789 : // The last context is being deleted, and we're already in the
790 : // process of shutting down, release the security manager.
791 :
792 0 : NS_IF_RELEASE(sSecurityManager);
793 : }
794 3 : }
795 :
796 : void
797 1 : nsJSContext::Destroy()
798 : {
799 1 : if (mGCOnDestruction) {
800 1 : PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY, mWindowProxy);
801 : }
802 :
803 1 : DropJSObjects(this);
804 1 : }
805 :
806 : // QueryInterface implementation for nsJSContext
807 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
808 :
809 6 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
810 6 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy)
811 6 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
812 :
813 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
814 0 : tmp->mIsInitialized = false;
815 0 : tmp->mGCOnDestruction = false;
816 0 : tmp->mWindowProxy = nullptr;
817 0 : tmp->Destroy();
818 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
819 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
820 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext)
821 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
822 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
823 :
824 61 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
825 28 : NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
826 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
827 0 : NS_INTERFACE_MAP_END
828 :
829 :
830 69 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
831 66 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
832 :
833 : #ifdef DEBUG
834 : bool
835 0 : AtomIsEventHandlerName(nsIAtom *aName)
836 : {
837 0 : const char16_t *name = aName->GetUTF16String();
838 :
839 : const char16_t *cp;
840 : char16_t c;
841 0 : for (cp = name; *cp != '\0'; ++cp)
842 : {
843 0 : c = *cp;
844 0 : if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
845 0 : return false;
846 : }
847 :
848 0 : return true;
849 : }
850 : #endif
851 :
852 : nsIScriptGlobalObject *
853 2 : nsJSContext::GetGlobalObject()
854 : {
855 : // Note: this could probably be simplified somewhat more; see bug 974327
856 : // comments 1 and 3.
857 2 : if (!mWindowProxy) {
858 0 : return nullptr;
859 : }
860 :
861 2 : MOZ_ASSERT(mGlobalObjectRef);
862 2 : return mGlobalObjectRef;
863 : }
864 :
865 : nsresult
866 5 : nsJSContext::InitContext()
867 : {
868 : // Make sure callers of this use
869 : // WillInitializeContext/DidInitializeContext around this call.
870 5 : NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
871 :
872 : // XXXbz Is there still a point to this function?
873 5 : return NS_OK;
874 : }
875 :
876 : nsresult
877 2 : nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
878 : {
879 4 : AutoJSAPI jsapi;
880 2 : if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
881 0 : return NS_ERROR_FAILURE;
882 : }
883 2 : JSContext* cx = jsapi.cx();
884 :
885 4 : JS::AutoValueVector args(cx);
886 :
887 4 : JS::Rooted<JSObject*> global(cx, GetWindowProxy());
888 : nsresult rv =
889 2 : ConvertSupportsTojsvals(aArgs, global, args);
890 2 : NS_ENSURE_SUCCESS(rv, rv);
891 :
892 : // got the arguments, now attach them.
893 :
894 12 : for (uint32_t i = 0; i < args.length(); ++i) {
895 10 : if (!JS_WrapValue(cx, args[i])) {
896 0 : return NS_ERROR_FAILURE;
897 : }
898 : }
899 :
900 4 : JS::Rooted<JSObject*> array(cx, ::JS_NewArrayObject(cx, args));
901 2 : if (!array) {
902 0 : return NS_ERROR_FAILURE;
903 : }
904 :
905 2 : return JS_DefineProperty(cx, aTarget, aPropName, array, 0) ? NS_OK : NS_ERROR_FAILURE;
906 : }
907 :
908 : nsresult
909 2 : nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
910 : JS::Handle<JSObject*> aScope,
911 : JS::AutoValueVector& aArgsOut)
912 : {
913 2 : nsresult rv = NS_OK;
914 :
915 : // If the array implements nsIJSArgArray, copy the contents and return.
916 4 : nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
917 2 : if (fastArray) {
918 : uint32_t argc;
919 : JS::Value* argv;
920 0 : rv = fastArray->GetArgs(&argc, reinterpret_cast<void **>(&argv));
921 0 : if (NS_SUCCEEDED(rv) && !aArgsOut.append(argv, argc)) {
922 0 : rv = NS_ERROR_OUT_OF_MEMORY;
923 : }
924 0 : return rv;
925 : }
926 :
927 : // Take the slower path converting each item.
928 : // Handle only nsIArray and nsIVariant. nsIArray is only needed for
929 : // SetProperty('arguments', ...);
930 :
931 2 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
932 2 : NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
933 4 : AutoJSContext cx;
934 :
935 2 : if (!aArgs)
936 0 : return NS_OK;
937 : uint32_t argCount;
938 : // This general purpose function may need to convert an arg array
939 : // (window.arguments, event-handler args) and a generic property.
940 4 : nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
941 :
942 2 : if (argsArray) {
943 2 : rv = argsArray->GetLength(&argCount);
944 2 : NS_ENSURE_SUCCESS(rv, rv);
945 2 : if (argCount == 0)
946 0 : return NS_OK;
947 : } else {
948 0 : argCount = 1; // the nsISupports which is not an array
949 : }
950 :
951 : // Use the caller's auto guards to release and unroot.
952 2 : if (!aArgsOut.resize(argCount)) {
953 0 : return NS_ERROR_OUT_OF_MEMORY;
954 : }
955 :
956 2 : if (argsArray) {
957 12 : for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
958 12 : nsCOMPtr<nsISupports> arg;
959 10 : JS::MutableHandle<JS::Value> thisVal = aArgsOut[argCtr];
960 20 : argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
961 20 : getter_AddRefs(arg));
962 10 : if (!arg) {
963 8 : thisVal.setNull();
964 8 : continue;
965 : }
966 4 : nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
967 2 : if (variant != nullptr) {
968 0 : rv = xpc->VariantToJS(cx, aScope, variant, thisVal);
969 : } else {
970 : // And finally, support the nsISupportsPrimitives supplied
971 : // by the AppShell. It generally will pass only strings, but
972 : // as we have code for handling all, we may as well use it.
973 2 : rv = AddSupportsPrimitiveTojsvals(arg, thisVal.address());
974 2 : if (rv == NS_ERROR_NO_INTERFACE) {
975 : // something else - probably an event object or similar -
976 : // just wrap it.
977 : #ifdef DEBUG
978 : // but first, check its not another nsISupportsPrimitive, as
979 : // these are now deprecated for use with script contexts.
980 4 : nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
981 2 : NS_ASSERTION(prim == nullptr,
982 : "Don't pass nsISupportsPrimitives - use nsIVariant!");
983 : #endif
984 4 : JSAutoCompartment ac(cx, aScope);
985 2 : rv = nsContentUtils::WrapNative(cx, arg, thisVal);
986 : }
987 : }
988 : }
989 : } else {
990 0 : nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
991 0 : if (variant) {
992 0 : rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut[0]);
993 : } else {
994 0 : NS_ERROR("Not an array, not an interface?");
995 0 : rv = NS_ERROR_UNEXPECTED;
996 : }
997 : }
998 2 : return rv;
999 : }
1000 :
1001 : // This really should go into xpconnect somewhere...
1002 : nsresult
1003 2 : nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv)
1004 : {
1005 2 : NS_PRECONDITION(aArg, "Empty arg");
1006 :
1007 4 : nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
1008 2 : if (!argPrimitive)
1009 2 : return NS_ERROR_NO_INTERFACE;
1010 :
1011 0 : AutoJSContext cx;
1012 : uint16_t type;
1013 0 : argPrimitive->GetType(&type);
1014 :
1015 0 : switch(type) {
1016 : case nsISupportsPrimitive::TYPE_CSTRING : {
1017 0 : nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
1018 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1019 :
1020 0 : nsAutoCString data;
1021 :
1022 0 : p->GetData(data);
1023 :
1024 :
1025 0 : JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
1026 0 : NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1027 :
1028 0 : aArgv->setString(str);
1029 :
1030 0 : break;
1031 : }
1032 : case nsISupportsPrimitive::TYPE_STRING : {
1033 0 : nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
1034 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1035 :
1036 0 : nsAutoString data;
1037 :
1038 0 : p->GetData(data);
1039 :
1040 : // cast is probably safe since wchar_t and char16_t are expected
1041 : // to be equivalent; both unsigned 16-bit entities
1042 : JSString *str =
1043 0 : ::JS_NewUCStringCopyN(cx, data.get(), data.Length());
1044 0 : NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1045 :
1046 0 : aArgv->setString(str);
1047 0 : break;
1048 : }
1049 : case nsISupportsPrimitive::TYPE_PRBOOL : {
1050 0 : nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
1051 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1052 :
1053 : bool data;
1054 :
1055 0 : p->GetData(&data);
1056 :
1057 0 : aArgv->setBoolean(data);
1058 :
1059 0 : break;
1060 : }
1061 : case nsISupportsPrimitive::TYPE_PRUINT8 : {
1062 0 : nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
1063 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1064 :
1065 : uint8_t data;
1066 :
1067 0 : p->GetData(&data);
1068 :
1069 0 : aArgv->setInt32(data);
1070 :
1071 0 : break;
1072 : }
1073 : case nsISupportsPrimitive::TYPE_PRUINT16 : {
1074 0 : nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
1075 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1076 :
1077 : uint16_t data;
1078 :
1079 0 : p->GetData(&data);
1080 :
1081 0 : aArgv->setInt32(data);
1082 :
1083 0 : break;
1084 : }
1085 : case nsISupportsPrimitive::TYPE_PRUINT32 : {
1086 0 : nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
1087 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1088 :
1089 : uint32_t data;
1090 :
1091 0 : p->GetData(&data);
1092 :
1093 0 : aArgv->setInt32(data);
1094 :
1095 0 : break;
1096 : }
1097 : case nsISupportsPrimitive::TYPE_CHAR : {
1098 0 : nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
1099 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1100 :
1101 : char data;
1102 :
1103 0 : p->GetData(&data);
1104 :
1105 0 : JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
1106 0 : NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1107 :
1108 0 : aArgv->setString(str);
1109 :
1110 0 : break;
1111 : }
1112 : case nsISupportsPrimitive::TYPE_PRINT16 : {
1113 0 : nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
1114 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1115 :
1116 : int16_t data;
1117 :
1118 0 : p->GetData(&data);
1119 :
1120 0 : aArgv->setInt32(data);
1121 :
1122 0 : break;
1123 : }
1124 : case nsISupportsPrimitive::TYPE_PRINT32 : {
1125 0 : nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
1126 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1127 :
1128 : int32_t data;
1129 :
1130 0 : p->GetData(&data);
1131 :
1132 0 : aArgv->setInt32(data);
1133 :
1134 0 : break;
1135 : }
1136 : case nsISupportsPrimitive::TYPE_FLOAT : {
1137 0 : nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
1138 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1139 :
1140 : float data;
1141 :
1142 0 : p->GetData(&data);
1143 :
1144 0 : *aArgv = ::JS_NumberValue(data);
1145 :
1146 0 : break;
1147 : }
1148 : case nsISupportsPrimitive::TYPE_DOUBLE : {
1149 0 : nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
1150 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1151 :
1152 : double data;
1153 :
1154 0 : p->GetData(&data);
1155 :
1156 0 : *aArgv = ::JS_NumberValue(data);
1157 :
1158 0 : break;
1159 : }
1160 : case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
1161 0 : nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
1162 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1163 :
1164 0 : nsCOMPtr<nsISupports> data;
1165 0 : nsIID *iid = nullptr;
1166 :
1167 0 : p->GetData(getter_AddRefs(data));
1168 0 : p->GetDataIID(&iid);
1169 0 : NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
1170 :
1171 0 : AutoFree iidGuard(iid); // Free iid upon destruction.
1172 :
1173 0 : JS::Rooted<JSObject*> scope(cx, GetWindowProxy());
1174 0 : JS::Rooted<JS::Value> v(cx);
1175 0 : JSAutoCompartment ac(cx, scope);
1176 0 : nsresult rv = nsContentUtils::WrapNative(cx, data, iid, &v);
1177 0 : NS_ENSURE_SUCCESS(rv, rv);
1178 :
1179 0 : *aArgv = v;
1180 :
1181 0 : break;
1182 : }
1183 : case nsISupportsPrimitive::TYPE_ID :
1184 : case nsISupportsPrimitive::TYPE_PRUINT64 :
1185 : case nsISupportsPrimitive::TYPE_PRINT64 :
1186 : case nsISupportsPrimitive::TYPE_PRTIME : {
1187 0 : NS_WARNING("Unsupported primitive type used");
1188 0 : aArgv->setNull();
1189 0 : break;
1190 : }
1191 : default : {
1192 0 : NS_WARNING("Unknown primitive type used");
1193 0 : aArgv->setNull();
1194 0 : break;
1195 : }
1196 : }
1197 0 : return NS_OK;
1198 : }
1199 :
1200 : #ifdef MOZ_JPROF
1201 :
1202 : #include <signal.h>
1203 :
1204 : inline bool
1205 : IsJProfAction(struct sigaction *action)
1206 : {
1207 : return (action->sa_sigaction &&
1208 : (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
1209 : }
1210 :
1211 : void NS_JProfStartProfiling();
1212 : void NS_JProfStopProfiling();
1213 : void NS_JProfClearCircular();
1214 :
1215 : static bool
1216 : JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1217 : {
1218 : NS_JProfStartProfiling();
1219 : return true;
1220 : }
1221 :
1222 : void NS_JProfStartProfiling()
1223 : {
1224 : // Figure out whether we're dealing with SIGPROF, SIGALRM, or
1225 : // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
1226 : // JP_RTC_HZ)
1227 : struct sigaction action;
1228 :
1229 : // Must check ALRM before PROF since both are enabled for real-time
1230 : sigaction(SIGALRM, nullptr, &action);
1231 : //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1232 : if (IsJProfAction(&action)) {
1233 : //printf("Beginning real-time jprof profiling.\n");
1234 : raise(SIGALRM);
1235 : return;
1236 : }
1237 :
1238 : sigaction(SIGPROF, nullptr, &action);
1239 : //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1240 : if (IsJProfAction(&action)) {
1241 : //printf("Beginning process-time jprof profiling.\n");
1242 : raise(SIGPROF);
1243 : return;
1244 : }
1245 :
1246 : sigaction(SIGPOLL, nullptr, &action);
1247 : //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1248 : if (IsJProfAction(&action)) {
1249 : //printf("Beginning rtc-based jprof profiling.\n");
1250 : raise(SIGPOLL);
1251 : return;
1252 : }
1253 :
1254 : printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
1255 : }
1256 :
1257 : static bool
1258 : JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1259 : {
1260 : NS_JProfStopProfiling();
1261 : return true;
1262 : }
1263 :
1264 : void
1265 : NS_JProfStopProfiling()
1266 : {
1267 : raise(SIGUSR1);
1268 : //printf("Stopped jprof profiling.\n");
1269 : }
1270 :
1271 : static bool
1272 : JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1273 : {
1274 : NS_JProfClearCircular();
1275 : return true;
1276 : }
1277 :
1278 : void
1279 : NS_JProfClearCircular()
1280 : {
1281 : raise(SIGUSR2);
1282 : //printf("cleared jprof buffer\n");
1283 : }
1284 :
1285 : static bool
1286 : JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1287 : {
1288 : // Not ideal...
1289 : NS_JProfStopProfiling();
1290 : NS_JProfStartProfiling();
1291 : return true;
1292 : }
1293 :
1294 : static const JSFunctionSpec JProfFunctions[] = {
1295 : JS_FS("JProfStartProfiling", JProfStartProfilingJS, 0, 0),
1296 : JS_FS("JProfStopProfiling", JProfStopProfilingJS, 0, 0),
1297 : JS_FS("JProfClearCircular", JProfClearCircularJS, 0, 0),
1298 : JS_FS("JProfSaveCircular", JProfSaveCircularJS, 0, 0),
1299 : JS_FS_END
1300 : };
1301 :
1302 : #endif /* defined(MOZ_JPROF) */
1303 :
1304 : nsresult
1305 7 : nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
1306 : {
1307 14 : AutoJSAPI jsapi;
1308 7 : jsapi.Init();
1309 7 : JSContext* cx = jsapi.cx();
1310 14 : JSAutoCompartment ac(cx, aGlobalObj);
1311 :
1312 : // Attempt to initialize profiling functions
1313 7 : ::JS_DefineProfilingFunctions(cx, aGlobalObj);
1314 :
1315 : #ifdef MOZ_JPROF
1316 : // Attempt to initialize JProf functions
1317 : ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
1318 : #endif
1319 :
1320 14 : return NS_OK;
1321 : }
1322 :
1323 : void
1324 13 : nsJSContext::WillInitializeContext()
1325 : {
1326 13 : mIsInitialized = false;
1327 13 : }
1328 :
1329 : void
1330 13 : nsJSContext::DidInitializeContext()
1331 : {
1332 13 : mIsInitialized = true;
1333 13 : }
1334 :
1335 : bool
1336 0 : nsJSContext::IsContextInitialized()
1337 : {
1338 0 : return mIsInitialized;
1339 : }
1340 :
1341 : bool
1342 5 : nsJSContext::GetProcessingScriptTag()
1343 : {
1344 5 : return mProcessingScriptTag;
1345 : }
1346 :
1347 : void
1348 10 : nsJSContext::SetProcessingScriptTag(bool aFlag)
1349 : {
1350 10 : mProcessingScriptTag = aFlag;
1351 10 : }
1352 :
1353 : void
1354 0 : FullGCTimerFired(nsITimer* aTimer, void* aClosure)
1355 : {
1356 0 : nsJSContext::KillFullGCTimer();
1357 0 : MOZ_ASSERT(!aClosure, "Don't pass a closure to FullGCTimerFired");
1358 : nsJSContext::GarbageCollectNow(JS::gcreason::FULL_GC_TIMER,
1359 0 : nsJSContext::IncrementalGC);
1360 0 : }
1361 :
1362 : //static
1363 : void
1364 3 : nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
1365 : IsIncremental aIncremental,
1366 : IsShrinking aShrinking,
1367 : int64_t aSliceMillis)
1368 : {
1369 4 : AUTO_PROFILER_LABEL_DYNAMIC("nsJSContext::GarbageCollectNow", GC,
1370 : JS::gcreason::ExplainReason(aReason));
1371 :
1372 3 : MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
1373 :
1374 3 : KillGCTimer();
1375 :
1376 : // Reset sPendingLoadCount in case the timer that fired was a
1377 : // timer we scheduled due to a normal GC timer firing while
1378 : // documents were loading. If this happens we're waiting for a
1379 : // document that is taking a long time to load, and we effectively
1380 : // ignore the fact that the currently loading documents are still
1381 : // loading and move on as if they weren't.
1382 3 : sPendingLoadCount = 0;
1383 3 : sLoadingInProgress = false;
1384 :
1385 : // We use danger::GetJSContext() since AutoJSAPI will assert if the current
1386 : // thread's context is null (such as during shutdown).
1387 3 : JSContext* cx = danger::GetJSContext();
1388 :
1389 3 : if (!nsContentUtils::XPConnect() || !cx) {
1390 0 : return;
1391 : }
1392 :
1393 3 : if (sCCLockedOut && aIncremental == IncrementalGC) {
1394 : // We're in the middle of incremental GC. Do another slice.
1395 2 : JS::PrepareForIncrementalGC(cx);
1396 2 : JS::IncrementalGCSlice(cx, aReason, aSliceMillis);
1397 2 : return;
1398 : }
1399 :
1400 1 : JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
1401 :
1402 1 : if (aIncremental == NonIncrementalGC || aReason == JS::gcreason::FULL_GC_TIMER) {
1403 0 : sNeedsFullGC = true;
1404 : }
1405 :
1406 1 : if (sNeedsFullGC) {
1407 1 : JS::PrepareForFullGC(cx);
1408 : } else {
1409 0 : CycleCollectedJSRuntime::Get()->PrepareWaitingZonesForGC();
1410 : }
1411 :
1412 1 : if (aIncremental == IncrementalGC) {
1413 1 : JS::StartIncrementalGC(cx, gckind, aReason, aSliceMillis);
1414 : } else {
1415 0 : JS::GCForReason(cx, gckind, aReason);
1416 : }
1417 : }
1418 :
1419 : static void
1420 0 : FinishAnyIncrementalGC()
1421 : {
1422 0 : AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GC);
1423 :
1424 0 : if (sCCLockedOut) {
1425 0 : AutoJSAPI jsapi;
1426 0 : jsapi.Init();
1427 :
1428 : // We're in the middle of an incremental GC, so finish it.
1429 0 : JS::PrepareForIncrementalGC(jsapi.cx());
1430 0 : JS::FinishIncrementalGC(jsapi.cx(), JS::gcreason::CC_FORCED);
1431 : }
1432 0 : }
1433 :
1434 : static void
1435 0 : FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless,
1436 : TimeStamp aDeadline)
1437 : {
1438 : AutoProfilerTracing
1439 0 : tracing("CC", aDeadline.IsNull() ? "ForgetSkippable" : "IdleForgetSkippable");
1440 0 : PRTime startTime = PR_Now();
1441 0 : TimeStamp startTimeStamp = TimeStamp::Now();
1442 0 : FinishAnyIncrementalGC();
1443 : bool earlyForgetSkippable =
1444 0 : sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS;
1445 :
1446 0 : int64_t budgetMs = aDeadline.IsNull() ?
1447 : kForgetSkippableSliceDuration :
1448 0 : int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
1449 0 : js::SliceBudget budget = js::SliceBudget(js::TimeBudget(budgetMs));
1450 0 : nsCycleCollector_forgetSkippable(budget, aRemoveChildless, earlyForgetSkippable);
1451 :
1452 0 : sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
1453 0 : ++sCleanupsSinceLastGC;
1454 0 : PRTime delta = PR_Now() - startTime;
1455 0 : if (sMinForgetSkippableTime > delta) {
1456 0 : sMinForgetSkippableTime = delta;
1457 : }
1458 0 : if (sMaxForgetSkippableTime < delta) {
1459 0 : sMaxForgetSkippableTime = delta;
1460 : }
1461 0 : sTotalForgetSkippableTime += delta;
1462 0 : sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
1463 0 : ++sForgetSkippableBeforeCC;
1464 :
1465 0 : TimeStamp now = TimeStamp::Now();
1466 0 : TimeDuration duration = now - startTimeStamp;
1467 0 : if (duration.ToSeconds()) {
1468 0 : TimeDuration idleDuration;
1469 0 : if (!aDeadline.IsNull()) {
1470 0 : if (aDeadline < now) {
1471 : // This slice overflowed the idle period.
1472 0 : idleDuration = aDeadline - startTimeStamp;
1473 : } else {
1474 0 : idleDuration = duration;
1475 : }
1476 : }
1477 :
1478 : uint32_t percent =
1479 0 : uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
1480 0 : Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE, percent);
1481 : }
1482 0 : }
1483 :
1484 : MOZ_ALWAYS_INLINE
1485 : static uint32_t
1486 0 : TimeBetween(TimeStamp start, TimeStamp end)
1487 : {
1488 0 : MOZ_ASSERT(end >= start);
1489 0 : return (uint32_t) ((end - start).ToMilliseconds());
1490 : }
1491 :
1492 : static uint32_t
1493 0 : TimeUntilNow(TimeStamp start)
1494 : {
1495 0 : if (start.IsNull()) {
1496 0 : return 0;
1497 : }
1498 0 : return TimeBetween(start, TimeStamp::Now());
1499 : }
1500 :
1501 : struct CycleCollectorStats
1502 : {
1503 : constexpr CycleCollectorStats() :
1504 : mMaxGCDuration(0), mRanSyncForgetSkippable(false), mSuspected(0),
1505 : mMaxSkippableDuration(0), mMaxSliceTime(0), mMaxSliceTimeSinceClear(0),
1506 : mTotalSliceTime(0), mAnyLockedOut(false), mFile(nullptr)
1507 : {}
1508 :
1509 3 : void Init()
1510 : {
1511 3 : Clear();
1512 3 : mMaxSliceTimeSinceClear = 0;
1513 :
1514 3 : char* env = getenv("MOZ_CCTIMER");
1515 3 : if (!env) {
1516 3 : return;
1517 : }
1518 0 : if (strcmp(env, "none") == 0) {
1519 0 : mFile = nullptr;
1520 0 : } else if (strcmp(env, "stdout") == 0) {
1521 0 : mFile = stdout;
1522 0 : } else if (strcmp(env, "stderr") == 0) {
1523 0 : mFile = stderr;
1524 : } else {
1525 0 : mFile = fopen(env, "a");
1526 0 : if (!mFile) {
1527 0 : MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
1528 : }
1529 : }
1530 : }
1531 :
1532 3 : void Clear()
1533 : {
1534 3 : if (mFile && mFile != stdout && mFile != stderr) {
1535 0 : fclose(mFile);
1536 : }
1537 3 : mBeginSliceTime = TimeStamp();
1538 3 : mEndSliceTime = TimeStamp();
1539 3 : mBeginTime = TimeStamp();
1540 3 : mMaxGCDuration = 0;
1541 3 : mRanSyncForgetSkippable = false;
1542 3 : mSuspected = 0;
1543 3 : mMaxSkippableDuration = 0;
1544 3 : mMaxSliceTime = 0;
1545 3 : mTotalSliceTime = 0;
1546 3 : mAnyLockedOut = false;
1547 3 : }
1548 :
1549 : void PrepareForCycleCollectionSlice(TimeStamp aDeadline = TimeStamp());
1550 :
1551 0 : void FinishCycleCollectionSlice()
1552 : {
1553 0 : if (mBeginSliceTime.IsNull()) {
1554 : // We already called this method from EndCycleCollectionCallback for this slice.
1555 0 : return;
1556 : }
1557 :
1558 0 : mEndSliceTime = TimeStamp::Now();
1559 0 : TimeDuration duration = mEndSliceTime - mBeginSliceTime;
1560 :
1561 0 : if (duration.ToSeconds()) {
1562 0 : TimeDuration idleDuration;
1563 0 : if (!mIdleDeadline.IsNull()) {
1564 0 : if (mIdleDeadline < mEndSliceTime) {
1565 : // This slice overflowed the idle period.
1566 0 : idleDuration = mIdleDeadline - mBeginSliceTime;
1567 : } else {
1568 0 : idleDuration = duration;
1569 : }
1570 : }
1571 :
1572 : uint32_t percent =
1573 0 : uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
1574 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE,
1575 0 : percent);
1576 : }
1577 :
1578 0 : uint32_t sliceTime = TimeBetween(mBeginSliceTime, mEndSliceTime);
1579 0 : mMaxSliceTime = std::max(mMaxSliceTime, sliceTime);
1580 0 : mMaxSliceTimeSinceClear = std::max(mMaxSliceTimeSinceClear, sliceTime);
1581 0 : mTotalSliceTime += sliceTime;
1582 0 : mBeginSliceTime = TimeStamp();
1583 : }
1584 :
1585 : void RunForgetSkippable();
1586 :
1587 : // Time the current slice began, including any GC finishing.
1588 : TimeStamp mBeginSliceTime;
1589 :
1590 : // Time the previous slice of the current CC ended.
1591 : TimeStamp mEndSliceTime;
1592 :
1593 : // Time the current cycle collection began.
1594 : TimeStamp mBeginTime;
1595 :
1596 : // The longest GC finishing duration for any slice of the current CC.
1597 : uint32_t mMaxGCDuration;
1598 :
1599 : // True if we ran sync forget skippable in any slice of the current CC.
1600 : bool mRanSyncForgetSkippable;
1601 :
1602 : // Number of suspected objects at the start of the current CC.
1603 : uint32_t mSuspected;
1604 :
1605 : // The longest duration spent on sync forget skippable in any slice of the
1606 : // current CC.
1607 : uint32_t mMaxSkippableDuration;
1608 :
1609 : // The longest pause of any slice in the current CC.
1610 : uint32_t mMaxSliceTime;
1611 :
1612 : // The longest slice time since ClearMaxCCSliceTime() was called.
1613 : uint32_t mMaxSliceTimeSinceClear;
1614 :
1615 : // The total amount of time spent actually running the current CC.
1616 : uint32_t mTotalSliceTime;
1617 :
1618 : // True if we were locked out by the GC in any slice of the current CC.
1619 : bool mAnyLockedOut;
1620 :
1621 : // A file to dump CC activity to; set by MOZ_CCTIMER environment variable.
1622 : FILE* mFile;
1623 :
1624 : // In case CC slice was triggered during idle time, set to the end of the idle
1625 : // period.
1626 : TimeStamp mIdleDeadline;
1627 : };
1628 :
1629 : CycleCollectorStats gCCStats;
1630 :
1631 : void
1632 0 : CycleCollectorStats::PrepareForCycleCollectionSlice(TimeStamp aDeadline)
1633 : {
1634 0 : mBeginSliceTime = TimeStamp::Now();
1635 0 : mIdleDeadline = aDeadline;
1636 :
1637 : // Before we begin the cycle collection, make sure there is no active GC.
1638 0 : if (sCCLockedOut) {
1639 0 : mAnyLockedOut = true;
1640 0 : FinishAnyIncrementalGC();
1641 0 : uint32_t gcTime = TimeBetween(mBeginSliceTime, TimeStamp::Now());
1642 0 : mMaxGCDuration = std::max(mMaxGCDuration, gcTime);
1643 : }
1644 0 : }
1645 :
1646 : void
1647 0 : CycleCollectorStats::RunForgetSkippable()
1648 : {
1649 : // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1650 : // is particularly useful if we recently finished a GC.
1651 0 : TimeStamp beginForgetSkippable = TimeStamp::Now();
1652 0 : bool ranSyncForgetSkippable = false;
1653 0 : while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
1654 0 : FireForgetSkippable(nsCycleCollector_suspectedCount(), false, TimeStamp());
1655 0 : ranSyncForgetSkippable = true;
1656 : }
1657 :
1658 0 : if (ranSyncForgetSkippable) {
1659 0 : mMaxSkippableDuration =
1660 0 : std::max(mMaxSkippableDuration, TimeUntilNow(beginForgetSkippable));
1661 0 : mRanSyncForgetSkippable = true;
1662 : }
1663 0 : }
1664 :
1665 : //static
1666 : void
1667 0 : nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener)
1668 : {
1669 0 : if (!NS_IsMainThread()) {
1670 0 : return;
1671 : }
1672 :
1673 0 : AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", CC);
1674 :
1675 0 : gCCStats.PrepareForCycleCollectionSlice(TimeStamp());
1676 0 : nsCycleCollector_collect(aListener);
1677 0 : gCCStats.FinishCycleCollectionSlice();
1678 : }
1679 :
1680 : //static
1681 : void
1682 0 : nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
1683 : {
1684 0 : if (!NS_IsMainThread()) {
1685 0 : return;
1686 : }
1687 :
1688 : AutoProfilerTracing
1689 0 : tracing("CC", aDeadline.IsNull() ? "CCSlice" : "IdleCCSlice");
1690 :
1691 0 : AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorSlice", CC);
1692 :
1693 0 : gCCStats.PrepareForCycleCollectionSlice(aDeadline);
1694 :
1695 : // Decide how long we want to budget for this slice. By default,
1696 : // use an unlimited budget.
1697 0 : js::SliceBudget budget = js::SliceBudget::unlimited();
1698 :
1699 0 : if (sIncrementalCC) {
1700 0 : int64_t baseBudget = kICCSliceBudget;
1701 0 : if (!aDeadline.IsNull()) {
1702 0 : baseBudget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
1703 : }
1704 :
1705 0 : if (gCCStats.mBeginTime.IsNull()) {
1706 : // If no CC is in progress, use the standard slice time.
1707 0 : budget = js::SliceBudget(js::TimeBudget(baseBudget));
1708 : } else {
1709 0 : TimeStamp now = TimeStamp::Now();
1710 :
1711 : // Only run a limited slice if we're within the max running time.
1712 0 : uint32_t runningTime = TimeBetween(gCCStats.mBeginTime, now);
1713 0 : if (runningTime < kMaxICCDuration) {
1714 0 : const float maxSlice = MainThreadIdlePeriod::GetLongIdlePeriod();
1715 :
1716 : // Try to make up for a delay in running this slice.
1717 : float sliceDelayMultiplier =
1718 0 : TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
1719 : float delaySliceBudget =
1720 0 : std::min(baseBudget * sliceDelayMultiplier, maxSlice);
1721 :
1722 : // Increase slice budgets up to |maxSlice| as we approach
1723 : // half way through the ICC, to avoid large sync CCs.
1724 0 : float percentToHalfDone = std::min(2.0f * runningTime / kMaxICCDuration, 1.0f);
1725 0 : float laterSliceBudget = maxSlice * percentToHalfDone;
1726 :
1727 0 : budget = js::SliceBudget(js::TimeBudget(std::max({delaySliceBudget,
1728 0 : laterSliceBudget, (float)baseBudget})));
1729 : }
1730 : }
1731 : }
1732 :
1733 0 : nsCycleCollector_collectSlice(budget,
1734 0 : aDeadline.IsNull() ||
1735 0 : (aDeadline - TimeStamp::Now()).ToMilliseconds() <
1736 0 : kICCSliceBudget);
1737 :
1738 0 : gCCStats.FinishCycleCollectionSlice();
1739 : }
1740 :
1741 : //static
1742 : void
1743 0 : nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget)
1744 : {
1745 0 : if (!NS_IsMainThread()) {
1746 0 : return;
1747 : }
1748 :
1749 0 : AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", CC);
1750 :
1751 0 : gCCStats.PrepareForCycleCollectionSlice();
1752 :
1753 0 : js::SliceBudget budget = js::SliceBudget(js::WorkBudget(aWorkBudget));
1754 0 : nsCycleCollector_collectSlice(budget);
1755 :
1756 0 : gCCStats.FinishCycleCollectionSlice();
1757 : }
1758 :
1759 : void
1760 0 : nsJSContext::ClearMaxCCSliceTime()
1761 : {
1762 0 : gCCStats.mMaxSliceTimeSinceClear = 0;
1763 0 : }
1764 :
1765 : uint32_t
1766 0 : nsJSContext::GetMaxCCSliceTimeSinceClear()
1767 : {
1768 0 : return gCCStats.mMaxSliceTimeSinceClear;
1769 : }
1770 :
1771 : static bool
1772 0 : ICCRunnerFired(TimeStamp aDeadline, void* aData)
1773 : {
1774 0 : if (sDidShutdown) {
1775 0 : return false;
1776 : }
1777 :
1778 : // Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
1779 : // to synchronously finish the GC, which is bad.
1780 :
1781 0 : if (sCCLockedOut) {
1782 0 : PRTime now = PR_Now();
1783 0 : if (sCCLockedOutTime == 0) {
1784 0 : sCCLockedOutTime = now;
1785 0 : return false;
1786 : }
1787 0 : if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
1788 0 : return false;
1789 : }
1790 : }
1791 :
1792 0 : nsJSContext::RunCycleCollectorSlice(aDeadline);
1793 0 : return true;
1794 : }
1795 :
1796 : //static
1797 : void
1798 0 : nsJSContext::BeginCycleCollectionCallback()
1799 : {
1800 0 : MOZ_ASSERT(NS_IsMainThread());
1801 :
1802 0 : gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
1803 0 : gCCStats.mSuspected = nsCycleCollector_suspectedCount();
1804 :
1805 0 : KillCCRunner();
1806 :
1807 0 : gCCStats.RunForgetSkippable();
1808 :
1809 0 : MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
1810 :
1811 : // Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
1812 : // an incremental collection, and we want to be sure to finish it.
1813 0 : sICCRunner = CollectorRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
1814 0 : kIdleICCSliceBudget, true);
1815 0 : }
1816 :
1817 : static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
1818 :
1819 : //static
1820 : void
1821 0 : nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
1822 : {
1823 0 : MOZ_ASSERT(NS_IsMainThread());
1824 :
1825 0 : nsJSContext::KillICCRunner();
1826 :
1827 : // Update timing information for the current slice before we log it, if
1828 : // we previously called PrepareForCycleCollectionSlice(). During shutdown
1829 : // CCs, this won't happen.
1830 0 : gCCStats.FinishCycleCollectionSlice();
1831 :
1832 0 : sCCollectedWaitingForGC += aResults.mFreedGCed;
1833 0 : sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
1834 :
1835 0 : TimeStamp endCCTimeStamp = TimeStamp::Now();
1836 0 : uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
1837 :
1838 0 : if (NeedsGCAfterCC()) {
1839 0 : PokeGC(JS::gcreason::CC_WAITING, nullptr,
1840 0 : NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
1841 : }
1842 :
1843 : // Log information about the CC via telemetry, JSON and the console.
1844 0 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut);
1845 0 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable);
1846 0 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
1847 0 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE, gCCStats.mMaxSliceTime);
1848 :
1849 0 : if (!sLastCCEndTime.IsNull()) {
1850 : // TimeBetween returns milliseconds, but we want to report seconds.
1851 0 : uint32_t timeBetween = TimeBetween(sLastCCEndTime, gCCStats.mBeginTime) / 1000;
1852 0 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
1853 : }
1854 0 : sLastCCEndTime = endCCTimeStamp;
1855 :
1856 0 : Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
1857 0 : sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
1858 :
1859 0 : PRTime delta = GetCollectionTimeDelta();
1860 :
1861 0 : uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
1862 0 : uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX)
1863 0 : ? 0 : sMinForgetSkippableTime;
1864 :
1865 0 : if (sPostGCEventsToConsole || gCCStats.mFile) {
1866 0 : nsCString mergeMsg;
1867 0 : if (aResults.mMergedZones) {
1868 0 : mergeMsg.AssignLiteral(" merged");
1869 : }
1870 :
1871 0 : nsCString gcMsg;
1872 0 : if (aResults.mForcedGC) {
1873 0 : gcMsg.AssignLiteral(", forced a GC");
1874 : }
1875 :
1876 : const char16_t *kFmt =
1877 : u"CC(T+%.1f)[%s-%i] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
1878 0 : u"ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu";
1879 0 : nsString msg;
1880 0 : msg.Adopt(nsTextFormatter::smprintf(kFmt, double(delta) / PR_USEC_PER_SEC,
1881 : ProcessNameForCollectorLog(), getpid(),
1882 : gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
1883 : aResults.mNumSlices, gCCStats.mSuspected,
1884 : aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
1885 : aResults.mFreedRefCounted, aResults.mFreedGCed,
1886 : sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
1887 : gcMsg.get(),
1888 : sForgetSkippableBeforeCC,
1889 0 : minForgetSkippableTime / PR_USEC_PER_MSEC,
1890 0 : sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
1891 0 : (sTotalForgetSkippableTime / cleanups) /
1892 : PR_USEC_PER_MSEC,
1893 0 : sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
1894 0 : gCCStats.mMaxSkippableDuration, sRemovedPurples));
1895 0 : if (sPostGCEventsToConsole) {
1896 : nsCOMPtr<nsIConsoleService> cs =
1897 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1898 0 : if (cs) {
1899 0 : cs->LogStringMessage(msg.get());
1900 : }
1901 : }
1902 0 : if (gCCStats.mFile) {
1903 0 : fprintf(gCCStats.mFile, "%s\n", NS_ConvertUTF16toUTF8(msg).get());
1904 : }
1905 : }
1906 :
1907 0 : if (sPostGCEventsToObserver) {
1908 : const char16_t* kJSONFmt =
1909 : u"{ \"timestamp\": %llu, "
1910 : u"\"duration\": %lu, "
1911 : u"\"max_slice_pause\": %lu, "
1912 : u"\"total_slice_pause\": %lu, "
1913 : u"\"max_finish_gc_duration\": %lu, "
1914 : u"\"max_sync_skippable_duration\": %lu, "
1915 : u"\"suspected\": %lu, "
1916 : u"\"visited\": { "
1917 : u"\"RCed\": %lu, "
1918 : u"\"GCed\": %lu }, "
1919 : u"\"collected\": { "
1920 : u"\"RCed\": %lu, "
1921 : u"\"GCed\": %lu }, "
1922 : u"\"waiting_for_gc\": %lu, "
1923 : u"\"zones_waiting_for_gc\": %lu, "
1924 : u"\"short_living_objects_waiting_for_gc\": %lu, "
1925 : u"\"forced_gc\": %d, "
1926 : u"\"forget_skippable\": { "
1927 : u"\"times_before_cc\": %lu, "
1928 : u"\"min\": %lu, "
1929 : u"\"max\": %lu, "
1930 : u"\"avg\": %lu, "
1931 : u"\"total\": %lu, "
1932 : u"\"removed\": %lu } "
1933 0 : u"}";
1934 0 : nsString json;
1935 :
1936 0 : json.Adopt(nsTextFormatter::smprintf(kJSONFmt, PR_Now(), ccNowDuration,
1937 : gCCStats.mMaxSliceTime,
1938 : gCCStats.mTotalSliceTime,
1939 : gCCStats.mMaxGCDuration,
1940 : gCCStats.mMaxSkippableDuration,
1941 : gCCStats.mSuspected,
1942 : aResults.mVisitedRefCounted, aResults.mVisitedGCed,
1943 : aResults.mFreedRefCounted, aResults.mFreedGCed,
1944 : sCCollectedWaitingForGC,
1945 : sCCollectedZonesWaitingForGC,
1946 : sLikelyShortLivingObjectsNeedingGC,
1947 0 : aResults.mForcedGC,
1948 : sForgetSkippableBeforeCC,
1949 0 : minForgetSkippableTime / PR_USEC_PER_MSEC,
1950 0 : sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
1951 0 : (sTotalForgetSkippableTime / cleanups) /
1952 : PR_USEC_PER_MSEC,
1953 0 : sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
1954 0 : sRemovedPurples));
1955 0 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
1956 0 : if (observerService) {
1957 0 : observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get());
1958 : }
1959 : }
1960 :
1961 : // Update global state to indicate we have just run a cycle collection.
1962 0 : sMinForgetSkippableTime = UINT32_MAX;
1963 0 : sMaxForgetSkippableTime = 0;
1964 0 : sTotalForgetSkippableTime = 0;
1965 0 : sRemovedPurples = 0;
1966 0 : sForgetSkippableBeforeCC = 0;
1967 0 : sNeedsFullCC = false;
1968 0 : sNeedsGCAfterCC = false;
1969 0 : gCCStats.Clear();
1970 0 : }
1971 :
1972 : // static
1973 : bool
1974 3 : InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
1975 : {
1976 3 : nsJSContext::KillInterSliceGCRunner();
1977 3 : MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
1978 : // We use longer budgets when timer runs since that means
1979 : // there hasn't been idle time recently and we may have significant amount
1980 : // garbage to collect.
1981 3 : int64_t budget = sActiveIntersliceGCBudget * 2;
1982 3 : if (!aDeadline.IsNull()) {
1983 0 : budget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
1984 : }
1985 :
1986 3 : TimeStamp startTimeStamp = TimeStamp::Now();
1987 3 : TimeDuration duration = sGCUnnotifiedTotalTime;
1988 3 : uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
1989 3 : nsJSContext::GarbageCollectNow(aData ?
1990 : static_cast<JS::gcreason::Reason>(reason) :
1991 : JS::gcreason::INTER_SLICE_GC,
1992 : nsJSContext::IncrementalGC,
1993 : nsJSContext::NonShrinkingGC,
1994 3 : budget);
1995 :
1996 3 : sGCUnnotifiedTotalTime = TimeDuration();
1997 3 : TimeStamp now = TimeStamp::Now();
1998 3 : TimeDuration sliceDuration = now - startTimeStamp;
1999 3 : duration += sliceDuration;
2000 3 : if (duration.ToSeconds()) {
2001 3 : TimeDuration idleDuration;
2002 3 : if (!aDeadline.IsNull()) {
2003 0 : if (aDeadline < now) {
2004 : // This slice overflowed the idle period.
2005 0 : idleDuration = aDeadline - startTimeStamp;
2006 : } else {
2007 : // Note, we don't want to use duration here, since it may contain
2008 : // data also from JS engine triggered GC slices.
2009 0 : idleDuration = sliceDuration;
2010 : }
2011 : }
2012 :
2013 : uint32_t percent =
2014 3 : uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
2015 3 : Telemetry::Accumulate(Telemetry::GC_SLICE_DURING_IDLE, percent);
2016 : }
2017 3 : return true;
2018 : }
2019 :
2020 : // static
2021 : void
2022 1 : GCTimerFired(nsITimer *aTimer, void *aClosure)
2023 : {
2024 1 : nsJSContext::KillGCTimer();
2025 : // Now start the actual GC after initial timer has fired.
2026 2 : sInterSliceGCRunner = CollectorRunner::Create(InterSliceGCRunnerFired,
2027 : NS_INTERSLICE_GC_DELAY,
2028 : sActiveIntersliceGCBudget,
2029 : false,
2030 1 : aClosure);
2031 1 : }
2032 :
2033 : // static
2034 : void
2035 0 : ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
2036 : {
2037 0 : nsJSContext::KillShrinkingGCTimer();
2038 0 : sIsCompactingOnUserInactive = true;
2039 : nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
2040 : nsJSContext::IncrementalGC,
2041 0 : nsJSContext::ShrinkingGC);
2042 0 : }
2043 :
2044 : static bool
2045 3 : ShouldTriggerCC(uint32_t aSuspected)
2046 : {
2047 3 : return sNeedsFullCC ||
2048 6 : aSuspected > NS_CC_PURPLE_LIMIT ||
2049 0 : (aSuspected > NS_CC_FORCED_PURPLE_LIMIT &&
2050 3 : TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
2051 : }
2052 :
2053 : static bool
2054 0 : CCRunnerFired(TimeStamp aDeadline, void* aData)
2055 : {
2056 0 : if (sDidShutdown) {
2057 0 : return false;
2058 : }
2059 :
2060 : static uint32_t ccDelay = NS_CC_DELAY;
2061 0 : if (sCCLockedOut) {
2062 0 : ccDelay = NS_CC_DELAY / 3;
2063 :
2064 0 : PRTime now = PR_Now();
2065 0 : if (sCCLockedOutTime == 0) {
2066 : // Reset sCCRunnerFireCount so that we run forgetSkippable
2067 : // often enough before CC. Because of reduced ccDelay
2068 : // forgetSkippable will be called just a few times.
2069 : // NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
2070 : // forgetSkippable and CycleCollectNow eventually.
2071 0 : sCCRunnerFireCount = 0;
2072 0 : sCCLockedOutTime = now;
2073 0 : return false;
2074 : }
2075 0 : if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
2076 0 : return false;
2077 : }
2078 : }
2079 :
2080 0 : ++sCCRunnerFireCount;
2081 :
2082 0 : bool didDoWork = false;
2083 :
2084 : // During early timer fires, we only run forgetSkippable. During the first
2085 : // late timer fire, we decide if we are going to have a second and final
2086 : // late timer fire, where we may begin to run the CC. Should run at least one
2087 : // early timer fire to allow cleanup before the CC.
2088 0 : int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1);
2089 0 : bool isLateTimerFire = sCCRunnerFireCount > numEarlyTimerFires;
2090 0 : uint32_t suspected = nsCycleCollector_suspectedCount();
2091 0 : if (isLateTimerFire && ShouldTriggerCC(suspected)) {
2092 0 : if (sCCRunnerFireCount == numEarlyTimerFires + 1) {
2093 0 : FireForgetSkippable(suspected, true, aDeadline);
2094 0 : didDoWork = true;
2095 0 : if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2096 : // Our efforts to avoid a CC have failed, so we return to let the
2097 : // timer fire once more to trigger a CC.
2098 :
2099 : // Clear content unbinder before the first CC slice.
2100 0 : Element::ClearContentUnbinder();
2101 : // And trigger deferred deletion too.
2102 0 : nsCycleCollector_doDeferredDeletion();
2103 0 : return didDoWork;
2104 : }
2105 : } else {
2106 : // We are in the final timer fire and still meet the conditions for
2107 : // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
2108 : // any because that will allow us to include the GC time in the CC pause.
2109 0 : nsJSContext::RunCycleCollectorSlice(aDeadline);
2110 0 : didDoWork = true;
2111 : }
2112 0 : } else if (((sPreviousSuspectedCount + 100) <= suspected) ||
2113 0 : (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
2114 : // Only do a forget skippable if there are more than a few new objects
2115 : // or we're doing the initial forget skippables.
2116 0 : FireForgetSkippable(suspected, false, aDeadline);
2117 0 : didDoWork = true;
2118 : }
2119 :
2120 0 : if (isLateTimerFire) {
2121 0 : ccDelay = NS_CC_DELAY;
2122 :
2123 : // We have either just run the CC or decided we don't want to run the CC
2124 : // next time, so kill the timer.
2125 0 : sPreviousSuspectedCount = 0;
2126 0 : nsJSContext::KillCCRunner();
2127 : }
2128 :
2129 0 : return didDoWork;
2130 : }
2131 :
2132 : // static
2133 : uint32_t
2134 0 : nsJSContext::CleanupsSinceLastGC()
2135 : {
2136 0 : return sCleanupsSinceLastGC;
2137 : }
2138 :
2139 : // static
2140 : void
2141 8 : nsJSContext::LoadStart()
2142 : {
2143 8 : sLoadingInProgress = true;
2144 8 : ++sPendingLoadCount;
2145 8 : }
2146 :
2147 : // static
2148 : void
2149 6 : nsJSContext::LoadEnd()
2150 : {
2151 6 : if (!sLoadingInProgress)
2152 0 : return;
2153 :
2154 : // sPendingLoadCount is not a well managed load counter (and doesn't
2155 : // need to be), so make sure we don't make it wrap backwards here.
2156 6 : if (sPendingLoadCount > 0) {
2157 6 : --sPendingLoadCount;
2158 6 : return;
2159 : }
2160 :
2161 0 : sLoadingInProgress = false;
2162 : }
2163 :
2164 : // Only trigger expensive timers when they have been checked a number of times.
2165 : static bool
2166 0 : ReadyToTriggerExpensiveCollectorTimer()
2167 : {
2168 0 : bool ready = kPokesBetweenExpensiveCollectorTriggers < ++sExpensiveCollectorPokes;
2169 0 : if (ready) {
2170 0 : sExpensiveCollectorPokes = 0;
2171 : }
2172 0 : return ready;
2173 : }
2174 :
2175 :
2176 : // Check all of the various collector timers/runners and see if they are waiting to fire.
2177 : // For the synchronous collector timers/runners, sGCTimer and sCCRunner, we only want to
2178 : // trigger the collection occasionally, because they are expensive. The incremental collector
2179 : // timers, sInterSliceGCRunner and sICCRunner, are fast and need to be run many times, so
2180 : // always run their corresponding timer.
2181 :
2182 : // This does not check sFullGCTimer, as that's a more expensive collection we run
2183 : // on a long timer.
2184 :
2185 : // static
2186 : void
2187 0 : nsJSContext::RunNextCollectorTimer()
2188 : {
2189 0 : if (sShuttingDown) {
2190 0 : return;
2191 : }
2192 :
2193 0 : if (sGCTimer) {
2194 0 : if (ReadyToTriggerExpensiveCollectorTimer()) {
2195 0 : GCTimerFired(nullptr, reinterpret_cast<void *>(JS::gcreason::DOM_WINDOW_UTILS));
2196 : }
2197 0 : return;
2198 : }
2199 :
2200 0 : if (sInterSliceGCRunner) {
2201 0 : InterSliceGCRunnerFired(TimeStamp(), nullptr);
2202 0 : return;
2203 : }
2204 :
2205 : // Check the CC timers after the GC timers, because the CC timers won't do
2206 : // anything if a GC is in progress.
2207 0 : MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
2208 :
2209 0 : if (sCCRunner) {
2210 0 : if (ReadyToTriggerExpensiveCollectorTimer()) {
2211 0 : CCRunnerFired(TimeStamp(), nullptr);
2212 : }
2213 0 : return;
2214 : }
2215 :
2216 0 : if (sICCRunner) {
2217 0 : ICCRunnerFired(TimeStamp(), nullptr);
2218 0 : return;
2219 : }
2220 : }
2221 :
2222 : // static
2223 : void
2224 12 : nsJSContext::PokeGC(JS::gcreason::Reason aReason,
2225 : JSObject* aObj,
2226 : int aDelay)
2227 : {
2228 12 : if (sShuttingDown) {
2229 0 : return;
2230 : }
2231 :
2232 12 : if (aObj) {
2233 11 : JS::Zone* zone = JS::GetObjectZone(aObj);
2234 11 : CycleCollectedJSRuntime::Get()->AddZoneWaitingForGC(zone);
2235 1 : } else if (aReason != JS::gcreason::CC_WAITING) {
2236 1 : sNeedsFullGC = true;
2237 : }
2238 :
2239 12 : if (sGCTimer || sInterSliceGCRunner) {
2240 : // There's already a timer for GC'ing, just return
2241 10 : return;
2242 : }
2243 :
2244 2 : if (sCCRunner) {
2245 : // Make sure CC is called...
2246 0 : sNeedsFullCC = true;
2247 : // and GC after it.
2248 0 : sNeedsGCAfterCC = true;
2249 0 : return;
2250 : }
2251 :
2252 2 : if (sICCRunner) {
2253 : // Make sure GC is called after the current CC completes.
2254 : // No need to set sNeedsFullCC because we are currently running a CC.
2255 0 : sNeedsGCAfterCC = true;
2256 0 : return;
2257 : }
2258 :
2259 2 : CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
2260 :
2261 2 : if (!sGCTimer) {
2262 : // Failed to create timer (probably because we're in XPCOM shutdown)
2263 0 : return;
2264 : }
2265 :
2266 : static bool first = true;
2267 :
2268 2 : sGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
2269 3 : sGCTimer->InitWithNamedFuncCallback(GCTimerFired,
2270 : reinterpret_cast<void *>(aReason),
2271 : aDelay
2272 1 : ? aDelay
2273 : : (first
2274 : ? NS_FIRST_GC_DELAY
2275 : : NS_GC_DELAY),
2276 : nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
2277 4 : "GCTimerFired");
2278 :
2279 2 : first = false;
2280 : }
2281 :
2282 : // static
2283 : void
2284 2 : nsJSContext::PokeShrinkingGC()
2285 : {
2286 2 : if (sShrinkingGCTimer || sShuttingDown) {
2287 0 : return;
2288 : }
2289 :
2290 2 : CallCreateInstance("@mozilla.org/timer;1", &sShrinkingGCTimer);
2291 :
2292 2 : if (!sShrinkingGCTimer) {
2293 : // Failed to create timer (probably because we're in XPCOM shutdown)
2294 0 : return;
2295 : }
2296 :
2297 2 : sShrinkingGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
2298 2 : sShrinkingGCTimer->InitWithNamedFuncCallback(ShrinkingGCTimerFired, nullptr,
2299 : sCompactOnUserInactiveDelay,
2300 : nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
2301 4 : "ShrinkingGCTimerFired");
2302 : }
2303 :
2304 : // static
2305 : void
2306 1227 : nsJSContext::MaybePokeCC()
2307 : {
2308 1227 : if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
2309 1227 : return;
2310 : }
2311 :
2312 0 : uint32_t sinceLastCCEnd = TimeUntilNow(sLastCCEndTime);
2313 0 : if (sinceLastCCEnd && sinceLastCCEnd < NS_CC_DELAY) {
2314 0 : return;
2315 : }
2316 :
2317 0 : if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2318 0 : sCCRunnerFireCount = 0;
2319 :
2320 : // We can kill some objects before running forgetSkippable.
2321 0 : nsCycleCollector_dispatchDeferredDeletion();
2322 :
2323 : sCCRunner =
2324 0 : CollectorRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
2325 0 : kForgetSkippableSliceDuration, true);
2326 : }
2327 : }
2328 :
2329 : //static
2330 : void
2331 4 : nsJSContext::KillGCTimer()
2332 : {
2333 4 : if (sGCTimer) {
2334 1 : sGCTimer->Cancel();
2335 1 : NS_RELEASE(sGCTimer);
2336 : }
2337 4 : }
2338 :
2339 : void
2340 0 : nsJSContext::KillFullGCTimer()
2341 : {
2342 0 : if (sFullGCTimer) {
2343 0 : sFullGCTimer->Cancel();
2344 0 : NS_RELEASE(sFullGCTimer);
2345 : }
2346 0 : }
2347 :
2348 : void
2349 6 : nsJSContext::KillInterSliceGCRunner()
2350 : {
2351 6 : if (sInterSliceGCRunner) {
2352 3 : sInterSliceGCRunner->Cancel();
2353 3 : sInterSliceGCRunner = nullptr;
2354 : }
2355 6 : }
2356 :
2357 : //static
2358 : void
2359 1 : nsJSContext::KillShrinkingGCTimer()
2360 : {
2361 1 : if (sShrinkingGCTimer) {
2362 1 : sShrinkingGCTimer->Cancel();
2363 1 : NS_RELEASE(sShrinkingGCTimer);
2364 : }
2365 1 : }
2366 :
2367 : //static
2368 : void
2369 0 : nsJSContext::KillCCRunner()
2370 : {
2371 0 : sCCLockedOutTime = 0;
2372 0 : if (sCCRunner) {
2373 0 : sCCRunner->Cancel();
2374 0 : sCCRunner = nullptr;
2375 : }
2376 0 : }
2377 :
2378 : //static
2379 : void
2380 0 : nsJSContext::KillICCRunner()
2381 : {
2382 0 : sCCLockedOutTime = 0;
2383 :
2384 0 : if (sICCRunner) {
2385 0 : sICCRunner->Cancel();
2386 0 : sICCRunner = nullptr;
2387 : }
2388 0 : }
2389 :
2390 0 : class NotifyGCEndRunnable : public Runnable
2391 : {
2392 : nsString mMessage;
2393 :
2394 : public:
2395 0 : explicit NotifyGCEndRunnable(const nsString& aMessage)
2396 0 : : mozilla::Runnable("NotifyGCEndRunnable")
2397 0 : , mMessage(aMessage)
2398 : {
2399 0 : }
2400 :
2401 : NS_DECL_NSIRUNNABLE
2402 : };
2403 :
2404 : NS_IMETHODIMP
2405 0 : NotifyGCEndRunnable::Run()
2406 : {
2407 0 : MOZ_ASSERT(NS_IsMainThread());
2408 :
2409 0 : nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2410 0 : if (!observerService) {
2411 0 : return NS_OK;
2412 : }
2413 :
2414 0 : const char16_t oomMsg[3] = { '{', '}', 0 };
2415 0 : const char16_t *toSend = mMessage.get() ? mMessage.get() : oomMsg;
2416 0 : observerService->NotifyObservers(nullptr, "garbage-collection-statistics", toSend);
2417 :
2418 0 : return NS_OK;
2419 : }
2420 :
2421 : static void
2422 7 : DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
2423 : {
2424 7 : NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
2425 :
2426 7 : switch (aProgress) {
2427 : case JS::GC_CYCLE_BEGIN: {
2428 : // Prevent cycle collections and shrinking during incremental GC.
2429 1 : sCCLockedOut = true;
2430 1 : break;
2431 : }
2432 :
2433 : case JS::GC_CYCLE_END: {
2434 0 : PRTime delta = GetCollectionTimeDelta();
2435 :
2436 0 : if (sPostGCEventsToConsole) {
2437 0 : nsString prefix, gcstats;
2438 0 : gcstats.Adopt(aDesc.formatSummaryMessage(aCx));
2439 0 : prefix.Adopt(nsTextFormatter::smprintf(u"GC(T+%.1f)[%s-%i] ",
2440 0 : double(delta) / PR_USEC_PER_SEC,
2441 : ProcessNameForCollectorLog(),
2442 0 : getpid()));
2443 0 : nsString msg = prefix + gcstats;
2444 0 : nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2445 0 : if (cs) {
2446 0 : cs->LogStringMessage(msg.get());
2447 : }
2448 : }
2449 :
2450 0 : if (!sShuttingDown) {
2451 0 : if (sPostGCEventsToObserver || Telemetry::CanRecordExtended()) {
2452 0 : nsString json;
2453 0 : json.Adopt(aDesc.formatJSON(aCx, PR_Now()));
2454 0 : RefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(json);
2455 0 : NS_DispatchToMainThread(notify);
2456 : }
2457 : }
2458 :
2459 0 : sCCLockedOut = false;
2460 0 : sIsCompactingOnUserInactive = false;
2461 :
2462 : // May need to kill the inter-slice GC runner
2463 0 : nsJSContext::KillInterSliceGCRunner();
2464 :
2465 0 : sCCollectedWaitingForGC = 0;
2466 0 : sCCollectedZonesWaitingForGC = 0;
2467 0 : sLikelyShortLivingObjectsNeedingGC = 0;
2468 0 : sCleanupsSinceLastGC = 0;
2469 0 : sNeedsFullCC = true;
2470 0 : sHasRunGC = true;
2471 0 : nsJSContext::MaybePokeCC();
2472 :
2473 0 : if (aDesc.isZone_) {
2474 0 : if (!sFullGCTimer && !sShuttingDown) {
2475 0 : CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
2476 0 : sFullGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
2477 0 : sFullGCTimer->InitWithNamedFuncCallback(FullGCTimerFired,
2478 : nullptr,
2479 : NS_FULL_GC_DELAY,
2480 : nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
2481 0 : "FullGCTimerFired");
2482 : }
2483 : } else {
2484 0 : nsJSContext::KillFullGCTimer();
2485 : }
2486 :
2487 0 : if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2488 0 : nsCycleCollector_dispatchDeferredDeletion();
2489 : }
2490 :
2491 0 : if (!aDesc.isZone_) {
2492 0 : sNeedsFullGC = false;
2493 : }
2494 :
2495 0 : break;
2496 : }
2497 :
2498 : case JS::GC_SLICE_BEGIN:
2499 3 : break;
2500 :
2501 : case JS::GC_SLICE_END:
2502 : sGCUnnotifiedTotalTime +=
2503 3 : aDesc.lastSliceEnd(aCx) - aDesc.lastSliceStart(aCx);
2504 :
2505 : // Schedule another GC slice if the GC has more work to do.
2506 3 : nsJSContext::KillInterSliceGCRunner();
2507 3 : if (!sShuttingDown && !aDesc.isComplete_) {
2508 : sInterSliceGCRunner =
2509 6 : CollectorRunner::Create(InterSliceGCRunnerFired, NS_INTERSLICE_GC_DELAY,
2510 3 : sActiveIntersliceGCBudget, false);
2511 : }
2512 :
2513 3 : if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2514 3 : nsCycleCollector_dispatchDeferredDeletion();
2515 : }
2516 :
2517 3 : if (sPostGCEventsToConsole) {
2518 0 : nsString prefix, gcstats;
2519 0 : gcstats.Adopt(aDesc.formatSliceMessage(aCx));
2520 0 : prefix.Adopt(nsTextFormatter::smprintf(u"[%s-%i] ",
2521 : ProcessNameForCollectorLog(),
2522 0 : getpid()));
2523 0 : nsString msg = prefix + gcstats;
2524 0 : nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2525 0 : if (cs) {
2526 0 : cs->LogStringMessage(msg.get());
2527 : }
2528 : }
2529 :
2530 3 : break;
2531 :
2532 : default:
2533 0 : MOZ_CRASH("Unexpected GCProgress value");
2534 : }
2535 :
2536 7 : if (sPrevGCSliceCallback) {
2537 7 : (*sPrevGCSliceCallback)(aCx, aProgress, aDesc);
2538 : }
2539 :
2540 7 : }
2541 :
2542 : void
2543 7 : nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
2544 : {
2545 7 : mWindowProxy = aWindowProxy;
2546 7 : }
2547 :
2548 : JSObject*
2549 14 : nsJSContext::GetWindowProxy()
2550 : {
2551 14 : return mWindowProxy;
2552 : }
2553 :
2554 : void
2555 0 : nsJSContext::LikelyShortLivingObjectCreated()
2556 : {
2557 0 : ++sLikelyShortLivingObjectsNeedingGC;
2558 0 : }
2559 :
2560 : void
2561 3 : mozilla::dom::StartupJSEnvironment()
2562 : {
2563 : // initialize all our statics, so that we can restart XPCOM
2564 3 : sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
2565 3 : sCCLockedOut = false;
2566 3 : sCCLockedOutTime = 0;
2567 3 : sLastCCEndTime = TimeStamp();
2568 3 : sHasRunGC = false;
2569 3 : sPendingLoadCount = 0;
2570 3 : sLoadingInProgress = false;
2571 3 : sCCollectedWaitingForGC = 0;
2572 3 : sCCollectedZonesWaitingForGC = 0;
2573 3 : sLikelyShortLivingObjectsNeedingGC = 0;
2574 3 : sPostGCEventsToConsole = false;
2575 3 : sNeedsFullCC = false;
2576 3 : sNeedsFullGC = true;
2577 3 : sNeedsGCAfterCC = false;
2578 3 : gNameSpaceManager = nullptr;
2579 3 : sIsInitialized = false;
2580 3 : sDidShutdown = false;
2581 3 : sShuttingDown = false;
2582 3 : sContextCount = 0;
2583 3 : sSecurityManager = nullptr;
2584 3 : gCCStats.Init();
2585 3 : sExpensiveCollectorPokes = 0;
2586 3 : }
2587 :
2588 : static void
2589 36 : SetGCParameter(JSGCParamKey aParam, uint32_t aValue)
2590 : {
2591 72 : AutoJSAPI jsapi;
2592 36 : jsapi.Init();
2593 36 : JS_SetGCParameter(jsapi.cx(), aParam, aValue);
2594 36 : }
2595 :
2596 : static void
2597 2 : SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
2598 : {
2599 2 : int32_t highwatermark = Preferences::GetInt(aPrefName, 128);
2600 2 : SetGCParameter(JSGC_MAX_MALLOC_BYTES,
2601 2 : highwatermark * 1024L * 1024L);
2602 2 : }
2603 :
2604 : static void
2605 2 : SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
2606 : {
2607 2 : int32_t pref = Preferences::GetInt(aPrefName, -1);
2608 : // handle overflow and negative pref values
2609 2 : uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024;
2610 2 : SetGCParameter(JSGC_MAX_BYTES, max);
2611 2 : }
2612 :
2613 : static void
2614 4 : SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
2615 : {
2616 4 : bool enableZoneGC = Preferences::GetBool("javascript.options.mem.gc_per_zone");
2617 4 : bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
2618 : JSGCMode mode;
2619 4 : if (enableIncrementalGC) {
2620 4 : mode = JSGC_MODE_INCREMENTAL;
2621 0 : } else if (enableZoneGC) {
2622 0 : mode = JSGC_MODE_ZONE;
2623 : } else {
2624 0 : mode = JSGC_MODE_GLOBAL;
2625 : }
2626 :
2627 4 : SetGCParameter(JSGC_MODE, mode);
2628 4 : }
2629 :
2630 : static void
2631 2 : SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
2632 : {
2633 2 : int32_t pref = Preferences::GetInt(aPrefName, -1);
2634 : // handle overflow and negative pref values
2635 2 : if (pref > 0 && pref < 100000) {
2636 2 : sActiveIntersliceGCBudget = pref;
2637 2 : SetGCParameter(JSGC_SLICE_TIME_BUDGET, pref);
2638 : }
2639 2 : }
2640 :
2641 : static void
2642 2 : SetMemoryGCCompactingPrefChangedCallback(const char* aPrefName, void* aClosure)
2643 : {
2644 2 : bool pref = Preferences::GetBool(aPrefName);
2645 2 : SetGCParameter(JSGC_COMPACTING_ENABLED, pref);
2646 2 : }
2647 :
2648 : static void
2649 18 : SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure)
2650 : {
2651 18 : int32_t pref = Preferences::GetInt(aPrefName, -1);
2652 : // handle overflow and negative pref values
2653 18 : if (pref >= 0 && pref < 10000) {
2654 18 : SetGCParameter((JSGCParamKey)(intptr_t)aClosure, pref);
2655 : }
2656 18 : }
2657 :
2658 : static void
2659 2 : SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure)
2660 : {
2661 2 : bool pref = Preferences::GetBool(aPrefName);
2662 2 : SetGCParameter(JSGC_DYNAMIC_HEAP_GROWTH, pref);
2663 2 : }
2664 :
2665 : static void
2666 2 : SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
2667 : {
2668 2 : bool pref = Preferences::GetBool(aPrefName);
2669 2 : SetGCParameter(JSGC_DYNAMIC_MARK_SLICE, pref);
2670 2 : }
2671 :
2672 : static void
2673 2 : SetMemoryGCRefreshFrameSlicesEnabledPrefChangedCallback(const char* aPrefName, void* aClosure)
2674 : {
2675 2 : bool pref = Preferences::GetBool(aPrefName);
2676 2 : SetGCParameter(JSGC_REFRESH_FRAME_SLICES_ENABLED, pref);
2677 2 : }
2678 :
2679 :
2680 : static void
2681 2 : SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure)
2682 : {
2683 2 : bool pref = Preferences::GetBool(aPrefName);
2684 2 : sIncrementalCC = pref;
2685 2 : }
2686 :
2687 : static bool
2688 0 : AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
2689 : const char16_t* aBegin,
2690 : const char16_t* aLimit,
2691 : size_t* aSize,
2692 : const uint8_t** aMemory,
2693 : intptr_t *aHandle)
2694 : {
2695 : nsIPrincipal* principal =
2696 0 : nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
2697 : return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory,
2698 0 : aHandle);
2699 : }
2700 :
2701 : static JS::AsmJSCacheResult
2702 0 : AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
2703 : const char16_t* aBegin,
2704 : const char16_t* aEnd,
2705 : size_t aSize,
2706 : uint8_t** aMemory,
2707 : intptr_t* aHandle)
2708 : {
2709 : nsIPrincipal* principal =
2710 0 : nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
2711 : return asmjscache::OpenEntryForWrite(principal, aBegin, aEnd, aSize, aMemory,
2712 0 : aHandle);
2713 : }
2714 :
2715 : class AsyncTaskRunnable final : public Runnable
2716 : {
2717 0 : ~AsyncTaskRunnable()
2718 0 : {
2719 0 : MOZ_ASSERT(!mTask);
2720 0 : }
2721 :
2722 : public:
2723 0 : explicit AsyncTaskRunnable(JS::AsyncTask* aTask)
2724 0 : : mozilla::Runnable("AsyncTaskRunnable")
2725 0 : , mTask(aTask)
2726 : {
2727 0 : MOZ_ASSERT(mTask);
2728 0 : }
2729 :
2730 : protected:
2731 0 : NS_IMETHOD Run() override
2732 : {
2733 0 : MOZ_ASSERT(NS_IsMainThread());
2734 :
2735 0 : AutoJSAPI jsapi;
2736 0 : jsapi.Init();
2737 0 : mTask->finish(jsapi.cx());
2738 0 : mTask = nullptr; // mTask may delete itself
2739 :
2740 0 : return NS_OK;
2741 : }
2742 :
2743 : private:
2744 : JS::AsyncTask* mTask;
2745 : };
2746 :
2747 : static bool
2748 0 : StartAsyncTaskCallback(JSContext* aCx, JS::AsyncTask* aTask)
2749 : {
2750 0 : return true;
2751 : }
2752 :
2753 : static bool
2754 0 : FinishAsyncTaskCallback(JS::AsyncTask* aTask)
2755 : {
2756 : // AsyncTasks can finish during shutdown so cannot simply
2757 : // NS_DispatchToMainThread.
2758 0 : nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
2759 0 : if (!mainTarget) {
2760 0 : return false;
2761 : }
2762 :
2763 0 : RefPtr<AsyncTaskRunnable> r = new AsyncTaskRunnable(aTask);
2764 0 : MOZ_ALWAYS_SUCCEEDS(mainTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
2765 0 : return true;
2766 : }
2767 :
2768 : void
2769 5 : nsJSContext::EnsureStatics()
2770 : {
2771 5 : if (sIsInitialized) {
2772 3 : if (!nsContentUtils::XPConnect()) {
2773 0 : MOZ_CRASH();
2774 : }
2775 3 : return;
2776 : }
2777 :
2778 : nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
2779 2 : &sSecurityManager);
2780 2 : if (NS_FAILED(rv)) {
2781 0 : MOZ_CRASH();
2782 : }
2783 :
2784 : // Let's make sure that our main thread is the same as the xpcom main thread.
2785 2 : MOZ_ASSERT(NS_IsMainThread());
2786 :
2787 4 : AutoJSAPI jsapi;
2788 2 : jsapi.Init();
2789 :
2790 2 : sPrevGCSliceCallback = JS::SetGCSliceCallback(jsapi.cx(), DOMGCSliceCallback);
2791 :
2792 : // Set up the asm.js cache callbacks
2793 : static const JS::AsmJSCacheOps asmJSCacheOps = {
2794 : AsmJSCacheOpenEntryForRead,
2795 : asmjscache::CloseEntryForRead,
2796 : AsmJSCacheOpenEntryForWrite,
2797 : asmjscache::CloseEntryForWrite
2798 : };
2799 2 : JS::SetAsmJSCacheOps(jsapi.cx(), &asmJSCacheOps);
2800 :
2801 2 : JS::SetAsyncTaskCallbacks(jsapi.cx(), StartAsyncTaskCallback, FinishAsyncTaskCallback);
2802 :
2803 : // Set these global xpconnect options...
2804 : Preferences::RegisterCallbackAndCall(SetMemoryHighWaterMarkPrefChangedCallback,
2805 2 : "javascript.options.mem.high_water_mark");
2806 :
2807 : Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback,
2808 2 : "javascript.options.mem.max");
2809 :
2810 : Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
2811 2 : "javascript.options.mem.gc_per_zone");
2812 :
2813 : Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
2814 2 : "javascript.options.mem.gc_incremental");
2815 :
2816 : Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback,
2817 2 : "javascript.options.mem.gc_incremental_slice_ms");
2818 :
2819 : Preferences::RegisterCallbackAndCall(SetMemoryGCCompactingPrefChangedCallback,
2820 2 : "javascript.options.mem.gc_compacting");
2821 :
2822 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2823 : "javascript.options.mem.gc_high_frequency_time_limit_ms",
2824 2 : (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
2825 :
2826 : Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback,
2827 2 : "javascript.options.mem.gc_dynamic_mark_slice");
2828 :
2829 : Preferences::RegisterCallbackAndCall(SetMemoryGCRefreshFrameSlicesEnabledPrefChangedCallback,
2830 2 : "javascript.options.mem.gc_refresh_frame_slices_enabled");
2831 :
2832 : Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback,
2833 2 : "javascript.options.mem.gc_dynamic_heap_growth");
2834 :
2835 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2836 : "javascript.options.mem.gc_low_frequency_heap_growth",
2837 2 : (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
2838 :
2839 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2840 : "javascript.options.mem.gc_high_frequency_heap_growth_min",
2841 2 : (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
2842 :
2843 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2844 : "javascript.options.mem.gc_high_frequency_heap_growth_max",
2845 2 : (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
2846 :
2847 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2848 : "javascript.options.mem.gc_high_frequency_low_limit_mb",
2849 2 : (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
2850 :
2851 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2852 : "javascript.options.mem.gc_high_frequency_high_limit_mb",
2853 2 : (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
2854 :
2855 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2856 : "javascript.options.mem.gc_allocation_threshold_mb",
2857 2 : (void *)JSGC_ALLOCATION_THRESHOLD);
2858 :
2859 : Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback,
2860 2 : "dom.cycle_collector.incremental");
2861 :
2862 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2863 : "javascript.options.mem.gc_min_empty_chunk_count",
2864 2 : (void *)JSGC_MIN_EMPTY_CHUNK_COUNT);
2865 :
2866 : Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2867 : "javascript.options.mem.gc_max_empty_chunk_count",
2868 2 : (void *)JSGC_MAX_EMPTY_CHUNK_COUNT);
2869 :
2870 4 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
2871 2 : if (!obs) {
2872 0 : MOZ_CRASH();
2873 : }
2874 :
2875 : Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
2876 : "javascript.options.gc_on_memory_pressure",
2877 2 : true);
2878 :
2879 : Preferences::AddBoolVarCache(&sCompactOnUserInactive,
2880 : "javascript.options.compact_on_user_inactive",
2881 2 : true);
2882 :
2883 : Preferences::AddUintVarCache(&sCompactOnUserInactiveDelay,
2884 : "javascript.options.compact_on_user_inactive_delay",
2885 2 : NS_DEAULT_INACTIVE_GC_DELAY);
2886 :
2887 : Preferences::AddBoolVarCache(&sPostGCEventsToConsole,
2888 2 : JS_OPTIONS_DOT_STR "mem.log");
2889 : Preferences::AddBoolVarCache(&sPostGCEventsToObserver,
2890 2 : JS_OPTIONS_DOT_STR "mem.notify");
2891 :
2892 2 : nsIObserver* observer = new nsJSEnvironmentObserver();
2893 2 : obs->AddObserver(observer, "memory-pressure", false);
2894 2 : obs->AddObserver(observer, "user-interaction-inactive", false);
2895 2 : obs->AddObserver(observer, "user-interaction-active", false);
2896 2 : obs->AddObserver(observer, "quit-application", false);
2897 2 : obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
2898 :
2899 2 : sIsInitialized = true;
2900 : }
2901 :
2902 : nsScriptNameSpaceManager*
2903 3170 : mozilla::dom::GetNameSpaceManager()
2904 : {
2905 3170 : if (sDidShutdown)
2906 0 : return nullptr;
2907 :
2908 3170 : if (!gNameSpaceManager) {
2909 3 : gNameSpaceManager = new nsScriptNameSpaceManager;
2910 3 : NS_ADDREF(gNameSpaceManager);
2911 :
2912 3 : nsresult rv = gNameSpaceManager->Init();
2913 3 : NS_ENSURE_SUCCESS(rv, nullptr);
2914 : }
2915 :
2916 3170 : return gNameSpaceManager;
2917 : }
2918 :
2919 : nsScriptNameSpaceManager*
2920 0 : mozilla::dom::PeekNameSpaceManager()
2921 : {
2922 0 : return gNameSpaceManager;
2923 : }
2924 :
2925 : void
2926 0 : mozilla::dom::ShutdownJSEnvironment()
2927 : {
2928 0 : KillTimers();
2929 :
2930 0 : NS_IF_RELEASE(gNameSpaceManager);
2931 :
2932 0 : if (!sContextCount) {
2933 : // We're being shutdown, and there are no more contexts
2934 : // alive, release the security manager.
2935 0 : NS_IF_RELEASE(sSecurityManager);
2936 : }
2937 :
2938 0 : sShuttingDown = true;
2939 0 : sDidShutdown = true;
2940 0 : }
2941 :
2942 : // A fast-array class for JS. This class supports both nsIJSScriptArray and
2943 : // nsIArray. If it is JS itself providing and consuming this class, all work
2944 : // can be done via nsIJSScriptArray, and avoid the conversion of elements
2945 : // to/from nsISupports.
2946 : // When consumed by non-JS (eg, another script language), conversion is done
2947 : // on-the-fly.
2948 : class nsJSArgArray final : public nsIJSArgArray {
2949 : public:
2950 : nsJSArgArray(JSContext *aContext, uint32_t argc, const JS::Value* argv,
2951 : nsresult *prv);
2952 :
2953 : // nsISupports
2954 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2955 0 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
2956 : nsIJSArgArray)
2957 :
2958 : // nsIArray
2959 : NS_DECL_NSIARRAY
2960 :
2961 : // nsIJSArgArray
2962 : nsresult GetArgs(uint32_t* argc, void** argv) override;
2963 :
2964 : void ReleaseJSObjects();
2965 :
2966 : protected:
2967 : ~nsJSArgArray();
2968 : JSContext *mContext;
2969 : JS::Heap<JS::Value> *mArgv;
2970 : uint32_t mArgc;
2971 : };
2972 :
2973 0 : nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc,
2974 0 : const JS::Value* argv, nsresult *prv)
2975 : : mContext(aContext)
2976 : , mArgv(nullptr)
2977 0 : , mArgc(argc)
2978 : {
2979 : // copy the array - we don't know its lifetime, and ours is tied to xpcom
2980 : // refcounting.
2981 0 : if (argc) {
2982 0 : mArgv = new (fallible) JS::Heap<JS::Value>[argc];
2983 0 : if (!mArgv) {
2984 0 : *prv = NS_ERROR_OUT_OF_MEMORY;
2985 0 : return;
2986 : }
2987 : }
2988 :
2989 : // Callers are allowed to pass in a null argv even for argc > 0. They can
2990 : // then use GetArgs to initialize the values.
2991 0 : if (argv) {
2992 0 : for (uint32_t i = 0; i < argc; ++i)
2993 0 : mArgv[i] = argv[i];
2994 : }
2995 :
2996 0 : if (argc > 0) {
2997 0 : mozilla::HoldJSObjects(this);
2998 : }
2999 :
3000 0 : *prv = NS_OK;
3001 : }
3002 :
3003 0 : nsJSArgArray::~nsJSArgArray()
3004 : {
3005 0 : ReleaseJSObjects();
3006 0 : }
3007 :
3008 : void
3009 0 : nsJSArgArray::ReleaseJSObjects()
3010 : {
3011 0 : if (mArgv) {
3012 0 : delete [] mArgv;
3013 : }
3014 0 : if (mArgc > 0) {
3015 0 : mArgc = 0;
3016 0 : mozilla::DropJSObjects(this);
3017 : }
3018 0 : }
3019 :
3020 : // QueryInterface implementation for nsJSArgArray
3021 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
3022 :
3023 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
3024 0 : tmp->ReleaseJSObjects();
3025 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3026 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
3027 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3028 :
3029 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
3030 0 : if (tmp->mArgv) {
3031 0 : for (uint32_t i = 0; i < tmp->mArgc; ++i) {
3032 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv[i])
3033 : }
3034 : }
3035 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
3036 :
3037 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
3038 0 : NS_INTERFACE_MAP_ENTRY(nsIArray)
3039 0 : NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
3040 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
3041 0 : NS_INTERFACE_MAP_END
3042 :
3043 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
3044 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
3045 :
3046 : nsresult
3047 0 : nsJSArgArray::GetArgs(uint32_t *argc, void **argv)
3048 : {
3049 0 : *argv = (void *)mArgv;
3050 0 : *argc = mArgc;
3051 0 : return NS_OK;
3052 : }
3053 :
3054 : // nsIArray impl
3055 0 : NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t *aLength)
3056 : {
3057 0 : *aLength = mArgc;
3058 0 : return NS_OK;
3059 : }
3060 :
3061 0 : NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID & uuid, void * *result)
3062 : {
3063 0 : *result = nullptr;
3064 0 : if (index >= mArgc)
3065 0 : return NS_ERROR_INVALID_ARG;
3066 :
3067 0 : if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
3068 : // Have to copy a Heap into a Rooted to work with it.
3069 0 : JS::Rooted<JS::Value> val(mContext, mArgv[index]);
3070 0 : return nsContentUtils::XPConnect()->JSToVariant(mContext, val,
3071 0 : (nsIVariant **)result);
3072 : }
3073 0 : NS_WARNING("nsJSArgArray only handles nsIVariant");
3074 0 : return NS_ERROR_NO_INTERFACE;
3075 : }
3076 :
3077 0 : NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval)
3078 : {
3079 0 : return NS_ERROR_NOT_IMPLEMENTED;
3080 : }
3081 :
3082 0 : NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
3083 : {
3084 0 : return NS_ERROR_NOT_IMPLEMENTED;
3085 : }
3086 :
3087 : // The factory function
3088 0 : nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc,
3089 : const JS::Value* argv, nsIJSArgArray **aArray)
3090 : {
3091 : nsresult rv;
3092 0 : nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc, argv, &rv);
3093 0 : if (NS_FAILED(rv)) {
3094 0 : return rv;
3095 : }
3096 0 : ret.forget(aArray);
3097 0 : return NS_OK;
3098 : }
|