|
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 } |