dom/base/nsJSEnvironment.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial