Merge inbound to m-c on a CLOSED TREE.
[gecko.git] / dom / base / nsJSEnvironment.cpp
blob887b4643539bb1400383ff167e020b9715b4ed9e
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 "nsJSUtils.h"
22 #include "nsIDocShell.h"
23 #include "nsIDocShellTreeItem.h"
24 #include "nsPresContext.h"
25 #include "nsIConsoleService.h"
26 #include "nsIScriptError.h"
27 #include "nsIInterfaceRequestor.h"
28 #include "nsIInterfaceRequestorUtils.h"
29 #include "nsIPrompt.h"
30 #include "nsIObserverService.h"
31 #include "nsITimer.h"
32 #include "nsIAtom.h"
33 #include "nsContentUtils.h"
34 #include "nsCxPusher.h"
35 #include "nsEventDispatcher.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"
43 #include "xpcpublic.h"
45 #include "js/OldDebugAPI.h"
46 #include "jswrapper.h"
47 #include "nsIArray.h"
48 #include "nsIObjectInputStream.h"
49 #include "nsIObjectOutputStream.h"
50 #include "prmem.h"
51 #include "WrapperFactory.h"
52 #include "nsGlobalWindow.h"
53 #include "nsScriptNameSpaceManager.h"
54 #include "StructuredCloneTags.h"
55 #include "mozilla/dom/ImageData.h"
56 #include "mozilla/dom/ImageDataBinding.h"
57 #include "nsAXPCNativeCallContext.h"
58 #include "mozilla/CycleCollectedJSRuntime.h"
60 #include "nsJSPrincipals.h"
62 #ifdef XP_MACOSX
63 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
64 #undef check
65 #endif
66 #include "AccessCheck.h"
68 #ifdef MOZ_JSDEBUGGER
69 #include "jsdIDebuggerService.h"
70 #endif
71 #ifdef MOZ_LOGGING
72 // Force PR_LOGGING so we can get JS strict warnings even in release builds
73 #define FORCE_PR_LOG 1
74 #endif
75 #include "prlog.h"
76 #include "prthread.h"
78 #include "mozilla/Preferences.h"
79 #include "mozilla/Telemetry.h"
80 #include "mozilla/dom/BindingUtils.h"
81 #include "mozilla/Attributes.h"
82 #include "mozilla/dom/asmjscache/AsmJSCache.h"
83 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
84 #include "mozilla/CycleCollectedJSRuntime.h"
85 #include "mozilla/ContentEvents.h"
87 #include "nsCycleCollectionNoteRootCallback.h"
88 #include "GeckoProfiler.h"
90 using namespace mozilla;
91 using namespace mozilla::dom;
93 const size_t gStackSize = 8192;
95 #ifdef PR_LOGGING
96 static PRLogModuleInfo* gJSDiagnostics;
97 #endif
99 // Thank you Microsoft!
100 #ifdef CompareString
101 #undef CompareString
102 #endif
104 #define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms
106 // The amount of time we wait from the first request to GC to actually
107 // doing the first GC.
108 #define NS_FIRST_GC_DELAY 10000 // ms
110 #define NS_FULL_GC_DELAY 60000 // ms
112 // Maximum amount of time that should elapse between incremental GC slices
113 #define NS_INTERSLICE_GC_DELAY 100 // ms
115 // If we haven't painted in 100ms, we allow for a longer GC budget
116 #define NS_INTERSLICE_GC_BUDGET 40 // ms
118 // The amount of time we wait between a request to CC (after GC ran)
119 // and doing the actual CC.
120 #define NS_CC_DELAY 6000 // ms
122 #define NS_CC_SKIPPABLE_DELAY 400 // ms
124 // Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT
125 // objects in the purple buffer.
126 #define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min
127 #define NS_CC_FORCED_PURPLE_LIMIT 10
129 // Don't allow an incremental GC to lock out the CC for too long.
130 #define NS_MAX_CC_LOCKEDOUT_TIME (15 * PR_USEC_PER_SEC) // 15 seconds
132 // Trigger a CC if the purple buffer exceeds this size when we check it.
133 #define NS_CC_PURPLE_LIMIT 200
135 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
137 // Large value used to specify that a script should run essentially forever
138 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
140 #define NS_MAJOR_FORGET_SKIPPABLE_CALLS 2
142 // if you add statics here, add them to the list in StartupJSEnvironment
144 static nsITimer *sGCTimer;
145 static nsITimer *sShrinkGCBuffersTimer;
146 static nsITimer *sCCTimer;
147 static nsITimer *sFullGCTimer;
148 static nsITimer *sInterSliceGCTimer;
150 static PRTime sLastCCEndTime;
152 static bool sCCLockedOut;
153 static PRTime sCCLockedOutTime;
155 static JS::GCSliceCallback sPrevGCSliceCallback;
157 static bool sHasRunGC;
159 // The number of currently pending document loads. This count isn't
160 // guaranteed to always reflect reality and can't easily as we don't
161 // have an easy place to know when a load ends or is interrupted in
162 // all cases. This counter also gets reset if we end up GC'ing while
163 // we're waiting for a slow page to load. IOW, this count may be 0
164 // even when there are pending loads.
165 static uint32_t sPendingLoadCount;
166 static bool sLoadingInProgress;
168 static uint32_t sCCollectedWaitingForGC;
169 static uint32_t sLikelyShortLivingObjectsNeedingGC;
170 static bool sPostGCEventsToConsole;
171 static bool sPostGCEventsToObserver;
172 static uint32_t sCCTimerFireCount = 0;
173 static uint32_t sMinForgetSkippableTime = UINT32_MAX;
174 static uint32_t sMaxForgetSkippableTime = 0;
175 static uint32_t sTotalForgetSkippableTime = 0;
176 static uint32_t sRemovedPurples = 0;
177 static uint32_t sForgetSkippableBeforeCC = 0;
178 static uint32_t sPreviousSuspectedCount = 0;
179 static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
180 static bool sNeedsFullCC = false;
181 static bool sNeedsGCAfterCC = false;
182 static nsJSContext *sContextList = nullptr;
184 static nsScriptNameSpaceManager *gNameSpaceManager;
186 static nsIJSRuntimeService *sRuntimeService;
188 static const char kJSRuntimeServiceContractID[] =
189 "@mozilla.org/js/xpc/RuntimeService;1";
191 static PRTime sFirstCollectionTime;
193 static JSRuntime *sRuntime;
195 static bool sIsInitialized;
196 static bool sDidShutdown;
197 static bool sShuttingDown;
198 static int32_t sContextCount;
200 static nsIScriptSecurityManager *sSecurityManager;
202 // nsJSEnvironmentObserver observes the memory-pressure notifications
203 // and forces a garbage collection and cycle collection when it happens, if
204 // the appropriate pref is set.
206 static bool sGCOnMemoryPressure;
208 static PRTime
209 GetCollectionTimeDelta()
211 PRTime now = PR_Now();
212 if (sFirstCollectionTime) {
213 return now - sFirstCollectionTime;
215 sFirstCollectionTime = now;
216 return 0;
219 static void
220 KillTimers()
222 nsJSContext::KillGCTimer();
223 nsJSContext::KillShrinkGCBuffersTimer();
224 nsJSContext::KillCCTimer();
225 nsJSContext::KillFullGCTimer();
226 nsJSContext::KillInterSliceGCTimer();
229 class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver
231 public:
232 NS_DECL_ISUPPORTS
233 NS_DECL_NSIOBSERVER
236 NS_IMPL_ISUPPORTS1(nsJSEnvironmentObserver, nsIObserver)
238 NS_IMETHODIMP
239 nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
240 const PRUnichar* aData)
242 if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) {
243 if(StringBeginsWith(nsDependentString(aData),
244 NS_LITERAL_STRING("low-memory-ongoing"))) {
245 // Don't GC/CC if we are in an ongoing low-memory state since its very
246 // slow and it likely won't help us anyway.
247 return NS_OK;
249 nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
250 nsJSContext::NonIncrementalGC,
251 nsJSContext::NonCompartmentGC,
252 nsJSContext::ShrinkingGC);
253 nsJSContext::CycleCollectNow();
254 } else if (!nsCRT::strcmp(aTopic, "quit-application")) {
255 sShuttingDown = true;
256 KillTimers();
259 return NS_OK;
262 class nsRootedJSValueArray {
263 public:
264 explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {}
266 void SetCapacity(JSContext *cx, size_t capacity) {
267 vals.SetCapacity(capacity);
268 // Values must be safe for the GC to inspect (they must not contain garbage).
269 memset(vals.Elements(), 0, vals.Capacity() * sizeof(JS::Value));
270 resetRooter(cx);
273 JS::Value *Elements() {
274 return vals.Elements();
277 private:
278 void resetRooter(JSContext *cx) {
279 avr.changeArray(vals.Elements(), vals.Length());
282 nsAutoTArray<JS::Value, 16> vals;
283 JS::AutoArrayRooter avr;
286 /****************************************************************
287 ************************** AutoFree ****************************
288 ****************************************************************/
290 class AutoFree {
291 public:
292 AutoFree(void *aPtr) : mPtr(aPtr) {
294 ~AutoFree() {
295 if (mPtr)
296 nsMemory::Free(mPtr);
298 void Invalidate() {
299 mPtr = 0;
301 private:
302 void *mPtr;
305 // A utility function for script languages to call. Although it looks small,
306 // the use of nsIDocShell and nsPresContext triggers a huge number of
307 // dependencies that most languages would not otherwise need.
308 // XXXmarkh - This function is mis-placed!
309 bool
310 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
311 InternalScriptErrorEvent *aErrorEvent,
312 nsEventStatus *aStatus)
314 bool called = false;
315 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
316 nsIDocShell *docShell = win ? win->GetDocShell() : nullptr;
317 if (docShell) {
318 nsRefPtr<nsPresContext> presContext;
319 docShell->GetPresContext(getter_AddRefs(presContext));
321 static int32_t errorDepth; // Recursion prevention
322 ++errorDepth;
324 if (presContext && errorDepth < 2) {
325 // Dispatch() must be synchronous for the recursion block
326 // (errorDepth) to work.
327 nsEventDispatcher::Dispatch(win, presContext, aErrorEvent, nullptr,
328 aStatus);
329 called = true;
331 --errorDepth;
333 return called;
336 namespace mozilla {
337 namespace dom {
339 AsyncErrorReporter::AsyncErrorReporter(JSRuntime* aRuntime,
340 JSErrorReport* aErrorReport,
341 const char* aFallbackMessage,
342 bool aIsChromeError,
343 nsPIDOMWindow* aWindow)
344 : mSourceLine(static_cast<const PRUnichar*>(aErrorReport->uclinebuf))
345 , mLineNumber(aErrorReport->lineno)
346 , mColumn(aErrorReport->uctokenptr - aErrorReport->uclinebuf)
347 , mFlags(aErrorReport->flags)
349 if (!aErrorReport->filename) {
350 mFileName.SetIsVoid(true);
351 } else {
352 mFileName.AssignWithConversion(aErrorReport->filename);
355 const PRUnichar* m = static_cast<const PRUnichar*>(aErrorReport->ucmessage);
356 if (m) {
357 const PRUnichar* n = static_cast<const PRUnichar*>
358 (js::GetErrorTypeName(aRuntime, aErrorReport->exnType));
359 if (n) {
360 mErrorMsg.Assign(n);
361 mErrorMsg.AppendLiteral(": ");
363 mErrorMsg.Append(m);
366 if (mErrorMsg.IsEmpty() && aFallbackMessage) {
367 mErrorMsg.AssignWithConversion(aFallbackMessage);
370 mCategory = aIsChromeError ? NS_LITERAL_CSTRING("chrome javascript") :
371 NS_LITERAL_CSTRING("content javascript");
373 mInnerWindowID = 0;
374 if (aWindow && aWindow->IsOuterWindow()) {
375 aWindow = aWindow->GetCurrentInnerWindow();
377 if (aWindow) {
378 mInnerWindowID = aWindow->WindowID();
382 void
383 AsyncErrorReporter::ReportError()
385 nsCOMPtr<nsIScriptError> errorObject =
386 do_CreateInstance("@mozilla.org/scripterror;1");
387 if (!errorObject) {
388 return;
391 nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName,
392 mSourceLine, mLineNumber,
393 mColumn, mFlags, mCategory,
394 mInnerWindowID);
395 if (NS_FAILED(rv)) {
396 return;
399 nsCOMPtr<nsIConsoleService> consoleService =
400 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
401 if (!consoleService) {
402 return;
405 consoleService->LogMessage(errorObject);
406 return;
409 } // namespace dom
410 } // namespace mozilla
412 class ScriptErrorEvent : public AsyncErrorReporter
414 public:
415 ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
416 JSRuntime* aRuntime,
417 JSErrorReport* aErrorReport,
418 const char* aFallbackMessage,
419 nsIPrincipal* aScriptOriginPrincipal,
420 nsIPrincipal* aGlobalPrincipal,
421 nsPIDOMWindow* aWindow,
422 bool aDispatchEvent)
423 // Pass an empty category, then compute ours
424 : AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage,
425 nsContentUtils::IsSystemPrincipal(aGlobalPrincipal),
426 aWindow)
427 , mScriptGlobal(aScriptGlobal)
428 , mOriginPrincipal(aScriptOriginPrincipal)
429 , mDispatchEvent(aDispatchEvent)
433 NS_IMETHOD Run()
435 nsEventStatus status = nsEventStatus_eIgnore;
436 // First, notify the DOM that we have a script error.
437 if (mDispatchEvent) {
438 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
439 nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
440 if (docShell &&
441 !JSREPORT_IS_WARNING(mFlags) &&
442 !sHandlingScriptError) {
443 sHandlingScriptError = true; // Recursion prevention
445 nsRefPtr<nsPresContext> presContext;
446 docShell->GetPresContext(getter_AddRefs(presContext));
448 if (presContext) {
449 InternalScriptErrorEvent errorevent(true, NS_LOAD_ERROR);
451 errorevent.fileName = mFileName.get();
453 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
454 NS_ENSURE_STATE(sop);
455 nsIPrincipal* p = sop->GetPrincipal();
456 NS_ENSURE_STATE(p);
458 bool sameOrigin = !mOriginPrincipal;
460 if (p && !sameOrigin) {
461 if (NS_FAILED(p->Subsumes(mOriginPrincipal, &sameOrigin))) {
462 sameOrigin = false;
466 NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
467 if (sameOrigin) {
468 errorevent.errorMsg = mErrorMsg.get();
469 errorevent.lineNr = mLineNumber;
470 } else {
471 NS_WARNING("Not same origin error!");
472 errorevent.errorMsg = xoriginMsg.get();
473 errorevent.lineNr = 0;
476 nsEventDispatcher::Dispatch(win, presContext, &errorevent, nullptr,
477 &status);
480 sHandlingScriptError = false;
484 if (status != nsEventStatus_eConsumeNoDefault) {
485 AsyncErrorReporter::ReportError();
488 return NS_OK;
491 private:
492 nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
493 nsCOMPtr<nsIPrincipal> mOriginPrincipal;
494 bool mDispatchEvent;
496 static bool sHandlingScriptError;
499 bool ScriptErrorEvent::sHandlingScriptError = false;
501 // NOTE: This function could be refactored to use the above. The only reason
502 // it has not been done is that the code below only fills the error event
503 // after it has a good nsPresContext - whereas using the above function
504 // would involve always filling it. Is that a concern?
505 void
506 NS_ScriptErrorReporter(JSContext *cx,
507 const char *message,
508 JSErrorReport *report)
510 // We don't want to report exceptions too eagerly, but warnings in the
511 // absence of werror are swallowed whole, so report those now.
512 if (!JSREPORT_IS_WARNING(report->flags)) {
513 nsIXPConnect* xpc = nsContentUtils::XPConnect();
514 JS::Rooted<JSScript*> script(cx);
515 if (JS_DescribeScriptedCaller(cx, &script, nullptr)) {
516 xpc->MarkErrorUnreported(cx);
517 return;
520 if (xpc) {
521 nsAXPCNativeCallContext *cc = nullptr;
522 xpc->GetCurrentNativeCallContext(&cc);
523 if (cc) {
524 nsAXPCNativeCallContext *prev = cc;
525 while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
526 uint16_t lang;
527 if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
528 lang == nsAXPCNativeCallContext::LANG_JS) {
529 xpc->MarkErrorUnreported(cx);
530 return;
537 // XXX this means we are not going to get error reports on non DOM contexts
538 nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx);
540 // Note: we must do this before running any more code on cx (if cx is the
541 // dynamic script context).
542 ::JS_ClearPendingException(cx);
544 if (context) {
545 nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
547 if (globalObject) {
549 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
550 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
551 do_QueryInterface(globalObject);
552 NS_ASSERTION(scriptPrincipal, "Global objects must implement "
553 "nsIScriptObjectPrincipal");
554 nsContentUtils::AddScriptRunner(
555 new ScriptErrorEvent(globalObject,
556 JS_GetRuntime(cx),
557 report,
558 message,
559 nsJSPrincipals::get(report->originPrincipals),
560 scriptPrincipal->GetPrincipal(),
561 win,
562 /* We do not try to report Out Of Memory via a dom
563 * event because the dom event handler would
564 * encounter an OOM exception trying to process the
565 * event, and then we'd need to generate a new OOM
566 * event for that new OOM instance -- this isn't
567 * pretty.
569 report->errorNumber != JSMSG_OUT_OF_MEMORY));
573 if (nsContentUtils::DOMWindowDumpEnabled()) {
574 // Print it to stderr as well, for the benefit of those invoking
575 // mozilla with -console.
576 nsAutoCString error;
577 error.Assign("JavaScript ");
578 if (JSREPORT_IS_STRICT(report->flags))
579 error.Append("strict ");
580 if (JSREPORT_IS_WARNING(report->flags))
581 error.Append("warning: ");
582 else
583 error.Append("error: ");
584 error.Append(report->filename);
585 error.Append(", line ");
586 error.AppendInt(report->lineno, 10);
587 error.Append(": ");
588 if (report->ucmessage) {
589 AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(report->ucmessage),
590 error);
591 } else {
592 error.Append(message);
595 fprintf(stderr, "%s\n", error.get());
596 fflush(stderr);
599 #ifdef PR_LOGGING
600 if (!gJSDiagnostics)
601 gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
603 if (gJSDiagnostics) {
604 PR_LOG(gJSDiagnostics,
605 JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
606 ("file %s, line %u: %s\n%s%s",
607 report->filename, report->lineno, message,
608 report->linebuf ? report->linebuf : "",
609 (report->linebuf &&
610 report->linebuf[strlen(report->linebuf)-1] != '\n')
611 ? "\n"
612 : ""));
614 #endif
617 #ifdef DEBUG
618 // A couple of useful functions to call when you're debugging.
619 nsGlobalWindow *
620 JSObject2Win(JSContext *cx, JSObject *obj)
622 nsIXPConnect *xpc = nsContentUtils::XPConnect();
623 if (!xpc) {
624 return nullptr;
627 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
628 xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
629 if (wrapper) {
630 nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
631 if (win) {
632 return static_cast<nsGlobalWindow *>
633 (static_cast<nsPIDOMWindow *>(win));
637 return nullptr;
640 void
641 PrintWinURI(nsGlobalWindow *win)
643 if (!win) {
644 printf("No window passed in.\n");
645 return;
648 nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
649 if (!doc) {
650 printf("No document in the window.\n");
651 return;
654 nsIURI *uri = doc->GetDocumentURI();
655 if (!uri) {
656 printf("Document doesn't have a URI.\n");
657 return;
660 nsAutoCString spec;
661 uri->GetSpec(spec);
662 printf("%s\n", spec.get());
665 void
666 PrintWinCodebase(nsGlobalWindow *win)
668 if (!win) {
669 printf("No window passed in.\n");
670 return;
673 nsIPrincipal *prin = win->GetPrincipal();
674 if (!prin) {
675 printf("Window doesn't have principals.\n");
676 return;
679 nsCOMPtr<nsIURI> uri;
680 prin->GetURI(getter_AddRefs(uri));
681 if (!uri) {
682 printf("No URI, maybe the system principal.\n");
683 return;
686 nsAutoCString spec;
687 uri->GetSpec(spec);
688 printf("%s\n", spec.get());
691 void
692 DumpString(const nsAString &str)
694 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
696 #endif
698 #define JS_OPTIONS_DOT_STR "javascript.options."
700 static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
701 static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
702 #ifdef DEBUG
703 static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
704 #endif
705 static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
706 #ifdef JS_GC_ZEAL
707 static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
708 static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
709 #endif
710 static const char js_typeinfer_content_str[] = JS_OPTIONS_DOT_STR "typeinference.content";
711 static const char js_typeinfer_chrome_str[] = JS_OPTIONS_DOT_STR "typeinference.chrome";
712 static const char js_jit_hardening_str[] = JS_OPTIONS_DOT_STR "jit_hardening";
713 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
714 static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify";
715 static const char js_asmjs_content_str[] = JS_OPTIONS_DOT_STR "asmjs";
716 static const char js_baselinejit_content_str[] = JS_OPTIONS_DOT_STR "baselinejit.content";
717 static const char js_baselinejit_chrome_str[] = JS_OPTIONS_DOT_STR "baselinejit.chrome";
718 static const char js_baselinejit_eager_str[] = JS_OPTIONS_DOT_STR "baselinejit.unsafe_eager_compilation";
719 static const char js_ion_content_str[] = JS_OPTIONS_DOT_STR "ion.content";
720 static const char js_ion_chrome_str[] = JS_OPTIONS_DOT_STR "ion.chrome";
721 static const char js_ion_eager_str[] = JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation";
722 static const char js_parallel_parsing_str[] = JS_OPTIONS_DOT_STR "parallel_parsing";
723 static const char js_ion_parallel_compilation_str[] = JS_OPTIONS_DOT_STR "ion.parallel_compilation";
726 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
728 nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
729 JSContext *cx = context->mContext;
731 sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
732 sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
734 JS::ContextOptionsRef(cx).setExtraWarnings(Preferences::GetBool(js_strict_option_str));
736 // The vanilla GetGlobalObject returns null if a global isn't set up on
737 // the context yet. We can sometimes be call midway through context init,
738 // So ask for the member directly instead.
739 nsIScriptGlobalObject *global = context->GetGlobalObjectRef();
741 // XXX should we check for sysprin instead of a chrome window, to make
742 // XXX components be covered by the chrome pref instead of the content one?
743 nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global));
744 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
746 bool useTypeInference = Preferences::GetBool((chromeWindow || !contentWindow) ?
747 js_typeinfer_chrome_str :
748 js_typeinfer_content_str);
749 bool useHardening = Preferences::GetBool(js_jit_hardening_str);
750 bool useBaselineJIT = Preferences::GetBool((chromeWindow || !contentWindow) ?
751 js_baselinejit_chrome_str :
752 js_baselinejit_content_str);
753 bool useBaselineJITEager = Preferences::GetBool(js_baselinejit_eager_str);
754 bool useIon = Preferences::GetBool((chromeWindow || !contentWindow) ?
755 js_ion_chrome_str :
756 js_ion_content_str);
757 bool useIonEager = Preferences::GetBool(js_ion_eager_str);
758 bool useAsmJS = Preferences::GetBool(js_asmjs_content_str);
759 bool parallelParsing = Preferences::GetBool(js_parallel_parsing_str);
760 bool parallelIonCompilation = Preferences::GetBool(js_ion_parallel_compilation_str);
761 nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
762 if (xr) {
763 bool safeMode = false;
764 xr->GetInSafeMode(&safeMode);
765 if (safeMode) {
766 useTypeInference = false;
767 useHardening = false;
768 useBaselineJIT = false;
769 useBaselineJITEager = false;
770 useIon = false;
771 useIonEager = false;
772 useAsmJS = false;
776 JS::ContextOptionsRef(cx).setTypeInference(useTypeInference)
777 .setBaseline(useBaselineJIT)
778 .setIon(useIon)
779 .setAsmJS(useAsmJS);
781 #ifdef DEBUG
782 // In debug builds, warnings are enabled in chrome context if
783 // javascript.options.strict.debug is true
784 if (Preferences::GetBool(js_strict_debug_option_str) &&
785 (chromeWindow || !contentWindow)) {
786 JS::ContextOptionsRef(cx).setExtraWarnings(true);
788 #endif
790 JS::ContextOptionsRef(cx).setWerror(Preferences::GetBool(js_werror_option_str));
792 ::JS_SetParallelParsingEnabled(context->mContext, parallelParsing);
793 ::JS_SetParallelIonCompilationEnabled(context->mContext, parallelIonCompilation);
795 ::JS_SetGlobalJitCompilerOption(context->mContext, JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER,
796 (useBaselineJITEager ? 0 : -1));
798 ::JS_SetGlobalJitCompilerOption(context->mContext, JSJITCOMPILER_ION_USECOUNT_TRIGGER,
799 (useIonEager ? 0 : -1));
801 JSRuntime *rt = JS_GetRuntime(context->mContext);
802 JS_SetJitHardening(rt, useHardening);
804 #ifdef JS_GC_ZEAL
805 int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
806 int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
807 if (zeal >= 0)
808 ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
809 #endif
811 return 0;
814 nsJSContext::nsJSContext(bool aGCOnDestruction,
815 nsIScriptGlobalObject* aGlobalObject)
816 : mWindowProxy(nullptr)
817 , mGCOnDestruction(aGCOnDestruction)
818 , mGlobalObjectRef(aGlobalObject)
820 EnsureStatics();
822 mNext = sContextList;
823 mPrev = &sContextList;
824 if (sContextList) {
825 sContextList->mPrev = &mNext;
827 sContextList = this;
829 ++sContextCount;
831 mContext = ::JS_NewContext(sRuntime, gStackSize);
832 if (mContext) {
833 ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
835 // Make sure the new context gets the default context options
836 JS::ContextOptionsRef(mContext).setPrivateIsNSISupports(true)
837 .setNoDefaultCompartmentObject(true);
839 // Watch for the JS boolean options
840 Preferences::RegisterCallback(JSOptionChangedCallback,
841 js_options_dot_str, this);
843 mIsInitialized = false;
844 mProcessingScriptTag = false;
845 HoldJSObjects(this);
848 nsJSContext::~nsJSContext()
850 *mPrev = mNext;
851 if (mNext) {
852 mNext->mPrev = mPrev;
855 mGlobalObjectRef = nullptr;
857 DestroyJSContext();
859 --sContextCount;
861 if (!sContextCount && sDidShutdown) {
862 // The last context is being deleted, and we're already in the
863 // process of shutting down, release the JS runtime service, and
864 // the security manager.
866 NS_IF_RELEASE(sRuntimeService);
867 NS_IF_RELEASE(sSecurityManager);
871 // This function is called either by the destructor or unlink, which means that
872 // it can never be called when there is an outstanding ref to the
873 // nsIScriptContext on the stack. Our stack-scoped cx pushers hold such a ref,
874 // so we can assume here that mContext is not on the stack (and therefore not
875 // in use).
876 void
877 nsJSContext::DestroyJSContext()
879 if (!mContext) {
880 return;
883 // Clear our entry in the JSContext, bugzilla bug 66413
884 ::JS_SetContextPrivate(mContext, nullptr);
886 // Unregister our "javascript.options.*" pref-changed callback.
887 Preferences::UnregisterCallback(JSOptionChangedCallback,
888 js_options_dot_str, this);
890 if (mGCOnDestruction) {
891 PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
894 JS_DestroyContextNoGC(mContext);
895 mContext = nullptr;
896 DropJSObjects(this);
899 // QueryInterface implementation for nsJSContext
900 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
902 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
903 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy)
904 NS_IMPL_CYCLE_COLLECTION_TRACE_END
906 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
907 NS_ASSERTION(!tmp->mContext || !js::ContextHasOutstandingRequests(tmp->mContext),
908 "Trying to unlink a context with outstanding requests.");
909 tmp->mIsInitialized = false;
910 tmp->mGCOnDestruction = false;
911 tmp->mWindowProxy = nullptr;
912 tmp->DestroyJSContext();
913 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
914 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
915 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
916 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
917 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
918 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
919 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
921 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
922 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
923 NS_INTERFACE_MAP_ENTRY(nsISupports)
924 NS_INTERFACE_MAP_END
927 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
928 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
930 nsrefcnt
931 nsJSContext::GetCCRefcnt()
933 nsrefcnt refcnt = mRefCnt.get();
935 // In the (abnormal) case of synchronous cycle-collection, the context may be
936 // actively running JS code in which case we must keep it alive by adding an
937 // extra refcount.
938 if (mContext && js::ContextHasOutstandingRequests(mContext)) {
939 refcnt++;
942 return refcnt;
945 nsresult
946 nsJSContext::EvaluateString(const nsAString& aScript,
947 JS::Handle<JSObject*> aScopeObject,
948 JS::CompileOptions& aCompileOptions,
949 bool aCoerceToString,
950 JS::Value* aRetValue,
951 void **aOffThreadToken)
953 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
954 AutoCxPusher pusher(mContext);
955 nsJSUtils::EvaluateOptions evalOptions;
956 evalOptions.setCoerceToString(aCoerceToString);
957 return nsJSUtils::EvaluateString(mContext, aScript, aScopeObject,
958 aCompileOptions, evalOptions, aRetValue,
959 aOffThreadToken);
962 #ifdef DEBUG
963 bool
964 AtomIsEventHandlerName(nsIAtom *aName)
966 const PRUnichar *name = aName->GetUTF16String();
968 const PRUnichar *cp;
969 PRUnichar c;
970 for (cp = name; *cp != '\0'; ++cp)
972 c = *cp;
973 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
974 return false;
977 return true;
979 #endif
981 // Helper function to find the JSObject associated with a (presumably DOM)
982 // interface.
983 nsresult
984 nsJSContext::JSObjectFromInterface(nsISupports* aTarget,
985 JS::Handle<JSObject*> aScope,
986 JSObject** aRet)
988 // It is legal to specify a null target.
989 if (!aTarget) {
990 *aRet = nullptr;
991 return NS_OK;
994 AutoJSContext cx;
996 // Get the jsobject associated with this target
997 // We don't wrap here because we trust the JS engine to wrap the target
998 // later.
999 JS::Rooted<JS::Value> v(cx);
1000 nsresult rv = nsContentUtils::WrapNative(cx, aScope, aTarget, &v);
1001 NS_ENSURE_SUCCESS(rv, rv);
1003 JSObject* obj = v.toObjectOrNull();
1004 if (obj) {
1005 JS::ExposeObjectToActiveJS(obj);
1008 #ifdef DEBUG
1009 JS::Rooted<JSObject*> rootedObj(cx, obj);
1010 nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
1011 nsCOMPtr<nsISupports> native =
1012 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, rootedObj);
1013 NS_ASSERTION(native == targetSupp, "Native should be the target!");
1014 obj = rootedObj;
1015 #endif
1017 *aRet = obj;
1018 return NS_OK;
1021 nsresult
1022 nsJSContext::BindCompiledEventHandler(nsISupports* aTarget,
1023 JS::Handle<JSObject*> aScope,
1024 JS::Handle<JSObject*> aHandler,
1025 JS::MutableHandle<JSObject*> aBoundHandler)
1027 NS_ENSURE_ARG(aHandler);
1028 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1029 NS_PRECONDITION(!aBoundHandler, "Shouldn't already have a bound handler!");
1031 if (aScope) {
1032 JS::ExposeObjectToActiveJS(aScope);
1034 JS::ExposeObjectToActiveJS(aHandler);
1035 AutoPushJSContext cx(mContext);
1037 // Get the jsobject associated with this target
1038 JS::Rooted<JSObject*> target(cx);
1039 JS::Rooted<JSObject*> scope(cx, aScope);
1040 nsresult rv = JSObjectFromInterface(aTarget, scope, target.address());
1041 NS_ENSURE_SUCCESS(rv, rv);
1043 #ifdef DEBUG
1045 JSAutoCompartment ac(cx, aHandler);
1046 NS_ASSERTION(JS_TypeOfValue(cx,
1047 OBJECT_TO_JSVAL(aHandler)) == JSTYPE_FUNCTION,
1048 "Event handler object not a function");
1050 #endif
1052 JSAutoCompartment ac(cx, target);
1054 JSObject* funobj;
1055 // Make sure the handler function is parented by its event target object
1056 if (aHandler) {
1057 funobj = JS_CloneFunctionObject(cx, aHandler, target);
1058 if (!funobj) {
1059 rv = NS_ERROR_OUT_OF_MEMORY;
1061 } else {
1062 funobj = nullptr;
1065 aBoundHandler.set(funobj);
1067 return rv;
1070 nsIScriptGlobalObject *
1071 nsJSContext::GetGlobalObject()
1073 AutoJSContext cx;
1074 JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
1075 if (!global) {
1076 return nullptr;
1079 if (mGlobalObjectRef)
1080 return mGlobalObjectRef;
1082 #ifdef DEBUG
1084 JSObject *inner = JS_ObjectToInnerObject(cx, global);
1086 // If this assertion hits then it means that we have a window object as
1087 // our global, but we never called CreateOuterObject.
1088 NS_ASSERTION(inner == global, "Shouldn't be able to innerize here");
1090 #endif
1092 const JSClass *c = JS_GetClass(global);
1094 // Whenever we end up with globals that are JSCLASS_IS_DOMJSCLASS
1095 // and have an nsISupports DOM object, we will need to modify this
1096 // check here.
1097 MOZ_ASSERT(!(c->flags & JSCLASS_IS_DOMJSCLASS));
1098 if ((~c->flags) & (JSCLASS_HAS_PRIVATE |
1099 JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
1100 return nullptr;
1103 nsISupports *priv = static_cast<nsISupports*>(js::GetObjectPrivate(global));
1105 nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native =
1106 do_QueryInterface(priv);
1108 nsCOMPtr<nsIScriptGlobalObject> sgo;
1109 if (wrapped_native) {
1110 // The global object is a XPConnect wrapped native, the native in
1111 // the wrapper might be the nsIScriptGlobalObject
1113 sgo = do_QueryWrappedNative(wrapped_native);
1114 } else {
1115 sgo = do_QueryInterface(priv);
1118 // This'll return a pointer to something we're about to release, but
1119 // that's ok, the JS object will hold it alive long enough.
1120 return sgo;
1123 JSContext*
1124 nsJSContext::GetNativeContext()
1126 return mContext;
1129 nsresult
1130 nsJSContext::InitContext()
1132 // Make sure callers of this use
1133 // WillInitializeContext/DidInitializeContext around this call.
1134 NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
1136 if (!mContext)
1137 return NS_ERROR_OUT_OF_MEMORY;
1139 ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
1141 JSOptionChangedCallback(js_options_dot_str, this);
1143 return NS_OK;
1146 nsresult
1147 nsJSContext::InitializeExternalClasses()
1149 nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
1150 NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
1152 return nameSpaceManager->InitForContext(this);
1155 nsresult
1156 nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
1158 uint32_t argc;
1159 JS::Value *argv = nullptr;
1161 nsCxPusher pusher;
1162 pusher.Push(mContext);
1164 Maybe<nsRootedJSValueArray> tempStorage;
1166 JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
1167 nsresult rv =
1168 ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage);
1169 NS_ENSURE_SUCCESS(rv, rv);
1171 JS::AutoArrayRooter array(mContext, argc, argv);
1173 // got the arguments, now attach them.
1175 for (uint32_t i = 0; i < argc; ++i) {
1176 if (!JS_WrapValue(mContext, array.handleAt(i))) {
1177 return NS_ERROR_FAILURE;
1181 JSObject *args = ::JS_NewArrayObject(mContext, argc, array.array);
1182 if (!args) {
1183 return NS_ERROR_FAILURE;
1185 JS::Value vargs = OBJECT_TO_JSVAL(args);
1187 return JS_DefineProperty(mContext, aTarget, aPropName, vargs,
1188 nullptr, nullptr, 0) ? NS_OK : NS_ERROR_FAILURE;
1191 nsresult
1192 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
1193 JS::Handle<JSObject*> aScope,
1194 uint32_t *aArgc,
1195 JS::Value **aArgv,
1196 Maybe<nsRootedJSValueArray> &aTempStorage)
1198 nsresult rv = NS_OK;
1200 // If the array implements nsIJSArgArray, just grab the values directly.
1201 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
1202 if (fastArray != nullptr)
1203 return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
1205 // Take the slower path converting each item.
1206 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
1207 // SetProperty('arguments', ...);
1209 *aArgv = nullptr;
1210 *aArgc = 0;
1212 nsIXPConnect *xpc = nsContentUtils::XPConnect();
1213 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
1214 AutoJSContext cx;
1216 if (!aArgs)
1217 return NS_OK;
1218 uint32_t argCount;
1219 // This general purpose function may need to convert an arg array
1220 // (window.arguments, event-handler args) and a generic property.
1221 nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
1223 if (argsArray) {
1224 rv = argsArray->GetLength(&argCount);
1225 NS_ENSURE_SUCCESS(rv, rv);
1226 if (argCount == 0)
1227 return NS_OK;
1228 } else {
1229 argCount = 1; // the nsISupports which is not an array
1232 // Use the caller's auto guards to release and unroot.
1233 aTempStorage.construct((JSContext*)cx);
1234 aTempStorage.ref().SetCapacity(cx, argCount);
1235 JS::Value *argv = aTempStorage.ref().Elements();
1237 if (argsArray) {
1238 for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
1239 nsCOMPtr<nsISupports> arg;
1240 JS::Value *thisval = argv + argCtr;
1241 argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
1242 getter_AddRefs(arg));
1243 if (!arg) {
1244 *thisval = JSVAL_NULL;
1245 continue;
1247 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
1248 if (variant != nullptr) {
1249 rv = xpc->VariantToJS(cx, aScope, variant, thisval);
1250 } else {
1251 // And finally, support the nsISupportsPrimitives supplied
1252 // by the AppShell. It generally will pass only strings, but
1253 // as we have code for handling all, we may as well use it.
1254 rv = AddSupportsPrimitiveTojsvals(arg, thisval);
1255 if (rv == NS_ERROR_NO_INTERFACE) {
1256 // something else - probably an event object or similar -
1257 // just wrap it.
1258 #ifdef DEBUG
1259 // but first, check its not another nsISupportsPrimitive, as
1260 // these are now deprecated for use with script contexts.
1261 nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
1262 NS_ASSERTION(prim == nullptr,
1263 "Don't pass nsISupportsPrimitives - use nsIVariant!");
1264 #endif
1265 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
1266 JS::Rooted<JS::Value> v(cx);
1267 rv = nsContentUtils::WrapNative(cx, aScope, arg, &v,
1268 getter_AddRefs(wrapper));
1269 if (NS_SUCCEEDED(rv)) {
1270 *thisval = v;
1275 } else {
1276 nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
1277 if (variant) {
1278 rv = xpc->VariantToJS(cx, aScope, variant, argv);
1279 } else {
1280 NS_ERROR("Not an array, not an interface?");
1281 rv = NS_ERROR_UNEXPECTED;
1284 if (NS_FAILED(rv))
1285 return rv;
1286 *aArgv = argv;
1287 *aArgc = argCount;
1288 return NS_OK;
1291 // This really should go into xpconnect somewhere...
1292 nsresult
1293 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv)
1295 NS_PRECONDITION(aArg, "Empty arg");
1297 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
1298 if (!argPrimitive)
1299 return NS_ERROR_NO_INTERFACE;
1301 AutoJSContext cx;
1302 uint16_t type;
1303 argPrimitive->GetType(&type);
1305 switch(type) {
1306 case nsISupportsPrimitive::TYPE_CSTRING : {
1307 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
1308 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1310 nsAutoCString data;
1312 p->GetData(data);
1315 JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
1316 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1318 *aArgv = STRING_TO_JSVAL(str);
1320 break;
1322 case nsISupportsPrimitive::TYPE_STRING : {
1323 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
1324 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1326 nsAutoString data;
1328 p->GetData(data);
1330 // cast is probably safe since wchar_t and jschar are expected
1331 // to be equivalent; both unsigned 16-bit entities
1332 JSString *str =
1333 ::JS_NewUCStringCopyN(cx, data.get(), data.Length());
1334 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1336 *aArgv = STRING_TO_JSVAL(str);
1337 break;
1339 case nsISupportsPrimitive::TYPE_PRBOOL : {
1340 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
1341 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1343 bool data;
1345 p->GetData(&data);
1347 *aArgv = BOOLEAN_TO_JSVAL(data);
1349 break;
1351 case nsISupportsPrimitive::TYPE_PRUINT8 : {
1352 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
1353 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1355 uint8_t data;
1357 p->GetData(&data);
1359 *aArgv = INT_TO_JSVAL(data);
1361 break;
1363 case nsISupportsPrimitive::TYPE_PRUINT16 : {
1364 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
1365 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1367 uint16_t data;
1369 p->GetData(&data);
1371 *aArgv = INT_TO_JSVAL(data);
1373 break;
1375 case nsISupportsPrimitive::TYPE_PRUINT32 : {
1376 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
1377 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1379 uint32_t data;
1381 p->GetData(&data);
1383 *aArgv = INT_TO_JSVAL(data);
1385 break;
1387 case nsISupportsPrimitive::TYPE_CHAR : {
1388 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
1389 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1391 char data;
1393 p->GetData(&data);
1395 JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
1396 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1398 *aArgv = STRING_TO_JSVAL(str);
1400 break;
1402 case nsISupportsPrimitive::TYPE_PRINT16 : {
1403 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
1404 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1406 int16_t data;
1408 p->GetData(&data);
1410 *aArgv = INT_TO_JSVAL(data);
1412 break;
1414 case nsISupportsPrimitive::TYPE_PRINT32 : {
1415 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
1416 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1418 int32_t data;
1420 p->GetData(&data);
1422 *aArgv = INT_TO_JSVAL(data);
1424 break;
1426 case nsISupportsPrimitive::TYPE_FLOAT : {
1427 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
1428 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1430 float data;
1432 p->GetData(&data);
1434 *aArgv = ::JS_NumberValue(data);
1436 break;
1438 case nsISupportsPrimitive::TYPE_DOUBLE : {
1439 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
1440 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1442 double data;
1444 p->GetData(&data);
1446 *aArgv = ::JS_NumberValue(data);
1448 break;
1450 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
1451 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
1452 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1454 nsCOMPtr<nsISupports> data;
1455 nsIID *iid = nullptr;
1457 p->GetData(getter_AddRefs(data));
1458 p->GetDataIID(&iid);
1459 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
1461 AutoFree iidGuard(iid); // Free iid upon destruction.
1463 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
1464 JS::Rooted<JSObject*> global(cx, GetWindowProxy());
1465 JS::Rooted<JS::Value> v(cx);
1466 nsresult rv = nsContentUtils::WrapNative(cx, global,
1467 data, iid, &v,
1468 getter_AddRefs(wrapper));
1469 NS_ENSURE_SUCCESS(rv, rv);
1471 *aArgv = v;
1473 break;
1475 case nsISupportsPrimitive::TYPE_ID :
1476 case nsISupportsPrimitive::TYPE_PRUINT64 :
1477 case nsISupportsPrimitive::TYPE_PRINT64 :
1478 case nsISupportsPrimitive::TYPE_PRTIME :
1479 case nsISupportsPrimitive::TYPE_VOID : {
1480 NS_WARNING("Unsupported primitive type used");
1481 *aArgv = JSVAL_NULL;
1482 break;
1484 default : {
1485 NS_WARNING("Unknown primitive type used");
1486 *aArgv = JSVAL_NULL;
1487 break;
1490 return NS_OK;
1493 #ifdef NS_TRACE_MALLOC
1495 #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
1496 #include <fcntl.h>
1497 #ifdef XP_UNIX
1498 #include <unistd.h>
1499 #endif
1500 #ifdef XP_WIN32
1501 #include <io.h>
1502 #endif
1503 #include "nsTraceMalloc.h"
1505 static bool
1506 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
1508 if (nsContentUtils::IsCallerChrome())
1509 return true;
1510 JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
1511 return false;
1514 static bool
1515 TraceMallocDisable(JSContext *cx, unsigned argc, JS::Value *vp)
1517 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1518 return false;
1520 NS_TraceMallocDisable();
1521 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1522 return true;
1525 static bool
1526 TraceMallocEnable(JSContext *cx, unsigned argc, JS::Value *vp)
1528 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1529 return false;
1531 NS_TraceMallocEnable();
1532 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1533 return true;
1536 static bool
1537 TraceMallocOpenLogFile(JSContext *cx, unsigned argc, JS::Value *vp)
1539 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1541 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1542 return false;
1544 int fd;
1545 if (argc == 0) {
1546 fd = -1;
1547 } else {
1548 JSString *str = JS::ToString(cx, args[0]);
1549 if (!str)
1550 return false;
1551 JSAutoByteString filename(cx, str);
1552 if (!filename)
1553 return false;
1554 fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
1555 if (fd < 0) {
1556 JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
1557 return false;
1560 args.rval().setInt32(fd);
1561 return true;
1564 static bool
1565 TraceMallocChangeLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
1567 JS::CallArgs args = CallArgsFromVp(argc, vp);
1569 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1570 return false;
1572 int32_t fd, oldfd;
1573 if (args.length() == 0) {
1574 oldfd = -1;
1575 } else {
1576 if (!JS::ToInt32(cx, args[0], &fd))
1577 return false;
1578 oldfd = NS_TraceMallocChangeLogFD(fd);
1579 if (oldfd == -2) {
1580 JS_ReportOutOfMemory(cx);
1581 return false;
1584 args.rval().setInt32(oldfd);
1585 return true;
1588 static bool
1589 TraceMallocCloseLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
1591 JS::CallArgs args = CallArgsFromVp(argc, vp);
1593 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1594 return false;
1596 int32_t fd;
1597 if (args.length() == 0) {
1598 args.rval().setUndefined();
1599 return true;
1601 if (!JS::ToInt32(cx, args[0], &fd))
1602 return false;
1603 NS_TraceMallocCloseLogFD((int) fd);
1604 args.rval().setInt32(fd);
1605 return true;
1608 static bool
1609 TraceMallocLogTimestamp(JSContext *cx, unsigned argc, JS::Value *vp)
1611 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1612 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1613 return false;
1615 JSString *str = JS::ToString(cx, args.get(0));
1616 if (!str)
1617 return false;
1618 JSAutoByteString caption(cx, str);
1619 if (!caption)
1620 return false;
1621 NS_TraceMallocLogTimestamp(caption.ptr());
1622 args.rval().setUndefined();
1623 return true;
1626 static bool
1627 TraceMallocDumpAllocations(JSContext *cx, unsigned argc, JS::Value *vp)
1629 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1630 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1631 return false;
1633 JSString *str = JS::ToString(cx, args.get(0));
1634 if (!str)
1635 return false;
1636 JSAutoByteString pathname(cx, str);
1637 if (!pathname)
1638 return false;
1639 if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
1640 JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
1641 return false;
1643 args.rval().setUndefined();
1644 return true;
1647 static const JSFunctionSpec TraceMallocFunctions[] = {
1648 JS_FS("TraceMallocDisable", TraceMallocDisable, 0, 0),
1649 JS_FS("TraceMallocEnable", TraceMallocEnable, 0, 0),
1650 JS_FS("TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0),
1651 JS_FS("TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0),
1652 JS_FS("TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0),
1653 JS_FS("TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0),
1654 JS_FS("TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0),
1655 JS_FS_END
1658 #endif /* NS_TRACE_MALLOC */
1660 #ifdef MOZ_DMD
1662 #include <errno.h>
1664 namespace mozilla {
1665 namespace dmd {
1667 // See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
1668 // how to use DMD.
1670 static bool
1671 ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
1673 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1674 JSString *str = JS::ToString(cx, args.get(0));
1675 if (!str)
1676 return false;
1677 JSAutoByteString pathname(cx, str);
1678 if (!pathname)
1679 return false;
1681 FILE* fp = fopen(pathname.ptr(), "w");
1682 if (!fp) {
1683 JS_ReportError(cx, "DMD can't open %s: %s",
1684 pathname.ptr(), strerror(errno));
1685 return false;
1688 dmd::ClearReports();
1689 fprintf(stderr, "DMD: running reporters...\n");
1690 dmd::RunReporters();
1691 dmd::Writer writer(FpWrite, fp);
1692 dmd::Dump(writer);
1694 fclose(fp);
1696 args.rval().setUndefined();
1697 return true;
1700 } // namespace dmd
1701 } // namespace mozilla
1703 static const JSFunctionSpec DMDFunctions[] = {
1704 JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0),
1705 JS_FS_END
1708 #endif // defined(MOZ_DMD)
1710 #ifdef MOZ_JPROF
1712 #include <signal.h>
1714 inline bool
1715 IsJProfAction(struct sigaction *action)
1717 return (action->sa_sigaction &&
1718 (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
1721 void NS_JProfStartProfiling();
1722 void NS_JProfStopProfiling();
1723 void NS_JProfClearCircular();
1725 static bool
1726 JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1728 NS_JProfStartProfiling();
1729 return true;
1732 void NS_JProfStartProfiling()
1734 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
1735 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
1736 // JP_RTC_HZ)
1737 struct sigaction action;
1739 // Must check ALRM before PROF since both are enabled for real-time
1740 sigaction(SIGALRM, nullptr, &action);
1741 //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1742 if (IsJProfAction(&action)) {
1743 //printf("Beginning real-time jprof profiling.\n");
1744 raise(SIGALRM);
1745 return;
1748 sigaction(SIGPROF, nullptr, &action);
1749 //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1750 if (IsJProfAction(&action)) {
1751 //printf("Beginning process-time jprof profiling.\n");
1752 raise(SIGPROF);
1753 return;
1756 sigaction(SIGPOLL, nullptr, &action);
1757 //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1758 if (IsJProfAction(&action)) {
1759 //printf("Beginning rtc-based jprof profiling.\n");
1760 raise(SIGPOLL);
1761 return;
1764 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
1767 static bool
1768 JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1770 NS_JProfStopProfiling();
1771 return true;
1774 void
1775 NS_JProfStopProfiling()
1777 raise(SIGUSR1);
1778 //printf("Stopped jprof profiling.\n");
1781 static bool
1782 JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1784 NS_JProfClearCircular();
1785 return true;
1788 void
1789 NS_JProfClearCircular()
1791 raise(SIGUSR2);
1792 //printf("cleared jprof buffer\n");
1795 static bool
1796 JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1798 // Not ideal...
1799 NS_JProfStopProfiling();
1800 NS_JProfStartProfiling();
1801 return true;
1804 static const JSFunctionSpec JProfFunctions[] = {
1805 JS_FS("JProfStartProfiling", JProfStartProfilingJS, 0, 0),
1806 JS_FS("JProfStopProfiling", JProfStopProfilingJS, 0, 0),
1807 JS_FS("JProfClearCircular", JProfClearCircularJS, 0, 0),
1808 JS_FS("JProfSaveCircular", JProfSaveCircularJS, 0, 0),
1809 JS_FS_END
1812 #endif /* defined(MOZ_JPROF) */
1814 nsresult
1815 nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
1817 nsresult rv = InitializeExternalClasses();
1818 NS_ENSURE_SUCCESS(rv, rv);
1820 JSOptionChangedCallback(js_options_dot_str, this);
1821 AutoPushJSContext cx(mContext);
1823 // Attempt to initialize profiling functions
1824 ::JS_DefineProfilingFunctions(cx, aGlobalObj);
1826 #ifdef NS_TRACE_MALLOC
1827 if (nsContentUtils::IsCallerChrome()) {
1828 // Attempt to initialize TraceMalloc functions
1829 ::JS_DefineFunctions(cx, aGlobalObj, TraceMallocFunctions);
1831 #endif
1833 #ifdef MOZ_DMD
1834 // Attempt to initialize DMD functions
1835 ::JS_DefineFunctions(cx, aGlobalObj, DMDFunctions);
1836 #endif
1838 #ifdef MOZ_JPROF
1839 // Attempt to initialize JProf functions
1840 ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
1841 #endif
1843 return rv;
1846 void
1847 nsJSContext::WillInitializeContext()
1849 mIsInitialized = false;
1852 void
1853 nsJSContext::DidInitializeContext()
1855 mIsInitialized = true;
1858 bool
1859 nsJSContext::IsContextInitialized()
1861 return mIsInitialized;
1864 bool
1865 nsJSContext::GetProcessingScriptTag()
1867 return mProcessingScriptTag;
1870 void
1871 nsJSContext::SetProcessingScriptTag(bool aFlag)
1873 mProcessingScriptTag = aFlag;
1876 void
1877 FullGCTimerFired(nsITimer* aTimer, void* aClosure)
1879 NS_RELEASE(sFullGCTimer);
1881 uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
1882 nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
1883 nsJSContext::IncrementalGC);
1886 //static
1887 void
1888 nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
1889 IsIncremental aIncremental,
1890 IsCompartment aCompartment,
1891 IsShrinking aShrinking,
1892 int64_t aSliceMillis)
1894 PROFILER_LABEL("GC", "GarbageCollectNow");
1896 MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
1898 KillGCTimer();
1899 KillShrinkGCBuffersTimer();
1901 // Reset sPendingLoadCount in case the timer that fired was a
1902 // timer we scheduled due to a normal GC timer firing while
1903 // documents were loading. If this happens we're waiting for a
1904 // document that is taking a long time to load, and we effectively
1905 // ignore the fact that the currently loading documents are still
1906 // loading and move on as if they weren't.
1907 sPendingLoadCount = 0;
1908 sLoadingInProgress = false;
1910 if (!nsContentUtils::XPConnect() || !sRuntime) {
1911 return;
1914 if (sCCLockedOut && aIncremental == IncrementalGC) {
1915 // We're in the middle of incremental GC. Do another slice.
1916 JS::PrepareForIncrementalGC(sRuntime);
1917 JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
1918 return;
1921 JS::PrepareForFullGC(sRuntime);
1922 if (aIncremental == IncrementalGC) {
1923 JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
1924 } else {
1925 JS::GCForReason(sRuntime, aReason);
1929 //static
1930 void
1931 nsJSContext::ShrinkGCBuffersNow()
1933 PROFILER_LABEL("GC", "ShrinkGCBuffersNow");
1935 KillShrinkGCBuffersTimer();
1937 JS::ShrinkGCBuffers(sRuntime);
1940 static void
1941 FinishAnyIncrementalGC()
1943 if (sCCLockedOut) {
1944 // We're in the middle of an incremental GC, so finish it.
1945 JS::PrepareForIncrementalGC(sRuntime);
1946 JS::FinishIncrementalGC(sRuntime, JS::gcreason::CC_FORCED);
1950 static void
1951 FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless)
1953 PRTime startTime = PR_Now();
1954 FinishAnyIncrementalGC();
1955 bool earlyForgetSkippable =
1956 sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS;
1957 nsCycleCollector_forgetSkippable(aRemoveChildless, earlyForgetSkippable);
1958 sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
1959 ++sCleanupsSinceLastGC;
1960 PRTime delta = PR_Now() - startTime;
1961 if (sMinForgetSkippableTime > delta) {
1962 sMinForgetSkippableTime = delta;
1964 if (sMaxForgetSkippableTime < delta) {
1965 sMaxForgetSkippableTime = delta;
1967 sTotalForgetSkippableTime += delta;
1968 sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
1969 ++sForgetSkippableBeforeCC;
1972 MOZ_ALWAYS_INLINE
1973 static uint32_t
1974 TimeBetween(PRTime start, PRTime end)
1976 MOZ_ASSERT(end >= start);
1977 return (uint32_t)(end - start) / PR_USEC_PER_MSEC;
1980 struct CycleCollectorStats
1982 void Clear()
1984 mBeginSliceTime = 0;
1985 mBeginTime = 0;
1986 mMaxGCDuration = 0;
1987 mRanSyncForgetSkippable = false;
1988 mSuspected = 0;
1989 mMaxSkippableDuration = 0;
1990 mAnyLockedOut = false;
1993 // Time the current slice began, including any GC finishing.
1994 PRTime mBeginSliceTime;
1996 // Time the current cycle collection began.
1997 PRTime mBeginTime;
1999 // The longest GC finishing duration for any slice of the current CC.
2000 uint32_t mMaxGCDuration;
2002 // True if we ran sync forget skippable in any slice of the current CC.
2003 bool mRanSyncForgetSkippable;
2005 // Number of suspected objects at the start of the current CC.
2006 uint32_t mSuspected;
2008 // The longest duration spent on sync forget skippable in any slice of the
2009 // current CC
2010 uint32_t mMaxSkippableDuration;
2012 // True if we were locked out by the GC in any slice of the current CC.
2013 bool mAnyLockedOut;
2016 CycleCollectorStats gCCStats;
2019 static void
2020 PrepareForCycleCollection(int32_t aExtraForgetSkippableCalls = 0)
2022 gCCStats.mBeginSliceTime = PR_Now();
2024 // Before we begin the cycle collection, make sure there is no active GC.
2025 PRTime endGCTime;
2026 if (sCCLockedOut) {
2027 gCCStats.mAnyLockedOut = true;
2028 FinishAnyIncrementalGC();
2029 endGCTime = PR_Now();
2030 uint32_t gcTime = TimeBetween(gCCStats.mBeginSliceTime, endGCTime);
2031 gCCStats.mMaxGCDuration = std::max(gCCStats.mMaxGCDuration, gcTime);
2032 } else {
2033 endGCTime = gCCStats.mBeginSliceTime;
2036 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
2037 // is particularly useful if we recently finished a GC.
2038 if (aExtraForgetSkippableCalls >= 0) {
2039 bool ranSyncForgetSkippable = false;
2040 while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
2041 FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
2042 ranSyncForgetSkippable = true;
2045 for (int32_t i = 0; i < aExtraForgetSkippableCalls; ++i) {
2046 FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
2047 ranSyncForgetSkippable = true;
2050 if (ranSyncForgetSkippable) {
2051 gCCStats.mMaxSkippableDuration =
2052 std::max(gCCStats.mMaxSkippableDuration, TimeBetween(endGCTime, PR_Now()));
2053 gCCStats.mRanSyncForgetSkippable = true;
2059 //static
2060 void
2061 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
2062 int32_t aExtraForgetSkippableCalls)
2064 if (!NS_IsMainThread()) {
2065 return;
2068 PROFILER_LABEL("CC", "CycleCollectNow");
2069 PrepareForCycleCollection(aExtraForgetSkippableCalls);
2070 nsCycleCollector_collect(aListener);
2073 //static
2074 void
2075 nsJSContext::ScheduledCycleCollectNow()
2077 if (!NS_IsMainThread()) {
2078 return;
2081 PROFILER_LABEL("CC", "ScheduledCycleCollectNow");
2082 PrepareForCycleCollection();
2083 nsCycleCollector_scheduledCollect();
2086 //static
2087 void
2088 nsJSContext::BeginCycleCollectionCallback()
2090 MOZ_ASSERT(NS_IsMainThread());
2092 gCCStats.mBeginTime = gCCStats.mBeginSliceTime ? gCCStats.mBeginSliceTime : PR_Now();
2093 gCCStats.mSuspected = nsCycleCollector_suspectedCount();
2095 KillCCTimer();
2098 //static
2099 void
2100 nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
2102 MOZ_ASSERT(NS_IsMainThread());
2104 sCCollectedWaitingForGC += aResults.mFreedRefCounted + aResults.mFreedGCed;
2106 // If we collected a substantial amount of cycles, poke the GC since more objects
2107 // might be unreachable now.
2108 if (sCCollectedWaitingForGC > 250 ||
2109 sLikelyShortLivingObjectsNeedingGC > 2500 ||
2110 sNeedsGCAfterCC) {
2111 PokeGC(JS::gcreason::CC_WAITING);
2114 PRTime endCCTime = PR_Now();
2116 // Log information about the CC via telemetry, JSON and the console.
2117 uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTime);
2118 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut);
2119 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable);
2120 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
2122 if (sLastCCEndTime) {
2123 uint32_t timeBetween = TimeBetween(sLastCCEndTime, gCCStats.mBeginTime);
2124 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
2126 sLastCCEndTime = endCCTime;
2128 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
2129 sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
2131 PRTime delta = GetCollectionTimeDelta();
2133 uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
2134 uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX)
2135 ? 0 : sMinForgetSkippableTime;
2137 if (sPostGCEventsToConsole) {
2138 nsCString mergeMsg;
2139 if (aResults.mMergedZones) {
2140 mergeMsg.AssignLiteral(" merged");
2143 nsCString gcMsg;
2144 if (aResults.mForcedGC) {
2145 gcMsg.AssignLiteral(", forced a GC");
2148 NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
2149 MOZ_UTF16("CC(T+%.1f) duration: %lums, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu waiting for GC)%s\n")
2150 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"));
2151 nsString msg;
2152 msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
2153 ccNowDuration, gCCStats.mSuspected,
2154 aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
2155 aResults.mFreedRefCounted, aResults.mFreedGCed,
2156 sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
2157 gcMsg.get(),
2158 sForgetSkippableBeforeCC,
2159 minForgetSkippableTime / PR_USEC_PER_MSEC,
2160 sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
2161 (sTotalForgetSkippableTime / cleanups) /
2162 PR_USEC_PER_MSEC,
2163 sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
2164 gCCStats.mMaxSkippableDuration, sRemovedPurples));
2165 nsCOMPtr<nsIConsoleService> cs =
2166 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2167 if (cs) {
2168 cs->LogStringMessage(msg.get());
2172 if (sPostGCEventsToObserver) {
2173 NS_NAMED_MULTILINE_LITERAL_STRING(kJSONFmt,
2174 MOZ_UTF16("{ \"timestamp\": %llu, ")
2175 MOZ_UTF16("\"duration\": %llu, ")
2176 MOZ_UTF16("\"max_finish_gc_duration\": %llu, ")
2177 MOZ_UTF16("\"max_sync_skippable_duration\": %llu, ")
2178 MOZ_UTF16("\"suspected\": %lu, ")
2179 MOZ_UTF16("\"visited\": { ")
2180 MOZ_UTF16("\"RCed\": %lu, ")
2181 MOZ_UTF16("\"GCed\": %lu }, ")
2182 MOZ_UTF16("\"collected\": { ")
2183 MOZ_UTF16("\"RCed\": %lu, ")
2184 MOZ_UTF16("\"GCed\": %lu }, ")
2185 MOZ_UTF16("\"waiting_for_gc\": %lu, ")
2186 MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
2187 MOZ_UTF16("\"forced_gc\": %d, ")
2188 MOZ_UTF16("\"forget_skippable\": { ")
2189 MOZ_UTF16("\"times_before_cc\": %lu, ")
2190 MOZ_UTF16("\"min\": %lu, ")
2191 MOZ_UTF16("\"max\": %lu, ")
2192 MOZ_UTF16("\"avg\": %lu, ")
2193 MOZ_UTF16("\"total\": %lu, ")
2194 MOZ_UTF16("\"removed\": %lu } ")
2195 MOZ_UTF16("}"));
2196 nsString json;
2197 json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime,
2198 ccNowDuration, gCCStats.mMaxGCDuration,
2199 gCCStats.mMaxSkippableDuration,
2200 gCCStats.mSuspected,
2201 aResults.mVisitedRefCounted, aResults.mVisitedGCed,
2202 aResults.mFreedRefCounted, aResults.mFreedGCed,
2203 sCCollectedWaitingForGC,
2204 sLikelyShortLivingObjectsNeedingGC,
2205 aResults.mForcedGC,
2206 sForgetSkippableBeforeCC,
2207 minForgetSkippableTime / PR_USEC_PER_MSEC,
2208 sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
2209 (sTotalForgetSkippableTime / cleanups) /
2210 PR_USEC_PER_MSEC,
2211 sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
2212 sRemovedPurples));
2213 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2214 if (observerService) {
2215 observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get());
2219 // Update global state to indicate we have just run a cycle collection.
2220 sMinForgetSkippableTime = UINT32_MAX;
2221 sMaxForgetSkippableTime = 0;
2222 sTotalForgetSkippableTime = 0;
2223 sRemovedPurples = 0;
2224 sForgetSkippableBeforeCC = 0;
2225 sNeedsFullCC = false;
2226 sNeedsGCAfterCC = false;
2227 gCCStats.Clear();
2230 // static
2231 void
2232 InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
2234 NS_RELEASE(sInterSliceGCTimer);
2235 nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
2236 nsJSContext::IncrementalGC,
2237 nsJSContext::CompartmentGC,
2238 nsJSContext::NonShrinkingGC,
2239 NS_INTERSLICE_GC_BUDGET);
2242 // static
2243 void
2244 GCTimerFired(nsITimer *aTimer, void *aClosure)
2246 NS_RELEASE(sGCTimer);
2248 uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
2249 nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
2250 nsJSContext::IncrementalGC,
2251 nsJSContext::CompartmentGC);
2254 void
2255 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
2257 NS_RELEASE(sShrinkGCBuffersTimer);
2259 nsJSContext::ShrinkGCBuffersNow();
2262 static bool
2263 ShouldTriggerCC(uint32_t aSuspected)
2265 return sNeedsFullCC ||
2266 aSuspected > NS_CC_PURPLE_LIMIT ||
2267 (aSuspected > NS_CC_FORCED_PURPLE_LIMIT &&
2268 sLastCCEndTime + NS_CC_FORCED < PR_Now());
2271 static void
2272 CCTimerFired(nsITimer *aTimer, void *aClosure)
2274 if (sDidShutdown) {
2275 return;
2278 static uint32_t ccDelay = NS_CC_DELAY;
2279 if (sCCLockedOut) {
2280 ccDelay = NS_CC_DELAY / 3;
2282 PRTime now = PR_Now();
2283 if (sCCLockedOutTime == 0) {
2284 // Reset sCCTimerFireCount so that we run forgetSkippable
2285 // often enough before CC. Because of reduced ccDelay
2286 // forgetSkippable will be called just a few times.
2287 // NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
2288 // forgetSkippable and CycleCollectNow eventually.
2289 sCCTimerFireCount = 0;
2290 sCCLockedOutTime = now;
2291 return;
2293 if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
2294 return;
2298 ++sCCTimerFireCount;
2300 // During early timer fires, we only run forgetSkippable. During the first
2301 // late timer fire, we decide if we are going to have a second and final
2302 // late timer fire, where we may run the CC.
2303 const uint32_t numEarlyTimerFires = ccDelay / NS_CC_SKIPPABLE_DELAY - 2;
2304 bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
2305 uint32_t suspected = nsCycleCollector_suspectedCount();
2306 if (isLateTimerFire && ShouldTriggerCC(suspected)) {
2307 if (sCCTimerFireCount == numEarlyTimerFires + 1) {
2308 FireForgetSkippable(suspected, true);
2309 if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2310 // Our efforts to avoid a CC have failed, so we return to let the
2311 // timer fire once more to trigger a CC.
2312 return;
2314 } else {
2315 // We are in the final timer fire and still meet the conditions for
2316 // triggering a CC. Let CycleCollectNow finish the current IGC, if any,
2317 // because that will allow us to include the GC time in the CC pause.
2318 nsJSContext::ScheduledCycleCollectNow();
2320 } else if ((sPreviousSuspectedCount + 100) <= suspected) {
2321 // Only do a forget skippable if there are more than a few new objects.
2322 FireForgetSkippable(suspected, false);
2325 if (isLateTimerFire) {
2326 ccDelay = NS_CC_DELAY;
2328 // We have either just run the CC or decided we don't want to run the CC
2329 // next time, so kill the timer.
2330 sPreviousSuspectedCount = 0;
2331 nsJSContext::KillCCTimer();
2335 // static
2336 uint32_t
2337 nsJSContext::CleanupsSinceLastGC()
2339 return sCleanupsSinceLastGC;
2342 // static
2343 void
2344 nsJSContext::LoadStart()
2346 sLoadingInProgress = true;
2347 ++sPendingLoadCount;
2350 // static
2351 void
2352 nsJSContext::LoadEnd()
2354 if (!sLoadingInProgress)
2355 return;
2357 // sPendingLoadCount is not a well managed load counter (and doesn't
2358 // need to be), so make sure we don't make it wrap backwards here.
2359 if (sPendingLoadCount > 0) {
2360 --sPendingLoadCount;
2361 return;
2364 // Its probably a good idea to GC soon since we have finished loading.
2365 sLoadingInProgress = false;
2366 PokeGC(JS::gcreason::LOAD_END);
2369 // static
2370 void
2371 nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
2373 if (sGCTimer || sInterSliceGCTimer || sShuttingDown) {
2374 // There's already a timer for GC'ing, just return
2375 return;
2378 if (sCCTimer) {
2379 // Make sure CC is called...
2380 sNeedsFullCC = true;
2381 // and GC after it.
2382 sNeedsGCAfterCC = true;
2383 return;
2386 CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
2388 if (!sGCTimer) {
2389 // Failed to create timer (probably because we're in XPCOM shutdown)
2390 return;
2393 static bool first = true;
2395 sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason),
2396 aDelay
2397 ? aDelay
2398 : (first
2399 ? NS_FIRST_GC_DELAY
2400 : NS_GC_DELAY),
2401 nsITimer::TYPE_ONE_SHOT);
2403 first = false;
2406 // static
2407 void
2408 nsJSContext::PokeShrinkGCBuffers()
2410 if (sShrinkGCBuffersTimer || sShuttingDown) {
2411 return;
2414 CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
2416 if (!sShrinkGCBuffersTimer) {
2417 // Failed to create timer (probably because we're in XPCOM shutdown)
2418 return;
2421 sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nullptr,
2422 NS_SHRINK_GC_BUFFERS_DELAY,
2423 nsITimer::TYPE_ONE_SHOT);
2426 // static
2427 void
2428 nsJSContext::MaybePokeCC()
2430 if (sCCTimer || sShuttingDown || !sHasRunGC) {
2431 return;
2434 if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2435 sCCTimerFireCount = 0;
2436 CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
2437 if (!sCCTimer) {
2438 return;
2440 // We can kill some objects before running forgetSkippable.
2441 nsCycleCollector_dispatchDeferredDeletion();
2443 sCCTimer->InitWithFuncCallback(CCTimerFired, nullptr,
2444 NS_CC_SKIPPABLE_DELAY,
2445 nsITimer::TYPE_REPEATING_SLACK);
2449 //static
2450 void
2451 nsJSContext::KillGCTimer()
2453 if (sGCTimer) {
2454 sGCTimer->Cancel();
2456 NS_RELEASE(sGCTimer);
2460 void
2461 nsJSContext::KillFullGCTimer()
2463 if (sFullGCTimer) {
2464 sFullGCTimer->Cancel();
2465 NS_RELEASE(sFullGCTimer);
2469 void
2470 nsJSContext::KillInterSliceGCTimer()
2472 if (sInterSliceGCTimer) {
2473 sInterSliceGCTimer->Cancel();
2474 NS_RELEASE(sInterSliceGCTimer);
2478 //static
2479 void
2480 nsJSContext::KillShrinkGCBuffersTimer()
2482 if (sShrinkGCBuffersTimer) {
2483 sShrinkGCBuffersTimer->Cancel();
2485 NS_RELEASE(sShrinkGCBuffersTimer);
2489 //static
2490 void
2491 nsJSContext::KillCCTimer()
2493 sCCLockedOutTime = 0;
2495 if (sCCTimer) {
2496 sCCTimer->Cancel();
2498 NS_RELEASE(sCCTimer);
2502 void
2503 nsJSContext::GC(JS::gcreason::Reason aReason)
2505 PokeGC(aReason);
2508 class NotifyGCEndRunnable : public nsRunnable
2510 nsString mMessage;
2512 public:
2513 NotifyGCEndRunnable(const nsString& aMessage) : mMessage(aMessage) {}
2515 NS_DECL_NSIRUNNABLE
2518 NS_IMETHODIMP
2519 NotifyGCEndRunnable::Run()
2521 MOZ_ASSERT(NS_IsMainThread());
2523 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2524 if (!observerService) {
2525 return NS_OK;
2528 const jschar oomMsg[3] = { '{', '}', 0 };
2529 const jschar *toSend = mMessage.get() ? mMessage.get() : oomMsg;
2530 observerService->NotifyObservers(nullptr, "garbage-collection-statistics", toSend);
2532 return NS_OK;
2535 static void
2536 DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
2538 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
2540 if (aProgress == JS::GC_CYCLE_END) {
2541 PRTime delta = GetCollectionTimeDelta();
2543 if (sPostGCEventsToConsole) {
2544 NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) ");
2545 nsString prefix, gcstats;
2546 gcstats.Adopt(aDesc.formatMessage(aRt));
2547 prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
2548 double(delta) / PR_USEC_PER_SEC));
2549 nsString msg = prefix + gcstats;
2550 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2551 if (cs) {
2552 cs->LogStringMessage(msg.get());
2556 if (sPostGCEventsToObserver) {
2557 nsString json;
2558 json.Adopt(aDesc.formatJSON(aRt, PR_Now()));
2559 nsRefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(json);
2560 NS_DispatchToMainThread(notify);
2564 // Prevent cycle collections and shrinking during incremental GC.
2565 if (aProgress == JS::GC_CYCLE_BEGIN) {
2566 sCCLockedOut = true;
2567 nsJSContext::KillShrinkGCBuffersTimer();
2568 } else if (aProgress == JS::GC_CYCLE_END) {
2569 sCCLockedOut = false;
2572 // The GC has more work to do, so schedule another GC slice.
2573 if (aProgress == JS::GC_SLICE_END) {
2574 nsJSContext::KillInterSliceGCTimer();
2575 if (!sShuttingDown) {
2576 CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
2577 sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired,
2578 nullptr,
2579 NS_INTERSLICE_GC_DELAY,
2580 nsITimer::TYPE_ONE_SHOT);
2584 if (aProgress == JS::GC_CYCLE_END) {
2585 // May need to kill the inter-slice GC timer
2586 nsJSContext::KillInterSliceGCTimer();
2588 sCCollectedWaitingForGC = 0;
2589 sLikelyShortLivingObjectsNeedingGC = 0;
2590 sCleanupsSinceLastGC = 0;
2591 sNeedsFullCC = true;
2592 sHasRunGC = true;
2593 nsJSContext::MaybePokeCC();
2595 if (aDesc.isCompartment_) {
2596 if (!sFullGCTimer && !sShuttingDown) {
2597 CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
2598 JS::gcreason::Reason reason = JS::gcreason::FULL_GC_TIMER;
2599 sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
2600 reinterpret_cast<void *>(reason),
2601 NS_FULL_GC_DELAY,
2602 nsITimer::TYPE_ONE_SHOT);
2604 } else {
2605 nsJSContext::KillFullGCTimer();
2607 // Avoid shrinking during heavy activity, which is suggested by
2608 // compartment GC.
2609 nsJSContext::PokeShrinkGCBuffers();
2613 if ((aProgress == JS::GC_SLICE_END || aProgress == JS::GC_CYCLE_END) &&
2614 ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2615 nsCycleCollector_dispatchDeferredDeletion();
2618 if (sPrevGCSliceCallback)
2619 (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
2622 void
2623 nsJSContext::ReportPendingException()
2625 if (mIsInitialized) {
2626 nsJSUtils::ReportPendingException(mContext);
2630 void
2631 nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
2633 mWindowProxy = aWindowProxy;
2636 JSObject*
2637 nsJSContext::GetWindowProxy()
2639 JSObject* windowProxy = GetWindowProxyPreserveColor();
2640 if (windowProxy) {
2641 JS::ExposeObjectToActiveJS(windowProxy);
2644 return windowProxy;
2647 JSObject*
2648 nsJSContext::GetWindowProxyPreserveColor()
2650 return mWindowProxy;
2653 void
2654 nsJSContext::LikelyShortLivingObjectCreated()
2656 ++sLikelyShortLivingObjectsNeedingGC;
2659 void
2660 mozilla::dom::StartupJSEnvironment()
2662 // initialize all our statics, so that we can restart XPCOM
2663 sGCTimer = sFullGCTimer = sCCTimer = nullptr;
2664 sCCLockedOut = false;
2665 sCCLockedOutTime = 0;
2666 sLastCCEndTime = 0;
2667 sHasRunGC = false;
2668 sPendingLoadCount = 0;
2669 sLoadingInProgress = false;
2670 sCCollectedWaitingForGC = 0;
2671 sLikelyShortLivingObjectsNeedingGC = 0;
2672 sPostGCEventsToConsole = false;
2673 sNeedsFullCC = false;
2674 sNeedsGCAfterCC = false;
2675 gNameSpaceManager = nullptr;
2676 sRuntimeService = nullptr;
2677 sRuntime = nullptr;
2678 sIsInitialized = false;
2679 sDidShutdown = false;
2680 sShuttingDown = false;
2681 sContextCount = 0;
2682 sSecurityManager = nullptr;
2683 gCCStats.Clear();
2686 static int
2687 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
2689 bool reportAll = Preferences::GetBool(aPrefName, false);
2690 nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
2691 return 0;
2694 static int
2695 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
2697 int32_t highwatermark = Preferences::GetInt(aPrefName, 128);
2699 JS_SetGCParameter(sRuntime, JSGC_MAX_MALLOC_BYTES,
2700 highwatermark * 1024L * 1024L);
2701 return 0;
2704 static int
2705 SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
2707 int32_t pref = Preferences::GetInt(aPrefName, -1);
2708 // handle overflow and negative pref values
2709 uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024;
2710 JS_SetGCParameter(sRuntime, JSGC_MAX_BYTES, max);
2711 return 0;
2714 static int
2715 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
2717 bool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
2718 bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
2719 JSGCMode mode;
2720 if (enableIncrementalGC) {
2721 mode = JSGC_MODE_INCREMENTAL;
2722 } else if (enableCompartmentGC) {
2723 mode = JSGC_MODE_COMPARTMENT;
2724 } else {
2725 mode = JSGC_MODE_GLOBAL;
2727 JS_SetGCParameter(sRuntime, JSGC_MODE, mode);
2728 return 0;
2731 static int
2732 SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
2734 int32_t pref = Preferences::GetInt(aPrefName, -1);
2735 // handle overflow and negative pref values
2736 if (pref > 0 && pref < 100000)
2737 JS_SetGCParameter(sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
2738 return 0;
2741 static int
2742 SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure)
2744 int32_t pref = Preferences::GetInt(aPrefName, -1);
2745 // handle overflow and negative pref values
2746 if (pref >= 0 && pref < 10000)
2747 JS_SetGCParameter(sRuntime, (JSGCParamKey)(intptr_t)aClosure, pref);
2748 return 0;
2751 static int
2752 SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure)
2754 bool pref = Preferences::GetBool(aPrefName);
2755 JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref);
2756 return 0;
2759 static int
2760 SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
2762 bool pref = Preferences::GetBool(aPrefName);
2763 JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
2764 return 0;
2767 JSObject*
2768 NS_DOMReadStructuredClone(JSContext* cx,
2769 JSStructuredCloneReader* reader,
2770 uint32_t tag,
2771 uint32_t data,
2772 void* closure)
2774 if (tag == SCTAG_DOM_IMAGEDATA) {
2775 // Read the information out of the stream.
2776 uint32_t width, height;
2777 JS::Rooted<JS::Value> dataArray(cx);
2778 if (!JS_ReadUint32Pair(reader, &width, &height) ||
2779 !JS_ReadTypedArray(reader, dataArray.address())) {
2780 return nullptr;
2782 MOZ_ASSERT(dataArray.isObject());
2784 // Construct the ImageData.
2785 nsRefPtr<ImageData> imageData = new ImageData(width, height,
2786 dataArray.toObject());
2787 // Wrap it in a JS::Value.
2788 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
2789 if (!global) {
2790 return nullptr;
2792 return imageData->WrapObject(cx, global);
2795 // Don't know what this is. Bail.
2796 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2797 return nullptr;
2800 bool
2801 NS_DOMWriteStructuredClone(JSContext* cx,
2802 JSStructuredCloneWriter* writer,
2803 JS::Handle<JSObject*> obj,
2804 void *closure)
2806 ImageData* imageData;
2807 nsresult rv = UNWRAP_OBJECT(ImageData, obj, imageData);
2808 if (NS_FAILED(rv)) {
2809 // Don't know what this is. Bail.
2810 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2811 return false;
2814 // Prepare the ImageData internals.
2815 uint32_t width = imageData->Width();
2816 uint32_t height = imageData->Height();
2817 JS::Rooted<JSObject*> dataArray(cx, imageData->GetDataObject());
2819 // Write the internals to the stream.
2820 JSAutoCompartment ac(cx, dataArray);
2821 return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) &&
2822 JS_WriteUint32Pair(writer, width, height) &&
2823 JS_WriteTypedArray(writer, JS::ObjectValue(*dataArray));
2826 void
2827 NS_DOMStructuredCloneError(JSContext* cx,
2828 uint32_t errorid)
2830 // We don't currently support any extensions to structured cloning.
2831 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2834 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
2836 void
2837 nsJSContext::EnsureStatics()
2839 if (sIsInitialized) {
2840 if (!nsContentUtils::XPConnect()) {
2841 MOZ_CRASH();
2843 return;
2846 nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
2847 &sSecurityManager);
2848 if (NS_FAILED(rv)) {
2849 MOZ_CRASH();
2852 rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
2853 if (NS_FAILED(rv)) {
2854 MOZ_CRASH();
2857 rv = sRuntimeService->GetRuntime(&sRuntime);
2858 if (NS_FAILED(rv)) {
2859 MOZ_CRASH();
2862 // Let's make sure that our main thread is the same as the xpcom main thread.
2863 MOZ_ASSERT(NS_IsMainThread());
2865 sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
2867 // Set up the structured clone callbacks.
2868 static JSStructuredCloneCallbacks cloneCallbacks = {
2869 NS_DOMReadStructuredClone,
2870 NS_DOMWriteStructuredClone,
2871 NS_DOMStructuredCloneError
2873 JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
2875 static js::DOMCallbacks DOMcallbacks = {
2876 InstanceClassHasProtoAtDepth
2878 SetDOMCallbacks(sRuntime, &DOMcallbacks);
2880 // Set up the asm.js cache callbacks
2881 static JS::AsmJSCacheOps asmJSCacheOps = {
2882 asmjscache::OpenEntryForRead,
2883 asmjscache::CloseEntryForRead,
2884 asmjscache::OpenEntryForWrite,
2885 asmjscache::CloseEntryForWrite,
2886 asmjscache::GetBuildId
2888 JS::SetAsmJSCacheOps(sRuntime, &asmJSCacheOps);
2890 // Set these global xpconnect options...
2891 Preferences::RegisterCallbackAndCall(ReportAllJSExceptionsPrefChangedCallback,
2892 "dom.report_all_js_exceptions");
2894 Preferences::RegisterCallbackAndCall(SetMemoryHighWaterMarkPrefChangedCallback,
2895 "javascript.options.mem.high_water_mark");
2897 Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback,
2898 "javascript.options.mem.max");
2900 Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
2901 "javascript.options.mem.gc_per_compartment");
2903 Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
2904 "javascript.options.mem.gc_incremental");
2906 Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback,
2907 "javascript.options.mem.gc_incremental_slice_ms");
2909 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2910 "javascript.options.mem.gc_high_frequency_time_limit_ms",
2911 (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
2913 Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback,
2914 "javascript.options.mem.gc_dynamic_mark_slice");
2916 Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback,
2917 "javascript.options.mem.gc_dynamic_heap_growth");
2919 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2920 "javascript.options.mem.gc_low_frequency_heap_growth",
2921 (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
2923 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2924 "javascript.options.mem.gc_high_frequency_heap_growth_min",
2925 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
2927 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2928 "javascript.options.mem.gc_high_frequency_heap_growth_max",
2929 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
2931 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2932 "javascript.options.mem.gc_high_frequency_low_limit_mb",
2933 (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
2935 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2936 "javascript.options.mem.gc_high_frequency_high_limit_mb",
2937 (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
2939 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2940 "javascript.options.mem.gc_allocation_threshold_mb",
2941 (void *)JSGC_ALLOCATION_THRESHOLD);
2943 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2944 "javascript.options.mem.gc_decommit_threshold_mb",
2945 (void *)JSGC_DECOMMIT_THRESHOLD);
2947 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
2948 if (!obs) {
2949 MOZ_CRASH();
2952 Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
2953 "javascript.options.gc_on_memory_pressure",
2954 true);
2956 nsIObserver* observer = new nsJSEnvironmentObserver();
2957 obs->AddObserver(observer, "memory-pressure", false);
2958 obs->AddObserver(observer, "quit-application", false);
2960 // Bug 907848 - We need to explicitly get the nsIDOMScriptObjectFactory
2961 // service in order to force its constructor to run, which registers a
2962 // shutdown observer. It would be nice to make this more explicit and less
2963 // side-effect-y.
2964 nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
2965 if (!factory) {
2966 MOZ_CRASH();
2969 sIsInitialized = true;
2972 nsScriptNameSpaceManager*
2973 mozilla::dom::GetNameSpaceManager()
2975 if (sDidShutdown)
2976 return nullptr;
2978 if (!gNameSpaceManager) {
2979 gNameSpaceManager = new nsScriptNameSpaceManager;
2980 NS_ADDREF(gNameSpaceManager);
2982 nsresult rv = gNameSpaceManager->Init();
2983 NS_ENSURE_SUCCESS(rv, nullptr);
2986 return gNameSpaceManager;
2989 void
2990 mozilla::dom::ShutdownJSEnvironment()
2992 KillTimers();
2994 NS_IF_RELEASE(gNameSpaceManager);
2996 if (!sContextCount) {
2997 // We're being shutdown, and there are no more contexts
2998 // alive, release the JS runtime service and the security manager.
3000 NS_IF_RELEASE(sRuntimeService);
3001 NS_IF_RELEASE(sSecurityManager);
3004 sShuttingDown = true;
3005 sDidShutdown = true;
3008 // A fast-array class for JS. This class supports both nsIJSScriptArray and
3009 // nsIArray. If it is JS itself providing and consuming this class, all work
3010 // can be done via nsIJSScriptArray, and avoid the conversion of elements
3011 // to/from nsISupports.
3012 // When consumed by non-JS (eg, another script language), conversion is done
3013 // on-the-fly.
3014 class nsJSArgArray MOZ_FINAL : public nsIJSArgArray {
3015 public:
3016 nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
3017 nsresult *prv);
3018 ~nsJSArgArray();
3019 // nsISupports
3020 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3021 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
3022 nsIJSArgArray)
3024 // nsIArray
3025 NS_DECL_NSIARRAY
3027 // nsIJSArgArray
3028 nsresult GetArgs(uint32_t *argc, void **argv);
3030 void ReleaseJSObjects();
3032 protected:
3033 JSContext *mContext;
3034 JS::Heap<JS::Value> *mArgv;
3035 uint32_t mArgc;
3038 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
3039 nsresult *prv) :
3040 mContext(aContext),
3041 mArgv(nullptr),
3042 mArgc(argc)
3044 // copy the array - we don't know its lifetime, and ours is tied to xpcom
3045 // refcounting.
3046 if (argc) {
3047 static const fallible_t fallible = fallible_t();
3048 mArgv = new (fallible) JS::Heap<JS::Value>[argc];
3049 if (!mArgv) {
3050 *prv = NS_ERROR_OUT_OF_MEMORY;
3051 return;
3055 // Callers are allowed to pass in a null argv even for argc > 0. They can
3056 // then use GetArgs to initialize the values.
3057 if (argv) {
3058 for (uint32_t i = 0; i < argc; ++i)
3059 mArgv[i] = argv[i];
3062 if (argc > 0) {
3063 mozilla::HoldJSObjects(this);
3066 *prv = NS_OK;
3069 nsJSArgArray::~nsJSArgArray()
3071 ReleaseJSObjects();
3074 void
3075 nsJSArgArray::ReleaseJSObjects()
3077 if (mArgv) {
3078 delete [] mArgv;
3080 if (mArgc > 0) {
3081 mArgc = 0;
3082 mozilla::DropJSObjects(this);
3086 // QueryInterface implementation for nsJSArgArray
3087 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
3089 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
3090 tmp->ReleaseJSObjects();
3091 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3092 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
3093 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
3094 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3096 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
3097 if (tmp->mArgv) {
3098 for (uint32_t i = 0; i < tmp->mArgc; ++i) {
3099 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mArgv[i])
3102 NS_IMPL_CYCLE_COLLECTION_TRACE_END
3104 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
3105 NS_INTERFACE_MAP_ENTRY(nsIArray)
3106 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
3107 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
3108 NS_INTERFACE_MAP_END
3110 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
3111 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
3113 nsresult
3114 nsJSArgArray::GetArgs(uint32_t *argc, void **argv)
3116 *argv = (void *)mArgv;
3117 *argc = mArgc;
3118 return NS_OK;
3121 // nsIArray impl
3122 NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t *aLength)
3124 *aLength = mArgc;
3125 return NS_OK;
3128 /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
3129 NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID & uuid, void * *result)
3131 *result = nullptr;
3132 if (index >= mArgc)
3133 return NS_ERROR_INVALID_ARG;
3135 if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
3136 return nsContentUtils::XPConnect()->JSToVariant(mContext, mArgv[index],
3137 (nsIVariant **)result);
3139 NS_WARNING("nsJSArgArray only handles nsIVariant");
3140 return NS_ERROR_NO_INTERFACE;
3143 /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
3144 NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval)
3146 return NS_ERROR_NOT_IMPLEMENTED;
3149 /* nsISimpleEnumerator enumerate (); */
3150 NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
3152 return NS_ERROR_NOT_IMPLEMENTED;
3155 // The factory function
3156 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc, void *argv,
3157 nsIJSArgArray **aArray)
3159 nsresult rv;
3160 nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
3161 static_cast<JS::Value *>(argv), &rv);
3162 if (ret == nullptr)
3163 return NS_ERROR_OUT_OF_MEMORY;
3164 if (NS_FAILED(rv)) {
3165 delete ret;
3166 return rv;
3168 return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);