Backed out changeset 9157ea42ff41 (bug 914925) for Android/B2G test bustage.
[gecko.git] / dom / base / nsJSEnvironment.cpp
blob9611e3728ac19f4a96978a612bd27943c5a80ead
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 "nsGUIEvent.h"
32 #include "nsITimer.h"
33 #include "nsIAtom.h"
34 #include "nsContentUtils.h"
35 #include "nsCxPusher.h"
36 #include "nsEventDispatcher.h"
37 #include "nsIContent.h"
38 #include "nsCycleCollector.h"
39 #include "nsNetUtil.h"
40 #include "nsXPCOMCIDInternal.h"
41 #include "nsIXULRuntime.h"
42 #include "nsTextFormatter.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/dom/ImageData.h"
57 #include "mozilla/dom/ImageDataBinding.h"
58 #include "nsAXPCNativeCallContext.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/CanvasRenderingContext2DBinding.h"
83 #include "mozilla/CycleCollectedJSRuntime.h"
85 #include "nsCycleCollectionNoteRootCallback.h"
86 #include "GeckoProfiler.h"
88 using namespace mozilla;
89 using namespace mozilla::dom;
91 const size_t gStackSize = 8192;
93 #ifdef PR_LOGGING
94 static PRLogModuleInfo* gJSDiagnostics;
95 #endif
97 // Thank you Microsoft!
98 #ifdef CompareString
99 #undef CompareString
100 #endif
102 #define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms
104 // The amount of time we wait from the first request to GC to actually
105 // doing the first GC.
106 #define NS_FIRST_GC_DELAY 10000 // ms
108 #define NS_FULL_GC_DELAY 60000 // ms
110 // Maximum amount of time that should elapse between incremental GC slices
111 #define NS_INTERSLICE_GC_DELAY 100 // ms
113 // If we haven't painted in 100ms, we allow for a longer GC budget
114 #define NS_INTERSLICE_GC_BUDGET 40 // ms
116 // The amount of time we wait between a request to CC (after GC ran)
117 // and doing the actual CC.
118 #define NS_CC_DELAY 6000 // ms
120 #define NS_CC_SKIPPABLE_DELAY 400 // ms
122 // Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT
123 // objects in the purple buffer.
124 #define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min
125 #define NS_CC_FORCED_PURPLE_LIMIT 10
127 // Don't allow an incremental GC to lock out the CC for too long.
128 #define NS_MAX_CC_LOCKEDOUT_TIME (15 * PR_USEC_PER_SEC) // 15 seconds
130 // Trigger a CC if the purple buffer exceeds this size when we check it.
131 #define NS_CC_PURPLE_LIMIT 200
133 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
135 // Large value used to specify that a script should run essentially forever
136 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
138 #define NS_MAJOR_FORGET_SKIPPABLE_CALLS 2
140 // if you add statics here, add them to the list in StartupJSEnvironment
142 static nsITimer *sGCTimer;
143 static nsITimer *sShrinkGCBuffersTimer;
144 static nsITimer *sCCTimer;
145 static nsITimer *sFullGCTimer;
146 static nsITimer *sInterSliceGCTimer;
148 static PRTime sLastCCEndTime;
150 static bool sCCLockedOut;
151 static PRTime sCCLockedOutTime;
153 static JS::GCSliceCallback sPrevGCSliceCallback;
155 static bool sHasRunGC;
157 // The number of currently pending document loads. This count isn't
158 // guaranteed to always reflect reality and can't easily as we don't
159 // have an easy place to know when a load ends or is interrupted in
160 // all cases. This counter also gets reset if we end up GC'ing while
161 // we're waiting for a slow page to load. IOW, this count may be 0
162 // even when there are pending loads.
163 static uint32_t sPendingLoadCount;
164 static bool sLoadingInProgress;
166 static uint32_t sCCollectedWaitingForGC;
167 static uint32_t sLikelyShortLivingObjectsNeedingGC;
168 static bool sPostGCEventsToConsole;
169 static bool sPostGCEventsToObserver;
170 static uint32_t sCCTimerFireCount = 0;
171 static uint32_t sMinForgetSkippableTime = UINT32_MAX;
172 static uint32_t sMaxForgetSkippableTime = 0;
173 static uint32_t sTotalForgetSkippableTime = 0;
174 static uint32_t sRemovedPurples = 0;
175 static uint32_t sForgetSkippableBeforeCC = 0;
176 static uint32_t sPreviousSuspectedCount = 0;
177 static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
178 static bool sNeedsFullCC = false;
179 static bool sNeedsGCAfterCC = false;
180 static nsJSContext *sContextList = nullptr;
182 static nsScriptNameSpaceManager *gNameSpaceManager;
184 static nsIJSRuntimeService *sRuntimeService;
186 static const char kJSRuntimeServiceContractID[] =
187 "@mozilla.org/js/xpc/RuntimeService;1";
189 static PRTime sFirstCollectionTime;
191 static JSRuntime *sRuntime;
193 static bool sIsInitialized;
194 static bool sDidShutdown;
195 static bool sShuttingDown;
196 static int32_t sContextCount;
198 static nsIScriptSecurityManager *sSecurityManager;
200 // nsJSEnvironmentObserver observes the memory-pressure notifications
201 // and forces a garbage collection and cycle collection when it happens, if
202 // the appropriate pref is set.
204 static bool sGCOnMemoryPressure;
206 static PRTime
207 GetCollectionTimeDelta()
209 PRTime now = PR_Now();
210 if (sFirstCollectionTime) {
211 return now - sFirstCollectionTime;
213 sFirstCollectionTime = now;
214 return 0;
217 static void
218 KillTimers()
220 nsJSContext::KillGCTimer();
221 nsJSContext::KillShrinkGCBuffersTimer();
222 nsJSContext::KillCCTimer();
223 nsJSContext::KillFullGCTimer();
224 nsJSContext::KillInterSliceGCTimer();
227 class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver
229 public:
230 NS_DECL_ISUPPORTS
231 NS_DECL_NSIOBSERVER
234 NS_IMPL_ISUPPORTS1(nsJSEnvironmentObserver, nsIObserver)
236 NS_IMETHODIMP
237 nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
238 const PRUnichar* aData)
240 if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) {
241 if(StringBeginsWith(nsDependentString(aData),
242 NS_LITERAL_STRING("low-memory-ongoing"))) {
243 // Don't GC/CC if we are in an ongoing low-memory state since its very
244 // slow and it likely won't help us anyway.
245 return NS_OK;
247 nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
248 nsJSContext::NonIncrementalGC,
249 nsJSContext::NonCompartmentGC,
250 nsJSContext::ShrinkingGC);
251 nsJSContext::CycleCollectNow();
252 } else if (!nsCRT::strcmp(aTopic, "quit-application")) {
253 sShuttingDown = true;
254 KillTimers();
257 return NS_OK;
260 class nsRootedJSValueArray {
261 public:
262 explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {}
264 void SetCapacity(JSContext *cx, size_t capacity) {
265 vals.SetCapacity(capacity);
266 // Values must be safe for the GC to inspect (they must not contain garbage).
267 memset(vals.Elements(), 0, vals.Capacity() * sizeof(JS::Value));
268 resetRooter(cx);
271 JS::Value *Elements() {
272 return vals.Elements();
275 private:
276 void resetRooter(JSContext *cx) {
277 avr.changeArray(vals.Elements(), vals.Length());
280 nsAutoTArray<JS::Value, 16> vals;
281 JS::AutoArrayRooter avr;
284 /****************************************************************
285 ************************** AutoFree ****************************
286 ****************************************************************/
288 class AutoFree {
289 public:
290 AutoFree(void *aPtr) : mPtr(aPtr) {
292 ~AutoFree() {
293 if (mPtr)
294 nsMemory::Free(mPtr);
296 void Invalidate() {
297 mPtr = 0;
299 private:
300 void *mPtr;
303 // A utility function for script languages to call. Although it looks small,
304 // the use of nsIDocShell and nsPresContext triggers a huge number of
305 // dependencies that most languages would not otherwise need.
306 // XXXmarkh - This function is mis-placed!
307 bool
308 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
309 nsScriptErrorEvent *aErrorEvent,
310 nsEventStatus *aStatus)
312 bool called = false;
313 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
314 nsIDocShell *docShell = win ? win->GetDocShell() : nullptr;
315 if (docShell) {
316 nsRefPtr<nsPresContext> presContext;
317 docShell->GetPresContext(getter_AddRefs(presContext));
319 static int32_t errorDepth; // Recursion prevention
320 ++errorDepth;
322 if (presContext && errorDepth < 2) {
323 // Dispatch() must be synchronous for the recursion block
324 // (errorDepth) to work.
325 nsEventDispatcher::Dispatch(win, presContext, aErrorEvent, nullptr,
326 aStatus);
327 called = true;
329 --errorDepth;
331 return called;
334 namespace mozilla {
335 namespace dom {
337 AsyncErrorReporter::AsyncErrorReporter(JSRuntime* aRuntime,
338 JSErrorReport* aErrorReport,
339 const char* aFallbackMessage,
340 nsIPrincipal* aGlobalPrincipal,
341 nsPIDOMWindow* aWindow)
342 : mSourceLine(static_cast<const PRUnichar*>(aErrorReport->uclinebuf))
343 , mLineNumber(aErrorReport->lineno)
344 , mColumn(aErrorReport->uctokenptr - aErrorReport->uclinebuf)
345 , mFlags(aErrorReport->flags)
347 if (!aErrorReport->filename) {
348 mFileName.SetIsVoid(true);
349 } else {
350 mFileName.AssignWithConversion(aErrorReport->filename);
353 const PRUnichar* m = static_cast<const PRUnichar*>(aErrorReport->ucmessage);
354 if (m) {
355 const PRUnichar* n = static_cast<const PRUnichar*>
356 (js::GetErrorTypeName(aRuntime, aErrorReport->exnType));
357 if (n) {
358 mErrorMsg.Assign(n);
359 mErrorMsg.AppendLiteral(": ");
361 mErrorMsg.Append(m);
364 if (mErrorMsg.IsEmpty() && aFallbackMessage) {
365 mErrorMsg.AssignWithConversion(aFallbackMessage);
368 mCategory = nsContentUtils::IsSystemPrincipal(aGlobalPrincipal) ?
369 NS_LITERAL_CSTRING("chrome javascript") :
370 NS_LITERAL_CSTRING("content javascript");
372 mInnerWindowID = 0;
373 if (aWindow && aWindow->IsOuterWindow()) {
374 aWindow = aWindow->GetCurrentInnerWindow();
376 if (aWindow) {
377 mInnerWindowID = aWindow->WindowID();
381 void
382 AsyncErrorReporter::ReportError()
384 nsCOMPtr<nsIScriptError> errorObject =
385 do_CreateInstance("@mozilla.org/scripterror;1");
386 if (!errorObject) {
387 return;
390 nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName,
391 mSourceLine, mLineNumber,
392 mColumn, mFlags, mCategory,
393 mInnerWindowID);
394 if (NS_FAILED(rv)) {
395 return;
398 nsCOMPtr<nsIConsoleService> consoleService =
399 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
400 if (!consoleService) {
401 return;
404 consoleService->LogMessage(errorObject);
405 return;
408 } // namespace dom
409 } // namespace mozilla
411 class ScriptErrorEvent : public AsyncErrorReporter
413 public:
414 ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
415 JSRuntime* aRuntime,
416 JSErrorReport* aErrorReport,
417 const char* aFallbackMessage,
418 nsIPrincipal* aScriptOriginPrincipal,
419 nsIPrincipal* aGlobalPrincipal,
420 nsPIDOMWindow* aWindow,
421 bool aDispatchEvent)
422 // Pass an empty category, then compute ours
423 : AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage,
424 aGlobalPrincipal, aWindow)
425 , mScriptGlobal(aScriptGlobal)
426 , mOriginPrincipal(aScriptOriginPrincipal)
427 , mDispatchEvent(aDispatchEvent)
431 NS_IMETHOD Run()
433 nsEventStatus status = nsEventStatus_eIgnore;
434 // First, notify the DOM that we have a script error.
435 if (mDispatchEvent) {
436 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
437 nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
438 if (docShell &&
439 !JSREPORT_IS_WARNING(mFlags) &&
440 !sHandlingScriptError) {
441 sHandlingScriptError = true; // Recursion prevention
443 nsRefPtr<nsPresContext> presContext;
444 docShell->GetPresContext(getter_AddRefs(presContext));
446 if (presContext) {
447 nsScriptErrorEvent errorevent(true, NS_LOAD_ERROR);
449 errorevent.fileName = mFileName.get();
451 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
452 NS_ENSURE_STATE(sop);
453 nsIPrincipal* p = sop->GetPrincipal();
454 NS_ENSURE_STATE(p);
456 bool sameOrigin = !mOriginPrincipal;
458 if (p && !sameOrigin) {
459 if (NS_FAILED(p->Subsumes(mOriginPrincipal, &sameOrigin))) {
460 sameOrigin = false;
464 NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
465 if (sameOrigin) {
466 errorevent.errorMsg = mErrorMsg.get();
467 errorevent.lineNr = mLineNumber;
468 } else {
469 NS_WARNING("Not same origin error!");
470 errorevent.errorMsg = xoriginMsg.get();
471 errorevent.lineNr = 0;
474 nsEventDispatcher::Dispatch(win, presContext, &errorevent, nullptr,
475 &status);
478 sHandlingScriptError = false;
482 if (status != nsEventStatus_eConsumeNoDefault) {
483 AsyncErrorReporter::ReportError();
486 return NS_OK;
489 private:
490 nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
491 nsCOMPtr<nsIPrincipal> mOriginPrincipal;
492 bool mDispatchEvent;
494 static bool sHandlingScriptError;
497 bool ScriptErrorEvent::sHandlingScriptError = false;
499 // NOTE: This function could be refactored to use the above. The only reason
500 // it has not been done is that the code below only fills the error event
501 // after it has a good nsPresContext - whereas using the above function
502 // would involve always filling it. Is that a concern?
503 void
504 NS_ScriptErrorReporter(JSContext *cx,
505 const char *message,
506 JSErrorReport *report)
508 // We don't want to report exceptions too eagerly, but warnings in the
509 // absence of werror are swallowed whole, so report those now.
510 if (!JSREPORT_IS_WARNING(report->flags)) {
511 nsIXPConnect* xpc = nsContentUtils::XPConnect();
512 if (JS_DescribeScriptedCaller(cx, nullptr, nullptr)) {
513 xpc->MarkErrorUnreported(cx);
514 return;
517 if (xpc) {
518 nsAXPCNativeCallContext *cc = nullptr;
519 xpc->GetCurrentNativeCallContext(&cc);
520 if (cc) {
521 nsAXPCNativeCallContext *prev = cc;
522 while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
523 uint16_t lang;
524 if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
525 lang == nsAXPCNativeCallContext::LANG_JS) {
526 xpc->MarkErrorUnreported(cx);
527 return;
534 // XXX this means we are not going to get error reports on non DOM contexts
535 nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx);
537 // Note: we must do this before running any more code on cx (if cx is the
538 // dynamic script context).
539 ::JS_ClearPendingException(cx);
541 if (context) {
542 nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
544 if (globalObject) {
546 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
547 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
548 do_QueryInterface(globalObject);
549 NS_ASSERTION(scriptPrincipal, "Global objects must implement "
550 "nsIScriptObjectPrincipal");
551 nsContentUtils::AddScriptRunner(
552 new ScriptErrorEvent(globalObject,
553 JS_GetRuntime(cx),
554 report,
555 message,
556 nsJSPrincipals::get(report->originPrincipals),
557 scriptPrincipal->GetPrincipal(),
558 win,
559 /* We do not try to report Out Of Memory via a dom
560 * event because the dom event handler would
561 * encounter an OOM exception trying to process the
562 * event, and then we'd need to generate a new OOM
563 * event for that new OOM instance -- this isn't
564 * pretty.
566 report->errorNumber != JSMSG_OUT_OF_MEMORY));
570 if (nsContentUtils::DOMWindowDumpEnabled()) {
571 // Print it to stderr as well, for the benefit of those invoking
572 // mozilla with -console.
573 nsAutoCString error;
574 error.Assign("JavaScript ");
575 if (JSREPORT_IS_STRICT(report->flags))
576 error.Append("strict ");
577 if (JSREPORT_IS_WARNING(report->flags))
578 error.Append("warning: ");
579 else
580 error.Append("error: ");
581 error.Append(report->filename);
582 error.Append(", line ");
583 error.AppendInt(report->lineno, 10);
584 error.Append(": ");
585 if (report->ucmessage) {
586 AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(report->ucmessage),
587 error);
588 } else {
589 error.Append(message);
592 fprintf(stderr, "%s\n", error.get());
593 fflush(stderr);
596 #ifdef PR_LOGGING
597 if (!gJSDiagnostics)
598 gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
600 if (gJSDiagnostics) {
601 PR_LOG(gJSDiagnostics,
602 JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
603 ("file %s, line %u: %s\n%s%s",
604 report->filename, report->lineno, message,
605 report->linebuf ? report->linebuf : "",
606 (report->linebuf &&
607 report->linebuf[strlen(report->linebuf)-1] != '\n')
608 ? "\n"
609 : ""));
611 #endif
614 #ifdef DEBUG
615 // A couple of useful functions to call when you're debugging.
616 nsGlobalWindow *
617 JSObject2Win(JSContext *cx, JSObject *obj)
619 nsIXPConnect *xpc = nsContentUtils::XPConnect();
620 if (!xpc) {
621 return nullptr;
624 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
625 xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
626 if (wrapper) {
627 nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
628 if (win) {
629 return static_cast<nsGlobalWindow *>
630 (static_cast<nsPIDOMWindow *>(win));
634 return nullptr;
637 void
638 PrintWinURI(nsGlobalWindow *win)
640 if (!win) {
641 printf("No window passed in.\n");
642 return;
645 nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
646 if (!doc) {
647 printf("No document in the window.\n");
648 return;
651 nsIURI *uri = doc->GetDocumentURI();
652 if (!uri) {
653 printf("Document doesn't have a URI.\n");
654 return;
657 nsAutoCString spec;
658 uri->GetSpec(spec);
659 printf("%s\n", spec.get());
662 void
663 PrintWinCodebase(nsGlobalWindow *win)
665 if (!win) {
666 printf("No window passed in.\n");
667 return;
670 nsIPrincipal *prin = win->GetPrincipal();
671 if (!prin) {
672 printf("Window doesn't have principals.\n");
673 return;
676 nsCOMPtr<nsIURI> uri;
677 prin->GetURI(getter_AddRefs(uri));
678 if (!uri) {
679 printf("No URI, maybe the system principal.\n");
680 return;
683 nsAutoCString spec;
684 uri->GetSpec(spec);
685 printf("%s\n", spec.get());
688 void
689 DumpString(const nsAString &str)
691 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
693 #endif
695 #define JS_OPTIONS_DOT_STR "javascript.options."
697 static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
698 static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
699 #ifdef DEBUG
700 static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
701 #endif
702 static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
703 #ifdef JS_GC_ZEAL
704 static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
705 static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
706 #endif
707 static const char js_typeinfer_content_str[] = JS_OPTIONS_DOT_STR "typeinference.content";
708 static const char js_typeinfer_chrome_str[] = JS_OPTIONS_DOT_STR "typeinference.chrome";
709 static const char js_jit_hardening_str[] = JS_OPTIONS_DOT_STR "jit_hardening";
710 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
711 static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify";
712 static const char js_asmjs_content_str[] = JS_OPTIONS_DOT_STR "asmjs";
713 static const char js_baselinejit_content_str[] = JS_OPTIONS_DOT_STR "baselinejit.content";
714 static const char js_baselinejit_chrome_str[] = JS_OPTIONS_DOT_STR "baselinejit.chrome";
715 static const char js_baselinejit_eager_str[] = JS_OPTIONS_DOT_STR "baselinejit.unsafe_eager_compilation";
716 static const char js_ion_content_str[] = JS_OPTIONS_DOT_STR "ion.content";
717 static const char js_ion_chrome_str[] = JS_OPTIONS_DOT_STR "ion.chrome";
718 static const char js_ion_eager_str[] = JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation";
719 static const char js_parallel_parsing_str[] = JS_OPTIONS_DOT_STR "parallel_parsing";
720 static const char js_ion_parallel_compilation_str[] = JS_OPTIONS_DOT_STR "ion.parallel_compilation";
723 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
725 nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
726 uint32_t oldDefaultJSOptions = context->mDefaultJSOptions;
727 uint32_t newDefaultJSOptions = oldDefaultJSOptions;
729 sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
730 sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
732 bool strict = Preferences::GetBool(js_strict_option_str);
733 if (strict)
734 newDefaultJSOptions |= JSOPTION_EXTRA_WARNINGS;
735 else
736 newDefaultJSOptions &= ~JSOPTION_EXTRA_WARNINGS;
738 // The vanilla GetGlobalObject returns null if a global isn't set up on
739 // the context yet. We can sometimes be call midway through context init,
740 // So ask for the member directly instead.
741 nsIScriptGlobalObject *global = context->GetGlobalObjectRef();
743 // XXX should we check for sysprin instead of a chrome window, to make
744 // XXX components be covered by the chrome pref instead of the content one?
745 nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global));
746 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
748 bool useTypeInference = Preferences::GetBool((chromeWindow || !contentWindow) ?
749 js_typeinfer_chrome_str :
750 js_typeinfer_content_str);
751 bool useHardening = Preferences::GetBool(js_jit_hardening_str);
752 bool useBaselineJIT = Preferences::GetBool((chromeWindow || !contentWindow) ?
753 js_baselinejit_chrome_str :
754 js_baselinejit_content_str);
755 bool useBaselineJITEager = Preferences::GetBool(js_baselinejit_eager_str);
756 bool useIon = Preferences::GetBool((chromeWindow || !contentWindow) ?
757 js_ion_chrome_str :
758 js_ion_content_str);
759 bool useIonEager = Preferences::GetBool(js_ion_eager_str);
760 bool useAsmJS = Preferences::GetBool(js_asmjs_content_str);
761 bool parallelParsing = Preferences::GetBool(js_parallel_parsing_str);
762 bool parallelIonCompilation = Preferences::GetBool(js_ion_parallel_compilation_str);
763 nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
764 if (xr) {
765 bool safeMode = false;
766 xr->GetInSafeMode(&safeMode);
767 if (safeMode) {
768 useTypeInference = false;
769 useHardening = false;
770 useBaselineJIT = false;
771 useBaselineJITEager = false;
772 useIon = false;
773 useIonEager = false;
774 useAsmJS = false;
778 if (useTypeInference)
779 newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
780 else
781 newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
783 if (useBaselineJIT)
784 newDefaultJSOptions |= JSOPTION_BASELINE;
785 else
786 newDefaultJSOptions &= ~JSOPTION_BASELINE;
788 if (useIon)
789 newDefaultJSOptions |= JSOPTION_ION;
790 else
791 newDefaultJSOptions &= ~JSOPTION_ION;
793 if (useAsmJS)
794 newDefaultJSOptions |= JSOPTION_ASMJS;
795 else
796 newDefaultJSOptions &= ~JSOPTION_ASMJS;
798 #ifdef DEBUG
799 // In debug builds, warnings are enabled in chrome context if
800 // javascript.options.strict.debug is true
801 bool strictDebug = Preferences::GetBool(js_strict_debug_option_str);
802 if (strictDebug && (newDefaultJSOptions & JSOPTION_EXTRA_WARNINGS) == 0) {
803 if (chromeWindow || !contentWindow)
804 newDefaultJSOptions |= JSOPTION_EXTRA_WARNINGS;
806 #endif
808 bool werror = Preferences::GetBool(js_werror_option_str);
809 if (werror)
810 newDefaultJSOptions |= JSOPTION_WERROR;
811 else
812 newDefaultJSOptions &= ~JSOPTION_WERROR;
814 ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSOPTION_MASK);
816 ::JS_SetParallelParsingEnabled(context->mContext, parallelParsing);
817 ::JS_SetParallelIonCompilationEnabled(context->mContext, parallelIonCompilation);
819 ::JS_SetGlobalJitCompilerOption(context->mContext, JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER,
820 (useBaselineJITEager ? 0 : -1));
822 ::JS_SetGlobalJitCompilerOption(context->mContext, JSJITCOMPILER_ION_USECOUNT_TRIGGER,
823 (useIonEager ? 0 : -1));
825 // Save the new defaults for the next page load (InitContext).
826 context->mDefaultJSOptions = newDefaultJSOptions;
828 JSRuntime *rt = JS_GetRuntime(context->mContext);
829 JS_SetJitHardening(rt, useHardening);
831 #ifdef JS_GC_ZEAL
832 int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
833 int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
834 if (zeal >= 0)
835 ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
836 #endif
838 return 0;
841 nsJSContext::nsJSContext(bool aGCOnDestruction,
842 nsIScriptGlobalObject* aGlobalObject)
843 : mWindowProxy(nullptr)
844 , mGCOnDestruction(aGCOnDestruction)
845 , mGlobalObjectRef(aGlobalObject)
847 EnsureStatics();
849 mNext = sContextList;
850 mPrev = &sContextList;
851 if (sContextList) {
852 sContextList->mPrev = &mNext;
854 sContextList = this;
856 ++sContextCount;
858 mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS |
859 JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT;
861 mContext = ::JS_NewContext(sRuntime, gStackSize);
862 if (mContext) {
863 ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
865 // Preserve any flags the context callback might have set.
866 mDefaultJSOptions |= ::JS_GetOptions(mContext);
868 // Make sure the new context gets the default context options
869 ::JS_SetOptions(mContext, mDefaultJSOptions);
871 // Watch for the JS boolean options
872 Preferences::RegisterCallback(JSOptionChangedCallback,
873 js_options_dot_str, this);
875 mIsInitialized = false;
876 mScriptsEnabled = true;
877 mProcessingScriptTag = false;
878 HoldJSObjects(this);
881 nsJSContext::~nsJSContext()
883 *mPrev = mNext;
884 if (mNext) {
885 mNext->mPrev = mPrev;
888 mGlobalObjectRef = nullptr;
890 DestroyJSContext();
892 --sContextCount;
894 if (!sContextCount && sDidShutdown) {
895 // The last context is being deleted, and we're already in the
896 // process of shutting down, release the JS runtime service, and
897 // the security manager.
899 NS_IF_RELEASE(sRuntimeService);
900 NS_IF_RELEASE(sSecurityManager);
904 // This function is called either by the destructor or unlink, which means that
905 // it can never be called when there is an outstanding ref to the
906 // nsIScriptContext on the stack. Our stack-scoped cx pushers hold such a ref,
907 // so we can assume here that mContext is not on the stack (and therefore not
908 // in use).
909 void
910 nsJSContext::DestroyJSContext()
912 if (!mContext) {
913 return;
916 // Clear our entry in the JSContext, bugzilla bug 66413
917 ::JS_SetContextPrivate(mContext, nullptr);
919 // Unregister our "javascript.options.*" pref-changed callback.
920 Preferences::UnregisterCallback(JSOptionChangedCallback,
921 js_options_dot_str, this);
923 if (mGCOnDestruction) {
924 PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
927 JS_DestroyContextNoGC(mContext);
928 mContext = nullptr;
929 DropJSObjects(this);
932 // QueryInterface implementation for nsJSContext
933 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
935 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
936 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy)
937 NS_IMPL_CYCLE_COLLECTION_TRACE_END
939 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
940 NS_ASSERTION(!tmp->mContext || !js::ContextHasOutstandingRequests(tmp->mContext),
941 "Trying to unlink a context with outstanding requests.");
942 tmp->mIsInitialized = false;
943 tmp->mGCOnDestruction = false;
944 tmp->mWindowProxy = nullptr;
945 tmp->DestroyJSContext();
946 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
947 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
948 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
949 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
950 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
951 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
952 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
954 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
955 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
956 NS_INTERFACE_MAP_ENTRY(nsISupports)
957 NS_INTERFACE_MAP_END
960 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
961 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
963 nsrefcnt
964 nsJSContext::GetCCRefcnt()
966 nsrefcnt refcnt = mRefCnt.get();
968 // In the (abnormal) case of synchronous cycle-collection, the context may be
969 // actively running JS code in which case we must keep it alive by adding an
970 // extra refcount.
971 if (mContext && js::ContextHasOutstandingRequests(mContext)) {
972 refcnt++;
975 return refcnt;
978 nsresult
979 nsJSContext::EvaluateString(const nsAString& aScript,
980 JS::Handle<JSObject*> aScopeObject,
981 JS::CompileOptions& aCompileOptions,
982 bool aCoerceToString,
983 JS::Value* aRetValue,
984 void **aOffThreadToken)
986 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
987 if (!mScriptsEnabled) {
988 return NS_OK;
991 AutoCxPusher pusher(mContext);
992 nsJSUtils::EvaluateOptions evalOptions;
993 evalOptions.setCoerceToString(aCoerceToString);
994 return nsJSUtils::EvaluateString(mContext, aScript, aScopeObject,
995 aCompileOptions, evalOptions, aRetValue,
996 aOffThreadToken);
999 #ifdef DEBUG
1000 bool
1001 AtomIsEventHandlerName(nsIAtom *aName)
1003 const PRUnichar *name = aName->GetUTF16String();
1005 const PRUnichar *cp;
1006 PRUnichar c;
1007 for (cp = name; *cp != '\0'; ++cp)
1009 c = *cp;
1010 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
1011 return false;
1014 return true;
1016 #endif
1018 // Helper function to find the JSObject associated with a (presumably DOM)
1019 // interface.
1020 nsresult
1021 nsJSContext::JSObjectFromInterface(nsISupports* aTarget,
1022 JS::Handle<JSObject*> aScope,
1023 JSObject** aRet)
1025 // It is legal to specify a null target.
1026 if (!aTarget) {
1027 *aRet = nullptr;
1028 return NS_OK;
1031 AutoJSContext cx;
1033 // Get the jsobject associated with this target
1034 // We don't wrap here because we trust the JS engine to wrap the target
1035 // later.
1036 JS::Rooted<JS::Value> v(cx);
1037 nsresult rv = nsContentUtils::WrapNative(cx, aScope, aTarget,
1038 v.address());
1039 NS_ENSURE_SUCCESS(rv, rv);
1041 #ifdef DEBUG
1042 nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
1043 nsCOMPtr<nsISupports> native =
1044 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
1045 JSVAL_TO_OBJECT(v));
1046 NS_ASSERTION(native == targetSupp, "Native should be the target!");
1047 #endif
1049 JSObject* obj = v.toObjectOrNull();
1050 if (obj) {
1051 JS::ExposeObjectToActiveJS(obj);
1054 *aRet = obj;
1055 return NS_OK;
1058 nsresult
1059 nsJSContext::BindCompiledEventHandler(nsISupports* aTarget,
1060 JS::Handle<JSObject*> aScope,
1061 JS::Handle<JSObject*> aHandler,
1062 JS::MutableHandle<JSObject*> aBoundHandler)
1064 NS_ENSURE_ARG(aHandler);
1065 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1066 NS_PRECONDITION(!aBoundHandler, "Shouldn't already have a bound handler!");
1068 if (aScope) {
1069 JS::ExposeObjectToActiveJS(aScope);
1071 JS::ExposeObjectToActiveJS(aHandler);
1072 AutoPushJSContext cx(mContext);
1074 // Get the jsobject associated with this target
1075 JS::Rooted<JSObject*> target(cx);
1076 JS::Rooted<JSObject*> scope(cx, aScope);
1077 nsresult rv = JSObjectFromInterface(aTarget, scope, target.address());
1078 NS_ENSURE_SUCCESS(rv, rv);
1080 #ifdef DEBUG
1082 JSAutoCompartment ac(cx, aHandler);
1083 NS_ASSERTION(JS_TypeOfValue(cx,
1084 OBJECT_TO_JSVAL(aHandler)) == JSTYPE_FUNCTION,
1085 "Event handler object not a function");
1087 #endif
1089 JSAutoCompartment ac(cx, target);
1091 JSObject* funobj;
1092 // Make sure the handler function is parented by its event target object
1093 if (aHandler) {
1094 funobj = JS_CloneFunctionObject(cx, aHandler, target);
1095 if (!funobj) {
1096 rv = NS_ERROR_OUT_OF_MEMORY;
1098 } else {
1099 funobj = NULL;
1102 aBoundHandler.set(funobj);
1104 return rv;
1107 nsIScriptGlobalObject *
1108 nsJSContext::GetGlobalObject()
1110 AutoJSContext cx;
1111 JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
1112 if (!global) {
1113 return nullptr;
1116 if (mGlobalObjectRef)
1117 return mGlobalObjectRef;
1119 #ifdef DEBUG
1121 JSObject *inner = JS_ObjectToInnerObject(cx, global);
1123 // If this assertion hits then it means that we have a window object as
1124 // our global, but we never called CreateOuterObject.
1125 NS_ASSERTION(inner == global, "Shouldn't be able to innerize here");
1127 #endif
1129 const JSClass *c = JS_GetClass(global);
1131 // Whenever we end up with globals that are JSCLASS_IS_DOMJSCLASS
1132 // and have an nsISupports DOM object, we will need to modify this
1133 // check here.
1134 MOZ_ASSERT(!(c->flags & JSCLASS_IS_DOMJSCLASS));
1135 if ((~c->flags) & (JSCLASS_HAS_PRIVATE |
1136 JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
1137 return nullptr;
1140 nsISupports *priv = static_cast<nsISupports*>(js::GetObjectPrivate(global));
1142 nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native =
1143 do_QueryInterface(priv);
1145 nsCOMPtr<nsIScriptGlobalObject> sgo;
1146 if (wrapped_native) {
1147 // The global object is a XPConnect wrapped native, the native in
1148 // the wrapper might be the nsIScriptGlobalObject
1150 sgo = do_QueryWrappedNative(wrapped_native);
1151 } else {
1152 sgo = do_QueryInterface(priv);
1155 // This'll return a pointer to something we're about to release, but
1156 // that's ok, the JS object will hold it alive long enough.
1157 return sgo;
1160 JSContext*
1161 nsJSContext::GetNativeContext()
1163 return mContext;
1166 nsresult
1167 nsJSContext::InitContext()
1169 // Make sure callers of this use
1170 // WillInitializeContext/DidInitializeContext around this call.
1171 NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
1173 if (!mContext)
1174 return NS_ERROR_OUT_OF_MEMORY;
1176 ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
1178 JSOptionChangedCallback(js_options_dot_str, this);
1180 return NS_OK;
1183 nsresult
1184 nsJSContext::InitializeExternalClasses()
1186 nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
1187 NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
1189 return nameSpaceManager->InitForContext(this);
1192 nsresult
1193 nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
1195 uint32_t argc;
1196 JS::Value *argv = nullptr;
1198 nsCxPusher pusher;
1199 pusher.Push(mContext);
1201 Maybe<nsRootedJSValueArray> tempStorage;
1203 JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
1204 nsresult rv =
1205 ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage);
1206 NS_ENSURE_SUCCESS(rv, rv);
1208 // got the arguments, now attach them.
1210 for (uint32_t i = 0; i < argc; ++i) {
1211 if (!JS_WrapValue(mContext, &argv[i])) {
1212 return NS_ERROR_FAILURE;
1216 JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
1217 JS::Value vargs = OBJECT_TO_JSVAL(args);
1219 return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0)
1220 ? NS_OK
1221 : NS_ERROR_FAILURE;
1224 nsresult
1225 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
1226 JS::Handle<JSObject*> aScope,
1227 uint32_t *aArgc,
1228 JS::Value **aArgv,
1229 Maybe<nsRootedJSValueArray> &aTempStorage)
1231 nsresult rv = NS_OK;
1233 // If the array implements nsIJSArgArray, just grab the values directly.
1234 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
1235 if (fastArray != nullptr)
1236 return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
1238 // Take the slower path converting each item.
1239 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
1240 // SetProperty('arguments', ...);
1242 *aArgv = nullptr;
1243 *aArgc = 0;
1245 nsIXPConnect *xpc = nsContentUtils::XPConnect();
1246 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
1247 AutoJSContext cx;
1249 if (!aArgs)
1250 return NS_OK;
1251 uint32_t argCount;
1252 // This general purpose function may need to convert an arg array
1253 // (window.arguments, event-handler args) and a generic property.
1254 nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
1256 if (argsArray) {
1257 rv = argsArray->GetLength(&argCount);
1258 NS_ENSURE_SUCCESS(rv, rv);
1259 if (argCount == 0)
1260 return NS_OK;
1261 } else {
1262 argCount = 1; // the nsISupports which is not an array
1265 // Use the caller's auto guards to release and unroot.
1266 aTempStorage.construct((JSContext*)cx);
1267 aTempStorage.ref().SetCapacity(cx, argCount);
1268 JS::Value *argv = aTempStorage.ref().Elements();
1270 if (argsArray) {
1271 for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
1272 nsCOMPtr<nsISupports> arg;
1273 JS::Value *thisval = argv + argCtr;
1274 argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
1275 getter_AddRefs(arg));
1276 if (!arg) {
1277 *thisval = JSVAL_NULL;
1278 continue;
1280 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
1281 if (variant != nullptr) {
1282 rv = xpc->VariantToJS(cx, aScope, variant, thisval);
1283 } else {
1284 // And finally, support the nsISupportsPrimitives supplied
1285 // by the AppShell. It generally will pass only strings, but
1286 // as we have code for handling all, we may as well use it.
1287 rv = AddSupportsPrimitiveTojsvals(arg, thisval);
1288 if (rv == NS_ERROR_NO_INTERFACE) {
1289 // something else - probably an event object or similar -
1290 // just wrap it.
1291 #ifdef DEBUG
1292 // but first, check its not another nsISupportsPrimitive, as
1293 // these are now deprecated for use with script contexts.
1294 nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
1295 NS_ASSERTION(prim == nullptr,
1296 "Don't pass nsISupportsPrimitives - use nsIVariant!");
1297 #endif
1298 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
1299 JS::Rooted<JS::Value> v(cx);
1300 rv = nsContentUtils::WrapNative(cx, aScope, arg, v.address(),
1301 getter_AddRefs(wrapper));
1302 if (NS_SUCCEEDED(rv)) {
1303 *thisval = v;
1308 } else {
1309 nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
1310 if (variant) {
1311 rv = xpc->VariantToJS(cx, aScope, variant, argv);
1312 } else {
1313 NS_ERROR("Not an array, not an interface?");
1314 rv = NS_ERROR_UNEXPECTED;
1317 if (NS_FAILED(rv))
1318 return rv;
1319 *aArgv = argv;
1320 *aArgc = argCount;
1321 return NS_OK;
1324 // This really should go into xpconnect somewhere...
1325 nsresult
1326 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv)
1328 NS_PRECONDITION(aArg, "Empty arg");
1330 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
1331 if (!argPrimitive)
1332 return NS_ERROR_NO_INTERFACE;
1334 AutoJSContext cx;
1335 uint16_t type;
1336 argPrimitive->GetType(&type);
1338 switch(type) {
1339 case nsISupportsPrimitive::TYPE_CSTRING : {
1340 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
1341 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1343 nsAutoCString data;
1345 p->GetData(data);
1348 JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
1349 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1351 *aArgv = STRING_TO_JSVAL(str);
1353 break;
1355 case nsISupportsPrimitive::TYPE_STRING : {
1356 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
1357 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1359 nsAutoString data;
1361 p->GetData(data);
1363 // cast is probably safe since wchar_t and jschar are expected
1364 // to be equivalent; both unsigned 16-bit entities
1365 JSString *str =
1366 ::JS_NewUCStringCopyN(cx,
1367 reinterpret_cast<const jschar *>(data.get()),
1368 data.Length());
1369 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1371 *aArgv = STRING_TO_JSVAL(str);
1372 break;
1374 case nsISupportsPrimitive::TYPE_PRBOOL : {
1375 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
1376 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1378 bool data;
1380 p->GetData(&data);
1382 *aArgv = BOOLEAN_TO_JSVAL(data);
1384 break;
1386 case nsISupportsPrimitive::TYPE_PRUINT8 : {
1387 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
1388 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1390 uint8_t data;
1392 p->GetData(&data);
1394 *aArgv = INT_TO_JSVAL(data);
1396 break;
1398 case nsISupportsPrimitive::TYPE_PRUINT16 : {
1399 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
1400 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1402 uint16_t data;
1404 p->GetData(&data);
1406 *aArgv = INT_TO_JSVAL(data);
1408 break;
1410 case nsISupportsPrimitive::TYPE_PRUINT32 : {
1411 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
1412 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1414 uint32_t data;
1416 p->GetData(&data);
1418 *aArgv = INT_TO_JSVAL(data);
1420 break;
1422 case nsISupportsPrimitive::TYPE_CHAR : {
1423 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
1424 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1426 char data;
1428 p->GetData(&data);
1430 JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
1431 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
1433 *aArgv = STRING_TO_JSVAL(str);
1435 break;
1437 case nsISupportsPrimitive::TYPE_PRINT16 : {
1438 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
1439 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1441 int16_t data;
1443 p->GetData(&data);
1445 *aArgv = INT_TO_JSVAL(data);
1447 break;
1449 case nsISupportsPrimitive::TYPE_PRINT32 : {
1450 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
1451 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1453 int32_t data;
1455 p->GetData(&data);
1457 *aArgv = INT_TO_JSVAL(data);
1459 break;
1461 case nsISupportsPrimitive::TYPE_FLOAT : {
1462 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
1463 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1465 float data;
1467 p->GetData(&data);
1469 *aArgv = ::JS_NumberValue(data);
1471 break;
1473 case nsISupportsPrimitive::TYPE_DOUBLE : {
1474 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
1475 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1477 double data;
1479 p->GetData(&data);
1481 *aArgv = ::JS_NumberValue(data);
1483 break;
1485 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
1486 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
1487 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
1489 nsCOMPtr<nsISupports> data;
1490 nsIID *iid = nullptr;
1492 p->GetData(getter_AddRefs(data));
1493 p->GetDataIID(&iid);
1494 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
1496 AutoFree iidGuard(iid); // Free iid upon destruction.
1498 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
1499 JS::Rooted<JSObject*> global(cx, GetWindowProxy());
1500 JS::Rooted<JS::Value> v(cx);
1501 nsresult rv = nsContentUtils::WrapNative(cx, global,
1502 data, iid, v.address(),
1503 getter_AddRefs(wrapper));
1504 NS_ENSURE_SUCCESS(rv, rv);
1506 *aArgv = v;
1508 break;
1510 case nsISupportsPrimitive::TYPE_ID :
1511 case nsISupportsPrimitive::TYPE_PRUINT64 :
1512 case nsISupportsPrimitive::TYPE_PRINT64 :
1513 case nsISupportsPrimitive::TYPE_PRTIME :
1514 case nsISupportsPrimitive::TYPE_VOID : {
1515 NS_WARNING("Unsupported primitive type used");
1516 *aArgv = JSVAL_NULL;
1517 break;
1519 default : {
1520 NS_WARNING("Unknown primitive type used");
1521 *aArgv = JSVAL_NULL;
1522 break;
1525 return NS_OK;
1528 #ifdef NS_TRACE_MALLOC
1530 #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
1531 #include <fcntl.h>
1532 #ifdef XP_UNIX
1533 #include <unistd.h>
1534 #endif
1535 #ifdef XP_WIN32
1536 #include <io.h>
1537 #endif
1538 #include "nsTraceMalloc.h"
1540 static bool
1541 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
1543 if (nsContentUtils::IsCallerChrome())
1544 return true;
1545 JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
1546 return false;
1549 static bool
1550 TraceMallocDisable(JSContext *cx, unsigned argc, JS::Value *vp)
1552 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1553 return false;
1555 NS_TraceMallocDisable();
1556 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1557 return true;
1560 static bool
1561 TraceMallocEnable(JSContext *cx, unsigned argc, JS::Value *vp)
1563 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1564 return false;
1566 NS_TraceMallocEnable();
1567 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1568 return true;
1571 static bool
1572 TraceMallocOpenLogFile(JSContext *cx, unsigned argc, JS::Value *vp)
1574 int fd;
1575 JSString *str;
1577 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1578 return false;
1580 if (argc == 0) {
1581 fd = -1;
1582 } else {
1583 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
1584 if (!str)
1585 return false;
1586 JSAutoByteString filename(cx, str);
1587 if (!filename)
1588 return false;
1589 fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
1590 if (fd < 0) {
1591 JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
1592 return false;
1595 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd));
1596 return true;
1599 static bool
1600 TraceMallocChangeLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
1602 JS::CallArgs args = CallArgsFromVp(argc, vp);
1604 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1605 return false;
1607 int32_t fd, oldfd;
1608 if (args.length() == 0) {
1609 oldfd = -1;
1610 } else {
1611 if (!JS::ToInt32(cx, args[0], &fd))
1612 return false;
1613 oldfd = NS_TraceMallocChangeLogFD(fd);
1614 if (oldfd == -2) {
1615 JS_ReportOutOfMemory(cx);
1616 return false;
1619 args.rval().setInt32(oldfd);
1620 return true;
1623 static bool
1624 TraceMallocCloseLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
1626 JS::CallArgs args = CallArgsFromVp(argc, vp);
1628 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1629 return false;
1631 int32_t fd;
1632 if (args.length() == 0) {
1633 args.rval().setUndefined();
1634 return true;
1636 if (!JS::ToInt32(cx, args[0], &fd))
1637 return false;
1638 NS_TraceMallocCloseLogFD((int) fd);
1639 args.rval().setInt32(fd);
1640 return true;
1643 static bool
1644 TraceMallocLogTimestamp(JSContext *cx, unsigned argc, JS::Value *vp)
1646 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1647 return false;
1649 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
1650 if (!str)
1651 return false;
1652 JSAutoByteString caption(cx, str);
1653 if (!caption)
1654 return false;
1655 NS_TraceMallocLogTimestamp(caption.ptr());
1656 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1657 return true;
1660 static bool
1661 TraceMallocDumpAllocations(JSContext *cx, unsigned argc, JS::Value *vp)
1663 if (!CheckUniversalXPConnectForTraceMalloc(cx))
1664 return false;
1666 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
1667 if (!str)
1668 return false;
1669 JSAutoByteString pathname(cx, str);
1670 if (!pathname)
1671 return false;
1672 if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
1673 JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
1674 return false;
1676 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1677 return true;
1680 static const JSFunctionSpec TraceMallocFunctions[] = {
1681 JS_FS("TraceMallocDisable", TraceMallocDisable, 0, 0),
1682 JS_FS("TraceMallocEnable", TraceMallocEnable, 0, 0),
1683 JS_FS("TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0),
1684 JS_FS("TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0),
1685 JS_FS("TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0),
1686 JS_FS("TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0),
1687 JS_FS("TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0),
1688 JS_FS_END
1691 #endif /* NS_TRACE_MALLOC */
1693 #ifdef MOZ_DMD
1695 #include <errno.h>
1697 namespace mozilla {
1698 namespace dmd {
1700 // See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
1701 // how to use DMD.
1703 static bool
1704 ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
1706 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
1707 if (!str)
1708 return false;
1709 JSAutoByteString pathname(cx, str);
1710 if (!pathname)
1711 return false;
1713 FILE* fp = fopen(pathname.ptr(), "w");
1714 if (!fp) {
1715 JS_ReportError(cx, "DMD can't open %s: %s",
1716 pathname.ptr(), strerror(errno));
1717 return false;
1720 dmd::ClearReports();
1721 fprintf(stderr, "DMD: running reporters...\n");
1722 dmd::RunReporters();
1723 dmd::Writer writer(FpWrite, fp);
1724 dmd::Dump(writer);
1726 fclose(fp);
1728 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1729 return true;
1732 } // namespace dmd
1733 } // namespace mozilla
1735 static const JSFunctionSpec DMDFunctions[] = {
1736 JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0),
1737 JS_FS_END
1740 #endif // defined(MOZ_DMD)
1742 #ifdef MOZ_JPROF
1744 #include <signal.h>
1746 inline bool
1747 IsJProfAction(struct sigaction *action)
1749 return (action->sa_sigaction &&
1750 (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
1753 void NS_JProfStartProfiling();
1754 void NS_JProfStopProfiling();
1755 void NS_JProfClearCircular();
1757 static bool
1758 JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1760 NS_JProfStartProfiling();
1761 return true;
1764 void NS_JProfStartProfiling()
1766 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
1767 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
1768 // JP_RTC_HZ)
1769 struct sigaction action;
1771 // Must check ALRM before PROF since both are enabled for real-time
1772 sigaction(SIGALRM, nullptr, &action);
1773 //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1774 if (IsJProfAction(&action)) {
1775 //printf("Beginning real-time jprof profiling.\n");
1776 raise(SIGALRM);
1777 return;
1780 sigaction(SIGPROF, nullptr, &action);
1781 //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1782 if (IsJProfAction(&action)) {
1783 //printf("Beginning process-time jprof profiling.\n");
1784 raise(SIGPROF);
1785 return;
1788 sigaction(SIGPOLL, nullptr, &action);
1789 //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
1790 if (IsJProfAction(&action)) {
1791 //printf("Beginning rtc-based jprof profiling.\n");
1792 raise(SIGPOLL);
1793 return;
1796 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
1799 static bool
1800 JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
1802 NS_JProfStopProfiling();
1803 return true;
1806 void
1807 NS_JProfStopProfiling()
1809 raise(SIGUSR1);
1810 //printf("Stopped jprof profiling.\n");
1813 static bool
1814 JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1816 NS_JProfClearCircular();
1817 return true;
1820 void
1821 NS_JProfClearCircular()
1823 raise(SIGUSR2);
1824 //printf("cleared jprof buffer\n");
1827 static bool
1828 JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
1830 // Not ideal...
1831 NS_JProfStopProfiling();
1832 NS_JProfStartProfiling();
1833 return true;
1836 static const JSFunctionSpec JProfFunctions[] = {
1837 JS_FS("JProfStartProfiling", JProfStartProfilingJS, 0, 0),
1838 JS_FS("JProfStopProfiling", JProfStopProfilingJS, 0, 0),
1839 JS_FS("JProfClearCircular", JProfClearCircularJS, 0, 0),
1840 JS_FS("JProfSaveCircular", JProfSaveCircularJS, 0, 0),
1841 JS_FS_END
1844 #endif /* defined(MOZ_JPROF) */
1846 nsresult
1847 nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
1849 nsresult rv = InitializeExternalClasses();
1850 NS_ENSURE_SUCCESS(rv, rv);
1852 JSOptionChangedCallback(js_options_dot_str, this);
1853 AutoPushJSContext cx(mContext);
1855 // Attempt to initialize profiling functions
1856 ::JS_DefineProfilingFunctions(cx, aGlobalObj);
1858 #ifdef NS_TRACE_MALLOC
1859 if (nsContentUtils::IsCallerChrome()) {
1860 // Attempt to initialize TraceMalloc functions
1861 ::JS_DefineFunctions(cx, aGlobalObj, TraceMallocFunctions);
1863 #endif
1865 #ifdef MOZ_DMD
1866 // Attempt to initialize DMD functions
1867 ::JS_DefineFunctions(cx, aGlobalObj, DMDFunctions);
1868 #endif
1870 #ifdef MOZ_JPROF
1871 // Attempt to initialize JProf functions
1872 ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
1873 #endif
1875 return rv;
1878 void
1879 nsJSContext::WillInitializeContext()
1881 mIsInitialized = false;
1884 void
1885 nsJSContext::DidInitializeContext()
1887 mIsInitialized = true;
1890 bool
1891 nsJSContext::IsContextInitialized()
1893 return mIsInitialized;
1896 bool
1897 nsJSContext::GetScriptsEnabled()
1899 return mScriptsEnabled;
1902 void
1903 nsJSContext::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
1905 // eeek - this seems the wrong way around - the global should callback
1906 // into each context, so every language is disabled.
1907 mScriptsEnabled = aEnabled;
1909 nsIScriptGlobalObject *global = GetGlobalObject();
1911 if (global) {
1912 global->SetScriptsEnabled(aEnabled, aFireTimeouts);
1917 bool
1918 nsJSContext::GetProcessingScriptTag()
1920 return mProcessingScriptTag;
1923 void
1924 nsJSContext::SetProcessingScriptTag(bool aFlag)
1926 mProcessingScriptTag = aFlag;
1929 void
1930 FullGCTimerFired(nsITimer* aTimer, void* aClosure)
1932 NS_RELEASE(sFullGCTimer);
1934 uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
1935 nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
1936 nsJSContext::IncrementalGC);
1939 //static
1940 void
1941 nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
1942 IsIncremental aIncremental,
1943 IsCompartment aCompartment,
1944 IsShrinking aShrinking,
1945 int64_t aSliceMillis)
1947 PROFILER_LABEL("GC", "GarbageCollectNow");
1949 MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
1951 KillGCTimer();
1952 KillShrinkGCBuffersTimer();
1954 // Reset sPendingLoadCount in case the timer that fired was a
1955 // timer we scheduled due to a normal GC timer firing while
1956 // documents were loading. If this happens we're waiting for a
1957 // document that is taking a long time to load, and we effectively
1958 // ignore the fact that the currently loading documents are still
1959 // loading and move on as if they weren't.
1960 sPendingLoadCount = 0;
1961 sLoadingInProgress = false;
1963 if (!nsContentUtils::XPConnect() || !sRuntime) {
1964 return;
1967 if (sCCLockedOut && aIncremental == IncrementalGC) {
1968 // We're in the middle of incremental GC. Do another slice.
1969 JS::PrepareForIncrementalGC(sRuntime);
1970 JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
1971 return;
1974 JS::PrepareForFullGC(sRuntime);
1975 if (aIncremental == IncrementalGC) {
1976 JS::IncrementalGC(sRuntime, aReason, aSliceMillis);
1977 } else {
1978 JS::GCForReason(sRuntime, aReason);
1982 //static
1983 void
1984 nsJSContext::ShrinkGCBuffersNow()
1986 PROFILER_LABEL("GC", "ShrinkGCBuffersNow");
1988 KillShrinkGCBuffersTimer();
1990 JS::ShrinkGCBuffers(sRuntime);
1993 static void
1994 FinishAnyIncrementalGC()
1996 if (sCCLockedOut) {
1997 // We're in the middle of an incremental GC, so finish it.
1998 JS::PrepareForIncrementalGC(sRuntime);
1999 JS::FinishIncrementalGC(sRuntime, JS::gcreason::CC_FORCED);
2003 static void
2004 FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless)
2006 PRTime startTime = PR_Now();
2007 FinishAnyIncrementalGC();
2008 bool earlyForgetSkippable =
2009 sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS;
2010 nsCycleCollector_forgetSkippable(aRemoveChildless, earlyForgetSkippable);
2011 sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
2012 ++sCleanupsSinceLastGC;
2013 PRTime delta = PR_Now() - startTime;
2014 if (sMinForgetSkippableTime > delta) {
2015 sMinForgetSkippableTime = delta;
2017 if (sMaxForgetSkippableTime < delta) {
2018 sMaxForgetSkippableTime = delta;
2020 sTotalForgetSkippableTime += delta;
2021 sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
2022 ++sForgetSkippableBeforeCC;
2025 MOZ_ALWAYS_INLINE
2026 static uint32_t
2027 TimeBetween(PRTime start, PRTime end)
2029 MOZ_ASSERT(end >= start);
2030 return (uint32_t)(end - start) / PR_USEC_PER_MSEC;
2033 //static
2034 void
2035 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
2036 int32_t aExtraForgetSkippableCalls,
2037 bool aManuallyTriggered)
2039 if (!NS_IsMainThread()) {
2040 return;
2043 PROFILER_LABEL("CC", "CycleCollectNow");
2045 PRTime start = PR_Now();
2047 // Before we begin the cycle collection, make sure there is no active GC.
2048 bool finishedIGC = sCCLockedOut;
2049 FinishAnyIncrementalGC();
2050 PRTime endGCTime = PR_Now();
2051 uint32_t gcDuration = TimeBetween(start, endGCTime);
2053 KillCCTimer();
2055 uint32_t suspected = nsCycleCollector_suspectedCount();
2056 bool ranSyncForgetSkippable = false;
2058 // Run forgetSkippable synchronously to reduce the size of the CC graph. This
2059 // is particularly useful if we recently finished a GC.
2060 if (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS &&
2061 aExtraForgetSkippableCalls >= 0) {
2062 while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
2063 FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
2064 ranSyncForgetSkippable = true;
2068 for (int32_t i = 0; i < aExtraForgetSkippableCalls; ++i) {
2069 FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
2070 ranSyncForgetSkippable = true;
2073 PRTime endSkippableTime = PR_Now();
2074 uint32_t skippableDuration = TimeBetween(endGCTime, endSkippableTime);
2076 // Prepare to actually run the CC.
2077 nsCycleCollectorResults ccResults;
2078 nsCycleCollector_collect(aManuallyTriggered, &ccResults, aListener);
2079 sCCollectedWaitingForGC += ccResults.mFreedRefCounted + ccResults.mFreedGCed;
2081 // If we collected a substantial amount of cycles, poke the GC since more objects
2082 // might be unreachable now.
2083 if (sCCollectedWaitingForGC > 250 ||
2084 sLikelyShortLivingObjectsNeedingGC > 2500 ||
2085 sNeedsGCAfterCC) {
2086 PokeGC(JS::gcreason::CC_WAITING);
2089 PRTime endCCTime = PR_Now();
2091 // Log information about the CC via telemetry, JSON and the console.
2092 uint32_t ccNowDuration = TimeBetween(start, endCCTime);
2093 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, finishedIGC);
2094 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, ranSyncForgetSkippable);
2095 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
2097 if (sLastCCEndTime) {
2098 uint32_t timeBetween = (uint32_t)(start - sLastCCEndTime) / PR_USEC_PER_SEC;
2099 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
2101 sLastCCEndTime = endCCTime;
2103 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
2104 sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
2106 PRTime delta = GetCollectionTimeDelta();
2108 uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
2109 uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX)
2110 ? 0 : sMinForgetSkippableTime;
2112 if (sPostGCEventsToConsole) {
2113 nsCString mergeMsg;
2114 if (ccResults.mMergedZones) {
2115 mergeMsg.AssignLiteral(" merged");
2118 nsCString gcMsg;
2119 if (ccResults.mForcedGC) {
2120 gcMsg.AssignLiteral(", forced a GC");
2123 NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
2124 NS_LL("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")
2125 NS_LL("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, sync: %lu ms, removed: %lu"));
2126 nsString msg;
2127 msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
2128 ccNowDuration, suspected,
2129 ccResults.mVisitedRefCounted, ccResults.mVisitedGCed, mergeMsg.get(),
2130 ccResults.mFreedRefCounted, ccResults.mFreedGCed,
2131 sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
2132 gcMsg.get(),
2133 sForgetSkippableBeforeCC,
2134 minForgetSkippableTime / PR_USEC_PER_MSEC,
2135 sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
2136 (sTotalForgetSkippableTime / cleanups) /
2137 PR_USEC_PER_MSEC,
2138 sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
2139 skippableDuration, sRemovedPurples));
2140 nsCOMPtr<nsIConsoleService> cs =
2141 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2142 if (cs) {
2143 cs->LogStringMessage(msg.get());
2147 if (sPostGCEventsToObserver) {
2148 NS_NAMED_MULTILINE_LITERAL_STRING(kJSONFmt,
2149 NS_LL("{ \"timestamp\": %llu, ")
2150 NS_LL("\"duration\": %llu, ")
2151 NS_LL("\"finish_gc_duration\": %llu, ")
2152 NS_LL("\"sync_skippable_duration\": %llu, ")
2153 NS_LL("\"suspected\": %lu, ")
2154 NS_LL("\"visited\": { ")
2155 NS_LL("\"RCed\": %lu, ")
2156 NS_LL("\"GCed\": %lu }, ")
2157 NS_LL("\"collected\": { ")
2158 NS_LL("\"RCed\": %lu, ")
2159 NS_LL("\"GCed\": %lu }, ")
2160 NS_LL("\"waiting_for_gc\": %lu, ")
2161 NS_LL("\"short_living_objects_waiting_for_gc\": %lu, ")
2162 NS_LL("\"forced_gc\": %d, ")
2163 NS_LL("\"forget_skippable\": { ")
2164 NS_LL("\"times_before_cc\": %lu, ")
2165 NS_LL("\"min\": %lu, ")
2166 NS_LL("\"max\": %lu, ")
2167 NS_LL("\"avg\": %lu, ")
2168 NS_LL("\"total\": %lu, ")
2169 NS_LL("\"removed\": %lu } ")
2170 NS_LL("}"));
2171 nsString json;
2172 json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime,
2173 ccNowDuration, gcDuration, skippableDuration,
2174 suspected,
2175 ccResults.mVisitedRefCounted, ccResults.mVisitedGCed,
2176 ccResults.mFreedRefCounted, ccResults.mFreedGCed,
2177 sCCollectedWaitingForGC,
2178 sLikelyShortLivingObjectsNeedingGC,
2179 ccResults.mForcedGC,
2180 sForgetSkippableBeforeCC,
2181 minForgetSkippableTime / PR_USEC_PER_MSEC,
2182 sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
2183 (sTotalForgetSkippableTime / cleanups) /
2184 PR_USEC_PER_MSEC,
2185 sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
2186 sRemovedPurples));
2187 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2188 if (observerService) {
2189 observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get());
2193 // Update global state to indicate we have just run a cycle collection.
2194 sMinForgetSkippableTime = UINT32_MAX;
2195 sMaxForgetSkippableTime = 0;
2196 sTotalForgetSkippableTime = 0;
2197 sRemovedPurples = 0;
2198 sForgetSkippableBeforeCC = 0;
2199 sNeedsFullCC = false;
2200 sNeedsGCAfterCC = false;
2203 // static
2204 void
2205 InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
2207 NS_RELEASE(sInterSliceGCTimer);
2208 nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
2209 nsJSContext::IncrementalGC,
2210 nsJSContext::CompartmentGC,
2211 nsJSContext::NonShrinkingGC,
2212 NS_INTERSLICE_GC_BUDGET);
2215 // static
2216 void
2217 GCTimerFired(nsITimer *aTimer, void *aClosure)
2219 NS_RELEASE(sGCTimer);
2221 uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
2222 nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
2223 nsJSContext::IncrementalGC,
2224 nsJSContext::CompartmentGC);
2227 void
2228 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
2230 NS_RELEASE(sShrinkGCBuffersTimer);
2232 nsJSContext::ShrinkGCBuffersNow();
2235 static bool
2236 ShouldTriggerCC(uint32_t aSuspected)
2238 return sNeedsFullCC ||
2239 aSuspected > NS_CC_PURPLE_LIMIT ||
2240 (aSuspected > NS_CC_FORCED_PURPLE_LIMIT &&
2241 sLastCCEndTime + NS_CC_FORCED < PR_Now());
2244 static void
2245 CCTimerFired(nsITimer *aTimer, void *aClosure)
2247 if (sDidShutdown) {
2248 return;
2251 static uint32_t ccDelay = NS_CC_DELAY;
2252 if (sCCLockedOut) {
2253 ccDelay = NS_CC_DELAY / 3;
2255 PRTime now = PR_Now();
2256 if (sCCLockedOutTime == 0) {
2257 // Reset sCCTimerFireCount so that we run forgetSkippable
2258 // often enough before CC. Because of reduced ccDelay
2259 // forgetSkippable will be called just a few times.
2260 // NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
2261 // forgetSkippable and CycleCollectNow eventually.
2262 sCCTimerFireCount = 0;
2263 sCCLockedOutTime = now;
2264 return;
2266 if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
2267 return;
2271 ++sCCTimerFireCount;
2273 // During early timer fires, we only run forgetSkippable. During the first
2274 // late timer fire, we decide if we are going to have a second and final
2275 // late timer fire, where we may run the CC.
2276 const uint32_t numEarlyTimerFires = ccDelay / NS_CC_SKIPPABLE_DELAY - 2;
2277 bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
2278 uint32_t suspected = nsCycleCollector_suspectedCount();
2279 if (isLateTimerFire && ShouldTriggerCC(suspected)) {
2280 if (sCCTimerFireCount == numEarlyTimerFires + 1) {
2281 FireForgetSkippable(suspected, true);
2282 if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2283 // Our efforts to avoid a CC have failed, so we return to let the
2284 // timer fire once more to trigger a CC.
2285 return;
2287 } else {
2288 // We are in the final timer fire and still meet the conditions for
2289 // triggering a CC. Let CycleCollectNow finish the current IGC, if any,
2290 // because that will allow us to include the GC time in the CC pause.
2291 nsJSContext::CycleCollectNow(nullptr, 0, false);
2293 } else if ((sPreviousSuspectedCount + 100) <= suspected) {
2294 // Only do a forget skippable if there are more than a few new objects.
2295 FireForgetSkippable(suspected, false);
2298 if (isLateTimerFire) {
2299 ccDelay = NS_CC_DELAY;
2301 // We have either just run the CC or decided we don't want to run the CC
2302 // next time, so kill the timer.
2303 sPreviousSuspectedCount = 0;
2304 nsJSContext::KillCCTimer();
2308 // static
2309 uint32_t
2310 nsJSContext::CleanupsSinceLastGC()
2312 return sCleanupsSinceLastGC;
2315 // static
2316 void
2317 nsJSContext::LoadStart()
2319 sLoadingInProgress = true;
2320 ++sPendingLoadCount;
2323 // static
2324 void
2325 nsJSContext::LoadEnd()
2327 if (!sLoadingInProgress)
2328 return;
2330 // sPendingLoadCount is not a well managed load counter (and doesn't
2331 // need to be), so make sure we don't make it wrap backwards here.
2332 if (sPendingLoadCount > 0) {
2333 --sPendingLoadCount;
2334 return;
2337 // Its probably a good idea to GC soon since we have finished loading.
2338 sLoadingInProgress = false;
2339 PokeGC(JS::gcreason::LOAD_END);
2342 // static
2343 void
2344 nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
2346 if (sGCTimer || sShuttingDown) {
2347 // There's already a timer for GC'ing, just return
2348 return;
2351 if (sCCTimer) {
2352 // Make sure CC is called...
2353 sNeedsFullCC = true;
2354 // and GC after it.
2355 sNeedsGCAfterCC = true;
2356 return;
2359 CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
2361 if (!sGCTimer) {
2362 // Failed to create timer (probably because we're in XPCOM shutdown)
2363 return;
2366 static bool first = true;
2368 sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason),
2369 aDelay
2370 ? aDelay
2371 : (first
2372 ? NS_FIRST_GC_DELAY
2373 : NS_GC_DELAY),
2374 nsITimer::TYPE_ONE_SHOT);
2376 first = false;
2379 // static
2380 void
2381 nsJSContext::PokeShrinkGCBuffers()
2383 if (sShrinkGCBuffersTimer || sShuttingDown) {
2384 return;
2387 CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
2389 if (!sShrinkGCBuffersTimer) {
2390 // Failed to create timer (probably because we're in XPCOM shutdown)
2391 return;
2394 sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nullptr,
2395 NS_SHRINK_GC_BUFFERS_DELAY,
2396 nsITimer::TYPE_ONE_SHOT);
2399 // static
2400 void
2401 nsJSContext::MaybePokeCC()
2403 if (sCCTimer || sShuttingDown || !sHasRunGC) {
2404 return;
2407 if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2408 sCCTimerFireCount = 0;
2409 CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
2410 if (!sCCTimer) {
2411 return;
2413 // We can kill some objects before running forgetSkippable.
2414 nsCycleCollector_dispatchDeferredDeletion();
2416 sCCTimer->InitWithFuncCallback(CCTimerFired, nullptr,
2417 NS_CC_SKIPPABLE_DELAY,
2418 nsITimer::TYPE_REPEATING_SLACK);
2422 //static
2423 void
2424 nsJSContext::KillGCTimer()
2426 if (sGCTimer) {
2427 sGCTimer->Cancel();
2429 NS_RELEASE(sGCTimer);
2433 void
2434 nsJSContext::KillFullGCTimer()
2436 if (sFullGCTimer) {
2437 sFullGCTimer->Cancel();
2438 NS_RELEASE(sFullGCTimer);
2442 void
2443 nsJSContext::KillInterSliceGCTimer()
2445 if (sInterSliceGCTimer) {
2446 sInterSliceGCTimer->Cancel();
2447 NS_RELEASE(sInterSliceGCTimer);
2451 //static
2452 void
2453 nsJSContext::KillShrinkGCBuffersTimer()
2455 if (sShrinkGCBuffersTimer) {
2456 sShrinkGCBuffersTimer->Cancel();
2458 NS_RELEASE(sShrinkGCBuffersTimer);
2462 //static
2463 void
2464 nsJSContext::KillCCTimer()
2466 sCCLockedOutTime = 0;
2468 if (sCCTimer) {
2469 sCCTimer->Cancel();
2471 NS_RELEASE(sCCTimer);
2475 void
2476 nsJSContext::GC(JS::gcreason::Reason aReason)
2478 PokeGC(aReason);
2481 class NotifyGCEndRunnable : public nsRunnable
2483 nsString mMessage;
2485 public:
2486 NotifyGCEndRunnable(const nsString& aMessage) : mMessage(aMessage) {}
2488 NS_DECL_NSIRUNNABLE
2491 NS_IMETHODIMP
2492 NotifyGCEndRunnable::Run()
2494 MOZ_ASSERT(NS_IsMainThread());
2496 nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
2497 if (!observerService) {
2498 return NS_OK;
2501 const jschar oomMsg[3] = { '{', '}', 0 };
2502 const jschar *toSend = mMessage.get() ? mMessage.get() : oomMsg;
2503 observerService->NotifyObservers(nullptr, "garbage-collection-statistics", toSend);
2505 return NS_OK;
2508 static void
2509 DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
2511 NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
2513 if (aProgress == JS::GC_CYCLE_END) {
2514 PRTime delta = GetCollectionTimeDelta();
2516 if (sPostGCEventsToConsole) {
2517 NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) ");
2518 nsString prefix, gcstats;
2519 gcstats.Adopt(aDesc.formatMessage(aRt));
2520 prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
2521 double(delta) / PR_USEC_PER_SEC));
2522 nsString msg = prefix + gcstats;
2523 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2524 if (cs) {
2525 cs->LogStringMessage(msg.get());
2529 if (sPostGCEventsToObserver) {
2530 nsString json;
2531 json.Adopt(aDesc.formatJSON(aRt, PR_Now()));
2532 nsRefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(json);
2533 NS_DispatchToMainThread(notify);
2537 // Prevent cycle collections and shrinking during incremental GC.
2538 if (aProgress == JS::GC_CYCLE_BEGIN) {
2539 sCCLockedOut = true;
2540 nsJSContext::KillShrinkGCBuffersTimer();
2541 } else if (aProgress == JS::GC_CYCLE_END) {
2542 sCCLockedOut = false;
2545 // The GC has more work to do, so schedule another GC slice.
2546 if (aProgress == JS::GC_SLICE_END) {
2547 nsJSContext::KillInterSliceGCTimer();
2548 if (!sShuttingDown) {
2549 CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
2550 sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired,
2551 NULL,
2552 NS_INTERSLICE_GC_DELAY,
2553 nsITimer::TYPE_ONE_SHOT);
2557 if (aProgress == JS::GC_CYCLE_END) {
2558 // May need to kill the inter-slice GC timer
2559 nsJSContext::KillInterSliceGCTimer();
2561 sCCollectedWaitingForGC = 0;
2562 sLikelyShortLivingObjectsNeedingGC = 0;
2563 sCleanupsSinceLastGC = 0;
2564 sNeedsFullCC = true;
2565 sHasRunGC = true;
2566 nsJSContext::MaybePokeCC();
2568 if (aDesc.isCompartment_) {
2569 if (!sFullGCTimer && !sShuttingDown) {
2570 CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
2571 JS::gcreason::Reason reason = JS::gcreason::FULL_GC_TIMER;
2572 sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
2573 reinterpret_cast<void *>(reason),
2574 NS_FULL_GC_DELAY,
2575 nsITimer::TYPE_ONE_SHOT);
2577 } else {
2578 nsJSContext::KillFullGCTimer();
2580 // Avoid shrinking during heavy activity, which is suggested by
2581 // compartment GC.
2582 nsJSContext::PokeShrinkGCBuffers();
2586 if ((aProgress == JS::GC_SLICE_END || aProgress == JS::GC_CYCLE_END) &&
2587 ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
2588 nsCycleCollector_dispatchDeferredDeletion();
2591 if (sPrevGCSliceCallback)
2592 (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
2595 void
2596 nsJSContext::ReportPendingException()
2598 if (mIsInitialized) {
2599 nsJSUtils::ReportPendingException(mContext);
2603 void
2604 nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
2606 mWindowProxy = aWindowProxy;
2609 JSObject*
2610 nsJSContext::GetWindowProxy()
2612 JSObject* windowProxy = GetWindowProxyPreserveColor();
2613 if (windowProxy) {
2614 JS::ExposeObjectToActiveJS(windowProxy);
2617 return windowProxy;
2620 JSObject*
2621 nsJSContext::GetWindowProxyPreserveColor()
2623 return mWindowProxy;
2626 void
2627 nsJSContext::LikelyShortLivingObjectCreated()
2629 ++sLikelyShortLivingObjectsNeedingGC;
2632 void
2633 mozilla::dom::StartupJSEnvironment()
2635 // initialize all our statics, so that we can restart XPCOM
2636 sGCTimer = sFullGCTimer = sCCTimer = nullptr;
2637 sCCLockedOut = false;
2638 sCCLockedOutTime = 0;
2639 sLastCCEndTime = 0;
2640 sHasRunGC = false;
2641 sPendingLoadCount = 0;
2642 sLoadingInProgress = false;
2643 sCCollectedWaitingForGC = 0;
2644 sLikelyShortLivingObjectsNeedingGC = 0;
2645 sPostGCEventsToConsole = false;
2646 sNeedsFullCC = false;
2647 sNeedsGCAfterCC = false;
2648 gNameSpaceManager = nullptr;
2649 sRuntimeService = nullptr;
2650 sRuntime = nullptr;
2651 sIsInitialized = false;
2652 sDidShutdown = false;
2653 sShuttingDown = false;
2654 sContextCount = 0;
2655 sSecurityManager = nullptr;
2658 static int
2659 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
2661 bool reportAll = Preferences::GetBool(aPrefName, false);
2662 nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
2663 return 0;
2666 static int
2667 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
2669 int32_t highwatermark = Preferences::GetInt(aPrefName, 128);
2671 JS_SetGCParameter(sRuntime, JSGC_MAX_MALLOC_BYTES,
2672 highwatermark * 1024L * 1024L);
2673 return 0;
2676 static int
2677 SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
2679 int32_t pref = Preferences::GetInt(aPrefName, -1);
2680 // handle overflow and negative pref values
2681 uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024;
2682 JS_SetGCParameter(sRuntime, JSGC_MAX_BYTES, max);
2683 return 0;
2686 static int
2687 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
2689 bool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
2690 bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
2691 JSGCMode mode;
2692 if (enableIncrementalGC) {
2693 mode = JSGC_MODE_INCREMENTAL;
2694 } else if (enableCompartmentGC) {
2695 mode = JSGC_MODE_COMPARTMENT;
2696 } else {
2697 mode = JSGC_MODE_GLOBAL;
2699 JS_SetGCParameter(sRuntime, JSGC_MODE, mode);
2700 return 0;
2703 static int
2704 SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
2706 int32_t pref = Preferences::GetInt(aPrefName, -1);
2707 // handle overflow and negative pref values
2708 if (pref > 0 && pref < 100000)
2709 JS_SetGCParameter(sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
2710 return 0;
2713 static int
2714 SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure)
2716 int32_t pref = Preferences::GetInt(aPrefName, -1);
2717 // handle overflow and negative pref values
2718 if (pref >= 0 && pref < 10000)
2719 JS_SetGCParameter(sRuntime, (JSGCParamKey)(intptr_t)aClosure, pref);
2720 return 0;
2723 static int
2724 SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure)
2726 bool pref = Preferences::GetBool(aPrefName);
2727 JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref);
2728 return 0;
2731 static int
2732 SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure)
2734 bool pref = Preferences::GetBool(aPrefName);
2735 JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref);
2736 return 0;
2739 JSObject*
2740 NS_DOMReadStructuredClone(JSContext* cx,
2741 JSStructuredCloneReader* reader,
2742 uint32_t tag,
2743 uint32_t data,
2744 void* closure)
2746 if (tag == SCTAG_DOM_IMAGEDATA) {
2747 // Read the information out of the stream.
2748 uint32_t width, height;
2749 JS::Rooted<JS::Value> dataArray(cx);
2750 if (!JS_ReadUint32Pair(reader, &width, &height) ||
2751 !JS_ReadTypedArray(reader, dataArray.address())) {
2752 return nullptr;
2754 MOZ_ASSERT(dataArray.isObject());
2756 // Construct the ImageData.
2757 nsRefPtr<ImageData> imageData = new ImageData(width, height,
2758 dataArray.toObject());
2759 // Wrap it in a JS::Value.
2760 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
2761 if (!global) {
2762 return nullptr;
2764 return imageData->WrapObject(cx, global);
2767 // Don't know what this is. Bail.
2768 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2769 return nullptr;
2772 bool
2773 NS_DOMWriteStructuredClone(JSContext* cx,
2774 JSStructuredCloneWriter* writer,
2775 JS::Handle<JSObject*> obj,
2776 void *closure)
2778 ImageData* imageData;
2779 nsresult rv = UNWRAP_OBJECT(ImageData, cx, obj, imageData);
2780 if (NS_FAILED(rv)) {
2781 // Don't know what this is. Bail.
2782 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2783 return false;
2786 // Prepare the ImageData internals.
2787 uint32_t width = imageData->Width();
2788 uint32_t height = imageData->Height();
2789 JS::Rooted<JSObject*> dataArray(cx, imageData->GetDataObject());
2791 // Write the internals to the stream.
2792 JSAutoCompartment ac(cx, dataArray);
2793 return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) &&
2794 JS_WriteUint32Pair(writer, width, height) &&
2795 JS_WriteTypedArray(writer, JS::ObjectValue(*dataArray));
2798 void
2799 NS_DOMStructuredCloneError(JSContext* cx,
2800 uint32_t errorid)
2802 // We don't currently support any extensions to structured cloning.
2803 xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
2806 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
2808 void
2809 nsJSContext::EnsureStatics()
2811 if (sIsInitialized) {
2812 if (!nsContentUtils::XPConnect()) {
2813 MOZ_CRASH();
2815 return;
2818 nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
2819 &sSecurityManager);
2820 if (NS_FAILED(rv)) {
2821 MOZ_CRASH();
2824 rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
2825 if (NS_FAILED(rv)) {
2826 MOZ_CRASH();
2829 rv = sRuntimeService->GetRuntime(&sRuntime);
2830 if (NS_FAILED(rv)) {
2831 MOZ_CRASH();
2834 // Let's make sure that our main thread is the same as the xpcom main thread.
2835 MOZ_ASSERT(NS_IsMainThread());
2837 sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
2839 // Set up the structured clone callbacks.
2840 static JSStructuredCloneCallbacks cloneCallbacks = {
2841 NS_DOMReadStructuredClone,
2842 NS_DOMWriteStructuredClone,
2843 NS_DOMStructuredCloneError
2845 JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
2847 static js::DOMCallbacks DOMcallbacks = {
2848 InstanceClassHasProtoAtDepth
2850 SetDOMCallbacks(sRuntime, &DOMcallbacks);
2852 // Set these global xpconnect options...
2853 Preferences::RegisterCallbackAndCall(ReportAllJSExceptionsPrefChangedCallback,
2854 "dom.report_all_js_exceptions");
2856 Preferences::RegisterCallbackAndCall(SetMemoryHighWaterMarkPrefChangedCallback,
2857 "javascript.options.mem.high_water_mark");
2859 Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback,
2860 "javascript.options.mem.max");
2862 Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
2863 "javascript.options.mem.gc_per_compartment");
2865 Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
2866 "javascript.options.mem.gc_incremental");
2868 Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback,
2869 "javascript.options.mem.gc_incremental_slice_ms");
2871 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2872 "javascript.options.mem.gc_high_frequency_time_limit_ms",
2873 (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
2875 Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback,
2876 "javascript.options.mem.gc_dynamic_mark_slice");
2878 Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback,
2879 "javascript.options.mem.gc_dynamic_heap_growth");
2881 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2882 "javascript.options.mem.gc_low_frequency_heap_growth",
2883 (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
2885 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2886 "javascript.options.mem.gc_high_frequency_heap_growth_min",
2887 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
2889 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2890 "javascript.options.mem.gc_high_frequency_heap_growth_max",
2891 (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
2893 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2894 "javascript.options.mem.gc_high_frequency_low_limit_mb",
2895 (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
2897 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2898 "javascript.options.mem.gc_high_frequency_high_limit_mb",
2899 (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
2901 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2902 "javascript.options.mem.gc_allocation_threshold_mb",
2903 (void *)JSGC_ALLOCATION_THRESHOLD);
2905 Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
2906 "javascript.options.mem.gc_decommit_threshold_mb",
2907 (void *)JSGC_DECOMMIT_THRESHOLD);
2909 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
2910 if (!obs) {
2911 MOZ_CRASH();
2914 Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
2915 "javascript.options.gc_on_memory_pressure",
2916 true);
2918 nsIObserver* observer = new nsJSEnvironmentObserver();
2919 obs->AddObserver(observer, "memory-pressure", false);
2920 obs->AddObserver(observer, "quit-application", false);
2922 // Bug 907848 - We need to explicitly get the nsIDOMScriptObjectFactory
2923 // service in order to force its constructor to run, which registers a
2924 // shutdown observer. It would be nice to make this more explicit and less
2925 // side-effect-y.
2926 nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
2927 if (!factory) {
2928 MOZ_CRASH();
2931 sIsInitialized = true;
2934 nsScriptNameSpaceManager*
2935 mozilla::dom::GetNameSpaceManager()
2937 if (sDidShutdown)
2938 return nullptr;
2940 if (!gNameSpaceManager) {
2941 gNameSpaceManager = new nsScriptNameSpaceManager;
2942 NS_ADDREF(gNameSpaceManager);
2944 nsresult rv = gNameSpaceManager->Init();
2945 NS_ENSURE_SUCCESS(rv, nullptr);
2948 return gNameSpaceManager;
2951 void
2952 mozilla::dom::ShutdownJSEnvironment()
2954 KillTimers();
2956 NS_IF_RELEASE(gNameSpaceManager);
2958 if (!sContextCount) {
2959 // We're being shutdown, and there are no more contexts
2960 // alive, release the JS runtime service and the security manager.
2962 NS_IF_RELEASE(sRuntimeService);
2963 NS_IF_RELEASE(sSecurityManager);
2966 sShuttingDown = true;
2967 sDidShutdown = true;
2970 // A fast-array class for JS. This class supports both nsIJSScriptArray and
2971 // nsIArray. If it is JS itself providing and consuming this class, all work
2972 // can be done via nsIJSScriptArray, and avoid the conversion of elements
2973 // to/from nsISupports.
2974 // When consumed by non-JS (eg, another script language), conversion is done
2975 // on-the-fly.
2976 class nsJSArgArray MOZ_FINAL : public nsIJSArgArray {
2977 public:
2978 nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
2979 nsresult *prv);
2980 ~nsJSArgArray();
2981 // nsISupports
2982 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
2983 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
2984 nsIJSArgArray)
2986 // nsIArray
2987 NS_DECL_NSIARRAY
2989 // nsIJSArgArray
2990 nsresult GetArgs(uint32_t *argc, void **argv);
2992 void ReleaseJSObjects();
2994 protected:
2995 JSContext *mContext;
2996 JS::Heap<JS::Value> *mArgv;
2997 uint32_t mArgc;
3000 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
3001 nsresult *prv) :
3002 mContext(aContext),
3003 mArgv(nullptr),
3004 mArgc(argc)
3006 // copy the array - we don't know its lifetime, and ours is tied to xpcom
3007 // refcounting.
3008 if (argc) {
3009 static const fallible_t fallible = fallible_t();
3010 mArgv = new (fallible) JS::Heap<JS::Value>[argc];
3011 if (!mArgv) {
3012 *prv = NS_ERROR_OUT_OF_MEMORY;
3013 return;
3017 // Callers are allowed to pass in a null argv even for argc > 0. They can
3018 // then use GetArgs to initialize the values.
3019 if (argv) {
3020 for (uint32_t i = 0; i < argc; ++i)
3021 mArgv[i] = argv[i];
3024 if (argc > 0) {
3025 mozilla::HoldJSObjects(this);
3028 *prv = NS_OK;
3031 nsJSArgArray::~nsJSArgArray()
3033 ReleaseJSObjects();
3036 void
3037 nsJSArgArray::ReleaseJSObjects()
3039 if (mArgv) {
3040 delete [] mArgv;
3042 if (mArgc > 0) {
3043 mArgc = 0;
3044 mozilla::DropJSObjects(this);
3048 // QueryInterface implementation for nsJSArgArray
3049 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
3051 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
3052 tmp->ReleaseJSObjects();
3053 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3054 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
3055 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
3056 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3058 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
3059 if (tmp->mArgv) {
3060 for (uint32_t i = 0; i < tmp->mArgc; ++i) {
3061 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mArgv[i])
3064 NS_IMPL_CYCLE_COLLECTION_TRACE_END
3066 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
3067 NS_INTERFACE_MAP_ENTRY(nsIArray)
3068 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
3069 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
3070 NS_INTERFACE_MAP_END
3072 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
3073 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
3075 nsresult
3076 nsJSArgArray::GetArgs(uint32_t *argc, void **argv)
3078 *argv = (void *)mArgv;
3079 *argc = mArgc;
3080 return NS_OK;
3083 // nsIArray impl
3084 NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t *aLength)
3086 *aLength = mArgc;
3087 return NS_OK;
3090 /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
3091 NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID & uuid, void * *result)
3093 *result = nullptr;
3094 if (index >= mArgc)
3095 return NS_ERROR_INVALID_ARG;
3097 if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
3098 return nsContentUtils::XPConnect()->JSToVariant(mContext, mArgv[index],
3099 (nsIVariant **)result);
3101 NS_WARNING("nsJSArgArray only handles nsIVariant");
3102 return NS_ERROR_NO_INTERFACE;
3105 /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
3106 NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval)
3108 return NS_ERROR_NOT_IMPLEMENTED;
3111 /* nsISimpleEnumerator enumerate (); */
3112 NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
3114 return NS_ERROR_NOT_IMPLEMENTED;
3117 // The factory function
3118 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc, void *argv,
3119 nsIJSArgArray **aArray)
3121 nsresult rv;
3122 nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
3123 static_cast<JS::Value *>(argv), &rv);
3124 if (ret == nullptr)
3125 return NS_ERROR_OUT_OF_MEMORY;
3126 if (NS_FAILED(rv)) {
3127 delete ret;
3128 return rv;
3130 return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);