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