dom/base/nsJSEnvironment.cpp

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

mercurial