dom/base/nsJSEnvironment.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial