dom/base/nsJSEnvironment.cpp

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

mercurial