1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/base/nsJSEnvironment.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3250 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsError.h" 1.11 +#include "nsJSEnvironment.h" 1.12 +#include "nsIScriptGlobalObject.h" 1.13 +#include "nsIScriptObjectPrincipal.h" 1.14 +#include "nsIDOMChromeWindow.h" 1.15 +#include "nsPIDOMWindow.h" 1.16 +#include "nsIScriptSecurityManager.h" 1.17 +#include "nsDOMCID.h" 1.18 +#include "nsIServiceManager.h" 1.19 +#include "nsIXPConnect.h" 1.20 +#include "nsIJSRuntimeService.h" 1.21 +#include "nsCOMPtr.h" 1.22 +#include "nsISupportsPrimitives.h" 1.23 +#include "nsReadableUtils.h" 1.24 +#include "nsJSUtils.h" 1.25 +#include "nsIDocShell.h" 1.26 +#include "nsIDocShellTreeItem.h" 1.27 +#include "nsPresContext.h" 1.28 +#include "nsIConsoleService.h" 1.29 +#include "nsIScriptError.h" 1.30 +#include "nsIInterfaceRequestor.h" 1.31 +#include "nsIInterfaceRequestorUtils.h" 1.32 +#include "nsIPrompt.h" 1.33 +#include "nsIObserverService.h" 1.34 +#include "nsITimer.h" 1.35 +#include "nsIAtom.h" 1.36 +#include "nsContentUtils.h" 1.37 +#include "nsCxPusher.h" 1.38 +#include "mozilla/EventDispatcher.h" 1.39 +#include "nsIContent.h" 1.40 +#include "nsCycleCollector.h" 1.41 +#include "nsNetUtil.h" 1.42 +#include "nsXPCOMCIDInternal.h" 1.43 +#include "nsIXULRuntime.h" 1.44 +#include "nsTextFormatter.h" 1.45 + 1.46 +#include "xpcpublic.h" 1.47 + 1.48 +#include "js/OldDebugAPI.h" 1.49 +#include "jswrapper.h" 1.50 +#include "nsIArray.h" 1.51 +#include "nsIObjectInputStream.h" 1.52 +#include "nsIObjectOutputStream.h" 1.53 +#include "prmem.h" 1.54 +#include "WrapperFactory.h" 1.55 +#include "nsGlobalWindow.h" 1.56 +#include "nsScriptNameSpaceManager.h" 1.57 +#include "StructuredCloneTags.h" 1.58 +#include "mozilla/AutoRestore.h" 1.59 +#include "mozilla/dom/ErrorEvent.h" 1.60 +#include "mozilla/dom/ImageData.h" 1.61 +#include "mozilla/dom/ImageDataBinding.h" 1.62 +#include "nsAXPCNativeCallContext.h" 1.63 +#include "mozilla/CycleCollectedJSRuntime.h" 1.64 + 1.65 +#include "nsJSPrincipals.h" 1.66 + 1.67 +#ifdef XP_MACOSX 1.68 +// AssertMacros.h defines 'check' and conflicts with AccessCheck.h 1.69 +#undef check 1.70 +#endif 1.71 +#include "AccessCheck.h" 1.72 + 1.73 +#ifdef MOZ_JSDEBUGGER 1.74 +#include "jsdIDebuggerService.h" 1.75 +#endif 1.76 +#ifdef MOZ_LOGGING 1.77 +// Force PR_LOGGING so we can get JS strict warnings even in release builds 1.78 +#define FORCE_PR_LOG 1 1.79 +#endif 1.80 +#include "prlog.h" 1.81 +#include "prthread.h" 1.82 + 1.83 +#include "mozilla/Preferences.h" 1.84 +#include "mozilla/Telemetry.h" 1.85 +#include "mozilla/dom/BindingUtils.h" 1.86 +#include "mozilla/Attributes.h" 1.87 +#include "mozilla/dom/asmjscache/AsmJSCache.h" 1.88 +#include "mozilla/dom/CanvasRenderingContext2DBinding.h" 1.89 +#include "mozilla/CycleCollectedJSRuntime.h" 1.90 +#include "mozilla/ContentEvents.h" 1.91 + 1.92 +#include "nsCycleCollectionNoteRootCallback.h" 1.93 +#include "GeckoProfiler.h" 1.94 + 1.95 +using namespace mozilla; 1.96 +using namespace mozilla::dom; 1.97 + 1.98 +const size_t gStackSize = 8192; 1.99 + 1.100 +#ifdef PR_LOGGING 1.101 +static PRLogModuleInfo* gJSDiagnostics; 1.102 +#endif 1.103 + 1.104 +// Thank you Microsoft! 1.105 +#ifdef CompareString 1.106 +#undef CompareString 1.107 +#endif 1.108 + 1.109 +#define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms 1.110 + 1.111 +// The amount of time we wait from the first request to GC to actually 1.112 +// doing the first GC. 1.113 +#define NS_FIRST_GC_DELAY 10000 // ms 1.114 + 1.115 +#define NS_FULL_GC_DELAY 60000 // ms 1.116 + 1.117 +// Maximum amount of time that should elapse between incremental GC slices 1.118 +#define NS_INTERSLICE_GC_DELAY 100 // ms 1.119 + 1.120 +// If we haven't painted in 100ms, we allow for a longer GC budget 1.121 +#define NS_INTERSLICE_GC_BUDGET 40 // ms 1.122 + 1.123 +// The amount of time we wait between a request to CC (after GC ran) 1.124 +// and doing the actual CC. 1.125 +#define NS_CC_DELAY 6000 // ms 1.126 + 1.127 +#define NS_CC_SKIPPABLE_DELAY 400 // ms 1.128 + 1.129 +// Maximum amount of time that should elapse between incremental CC slices 1.130 +static const int64_t kICCIntersliceDelay = 32; // ms 1.131 + 1.132 +// Time budget for an incremental CC slice 1.133 +static const int64_t kICCSliceBudget = 10; // ms 1.134 + 1.135 +// Maximum total duration for an ICC 1.136 +static const uint32_t kMaxICCDuration = 2000; // ms 1.137 + 1.138 +// Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT 1.139 +// objects in the purple buffer. 1.140 +#define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min 1.141 +#define NS_CC_FORCED_PURPLE_LIMIT 10 1.142 + 1.143 +// Don't allow an incremental GC to lock out the CC for too long. 1.144 +#define NS_MAX_CC_LOCKEDOUT_TIME (15 * PR_USEC_PER_SEC) // 15 seconds 1.145 + 1.146 +// Trigger a CC if the purple buffer exceeds this size when we check it. 1.147 +#define NS_CC_PURPLE_LIMIT 200 1.148 + 1.149 +#define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT 1.150 + 1.151 +// Large value used to specify that a script should run essentially forever 1.152 +#define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32) 1.153 + 1.154 +#define NS_MAJOR_FORGET_SKIPPABLE_CALLS 2 1.155 + 1.156 +// if you add statics here, add them to the list in StartupJSEnvironment 1.157 + 1.158 +static nsITimer *sGCTimer; 1.159 +static nsITimer *sShrinkGCBuffersTimer; 1.160 +static nsITimer *sCCTimer; 1.161 +static nsITimer *sICCTimer; 1.162 +static nsITimer *sFullGCTimer; 1.163 +static nsITimer *sInterSliceGCTimer; 1.164 + 1.165 +static TimeStamp sLastCCEndTime; 1.166 + 1.167 +static bool sCCLockedOut; 1.168 +static PRTime sCCLockedOutTime; 1.169 + 1.170 +static JS::GCSliceCallback sPrevGCSliceCallback; 1.171 + 1.172 +static bool sHasRunGC; 1.173 + 1.174 +// The number of currently pending document loads. This count isn't 1.175 +// guaranteed to always reflect reality and can't easily as we don't 1.176 +// have an easy place to know when a load ends or is interrupted in 1.177 +// all cases. This counter also gets reset if we end up GC'ing while 1.178 +// we're waiting for a slow page to load. IOW, this count may be 0 1.179 +// even when there are pending loads. 1.180 +static uint32_t sPendingLoadCount; 1.181 +static bool sLoadingInProgress; 1.182 + 1.183 +static uint32_t sCCollectedWaitingForGC; 1.184 +static uint32_t sLikelyShortLivingObjectsNeedingGC; 1.185 +static bool sPostGCEventsToConsole; 1.186 +static bool sPostGCEventsToObserver; 1.187 +static int32_t sCCTimerFireCount = 0; 1.188 +static uint32_t sMinForgetSkippableTime = UINT32_MAX; 1.189 +static uint32_t sMaxForgetSkippableTime = 0; 1.190 +static uint32_t sTotalForgetSkippableTime = 0; 1.191 +static uint32_t sRemovedPurples = 0; 1.192 +static uint32_t sForgetSkippableBeforeCC = 0; 1.193 +static uint32_t sPreviousSuspectedCount = 0; 1.194 +static uint32_t sCleanupsSinceLastGC = UINT32_MAX; 1.195 +static bool sNeedsFullCC = false; 1.196 +static bool sNeedsGCAfterCC = false; 1.197 +static bool sIncrementalCC = false; 1.198 + 1.199 +static nsScriptNameSpaceManager *gNameSpaceManager; 1.200 + 1.201 +static nsIJSRuntimeService *sRuntimeService; 1.202 + 1.203 +static const char kJSRuntimeServiceContractID[] = 1.204 + "@mozilla.org/js/xpc/RuntimeService;1"; 1.205 + 1.206 +static PRTime sFirstCollectionTime; 1.207 + 1.208 +static JSRuntime *sRuntime; 1.209 + 1.210 +static bool sIsInitialized; 1.211 +static bool sDidShutdown; 1.212 +static bool sShuttingDown; 1.213 +static int32_t sContextCount; 1.214 + 1.215 +static nsIScriptSecurityManager *sSecurityManager; 1.216 + 1.217 +// nsJSEnvironmentObserver observes the memory-pressure notifications 1.218 +// and forces a garbage collection and cycle collection when it happens, if 1.219 +// the appropriate pref is set. 1.220 + 1.221 +static bool sGCOnMemoryPressure; 1.222 + 1.223 +// In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more 1.224 +// aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps 1.225 +// us from triggering expensive full collections too frequently. 1.226 +static int32_t sExpensiveCollectorPokes = 0; 1.227 +static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5; 1.228 + 1.229 +static PRTime 1.230 +GetCollectionTimeDelta() 1.231 +{ 1.232 + PRTime now = PR_Now(); 1.233 + if (sFirstCollectionTime) { 1.234 + return now - sFirstCollectionTime; 1.235 + } 1.236 + sFirstCollectionTime = now; 1.237 + return 0; 1.238 +} 1.239 + 1.240 +static void 1.241 +KillTimers() 1.242 +{ 1.243 + nsJSContext::KillGCTimer(); 1.244 + nsJSContext::KillShrinkGCBuffersTimer(); 1.245 + nsJSContext::KillCCTimer(); 1.246 + nsJSContext::KillICCTimer(); 1.247 + nsJSContext::KillFullGCTimer(); 1.248 + nsJSContext::KillInterSliceGCTimer(); 1.249 +} 1.250 + 1.251 +// If we collected a substantial amount of cycles, poke the GC since more objects 1.252 +// might be unreachable now. 1.253 +static bool 1.254 +NeedsGCAfterCC() 1.255 +{ 1.256 + return sCCollectedWaitingForGC > 250 || 1.257 + sLikelyShortLivingObjectsNeedingGC > 2500 || 1.258 + sNeedsGCAfterCC; 1.259 +} 1.260 + 1.261 +class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver 1.262 +{ 1.263 +public: 1.264 + NS_DECL_ISUPPORTS 1.265 + NS_DECL_NSIOBSERVER 1.266 +}; 1.267 + 1.268 +NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver, nsIObserver) 1.269 + 1.270 +NS_IMETHODIMP 1.271 +nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic, 1.272 + const char16_t* aData) 1.273 +{ 1.274 + if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) { 1.275 + if(StringBeginsWith(nsDependentString(aData), 1.276 + NS_LITERAL_STRING("low-memory-ongoing"))) { 1.277 + // Don't GC/CC if we are in an ongoing low-memory state since its very 1.278 + // slow and it likely won't help us anyway. 1.279 + return NS_OK; 1.280 + } 1.281 + nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE, 1.282 + nsJSContext::NonIncrementalGC, 1.283 + nsJSContext::NonCompartmentGC, 1.284 + nsJSContext::ShrinkingGC); 1.285 + nsJSContext::CycleCollectNow(); 1.286 + if (NeedsGCAfterCC()) { 1.287 + nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE, 1.288 + nsJSContext::NonIncrementalGC, 1.289 + nsJSContext::NonCompartmentGC, 1.290 + nsJSContext::ShrinkingGC); 1.291 + } 1.292 + } else if (!nsCRT::strcmp(aTopic, "quit-application")) { 1.293 + sShuttingDown = true; 1.294 + KillTimers(); 1.295 + } 1.296 + 1.297 + return NS_OK; 1.298 +} 1.299 + 1.300 +/**************************************************************** 1.301 + ************************** AutoFree **************************** 1.302 + ****************************************************************/ 1.303 + 1.304 +class AutoFree { 1.305 +public: 1.306 + AutoFree(void *aPtr) : mPtr(aPtr) { 1.307 + } 1.308 + ~AutoFree() { 1.309 + if (mPtr) 1.310 + nsMemory::Free(mPtr); 1.311 + } 1.312 + void Invalidate() { 1.313 + mPtr = 0; 1.314 + } 1.315 +private: 1.316 + void *mPtr; 1.317 +}; 1.318 + 1.319 +// A utility function for script languages to call. Although it looks small, 1.320 +// the use of nsIDocShell and nsPresContext triggers a huge number of 1.321 +// dependencies that most languages would not otherwise need. 1.322 +// XXXmarkh - This function is mis-placed! 1.323 +bool 1.324 +NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal, 1.325 + const ErrorEventInit &aErrorEventInit, 1.326 + nsEventStatus *aStatus) 1.327 +{ 1.328 + bool called = false; 1.329 + nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal)); 1.330 + nsIDocShell *docShell = win ? win->GetDocShell() : nullptr; 1.331 + if (docShell) { 1.332 + nsRefPtr<nsPresContext> presContext; 1.333 + docShell->GetPresContext(getter_AddRefs(presContext)); 1.334 + 1.335 + static int32_t errorDepth; // Recursion prevention 1.336 + ++errorDepth; 1.337 + 1.338 + if (errorDepth < 2) { 1.339 + // Dispatch() must be synchronous for the recursion block 1.340 + // (errorDepth) to work. 1.341 + nsRefPtr<ErrorEvent> event = 1.342 + ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(win.get()), 1.343 + NS_LITERAL_STRING("error"), 1.344 + aErrorEventInit); 1.345 + event->SetTrusted(true); 1.346 + 1.347 + EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext, 1.348 + aStatus); 1.349 + called = true; 1.350 + } 1.351 + --errorDepth; 1.352 + } 1.353 + return called; 1.354 +} 1.355 + 1.356 +namespace mozilla { 1.357 +namespace dom { 1.358 + 1.359 +AsyncErrorReporter::AsyncErrorReporter(JSRuntime* aRuntime, 1.360 + JSErrorReport* aErrorReport, 1.361 + const char* aFallbackMessage, 1.362 + bool aIsChromeError, 1.363 + nsPIDOMWindow* aWindow) 1.364 + : mSourceLine(static_cast<const char16_t*>(aErrorReport->uclinebuf)) 1.365 + , mLineNumber(aErrorReport->lineno) 1.366 + , mColumn(aErrorReport->column) 1.367 + , mFlags(aErrorReport->flags) 1.368 +{ 1.369 + if (!aErrorReport->filename) { 1.370 + mFileName.SetIsVoid(true); 1.371 + } else { 1.372 + mFileName.AssignWithConversion(aErrorReport->filename); 1.373 + } 1.374 + 1.375 + const char16_t* m = static_cast<const char16_t*>(aErrorReport->ucmessage); 1.376 + if (m) { 1.377 + const char16_t* n = static_cast<const char16_t*> 1.378 + (js::GetErrorTypeName(aRuntime, aErrorReport->exnType)); 1.379 + if (n) { 1.380 + mErrorMsg.Assign(n); 1.381 + mErrorMsg.AppendLiteral(": "); 1.382 + } 1.383 + mErrorMsg.Append(m); 1.384 + } 1.385 + 1.386 + if (mErrorMsg.IsEmpty() && aFallbackMessage) { 1.387 + mErrorMsg.AssignWithConversion(aFallbackMessage); 1.388 + } 1.389 + 1.390 + mCategory = aIsChromeError ? NS_LITERAL_CSTRING("chrome javascript") : 1.391 + NS_LITERAL_CSTRING("content javascript"); 1.392 + 1.393 + mInnerWindowID = 0; 1.394 + if (aWindow && aWindow->IsOuterWindow()) { 1.395 + aWindow = aWindow->GetCurrentInnerWindow(); 1.396 + } 1.397 + if (aWindow) { 1.398 + mInnerWindowID = aWindow->WindowID(); 1.399 + } 1.400 +} 1.401 + 1.402 +void 1.403 +AsyncErrorReporter::ReportError() 1.404 +{ 1.405 + nsCOMPtr<nsIScriptError> errorObject = 1.406 + do_CreateInstance("@mozilla.org/scripterror;1"); 1.407 + if (!errorObject) { 1.408 + return; 1.409 + } 1.410 + 1.411 + nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, 1.412 + mSourceLine, mLineNumber, 1.413 + mColumn, mFlags, mCategory, 1.414 + mInnerWindowID); 1.415 + if (NS_FAILED(rv)) { 1.416 + return; 1.417 + } 1.418 + 1.419 + nsCOMPtr<nsIConsoleService> consoleService = 1.420 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.421 + if (!consoleService) { 1.422 + return; 1.423 + } 1.424 + 1.425 + consoleService->LogMessage(errorObject); 1.426 + return; 1.427 +} 1.428 + 1.429 +} // namespace dom 1.430 +} // namespace mozilla 1.431 + 1.432 +class ScriptErrorEvent : public AsyncErrorReporter 1.433 +{ 1.434 +public: 1.435 + ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal, 1.436 + JSRuntime* aRuntime, 1.437 + JSErrorReport* aErrorReport, 1.438 + const char* aFallbackMessage, 1.439 + nsIPrincipal* aScriptOriginPrincipal, 1.440 + nsIPrincipal* aGlobalPrincipal, 1.441 + nsPIDOMWindow* aWindow, 1.442 + JS::Handle<JS::Value> aError, 1.443 + bool aDispatchEvent) 1.444 + // Pass an empty category, then compute ours 1.445 + : AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage, 1.446 + nsContentUtils::IsSystemPrincipal(aGlobalPrincipal), 1.447 + aWindow) 1.448 + , mScriptGlobal(aScriptGlobal) 1.449 + , mOriginPrincipal(aScriptOriginPrincipal) 1.450 + , mDispatchEvent(aDispatchEvent) 1.451 + , mError(aRuntime, aError) 1.452 + { 1.453 + } 1.454 + 1.455 + NS_IMETHOD Run() 1.456 + { 1.457 + nsEventStatus status = nsEventStatus_eIgnore; 1.458 + // First, notify the DOM that we have a script error. 1.459 + if (mDispatchEvent) { 1.460 + nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal)); 1.461 + nsIDocShell* docShell = win ? win->GetDocShell() : nullptr; 1.462 + if (docShell && 1.463 + !JSREPORT_IS_WARNING(mFlags) && 1.464 + !sHandlingScriptError) { 1.465 + AutoRestore<bool> recursionGuard(sHandlingScriptError); 1.466 + sHandlingScriptError = true; 1.467 + 1.468 + nsRefPtr<nsPresContext> presContext; 1.469 + docShell->GetPresContext(getter_AddRefs(presContext)); 1.470 + 1.471 + ThreadsafeAutoJSContext cx; 1.472 + RootedDictionary<ErrorEventInit> init(cx); 1.473 + init.mCancelable = true; 1.474 + init.mFilename = mFileName; 1.475 + init.mBubbles = true; 1.476 + 1.477 + nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win)); 1.478 + NS_ENSURE_STATE(sop); 1.479 + nsIPrincipal* p = sop->GetPrincipal(); 1.480 + NS_ENSURE_STATE(p); 1.481 + 1.482 + bool sameOrigin = !mOriginPrincipal; 1.483 + 1.484 + if (p && !sameOrigin) { 1.485 + if (NS_FAILED(p->Subsumes(mOriginPrincipal, &sameOrigin))) { 1.486 + sameOrigin = false; 1.487 + } 1.488 + } 1.489 + 1.490 + NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error."); 1.491 + if (sameOrigin) { 1.492 + init.mMessage = mErrorMsg; 1.493 + init.mLineno = mLineNumber; 1.494 + init.mColno = mColumn; 1.495 + init.mError = mError; 1.496 + } else { 1.497 + NS_WARNING("Not same origin error!"); 1.498 + init.mMessage = xoriginMsg; 1.499 + init.mLineno = 0; 1.500 + } 1.501 + 1.502 + nsRefPtr<ErrorEvent> event = 1.503 + ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(win.get()), 1.504 + NS_LITERAL_STRING("error"), init); 1.505 + event->SetTrusted(true); 1.506 + 1.507 + EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext, 1.508 + &status); 1.509 + } 1.510 + } 1.511 + 1.512 + if (status != nsEventStatus_eConsumeNoDefault) { 1.513 + AsyncErrorReporter::ReportError(); 1.514 + } 1.515 + 1.516 + return NS_OK; 1.517 + } 1.518 + 1.519 +private: 1.520 + nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal; 1.521 + nsCOMPtr<nsIPrincipal> mOriginPrincipal; 1.522 + bool mDispatchEvent; 1.523 + JS::PersistentRootedValue mError; 1.524 + 1.525 + static bool sHandlingScriptError; 1.526 +}; 1.527 + 1.528 +bool ScriptErrorEvent::sHandlingScriptError = false; 1.529 + 1.530 +// NOTE: This function could be refactored to use the above. The only reason 1.531 +// it has not been done is that the code below only fills the error event 1.532 +// after it has a good nsPresContext - whereas using the above function 1.533 +// would involve always filling it. Is that a concern? 1.534 +void 1.535 +NS_ScriptErrorReporter(JSContext *cx, 1.536 + const char *message, 1.537 + JSErrorReport *report) 1.538 +{ 1.539 + // We don't want to report exceptions too eagerly, but warnings in the 1.540 + // absence of werror are swallowed whole, so report those now. 1.541 + if (!JSREPORT_IS_WARNING(report->flags)) { 1.542 + nsIXPConnect* xpc = nsContentUtils::XPConnect(); 1.543 + if (JS::DescribeScriptedCaller(cx)) { 1.544 + xpc->MarkErrorUnreported(cx); 1.545 + return; 1.546 + } 1.547 + 1.548 + if (xpc) { 1.549 + nsAXPCNativeCallContext *cc = nullptr; 1.550 + xpc->GetCurrentNativeCallContext(&cc); 1.551 + if (cc) { 1.552 + nsAXPCNativeCallContext *prev = cc; 1.553 + while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) { 1.554 + uint16_t lang; 1.555 + if (NS_SUCCEEDED(prev->GetLanguage(&lang)) && 1.556 + lang == nsAXPCNativeCallContext::LANG_JS) { 1.557 + xpc->MarkErrorUnreported(cx); 1.558 + return; 1.559 + } 1.560 + } 1.561 + } 1.562 + } 1.563 + } 1.564 + 1.565 + // XXX this means we are not going to get error reports on non DOM contexts 1.566 + nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx); 1.567 + 1.568 + JS::Rooted<JS::Value> exception(cx); 1.569 + ::JS_GetPendingException(cx, &exception); 1.570 + 1.571 + // Note: we must do this before running any more code on cx (if cx is the 1.572 + // dynamic script context). 1.573 + ::JS_ClearPendingException(cx); 1.574 + 1.575 + if (context) { 1.576 + nsIScriptGlobalObject *globalObject = context->GetGlobalObject(); 1.577 + 1.578 + if (globalObject) { 1.579 + 1.580 + nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject); 1.581 + nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = 1.582 + do_QueryInterface(globalObject); 1.583 + NS_ASSERTION(scriptPrincipal, "Global objects must implement " 1.584 + "nsIScriptObjectPrincipal"); 1.585 + nsContentUtils::AddScriptRunner( 1.586 + new ScriptErrorEvent(globalObject, 1.587 + JS_GetRuntime(cx), 1.588 + report, 1.589 + message, 1.590 + nsJSPrincipals::get(report->originPrincipals), 1.591 + scriptPrincipal->GetPrincipal(), 1.592 + win, 1.593 + exception, 1.594 + /* We do not try to report Out Of Memory via a dom 1.595 + * event because the dom event handler would 1.596 + * encounter an OOM exception trying to process the 1.597 + * event, and then we'd need to generate a new OOM 1.598 + * event for that new OOM instance -- this isn't 1.599 + * pretty. 1.600 + */ 1.601 + report->errorNumber != JSMSG_OUT_OF_MEMORY)); 1.602 + } 1.603 + } 1.604 + 1.605 + if (nsContentUtils::DOMWindowDumpEnabled()) { 1.606 + // Print it to stderr as well, for the benefit of those invoking 1.607 + // mozilla with -console. 1.608 + nsAutoCString error; 1.609 + error.Assign("JavaScript "); 1.610 + if (JSREPORT_IS_STRICT(report->flags)) 1.611 + error.Append("strict "); 1.612 + if (JSREPORT_IS_WARNING(report->flags)) 1.613 + error.Append("warning: "); 1.614 + else 1.615 + error.Append("error: "); 1.616 + error.Append(report->filename); 1.617 + error.Append(", line "); 1.618 + error.AppendInt(report->lineno, 10); 1.619 + error.Append(": "); 1.620 + if (report->ucmessage) { 1.621 + AppendUTF16toUTF8(reinterpret_cast<const char16_t*>(report->ucmessage), 1.622 + error); 1.623 + } else { 1.624 + error.Append(message); 1.625 + } 1.626 + 1.627 + fprintf(stderr, "%s\n", error.get()); 1.628 + fflush(stderr); 1.629 + } 1.630 + 1.631 +#ifdef PR_LOGGING 1.632 + if (!gJSDiagnostics) 1.633 + gJSDiagnostics = PR_NewLogModule("JSDiagnostics"); 1.634 + 1.635 + if (gJSDiagnostics) { 1.636 + PR_LOG(gJSDiagnostics, 1.637 + JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR, 1.638 + ("file %s, line %u: %s\n%s%s", 1.639 + report->filename, report->lineno, message, 1.640 + report->linebuf ? report->linebuf : "", 1.641 + (report->linebuf && 1.642 + report->linebuf[strlen(report->linebuf)-1] != '\n') 1.643 + ? "\n" 1.644 + : "")); 1.645 + } 1.646 +#endif 1.647 +} 1.648 + 1.649 +#ifdef DEBUG 1.650 +// A couple of useful functions to call when you're debugging. 1.651 +nsGlobalWindow * 1.652 +JSObject2Win(JSObject *obj) 1.653 +{ 1.654 + return xpc::WindowOrNull(obj); 1.655 +} 1.656 + 1.657 +void 1.658 +PrintWinURI(nsGlobalWindow *win) 1.659 +{ 1.660 + if (!win) { 1.661 + printf("No window passed in.\n"); 1.662 + return; 1.663 + } 1.664 + 1.665 + nsCOMPtr<nsIDocument> doc = win->GetExtantDoc(); 1.666 + if (!doc) { 1.667 + printf("No document in the window.\n"); 1.668 + return; 1.669 + } 1.670 + 1.671 + nsIURI *uri = doc->GetDocumentURI(); 1.672 + if (!uri) { 1.673 + printf("Document doesn't have a URI.\n"); 1.674 + return; 1.675 + } 1.676 + 1.677 + nsAutoCString spec; 1.678 + uri->GetSpec(spec); 1.679 + printf("%s\n", spec.get()); 1.680 +} 1.681 + 1.682 +void 1.683 +PrintWinCodebase(nsGlobalWindow *win) 1.684 +{ 1.685 + if (!win) { 1.686 + printf("No window passed in.\n"); 1.687 + return; 1.688 + } 1.689 + 1.690 + nsIPrincipal *prin = win->GetPrincipal(); 1.691 + if (!prin) { 1.692 + printf("Window doesn't have principals.\n"); 1.693 + return; 1.694 + } 1.695 + 1.696 + nsCOMPtr<nsIURI> uri; 1.697 + prin->GetURI(getter_AddRefs(uri)); 1.698 + if (!uri) { 1.699 + printf("No URI, maybe the system principal.\n"); 1.700 + return; 1.701 + } 1.702 + 1.703 + nsAutoCString spec; 1.704 + uri->GetSpec(spec); 1.705 + printf("%s\n", spec.get()); 1.706 +} 1.707 + 1.708 +void 1.709 +DumpString(const nsAString &str) 1.710 +{ 1.711 + printf("%s\n", NS_ConvertUTF16toUTF8(str).get()); 1.712 +} 1.713 +#endif 1.714 + 1.715 +#define JS_OPTIONS_DOT_STR "javascript.options." 1.716 + 1.717 +static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR; 1.718 +static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict"; 1.719 +#ifdef DEBUG 1.720 +static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug"; 1.721 +#endif 1.722 +static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror"; 1.723 +#ifdef JS_GC_ZEAL 1.724 +static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal"; 1.725 +static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency"; 1.726 +#endif 1.727 +static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log"; 1.728 +static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify"; 1.729 + 1.730 +void 1.731 +nsJSContext::JSOptionChangedCallback(const char *pref, void *data) 1.732 +{ 1.733 + nsJSContext *context = reinterpret_cast<nsJSContext *>(data); 1.734 + JSContext *cx = context->mContext; 1.735 + 1.736 + sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str); 1.737 + sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str); 1.738 + 1.739 + JS::ContextOptionsRef(cx).setExtraWarnings(Preferences::GetBool(js_strict_option_str)); 1.740 + 1.741 + // The vanilla GetGlobalObject returns null if a global isn't set up on 1.742 + // the context yet. We can sometimes be call midway through context init, 1.743 + // So ask for the member directly instead. 1.744 + nsIScriptGlobalObject *global = context->GetGlobalObjectRef(); 1.745 + 1.746 + // XXX should we check for sysprin instead of a chrome window, to make 1.747 + // XXX components be covered by the chrome pref instead of the content one? 1.748 + nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global)); 1.749 + nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global)); 1.750 + 1.751 +#ifdef DEBUG 1.752 + // In debug builds, warnings are enabled in chrome context if 1.753 + // javascript.options.strict.debug is true 1.754 + if (Preferences::GetBool(js_strict_debug_option_str) && 1.755 + (chromeWindow || !contentWindow)) { 1.756 + JS::ContextOptionsRef(cx).setExtraWarnings(true); 1.757 + } 1.758 +#endif 1.759 + 1.760 + JS::ContextOptionsRef(cx).setWerror(Preferences::GetBool(js_werror_option_str)); 1.761 + 1.762 +#ifdef JS_GC_ZEAL 1.763 + int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1); 1.764 + int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ); 1.765 + if (zeal >= 0) 1.766 + ::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency); 1.767 +#endif 1.768 +} 1.769 + 1.770 +nsJSContext::nsJSContext(bool aGCOnDestruction, 1.771 + nsIScriptGlobalObject* aGlobalObject) 1.772 + : mWindowProxy(nullptr) 1.773 + , mGCOnDestruction(aGCOnDestruction) 1.774 + , mGlobalObjectRef(aGlobalObject) 1.775 +{ 1.776 + EnsureStatics(); 1.777 + 1.778 + ++sContextCount; 1.779 + 1.780 + mContext = ::JS_NewContext(sRuntime, gStackSize); 1.781 + if (mContext) { 1.782 + ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this)); 1.783 + 1.784 + // Make sure the new context gets the default context options 1.785 + JS::ContextOptionsRef(mContext).setPrivateIsNSISupports(true) 1.786 + .setNoDefaultCompartmentObject(true); 1.787 + 1.788 + // Watch for the JS boolean options 1.789 + Preferences::RegisterCallback(JSOptionChangedCallback, 1.790 + js_options_dot_str, this); 1.791 + } 1.792 + mIsInitialized = false; 1.793 + mProcessingScriptTag = false; 1.794 + HoldJSObjects(this); 1.795 +} 1.796 + 1.797 +nsJSContext::~nsJSContext() 1.798 +{ 1.799 + mGlobalObjectRef = nullptr; 1.800 + 1.801 + DestroyJSContext(); 1.802 + 1.803 + --sContextCount; 1.804 + 1.805 + if (!sContextCount && sDidShutdown) { 1.806 + // The last context is being deleted, and we're already in the 1.807 + // process of shutting down, release the JS runtime service, and 1.808 + // the security manager. 1.809 + 1.810 + NS_IF_RELEASE(sRuntimeService); 1.811 + NS_IF_RELEASE(sSecurityManager); 1.812 + } 1.813 +} 1.814 + 1.815 +// This function is called either by the destructor or unlink, which means that 1.816 +// it can never be called when there is an outstanding ref to the 1.817 +// nsIScriptContext on the stack. Our stack-scoped cx pushers hold such a ref, 1.818 +// so we can assume here that mContext is not on the stack (and therefore not 1.819 +// in use). 1.820 +void 1.821 +nsJSContext::DestroyJSContext() 1.822 +{ 1.823 + if (!mContext) { 1.824 + return; 1.825 + } 1.826 + 1.827 + // Clear our entry in the JSContext, bugzilla bug 66413 1.828 + ::JS_SetContextPrivate(mContext, nullptr); 1.829 + 1.830 + // Unregister our "javascript.options.*" pref-changed callback. 1.831 + Preferences::UnregisterCallback(JSOptionChangedCallback, 1.832 + js_options_dot_str, this); 1.833 + 1.834 + if (mGCOnDestruction) { 1.835 + PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY); 1.836 + } 1.837 + 1.838 + JS_DestroyContextNoGC(mContext); 1.839 + mContext = nullptr; 1.840 + DropJSObjects(this); 1.841 +} 1.842 + 1.843 +// QueryInterface implementation for nsJSContext 1.844 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext) 1.845 + 1.846 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext) 1.847 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy) 1.848 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.849 + 1.850 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext) 1.851 + NS_ASSERTION(!tmp->mContext || !js::ContextHasOutstandingRequests(tmp->mContext), 1.852 + "Trying to unlink a context with outstanding requests."); 1.853 + tmp->mIsInitialized = false; 1.854 + tmp->mGCOnDestruction = false; 1.855 + tmp->mWindowProxy = nullptr; 1.856 + tmp->DestroyJSContext(); 1.857 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef) 1.858 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.859 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext) 1.860 + NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt()) 1.861 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef) 1.862 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.863 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.864 + 1.865 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext) 1.866 + NS_INTERFACE_MAP_ENTRY(nsIScriptContext) 1.867 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.868 +NS_INTERFACE_MAP_END 1.869 + 1.870 + 1.871 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext) 1.872 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext) 1.873 + 1.874 +nsrefcnt 1.875 +nsJSContext::GetCCRefcnt() 1.876 +{ 1.877 + nsrefcnt refcnt = mRefCnt.get(); 1.878 + 1.879 + // In the (abnormal) case of synchronous cycle-collection, the context may be 1.880 + // actively running JS code in which case we must keep it alive by adding an 1.881 + // extra refcount. 1.882 + if (mContext && js::ContextHasOutstandingRequests(mContext)) { 1.883 + refcnt++; 1.884 + } 1.885 + 1.886 + return refcnt; 1.887 +} 1.888 + 1.889 +#ifdef DEBUG 1.890 +bool 1.891 +AtomIsEventHandlerName(nsIAtom *aName) 1.892 +{ 1.893 + const char16_t *name = aName->GetUTF16String(); 1.894 + 1.895 + const char16_t *cp; 1.896 + char16_t c; 1.897 + for (cp = name; *cp != '\0'; ++cp) 1.898 + { 1.899 + c = *cp; 1.900 + if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) 1.901 + return false; 1.902 + } 1.903 + 1.904 + return true; 1.905 +} 1.906 +#endif 1.907 + 1.908 +nsIScriptGlobalObject * 1.909 +nsJSContext::GetGlobalObject() 1.910 +{ 1.911 + AutoJSContext cx; 1.912 + JS::Rooted<JSObject*> global(mContext, GetWindowProxy()); 1.913 + if (!global) { 1.914 + return nullptr; 1.915 + } 1.916 + 1.917 + if (mGlobalObjectRef) 1.918 + return mGlobalObjectRef; 1.919 + 1.920 +#ifdef DEBUG 1.921 + { 1.922 + JSObject *inner = JS_ObjectToInnerObject(cx, global); 1.923 + 1.924 + // If this assertion hits then it means that we have a window object as 1.925 + // our global, but we never called CreateOuterObject. 1.926 + NS_ASSERTION(inner == global, "Shouldn't be able to innerize here"); 1.927 + } 1.928 +#endif 1.929 + 1.930 + const JSClass *c = JS_GetClass(global); 1.931 + 1.932 + nsCOMPtr<nsIScriptGlobalObject> sgo; 1.933 + if (IsDOMClass(c)) { 1.934 + sgo = do_QueryInterface(UnwrapDOMObjectToISupports(global)); 1.935 + } else { 1.936 + if ((~c->flags) & (JSCLASS_HAS_PRIVATE | 1.937 + JSCLASS_PRIVATE_IS_NSISUPPORTS)) { 1.938 + return nullptr; 1.939 + } 1.940 + 1.941 + nsISupports *priv = static_cast<nsISupports*>(js::GetObjectPrivate(global)); 1.942 + 1.943 + nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native = 1.944 + do_QueryInterface(priv); 1.945 + if (wrapped_native) { 1.946 + // The global object is a XPConnect wrapped native, the native in 1.947 + // the wrapper might be the nsIScriptGlobalObject 1.948 + 1.949 + sgo = do_QueryWrappedNative(wrapped_native); 1.950 + } else { 1.951 + sgo = do_QueryInterface(priv); 1.952 + } 1.953 + } 1.954 + 1.955 + // This'll return a pointer to something we're about to release, but 1.956 + // that's ok, the JS object will hold it alive long enough. 1.957 + return sgo; 1.958 +} 1.959 + 1.960 +JSContext* 1.961 +nsJSContext::GetNativeContext() 1.962 +{ 1.963 + return mContext; 1.964 +} 1.965 + 1.966 +nsresult 1.967 +nsJSContext::InitContext() 1.968 +{ 1.969 + // Make sure callers of this use 1.970 + // WillInitializeContext/DidInitializeContext around this call. 1.971 + NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED); 1.972 + 1.973 + if (!mContext) 1.974 + return NS_ERROR_OUT_OF_MEMORY; 1.975 + 1.976 + ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter); 1.977 + 1.978 + JSOptionChangedCallback(js_options_dot_str, this); 1.979 + 1.980 + return NS_OK; 1.981 +} 1.982 + 1.983 +nsresult 1.984 +nsJSContext::InitializeExternalClasses() 1.985 +{ 1.986 + nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager(); 1.987 + NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED); 1.988 + 1.989 + return nameSpaceManager->InitForContext(this); 1.990 +} 1.991 + 1.992 +nsresult 1.993 +nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs) 1.994 +{ 1.995 + nsCxPusher pusher; 1.996 + pusher.Push(mContext); 1.997 + 1.998 + JS::AutoValueVector args(mContext); 1.999 + 1.1000 + JS::Rooted<JSObject*> global(mContext, GetWindowProxy()); 1.1001 + nsresult rv = 1.1002 + ConvertSupportsTojsvals(aArgs, global, args); 1.1003 + NS_ENSURE_SUCCESS(rv, rv); 1.1004 + 1.1005 + // got the arguments, now attach them. 1.1006 + 1.1007 + for (uint32_t i = 0; i < args.length(); ++i) { 1.1008 + if (!JS_WrapValue(mContext, args.handleAt(i))) { 1.1009 + return NS_ERROR_FAILURE; 1.1010 + } 1.1011 + } 1.1012 + 1.1013 + JS::Rooted<JSObject*> array(mContext, ::JS_NewArrayObject(mContext, args)); 1.1014 + if (!array) { 1.1015 + return NS_ERROR_FAILURE; 1.1016 + } 1.1017 + 1.1018 + return JS_DefineProperty(mContext, aTarget, aPropName, array, 0) ? NS_OK : NS_ERROR_FAILURE; 1.1019 +} 1.1020 + 1.1021 +nsresult 1.1022 +nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs, 1.1023 + JS::Handle<JSObject*> aScope, 1.1024 + JS::AutoValueVector& aArgsOut) 1.1025 +{ 1.1026 + nsresult rv = NS_OK; 1.1027 + 1.1028 + // If the array implements nsIJSArgArray, copy the contents and return. 1.1029 + nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs); 1.1030 + if (fastArray) { 1.1031 + uint32_t argc; 1.1032 + JS::Value* argv; 1.1033 + rv = fastArray->GetArgs(&argc, reinterpret_cast<void **>(&argv)); 1.1034 + if (NS_SUCCEEDED(rv) && !aArgsOut.append(argv, argc)) { 1.1035 + rv = NS_ERROR_OUT_OF_MEMORY; 1.1036 + } 1.1037 + return rv; 1.1038 + } 1.1039 + 1.1040 + // Take the slower path converting each item. 1.1041 + // Handle only nsIArray and nsIVariant. nsIArray is only needed for 1.1042 + // SetProperty('arguments', ...); 1.1043 + 1.1044 + nsIXPConnect *xpc = nsContentUtils::XPConnect(); 1.1045 + NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED); 1.1046 + AutoJSContext cx; 1.1047 + 1.1048 + if (!aArgs) 1.1049 + return NS_OK; 1.1050 + uint32_t argCount; 1.1051 + // This general purpose function may need to convert an arg array 1.1052 + // (window.arguments, event-handler args) and a generic property. 1.1053 + nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs)); 1.1054 + 1.1055 + if (argsArray) { 1.1056 + rv = argsArray->GetLength(&argCount); 1.1057 + NS_ENSURE_SUCCESS(rv, rv); 1.1058 + if (argCount == 0) 1.1059 + return NS_OK; 1.1060 + } else { 1.1061 + argCount = 1; // the nsISupports which is not an array 1.1062 + } 1.1063 + 1.1064 + // Use the caller's auto guards to release and unroot. 1.1065 + if (!aArgsOut.resize(argCount)) { 1.1066 + return NS_ERROR_OUT_OF_MEMORY; 1.1067 + } 1.1068 + 1.1069 + if (argsArray) { 1.1070 + for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) { 1.1071 + nsCOMPtr<nsISupports> arg; 1.1072 + JS::MutableHandle<JS::Value> thisVal = aArgsOut.handleAt(argCtr); 1.1073 + argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports), 1.1074 + getter_AddRefs(arg)); 1.1075 + if (!arg) { 1.1076 + thisVal.setNull(); 1.1077 + continue; 1.1078 + } 1.1079 + nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg)); 1.1080 + if (variant != nullptr) { 1.1081 + rv = xpc->VariantToJS(cx, aScope, variant, thisVal); 1.1082 + } else { 1.1083 + // And finally, support the nsISupportsPrimitives supplied 1.1084 + // by the AppShell. It generally will pass only strings, but 1.1085 + // as we have code for handling all, we may as well use it. 1.1086 + rv = AddSupportsPrimitiveTojsvals(arg, thisVal.address()); 1.1087 + if (rv == NS_ERROR_NO_INTERFACE) { 1.1088 + // something else - probably an event object or similar - 1.1089 + // just wrap it. 1.1090 +#ifdef DEBUG 1.1091 + // but first, check its not another nsISupportsPrimitive, as 1.1092 + // these are now deprecated for use with script contexts. 1.1093 + nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg)); 1.1094 + NS_ASSERTION(prim == nullptr, 1.1095 + "Don't pass nsISupportsPrimitives - use nsIVariant!"); 1.1096 +#endif 1.1097 + JSAutoCompartment ac(cx, aScope); 1.1098 + rv = nsContentUtils::WrapNative(cx, arg, thisVal); 1.1099 + } 1.1100 + } 1.1101 + } 1.1102 + } else { 1.1103 + nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs); 1.1104 + if (variant) { 1.1105 + rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut.handleAt(0)); 1.1106 + } else { 1.1107 + NS_ERROR("Not an array, not an interface?"); 1.1108 + rv = NS_ERROR_UNEXPECTED; 1.1109 + } 1.1110 + } 1.1111 + return rv; 1.1112 +} 1.1113 + 1.1114 +// This really should go into xpconnect somewhere... 1.1115 +nsresult 1.1116 +nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv) 1.1117 +{ 1.1118 + NS_PRECONDITION(aArg, "Empty arg"); 1.1119 + 1.1120 + nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg)); 1.1121 + if (!argPrimitive) 1.1122 + return NS_ERROR_NO_INTERFACE; 1.1123 + 1.1124 + AutoJSContext cx; 1.1125 + uint16_t type; 1.1126 + argPrimitive->GetType(&type); 1.1127 + 1.1128 + switch(type) { 1.1129 + case nsISupportsPrimitive::TYPE_CSTRING : { 1.1130 + nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive)); 1.1131 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1132 + 1.1133 + nsAutoCString data; 1.1134 + 1.1135 + p->GetData(data); 1.1136 + 1.1137 + 1.1138 + JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length()); 1.1139 + NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); 1.1140 + 1.1141 + *aArgv = STRING_TO_JSVAL(str); 1.1142 + 1.1143 + break; 1.1144 + } 1.1145 + case nsISupportsPrimitive::TYPE_STRING : { 1.1146 + nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive)); 1.1147 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1148 + 1.1149 + nsAutoString data; 1.1150 + 1.1151 + p->GetData(data); 1.1152 + 1.1153 + // cast is probably safe since wchar_t and jschar are expected 1.1154 + // to be equivalent; both unsigned 16-bit entities 1.1155 + JSString *str = 1.1156 + ::JS_NewUCStringCopyN(cx, data.get(), data.Length()); 1.1157 + NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); 1.1158 + 1.1159 + *aArgv = STRING_TO_JSVAL(str); 1.1160 + break; 1.1161 + } 1.1162 + case nsISupportsPrimitive::TYPE_PRBOOL : { 1.1163 + nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive)); 1.1164 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1165 + 1.1166 + bool data; 1.1167 + 1.1168 + p->GetData(&data); 1.1169 + 1.1170 + *aArgv = BOOLEAN_TO_JSVAL(data); 1.1171 + 1.1172 + break; 1.1173 + } 1.1174 + case nsISupportsPrimitive::TYPE_PRUINT8 : { 1.1175 + nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive)); 1.1176 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1177 + 1.1178 + uint8_t data; 1.1179 + 1.1180 + p->GetData(&data); 1.1181 + 1.1182 + *aArgv = INT_TO_JSVAL(data); 1.1183 + 1.1184 + break; 1.1185 + } 1.1186 + case nsISupportsPrimitive::TYPE_PRUINT16 : { 1.1187 + nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive)); 1.1188 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1189 + 1.1190 + uint16_t data; 1.1191 + 1.1192 + p->GetData(&data); 1.1193 + 1.1194 + *aArgv = INT_TO_JSVAL(data); 1.1195 + 1.1196 + break; 1.1197 + } 1.1198 + case nsISupportsPrimitive::TYPE_PRUINT32 : { 1.1199 + nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive)); 1.1200 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1201 + 1.1202 + uint32_t data; 1.1203 + 1.1204 + p->GetData(&data); 1.1205 + 1.1206 + *aArgv = INT_TO_JSVAL(data); 1.1207 + 1.1208 + break; 1.1209 + } 1.1210 + case nsISupportsPrimitive::TYPE_CHAR : { 1.1211 + nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive)); 1.1212 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1213 + 1.1214 + char data; 1.1215 + 1.1216 + p->GetData(&data); 1.1217 + 1.1218 + JSString *str = ::JS_NewStringCopyN(cx, &data, 1); 1.1219 + NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); 1.1220 + 1.1221 + *aArgv = STRING_TO_JSVAL(str); 1.1222 + 1.1223 + break; 1.1224 + } 1.1225 + case nsISupportsPrimitive::TYPE_PRINT16 : { 1.1226 + nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive)); 1.1227 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1228 + 1.1229 + int16_t data; 1.1230 + 1.1231 + p->GetData(&data); 1.1232 + 1.1233 + *aArgv = INT_TO_JSVAL(data); 1.1234 + 1.1235 + break; 1.1236 + } 1.1237 + case nsISupportsPrimitive::TYPE_PRINT32 : { 1.1238 + nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive)); 1.1239 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1240 + 1.1241 + int32_t data; 1.1242 + 1.1243 + p->GetData(&data); 1.1244 + 1.1245 + *aArgv = INT_TO_JSVAL(data); 1.1246 + 1.1247 + break; 1.1248 + } 1.1249 + case nsISupportsPrimitive::TYPE_FLOAT : { 1.1250 + nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive)); 1.1251 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1252 + 1.1253 + float data; 1.1254 + 1.1255 + p->GetData(&data); 1.1256 + 1.1257 + *aArgv = ::JS_NumberValue(data); 1.1258 + 1.1259 + break; 1.1260 + } 1.1261 + case nsISupportsPrimitive::TYPE_DOUBLE : { 1.1262 + nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive)); 1.1263 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1264 + 1.1265 + double data; 1.1266 + 1.1267 + p->GetData(&data); 1.1268 + 1.1269 + *aArgv = ::JS_NumberValue(data); 1.1270 + 1.1271 + break; 1.1272 + } 1.1273 + case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : { 1.1274 + nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive)); 1.1275 + NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED); 1.1276 + 1.1277 + nsCOMPtr<nsISupports> data; 1.1278 + nsIID *iid = nullptr; 1.1279 + 1.1280 + p->GetData(getter_AddRefs(data)); 1.1281 + p->GetDataIID(&iid); 1.1282 + NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED); 1.1283 + 1.1284 + AutoFree iidGuard(iid); // Free iid upon destruction. 1.1285 + 1.1286 + JS::Rooted<JSObject*> scope(cx, GetWindowProxy()); 1.1287 + JS::Rooted<JS::Value> v(cx); 1.1288 + JSAutoCompartment ac(cx, scope); 1.1289 + nsresult rv = nsContentUtils::WrapNative(cx, data, iid, &v); 1.1290 + NS_ENSURE_SUCCESS(rv, rv); 1.1291 + 1.1292 + *aArgv = v; 1.1293 + 1.1294 + break; 1.1295 + } 1.1296 + case nsISupportsPrimitive::TYPE_ID : 1.1297 + case nsISupportsPrimitive::TYPE_PRUINT64 : 1.1298 + case nsISupportsPrimitive::TYPE_PRINT64 : 1.1299 + case nsISupportsPrimitive::TYPE_PRTIME : 1.1300 + case nsISupportsPrimitive::TYPE_VOID : { 1.1301 + NS_WARNING("Unsupported primitive type used"); 1.1302 + *aArgv = JSVAL_NULL; 1.1303 + break; 1.1304 + } 1.1305 + default : { 1.1306 + NS_WARNING("Unknown primitive type used"); 1.1307 + *aArgv = JSVAL_NULL; 1.1308 + break; 1.1309 + } 1.1310 + } 1.1311 + return NS_OK; 1.1312 +} 1.1313 + 1.1314 +#ifdef NS_TRACE_MALLOC 1.1315 + 1.1316 +#include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC 1.1317 +#include <fcntl.h> 1.1318 +#ifdef XP_UNIX 1.1319 +#include <unistd.h> 1.1320 +#endif 1.1321 +#ifdef XP_WIN32 1.1322 +#include <io.h> 1.1323 +#endif 1.1324 +#include "nsTraceMalloc.h" 1.1325 + 1.1326 +static bool 1.1327 +CheckUniversalXPConnectForTraceMalloc(JSContext *cx) 1.1328 +{ 1.1329 + if (nsContentUtils::IsCallerChrome()) 1.1330 + return true; 1.1331 + JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect"); 1.1332 + return false; 1.1333 +} 1.1334 + 1.1335 +static bool 1.1336 +TraceMallocDisable(JSContext *cx, unsigned argc, JS::Value *vp) 1.1337 +{ 1.1338 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1339 + 1.1340 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1341 + return false; 1.1342 + 1.1343 + NS_TraceMallocDisable(); 1.1344 + args.rval().setUndefined(); 1.1345 + return true; 1.1346 +} 1.1347 + 1.1348 +static bool 1.1349 +TraceMallocEnable(JSContext *cx, unsigned argc, JS::Value *vp) 1.1350 +{ 1.1351 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1352 + 1.1353 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1354 + return false; 1.1355 + 1.1356 + NS_TraceMallocEnable(); 1.1357 + args.rval().setUndefined(); 1.1358 + return true; 1.1359 +} 1.1360 + 1.1361 +static bool 1.1362 +TraceMallocOpenLogFile(JSContext *cx, unsigned argc, JS::Value *vp) 1.1363 +{ 1.1364 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1365 + 1.1366 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1367 + return false; 1.1368 + 1.1369 + int fd; 1.1370 + if (argc == 0) { 1.1371 + fd = -1; 1.1372 + } else { 1.1373 + JSString *str = JS::ToString(cx, args[0]); 1.1374 + if (!str) 1.1375 + return false; 1.1376 + JSAutoByteString filename(cx, str); 1.1377 + if (!filename) 1.1378 + return false; 1.1379 + fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644); 1.1380 + if (fd < 0) { 1.1381 + JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno)); 1.1382 + return false; 1.1383 + } 1.1384 + } 1.1385 + args.rval().setInt32(fd); 1.1386 + return true; 1.1387 +} 1.1388 + 1.1389 +static bool 1.1390 +TraceMallocChangeLogFD(JSContext *cx, unsigned argc, JS::Value *vp) 1.1391 +{ 1.1392 + JS::CallArgs args = CallArgsFromVp(argc, vp); 1.1393 + 1.1394 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1395 + return false; 1.1396 + 1.1397 + int32_t fd, oldfd; 1.1398 + if (args.length() == 0) { 1.1399 + oldfd = -1; 1.1400 + } else { 1.1401 + if (!JS::ToInt32(cx, args[0], &fd)) 1.1402 + return false; 1.1403 + oldfd = NS_TraceMallocChangeLogFD(fd); 1.1404 + if (oldfd == -2) { 1.1405 + JS_ReportOutOfMemory(cx); 1.1406 + return false; 1.1407 + } 1.1408 + } 1.1409 + args.rval().setInt32(oldfd); 1.1410 + return true; 1.1411 +} 1.1412 + 1.1413 +static bool 1.1414 +TraceMallocCloseLogFD(JSContext *cx, unsigned argc, JS::Value *vp) 1.1415 +{ 1.1416 + JS::CallArgs args = CallArgsFromVp(argc, vp); 1.1417 + 1.1418 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1419 + return false; 1.1420 + 1.1421 + int32_t fd; 1.1422 + if (args.length() == 0) { 1.1423 + args.rval().setUndefined(); 1.1424 + return true; 1.1425 + } 1.1426 + if (!JS::ToInt32(cx, args[0], &fd)) 1.1427 + return false; 1.1428 + NS_TraceMallocCloseLogFD((int) fd); 1.1429 + args.rval().setInt32(fd); 1.1430 + return true; 1.1431 +} 1.1432 + 1.1433 +static bool 1.1434 +TraceMallocLogTimestamp(JSContext *cx, unsigned argc, JS::Value *vp) 1.1435 +{ 1.1436 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1437 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1438 + return false; 1.1439 + 1.1440 + JSString *str = JS::ToString(cx, args.get(0)); 1.1441 + if (!str) 1.1442 + return false; 1.1443 + JSAutoByteString caption(cx, str); 1.1444 + if (!caption) 1.1445 + return false; 1.1446 + NS_TraceMallocLogTimestamp(caption.ptr()); 1.1447 + args.rval().setUndefined(); 1.1448 + return true; 1.1449 +} 1.1450 + 1.1451 +static bool 1.1452 +TraceMallocDumpAllocations(JSContext *cx, unsigned argc, JS::Value *vp) 1.1453 +{ 1.1454 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1455 + if (!CheckUniversalXPConnectForTraceMalloc(cx)) 1.1456 + return false; 1.1457 + 1.1458 + JSString *str = JS::ToString(cx, args.get(0)); 1.1459 + if (!str) 1.1460 + return false; 1.1461 + JSAutoByteString pathname(cx, str); 1.1462 + if (!pathname) 1.1463 + return false; 1.1464 + if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) { 1.1465 + JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno)); 1.1466 + return false; 1.1467 + } 1.1468 + args.rval().setUndefined(); 1.1469 + return true; 1.1470 +} 1.1471 + 1.1472 +static const JSFunctionSpec TraceMallocFunctions[] = { 1.1473 + JS_FS("TraceMallocDisable", TraceMallocDisable, 0, 0), 1.1474 + JS_FS("TraceMallocEnable", TraceMallocEnable, 0, 0), 1.1475 + JS_FS("TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0), 1.1476 + JS_FS("TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0), 1.1477 + JS_FS("TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0), 1.1478 + JS_FS("TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0), 1.1479 + JS_FS("TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0), 1.1480 + JS_FS_END 1.1481 +}; 1.1482 + 1.1483 +#endif /* NS_TRACE_MALLOC */ 1.1484 + 1.1485 +#ifdef MOZ_DMD 1.1486 + 1.1487 +#include <errno.h> 1.1488 + 1.1489 +namespace mozilla { 1.1490 +namespace dmd { 1.1491 + 1.1492 +// See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on 1.1493 +// how to use DMD. 1.1494 + 1.1495 +static bool 1.1496 +ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp) 1.1497 +{ 1.1498 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.1499 + JSString *str = JS::ToString(cx, args.get(0)); 1.1500 + if (!str) 1.1501 + return false; 1.1502 + JSAutoByteString pathname(cx, str); 1.1503 + if (!pathname) 1.1504 + return false; 1.1505 + 1.1506 + FILE* fp = fopen(pathname.ptr(), "w"); 1.1507 + if (!fp) { 1.1508 + JS_ReportError(cx, "DMD can't open %s: %s", 1.1509 + pathname.ptr(), strerror(errno)); 1.1510 + return false; 1.1511 + } 1.1512 + 1.1513 + dmd::ClearReports(); 1.1514 + fprintf(stderr, "DMD: running reporters...\n"); 1.1515 + dmd::RunReportersForThisProcess(); 1.1516 + dmd::Writer writer(FpWrite, fp); 1.1517 + dmd::Dump(writer); 1.1518 + 1.1519 + fclose(fp); 1.1520 + 1.1521 + args.rval().setUndefined(); 1.1522 + return true; 1.1523 +} 1.1524 + 1.1525 +} // namespace dmd 1.1526 +} // namespace mozilla 1.1527 + 1.1528 +static const JSFunctionSpec DMDFunctions[] = { 1.1529 + JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0), 1.1530 + JS_FS_END 1.1531 +}; 1.1532 + 1.1533 +#endif // defined(MOZ_DMD) 1.1534 + 1.1535 +#ifdef MOZ_JPROF 1.1536 + 1.1537 +#include <signal.h> 1.1538 + 1.1539 +inline bool 1.1540 +IsJProfAction(struct sigaction *action) 1.1541 +{ 1.1542 + return (action->sa_sigaction && 1.1543 + (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO)); 1.1544 +} 1.1545 + 1.1546 +void NS_JProfStartProfiling(); 1.1547 +void NS_JProfStopProfiling(); 1.1548 +void NS_JProfClearCircular(); 1.1549 + 1.1550 +static bool 1.1551 +JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp) 1.1552 +{ 1.1553 + NS_JProfStartProfiling(); 1.1554 + return true; 1.1555 +} 1.1556 + 1.1557 +void NS_JProfStartProfiling() 1.1558 +{ 1.1559 + // Figure out whether we're dealing with SIGPROF, SIGALRM, or 1.1560 + // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for 1.1561 + // JP_RTC_HZ) 1.1562 + struct sigaction action; 1.1563 + 1.1564 + // Must check ALRM before PROF since both are enabled for real-time 1.1565 + sigaction(SIGALRM, nullptr, &action); 1.1566 + //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags); 1.1567 + if (IsJProfAction(&action)) { 1.1568 + //printf("Beginning real-time jprof profiling.\n"); 1.1569 + raise(SIGALRM); 1.1570 + return; 1.1571 + } 1.1572 + 1.1573 + sigaction(SIGPROF, nullptr, &action); 1.1574 + //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags); 1.1575 + if (IsJProfAction(&action)) { 1.1576 + //printf("Beginning process-time jprof profiling.\n"); 1.1577 + raise(SIGPROF); 1.1578 + return; 1.1579 + } 1.1580 + 1.1581 + sigaction(SIGPOLL, nullptr, &action); 1.1582 + //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags); 1.1583 + if (IsJProfAction(&action)) { 1.1584 + //printf("Beginning rtc-based jprof profiling.\n"); 1.1585 + raise(SIGPOLL); 1.1586 + return; 1.1587 + } 1.1588 + 1.1589 + printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n"); 1.1590 +} 1.1591 + 1.1592 +static bool 1.1593 +JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp) 1.1594 +{ 1.1595 + NS_JProfStopProfiling(); 1.1596 + return true; 1.1597 +} 1.1598 + 1.1599 +void 1.1600 +NS_JProfStopProfiling() 1.1601 +{ 1.1602 + raise(SIGUSR1); 1.1603 + //printf("Stopped jprof profiling.\n"); 1.1604 +} 1.1605 + 1.1606 +static bool 1.1607 +JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp) 1.1608 +{ 1.1609 + NS_JProfClearCircular(); 1.1610 + return true; 1.1611 +} 1.1612 + 1.1613 +void 1.1614 +NS_JProfClearCircular() 1.1615 +{ 1.1616 + raise(SIGUSR2); 1.1617 + //printf("cleared jprof buffer\n"); 1.1618 +} 1.1619 + 1.1620 +static bool 1.1621 +JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp) 1.1622 +{ 1.1623 + // Not ideal... 1.1624 + NS_JProfStopProfiling(); 1.1625 + NS_JProfStartProfiling(); 1.1626 + return true; 1.1627 +} 1.1628 + 1.1629 +static const JSFunctionSpec JProfFunctions[] = { 1.1630 + JS_FS("JProfStartProfiling", JProfStartProfilingJS, 0, 0), 1.1631 + JS_FS("JProfStopProfiling", JProfStopProfilingJS, 0, 0), 1.1632 + JS_FS("JProfClearCircular", JProfClearCircularJS, 0, 0), 1.1633 + JS_FS("JProfSaveCircular", JProfSaveCircularJS, 0, 0), 1.1634 + JS_FS_END 1.1635 +}; 1.1636 + 1.1637 +#endif /* defined(MOZ_JPROF) */ 1.1638 + 1.1639 +nsresult 1.1640 +nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj) 1.1641 +{ 1.1642 + nsresult rv = InitializeExternalClasses(); 1.1643 + NS_ENSURE_SUCCESS(rv, rv); 1.1644 + 1.1645 + JSOptionChangedCallback(js_options_dot_str, this); 1.1646 + AutoPushJSContext cx(mContext); 1.1647 + 1.1648 + // Attempt to initialize profiling functions 1.1649 + ::JS_DefineProfilingFunctions(cx, aGlobalObj); 1.1650 + 1.1651 +#ifdef NS_TRACE_MALLOC 1.1652 + if (nsContentUtils::IsCallerChrome()) { 1.1653 + // Attempt to initialize TraceMalloc functions 1.1654 + ::JS_DefineFunctions(cx, aGlobalObj, TraceMallocFunctions); 1.1655 + } 1.1656 +#endif 1.1657 + 1.1658 +#ifdef MOZ_DMD 1.1659 + // Attempt to initialize DMD functions 1.1660 + ::JS_DefineFunctions(cx, aGlobalObj, DMDFunctions); 1.1661 +#endif 1.1662 + 1.1663 +#ifdef MOZ_JPROF 1.1664 + // Attempt to initialize JProf functions 1.1665 + ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions); 1.1666 +#endif 1.1667 + 1.1668 + return rv; 1.1669 +} 1.1670 + 1.1671 +void 1.1672 +nsJSContext::WillInitializeContext() 1.1673 +{ 1.1674 + mIsInitialized = false; 1.1675 +} 1.1676 + 1.1677 +void 1.1678 +nsJSContext::DidInitializeContext() 1.1679 +{ 1.1680 + mIsInitialized = true; 1.1681 +} 1.1682 + 1.1683 +bool 1.1684 +nsJSContext::IsContextInitialized() 1.1685 +{ 1.1686 + return mIsInitialized; 1.1687 +} 1.1688 + 1.1689 +bool 1.1690 +nsJSContext::GetProcessingScriptTag() 1.1691 +{ 1.1692 + return mProcessingScriptTag; 1.1693 +} 1.1694 + 1.1695 +void 1.1696 +nsJSContext::SetProcessingScriptTag(bool aFlag) 1.1697 +{ 1.1698 + mProcessingScriptTag = aFlag; 1.1699 +} 1.1700 + 1.1701 +void 1.1702 +FullGCTimerFired(nsITimer* aTimer, void* aClosure) 1.1703 +{ 1.1704 + nsJSContext::KillFullGCTimer(); 1.1705 + uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure); 1.1706 + nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason), 1.1707 + nsJSContext::IncrementalGC); 1.1708 +} 1.1709 + 1.1710 +//static 1.1711 +void 1.1712 +nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason, 1.1713 + IsIncremental aIncremental, 1.1714 + IsCompartment aCompartment, 1.1715 + IsShrinking aShrinking, 1.1716 + int64_t aSliceMillis) 1.1717 +{ 1.1718 + PROFILER_LABEL("GC", "GarbageCollectNow"); 1.1719 + 1.1720 + MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC); 1.1721 + 1.1722 + KillGCTimer(); 1.1723 + KillShrinkGCBuffersTimer(); 1.1724 + 1.1725 + // Reset sPendingLoadCount in case the timer that fired was a 1.1726 + // timer we scheduled due to a normal GC timer firing while 1.1727 + // documents were loading. If this happens we're waiting for a 1.1728 + // document that is taking a long time to load, and we effectively 1.1729 + // ignore the fact that the currently loading documents are still 1.1730 + // loading and move on as if they weren't. 1.1731 + sPendingLoadCount = 0; 1.1732 + sLoadingInProgress = false; 1.1733 + 1.1734 + if (!nsContentUtils::XPConnect() || !sRuntime) { 1.1735 + return; 1.1736 + } 1.1737 + 1.1738 + if (sCCLockedOut && aIncremental == IncrementalGC) { 1.1739 + // We're in the middle of incremental GC. Do another slice. 1.1740 + JS::PrepareForIncrementalGC(sRuntime); 1.1741 + JS::IncrementalGC(sRuntime, aReason, aSliceMillis); 1.1742 + return; 1.1743 + } 1.1744 + 1.1745 + JS::PrepareForFullGC(sRuntime); 1.1746 + if (aIncremental == IncrementalGC) { 1.1747 + MOZ_ASSERT(aShrinking == NonShrinkingGC); 1.1748 + JS::IncrementalGC(sRuntime, aReason, aSliceMillis); 1.1749 + } else if (aShrinking == ShrinkingGC) { 1.1750 + JS::ShrinkingGC(sRuntime, aReason); 1.1751 + } else { 1.1752 + JS::GCForReason(sRuntime, aReason); 1.1753 + } 1.1754 +} 1.1755 + 1.1756 +//static 1.1757 +void 1.1758 +nsJSContext::ShrinkGCBuffersNow() 1.1759 +{ 1.1760 + PROFILER_LABEL("GC", "ShrinkGCBuffersNow"); 1.1761 + 1.1762 + KillShrinkGCBuffersTimer(); 1.1763 + 1.1764 + JS::ShrinkGCBuffers(sRuntime); 1.1765 +} 1.1766 + 1.1767 +static void 1.1768 +FinishAnyIncrementalGC() 1.1769 +{ 1.1770 + if (sCCLockedOut) { 1.1771 + // We're in the middle of an incremental GC, so finish it. 1.1772 + JS::PrepareForIncrementalGC(sRuntime); 1.1773 + JS::FinishIncrementalGC(sRuntime, JS::gcreason::CC_FORCED); 1.1774 + } 1.1775 +} 1.1776 + 1.1777 +static void 1.1778 +FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless) 1.1779 +{ 1.1780 + PRTime startTime = PR_Now(); 1.1781 + FinishAnyIncrementalGC(); 1.1782 + bool earlyForgetSkippable = 1.1783 + sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS; 1.1784 + nsCycleCollector_forgetSkippable(aRemoveChildless, earlyForgetSkippable); 1.1785 + sPreviousSuspectedCount = nsCycleCollector_suspectedCount(); 1.1786 + ++sCleanupsSinceLastGC; 1.1787 + PRTime delta = PR_Now() - startTime; 1.1788 + if (sMinForgetSkippableTime > delta) { 1.1789 + sMinForgetSkippableTime = delta; 1.1790 + } 1.1791 + if (sMaxForgetSkippableTime < delta) { 1.1792 + sMaxForgetSkippableTime = delta; 1.1793 + } 1.1794 + sTotalForgetSkippableTime += delta; 1.1795 + sRemovedPurples += (aSuspected - sPreviousSuspectedCount); 1.1796 + ++sForgetSkippableBeforeCC; 1.1797 +} 1.1798 + 1.1799 +MOZ_ALWAYS_INLINE 1.1800 +static uint32_t 1.1801 +TimeBetween(TimeStamp start, TimeStamp end) 1.1802 +{ 1.1803 + MOZ_ASSERT(end >= start); 1.1804 + return (uint32_t) ((end - start).ToMilliseconds()); 1.1805 +} 1.1806 + 1.1807 +static uint32_t 1.1808 +TimeUntilNow(TimeStamp start) 1.1809 +{ 1.1810 + if (start.IsNull()) { 1.1811 + return 0; 1.1812 + } 1.1813 + return TimeBetween(start, TimeStamp::Now()); 1.1814 +} 1.1815 + 1.1816 +struct CycleCollectorStats 1.1817 +{ 1.1818 + void Clear() 1.1819 + { 1.1820 + mBeginSliceTime = TimeStamp(); 1.1821 + mEndSliceTime = TimeStamp(); 1.1822 + mBeginTime = TimeStamp(); 1.1823 + mMaxGCDuration = 0; 1.1824 + mRanSyncForgetSkippable = false; 1.1825 + mSuspected = 0; 1.1826 + mMaxSkippableDuration = 0; 1.1827 + mMaxSliceTime = 0; 1.1828 + mTotalSliceTime = 0; 1.1829 + mAnyLockedOut = false; 1.1830 + mExtraForgetSkippableCalls = 0; 1.1831 + } 1.1832 + 1.1833 + void PrepareForCycleCollectionSlice(int32_t aExtraForgetSkippableCalls = 0); 1.1834 + 1.1835 + void FinishCycleCollectionSlice() 1.1836 + { 1.1837 + if (mBeginSliceTime.IsNull()) { 1.1838 + // We already called this method from EndCycleCollectionCallback for this slice. 1.1839 + return; 1.1840 + } 1.1841 + 1.1842 + mEndSliceTime = TimeStamp::Now(); 1.1843 + uint32_t sliceTime = TimeBetween(mBeginSliceTime, mEndSliceTime); 1.1844 + mMaxSliceTime = std::max(mMaxSliceTime, sliceTime); 1.1845 + mTotalSliceTime += sliceTime; 1.1846 + mBeginSliceTime = TimeStamp(); 1.1847 + MOZ_ASSERT(mExtraForgetSkippableCalls == 0, "Forget to reset extra forget skippable calls?"); 1.1848 + } 1.1849 + 1.1850 + void RunForgetSkippable(); 1.1851 + 1.1852 + // Time the current slice began, including any GC finishing. 1.1853 + TimeStamp mBeginSliceTime; 1.1854 + 1.1855 + // Time the previous slice of the current CC ended. 1.1856 + TimeStamp mEndSliceTime; 1.1857 + 1.1858 + // Time the current cycle collection began. 1.1859 + TimeStamp mBeginTime; 1.1860 + 1.1861 + // The longest GC finishing duration for any slice of the current CC. 1.1862 + uint32_t mMaxGCDuration; 1.1863 + 1.1864 + // True if we ran sync forget skippable in any slice of the current CC. 1.1865 + bool mRanSyncForgetSkippable; 1.1866 + 1.1867 + // Number of suspected objects at the start of the current CC. 1.1868 + uint32_t mSuspected; 1.1869 + 1.1870 + // The longest duration spent on sync forget skippable in any slice of the 1.1871 + // current CC. 1.1872 + uint32_t mMaxSkippableDuration; 1.1873 + 1.1874 + // The longest pause of any slice in the current CC. 1.1875 + uint32_t mMaxSliceTime; 1.1876 + 1.1877 + // The total amount of time spent actually running the current CC. 1.1878 + uint32_t mTotalSliceTime; 1.1879 + 1.1880 + // True if we were locked out by the GC in any slice of the current CC. 1.1881 + bool mAnyLockedOut; 1.1882 + 1.1883 + int32_t mExtraForgetSkippableCalls; 1.1884 +}; 1.1885 + 1.1886 +CycleCollectorStats gCCStats; 1.1887 + 1.1888 +void 1.1889 +CycleCollectorStats::PrepareForCycleCollectionSlice(int32_t aExtraForgetSkippableCalls) 1.1890 +{ 1.1891 + mBeginSliceTime = TimeStamp::Now(); 1.1892 + 1.1893 + // Before we begin the cycle collection, make sure there is no active GC. 1.1894 + if (sCCLockedOut) { 1.1895 + mAnyLockedOut = true; 1.1896 + FinishAnyIncrementalGC(); 1.1897 + uint32_t gcTime = TimeBetween(mBeginSliceTime, TimeStamp::Now()); 1.1898 + mMaxGCDuration = std::max(mMaxGCDuration, gcTime); 1.1899 + } 1.1900 + 1.1901 + mExtraForgetSkippableCalls = aExtraForgetSkippableCalls; 1.1902 +} 1.1903 + 1.1904 +void 1.1905 +CycleCollectorStats::RunForgetSkippable() 1.1906 +{ 1.1907 + // Run forgetSkippable synchronously to reduce the size of the CC graph. This 1.1908 + // is particularly useful if we recently finished a GC. 1.1909 + if (mExtraForgetSkippableCalls >= 0) { 1.1910 + TimeStamp beginForgetSkippable = TimeStamp::Now(); 1.1911 + bool ranSyncForgetSkippable = false; 1.1912 + while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) { 1.1913 + FireForgetSkippable(nsCycleCollector_suspectedCount(), false); 1.1914 + ranSyncForgetSkippable = true; 1.1915 + } 1.1916 + 1.1917 + for (int32_t i = 0; i < mExtraForgetSkippableCalls; ++i) { 1.1918 + FireForgetSkippable(nsCycleCollector_suspectedCount(), false); 1.1919 + ranSyncForgetSkippable = true; 1.1920 + } 1.1921 + 1.1922 + if (ranSyncForgetSkippable) { 1.1923 + mMaxSkippableDuration = 1.1924 + std::max(mMaxSkippableDuration, TimeUntilNow(beginForgetSkippable)); 1.1925 + mRanSyncForgetSkippable = true; 1.1926 + } 1.1927 + 1.1928 + } 1.1929 + mExtraForgetSkippableCalls = 0; 1.1930 +} 1.1931 + 1.1932 +//static 1.1933 +void 1.1934 +nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener, 1.1935 + int32_t aExtraForgetSkippableCalls) 1.1936 +{ 1.1937 + if (!NS_IsMainThread()) { 1.1938 + return; 1.1939 + } 1.1940 + 1.1941 + PROFILER_LABEL("CC", "CycleCollectNow"); 1.1942 + gCCStats.PrepareForCycleCollectionSlice(aExtraForgetSkippableCalls); 1.1943 + nsCycleCollector_collect(aListener); 1.1944 + gCCStats.FinishCycleCollectionSlice(); 1.1945 +} 1.1946 + 1.1947 +//static 1.1948 +void 1.1949 +nsJSContext::RunCycleCollectorSlice() 1.1950 +{ 1.1951 + if (!NS_IsMainThread()) { 1.1952 + return; 1.1953 + } 1.1954 + 1.1955 + PROFILER_LABEL("CC", "RunCycleCollectorSlice"); 1.1956 + 1.1957 + gCCStats.PrepareForCycleCollectionSlice(); 1.1958 + 1.1959 + // Decide how long we want to budget for this slice. By default, 1.1960 + // use an unlimited budget. 1.1961 + int64_t sliceBudget = -1; 1.1962 + 1.1963 + if (sIncrementalCC) { 1.1964 + if (gCCStats.mBeginTime.IsNull()) { 1.1965 + // If no CC is in progress, use the standard slice time. 1.1966 + sliceBudget = kICCSliceBudget; 1.1967 + } else { 1.1968 + TimeStamp now = TimeStamp::Now(); 1.1969 + 1.1970 + // Only run a limited slice if we're within the max running time. 1.1971 + if (TimeBetween(gCCStats.mBeginTime, now) < kMaxICCDuration) { 1.1972 + float sliceMultiplier = std::max(TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay, 1.0f); 1.1973 + sliceBudget = kICCSliceBudget * sliceMultiplier; 1.1974 + } 1.1975 + } 1.1976 + } 1.1977 + 1.1978 + nsCycleCollector_collectSlice(sliceBudget); 1.1979 + 1.1980 + gCCStats.FinishCycleCollectionSlice(); 1.1981 +} 1.1982 + 1.1983 +static void 1.1984 +ICCTimerFired(nsITimer* aTimer, void* aClosure) 1.1985 +{ 1.1986 + if (sDidShutdown) { 1.1987 + return; 1.1988 + } 1.1989 + 1.1990 + // Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us 1.1991 + // to synchronously finish the GC, which is bad. 1.1992 + 1.1993 + if (sCCLockedOut) { 1.1994 + PRTime now = PR_Now(); 1.1995 + if (sCCLockedOutTime == 0) { 1.1996 + sCCLockedOutTime = now; 1.1997 + return; 1.1998 + } 1.1999 + if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) { 1.2000 + return; 1.2001 + } 1.2002 + } 1.2003 + 1.2004 + nsJSContext::RunCycleCollectorSlice(); 1.2005 +} 1.2006 + 1.2007 +//static 1.2008 +void 1.2009 +nsJSContext::BeginCycleCollectionCallback() 1.2010 +{ 1.2011 + MOZ_ASSERT(NS_IsMainThread()); 1.2012 + 1.2013 + gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime; 1.2014 + gCCStats.mSuspected = nsCycleCollector_suspectedCount(); 1.2015 + 1.2016 + KillCCTimer(); 1.2017 + 1.2018 + gCCStats.RunForgetSkippable(); 1.2019 + 1.2020 + MOZ_ASSERT(!sICCTimer, "Tried to create a new ICC timer when one already existed."); 1.2021 + 1.2022 + if (!sIncrementalCC) { 1.2023 + return; 1.2024 + } 1.2025 + 1.2026 + CallCreateInstance("@mozilla.org/timer;1", &sICCTimer); 1.2027 + if (sICCTimer) { 1.2028 + sICCTimer->InitWithFuncCallback(ICCTimerFired, 1.2029 + nullptr, 1.2030 + kICCIntersliceDelay, 1.2031 + nsITimer::TYPE_REPEATING_SLACK); 1.2032 + } 1.2033 +} 1.2034 + 1.2035 +//static 1.2036 +void 1.2037 +nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults) 1.2038 +{ 1.2039 + MOZ_ASSERT(NS_IsMainThread()); 1.2040 + 1.2041 + nsJSContext::KillICCTimer(); 1.2042 + 1.2043 + // Update timing information for the current slice before we log it, if 1.2044 + // we previously called PrepareForCycleCollectionSlice(). During shutdown 1.2045 + // CCs, this won't happen. 1.2046 + gCCStats.FinishCycleCollectionSlice(); 1.2047 + 1.2048 + sCCollectedWaitingForGC += aResults.mFreedRefCounted + aResults.mFreedGCed; 1.2049 + 1.2050 + if (NeedsGCAfterCC()) { 1.2051 + PokeGC(JS::gcreason::CC_WAITING); 1.2052 + } 1.2053 + 1.2054 + TimeStamp endCCTimeStamp = TimeStamp::Now(); 1.2055 + 1.2056 + PRTime endCCTime; 1.2057 + if (sPostGCEventsToObserver) { 1.2058 + endCCTime = PR_Now(); 1.2059 + } 1.2060 + 1.2061 + // Log information about the CC via telemetry, JSON and the console. 1.2062 + uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp); 1.2063 + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut); 1.2064 + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable); 1.2065 + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration); 1.2066 + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE, gCCStats.mMaxSliceTime); 1.2067 + 1.2068 + if (!sLastCCEndTime.IsNull()) { 1.2069 + // TimeBetween returns milliseconds, but we want to report seconds. 1.2070 + uint32_t timeBetween = TimeBetween(sLastCCEndTime, gCCStats.mBeginTime) / 1000; 1.2071 + Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween); 1.2072 + } 1.2073 + sLastCCEndTime = endCCTimeStamp; 1.2074 + 1.2075 + Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX, 1.2076 + sMaxForgetSkippableTime / PR_USEC_PER_MSEC); 1.2077 + 1.2078 + PRTime delta = GetCollectionTimeDelta(); 1.2079 + 1.2080 + uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1; 1.2081 + uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX) 1.2082 + ? 0 : sMinForgetSkippableTime; 1.2083 + 1.2084 + if (sPostGCEventsToConsole) { 1.2085 + nsCString mergeMsg; 1.2086 + if (aResults.mMergedZones) { 1.2087 + mergeMsg.AssignLiteral(" merged"); 1.2088 + } 1.2089 + 1.2090 + nsCString gcMsg; 1.2091 + if (aResults.mForcedGC) { 1.2092 + gcMsg.AssignLiteral(", forced a GC"); 1.2093 + } 1.2094 + 1.2095 + NS_NAMED_MULTILINE_LITERAL_STRING(kFmt, 1.2096 + 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") 1.2097 + 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")); 1.2098 + nsString msg; 1.2099 + msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC, 1.2100 + gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime, 1.2101 + gCCStats.mSuspected, 1.2102 + aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(), 1.2103 + aResults.mFreedRefCounted, aResults.mFreedGCed, 1.2104 + sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC, 1.2105 + gcMsg.get(), 1.2106 + sForgetSkippableBeforeCC, 1.2107 + minForgetSkippableTime / PR_USEC_PER_MSEC, 1.2108 + sMaxForgetSkippableTime / PR_USEC_PER_MSEC, 1.2109 + (sTotalForgetSkippableTime / cleanups) / 1.2110 + PR_USEC_PER_MSEC, 1.2111 + sTotalForgetSkippableTime / PR_USEC_PER_MSEC, 1.2112 + gCCStats.mMaxSkippableDuration, sRemovedPurples)); 1.2113 + nsCOMPtr<nsIConsoleService> cs = 1.2114 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.2115 + if (cs) { 1.2116 + cs->LogStringMessage(msg.get()); 1.2117 + } 1.2118 + } 1.2119 + 1.2120 + if (sPostGCEventsToObserver) { 1.2121 + NS_NAMED_MULTILINE_LITERAL_STRING(kJSONFmt, 1.2122 + MOZ_UTF16("{ \"timestamp\": %llu, ") 1.2123 + MOZ_UTF16("\"duration\": %lu, ") 1.2124 + MOZ_UTF16("\"max_slice_pause\": %lu, ") 1.2125 + MOZ_UTF16("\"total_slice_pause\": %lu, ") 1.2126 + MOZ_UTF16("\"max_finish_gc_duration\": %lu, ") 1.2127 + MOZ_UTF16("\"max_sync_skippable_duration\": %lu, ") 1.2128 + MOZ_UTF16("\"suspected\": %lu, ") 1.2129 + MOZ_UTF16("\"visited\": { ") 1.2130 + MOZ_UTF16("\"RCed\": %lu, ") 1.2131 + MOZ_UTF16("\"GCed\": %lu }, ") 1.2132 + MOZ_UTF16("\"collected\": { ") 1.2133 + MOZ_UTF16("\"RCed\": %lu, ") 1.2134 + MOZ_UTF16("\"GCed\": %lu }, ") 1.2135 + MOZ_UTF16("\"waiting_for_gc\": %lu, ") 1.2136 + MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ") 1.2137 + MOZ_UTF16("\"forced_gc\": %d, ") 1.2138 + MOZ_UTF16("\"forget_skippable\": { ") 1.2139 + MOZ_UTF16("\"times_before_cc\": %lu, ") 1.2140 + MOZ_UTF16("\"min\": %lu, ") 1.2141 + MOZ_UTF16("\"max\": %lu, ") 1.2142 + MOZ_UTF16("\"avg\": %lu, ") 1.2143 + MOZ_UTF16("\"total\": %lu, ") 1.2144 + MOZ_UTF16("\"removed\": %lu } ") 1.2145 + MOZ_UTF16("}")); 1.2146 + nsString json; 1.2147 + json.Adopt(nsTextFormatter::smprintf(kJSONFmt.get(), endCCTime, ccNowDuration, 1.2148 + gCCStats.mMaxSliceTime, 1.2149 + gCCStats.mTotalSliceTime, 1.2150 + gCCStats.mMaxGCDuration, 1.2151 + gCCStats.mMaxSkippableDuration, 1.2152 + gCCStats.mSuspected, 1.2153 + aResults.mVisitedRefCounted, aResults.mVisitedGCed, 1.2154 + aResults.mFreedRefCounted, aResults.mFreedGCed, 1.2155 + sCCollectedWaitingForGC, 1.2156 + sLikelyShortLivingObjectsNeedingGC, 1.2157 + aResults.mForcedGC, 1.2158 + sForgetSkippableBeforeCC, 1.2159 + minForgetSkippableTime / PR_USEC_PER_MSEC, 1.2160 + sMaxForgetSkippableTime / PR_USEC_PER_MSEC, 1.2161 + (sTotalForgetSkippableTime / cleanups) / 1.2162 + PR_USEC_PER_MSEC, 1.2163 + sTotalForgetSkippableTime / PR_USEC_PER_MSEC, 1.2164 + sRemovedPurples)); 1.2165 + nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); 1.2166 + if (observerService) { 1.2167 + observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get()); 1.2168 + } 1.2169 + } 1.2170 + 1.2171 + // Update global state to indicate we have just run a cycle collection. 1.2172 + sMinForgetSkippableTime = UINT32_MAX; 1.2173 + sMaxForgetSkippableTime = 0; 1.2174 + sTotalForgetSkippableTime = 0; 1.2175 + sRemovedPurples = 0; 1.2176 + sForgetSkippableBeforeCC = 0; 1.2177 + sNeedsFullCC = false; 1.2178 + sNeedsGCAfterCC = false; 1.2179 + gCCStats.Clear(); 1.2180 +} 1.2181 + 1.2182 +// static 1.2183 +void 1.2184 +InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure) 1.2185 +{ 1.2186 + nsJSContext::KillInterSliceGCTimer(); 1.2187 + nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC, 1.2188 + nsJSContext::IncrementalGC, 1.2189 + nsJSContext::CompartmentGC, 1.2190 + nsJSContext::NonShrinkingGC, 1.2191 + NS_INTERSLICE_GC_BUDGET); 1.2192 +} 1.2193 + 1.2194 +// static 1.2195 +void 1.2196 +GCTimerFired(nsITimer *aTimer, void *aClosure) 1.2197 +{ 1.2198 + nsJSContext::KillGCTimer(); 1.2199 + uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure); 1.2200 + nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason), 1.2201 + nsJSContext::IncrementalGC, 1.2202 + nsJSContext::CompartmentGC); 1.2203 +} 1.2204 + 1.2205 +void 1.2206 +ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure) 1.2207 +{ 1.2208 + nsJSContext::KillShrinkGCBuffersTimer(); 1.2209 + nsJSContext::ShrinkGCBuffersNow(); 1.2210 +} 1.2211 + 1.2212 +static bool 1.2213 +ShouldTriggerCC(uint32_t aSuspected) 1.2214 +{ 1.2215 + return sNeedsFullCC || 1.2216 + aSuspected > NS_CC_PURPLE_LIMIT || 1.2217 + (aSuspected > NS_CC_FORCED_PURPLE_LIMIT && 1.2218 + TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED); 1.2219 +} 1.2220 + 1.2221 +static uint32_t 1.2222 +TimeToNextCC() 1.2223 +{ 1.2224 + if (sIncrementalCC) { 1.2225 + return NS_CC_DELAY - kMaxICCDuration; 1.2226 + } 1.2227 + return NS_CC_DELAY; 1.2228 +} 1.2229 + 1.2230 +static_assert(NS_CC_DELAY > kMaxICCDuration, "ICC shouldn't reduce CC delay to 0"); 1.2231 + 1.2232 +static void 1.2233 +CCTimerFired(nsITimer *aTimer, void *aClosure) 1.2234 +{ 1.2235 + if (sDidShutdown) { 1.2236 + return; 1.2237 + } 1.2238 + 1.2239 + static uint32_t ccDelay = NS_CC_DELAY; 1.2240 + if (sCCLockedOut) { 1.2241 + ccDelay = TimeToNextCC() / 3; 1.2242 + 1.2243 + PRTime now = PR_Now(); 1.2244 + if (sCCLockedOutTime == 0) { 1.2245 + // Reset sCCTimerFireCount so that we run forgetSkippable 1.2246 + // often enough before CC. Because of reduced ccDelay 1.2247 + // forgetSkippable will be called just a few times. 1.2248 + // NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling 1.2249 + // forgetSkippable and CycleCollectNow eventually. 1.2250 + sCCTimerFireCount = 0; 1.2251 + sCCLockedOutTime = now; 1.2252 + return; 1.2253 + } 1.2254 + if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) { 1.2255 + return; 1.2256 + } 1.2257 + } 1.2258 + 1.2259 + ++sCCTimerFireCount; 1.2260 + 1.2261 + // During early timer fires, we only run forgetSkippable. During the first 1.2262 + // late timer fire, we decide if we are going to have a second and final 1.2263 + // late timer fire, where we may begin to run the CC. Should run at least one 1.2264 + // early timer fire to allow cleanup before the CC. 1.2265 + int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1); 1.2266 + bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires; 1.2267 + uint32_t suspected = nsCycleCollector_suspectedCount(); 1.2268 + if (isLateTimerFire && ShouldTriggerCC(suspected)) { 1.2269 + if (sCCTimerFireCount == numEarlyTimerFires + 1) { 1.2270 + FireForgetSkippable(suspected, true); 1.2271 + if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) { 1.2272 + // Our efforts to avoid a CC have failed, so we return to let the 1.2273 + // timer fire once more to trigger a CC. 1.2274 + return; 1.2275 + } 1.2276 + } else { 1.2277 + // We are in the final timer fire and still meet the conditions for 1.2278 + // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if 1.2279 + // any because that will allow us to include the GC time in the CC pause. 1.2280 + nsJSContext::RunCycleCollectorSlice(); 1.2281 + } 1.2282 + } else if ((sPreviousSuspectedCount + 100) <= suspected) { 1.2283 + // Only do a forget skippable if there are more than a few new objects. 1.2284 + FireForgetSkippable(suspected, false); 1.2285 + } 1.2286 + 1.2287 + if (isLateTimerFire) { 1.2288 + ccDelay = TimeToNextCC(); 1.2289 + 1.2290 + // We have either just run the CC or decided we don't want to run the CC 1.2291 + // next time, so kill the timer. 1.2292 + sPreviousSuspectedCount = 0; 1.2293 + nsJSContext::KillCCTimer(); 1.2294 + } 1.2295 +} 1.2296 + 1.2297 +// static 1.2298 +uint32_t 1.2299 +nsJSContext::CleanupsSinceLastGC() 1.2300 +{ 1.2301 + return sCleanupsSinceLastGC; 1.2302 +} 1.2303 + 1.2304 +// static 1.2305 +void 1.2306 +nsJSContext::LoadStart() 1.2307 +{ 1.2308 + sLoadingInProgress = true; 1.2309 + ++sPendingLoadCount; 1.2310 +} 1.2311 + 1.2312 +// static 1.2313 +void 1.2314 +nsJSContext::LoadEnd() 1.2315 +{ 1.2316 + if (!sLoadingInProgress) 1.2317 + return; 1.2318 + 1.2319 + // sPendingLoadCount is not a well managed load counter (and doesn't 1.2320 + // need to be), so make sure we don't make it wrap backwards here. 1.2321 + if (sPendingLoadCount > 0) { 1.2322 + --sPendingLoadCount; 1.2323 + return; 1.2324 + } 1.2325 + 1.2326 + // Its probably a good idea to GC soon since we have finished loading. 1.2327 + sLoadingInProgress = false; 1.2328 + PokeGC(JS::gcreason::LOAD_END); 1.2329 +} 1.2330 + 1.2331 +// Only trigger expensive timers when they have been checked a number of times. 1.2332 +static bool 1.2333 +ReadyToTriggerExpensiveCollectorTimer() 1.2334 +{ 1.2335 + bool ready = kPokesBetweenExpensiveCollectorTriggers < ++sExpensiveCollectorPokes; 1.2336 + if (ready) { 1.2337 + sExpensiveCollectorPokes = 0; 1.2338 + } 1.2339 + return ready; 1.2340 +} 1.2341 + 1.2342 + 1.2343 +// Check all of the various collector timers and see if they are waiting to fire. 1.2344 +// For the synchronous collector timers, sGCTimer and sCCTimer, we only want to trigger 1.2345 +// the collection occasionally, because they are expensive. The incremental collector 1.2346 +// timers, sInterSliceGCTimer and sICCTimer, are fast and need to be run many times, so 1.2347 +// always run their corresponding timer. 1.2348 + 1.2349 +// This does not check sFullGCTimer, as that's an even more expensive collector we run 1.2350 +// on a long timer. 1.2351 + 1.2352 +// static 1.2353 +void 1.2354 +nsJSContext::RunNextCollectorTimer() 1.2355 +{ 1.2356 + if (sShuttingDown) { 1.2357 + return; 1.2358 + } 1.2359 + 1.2360 + if (sGCTimer) { 1.2361 + if (ReadyToTriggerExpensiveCollectorTimer()) { 1.2362 + GCTimerFired(nullptr, reinterpret_cast<void *>(JS::gcreason::DOM_WINDOW_UTILS)); 1.2363 + } 1.2364 + return; 1.2365 + } 1.2366 + 1.2367 + if (sInterSliceGCTimer) { 1.2368 + InterSliceGCTimerFired(nullptr, nullptr); 1.2369 + return; 1.2370 + } 1.2371 + 1.2372 + // Check the CC timers after the GC timers, because the CC timers won't do 1.2373 + // anything if a GC is in progress. 1.2374 + MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out."); 1.2375 + 1.2376 + if (sCCTimer) { 1.2377 + if (ReadyToTriggerExpensiveCollectorTimer()) { 1.2378 + CCTimerFired(nullptr, nullptr); 1.2379 + } 1.2380 + return; 1.2381 + } 1.2382 + 1.2383 + if (sICCTimer) { 1.2384 + ICCTimerFired(nullptr, nullptr); 1.2385 + return; 1.2386 + } 1.2387 +} 1.2388 + 1.2389 +// static 1.2390 +void 1.2391 +nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay) 1.2392 +{ 1.2393 + if (sGCTimer || sInterSliceGCTimer || sShuttingDown) { 1.2394 + // There's already a timer for GC'ing, just return 1.2395 + return; 1.2396 + } 1.2397 + 1.2398 + if (sCCTimer) { 1.2399 + // Make sure CC is called... 1.2400 + sNeedsFullCC = true; 1.2401 + // and GC after it. 1.2402 + sNeedsGCAfterCC = true; 1.2403 + return; 1.2404 + } 1.2405 + 1.2406 + if (sICCTimer) { 1.2407 + // Make sure GC is called after the current CC completes. 1.2408 + // No need to set sNeedsFullCC because we are currently running a CC. 1.2409 + sNeedsGCAfterCC = true; 1.2410 + return; 1.2411 + } 1.2412 + 1.2413 + CallCreateInstance("@mozilla.org/timer;1", &sGCTimer); 1.2414 + 1.2415 + if (!sGCTimer) { 1.2416 + // Failed to create timer (probably because we're in XPCOM shutdown) 1.2417 + return; 1.2418 + } 1.2419 + 1.2420 + static bool first = true; 1.2421 + 1.2422 + sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason), 1.2423 + aDelay 1.2424 + ? aDelay 1.2425 + : (first 1.2426 + ? NS_FIRST_GC_DELAY 1.2427 + : NS_GC_DELAY), 1.2428 + nsITimer::TYPE_ONE_SHOT); 1.2429 + 1.2430 + first = false; 1.2431 +} 1.2432 + 1.2433 +// static 1.2434 +void 1.2435 +nsJSContext::PokeShrinkGCBuffers() 1.2436 +{ 1.2437 + if (sShrinkGCBuffersTimer || sShuttingDown) { 1.2438 + return; 1.2439 + } 1.2440 + 1.2441 + CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer); 1.2442 + 1.2443 + if (!sShrinkGCBuffersTimer) { 1.2444 + // Failed to create timer (probably because we're in XPCOM shutdown) 1.2445 + return; 1.2446 + } 1.2447 + 1.2448 + sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nullptr, 1.2449 + NS_SHRINK_GC_BUFFERS_DELAY, 1.2450 + nsITimer::TYPE_ONE_SHOT); 1.2451 +} 1.2452 + 1.2453 +// static 1.2454 +void 1.2455 +nsJSContext::MaybePokeCC() 1.2456 +{ 1.2457 + if (sCCTimer || sICCTimer || sShuttingDown || !sHasRunGC) { 1.2458 + return; 1.2459 + } 1.2460 + 1.2461 + if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) { 1.2462 + sCCTimerFireCount = 0; 1.2463 + CallCreateInstance("@mozilla.org/timer;1", &sCCTimer); 1.2464 + if (!sCCTimer) { 1.2465 + return; 1.2466 + } 1.2467 + // We can kill some objects before running forgetSkippable. 1.2468 + nsCycleCollector_dispatchDeferredDeletion(); 1.2469 + 1.2470 + sCCTimer->InitWithFuncCallback(CCTimerFired, nullptr, 1.2471 + NS_CC_SKIPPABLE_DELAY, 1.2472 + nsITimer::TYPE_REPEATING_SLACK); 1.2473 + } 1.2474 +} 1.2475 + 1.2476 +//static 1.2477 +void 1.2478 +nsJSContext::KillGCTimer() 1.2479 +{ 1.2480 + if (sGCTimer) { 1.2481 + sGCTimer->Cancel(); 1.2482 + NS_RELEASE(sGCTimer); 1.2483 + } 1.2484 +} 1.2485 + 1.2486 +void 1.2487 +nsJSContext::KillFullGCTimer() 1.2488 +{ 1.2489 + if (sFullGCTimer) { 1.2490 + sFullGCTimer->Cancel(); 1.2491 + NS_RELEASE(sFullGCTimer); 1.2492 + } 1.2493 +} 1.2494 + 1.2495 +void 1.2496 +nsJSContext::KillInterSliceGCTimer() 1.2497 +{ 1.2498 + if (sInterSliceGCTimer) { 1.2499 + sInterSliceGCTimer->Cancel(); 1.2500 + NS_RELEASE(sInterSliceGCTimer); 1.2501 + } 1.2502 +} 1.2503 + 1.2504 +//static 1.2505 +void 1.2506 +nsJSContext::KillShrinkGCBuffersTimer() 1.2507 +{ 1.2508 + if (sShrinkGCBuffersTimer) { 1.2509 + sShrinkGCBuffersTimer->Cancel(); 1.2510 + NS_RELEASE(sShrinkGCBuffersTimer); 1.2511 + } 1.2512 +} 1.2513 + 1.2514 +//static 1.2515 +void 1.2516 +nsJSContext::KillCCTimer() 1.2517 +{ 1.2518 + sCCLockedOutTime = 0; 1.2519 + if (sCCTimer) { 1.2520 + sCCTimer->Cancel(); 1.2521 + NS_RELEASE(sCCTimer); 1.2522 + } 1.2523 +} 1.2524 + 1.2525 +//static 1.2526 +void 1.2527 +nsJSContext::KillICCTimer() 1.2528 +{ 1.2529 + sCCLockedOutTime = 0; 1.2530 + 1.2531 + if (sICCTimer) { 1.2532 + sICCTimer->Cancel(); 1.2533 + NS_RELEASE(sICCTimer); 1.2534 + } 1.2535 +} 1.2536 + 1.2537 +void 1.2538 +nsJSContext::GC(JS::gcreason::Reason aReason) 1.2539 +{ 1.2540 + PokeGC(aReason); 1.2541 +} 1.2542 + 1.2543 +class NotifyGCEndRunnable : public nsRunnable 1.2544 +{ 1.2545 + nsString mMessage; 1.2546 + 1.2547 +public: 1.2548 + NotifyGCEndRunnable(const nsString& aMessage) : mMessage(aMessage) {} 1.2549 + 1.2550 + NS_DECL_NSIRUNNABLE 1.2551 +}; 1.2552 + 1.2553 +NS_IMETHODIMP 1.2554 +NotifyGCEndRunnable::Run() 1.2555 +{ 1.2556 + MOZ_ASSERT(NS_IsMainThread()); 1.2557 + 1.2558 + nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); 1.2559 + if (!observerService) { 1.2560 + return NS_OK; 1.2561 + } 1.2562 + 1.2563 + const jschar oomMsg[3] = { '{', '}', 0 }; 1.2564 + const jschar *toSend = mMessage.get() ? mMessage.get() : oomMsg; 1.2565 + observerService->NotifyObservers(nullptr, "garbage-collection-statistics", toSend); 1.2566 + 1.2567 + return NS_OK; 1.2568 +} 1.2569 + 1.2570 +static void 1.2571 +DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescription &aDesc) 1.2572 +{ 1.2573 + NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread"); 1.2574 + 1.2575 + if (aProgress == JS::GC_CYCLE_END) { 1.2576 + PRTime delta = GetCollectionTimeDelta(); 1.2577 + 1.2578 + if (sPostGCEventsToConsole) { 1.2579 + NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) "); 1.2580 + nsString prefix, gcstats; 1.2581 + gcstats.Adopt(aDesc.formatMessage(aRt)); 1.2582 + prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(), 1.2583 + double(delta) / PR_USEC_PER_SEC)); 1.2584 + nsString msg = prefix + gcstats; 1.2585 + nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.2586 + if (cs) { 1.2587 + cs->LogStringMessage(msg.get()); 1.2588 + } 1.2589 + } 1.2590 + 1.2591 + if (sPostGCEventsToObserver) { 1.2592 + nsString json; 1.2593 + json.Adopt(aDesc.formatJSON(aRt, PR_Now())); 1.2594 + nsRefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(json); 1.2595 + NS_DispatchToMainThread(notify); 1.2596 + } 1.2597 + } 1.2598 + 1.2599 + // Prevent cycle collections and shrinking during incremental GC. 1.2600 + if (aProgress == JS::GC_CYCLE_BEGIN) { 1.2601 + sCCLockedOut = true; 1.2602 + nsJSContext::KillShrinkGCBuffersTimer(); 1.2603 + } else if (aProgress == JS::GC_CYCLE_END) { 1.2604 + sCCLockedOut = false; 1.2605 + } 1.2606 + 1.2607 + // The GC has more work to do, so schedule another GC slice. 1.2608 + if (aProgress == JS::GC_SLICE_END) { 1.2609 + nsJSContext::KillInterSliceGCTimer(); 1.2610 + if (!sShuttingDown) { 1.2611 + CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer); 1.2612 + sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired, 1.2613 + nullptr, 1.2614 + NS_INTERSLICE_GC_DELAY, 1.2615 + nsITimer::TYPE_ONE_SHOT); 1.2616 + } 1.2617 + } 1.2618 + 1.2619 + if (aProgress == JS::GC_CYCLE_END) { 1.2620 + // May need to kill the inter-slice GC timer 1.2621 + nsJSContext::KillInterSliceGCTimer(); 1.2622 + 1.2623 + sCCollectedWaitingForGC = 0; 1.2624 + sLikelyShortLivingObjectsNeedingGC = 0; 1.2625 + sCleanupsSinceLastGC = 0; 1.2626 + sNeedsFullCC = true; 1.2627 + sHasRunGC = true; 1.2628 + nsJSContext::MaybePokeCC(); 1.2629 + 1.2630 + if (aDesc.isCompartment_) { 1.2631 + if (!sFullGCTimer && !sShuttingDown) { 1.2632 + CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer); 1.2633 + JS::gcreason::Reason reason = JS::gcreason::FULL_GC_TIMER; 1.2634 + sFullGCTimer->InitWithFuncCallback(FullGCTimerFired, 1.2635 + reinterpret_cast<void *>(reason), 1.2636 + NS_FULL_GC_DELAY, 1.2637 + nsITimer::TYPE_ONE_SHOT); 1.2638 + } 1.2639 + } else { 1.2640 + nsJSContext::KillFullGCTimer(); 1.2641 + 1.2642 + // Avoid shrinking during heavy activity, which is suggested by 1.2643 + // compartment GC. 1.2644 + nsJSContext::PokeShrinkGCBuffers(); 1.2645 + } 1.2646 + } 1.2647 + 1.2648 + if ((aProgress == JS::GC_SLICE_END || aProgress == JS::GC_CYCLE_END) && 1.2649 + ShouldTriggerCC(nsCycleCollector_suspectedCount())) { 1.2650 + nsCycleCollector_dispatchDeferredDeletion(); 1.2651 + } 1.2652 + 1.2653 + if (sPrevGCSliceCallback) 1.2654 + (*sPrevGCSliceCallback)(aRt, aProgress, aDesc); 1.2655 +} 1.2656 + 1.2657 +void 1.2658 +nsJSContext::ReportPendingException() 1.2659 +{ 1.2660 + if (mIsInitialized) { 1.2661 + nsJSUtils::ReportPendingException(mContext); 1.2662 + } 1.2663 +} 1.2664 + 1.2665 +void 1.2666 +nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) 1.2667 +{ 1.2668 + mWindowProxy = aWindowProxy; 1.2669 +} 1.2670 + 1.2671 +JSObject* 1.2672 +nsJSContext::GetWindowProxy() 1.2673 +{ 1.2674 + JSObject* windowProxy = GetWindowProxyPreserveColor(); 1.2675 + if (windowProxy) { 1.2676 + JS::ExposeObjectToActiveJS(windowProxy); 1.2677 + } 1.2678 + 1.2679 + return windowProxy; 1.2680 +} 1.2681 + 1.2682 +JSObject* 1.2683 +nsJSContext::GetWindowProxyPreserveColor() 1.2684 +{ 1.2685 + return mWindowProxy; 1.2686 +} 1.2687 + 1.2688 +void 1.2689 +nsJSContext::LikelyShortLivingObjectCreated() 1.2690 +{ 1.2691 + ++sLikelyShortLivingObjectsNeedingGC; 1.2692 +} 1.2693 + 1.2694 +void 1.2695 +mozilla::dom::StartupJSEnvironment() 1.2696 +{ 1.2697 + // initialize all our statics, so that we can restart XPCOM 1.2698 + sGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr; 1.2699 + sCCLockedOut = false; 1.2700 + sCCLockedOutTime = 0; 1.2701 + sLastCCEndTime = TimeStamp(); 1.2702 + sHasRunGC = false; 1.2703 + sPendingLoadCount = 0; 1.2704 + sLoadingInProgress = false; 1.2705 + sCCollectedWaitingForGC = 0; 1.2706 + sLikelyShortLivingObjectsNeedingGC = 0; 1.2707 + sPostGCEventsToConsole = false; 1.2708 + sNeedsFullCC = false; 1.2709 + sNeedsGCAfterCC = false; 1.2710 + gNameSpaceManager = nullptr; 1.2711 + sRuntimeService = nullptr; 1.2712 + sRuntime = nullptr; 1.2713 + sIsInitialized = false; 1.2714 + sDidShutdown = false; 1.2715 + sShuttingDown = false; 1.2716 + sContextCount = 0; 1.2717 + sSecurityManager = nullptr; 1.2718 + gCCStats.Clear(); 1.2719 + sExpensiveCollectorPokes = 0; 1.2720 +} 1.2721 + 1.2722 +static void 1.2723 +ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure) 1.2724 +{ 1.2725 + bool reportAll = Preferences::GetBool(aPrefName, false); 1.2726 + nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll); 1.2727 +} 1.2728 + 1.2729 +static void 1.2730 +SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure) 1.2731 +{ 1.2732 + int32_t highwatermark = Preferences::GetInt(aPrefName, 128); 1.2733 + 1.2734 + JS_SetGCParameter(sRuntime, JSGC_MAX_MALLOC_BYTES, 1.2735 + highwatermark * 1024L * 1024L); 1.2736 +} 1.2737 + 1.2738 +static void 1.2739 +SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure) 1.2740 +{ 1.2741 + int32_t pref = Preferences::GetInt(aPrefName, -1); 1.2742 + // handle overflow and negative pref values 1.2743 + uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024; 1.2744 + JS_SetGCParameter(sRuntime, JSGC_MAX_BYTES, max); 1.2745 +} 1.2746 + 1.2747 +static void 1.2748 +SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure) 1.2749 +{ 1.2750 + bool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment"); 1.2751 + bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental"); 1.2752 + JSGCMode mode; 1.2753 + if (enableIncrementalGC) { 1.2754 + mode = JSGC_MODE_INCREMENTAL; 1.2755 + } else if (enableCompartmentGC) { 1.2756 + mode = JSGC_MODE_COMPARTMENT; 1.2757 + } else { 1.2758 + mode = JSGC_MODE_GLOBAL; 1.2759 + } 1.2760 + JS_SetGCParameter(sRuntime, JSGC_MODE, mode); 1.2761 +} 1.2762 + 1.2763 +static void 1.2764 +SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure) 1.2765 +{ 1.2766 + int32_t pref = Preferences::GetInt(aPrefName, -1); 1.2767 + // handle overflow and negative pref values 1.2768 + if (pref > 0 && pref < 100000) 1.2769 + JS_SetGCParameter(sRuntime, JSGC_SLICE_TIME_BUDGET, pref); 1.2770 +} 1.2771 + 1.2772 +static void 1.2773 +SetMemoryGCPrefChangedCallback(const char* aPrefName, void* aClosure) 1.2774 +{ 1.2775 + int32_t pref = Preferences::GetInt(aPrefName, -1); 1.2776 + // handle overflow and negative pref values 1.2777 + if (pref >= 0 && pref < 10000) 1.2778 + JS_SetGCParameter(sRuntime, (JSGCParamKey)(intptr_t)aClosure, pref); 1.2779 +} 1.2780 + 1.2781 +static void 1.2782 +SetMemoryGCDynamicHeapGrowthPrefChangedCallback(const char* aPrefName, void* aClosure) 1.2783 +{ 1.2784 + bool pref = Preferences::GetBool(aPrefName); 1.2785 + JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_HEAP_GROWTH, pref); 1.2786 +} 1.2787 + 1.2788 +static void 1.2789 +SetMemoryGCDynamicMarkSlicePrefChangedCallback(const char* aPrefName, void* aClosure) 1.2790 +{ 1.2791 + bool pref = Preferences::GetBool(aPrefName); 1.2792 + JS_SetGCParameter(sRuntime, JSGC_DYNAMIC_MARK_SLICE, pref); 1.2793 +} 1.2794 + 1.2795 +static void 1.2796 +SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure) 1.2797 +{ 1.2798 + bool pref = Preferences::GetBool(aPrefName); 1.2799 + sIncrementalCC = pref; 1.2800 +} 1.2801 + 1.2802 +JSObject* 1.2803 +NS_DOMReadStructuredClone(JSContext* cx, 1.2804 + JSStructuredCloneReader* reader, 1.2805 + uint32_t tag, 1.2806 + uint32_t data, 1.2807 + void* closure) 1.2808 +{ 1.2809 + if (tag == SCTAG_DOM_IMAGEDATA) { 1.2810 + // Read the information out of the stream. 1.2811 + uint32_t width, height; 1.2812 + JS::Rooted<JS::Value> dataArray(cx); 1.2813 + if (!JS_ReadUint32Pair(reader, &width, &height) || 1.2814 + !JS_ReadTypedArray(reader, &dataArray)) { 1.2815 + return nullptr; 1.2816 + } 1.2817 + MOZ_ASSERT(dataArray.isObject()); 1.2818 + 1.2819 + // Protect the result from a moving GC in ~nsRefPtr. 1.2820 + JS::Rooted<JSObject*> result(cx); 1.2821 + { 1.2822 + // Construct the ImageData. 1.2823 + nsRefPtr<ImageData> imageData = new ImageData(width, height, 1.2824 + dataArray.toObject()); 1.2825 + // Wrap it in a JS::Value. 1.2826 + result = imageData->WrapObject(cx); 1.2827 + } 1.2828 + return result; 1.2829 + } 1.2830 + 1.2831 + // Don't know what this is. Bail. 1.2832 + xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR); 1.2833 + return nullptr; 1.2834 +} 1.2835 + 1.2836 +bool 1.2837 +NS_DOMWriteStructuredClone(JSContext* cx, 1.2838 + JSStructuredCloneWriter* writer, 1.2839 + JS::Handle<JSObject*> obj, 1.2840 + void *closure) 1.2841 +{ 1.2842 + ImageData* imageData; 1.2843 + nsresult rv = UNWRAP_OBJECT(ImageData, obj, imageData); 1.2844 + if (NS_FAILED(rv)) { 1.2845 + // Don't know what this is. Bail. 1.2846 + xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR); 1.2847 + return false; 1.2848 + } 1.2849 + 1.2850 + // Prepare the ImageData internals. 1.2851 + uint32_t width = imageData->Width(); 1.2852 + uint32_t height = imageData->Height(); 1.2853 + JS::Rooted<JSObject*> dataArray(cx, imageData->GetDataObject()); 1.2854 + 1.2855 + // Write the internals to the stream. 1.2856 + JSAutoCompartment ac(cx, dataArray); 1.2857 + JS::Rooted<JS::Value> arrayValue(cx, JS::ObjectValue(*dataArray)); 1.2858 + return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) && 1.2859 + JS_WriteUint32Pair(writer, width, height) && 1.2860 + JS_WriteTypedArray(writer, arrayValue); 1.2861 +} 1.2862 + 1.2863 +void 1.2864 +NS_DOMStructuredCloneError(JSContext* cx, 1.2865 + uint32_t errorid) 1.2866 +{ 1.2867 + // We don't currently support any extensions to structured cloning. 1.2868 + xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR); 1.2869 +} 1.2870 + 1.2871 +static bool 1.2872 +AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal, 1.2873 + const jschar* aBegin, 1.2874 + const jschar* aLimit, 1.2875 + size_t* aSize, 1.2876 + const uint8_t** aMemory, 1.2877 + intptr_t *aHandle) 1.2878 +{ 1.2879 + nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aGlobal); 1.2880 + return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory, 1.2881 + aHandle); 1.2882 +} 1.2883 + 1.2884 +static bool 1.2885 +AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal, 1.2886 + bool aInstalled, 1.2887 + const jschar* aBegin, 1.2888 + const jschar* aEnd, 1.2889 + size_t aSize, 1.2890 + uint8_t** aMemory, 1.2891 + intptr_t* aHandle) 1.2892 +{ 1.2893 + nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aGlobal); 1.2894 + return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd, 1.2895 + aSize, aMemory, aHandle); 1.2896 +} 1.2897 + 1.2898 +static void 1.2899 +OnLargeAllocationFailure() 1.2900 +{ 1.2901 + nsCOMPtr<nsIObserverService> os = 1.2902 + mozilla::services::GetObserverService(); 1.2903 + if (os) { 1.2904 + os->NotifyObservers(nullptr, "memory-pressure", MOZ_UTF16("heap-minimize")); 1.2905 + } 1.2906 +} 1.2907 + 1.2908 +static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); 1.2909 + 1.2910 +void 1.2911 +nsJSContext::EnsureStatics() 1.2912 +{ 1.2913 + if (sIsInitialized) { 1.2914 + if (!nsContentUtils::XPConnect()) { 1.2915 + MOZ_CRASH(); 1.2916 + } 1.2917 + return; 1.2918 + } 1.2919 + 1.2920 + nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, 1.2921 + &sSecurityManager); 1.2922 + if (NS_FAILED(rv)) { 1.2923 + MOZ_CRASH(); 1.2924 + } 1.2925 + 1.2926 + rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService); 1.2927 + if (NS_FAILED(rv)) { 1.2928 + MOZ_CRASH(); 1.2929 + } 1.2930 + 1.2931 + rv = sRuntimeService->GetRuntime(&sRuntime); 1.2932 + if (NS_FAILED(rv)) { 1.2933 + MOZ_CRASH(); 1.2934 + } 1.2935 + 1.2936 + // Let's make sure that our main thread is the same as the xpcom main thread. 1.2937 + MOZ_ASSERT(NS_IsMainThread()); 1.2938 + 1.2939 + sPrevGCSliceCallback = JS::SetGCSliceCallback(sRuntime, DOMGCSliceCallback); 1.2940 + 1.2941 + // Set up the structured clone callbacks. 1.2942 + static JSStructuredCloneCallbacks cloneCallbacks = { 1.2943 + NS_DOMReadStructuredClone, 1.2944 + NS_DOMWriteStructuredClone, 1.2945 + NS_DOMStructuredCloneError, 1.2946 + nullptr, 1.2947 + nullptr, 1.2948 + nullptr 1.2949 + }; 1.2950 + JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks); 1.2951 + 1.2952 + static js::DOMCallbacks DOMcallbacks = { 1.2953 + InstanceClassHasProtoAtDepth 1.2954 + }; 1.2955 + SetDOMCallbacks(sRuntime, &DOMcallbacks); 1.2956 + 1.2957 + // Set up the asm.js cache callbacks 1.2958 + static JS::AsmJSCacheOps asmJSCacheOps = { 1.2959 + AsmJSCacheOpenEntryForRead, 1.2960 + asmjscache::CloseEntryForRead, 1.2961 + AsmJSCacheOpenEntryForWrite, 1.2962 + asmjscache::CloseEntryForWrite, 1.2963 + asmjscache::GetBuildId 1.2964 + }; 1.2965 + JS::SetAsmJSCacheOps(sRuntime, &asmJSCacheOps); 1.2966 + 1.2967 + JS::SetLargeAllocationFailureCallback(sRuntime, OnLargeAllocationFailure); 1.2968 + 1.2969 + // Set these global xpconnect options... 1.2970 + Preferences::RegisterCallbackAndCall(ReportAllJSExceptionsPrefChangedCallback, 1.2971 + "dom.report_all_js_exceptions"); 1.2972 + 1.2973 + Preferences::RegisterCallbackAndCall(SetMemoryHighWaterMarkPrefChangedCallback, 1.2974 + "javascript.options.mem.high_water_mark"); 1.2975 + 1.2976 + Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback, 1.2977 + "javascript.options.mem.max"); 1.2978 + 1.2979 + Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback, 1.2980 + "javascript.options.mem.gc_per_compartment"); 1.2981 + 1.2982 + Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback, 1.2983 + "javascript.options.mem.gc_incremental"); 1.2984 + 1.2985 + Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback, 1.2986 + "javascript.options.mem.gc_incremental_slice_ms"); 1.2987 + 1.2988 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.2989 + "javascript.options.mem.gc_high_frequency_time_limit_ms", 1.2990 + (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT); 1.2991 + 1.2992 + Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicMarkSlicePrefChangedCallback, 1.2993 + "javascript.options.mem.gc_dynamic_mark_slice"); 1.2994 + 1.2995 + Preferences::RegisterCallbackAndCall(SetMemoryGCDynamicHeapGrowthPrefChangedCallback, 1.2996 + "javascript.options.mem.gc_dynamic_heap_growth"); 1.2997 + 1.2998 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.2999 + "javascript.options.mem.gc_low_frequency_heap_growth", 1.3000 + (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH); 1.3001 + 1.3002 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.3003 + "javascript.options.mem.gc_high_frequency_heap_growth_min", 1.3004 + (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN); 1.3005 + 1.3006 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.3007 + "javascript.options.mem.gc_high_frequency_heap_growth_max", 1.3008 + (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX); 1.3009 + 1.3010 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.3011 + "javascript.options.mem.gc_high_frequency_low_limit_mb", 1.3012 + (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT); 1.3013 + 1.3014 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.3015 + "javascript.options.mem.gc_high_frequency_high_limit_mb", 1.3016 + (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT); 1.3017 + 1.3018 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.3019 + "javascript.options.mem.gc_allocation_threshold_mb", 1.3020 + (void *)JSGC_ALLOCATION_THRESHOLD); 1.3021 + 1.3022 + Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback, 1.3023 + "javascript.options.mem.gc_decommit_threshold_mb", 1.3024 + (void *)JSGC_DECOMMIT_THRESHOLD); 1.3025 + 1.3026 + Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback, 1.3027 + "dom.cycle_collector.incremental"); 1.3028 + 1.3029 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.3030 + if (!obs) { 1.3031 + MOZ_CRASH(); 1.3032 + } 1.3033 + 1.3034 + Preferences::AddBoolVarCache(&sGCOnMemoryPressure, 1.3035 + "javascript.options.gc_on_memory_pressure", 1.3036 + true); 1.3037 + 1.3038 + nsIObserver* observer = new nsJSEnvironmentObserver(); 1.3039 + obs->AddObserver(observer, "memory-pressure", false); 1.3040 + obs->AddObserver(observer, "quit-application", false); 1.3041 + 1.3042 + // Bug 907848 - We need to explicitly get the nsIDOMScriptObjectFactory 1.3043 + // service in order to force its constructor to run, which registers a 1.3044 + // shutdown observer. It would be nice to make this more explicit and less 1.3045 + // side-effect-y. 1.3046 + nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID); 1.3047 + if (!factory) { 1.3048 + MOZ_CRASH(); 1.3049 + } 1.3050 + 1.3051 + sIsInitialized = true; 1.3052 +} 1.3053 + 1.3054 +nsScriptNameSpaceManager* 1.3055 +mozilla::dom::GetNameSpaceManager() 1.3056 +{ 1.3057 + if (sDidShutdown) 1.3058 + return nullptr; 1.3059 + 1.3060 + if (!gNameSpaceManager) { 1.3061 + gNameSpaceManager = new nsScriptNameSpaceManager; 1.3062 + NS_ADDREF(gNameSpaceManager); 1.3063 + 1.3064 + nsresult rv = gNameSpaceManager->Init(); 1.3065 + NS_ENSURE_SUCCESS(rv, nullptr); 1.3066 + } 1.3067 + 1.3068 + return gNameSpaceManager; 1.3069 +} 1.3070 + 1.3071 +void 1.3072 +mozilla::dom::ShutdownJSEnvironment() 1.3073 +{ 1.3074 + KillTimers(); 1.3075 + 1.3076 + NS_IF_RELEASE(gNameSpaceManager); 1.3077 + 1.3078 + if (!sContextCount) { 1.3079 + // We're being shutdown, and there are no more contexts 1.3080 + // alive, release the JS runtime service and the security manager. 1.3081 + 1.3082 + NS_IF_RELEASE(sRuntimeService); 1.3083 + NS_IF_RELEASE(sSecurityManager); 1.3084 + } 1.3085 + 1.3086 + sShuttingDown = true; 1.3087 + sDidShutdown = true; 1.3088 +} 1.3089 + 1.3090 +// A fast-array class for JS. This class supports both nsIJSScriptArray and 1.3091 +// nsIArray. If it is JS itself providing and consuming this class, all work 1.3092 +// can be done via nsIJSScriptArray, and avoid the conversion of elements 1.3093 +// to/from nsISupports. 1.3094 +// When consumed by non-JS (eg, another script language), conversion is done 1.3095 +// on-the-fly. 1.3096 +class nsJSArgArray MOZ_FINAL : public nsIJSArgArray { 1.3097 +public: 1.3098 + nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv, 1.3099 + nsresult *prv); 1.3100 + ~nsJSArgArray(); 1.3101 + // nsISupports 1.3102 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.3103 + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray, 1.3104 + nsIJSArgArray) 1.3105 + 1.3106 + // nsIArray 1.3107 + NS_DECL_NSIARRAY 1.3108 + 1.3109 + // nsIJSArgArray 1.3110 + nsresult GetArgs(uint32_t *argc, void **argv); 1.3111 + 1.3112 + void ReleaseJSObjects(); 1.3113 + 1.3114 +protected: 1.3115 + JSContext *mContext; 1.3116 + JS::Heap<JS::Value> *mArgv; 1.3117 + uint32_t mArgc; 1.3118 +}; 1.3119 + 1.3120 +nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv, 1.3121 + nsresult *prv) : 1.3122 + mContext(aContext), 1.3123 + mArgv(nullptr), 1.3124 + mArgc(argc) 1.3125 +{ 1.3126 + // copy the array - we don't know its lifetime, and ours is tied to xpcom 1.3127 + // refcounting. 1.3128 + if (argc) { 1.3129 + static const fallible_t fallible = fallible_t(); 1.3130 + mArgv = new (fallible) JS::Heap<JS::Value>[argc]; 1.3131 + if (!mArgv) { 1.3132 + *prv = NS_ERROR_OUT_OF_MEMORY; 1.3133 + return; 1.3134 + } 1.3135 + } 1.3136 + 1.3137 + // Callers are allowed to pass in a null argv even for argc > 0. They can 1.3138 + // then use GetArgs to initialize the values. 1.3139 + if (argv) { 1.3140 + for (uint32_t i = 0; i < argc; ++i) 1.3141 + mArgv[i] = argv[i]; 1.3142 + } 1.3143 + 1.3144 + if (argc > 0) { 1.3145 + mozilla::HoldJSObjects(this); 1.3146 + } 1.3147 + 1.3148 + *prv = NS_OK; 1.3149 +} 1.3150 + 1.3151 +nsJSArgArray::~nsJSArgArray() 1.3152 +{ 1.3153 + ReleaseJSObjects(); 1.3154 +} 1.3155 + 1.3156 +void 1.3157 +nsJSArgArray::ReleaseJSObjects() 1.3158 +{ 1.3159 + if (mArgv) { 1.3160 + delete [] mArgv; 1.3161 + } 1.3162 + if (mArgc > 0) { 1.3163 + mArgc = 0; 1.3164 + mozilla::DropJSObjects(this); 1.3165 + } 1.3166 +} 1.3167 + 1.3168 +// QueryInterface implementation for nsJSArgArray 1.3169 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray) 1.3170 + 1.3171 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray) 1.3172 + tmp->ReleaseJSObjects(); 1.3173 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.3174 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray) 1.3175 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.3176 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.3177 + 1.3178 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray) 1.3179 + if (tmp->mArgv) { 1.3180 + for (uint32_t i = 0; i < tmp->mArgc; ++i) { 1.3181 + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mArgv[i]) 1.3182 + } 1.3183 + } 1.3184 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.3185 + 1.3186 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray) 1.3187 + NS_INTERFACE_MAP_ENTRY(nsIArray) 1.3188 + NS_INTERFACE_MAP_ENTRY(nsIJSArgArray) 1.3189 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray) 1.3190 +NS_INTERFACE_MAP_END 1.3191 + 1.3192 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray) 1.3193 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray) 1.3194 + 1.3195 +nsresult 1.3196 +nsJSArgArray::GetArgs(uint32_t *argc, void **argv) 1.3197 +{ 1.3198 + *argv = (void *)mArgv; 1.3199 + *argc = mArgc; 1.3200 + return NS_OK; 1.3201 +} 1.3202 + 1.3203 +// nsIArray impl 1.3204 +NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t *aLength) 1.3205 +{ 1.3206 + *aLength = mArgc; 1.3207 + return NS_OK; 1.3208 +} 1.3209 + 1.3210 +/* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */ 1.3211 +NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID & uuid, void * *result) 1.3212 +{ 1.3213 + *result = nullptr; 1.3214 + if (index >= mArgc) 1.3215 + return NS_ERROR_INVALID_ARG; 1.3216 + 1.3217 + if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) { 1.3218 + // Have to copy a Heap into a Rooted to work with it. 1.3219 + JS::Rooted<JS::Value> val(mContext, mArgv[index]); 1.3220 + return nsContentUtils::XPConnect()->JSToVariant(mContext, val, 1.3221 + (nsIVariant **)result); 1.3222 + } 1.3223 + NS_WARNING("nsJSArgArray only handles nsIVariant"); 1.3224 + return NS_ERROR_NO_INTERFACE; 1.3225 +} 1.3226 + 1.3227 +/* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */ 1.3228 +NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval) 1.3229 +{ 1.3230 + return NS_ERROR_NOT_IMPLEMENTED; 1.3231 +} 1.3232 + 1.3233 +/* nsISimpleEnumerator enumerate (); */ 1.3234 +NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval) 1.3235 +{ 1.3236 + return NS_ERROR_NOT_IMPLEMENTED; 1.3237 +} 1.3238 + 1.3239 +// The factory function 1.3240 +nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc, void *argv, 1.3241 + nsIJSArgArray **aArray) 1.3242 +{ 1.3243 + nsresult rv; 1.3244 + nsJSArgArray *ret = new nsJSArgArray(aContext, argc, 1.3245 + static_cast<JS::Value *>(argv), &rv); 1.3246 + if (ret == nullptr) 1.3247 + return NS_ERROR_OUT_OF_MEMORY; 1.3248 + if (NS_FAILED(rv)) { 1.3249 + delete ret; 1.3250 + return rv; 1.3251 + } 1.3252 + return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray); 1.3253 +}