Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsJSEnvironment.cpp
blobce8a4e6df41c68f60440a5c97abeb0c77c5ecc35
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsError.h"
8 #include "nsJSEnvironment.h"
9 #include "nsIScriptGlobalObject.h"
10 #include "nsIScriptObjectPrincipal.h"
11 #include "nsIDOMChromeWindow.h"
12 #include "nsPIDOMWindow.h"
13 #include "nsIScriptSecurityManager.h"
14 #include "nsDOMCID.h"
15 #include "nsIServiceManager.h"
16 #include "nsIXPConnect.h"
17 #include "nsIJSRuntimeService.h"
18 #include "nsCOMPtr.h"
19 #include "nsISupportsPrimitives.h"
20 #include "nsReadableUtils.h"
21 #include "nsDOMJSUtils.h"
22 #include "nsJSUtils.h"
23 #include "nsIDocShell.h"
24 #include "nsIDocShellTreeItem.h"
25 #include "nsPresContext.h"
26 #include "nsIConsoleService.h"
27 #include "nsIScriptError.h"
28 #include "nsIInterfaceRequestor.h"
29 #include "nsIInterfaceRequestorUtils.h"
30 #include "nsIPrompt.h"
31 #include "nsIObserverService.h"
32 #include "nsITimer.h"
33 #include "nsIAtom.h"
34 #include "nsContentUtils.h"
35 #include "mozilla/EventDispatcher.h"
36 #include "nsIContent.h"
37 #include "nsCycleCollector.h"
38 #include "nsNetUtil.h"
39 #include "nsXPCOMCIDInternal.h"
40 #include "nsIXULRuntime.h"
41 #include "nsTextFormatter.h"
42 #include "ScriptSettings.h"
44 #include "xpcpublic.h"
46 #include "js/OldDebugAPI.h"
47 #include "jswrapper.h"
48 #include "nsIArray.h"
49 #include "nsIObjectInputStream.h"
50 #include "nsIObjectOutputStream.h"
51 #include "prmem.h"
52 #include "WrapperFactory.h"
53 #include "nsGlobalWindow.h"
54 #include "nsScriptNameSpaceManager.h"
55 #include "StructuredCloneTags.h"
56 #include "mozilla/AutoRestore.h"
57 #include "mozilla/dom/CryptoKey.h"
58 #include "mozilla/dom/ErrorEvent.h"
59 #include "mozilla/dom/ImageDataBinding.h"
60 #include "mozilla/dom/ImageData.h"
61 #include "mozilla/dom/StructuredClone.h"
62 #include "mozilla/dom/SubtleCryptoBinding.h"
63 #include "mozilla/ipc/BackgroundUtils.h"
64 #include "mozilla/ipc/PBackgroundSharedTypes.h"
65 #include "nsAXPCNativeCallContext.h"
66 #include "mozilla/CycleCollectedJSRuntime.h"
68 #include "nsJSPrincipals.h"
70 #ifdef XP_MACOSX
71 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
72 #undef check
73 #endif
74 #include "AccessCheck.h"
76 #ifdef MOZ_LOGGING
77 // Force PR_LOGGING so we can get JS strict warnings even in release builds
78 #define FORCE_PR_LOG 1
79 #endif
80 #include "prlog.h"
81 #include "prthread.h"
83 #include "mozilla/Preferences.h"
84 #include "mozilla/Telemetry.h"
85 #include "mozilla/dom/BindingUtils.h"
86 #include "mozilla/Attributes.h"
87 #include "mozilla/dom/asmjscache/AsmJSCache.h"
88 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
89 #include "mozilla/CycleCollectedJSRuntime.h"
90 #include "mozilla/ContentEvents.h"
92 #include "nsCycleCollectionNoteRootCallback.h"
93 #include "GeckoProfiler.h"
95 using namespace mozilla;
96 using namespace mozilla::dom;
98 const size_t gStackSize = 8192;
100 #ifdef PR_LOGGING
101 static PRLogModuleInfo* gJSDiagnostics;
102 #endif
104 // Thank you Microsoft!
105 #ifdef CompareString
106 #undef CompareString
107 #endif
109 #define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms
111 // The amount of time we wait from the first request to GC to actually
112 // doing the first GC.
113 #define NS_FIRST_GC_DELAY 10000 // ms
115 #define NS_FULL_GC_DELAY 60000 // ms
117 // Maximum amount of time that should elapse between incremental GC slices
118 #define NS_INTERSLICE_GC_DELAY 100 // ms
120 // If we haven't painted in 100ms, we allow for a longer GC budget
121 #define NS_INTERSLICE_GC_BUDGET 40 // ms
123 // The amount of time we wait between a request to CC (after GC ran)
124 // and doing the actual CC.
125 #define NS_CC_DELAY 6000 // ms
127 #define NS_CC_SKIPPABLE_DELAY 400 // ms
129 // Maximum amount of time that should elapse between incremental CC slices
130 static const int64_t kICCIntersliceDelay = 32; // ms
132 // Time budget for an incremental CC slice
133 static const int64_t kICCSliceBudget = 10; // ms
135 // Maximum total duration for an ICC
136 static const uint32_t kMaxICCDuration = 2000; // ms
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
143 // Don't allow an incremental GC to lock out the CC for too long.
144 #define NS_MAX_CC_LOCKEDOUT_TIME (15 * PR_USEC_PER_SEC) // 15 seconds
146 // Trigger a CC if the purple buffer exceeds this size when we check it.
147 #define NS_CC_PURPLE_LIMIT 200
149 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
151 // Large value used to specify that a script should run essentially forever
152 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
154 #define NS_MAJOR_FORGET_SKIPPABLE_CALLS 2
156 // if you add statics here, add them to the list in StartupJSEnvironment
158 static nsITimer *sGCTimer;
159 static nsITimer *sShrinkGCBuffersTimer;
160 static nsITimer *sCCTimer;
161 static nsITimer *sICCTimer;
162 static nsITimer *sFullGCTimer;
163 static nsITimer *sInterSliceGCTimer;
165 static TimeStamp sLastCCEndTime;
167 static bool sCCLockedOut;
168 static PRTime sCCLockedOutTime;
170 static JS::GCSliceCallback sPrevGCSliceCallback;
172 static bool sHasRunGC;
174 // The number of currently pending document loads. This count isn't
175 // guaranteed to always reflect reality and can't easily as we don't
176 // have an easy place to know when a load ends or is interrupted in
177 // all cases. This counter also gets reset if we end up GC'ing while
178 // we're waiting for a slow page to load. IOW, this count may be 0
179 // even when there are pending loads.
180 static uint32_t sPendingLoadCount;
181 static bool sLoadingInProgress;
183 static uint32_t sCCollectedWaitingForGC;
184 static uint32_t sLikelyShortLivingObjectsNeedingGC;
185 static bool sPostGCEventsToConsole;
186 static bool sPostGCEventsToObserver;
187 static int32_t sCCTimerFireCount = 0;
188 static uint32_t sMinForgetSkippableTime = UINT32_MAX;
189 static uint32_t sMaxForgetSkippableTime = 0;
190 static uint32_t sTotalForgetSkippableTime = 0;
191 static uint32_t sRemovedPurples = 0;
192 static uint32_t sForgetSkippableBeforeCC = 0;
193 static uint32_t sPreviousSuspectedCount = 0;
194 static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
195 static bool sNeedsFullCC = false;
196 static bool sNeedsGCAfterCC = false;
197 static bool sIncrementalCC = false;
199 static nsScriptNameSpaceManager *gNameSpaceManager;
201 static nsIJSRuntimeService *sRuntimeService;
203 static const char kJSRuntimeServiceContractID[] =
204 "@mozilla.org/js/xpc/RuntimeService;1";
206 static PRTime sFirstCollectionTime;
208 static JSRuntime *sRuntime;
210 static bool sIsInitialized;
211 static bool sDidShutdown;
212 static bool sShuttingDown;
213 static int32_t sContextCount;
215 static nsIScriptSecurityManager *sSecurityManager;
217 // nsJSEnvironmentObserver observes the memory-pressure notifications
218 // and forces a garbage collection and cycle collection when it happens, if
219 // the appropriate pref is set.
221 static bool sGCOnMemoryPressure;
223 // In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
224 // aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
225 // us from triggering expensive full collections too frequently.
226 static int32_t sExpensiveCollectorPokes = 0;
227 static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
229 static PRTime
230 GetCollectionTimeDelta()
232 PRTime now = PR_Now();
233 if (sFirstCollectionTime) {
234 return now - sFirstCollectionTime;
236 sFirstCollectionTime = now;
237 return 0;
240 static void
241 KillTimers()
243 nsJSContext::KillGCTimer();
244 nsJSContext::KillShrinkGCBuffersTimer();
245 nsJSContext::KillCCTimer();
246 nsJSContext::KillICCTimer();
247 nsJSContext::KillFullGCTimer();
248 nsJSContext::KillInterSliceGCTimer();
251 // If we collected a substantial amount of cycles, poke the GC since more objects
252 // might be unreachable now.
253 static bool
254 NeedsGCAfterCC()
256 return sCCollectedWaitingForGC > 250 ||
257 sLikelyShortLivingObjectsNeedingGC > 2500 ||
258 sNeedsGCAfterCC;
261 class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver
263 ~nsJSEnvironmentObserver() {}
264 public:
265 NS_DECL_ISUPPORTS
266 NS_DECL_NSIOBSERVER
269 NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver, nsIObserver)
271 NS_IMETHODIMP
272 nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
273 const char16_t* aData)
275 if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) {
276 if(StringBeginsWith(nsDependentString(aData),
277 NS_LITERAL_STRING("low-memory-ongoing"))) {
278 // Don't GC/CC if we are in an ongoing low-memory state since its very
279 // slow and it likely won't help us anyway.
280 return NS_OK;
282 nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
283 nsJSContext::NonIncrementalGC,
284 nsJSContext::ShrinkingGC);
285 nsJSContext::CycleCollectNow();
286 if (NeedsGCAfterCC()) {
287 nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
288 nsJSContext::NonIncrementalGC,
289 nsJSContext::ShrinkingGC);
291 } else if (!nsCRT::strcmp(aTopic, "quit-application")) {
292 sShuttingDown = true;
293 KillTimers();
296 return NS_OK;
299 /****************************************************************
300 ************************** AutoFree ****************************
301 ****************************************************************/
303 class AutoFree {
304 public:
305 explicit AutoFree(void* aPtr) : mPtr(aPtr) {
307 ~AutoFree() {
308 if (mPtr)
309 nsMemory::Free(mPtr);
311 void Invalidate() {
312 mPtr = 0;
314 private:
315 void *mPtr;
318 // A utility function for script languages to call. Although it looks small,
319 // the use of nsIDocShell and nsPresContext triggers a huge number of
320 // dependencies that most languages would not otherwise need.
321 // XXXmarkh - This function is mis-placed!
322 bool
323 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
324 const ErrorEventInit &aErrorEventInit,
325 nsEventStatus *aStatus)
327 bool called = false;
328 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
329 nsIDocShell *docShell = win ? win->GetDocShell() : nullptr;
330 if (docShell) {
331 nsRefPtr<nsPresContext> presContext;
332 docShell->GetPresContext(getter_AddRefs(presContext));
334 static int32_t errorDepth; // Recursion prevention
335 ++errorDepth;
337 if (errorDepth < 2) {
338 // Dispatch() must be synchronous for the recursion block
339 // (errorDepth) to work.
340 nsRefPtr<ErrorEvent> event =
341 ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(win.get()),
342 NS_LITERAL_STRING("error"),
343 aErrorEventInit);
344 event->SetTrusted(true);
346 EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
347 aStatus);
348 called = true;
350 --errorDepth;
352 return called;
355 namespace mozilla {
356 namespace dom {
358 AsyncErrorReporter::AsyncErrorReporter(JSRuntime* aRuntime,
359 JSErrorReport* aErrorReport,
360 const char* aFallbackMessage,
361 bool aIsChromeError,
362 nsPIDOMWindow* aWindow)
363 : mSourceLine(static_cast<const char16_t*>(aErrorReport->uclinebuf))
364 , mLineNumber(aErrorReport->lineno)
365 , mColumn(aErrorReport->column)
366 , mFlags(aErrorReport->flags)
368 if (!aErrorReport->filename) {
369 mFileName.SetIsVoid(true);
370 } else {
371 mFileName.AssignWithConversion(aErrorReport->filename);
374 const char16_t* m = static_cast<const char16_t*>(aErrorReport->ucmessage);
375 if (m) {
376 JSFlatString* name = js::GetErrorTypeName(aRuntime, aErrorReport->exnType);
377 if (name) {
378 AssignJSFlatString(mErrorMsg, name);
379 mErrorMsg.AppendLiteral(": ");
381 mErrorMsg.Append(m);
384 if (mErrorMsg.IsEmpty() && aFallbackMessage) {
385 mErrorMsg.AssignWithConversion(aFallbackMessage);
388 mCategory = aIsChromeError ? NS_LITERAL_CSTRING("chrome javascript") :
389 NS_LITERAL_CSTRING("content javascript");
391 mInnerWindowID = 0;
392 if (aWindow) {
393 MOZ_ASSERT(aWindow->IsInnerWindow());
394 mInnerWindowID = aWindow->WindowID();
398 void
399 AsyncErrorReporter::ReportError()
401 nsCOMPtr<nsIScriptError> errorObject =
402 do_CreateInstance("@mozilla.org/scripterror;1");
403 if (!errorObject) {
404 return;
407 nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName,
408 mSourceLine, mLineNumber,
409 mColumn, mFlags, mCategory,
410 mInnerWindowID);
411 if (NS_FAILED(rv)) {
412 return;
415 nsCOMPtr<nsIConsoleService> consoleService =
416 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
417 if (!consoleService) {
418 return;
421 consoleService->LogMessage(errorObject);
422 return;
425 } // namespace dom
426 } // namespace mozilla
428 class ScriptErrorEvent : public AsyncErrorReporter
430 public:
431 ScriptErrorEvent(JSRuntime* aRuntime,
432 JSErrorReport* aErrorReport,
433 const char* aFallbackMessage,
434 nsIPrincipal* aScriptOriginPrincipal,
435 nsIPrincipal* aGlobalPrincipal,
436 nsPIDOMWindow* aWindow,
437 JS::Handle<JS::Value> aError,
438 bool aDispatchEvent)
439 // Pass an empty category, then compute ours
440 : AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage,
441 nsContentUtils::IsSystemPrincipal(aGlobalPrincipal),
442 aWindow)
443 , mOriginPrincipal(aScriptOriginPrincipal)
444 , mDispatchEvent(aDispatchEvent)
445 , mError(aRuntime, aError)
446 , mWindow(aWindow)
448 MOZ_ASSERT_IF(mWindow, mWindow->IsInnerWindow());
451 NS_IMETHOD Run()
453 nsEventStatus status = nsEventStatus_eIgnore;
454 // First, notify the DOM that we have a script error, but only if
455 // our window is still the current inner, if we're associated with a window.
456 if (mDispatchEvent && (!mWindow || mWindow->IsCurrentInnerWindow())) {
457 nsIDocShell* docShell = mWindow ? mWindow->GetDocShell() : nullptr;
458 if (docShell &&
459 !JSREPORT_IS_WARNING(mFlags) &&
460 !sHandlingScriptError) {
461 AutoRestore<bool> recursionGuard(sHandlingScriptError);
462 sHandlingScriptError = true;
464 nsRefPtr<nsPresContext> presContext;
465 docShell->GetPresContext(getter_AddRefs(presContext));
467 ThreadsafeAutoJSContext cx;
468 RootedDictionary<ErrorEventInit> init(cx);
469 init.mCancelable = true;
470 init.mFilename = mFileName;
471 init.mBubbles = true;
473 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(mWindow));
474 NS_ENSURE_STATE(sop);
475 nsIPrincipal* p = sop->GetPrincipal();
476 NS_ENSURE_STATE(p);
478 bool sameOrigin = !mOriginPrincipal;
480 if (p && !sameOrigin) {
481 if (NS_FAILED(p->Subsumes(mOriginPrincipal, &sameOrigin))) {
482 sameOrigin = false;
486 NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
487 if (sameOrigin) {
488 init.mMessage = mErrorMsg;
489 init.mLineno = mLineNumber;
490 init.mColno = mColumn;
491 init.mError = mError;
492 } else {
493 NS_WARNING("Not same origin error!");
494 init.mMessage = xoriginMsg;
495 init.mLineno = 0;
498 nsRefPtr<ErrorEvent> event =
499 ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(mWindow.get()),
500 NS_LITERAL_STRING("error"), init);
501 event->SetTrusted(true);
503 EventDispatcher::DispatchDOMEvent(mWindow, nullptr, event, presContext,
504 &status);
508 if (status != nsEventStatus_eConsumeNoDefault) {
509 AsyncErrorReporter::ReportError();
512 return NS_OK;
515 private:
516 nsCOMPtr<nsIPrincipal> mOriginPrincipal;
517 bool mDispatchEvent;
518 JS::PersistentRootedValue mError;
519 nsCOMPtr<nsPIDOMWindow> mWindow;
521 static bool sHandlingScriptError;
524 bool ScriptErrorEvent::sHandlingScriptError = false;
526 // NOTE: This function could be refactored to use the above. The only reason
527 // it has not been done is that the code below only fills the error event
528 // after it has a good nsPresContext - whereas using the above function
529 // would involve always filling it. Is that a concern?
530 void
531 NS_ScriptErrorReporter(JSContext *cx,
532 const char *message,
533 JSErrorReport *report)
535 // We don't want to report exceptions too eagerly, but warnings in the
536 // absence of werror are swallowed whole, so report those now.
537 if (!JSREPORT_IS_WARNING(report->flags)) {
538 nsIXPConnect* xpc = nsContentUtils::XPConnect();
539 if (JS::DescribeScriptedCaller(cx)) {
540 xpc->MarkErrorUnreported(cx);
541 return;
544 if (xpc) {
545 nsAXPCNativeCallContext *cc = nullptr;
546 xpc->GetCurrentNativeCallContext(&cc);
547 if (cc) {
548 nsAXPCNativeCallContext *prev = cc;
549 while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
550 uint16_t lang;
551 if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
552 lang == nsAXPCNativeCallContext::LANG_JS) {
553 xpc->MarkErrorUnreported(cx);
554 return;
561 JS::Rooted<JS::Value> exception(cx);
562 ::JS_GetPendingException(cx, &exception);
564 // Note: we must do this before running any more code on cx.
565 ::JS_ClearPendingException(cx);
567 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
568 nsCOMPtr<nsIGlobalObject> globalObject;
569 if (nsIScriptContext* scx = GetScriptContextFromJSContext(cx)) {
570 nsCOMPtr<nsPIDOMWindow> outer = do_QueryInterface(scx->GetGlobalObject());
571 if (outer) {
572 globalObject = static_cast<nsGlobalWindow*>(outer->GetCurrentInnerWindow());
575 if (globalObject) {
577 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
578 MOZ_ASSERT_IF(win, win->IsInnerWindow());
579 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
580 do_QueryInterface(globalObject);
581 NS_ASSERTION(scriptPrincipal, "Global objects must implement "
582 "nsIScriptObjectPrincipal");
583 nsContentUtils::AddScriptRunner(
584 new ScriptErrorEvent(JS_GetRuntime(cx),
585 report,
586 message,
587 nsJSPrincipals::get(report->originPrincipals),
588 scriptPrincipal->GetPrincipal(),
589 win,
590 exception,
591 /* We do not try to report Out Of Memory via a dom
592 * event because the dom event handler would
593 * encounter an OOM exception trying to process the
594 * event, and then we'd need to generate a new OOM
595 * event for that new OOM instance -- this isn't
596 * pretty.
598 report->errorNumber != JSMSG_OUT_OF_MEMORY));
601 if (nsContentUtils::DOMWindowDumpEnabled()) {
602 // Print it to stderr as well, for the benefit of those invoking
603 // mozilla with -console.
604 nsAutoCString error;
605 error.AssignLiteral("JavaScript ");
606 if (JSREPORT_IS_STRICT(report->flags))
607 error.AppendLiteral("strict ");
608 if (JSREPORT_IS_WARNING(report->flags))
609 error.AppendLiteral("warning: ");
610 else
611 error.AppendLiteral("error: ");
612 error.Append(report->filename);
613 error.AppendLiteral(", line ");
614 error.AppendInt(report->lineno, 10);
615 error.AppendLiteral(": ");
616 if (report->ucmessage) {
617 AppendUTF16toUTF8(reinterpret_cast<const char16_t*>(report->ucmessage),
618 error);
619 } else {
620 error.Append(message);
623 fprintf(stderr, "%s\n", error.get());
624 fflush(stderr);
627 #ifdef PR_LOGGING
628 if (!gJSDiagnostics)
629 gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
631 if (gJSDiagnostics) {
632 PR_LOG(gJSDiagnostics,
633 JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
634 ("file %s, line %u: %s\n%s%s",
635 report->filename, report->lineno, message,
636 report->linebuf ? report->linebuf : "",
637 (report->linebuf &&
638 report->linebuf[strlen(report->linebuf)-1] != '\n')
639 ? "\n"
640 : ""));
642 #endif
645 #ifdef DEBUG
646 // A couple of useful functions to call when you're debugging.
647 nsGlobalWindow *
648 JSObject2Win(JSObject *obj)
650 return xpc::WindowOrNull(obj);
653 void
654 PrintWinURI(nsGlobalWindow *win)
656 if (!win) {
657 printf("No window passed in.\n");
658 return;
661 nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
662 if (!doc) {
663 printf("No document in the window.\n");
664 return;
667 nsIURI *uri = doc->GetDocumentURI();
668 if (!uri) {
669 printf("Document doesn't have a URI.\n");
670 return;
673 nsAutoCString spec;
674 uri->GetSpec(spec);
675 printf("%s\n", spec.get());
678 void
679 PrintWinCodebase(nsGlobalWindow *win)
681 if (!win) {
682 printf("No window passed in.\n");
683 return;
686 nsIPrincipal *prin = win->GetPrincipal();
687 if (!prin) {
688 printf("Window doesn't have principals.\n");
689 return;
692 nsCOMPtr<nsIURI> uri;
693 prin->GetURI(getter_AddRefs(uri));
694 if (!uri) {
695 printf("No URI, maybe the system principal.\n");
696 return;
699 nsAutoCString spec;
700 uri->GetSpec(spec);
701 printf("%s\n", spec.get());
704 void
705 DumpString(const nsAString &str)
707 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
709 #endif
711 #define JS_OPTIONS_DOT_STR "javascript.options."
713 static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
714 #ifdef JS_GC_ZEAL
715 static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
716 static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
717 #endif
718 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
719 static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify";
721 void
722 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
724 sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
725 sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
727 #ifdef JS_GC_ZEAL
728 nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
729 int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
730 int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
731 if (zeal >= 0)
732 ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
733 #endif
736 nsJSContext::nsJSContext(bool aGCOnDestruction,
737 nsIScriptGlobalObject* aGlobalObject)
738 : mWindowProxy(nullptr)
739 , mGCOnDestruction(aGCOnDestruction)
740 , mGlobalObjectRef(aGlobalObject)
742 EnsureStatics();
744 ++sContextCount;
746 mContext = ::JS_NewContext(sRuntime, gStackSize);
747 if (mContext) {
748 ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
750 // Make sure the new context gets the default context options
751 JS::ContextOptionsRef(mContext).setPrivateIsNSISupports(true);
753 // Watch for the JS boolean options
754 Preferences::RegisterCallback(JSOptionChangedCallback,
755 js_options_dot_str, this);
757 mIsInitialized = false;
758 mProcessingScriptTag = false;
759 HoldJSObjects(this);
762 nsJSContext::~nsJSContext()
764 mGlobalObjectRef = nullptr;
766 DestroyJSContext();
768 --sContextCount;
770 if (!sContextCount && sDidShutdown) {
771 // The last context is being deleted, and we're already in the
772 // process of shutting down, release the JS runtime service, and
773 // the security manager.
775 NS_IF_RELEASE(sRuntimeService);
776 NS_IF_RELEASE(sSecurityManager);
780 // This function is called either by the destructor or unlink, which means that
781 // it can never be called when there is an outstanding ref to the
782 // nsIScriptContext on the stack. Our stack-scoped cx pushers hold such a ref,
783 // so we can assume here that mContext is not on the stack (and therefore not
784 // in use).
785 void
786 nsJSContext::DestroyJSContext()
788 if (!mContext) {
789 return;
792 // Clear our entry in the JSContext, bugzilla bug 66413
793 ::JS_SetContextPrivate(mContext, nullptr);
795 // Unregister our "javascript.options.*" pref-changed callback.
796 Preferences::UnregisterCallback(JSOptionChangedCallback,
797 js_options_dot_str, this);
799 if (mGCOnDestruction) {
800 PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
803 JS_DestroyContextNoGC(mContext);
804 mContext = nullptr;
805 DropJSObjects(this);
808 // QueryInterface implementation for nsJSContext
809 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
811 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
812 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy)
813 NS_IMPL_CYCLE_COLLECTION_TRACE_END
815 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
816 NS_ASSERTION(!tmp->mContext || !js::ContextHasOutstandingRequests(tmp->mContext),
817 "Trying to unlink a context with outstanding requests.");
818 tmp->mIsInitialized = false;
819 tmp->mGCOnDestruction = false;
820 tmp->mWindowProxy = nullptr;
821 tmp->DestroyJSContext();
822 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
823 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
824 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
825 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
826 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
827 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
828 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
830 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
831 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
832 NS_INTERFACE_MAP_ENTRY(nsISupports)
833 NS_INTERFACE_MAP_END
836 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
837 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
839 nsrefcnt
840 nsJSContext::GetCCRefcnt()
842 nsrefcnt refcnt = mRefCnt.get();
844 // In the (abnormal) case of synchronous cycle-collection, the context may be
845 // actively running JS code in which case we must keep it alive by adding an
846 // extra refcount.
847 if (mContext && js::ContextHasOutstandingRequests(mContext)) {
848 refcnt++;
851 return refcnt;
854 #ifdef DEBUG
855 bool
856 AtomIsEventHandlerName(nsIAtom *aName)
858 const char16_t *name = aName->GetUTF16String();
860 const char16_t *cp;
861 char16_t c;
862 for (cp = name; *cp != '\0'; ++cp)
864 c = *cp;
865 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
866 return false;
869 return true;
871 #endif
873 nsIScriptGlobalObject *
874 nsJSContext::GetGlobalObject()
876 // Note: this could probably be simplified somewhat more; see bug 974327
877 // comments 1 and 3.
878 if (!mWindowProxy) {
879 return nullptr;
882 MOZ_ASSERT(mGlobalObjectRef);
883 return mGlobalObjectRef;
886 JSContext*
887 nsJSContext::GetNativeContext()
889 return mContext;
892 nsresult
893 nsJSContext::InitContext()
895 // Make sure callers of this use
896 // WillInitializeContext/DidInitializeContext around this call.
897 NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
899 if (!mContext)
900 return NS_ERROR_OUT_OF_MEMORY;
902 ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
904 JSOptionChangedCallback(js_options_dot_str, this);
906 return NS_OK;
909 nsresult
910 nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
912 AutoJSAPI jsapi;
913 if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject()))) {
914 return NS_ERROR_FAILURE;
916 MOZ_ASSERT(jsapi.cx() == mContext,
917 "AutoJSAPI should have found our own JSContext*");
919 JS::AutoValueVector args(mContext);
921 JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
922 nsresult rv =
923 ConvertSupportsTojsvals(aArgs, global, args);
924 NS_ENSURE_SUCCESS(rv, rv);
926 // got the arguments, now attach them.
928 for (uint32_t i = 0; i < args.length(); ++i) {
929 if (!JS_WrapValue(mContext, args[i])) {
930 return NS_ERROR_FAILURE;
934 JS::Rooted<JSObject*> array(mContext, ::JS_NewArrayObject(mContext, args));
935 if (!array) {
936 return NS_ERROR_FAILURE;
939 return JS_DefineProperty(mContext, aTarget, aPropName, array, 0) ? NS_OK : NS_ERROR_FAILURE;
942 nsresult
943 nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
944 JS::Handle<JSObject*> aScope,
945 JS::AutoValueVector& aArgsOut)
947 nsresult rv = NS_OK;
949 // If the array implements nsIJSArgArray, copy the contents and return.
950 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
951 if (fastArray) {
952 uint32_t argc;
953 JS::Value* argv;
954 rv = fastArray->GetArgs(&argc, reinterpret_cast<void **>(&argv));
955 if (NS_SUCCEEDED(rv) && !aArgsOut.append(argv, argc)) {
956 rv = NS_ERROR_OUT_OF_MEMORY;
958 return rv;
961 // Take the slower path converting each item.
962 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
963 // SetProperty('arguments', ...);
965 nsIXPConnect *xpc = nsContentUtils::XPConnect();
966 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
967 AutoJSContext cx;
969 if (!aArgs)
970 return NS_OK;
971 uint32_t argCount;
972 // This general purpose function may need to convert an arg array
973 // (window.arguments, event-handler args) and a generic property.
974 nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
976 if (argsArray) {
977 rv = argsArray->GetLength(&argCount);
978 NS_ENSURE_SUCCESS(rv, rv);
979 if (argCount == 0)
980 return NS_OK;
981 } else {
982 argCount = 1; // the nsISupports which is not an array
985 // Use the caller's auto guards to release and unroot.
986 if (!aArgsOut.resize(argCount)) {
987 return NS_ERROR_OUT_OF_MEMORY;
990 if (argsArray) {
991 for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
992 nsCOMPtr<nsISupports> arg;
993 JS::MutableHandle<JS::Value> thisVal = aArgsOut[argCtr];
994 argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
995 getter_AddRefs(arg));
996 if (!arg) {
997 thisVal.setNull();
998 continue;
1000 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
1001 if (variant != nullptr) {
1002 rv = xpc->VariantToJS(cx, aScope, variant, thisVal);
1003 } else {
1004 // And finally, support the nsISupportsPrimitives supplied
1005 // by the AppShell. It generally will pass only strings, but
1006 // as we have code for handling all, we may as well use it.
1007 rv = AddSupportsPrimitiveTojsvals(arg, thisVal.address());
1008 if (rv == NS_ERROR_NO_INTERFACE) {
1009 // something else - probably an event object or similar -
1010 // just wrap it.
1011 #ifdef DEBUG
1012 // but first, check its not another nsISupportsPrimitive, as
1013 // these are now deprecated for use with script contexts.
1014 nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
1015 NS_ASSERTION(prim == nullptr,
1016 "Don't pass nsISupportsPrimitives - use nsIVariant!");
1017 #endif
1018 JSAutoCompartment ac(cx, aScope);
1019 rv = nsContentUtils::WrapNative(cx, arg, thisVal);
1023 } else {
1024 nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
1025 if (variant) {
1026 rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut[0]);
1027 } else {
1028 NS_ERROR("Not an array, not an interface?");
1029 rv = NS_ERROR_UNEXPECTED;
1032 return rv;
1035 // This really should go into xpconnect somewhere...
1036 nsresult
1037 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv)
1039 NS_PRECONDITION(aArg, "Empty arg");
1041 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
1042 if (!argPrimitive)
1043 return NS_ERROR_NO_INTERFACE;
1045 AutoJSContext cx;
1046 uint16_t type;
1047 argPrimitive->GetType(&type);
1049 switch(type) {
1050 case nsISupportsPrimitive::TYPE_CSTRING : {
1051 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
1052 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1054 nsAutoCString data;
1056 p->GetData(data);
1059 JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
1060 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1062 *aArgv = STRING_TO_JSVAL(str);
1064 break;
1066 case nsISupportsPrimitive::TYPE_STRING : {
1067 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
1068 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1070 nsAutoString data;
1072 p->GetData(data);
1074 // cast is probably safe since wchar_t and jschar are expected
1075 // to be equivalent; both unsigned 16-bit entities
1076 JSString *str =
1077 ::JS_NewUCStringCopyN(cx, data.get(), data.Length());
1078 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1080 *aArgv = STRING_TO_JSVAL(str);
1081 break;
1083 case nsISupportsPrimitive::TYPE_PRBOOL : {
1084 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
1085 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1087 bool data;
1089 p->GetData(&data);
1091 *aArgv = BOOLEAN_TO_JSVAL(data);
1093 break;
1095 case nsISupportsPrimitive::TYPE_PRUINT8 : {
1096 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
1097 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1099 uint8_t data;
1101 p->GetData(&data);
1103 *aArgv = INT_TO_JSVAL(data);
1105 break;
1107 case nsISupportsPrimitive::TYPE_PRUINT16 : {
1108 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
1109 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1111 uint16_t data;
1113 p->GetData(&data);
1115 *aArgv = INT_TO_JSVAL(data);
1117 break;
1119 case nsISupportsPrimitive::TYPE_PRUINT32 : {
1120 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
1121 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1123 uint32_t data;
1125 p->GetData(&data);
1127 *aArgv = INT_TO_JSVAL(data);
1129 break;
1131 case nsISupportsPrimitive::TYPE_CHAR : {
1132 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
1133 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1135 char data;
1137 p->GetData(&data);
1139 JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
1140 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1142 *aArgv = STRING_TO_JSVAL(str);
1144 break;
1146 case nsISupportsPrimitive::TYPE_PRINT16 : {
1147 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
1148 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1150 int16_t data;
1152 p->GetData(&data);
1154 *aArgv = INT_TO_JSVAL(data);
1156 break;
1158 case nsISupportsPrimitive::TYPE_PRINT32 : {
1159 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
1160 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1162 int32_t data;
1164 p->GetData(&data);
1166 *aArgv = INT_TO_JSVAL(data);
1168 break;
1170 case nsISupportsPrimitive::TYPE_FLOAT : {
1171 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
1172 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1174 float data;
1176 p->GetData(&data);
1178 *aArgv = ::JS_NumberValue(data);
1180 break;
1182 case nsISupportsPrimitive::TYPE_DOUBLE : {
1183 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
1184 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1186 double data;
1188 p->GetData(&data);
1190 *aArgv = ::JS_NumberValue(data);
1192 break;
1194 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
1195 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
1196 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1198 nsCOMPtr<nsISupports> data;
1199 nsIID *iid = nullptr;
1201 p->GetData(getter_AddRefs(data));
1202 p->GetDataIID(&iid);
1203 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
1205 AutoFree iidGuard(iid); // Free iid upon destruction.
1207 JS::Rooted<JSObject*> scope(cx, GetWindowProxy());
1208 JS::Rooted<JS::Value> v(cx);
1209 JSAutoCompartment ac(cx, scope);
1210 nsresult rv = nsContentUtils::WrapNative(cx, data, iid, &v);
1211 NS_ENSURE_SUCCESS(rv, rv);
1213 *aArgv = v;
1215 break;
1217 case nsISupportsPrimitive::TYPE_ID :
1218 case nsISupportsPrimitive::TYPE_PRUINT64 :
1219 case nsISupportsPrimitive::TYPE_PRINT64 :
1220 case nsISupportsPrimitive::TYPE_PRTIME :
1221 case nsISupportsPrimitive::TYPE_VOID : {
1222 NS_WARNING("Unsupported primitive type used");
1223 *aArgv = JSVAL_NULL;
1224 break;
1226 default : {
1227 NS_WARNING("Unknown primitive type used");
1228 *aArgv = JSVAL_NULL;
1229 break;
1232 return NS_OK;
1235 #ifdef NS_TRACE_MALLOC
1237 #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
1238 #include <fcntl.h>
1239 #ifdef XP_UNIX
1240 #include <unistd.h>
1241 #endif
1242 #ifdef XP_WIN32
1243 #include <io.h>
1244 #endif
1245 #include "nsTraceMalloc.h"
1247 static bool
1248 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
1250 if (nsContentUtils::IsCallerChrome())
1251 return true;
1252 JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
1253 return false;
1256 static bool
1257 TraceMallocDisable(JSContext *cx, unsigned argc, JS::Value *vp)
1259 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1261 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1262 return false;
1264 NS_TraceMallocDisable();
1265 args.rval().setUndefined();
1266 return true;
1269 static bool
1270 TraceMallocEnable(JSContext *cx, unsigned argc, JS::Value *vp)
1272 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1274 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1275 return false;
1277 NS_TraceMallocEnable();
1278 args.rval().setUndefined();
1279 return true;
1282 static bool
1283 TraceMallocOpenLogFile(JSContext *cx, unsigned argc, JS::Value *vp)
1285 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1287 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1288 return false;
1290 int fd;
1291 if (argc == 0) {
1292 fd = -1;
1293 } else {
1294 JSString *str = JS::ToString(cx, args[0]);
1295 if (!str)
1296 return false;
1297 JSAutoByteString filename(cx, str);
1298 if (!filename)
1299 return false;
1300 fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
1301 if (fd < 0) {
1302 JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
1303 return false;
1306 args.rval().setInt32(fd);
1307 return true;
1310 static bool
1311 TraceMallocChangeLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
1313 JS::CallArgs args = CallArgsFromVp(argc, vp);
1315 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1316 return false;
1318 int32_t fd, oldfd;
1319 if (args.length() == 0) {
1320 oldfd = -1;
1321 } else {
1322 if (!JS::ToInt32(cx, args[0], &fd))
1323 return false;
1324 oldfd = NS_TraceMallocChangeLogFD(fd);
1325 if (oldfd == -2) {
1326 JS_ReportOutOfMemory(cx);
1327 return false;
1330 args.rval().setInt32(oldfd);
1331 return true;
1334 static bool
1335 TraceMallocCloseLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
1337 JS::CallArgs args = CallArgsFromVp(argc, vp);
1339 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1340 return false;
1342 int32_t fd;
1343 if (args.length() == 0) {
1344 args.rval().setUndefined();
1345 return true;
1347 if (!JS::ToInt32(cx, args[0], &fd))
1348 return false;
1349 NS_TraceMallocCloseLogFD((int) fd);
1350 args.rval().setInt32(fd);
1351 return true;
1354 static bool
1355 TraceMallocLogTimestamp(JSContext *cx, unsigned argc, JS::Value *vp)
1357 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1358 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1359 return false;
1361 JSString *str = JS::ToString(cx, args.get(0));
1362 if (!str)
1363 return false;
1364 JSAutoByteString caption(cx, str);
1365 if (!caption)
1366 return false;
1367 NS_TraceMallocLogTimestamp(caption.ptr());
1368 args.rval().setUndefined();
1369 return true;
1372 static bool
1373 TraceMallocDumpAllocations(JSContext *cx, unsigned argc, JS::Value *vp)
1375 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1376 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1377 return false;
1379 JSString *str = JS::ToString(cx, args.get(0));
1380 if (!str)
1381 return false;
1382 JSAutoByteString pathname(cx, str);
1383 if (!pathname)
1384 return false;
1385 if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
1386 JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
1387 return false;
1389 args.rval().setUndefined();
1390 return true;
1393 static const JSFunctionSpec TraceMallocFunctions[] = {
1394 JS_FS("TraceMallocDisable", TraceMallocDisable, 0, 0),
1395 JS_FS("TraceMallocEnable", TraceMallocEnable, 0, 0),
1396 JS_FS("TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0),
1397 JS_FS("TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0),
1398 JS_FS("TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0),
1399 JS_FS("TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0),
1400 JS_FS("TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0),
1401 JS_FS_END
1404 #endif /* NS_TRACE_MALLOC */
1406 #ifdef MOZ_DMD
1408 #include <errno.h>
1410 namespace mozilla {
1411 namespace dmd {
1413 // See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
1414 // how to use DMD.
1416 static FILE *
1417 OpenDMDOutputFile(JSContext *cx, JS::CallArgs &args)
1419 JSString *str = JS::ToString(cx, args.get(0));
1420 if (!str)
1421 return nullptr;
1422 JSAutoByteString pathname(cx, str);
1423 if (!pathname)
1424 return nullptr;
1426 FILE* fp = fopen(pathname.ptr(), "w");
1427 if (!fp) {
1428 JS_ReportError(cx, "DMD can't open %s: %s",
1429 pathname.ptr(), strerror(errno));
1430 return nullptr;
1432 return fp;
1435 static bool
1436 AnalyzeReports(JSContext *cx, unsigned argc, JS::Value *vp)
1438 if (!dmd::IsRunning()) {
1439 JS_ReportError(cx, "DMD is not running");
1440 return false;
1443 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1444 FILE *fp = OpenDMDOutputFile(cx, args);
1445 if (!fp) {
1446 return false;
1449 dmd::ClearReports();
1450 dmd::RunReportersForThisProcess();
1451 dmd::Writer writer(FpWrite, fp);
1452 dmd::AnalyzeReports(writer);
1454 fclose(fp);
1456 args.rval().setUndefined();
1457 return true;
1460 // This will be removed eventually.
1461 static bool
1462 ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
1464 JS_ReportWarning(cx, "DMDReportAndDump() is deprecated; "
1465 "please use DMDAnalyzeReports() instead");
1467 return AnalyzeReports(cx, argc, vp);
1470 static bool
1471 AnalyzeHeap(JSContext *cx, unsigned argc, JS::Value *vp)
1473 if (!dmd::IsRunning()) {
1474 JS_ReportError(cx, "DMD is not running");
1475 return false;
1478 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1479 FILE *fp = OpenDMDOutputFile(cx, args);
1480 if (!fp) {
1481 return false;
1484 dmd::Writer writer(FpWrite, fp);
1485 dmd::AnalyzeHeap(writer);
1487 fclose(fp);
1489 args.rval().setUndefined();
1490 return true;
1493 } // namespace dmd
1494 } // namespace mozilla
1496 static const JSFunctionSpec DMDFunctions[] = {
1497 JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0),
1498 JS_FS("DMDAnalyzeReports", dmd::AnalyzeReports, 1, 0),
1499 JS_FS("DMDAnalyzeHeap", dmd::AnalyzeHeap, 1, 0),
1500 JS_FS_END
1503 #endif // defined(MOZ_DMD)
1505 #ifdef MOZ_JPROF
1507 #include <signal.h>
1509 inline bool
1510 IsJProfAction(struct sigaction *action)
1512 return (action->sa_sigaction &&
1513 (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
1516 void NS_JProfStartProfiling();
1517 void NS_JProfStopProfiling();
1518 void NS_JProfClearCircular();
1520 static bool
1521 JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1523 NS_JProfStartProfiling();
1524 return true;
1527 void NS_JProfStartProfiling()
1529 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
1530 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
1531 // JP_RTC_HZ)
1532 struct sigaction action;
1534 // Must check ALRM before PROF since both are enabled for real-time
1535 sigaction(SIGALRM, nullptr, &action);
1536 //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1537 if (IsJProfAction(&action)) {
1538 //printf("Beginning real-time jprof profiling.\n");
1539 raise(SIGALRM);
1540 return;
1543 sigaction(SIGPROF, nullptr, &action);
1544 //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1545 if (IsJProfAction(&action)) {
1546 //printf("Beginning process-time jprof profiling.\n");
1547 raise(SIGPROF);
1548 return;
1551 sigaction(SIGPOLL, nullptr, &action);
1552 //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1553 if (IsJProfAction(&action)) {
1554 //printf("Beginning rtc-based jprof profiling.\n");
1555 raise(SIGPOLL);
1556 return;
1559 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
1562 static bool
1563 JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1565 NS_JProfStopProfiling();
1566 return true;
1569 void
1570 NS_JProfStopProfiling()
1572 raise(SIGUSR1);
1573 //printf("Stopped jprof profiling.\n");
1576 static bool
1577 JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1579 NS_JProfClearCircular();
1580 return true;
1583 void
1584 NS_JProfClearCircular()
1586 raise(SIGUSR2);
1587 //printf("cleared jprof buffer\n");
1590 static bool
1591 JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1593 // Not ideal...
1594 NS_JProfStopProfiling();
1595 NS_JProfStartProfiling();
1596 return true;
1599 static const JSFunctionSpec JProfFunctions[] = {
1600 JS_FS("JProfStartProfiling", JProfStartProfilingJS, 0, 0),
1601 JS_FS("JProfStopProfiling", JProfStopProfilingJS, 0, 0),
1602 JS_FS("JProfClearCircular", JProfClearCircularJS, 0, 0),
1603 JS_FS("JProfSaveCircular", JProfSaveCircularJS, 0, 0),
1604 JS_FS_END
1607 #endif /* defined(MOZ_JPROF) */
1609 nsresult
1610 nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
1612 JSOptionChangedCallback(js_options_dot_str, this);
1613 AutoJSAPI jsapi;
1614 jsapi.Init();
1615 JSContext* cx = jsapi.cx();
1616 JSAutoCompartment ac(cx, aGlobalObj);
1618 // Attempt to initialize profiling functions
1619 ::JS_DefineProfilingFunctions(cx, aGlobalObj);
1621 #ifdef NS_TRACE_MALLOC
1622 if (nsContentUtils::IsCallerChrome()) {
1623 // Attempt to initialize TraceMalloc functions
1624 ::JS_DefineFunctions(cx, aGlobalObj, TraceMallocFunctions);
1626 #endif
1628 #ifdef MOZ_DMD
1629 if (nsContentUtils::IsCallerChrome()) {
1630 // Attempt to initialize DMD functions
1631 ::JS_DefineFunctions(cx, aGlobalObj, DMDFunctions);
1633 #endif
1635 #ifdef MOZ_JPROF
1636 // Attempt to initialize JProf functions
1637 ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
1638 #endif
1640 return NS_OK;
1643 void
1644 nsJSContext::WillInitializeContext()
1646 mIsInitialized = false;
1649 void
1650 nsJSContext::DidInitializeContext()
1652 mIsInitialized = true;
1655 bool
1656 nsJSContext::IsContextInitialized()
1658 return mIsInitialized;
1661 bool
1662 nsJSContext::GetProcessingScriptTag()
1664 return mProcessingScriptTag;
1667 void
1668 nsJSContext::SetProcessingScriptTag(bool aFlag)
1670 mProcessingScriptTag = aFlag;
1673 void
1674 FullGCTimerFired(nsITimer* aTimer, void* aClosure)
1676 nsJSContext::KillFullGCTimer();
1677 uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
1678 nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
1679 nsJSContext::IncrementalGC);
1682 //static
1683 void
1684 nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
1685 IsIncremental aIncremental,
1686 IsShrinking aShrinking,
1687 int64_t aSliceMillis)
1689 PROFILER_LABEL("nsJSContext", "GarbageCollectNow",
1690 js::ProfileEntry::Category::GC);
1692 MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
1694 KillGCTimer();
1695 KillShrinkGCBuffersTimer();
1697 // Reset sPendingLoadCount in case the timer that fired was a
1698 // timer we scheduled due to a normal GC timer firing while
1699 // documents were loading. If this happens we're waiting for a
1700 // document that is taking a long time to load, and we effectively
1701 // ignore the fact that the currently loading documents are still
1702 // loading and move on as if they weren't.
1703 sPendingLoadCount = 0;
1704 sLoadingInProgress = false;
1706 if (!nsContentUtils::XPConnect() || !sRuntime) {
1707 return;
1710 if (sCCLockedOut && aIncremental == IncrementalGC) {
1711 // We're in the middle of incremental GC. Do another slice.
1712 JS::PrepareForIncrementalGC(sRuntime);
1713 JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
1714 return;
1717 JS::PrepareForFullGC(sRuntime);
1718 if (aIncremental == IncrementalGC) {
1719 MOZ_ASSERT(aShrinking == NonShrinkingGC);
1720 JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
1721 } else if (aShrinking == ShrinkingGC) {
1722 JS::ShrinkingGC(sRuntime, aReason);
1723 } else {
1724 JS::GCForReason(sRuntime, aReason);
1728 //static
1729 void
1730 nsJSContext::ShrinkGCBuffersNow()
1732 PROFILER_LABEL("nsJSContext", "ShrinkGCBuffersNow",
1733 js::ProfileEntry::Category::GC);
1735 KillShrinkGCBuffersTimer();
1737 JS::ShrinkGCBuffers(sRuntime);
1740 static void
1741 FinishAnyIncrementalGC()
1743 if (sCCLockedOut) {
1744 // We're in the middle of an incremental GC, so finish it.
1745 JS::PrepareForIncrementalGC(sRuntime);
1746 JS::FinishIncrementalGC(sRuntime, JS::gcreason::CC_FORCED);
1750 static void
1751 FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless)
1753 PRTime startTime = PR_Now();
1754 FinishAnyIncrementalGC();
1755 bool earlyForgetSkippable =
1756 sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS;
1757 nsCycleCollector_forgetSkippable(aRemoveChildless, earlyForgetSkippable);
1758 sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
1759 ++sCleanupsSinceLastGC;
1760 PRTime delta = PR_Now() - startTime;
1761 if (sMinForgetSkippableTime > delta) {
1762 sMinForgetSkippableTime = delta;
1764 if (sMaxForgetSkippableTime < delta) {
1765 sMaxForgetSkippableTime = delta;
1767 sTotalForgetSkippableTime += delta;
1768 sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
1769 ++sForgetSkippableBeforeCC;
1772 MOZ_ALWAYS_INLINE
1773 static uint32_t
1774 TimeBetween(TimeStamp start, TimeStamp end)
1776 MOZ_ASSERT(end >= start);
1777 return (uint32_t) ((end - start).ToMilliseconds());
1780 static uint32_t
1781 TimeUntilNow(TimeStamp start)
1783 if (start.IsNull()) {
1784 return 0;
1786 return TimeBetween(start, TimeStamp::Now());
1789 struct CycleCollectorStats
1791 void Init()
1793 Clear();
1794 mMaxSliceTimeSinceClear = 0;
1797 void Clear()
1799 mBeginSliceTime = TimeStamp();
1800 mEndSliceTime = TimeStamp();
1801 mBeginTime = TimeStamp();
1802 mMaxGCDuration = 0;
1803 mRanSyncForgetSkippable = false;
1804 mSuspected = 0;
1805 mMaxSkippableDuration = 0;
1806 mMaxSliceTime = 0;
1807 mTotalSliceTime = 0;
1808 mAnyLockedOut = false;
1809 mExtraForgetSkippableCalls = 0;
1812 void PrepareForCycleCollectionSlice(int32_t aExtraForgetSkippableCalls = 0);
1814 void FinishCycleCollectionSlice()
1816 if (mBeginSliceTime.IsNull()) {
1817 // We already called this method from EndCycleCollectionCallback for this slice.
1818 return;
1821 mEndSliceTime = TimeStamp::Now();
1822 uint32_t sliceTime = TimeBetween(mBeginSliceTime, mEndSliceTime);
1823 mMaxSliceTime = std::max(mMaxSliceTime, sliceTime);
1824 mMaxSliceTimeSinceClear = std::max(mMaxSliceTimeSinceClear, sliceTime);
1825 mTotalSliceTime += sliceTime;
1826 mBeginSliceTime = TimeStamp();
1827 MOZ_ASSERT(mExtraForgetSkippableCalls == 0, "Forget to reset extra forget skippable calls?");
1830 void RunForgetSkippable();
1832 // Time the current slice began, including any GC finishing.
1833 TimeStamp mBeginSliceTime;
1835 // Time the previous slice of the current CC ended.
1836 TimeStamp mEndSliceTime;
1838 // Time the current cycle collection began.
1839 TimeStamp mBeginTime;
1841 // The longest GC finishing duration for any slice of the current CC.
1842 uint32_t mMaxGCDuration;
1844 // True if we ran sync forget skippable in any slice of the current CC.
1845 bool mRanSyncForgetSkippable;
1847 // Number of suspected objects at the start of the current CC.
1848 uint32_t mSuspected;
1850 // The longest duration spent on sync forget skippable in any slice of the
1851 // current CC.
1852 uint32_t mMaxSkippableDuration;
1854 // The longest pause of any slice in the current CC.
1855 uint32_t mMaxSliceTime;
1857 // The longest slice time since ClearMaxCCSliceTime() was called.
1858 uint32_t mMaxSliceTimeSinceClear;
1860 // The total amount of time spent actually running the current CC.
1861 uint32_t mTotalSliceTime;
1863 // True if we were locked out by the GC in any slice of the current CC.
1864 bool mAnyLockedOut;
1866 int32_t mExtraForgetSkippableCalls;
1869 CycleCollectorStats gCCStats;
1871 void
1872 CycleCollectorStats::PrepareForCycleCollectionSlice(int32_t aExtraForgetSkippableCalls)
1874 mBeginSliceTime = TimeStamp::Now();
1876 // Before we begin the cycle collection, make sure there is no active GC.
1877 if (sCCLockedOut) {
1878 mAnyLockedOut = true;
1879 FinishAnyIncrementalGC();
1880 uint32_t gcTime = TimeBetween(mBeginSliceTime, TimeStamp::Now());
1881 mMaxGCDuration = std::max(mMaxGCDuration, gcTime);
1884 mExtraForgetSkippableCalls = aExtraForgetSkippableCalls;
1887 void
1888 CycleCollectorStats::RunForgetSkippable()
1890 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
1891 // is particularly useful if we recently finished a GC.
1892 if (mExtraForgetSkippableCalls >= 0) {
1893 TimeStamp beginForgetSkippable = TimeStamp::Now();
1894 bool ranSyncForgetSkippable = false;
1895 while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
1896 FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
1897 ranSyncForgetSkippable = true;
1900 for (int32_t i = 0; i < mExtraForgetSkippableCalls; ++i) {
1901 FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
1902 ranSyncForgetSkippable = true;
1905 if (ranSyncForgetSkippable) {
1906 mMaxSkippableDuration =
1907 std::max(mMaxSkippableDuration, TimeUntilNow(beginForgetSkippable));
1908 mRanSyncForgetSkippable = true;
1912 mExtraForgetSkippableCalls = 0;
1915 //static
1916 void
1917 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
1918 int32_t aExtraForgetSkippableCalls)
1920 if (!NS_IsMainThread()) {
1921 return;
1924 PROFILER_LABEL("nsJSContext", "CycleCollectNow",
1925 js::ProfileEntry::Category::CC);
1927 gCCStats.PrepareForCycleCollectionSlice(aExtraForgetSkippableCalls);
1928 nsCycleCollector_collect(aListener);
1929 gCCStats.FinishCycleCollectionSlice();
1932 //static
1933 void
1934 nsJSContext::RunCycleCollectorSlice()
1936 if (!NS_IsMainThread()) {
1937 return;
1940 PROFILER_LABEL("nsJSContext", "RunCycleCollectorSlice",
1941 js::ProfileEntry::Category::CC);
1943 gCCStats.PrepareForCycleCollectionSlice();
1945 // Decide how long we want to budget for this slice. By default,
1946 // use an unlimited budget.
1947 int64_t sliceBudget = -1;
1949 if (sIncrementalCC) {
1950 if (gCCStats.mBeginTime.IsNull()) {
1951 // If no CC is in progress, use the standard slice time.
1952 sliceBudget = kICCSliceBudget;
1953 } else {
1954 TimeStamp now = TimeStamp::Now();
1956 // Only run a limited slice if we're within the max running time.
1957 if (TimeBetween(gCCStats.mBeginTime, now) < kMaxICCDuration) {
1958 float sliceMultiplier = std::max(TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay, 1.0f);
1959 sliceBudget = kICCSliceBudget * sliceMultiplier;
1964 nsCycleCollector_collectSlice(sliceBudget);
1966 gCCStats.FinishCycleCollectionSlice();
1969 //static
1970 void
1971 nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget)
1973 if (!NS_IsMainThread()) {
1974 return;
1977 PROFILER_LABEL("nsJSContext", "RunCycleCollectorWorkSlice",
1978 js::ProfileEntry::Category::CC);
1980 gCCStats.PrepareForCycleCollectionSlice();
1981 nsCycleCollector_collectSliceWork(aWorkBudget);
1982 gCCStats.FinishCycleCollectionSlice();
1985 void
1986 nsJSContext::ClearMaxCCSliceTime()
1988 gCCStats.mMaxSliceTimeSinceClear = 0;
1991 uint32_t
1992 nsJSContext::GetMaxCCSliceTimeSinceClear()
1994 return gCCStats.mMaxSliceTimeSinceClear;
1997 static void
1998 ICCTimerFired(nsITimer* aTimer, void* aClosure)
2000 if (sDidShutdown) {
2001 return;
2004 // Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
2005 // to synchronously finish the GC, which is bad.
2007 if (sCCLockedOut) {
2008 PRTime now = PR_Now();
2009 if (sCCLockedOutTime == 0) {
2010 sCCLockedOutTime = now;
2011 return;
2013 if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
2014 return;
2018 nsJSContext::RunCycleCollectorSlice();
2021 //static
2022 void
2023 nsJSContext::BeginCycleCollectionCallback()
2025 MOZ_ASSERT(NS_IsMainThread());
2027 gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
2028 gCCStats.mSuspected = nsCycleCollector_suspectedCount();
2030 KillCCTimer();
2032 gCCStats.RunForgetSkippable();
2034 MOZ_ASSERT(!sICCTimer, "Tried to create a new ICC timer when one already existed.");
2036 if (!sIncrementalCC) {
2037 return;
2040 CallCreateInstance("@mozilla.org/timer;1", &sICCTimer);
2041 if (sICCTimer) {
2042 sICCTimer->InitWithFuncCallback(ICCTimerFired,
2043 nullptr,
2044 kICCIntersliceDelay,
2045 nsITimer::TYPE_REPEATING_SLACK);
2049 static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
2051 //static
2052 void
2053 nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
2055 MOZ_ASSERT(NS_IsMainThread());
2057 nsJSContext::KillICCTimer();
2059 // Update timing information for the current slice before we log it, if
2060 // we previously called PrepareForCycleCollectionSlice(). During shutdown
2061 // CCs, this won't happen.
2062 gCCStats.FinishCycleCollectionSlice();
2064 sCCollectedWaitingForGC += aResults.mFreedRefCounted + aResults.mFreedGCed;
2066 TimeStamp endCCTimeStamp = TimeStamp::Now();
2067 uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
2069 if (NeedsGCAfterCC()) {
2070 PokeGC(JS::gcreason::CC_WAITING,
2071 NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
2074 PRTime endCCTime;
2075 if (sPostGCEventsToObserver) {
2076 endCCTime = PR_Now();
2079 // Log information about the CC via telemetry, JSON and the console.
2080 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut);
2081 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable);
2082 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
2083 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE, gCCStats.mMaxSliceTime);
2085 if (!sLastCCEndTime.IsNull()) {
2086 // TimeBetween returns milliseconds, but we want to report seconds.
2087 uint32_t timeBetween = TimeBetween(sLastCCEndTime, gCCStats.mBeginTime) / 1000;
2088 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
2090 sLastCCEndTime = endCCTimeStamp;
2092 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
2093 sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
2095 PRTime delta = GetCollectionTimeDelta();
2097 uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
2098 uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX)
2099 ? 0 : sMinForgetSkippableTime;
2101 if (sPostGCEventsToConsole) {
2102 nsCString mergeMsg;
2103 if (aResults.mMergedZones) {
2104 mergeMsg.AssignLiteral(" merged");
2107 nsCString gcMsg;
2108 if (aResults.mForcedGC) {
2109 gcMsg.AssignLiteral(", forced a GC");
2112 NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
2113 MOZ_UTF16("CC(T+%.1f) 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 waiting for GC)%s\n")
2114 MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
2115 nsString msg;
2116 msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
2117 gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
2118 aResults.mNumSlices, gCCStats.mSuspected,
2119 aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
2120 aResults.mFreedRefCounted, aResults.mFreedGCed,
2121 sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
2122 gcMsg.get(),
2123 sForgetSkippableBeforeCC,
2124 minForgetSkippableTime / PR_USEC_PER_MSEC,
2125 sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
2126 (sTotalForgetSkippableTime / cleanups) /
2127 PR_USEC_PER_MSEC,
2128 sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
2129 gCCStats.mMaxSkippableDuration, sRemovedPurples));
2130 nsCOMPtr<nsIConsoleService> cs =
2131 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2132 if (cs) {
2133 cs->LogStringMessage(msg.get());
2137 if (sPostGCEventsToObserver) {
2138 NS_NAMED_MULTILINE_LITERAL_STRING(kJSONFmt,
2139 MOZ_UTF16("{ \"timestamp\": %llu, ")
2140 MOZ_UTF16("\"duration\": %lu, ")
2141 MOZ_UTF16("\"max_slice_pause\": %lu, ")
2142 MOZ_UTF16("\"total_slice_pause\": %lu, ")
2143 MOZ_UTF16("\"max_finish_gc_duration\": %lu, ")
2144 MOZ_UTF16("\"max_sync_skippable_duration\": %lu, ")
2145 MOZ_UTF16("\"suspected\": %lu, ")
2146 MOZ_UTF16("\"visited\": { ")
2147 MOZ_UTF16("\"RCed\": %lu, ")
2148 MOZ_UTF16("\"GCed\": %lu }, ")
2149 MOZ_UTF16("\"collected\": { ")
2150 MOZ_UTF16("\"RCed\": %lu, ")
2151 MOZ_UTF16("\"GCed\": %lu }, ")
2152 MOZ_UTF16("\"waiting_for_gc\": %lu, ")
2153 MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
2154 MOZ_UTF16("\"forced_gc\": %d, ")
2155 MOZ_UTF16("\"forget_skippable\": { ")
2156 MOZ_UTF16("\"times_before_cc\": %lu, ")
2157 MOZ_UTF16("\"min\": %lu, ")
2158 MOZ_UTF16("\"max\": %lu, ")
2159 MOZ_UTF16("\"avg\": %lu, ")
2160 MOZ_UTF16("\"total\": %lu, ")
2161 MOZ_UTF16("\"removed\": %lu } ")
2162 MOZ_UTF16("}"));
2163 nsString json;
2164 json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime, ccNowDuration,
2165 gCCStats.mMaxSliceTime,
2166 gCCStats.mTotalSliceTime,
2167 gCCStats.mMaxGCDuration,
2168 gCCStats.mMaxSkippableDuration,
2169 gCCStats.mSuspected,
2170 aResults.mVisitedRefCounted, aResults.mVisitedGCed,
2171 aResults.mFreedRefCounted, aResults.mFreedGCed,
2172 sCCollectedWaitingForGC,
2173 sLikelyShortLivingObjectsNeedingGC,
2174 aResults.mForcedGC,
2175 sForgetSkippableBeforeCC,
2176 minForgetSkippableTime / PR_USEC_PER_MSEC,
2177 sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
2178 (sTotalForgetSkippableTime / cleanups) /
2179 PR_USEC_PER_MSEC,
2180 sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
2181 sRemovedPurples));
2182 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2183 if (observerService) {
2184 observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get());
2188 // Update global state to indicate we have just run a cycle collection.
2189 sMinForgetSkippableTime = UINT32_MAX;
2190 sMaxForgetSkippableTime = 0;
2191 sTotalForgetSkippableTime = 0;
2192 sRemovedPurples = 0;
2193 sForgetSkippableBeforeCC = 0;
2194 sNeedsFullCC = false;
2195 sNeedsGCAfterCC = false;
2196 gCCStats.Clear();
2199 // static
2200 void
2201 InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
2203 nsJSContext::KillInterSliceGCTimer();
2204 nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
2205 nsJSContext::IncrementalGC,
2206 nsJSContext::NonShrinkingGC,
2207 NS_INTERSLICE_GC_BUDGET);
2210 // static
2211 void
2212 GCTimerFired(nsITimer *aTimer, void *aClosure)
2214 nsJSContext::KillGCTimer();
2215 uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
2216 nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
2217 nsJSContext::IncrementalGC);
2220 void
2221 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
2223 nsJSContext::KillShrinkGCBuffersTimer();
2224 nsJSContext::ShrinkGCBuffersNow();
2227 static bool
2228 ShouldTriggerCC(uint32_t aSuspected)
2230 return sNeedsFullCC ||
2231 aSuspected > NS_CC_PURPLE_LIMIT ||
2232 (aSuspected > NS_CC_FORCED_PURPLE_LIMIT &&
2233 TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
2236 static void
2237 CCTimerFired(nsITimer *aTimer, void *aClosure)
2239 if (sDidShutdown) {
2240 return;
2243 static uint32_t ccDelay = NS_CC_DELAY;
2244 if (sCCLockedOut) {
2245 ccDelay = NS_CC_DELAY / 3;
2247 PRTime now = PR_Now();
2248 if (sCCLockedOutTime == 0) {
2249 // Reset sCCTimerFireCount so that we run forgetSkippable
2250 // often enough before CC. Because of reduced ccDelay
2251 // forgetSkippable will be called just a few times.
2252 // NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
2253 // forgetSkippable and CycleCollectNow eventually.
2254 sCCTimerFireCount = 0;
2255 sCCLockedOutTime = now;
2256 return;
2258 if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
2259 return;
2263 ++sCCTimerFireCount;
2265 // During early timer fires, we only run forgetSkippable. During the first
2266 // late timer fire, we decide if we are going to have a second and final
2267 // late timer fire, where we may begin to run the CC. Should run at least one
2268 // early timer fire to allow cleanup before the CC.
2269 int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1);
2270 bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
2271 uint32_t suspected = nsCycleCollector_suspectedCount();
2272 if (isLateTimerFire && ShouldTriggerCC(suspected)) {
2273 if (sCCTimerFireCount == numEarlyTimerFires + 1) {
2274 FireForgetSkippable(suspected, true);
2275 if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2276 // Our efforts to avoid a CC have failed, so we return to let the
2277 // timer fire once more to trigger a CC.
2278 return;
2280 } else {
2281 // We are in the final timer fire and still meet the conditions for
2282 // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
2283 // any because that will allow us to include the GC time in the CC pause.
2284 nsJSContext::RunCycleCollectorSlice();
2286 } else if ((sPreviousSuspectedCount + 100) <= suspected) {
2287 // Only do a forget skippable if there are more than a few new objects.
2288 FireForgetSkippable(suspected, false);
2291 if (isLateTimerFire) {
2292 ccDelay = NS_CC_DELAY;
2294 // We have either just run the CC or decided we don't want to run the CC
2295 // next time, so kill the timer.
2296 sPreviousSuspectedCount = 0;
2297 nsJSContext::KillCCTimer();
2301 // static
2302 uint32_t
2303 nsJSContext::CleanupsSinceLastGC()
2305 return sCleanupsSinceLastGC;
2308 // static
2309 void
2310 nsJSContext::LoadStart()
2312 sLoadingInProgress = true;
2313 ++sPendingLoadCount;
2316 // static
2317 void
2318 nsJSContext::LoadEnd()
2320 if (!sLoadingInProgress)
2321 return;
2323 // sPendingLoadCount is not a well managed load counter (and doesn't
2324 // need to be), so make sure we don't make it wrap backwards here.
2325 if (sPendingLoadCount > 0) {
2326 --sPendingLoadCount;
2327 return;
2330 // Its probably a good idea to GC soon since we have finished loading.
2331 sLoadingInProgress = false;
2332 PokeGC(JS::gcreason::LOAD_END);
2335 // Only trigger expensive timers when they have been checked a number of times.
2336 static bool
2337 ReadyToTriggerExpensiveCollectorTimer()
2339 bool ready = kPokesBetweenExpensiveCollectorTriggers < ++sExpensiveCollectorPokes;
2340 if (ready) {
2341 sExpensiveCollectorPokes = 0;
2343 return ready;
2347 // Check all of the various collector timers and see if they are waiting to fire.
2348 // For the synchronous collector timers, sGCTimer and sCCTimer, we only want to trigger
2349 // the collection occasionally, because they are expensive. The incremental collector
2350 // timers, sInterSliceGCTimer and sICCTimer, are fast and need to be run many times, so
2351 // always run their corresponding timer.
2353 // This does not check sFullGCTimer, as that's an even more expensive collector we run
2354 // on a long timer.
2356 // static
2357 void
2358 nsJSContext::RunNextCollectorTimer()
2360 if (sShuttingDown) {
2361 return;
2364 if (sGCTimer) {
2365 if (ReadyToTriggerExpensiveCollectorTimer()) {
2366 GCTimerFired(nullptr, reinterpret_cast<void *>(JS::gcreason::DOM_WINDOW_UTILS));
2368 return;
2371 if (sInterSliceGCTimer) {
2372 InterSliceGCTimerFired(nullptr, nullptr);
2373 return;
2376 // Check the CC timers after the GC timers, because the CC timers won't do
2377 // anything if a GC is in progress.
2378 MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
2380 if (sCCTimer) {
2381 if (ReadyToTriggerExpensiveCollectorTimer()) {
2382 CCTimerFired(nullptr, nullptr);
2384 return;
2387 if (sICCTimer) {
2388 ICCTimerFired(nullptr, nullptr);
2389 return;
2393 // static
2394 void
2395 nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
2397 if (sGCTimer || sInterSliceGCTimer || sShuttingDown) {
2398 // There's already a timer for GC'ing, just return
2399 return;
2402 if (sCCTimer) {
2403 // Make sure CC is called...
2404 sNeedsFullCC = true;
2405 // and GC after it.
2406 sNeedsGCAfterCC = true;
2407 return;
2410 if (sICCTimer) {
2411 // Make sure GC is called after the current CC completes.
2412 // No need to set sNeedsFullCC because we are currently running a CC.
2413 sNeedsGCAfterCC = true;
2414 return;
2417 CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
2419 if (!sGCTimer) {
2420 // Failed to create timer (probably because we're in XPCOM shutdown)
2421 return;
2424 static bool first = true;
2426 sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason),
2427 aDelay
2428 ? aDelay
2429 : (first
2430 ? NS_FIRST_GC_DELAY
2431 : NS_GC_DELAY),
2432 nsITimer::TYPE_ONE_SHOT);
2434 first = false;
2437 // static
2438 void
2439 nsJSContext::PokeShrinkGCBuffers()
2441 if (sShrinkGCBuffersTimer || sShuttingDown) {
2442 return;
2445 CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
2447 if (!sShrinkGCBuffersTimer) {
2448 // Failed to create timer (probably because we're in XPCOM shutdown)
2449 return;
2452 sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nullptr,
2453 NS_SHRINK_GC_BUFFERS_DELAY,
2454 nsITimer::TYPE_ONE_SHOT);
2457 // static
2458 void
2459 nsJSContext::MaybePokeCC()
2461 if (sCCTimer || sICCTimer || sShuttingDown || !sHasRunGC) {
2462 return;
2465 if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2466 sCCTimerFireCount = 0;
2467 CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
2468 if (!sCCTimer) {
2469 return;
2471 // We can kill some objects before running forgetSkippable.
2472 nsCycleCollector_dispatchDeferredDeletion();
2474 sCCTimer->InitWithFuncCallback(CCTimerFired, nullptr,
2475 NS_CC_SKIPPABLE_DELAY,
2476 nsITimer::TYPE_REPEATING_SLACK);
2480 //static
2481 void
2482 nsJSContext::KillGCTimer()
2484 if (sGCTimer) {
2485 sGCTimer->Cancel();
2486 NS_RELEASE(sGCTimer);
2490 void
2491 nsJSContext::KillFullGCTimer()
2493 if (sFullGCTimer) {
2494 sFullGCTimer->Cancel();
2495 NS_RELEASE(sFullGCTimer);
2499 void
2500 nsJSContext::KillInterSliceGCTimer()
2502 if (sInterSliceGCTimer) {
2503 sInterSliceGCTimer->Cancel();
2504 NS_RELEASE(sInterSliceGCTimer);
2508 //static
2509 void
2510 nsJSContext::KillShrinkGCBuffersTimer()
2512 if (sShrinkGCBuffersTimer) {
2513 sShrinkGCBuffersTimer->Cancel();
2514 NS_RELEASE(sShrinkGCBuffersTimer);
2518 //static
2519 void
2520 nsJSContext::KillCCTimer()
2522 sCCLockedOutTime = 0;
2523 if (sCCTimer) {
2524 sCCTimer->Cancel();
2525 NS_RELEASE(sCCTimer);
2529 //static
2530 void
2531 nsJSContext::KillICCTimer()
2533 sCCLockedOutTime = 0;
2535 if (sICCTimer) {
2536 sICCTimer->Cancel();
2537 NS_RELEASE(sICCTimer);
2541 void
2542 nsJSContext::GC(JS::gcreason::Reason aReason)
2544 PokeGC(aReason);
2547 class NotifyGCEndRunnable : public nsRunnable
2549 nsString mMessage;
2551 public:
2552 explicit NotifyGCEndRunnable(const nsString& aMessage) : mMessage(aMessage) {}
2554 NS_DECL_NSIRUNNABLE
2557 NS_IMETHODIMP
2558 NotifyGCEndRunnable::Run()
2560 MOZ_ASSERT(NS_IsMainThread());
2562 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2563 if (!observerService) {
2564 return NS_OK;
2567 const jschar oomMsg[3] = { '{', '}', 0 };
2568 const jschar *toSend = mMessage.get() ? mMessage.get() : oomMsg;
2569 observerService->NotifyObservers(nullptr, "garbage-collection-statistics", toSend);
2571 return NS_OK;
2574 static void
2575 DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
2577 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
2579 if (aProgress == JS::GC_CYCLE_END) {
2580 PRTime delta = GetCollectionTimeDelta();
2582 if (sPostGCEventsToConsole) {
2583 NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) ");
2584 nsString prefix, gcstats;
2585 gcstats.Adopt(aDesc.formatMessage(aRt));
2586 prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
2587 double(delta) / PR_USEC_PER_SEC));
2588 nsString msg = prefix + gcstats;
2589 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2590 if (cs) {
2591 cs->LogStringMessage(msg.get());
2595 if (sPostGCEventsToObserver) {
2596 nsString json;
2597 json.Adopt(aDesc.formatJSON(aRt, PR_Now()));
2598 nsRefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(json);
2599 NS_DispatchToMainThread(notify);
2603 // Prevent cycle collections and shrinking during incremental GC.
2604 if (aProgress == JS::GC_CYCLE_BEGIN) {
2605 sCCLockedOut = true;
2606 nsJSContext::KillShrinkGCBuffersTimer();
2607 } else if (aProgress == JS::GC_CYCLE_END) {
2608 sCCLockedOut = false;
2611 // The GC has more work to do, so schedule another GC slice.
2612 if (aProgress == JS::GC_SLICE_END) {
2613 nsJSContext::KillInterSliceGCTimer();
2614 if (!sShuttingDown) {
2615 CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
2616 sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired,
2617 nullptr,
2618 NS_INTERSLICE_GC_DELAY,
2619 nsITimer::TYPE_ONE_SHOT);
2623 if (aProgress == JS::GC_CYCLE_END) {
2624 // May need to kill the inter-slice GC timer
2625 nsJSContext::KillInterSliceGCTimer();
2627 sCCollectedWaitingForGC = 0;
2628 sLikelyShortLivingObjectsNeedingGC = 0;
2629 sCleanupsSinceLastGC = 0;
2630 sNeedsFullCC = true;
2631 sHasRunGC = true;
2632 nsJSContext::MaybePokeCC();
2634 if (aDesc.isCompartment_) {
2635 if (!sFullGCTimer && !sShuttingDown) {
2636 CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
2637 JS::gcreason::Reason reason = JS::gcreason::FULL_GC_TIMER;
2638 sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
2639 reinterpret_cast<void *>(reason),
2640 NS_FULL_GC_DELAY,
2641 nsITimer::TYPE_ONE_SHOT);
2643 } else {
2644 nsJSContext::KillFullGCTimer();
2646 // Avoid shrinking during heavy activity, which is suggested by
2647 // compartment GC.
2648 nsJSContext::PokeShrinkGCBuffers();
2652 if ((aProgress == JS::GC_SLICE_END || aProgress == JS::GC_CYCLE_END) &&
2653 ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2654 nsCycleCollector_dispatchDeferredDeletion();
2657 if (sPrevGCSliceCallback)
2658 (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
2661 void
2662 nsJSContext::ReportPendingException()
2664 if (mIsInitialized) {
2665 nsJSUtils::ReportPendingException(mContext);
2669 void
2670 nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
2672 mWindowProxy = aWindowProxy;
2675 JSObject*
2676 nsJSContext::GetWindowProxy()
2678 JSObject* windowProxy = GetWindowProxyPreserveColor();
2679 if (windowProxy) {
2680 JS::ExposeObjectToActiveJS(windowProxy);
2683 return windowProxy;
2686 JSObject*
2687 nsJSContext::GetWindowProxyPreserveColor()
2689 return mWindowProxy;
2692 void
2693 nsJSContext::LikelyShortLivingObjectCreated()
2695 ++sLikelyShortLivingObjectsNeedingGC;
2698 void
2699 mozilla::dom::StartupJSEnvironment()
2701 // initialize all our statics, so that we can restart XPCOM
2702 sGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
2703 sCCLockedOut = false;
2704 sCCLockedOutTime = 0;
2705 sLastCCEndTime = TimeStamp();
2706 sHasRunGC = false;
2707 sPendingLoadCount = 0;
2708 sLoadingInProgress = false;
2709 sCCollectedWaitingForGC = 0;
2710 sLikelyShortLivingObjectsNeedingGC = 0;
2711 sPostGCEventsToConsole = false;
2712 sNeedsFullCC = false;
2713 sNeedsGCAfterCC = false;
2714 gNameSpaceManager = nullptr;
2715 sRuntimeService = nullptr;
2716 sRuntime = nullptr;
2717 sIsInitialized = false;
2718 sDidShutdown = false;
2719 sShuttingDown = false;
2720 sContextCount = 0;
2721 sSecurityManager = nullptr;
2722 gCCStats.Init();
2723 sExpensiveCollectorPokes = 0;
2726 static void
2727 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
2729 bool reportAll = Preferences::GetBool(aPrefName, false);
2730 nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
2733 static void
2734 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
2736 int32_t highwatermark = Preferences::GetInt(aPrefName, 128);
2738 JS_SetGCParameter(sRuntime, JSGC_MAX_MALLOC_BYTES,
2739 highwatermark * 1024L * 1024L);
2742 static void
2743 SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
2745 int32_t pref = Preferences::GetInt(aPrefName, -1);
2746 // handle overflow and negative pref values
2747 uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024;
2748 JS_SetGCParameter(sRuntime, JSGC_MAX_BYTES, max);
2751 static void
2752 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
2754 bool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
2755 bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
2756 JSGCMode mode;
2757 if (enableIncrementalGC) {
2758 mode = JSGC_MODE_INCREMENTAL;
2759 } else if (enableCompartmentGC) {
2760 mode = JSGC_MODE_COMPARTMENT;
2761 } else {
2762 mode = JSGC_MODE_GLOBAL;
2764 JS_SetGCParameter(sRuntime, JSGC_MODE, mode);
2767 static void
2768 SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
2770 int32_t pref = Preferences::GetInt(aPrefName, -1);
2771 // handle overflow and negative pref values
2772 if (pref > 0 && pref < 100000)
2773 JS_SetGCParameter(sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
2776 static void
2777 SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure)
2779 int32_t pref = Preferences::GetInt(aPrefName, -1);
2780 // handle overflow and negative pref values
2781 if (pref >= 0 && pref < 10000)
2782 JS_SetGCParameter(sRuntime, (JSGCParamKey)(intptr_t)aClosure, pref);
2785 static void
2786 SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure)
2788 bool pref = Preferences::GetBool(aPrefName);
2789 JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref);
2792 static void
2793 SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
2795 bool pref = Preferences::GetBool(aPrefName);
2796 JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
2799 static void
2800 SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure)
2802 bool pref = Preferences::GetBool(aPrefName);
2803 sIncrementalCC = pref;
2806 JSObject*
2807 NS_DOMReadStructuredClone(JSContext* cx,
2808 JSStructuredCloneReader* reader,
2809 uint32_t tag,
2810 uint32_t data,
2811 void* closure)
2813 if (tag == SCTAG_DOM_IMAGEDATA) {
2814 return ReadStructuredCloneImageData(cx, reader);
2815 } else if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
2816 nsIGlobalObject *global = xpc::GetNativeForGlobal(JS::CurrentGlobalOrNull(cx));
2817 if (!global) {
2818 return nullptr;
2821 // Prevent the return value from being trashed by a GC during ~nsRefPtr.
2822 JS::Rooted<JSObject*> result(cx);
2824 nsRefPtr<CryptoKey> key = new CryptoKey(global);
2825 if (!key->ReadStructuredClone(reader)) {
2826 result = nullptr;
2827 } else {
2828 result = key->WrapObject(cx);
2831 return result;
2832 } else if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
2833 tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
2834 tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
2835 mozilla::ipc::PrincipalInfo info;
2836 if (tag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
2837 info = mozilla::ipc::SystemPrincipalInfo();
2838 } else if (tag == SCTAG_DOM_NULL_PRINCIPAL) {
2839 info = mozilla::ipc::NullPrincipalInfo();
2840 } else {
2841 uint32_t appId = data;
2843 uint32_t isInBrowserElement, specLength;
2844 if (!JS_ReadUint32Pair(reader, &isInBrowserElement, &specLength)) {
2845 return nullptr;
2848 nsAutoCString spec;
2849 spec.SetLength(specLength);
2850 if (!JS_ReadBytes(reader, spec.BeginWriting(), specLength)) {
2851 return nullptr;
2854 info = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement, spec);
2857 nsresult rv;
2858 nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(info, &rv);
2859 if (NS_WARN_IF(NS_FAILED(rv))) {
2860 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2861 return nullptr;
2864 JS::RootedValue result(cx);
2865 rv = nsContentUtils::WrapNative(cx, principal, &NS_GET_IID(nsIPrincipal), &result);
2866 if (NS_FAILED(rv)) {
2867 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2868 return nullptr;
2871 return result.toObjectOrNull();
2874 // Don't know what this is. Bail.
2875 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2876 return nullptr;
2879 bool
2880 NS_DOMWriteStructuredClone(JSContext* cx,
2881 JSStructuredCloneWriter* writer,
2882 JS::Handle<JSObject*> obj,
2883 void *closure)
2885 // Handle ImageData cloning
2886 ImageData* imageData;
2887 if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, obj, imageData))) {
2888 return WriteStructuredCloneImageData(cx, writer, imageData);
2891 // Handle Key cloning
2892 CryptoKey* key;
2893 if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, obj, key))) {
2894 return JS_WriteUint32Pair(writer, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
2895 key->WriteStructuredClone(writer);
2898 if (xpc::IsReflector(obj)) {
2899 nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
2900 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
2901 if (principal) {
2902 mozilla::ipc::PrincipalInfo info;
2903 if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
2904 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2905 return false;
2908 if (info.type() == mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
2909 return JS_WriteUint32Pair(writer, SCTAG_DOM_NULL_PRINCIPAL, 0);
2911 if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
2912 return JS_WriteUint32Pair(writer, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
2915 MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
2916 const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
2917 return JS_WriteUint32Pair(writer, SCTAG_DOM_CONTENT_PRINCIPAL, cInfo.appId()) &&
2918 JS_WriteUint32Pair(writer, cInfo.isInBrowserElement(), cInfo.spec().Length()) &&
2919 JS_WriteBytes(writer, cInfo.spec().get(), cInfo.spec().Length());
2923 // Don't know what this is
2924 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2925 return false;
2928 void
2929 NS_DOMStructuredCloneError(JSContext* cx,
2930 uint32_t errorid)
2932 // We don't currently support any extensions to structured cloning.
2933 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2936 static bool
2937 AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
2938 const jschar* aBegin,
2939 const jschar* aLimit,
2940 size_t* aSize,
2941 const uint8_t** aMemory,
2942 intptr_t *aHandle)
2944 nsIPrincipal* principal =
2945 nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
2946 return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory,
2947 aHandle);
2950 static bool
2951 AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
2952 bool aInstalled,
2953 const jschar* aBegin,
2954 const jschar* aEnd,
2955 size_t aSize,
2956 uint8_t** aMemory,
2957 intptr_t* aHandle)
2959 nsIPrincipal* principal =
2960 nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
2961 return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd,
2962 aSize, aMemory, aHandle);
2965 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
2967 void
2968 nsJSContext::EnsureStatics()
2970 if (sIsInitialized) {
2971 if (!nsContentUtils::XPConnect()) {
2972 MOZ_CRASH();
2974 return;
2977 nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
2978 &sSecurityManager);
2979 if (NS_FAILED(rv)) {
2980 MOZ_CRASH();
2983 rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
2984 if (NS_FAILED(rv)) {
2985 MOZ_CRASH();
2988 rv = sRuntimeService->GetRuntime(&sRuntime);
2989 if (NS_FAILED(rv)) {
2990 MOZ_CRASH();
2993 // Let's make sure that our main thread is the same as the xpcom main thread.
2994 MOZ_ASSERT(NS_IsMainThread());
2996 sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
2998 // Set up the structured clone callbacks.
2999 static JSStructuredCloneCallbacks cloneCallbacks = {
3000 NS_DOMReadStructuredClone,
3001 NS_DOMWriteStructuredClone,
3002 NS_DOMStructuredCloneError,
3003 nullptr,
3004 nullptr,
3005 nullptr
3007 JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
3009 // Set up the asm.js cache callbacks
3010 static JS::AsmJSCacheOps asmJSCacheOps = {
3011 AsmJSCacheOpenEntryForRead,
3012 asmjscache::CloseEntryForRead,
3013 AsmJSCacheOpenEntryForWrite,
3014 asmjscache::CloseEntryForWrite,
3015 asmjscache::GetBuildId
3017 JS::SetAsmJSCacheOps(sRuntime, &asmJSCacheOps);
3019 // Set these global xpconnect options...
3020 Preferences::RegisterCallbackAndCall(ReportAllJSExceptionsPrefChangedCallback,
3021 "dom.report_all_js_exceptions");
3023 Preferences::RegisterCallbackAndCall(SetMemoryHighWaterMarkPrefChangedCallback,
3024 "javascript.options.mem.high_water_mark");
3026 Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback,
3027 "javascript.options.mem.max");
3029 Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
3030 "javascript.options.mem.gc_per_compartment");
3032 Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
3033 "javascript.options.mem.gc_incremental");
3035 Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback,
3036 "javascript.options.mem.gc_incremental_slice_ms");
3038 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3039 "javascript.options.mem.gc_high_frequency_time_limit_ms",
3040 (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
3042 Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback,
3043 "javascript.options.mem.gc_dynamic_mark_slice");
3045 Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback,
3046 "javascript.options.mem.gc_dynamic_heap_growth");
3048 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3049 "javascript.options.mem.gc_low_frequency_heap_growth",
3050 (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
3052 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3053 "javascript.options.mem.gc_high_frequency_heap_growth_min",
3054 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
3056 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3057 "javascript.options.mem.gc_high_frequency_heap_growth_max",
3058 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
3060 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3061 "javascript.options.mem.gc_high_frequency_low_limit_mb",
3062 (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
3064 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3065 "javascript.options.mem.gc_high_frequency_high_limit_mb",
3066 (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
3068 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3069 "javascript.options.mem.gc_allocation_threshold_mb",
3070 (void *)JSGC_ALLOCATION_THRESHOLD);
3072 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3073 "javascript.options.mem.gc_decommit_threshold_mb",
3074 (void *)JSGC_DECOMMIT_THRESHOLD);
3076 Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback,
3077 "dom.cycle_collector.incremental");
3079 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3080 "javascript.options.mem.gc_min_empty_chunk_count",
3081 (void *)JSGC_MIN_EMPTY_CHUNK_COUNT);
3083 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
3084 "javascript.options.mem.gc_max_empty_chunk_count",
3085 (void *)JSGC_MAX_EMPTY_CHUNK_COUNT);
3087 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3088 if (!obs) {
3089 MOZ_CRASH();
3092 Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
3093 "javascript.options.gc_on_memory_pressure",
3094 true);
3096 nsIObserver* observer = new nsJSEnvironmentObserver();
3097 obs->AddObserver(observer, "memory-pressure", false);
3098 obs->AddObserver(observer, "quit-application", false);
3100 // Bug 907848 - We need to explicitly get the nsIDOMScriptObjectFactory
3101 // service in order to force its constructor to run, which registers a
3102 // shutdown observer. It would be nice to make this more explicit and less
3103 // side-effect-y.
3104 nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
3105 if (!factory) {
3106 MOZ_CRASH();
3109 sIsInitialized = true;
3112 nsScriptNameSpaceManager*
3113 mozilla::dom::GetNameSpaceManager()
3115 if (sDidShutdown)
3116 return nullptr;
3118 if (!gNameSpaceManager) {
3119 gNameSpaceManager = new nsScriptNameSpaceManager;
3120 NS_ADDREF(gNameSpaceManager);
3122 nsresult rv = gNameSpaceManager->Init();
3123 NS_ENSURE_SUCCESS(rv, nullptr);
3126 return gNameSpaceManager;
3129 void
3130 mozilla::dom::ShutdownJSEnvironment()
3132 KillTimers();
3134 NS_IF_RELEASE(gNameSpaceManager);
3136 if (!sContextCount) {
3137 // We're being shutdown, and there are no more contexts
3138 // alive, release the JS runtime service and the security manager.
3140 NS_IF_RELEASE(sRuntimeService);
3141 NS_IF_RELEASE(sSecurityManager);
3144 sShuttingDown = true;
3145 sDidShutdown = true;
3148 // A fast-array class for JS. This class supports both nsIJSScriptArray and
3149 // nsIArray. If it is JS itself providing and consuming this class, all work
3150 // can be done via nsIJSScriptArray, and avoid the conversion of elements
3151 // to/from nsISupports.
3152 // When consumed by non-JS (eg, another script language), conversion is done
3153 // on-the-fly.
3154 class nsJSArgArray MOZ_FINAL : public nsIJSArgArray {
3155 public:
3156 nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
3157 nsresult *prv);
3159 // nsISupports
3160 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3161 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
3162 nsIJSArgArray)
3164 // nsIArray
3165 NS_DECL_NSIARRAY
3167 // nsIJSArgArray
3168 nsresult GetArgs(uint32_t *argc, void **argv);
3170 void ReleaseJSObjects();
3172 protected:
3173 ~nsJSArgArray();
3174 JSContext *mContext;
3175 JS::Heap<JS::Value> *mArgv;
3176 uint32_t mArgc;
3179 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
3180 nsresult *prv) :
3181 mContext(aContext),
3182 mArgv(nullptr),
3183 mArgc(argc)
3185 // copy the array - we don't know its lifetime, and ours is tied to xpcom
3186 // refcounting.
3187 if (argc) {
3188 static const fallible_t fallible = fallible_t();
3189 mArgv = new (fallible) JS::Heap<JS::Value>[argc];
3190 if (!mArgv) {
3191 *prv = NS_ERROR_OUT_OF_MEMORY;
3192 return;
3196 // Callers are allowed to pass in a null argv even for argc > 0. They can
3197 // then use GetArgs to initialize the values.
3198 if (argv) {
3199 for (uint32_t i = 0; i < argc; ++i)
3200 mArgv[i] = argv[i];
3203 if (argc > 0) {
3204 mozilla::HoldJSObjects(this);
3207 *prv = NS_OK;
3210 nsJSArgArray::~nsJSArgArray()
3212 ReleaseJSObjects();
3215 void
3216 nsJSArgArray::ReleaseJSObjects()
3218 if (mArgv) {
3219 delete [] mArgv;
3221 if (mArgc > 0) {
3222 mArgc = 0;
3223 mozilla::DropJSObjects(this);
3227 // QueryInterface implementation for nsJSArgArray
3228 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
3230 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
3231 tmp->ReleaseJSObjects();
3232 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3233 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
3234 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
3235 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3237 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
3238 if (tmp->mArgv) {
3239 for (uint32_t i = 0; i < tmp->mArgc; ++i) {
3240 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mArgv[i])
3243 NS_IMPL_CYCLE_COLLECTION_TRACE_END
3245 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
3246 NS_INTERFACE_MAP_ENTRY(nsIArray)
3247 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
3248 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
3249 NS_INTERFACE_MAP_END
3251 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
3252 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
3254 nsresult
3255 nsJSArgArray::GetArgs(uint32_t *argc, void **argv)
3257 *argv = (void *)mArgv;
3258 *argc = mArgc;
3259 return NS_OK;
3262 // nsIArray impl
3263 NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t *aLength)
3265 *aLength = mArgc;
3266 return NS_OK;
3269 /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
3270 NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID & uuid, void * *result)
3272 *result = nullptr;
3273 if (index >= mArgc)
3274 return NS_ERROR_INVALID_ARG;
3276 if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
3277 // Have to copy a Heap into a Rooted to work with it.
3278 JS::Rooted<JS::Value> val(mContext, mArgv[index]);
3279 return nsContentUtils::XPConnect()->JSToVariant(mContext, val,
3280 (nsIVariant **)result);
3282 NS_WARNING("nsJSArgArray only handles nsIVariant");
3283 return NS_ERROR_NO_INTERFACE;
3286 /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
3287 NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval)
3289 return NS_ERROR_NOT_IMPLEMENTED;
3292 /* nsISimpleEnumerator enumerate (); */
3293 NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
3295 return NS_ERROR_NOT_IMPLEMENTED;
3298 // The factory function
3299 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc, void *argv,
3300 nsIJSArgArray **aArray)
3302 nsresult rv;
3303 nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc,
3304 static_cast<JS::Value *>(argv), &rv);
3305 if (NS_FAILED(rv)) {
3306 return rv;
3308 ret.forget(aArray);
3309 return NS_OK;