LCOV - code coverage report
Current view: top level - dom/base - nsJSEnvironment.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 1144 0.1 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       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 "nsCOMPtr.h"
      18             : #include "nsISupportsPrimitives.h"
      19             : #include "nsReadableUtils.h"
      20             : #include "nsDOMJSUtils.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 "nsAtom.h"
      33             : #include "nsContentUtils.h"
      34             : #include "mozilla/EventDispatcher.h"
      35             : #include "nsIContent.h"
      36             : #include "nsCycleCollector.h"
      37             : #include "nsXPCOMCIDInternal.h"
      38             : #include "nsIXULRuntime.h"
      39             : #include "nsTextFormatter.h"
      40             : #ifdef XP_WIN
      41             : #include <process.h>
      42             : #define getpid _getpid
      43             : #else
      44             : #include <unistd.h> // for getpid()
      45             : #endif
      46             : #include "xpcpublic.h"
      47             : 
      48             : #include "jsapi.h"
      49             : #include "js/Wrapper.h"
      50             : #include "js/SliceBudget.h"
      51             : #include "nsIArray.h"
      52             : #include "nsIObjectInputStream.h"
      53             : #include "nsIObjectOutputStream.h"
      54             : #include "WrapperFactory.h"
      55             : #include "nsGlobalWindow.h"
      56             : #include "mozilla/AutoRestore.h"
      57             : #include "mozilla/MainThreadIdlePeriod.h"
      58             : #include "mozilla/StaticPrefs.h"
      59             : #include "mozilla/StaticPtr.h"
      60             : #include "mozilla/dom/DOMException.h"
      61             : #include "mozilla/dom/DOMExceptionBinding.h"
      62             : #include "mozilla/dom/Element.h"
      63             : #include "mozilla/dom/ErrorEvent.h"
      64             : #include "mozilla/dom/FetchUtil.h"
      65             : #include "mozilla/dom/ScriptSettings.h"
      66             : #include "mozilla/CycleCollectedJSRuntime.h"
      67             : #include "mozilla/SystemGroup.h"
      68             : #include "nsRefreshDriver.h"
      69             : #include "nsJSPrincipals.h"
      70             : 
      71             : #ifdef XP_MACOSX
      72             : // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
      73             : #undef check
      74             : #endif
      75             : #include "AccessCheck.h"
      76             : 
      77             : #include "mozilla/Logging.h"
      78             : #include "prthread.h"
      79             : 
      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/ContentEvents.h"
      87             : #include "mozilla/CycleCollectedJSContext.h"
      88             : #include "nsCycleCollectionNoteRootCallback.h"
      89             : #include "GeckoProfiler.h"
      90             : #include "mozilla/IdleTaskRunner.h"
      91             : #include "nsIDocShell.h"
      92             : #include "nsIPresShell.h"
      93             : #include "nsViewManager.h"
      94             : #include "mozilla/EventStateManager.h"
      95             : 
      96             : using namespace mozilla;
      97             : using namespace mozilla::dom;
      98             : 
      99             : const size_t gStackSize = 8192;
     100             : 
     101             : // Thank you Microsoft!
     102             : #ifdef CompareString
     103             : #undef CompareString
     104             : #endif
     105             : 
     106             : #define NS_SHRINK_GC_BUFFERS_DELAY  4000 // ms
     107             : 
     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
     111             : 
     112             : #define NS_FULL_GC_DELAY            60000 // ms
     113             : 
     114             : // Maximum amount of time that should elapse between incremental GC slices
     115             : #define NS_INTERSLICE_GC_DELAY      100 // ms
     116             : 
     117             : // The amount of time we wait between a request to CC (after GC ran)
     118             : // and doing the actual CC.
     119             : #define NS_CC_DELAY                 6000 // ms
     120             : 
     121             : #define NS_CC_SKIPPABLE_DELAY       250 // ms
     122             : 
     123             : // ForgetSkippable is usually fast, so we can use small budgets.
     124             : // This isn't a real budget but a hint to IdleTaskRunner whether there
     125             : // is enough time to call ForgetSkippable.
     126             : static const int64_t kForgetSkippableSliceDuration = 2;
     127             : 
     128             : // Maximum amount of time that should elapse between incremental CC slices
     129             : static const int64_t kICCIntersliceDelay = 64; // ms
     130             : 
     131             : // Time budget for an incremental CC slice when using timer to run it.
     132             : static const int64_t kICCSliceBudget = 3; // ms
     133             : // Minimum budget for an incremental CC slice when using idle time to run it.
     134             : static const int64_t kIdleICCSliceBudget = 2; // ms
     135             : 
     136             : // Maximum total duration for an ICC
     137             : static const uint32_t kMaxICCDuration = 2000; // ms
     138             : 
     139             : // Force a CC after this long if there's more than NS_CC_FORCED_PURPLE_LIMIT
     140             : // objects in the purple buffer.
     141             : #define NS_CC_FORCED                (2 * 60 * PR_USEC_PER_SEC) // 2 min
     142             : #define NS_CC_FORCED_PURPLE_LIMIT   10
     143             : 
     144             : // Don't allow an incremental GC to lock out the CC for too long.
     145             : #define NS_MAX_CC_LOCKEDOUT_TIME    (30 * PR_USEC_PER_SEC) // 30 seconds
     146             : 
     147             : // Trigger a CC if the purple buffer exceeds this size when we check it.
     148             : #define NS_CC_PURPLE_LIMIT          200
     149             : 
     150             : // Large value used to specify that a script should run essentially forever
     151             : #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
     152             : 
     153             : // if you add statics here, add them to the list in StartupJSEnvironment
     154             : 
     155             : static nsITimer *sGCTimer;
     156             : static nsITimer *sShrinkingGCTimer;
     157           0 : static StaticRefPtr<IdleTaskRunner> sCCRunner;
     158           0 : static StaticRefPtr<IdleTaskRunner> sICCRunner;
     159             : static nsITimer *sFullGCTimer;
     160           0 : static StaticRefPtr<IdleTaskRunner> sInterSliceGCRunner;
     161             : 
     162             : static TimeStamp sLastCCEndTime;
     163             : 
     164             : static bool sCCLockedOut;
     165             : static PRTime sCCLockedOutTime;
     166             : 
     167             : static JS::GCSliceCallback sPrevGCSliceCallback;
     168             : 
     169             : static bool sHasRunGC;
     170             : 
     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;
     179             : 
     180             : static uint32_t sCCollectedWaitingForGC;
     181             : static uint32_t sCCollectedZonesWaitingForGC;
     182             : static uint32_t sLikelyShortLivingObjectsNeedingGC;
     183             : static int32_t sCCRunnerFireCount = 0;
     184             : static uint32_t sMinForgetSkippableTime = UINT32_MAX;
     185             : static uint32_t sMaxForgetSkippableTime = 0;
     186             : static uint32_t sTotalForgetSkippableTime = 0;
     187             : static uint32_t sRemovedPurples = 0;
     188             : static uint32_t sForgetSkippableBeforeCC = 0;
     189             : static uint32_t sPreviousSuspectedCount = 0;
     190             : static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
     191             : static bool sNeedsFullCC = false;
     192             : static bool sNeedsFullGC = false;
     193             : static bool sNeedsGCAfterCC = false;
     194             : static bool sIncrementalCC = false;
     195             : static int32_t sActiveIntersliceGCBudget = 5; // ms;
     196             : 
     197             : static PRTime sFirstCollectionTime;
     198             : 
     199             : static bool sIsInitialized;
     200             : static bool sDidShutdown;
     201             : static bool sShuttingDown;
     202             : 
     203             : // nsJSEnvironmentObserver observes the user-interaction-inactive notifications
     204             : // and triggers a shrinking a garbage collection if the user is still inactive
     205             : // after NS_SHRINKING_GC_DELAY ms later, if the appropriate pref is set.
     206             : 
     207             : static bool sIsCompactingOnUserInactive = false;
     208             : 
     209             : static TimeDuration sGCUnnotifiedTotalTime;
     210             : 
     211             : static const char*
     212           0 : ProcessNameForCollectorLog()
     213             : {
     214           0 :   return XRE_GetProcessType() == GeckoProcessType_Default ?
     215           0 :     "default" : "content";
     216             : }
     217             : 
     218             : namespace xpc {
     219             : 
     220             : // This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC
     221             : // Exceptions.
     222             : //
     223             : // Note that the returned object is _not_ wrapped into the compartment of
     224             : // exceptionValue.
     225             : JSObject*
     226           0 : FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
     227             :                                    JS::HandleValue exceptionValue)
     228             : {
     229           0 :   if (!exceptionValue.isObject()) {
     230             :     return nullptr;
     231             :   }
     232             : 
     233           0 :   if (win && win->AsGlobal()->IsDying()) {
     234             :     // Pretend like we have no stack, so we don't end up keeping the global
     235             :     // alive via the stack.
     236             :     return nullptr;
     237             :   }
     238             : 
     239           0 :   JS::RootingContext* rcx = RootingCx();
     240           0 :   JS::RootedObject exceptionObject(rcx, &exceptionValue.toObject());
     241           0 :   JSObject* stackObject = JS::ExceptionStackOrNull(exceptionObject);
     242           0 :   if (stackObject) {
     243             :     return stackObject;
     244             :   }
     245             : 
     246             :   // It is not a JS Exception, try DOM Exception.
     247           0 :   RefPtr<Exception> exception;
     248           0 :   UNWRAP_OBJECT(DOMException, exceptionObject, exception);
     249           0 :   if (!exception) {
     250             :     // Not a DOM Exception, try XPC Exception.
     251           0 :     UNWRAP_OBJECT(Exception, exceptionObject, exception);
     252           0 :     if (!exception) {
     253             :       return nullptr;
     254             :     }
     255             :   }
     256             : 
     257           0 :   nsCOMPtr<nsIStackFrame> stack = exception->GetLocation();
     258           0 :   if (!stack) {
     259             :     return nullptr;
     260             :   }
     261           0 :   JS::RootedValue value(rcx);
     262           0 :   stack->GetNativeSavedFrame(&value);
     263           0 :   if (value.isObject()) {
     264           0 :     return &value.toObject();
     265             :   }
     266             :   return nullptr;
     267             : }
     268             : 
     269             : } /* namespace xpc */
     270             : 
     271             : static PRTime
     272           0 : GetCollectionTimeDelta()
     273             : {
     274           0 :   PRTime now = PR_Now();
     275           0 :   if (sFirstCollectionTime) {
     276           0 :     return now - sFirstCollectionTime;
     277             :   }
     278           0 :   sFirstCollectionTime = now;
     279           0 :   return 0;
     280             : }
     281             : 
     282             : static void
     283           0 : KillTimers()
     284             : {
     285           0 :   nsJSContext::KillGCTimer();
     286           0 :   nsJSContext::KillShrinkingGCTimer();
     287           0 :   nsJSContext::KillCCRunner();
     288           0 :   nsJSContext::KillICCRunner();
     289           0 :   nsJSContext::KillFullGCTimer();
     290           0 :   nsJSContext::KillInterSliceGCRunner();
     291           0 : }
     292             : 
     293             : // If we collected a substantial amount of cycles, poke the GC since more objects
     294             : // might be unreachable now.
     295             : static bool
     296           0 : NeedsGCAfterCC()
     297             : {
     298           0 :   return sCCollectedWaitingForGC > 250 ||
     299           0 :     sCCollectedZonesWaitingForGC > 0 ||
     300           0 :     sLikelyShortLivingObjectsNeedingGC > 2500 ||
     301           0 :     sNeedsGCAfterCC;
     302             : }
     303             : 
     304           0 : class nsJSEnvironmentObserver final : public nsIObserver
     305             : {
     306           0 :   ~nsJSEnvironmentObserver() {}
     307             : public:
     308             :   NS_DECL_ISUPPORTS
     309             :   NS_DECL_NSIOBSERVER
     310             : };
     311             : 
     312           0 : NS_IMPL_ISUPPORTS(nsJSEnvironmentObserver, nsIObserver)
     313             : 
     314             : NS_IMETHODIMP
     315           0 : nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
     316             :                                  const char16_t* aData)
     317             : {
     318           0 :   if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
     319           0 :     if (StaticPrefs::javascript_options_gc_on_memory_pressure()) {
     320           0 :       if (StringBeginsWith(nsDependentString(aData),
     321           0 :                            NS_LITERAL_STRING("low-memory-ongoing"))) {
     322             :         // Don't GC/CC if we are in an ongoing low-memory state since its very
     323             :         // slow and it likely won't help us anyway.
     324             :         return NS_OK;
     325             :       }
     326             :       nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
     327             :                                      nsJSContext::NonIncrementalGC,
     328           0 :                                      nsJSContext::ShrinkingGC);
     329           0 :       nsJSContext::CycleCollectNow();
     330           0 :       if (NeedsGCAfterCC()) {
     331             :         nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
     332             :                                        nsJSContext::NonIncrementalGC,
     333           0 :                                        nsJSContext::ShrinkingGC);
     334             :       }
     335             :     }
     336           0 :   } else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
     337           0 :     if (StaticPrefs::javascript_options_compact_on_user_inactive()) {
     338           0 :       nsJSContext::PokeShrinkingGC();
     339             :     }
     340           0 :   } else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
     341           0 :     nsJSContext::KillShrinkingGCTimer();
     342           0 :     if (sIsCompactingOnUserInactive) {
     343           0 :       AutoJSAPI jsapi;
     344           0 :       jsapi.Init();
     345           0 :       JS::AbortIncrementalGC(jsapi.cx());
     346             :     }
     347           0 :     MOZ_ASSERT(!sIsCompactingOnUserInactive);
     348           0 :   } else if (!nsCRT::strcmp(aTopic, "quit-application") ||
     349           0 :              !nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     350           0 :     sShuttingDown = true;
     351           0 :     KillTimers();
     352             :   }
     353             : 
     354             :   return NS_OK;
     355             : }
     356             : 
     357             : /****************************************************************
     358             :  ************************** AutoFree ****************************
     359             :  ****************************************************************/
     360             : 
     361             : class AutoFree {
     362             : public:
     363           0 :   explicit AutoFree(void* aPtr) : mPtr(aPtr) {
     364             :   }
     365           0 :   ~AutoFree() {
     366           0 :     if (mPtr)
     367           0 :       free(mPtr);
     368           0 :   }
     369             :   void Invalidate() {
     370             :     mPtr = 0;
     371             :   }
     372             : private:
     373             :   void *mPtr;
     374             : };
     375             : 
     376             : // A utility function for script languages to call.  Although it looks small,
     377             : // the use of nsIDocShell and nsPresContext triggers a huge number of
     378             : // dependencies that most languages would not otherwise need.
     379             : // XXXmarkh - This function is mis-placed!
     380             : bool
     381           0 : NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
     382             :                      const ErrorEventInit &aErrorEventInit,
     383             :                      nsEventStatus *aStatus)
     384             : {
     385           0 :   bool called = false;
     386           0 :   nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aScriptGlobal));
     387           0 :   nsIDocShell *docShell = win ? win->GetDocShell() : nullptr;
     388           0 :   if (docShell) {
     389           0 :     RefPtr<nsPresContext> presContext;
     390           0 :     docShell->GetPresContext(getter_AddRefs(presContext));
     391             : 
     392             :     static int32_t errorDepth; // Recursion prevention
     393           0 :     ++errorDepth;
     394             : 
     395           0 :     if (errorDepth < 2) {
     396             :       // Dispatch() must be synchronous for the recursion block
     397             :       // (errorDepth) to work.
     398             :       RefPtr<ErrorEvent> event =
     399           0 :         ErrorEvent::Constructor(nsGlobalWindowInner::Cast(win),
     400           0 :                                 NS_LITERAL_STRING("error"),
     401           0 :                                 aErrorEventInit);
     402           0 :       event->SetTrusted(true);
     403             : 
     404           0 :       EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
     405           0 :                                         aStatus);
     406           0 :       called = true;
     407             :     }
     408           0 :     --errorDepth;
     409             :   }
     410           0 :   return called;
     411             : }
     412             : 
     413           0 : class ScriptErrorEvent : public Runnable
     414             : {
     415             : public:
     416           0 :   ScriptErrorEvent(nsPIDOMWindowInner* aWindow,
     417             :                    JS::RootingContext* aRootingCx,
     418             :                    xpc::ErrorReport* aReport,
     419             :                    JS::Handle<JS::Value> aError)
     420           0 :     : mozilla::Runnable("ScriptErrorEvent")
     421             :     , mWindow(aWindow)
     422             :     , mReport(aReport)
     423           0 :     , mError(aRootingCx, aError)
     424           0 :   {}
     425             : 
     426           0 :   NS_IMETHOD Run() override
     427             :   {
     428           0 :     nsEventStatus status = nsEventStatus_eIgnore;
     429           0 :     nsPIDOMWindowInner* win = mWindow;
     430           0 :     MOZ_ASSERT(win);
     431           0 :     MOZ_ASSERT(NS_IsMainThread());
     432             :     // First, notify the DOM that we have a script error, but only if
     433             :     // our window is still the current inner.
     434           0 :     JS::RootingContext* rootingCx = RootingCx();
     435           0 :     if (win->IsCurrentInnerWindow() && win->GetDocShell() && !sHandlingScriptError) {
     436           0 :       AutoRestore<bool> recursionGuard(sHandlingScriptError);
     437           0 :       sHandlingScriptError = true;
     438             : 
     439           0 :       RefPtr<nsPresContext> presContext;
     440           0 :       win->GetDocShell()->GetPresContext(getter_AddRefs(presContext));
     441             : 
     442           0 :       RootedDictionary<ErrorEventInit> init(rootingCx);
     443           0 :       init.mCancelable = true;
     444           0 :       init.mFilename = mReport->mFileName;
     445           0 :       init.mBubbles = true;
     446             : 
     447           0 :       NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
     448           0 :       if (!mReport->mIsMuted) {
     449           0 :         init.mMessage = mReport->mErrorMsg;
     450           0 :         init.mLineno = mReport->mLineNumber;
     451           0 :         init.mColno = mReport->mColumn;
     452           0 :         init.mError = mError;
     453             :       } else {
     454           0 :         NS_WARNING("Not same origin error!");
     455           0 :         init.mMessage = xoriginMsg;
     456           0 :         init.mLineno = 0;
     457             :       }
     458             : 
     459             :       RefPtr<ErrorEvent> event =
     460           0 :         ErrorEvent::Constructor(nsGlobalWindowInner::Cast(win),
     461           0 :                                 NS_LITERAL_STRING("error"), init);
     462           0 :       event->SetTrusted(true);
     463             : 
     464           0 :       EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
     465           0 :                                         &status);
     466             :     }
     467             : 
     468           0 :     if (status != nsEventStatus_eConsumeNoDefault) {
     469             :       JS::Rooted<JSObject*> stack(rootingCx,
     470           0 :         xpc::FindExceptionStackForConsoleReport(win, mError));
     471           0 :       mReport->LogToConsoleWithStack(stack);
     472             :     }
     473             : 
     474           0 :     return NS_OK;
     475             :   }
     476             : 
     477             : private:
     478             :   nsCOMPtr<nsPIDOMWindowInner>  mWindow;
     479             :   RefPtr<xpc::ErrorReport>      mReport;
     480             :   JS::PersistentRootedValue       mError;
     481             : 
     482             :   static bool sHandlingScriptError;
     483             : };
     484             : 
     485             : bool ScriptErrorEvent::sHandlingScriptError = false;
     486             : 
     487             : // This temporarily lives here to avoid code churn. It will go away entirely
     488             : // soon.
     489             : namespace xpc {
     490             : 
     491             : void
     492           0 : DispatchScriptErrorEvent(nsPIDOMWindowInner *win, JS::RootingContext* rootingCx,
     493             :                          xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception)
     494             : {
     495           0 :   nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception));
     496           0 : }
     497             : 
     498             : } /* namespace xpc */
     499             : 
     500             : #ifdef DEBUG
     501             : // A couple of useful functions to call when you're debugging.
     502             : nsGlobalWindowInner *
     503           0 : JSObject2Win(JSObject *obj)
     504             : {
     505           0 :   return xpc::WindowOrNull(obj);
     506             : }
     507             : 
     508             : template<typename T>
     509             : void
     510           0 : PrintWinURI(T *win)
     511             : {
     512           0 :   if (!win) {
     513             :     printf("No window passed in.\n");
     514           0 :     return;
     515             :   }
     516             : 
     517           0 :   nsCOMPtr<nsIDocument> doc = win->GetExtantDoc();
     518           0 :   if (!doc) {
     519             :     printf("No document in the window.\n");
     520           0 :     return;
     521             :   }
     522             : 
     523           0 :   nsIURI *uri = doc->GetDocumentURI();
     524           0 :   if (!uri) {
     525             :     printf("Document doesn't have a URI.\n");
     526             :     return;
     527             :   }
     528             : 
     529           0 :   printf("%s\n", uri->GetSpecOrDefault().get());
     530             : }
     531             : 
     532             : void
     533           0 : PrintWinURIInner(nsGlobalWindowInner* aWin)
     534             : {
     535           0 :   return PrintWinURI(aWin);
     536             : }
     537             : 
     538             : void
     539           0 : PrintWinURIOuter(nsGlobalWindowOuter* aWin)
     540             : {
     541           0 :   return PrintWinURI(aWin);
     542             : }
     543             : 
     544             : template<typename T>
     545             : void
     546           0 : PrintWinCodebase(T *win)
     547             : {
     548           0 :   if (!win) {
     549             :     printf("No window passed in.\n");
     550           0 :     return;
     551             :   }
     552             : 
     553           0 :   nsIPrincipal *prin = win->GetPrincipal();
     554           0 :   if (!prin) {
     555             :     printf("Window doesn't have principals.\n");
     556             :     return;
     557             :   }
     558             : 
     559           0 :   nsCOMPtr<nsIURI> uri;
     560           0 :   prin->GetURI(getter_AddRefs(uri));
     561           0 :   if (!uri) {
     562           0 :     printf("No URI, maybe the system principal.\n");
     563           0 :     return;
     564             :   }
     565             : 
     566           0 :   printf("%s\n", uri->GetSpecOrDefault().get());
     567             : }
     568             : 
     569             : void
     570           0 : PrintWinCodebaseInner(nsGlobalWindowInner* aWin)
     571             : {
     572           0 :   return PrintWinCodebase(aWin);
     573             : }
     574             : 
     575             : void
     576           0 : PrintWinCodebaseOuter(nsGlobalWindowOuter* aWin)
     577             : {
     578           0 :   return PrintWinCodebase(aWin);
     579             : }
     580             : 
     581             : void
     582           0 : DumpString(const nsAString &str)
     583             : {
     584           0 :   printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
     585           0 : }
     586             : #endif
     587             : 
     588           0 : nsJSContext::nsJSContext(bool aGCOnDestruction,
     589           0 :                          nsIScriptGlobalObject* aGlobalObject)
     590             :   : mWindowProxy(nullptr)
     591             :   , mGCOnDestruction(aGCOnDestruction)
     592           0 :   , mGlobalObjectRef(aGlobalObject)
     593             : {
     594           0 :   EnsureStatics();
     595             : 
     596           0 :   mIsInitialized = false;
     597           0 :   mProcessingScriptTag = false;
     598           0 :   HoldJSObjects(this);
     599           0 : }
     600             : 
     601           0 : nsJSContext::~nsJSContext()
     602             : {
     603           0 :   mGlobalObjectRef = nullptr;
     604             : 
     605           0 :   Destroy();
     606           0 : }
     607             : 
     608             : void
     609           0 : nsJSContext::Destroy()
     610             : {
     611           0 :   if (mGCOnDestruction) {
     612           0 :     PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY, mWindowProxy);
     613             :   }
     614             : 
     615           0 :   DropJSObjects(this);
     616           0 : }
     617             : 
     618             : // QueryInterface implementation for nsJSContext
     619             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
     620             : 
     621           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
     622           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy)
     623           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     624             : 
     625           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
     626           0 :   tmp->mIsInitialized = false;
     627           0 :   tmp->mGCOnDestruction = false;
     628           0 :   tmp->mWindowProxy = nullptr;
     629           0 :   tmp->Destroy();
     630           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
     631           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     632           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext)
     633           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
     634           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     635             : 
     636           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
     637           0 :   NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
     638           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     639           0 : NS_INTERFACE_MAP_END
     640             : 
     641             : 
     642           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
     643           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
     644             : 
     645             : #ifdef DEBUG
     646             : bool
     647           0 : AtomIsEventHandlerName(nsAtom *aName)
     648             : {
     649           0 :   const char16_t *name = aName->GetUTF16String();
     650             : 
     651             :   const char16_t *cp;
     652             :   char16_t c;
     653           0 :   for (cp = name; *cp != '\0'; ++cp)
     654             :   {
     655           0 :     c = *cp;
     656           0 :     if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
     657             :       return false;
     658             :   }
     659             : 
     660             :   return true;
     661             : }
     662             : #endif
     663             : 
     664             : nsIScriptGlobalObject *
     665           0 : nsJSContext::GetGlobalObject()
     666             : {
     667             :   // Note: this could probably be simplified somewhat more; see bug 974327
     668             :   // comments 1 and 3.
     669           0 :   if (!mWindowProxy) {
     670             :     return nullptr;
     671             :   }
     672             : 
     673           0 :   MOZ_ASSERT(mGlobalObjectRef);
     674             :   return mGlobalObjectRef;
     675             : }
     676             : 
     677             : nsresult
     678           0 : nsJSContext::InitContext()
     679             : {
     680             :   // Make sure callers of this use
     681             :   // WillInitializeContext/DidInitializeContext around this call.
     682           0 :   NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
     683             : 
     684             :   // XXXbz Is there still a point to this function?
     685             :   return NS_OK;
     686             : }
     687             : 
     688             : nsresult
     689           0 : nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
     690             : {
     691           0 :   AutoJSAPI jsapi;
     692           0 :   if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
     693             :     return NS_ERROR_FAILURE;
     694             :   }
     695           0 :   JSContext* cx = jsapi.cx();
     696             : 
     697           0 :   JS::AutoValueVector args(cx);
     698             : 
     699           0 :   JS::Rooted<JSObject*> global(cx, GetWindowProxy());
     700             :   nsresult rv =
     701           0 :     ConvertSupportsTojsvals(aArgs, global, args);
     702           0 :   NS_ENSURE_SUCCESS(rv, rv);
     703             : 
     704             :   // got the arguments, now attach them.
     705             : 
     706           0 :   for (uint32_t i = 0; i < args.length(); ++i) {
     707           0 :     if (!JS_WrapValue(cx, args[i])) {
     708             :       return NS_ERROR_FAILURE;
     709             :     }
     710             :   }
     711             : 
     712           0 :   JS::Rooted<JSObject*> array(cx, ::JS_NewArrayObject(cx, args));
     713           0 :   if (!array) {
     714             :     return NS_ERROR_FAILURE;
     715             :   }
     716             : 
     717           0 :   return JS_DefineProperty(cx, aTarget, aPropName, array, 0) ? NS_OK : NS_ERROR_FAILURE;
     718             : }
     719             : 
     720             : nsresult
     721           0 : nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
     722             :                                      JS::Handle<JSObject*> aScope,
     723             :                                      JS::AutoValueVector& aArgsOut)
     724             : {
     725           0 :   nsresult rv = NS_OK;
     726             : 
     727             :   // If the array implements nsIJSArgArray, copy the contents and return.
     728           0 :   nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
     729           0 :   if (fastArray) {
     730             :     uint32_t argc;
     731             :     JS::Value* argv;
     732           0 :     rv = fastArray->GetArgs(&argc, reinterpret_cast<void **>(&argv));
     733           0 :     if (NS_SUCCEEDED(rv) && !aArgsOut.append(argv, argc)) {
     734           0 :       rv = NS_ERROR_OUT_OF_MEMORY;
     735             :     }
     736             :     return rv;
     737             :   }
     738             : 
     739             :   // Take the slower path converting each item.
     740             :   // Handle only nsIArray and nsIVariant.  nsIArray is only needed for
     741             :   // SetProperty('arguments', ...);
     742             : 
     743           0 :   nsIXPConnect *xpc = nsContentUtils::XPConnect();
     744           0 :   NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
     745           0 :   AutoJSContext cx;
     746             : 
     747           0 :   if (!aArgs)
     748             :     return NS_OK;
     749             :   uint32_t argCount;
     750             :   // This general purpose function may need to convert an arg array
     751             :   // (window.arguments, event-handler args) and a generic property.
     752           0 :   nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
     753             : 
     754           0 :   if (argsArray) {
     755           0 :     rv = argsArray->GetLength(&argCount);
     756           0 :     NS_ENSURE_SUCCESS(rv, rv);
     757           0 :     if (argCount == 0)
     758             :       return NS_OK;
     759             :   } else {
     760           0 :     argCount = 1; // the nsISupports which is not an array
     761             :   }
     762             : 
     763             :   // Use the caller's auto guards to release and unroot.
     764           0 :   if (!aArgsOut.resize(argCount)) {
     765             :     return NS_ERROR_OUT_OF_MEMORY;
     766             :   }
     767             : 
     768           0 :   if (argsArray) {
     769           0 :     for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
     770           0 :       nsCOMPtr<nsISupports> arg;
     771           0 :       JS::MutableHandle<JS::Value> thisVal = aArgsOut[argCtr];
     772           0 :       argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
     773           0 :                                 getter_AddRefs(arg));
     774           0 :       if (!arg) {
     775           0 :         thisVal.setNull();
     776           0 :         continue;
     777             :       }
     778           0 :       nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
     779           0 :       if (variant != nullptr) {
     780           0 :         rv = xpc->VariantToJS(cx, aScope, variant, thisVal);
     781             :       } else {
     782             :         // And finally, support the nsISupportsPrimitives supplied
     783             :         // by the AppShell.  It generally will pass only strings, but
     784             :         // as we have code for handling all, we may as well use it.
     785           0 :         rv = AddSupportsPrimitiveTojsvals(arg, thisVal.address());
     786           0 :         if (rv == NS_ERROR_NO_INTERFACE) {
     787             :           // something else - probably an event object or similar -
     788             :           // just wrap it.
     789             : #ifdef DEBUG
     790             :           // but first, check its not another nsISupportsPrimitive, as
     791             :           // these are now deprecated for use with script contexts.
     792           0 :           nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
     793           0 :           NS_ASSERTION(prim == nullptr,
     794             :                        "Don't pass nsISupportsPrimitives - use nsIVariant!");
     795             : #endif
     796           0 :           JSAutoRealm ar(cx, aScope);
     797           0 :           rv = nsContentUtils::WrapNative(cx, arg, thisVal);
     798             :         }
     799             :       }
     800             :     }
     801             :   } else {
     802           0 :     nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
     803             :     if (variant) {
     804           0 :       rv = xpc->VariantToJS(cx, aScope, variant, aArgsOut[0]);
     805             :     } else {
     806           0 :       NS_ERROR("Not an array, not an interface?");
     807           0 :       rv = NS_ERROR_UNEXPECTED;
     808             :     }
     809             :   }
     810             :   return rv;
     811             : }
     812             : 
     813             : // This really should go into xpconnect somewhere...
     814             : nsresult
     815           0 : nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv)
     816             : {
     817           0 :   MOZ_ASSERT(aArg, "Empty arg");
     818             : 
     819           0 :   nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
     820           0 :   if (!argPrimitive)
     821             :     return NS_ERROR_NO_INTERFACE;
     822             : 
     823           0 :   AutoJSContext cx;
     824             :   uint16_t type;
     825           0 :   argPrimitive->GetType(&type);
     826             : 
     827           0 :   switch(type) {
     828             :     case nsISupportsPrimitive::TYPE_CSTRING : {
     829           0 :       nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
     830           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     831             : 
     832           0 :       nsAutoCString data;
     833             : 
     834           0 :       p->GetData(data);
     835             : 
     836             : 
     837           0 :       JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
     838           0 :       NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
     839             : 
     840           0 :       aArgv->setString(str);
     841             : 
     842           0 :       break;
     843             :     }
     844             :     case nsISupportsPrimitive::TYPE_STRING : {
     845           0 :       nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
     846           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     847             : 
     848           0 :       nsAutoString data;
     849             : 
     850           0 :       p->GetData(data);
     851             : 
     852             :       // cast is probably safe since wchar_t and char16_t are expected
     853             :       // to be equivalent; both unsigned 16-bit entities
     854             :       JSString *str =
     855           0 :         ::JS_NewUCStringCopyN(cx, data.get(), data.Length());
     856           0 :       NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
     857             : 
     858           0 :       aArgv->setString(str);
     859           0 :       break;
     860             :     }
     861             :     case nsISupportsPrimitive::TYPE_PRBOOL : {
     862           0 :       nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
     863           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     864             : 
     865             :       bool data;
     866             : 
     867           0 :       p->GetData(&data);
     868             : 
     869           0 :       aArgv->setBoolean(data);
     870             : 
     871           0 :       break;
     872             :     }
     873             :     case nsISupportsPrimitive::TYPE_PRUINT8 : {
     874           0 :       nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
     875           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     876             : 
     877             :       uint8_t data;
     878             : 
     879           0 :       p->GetData(&data);
     880             : 
     881           0 :       aArgv->setInt32(data);
     882             : 
     883           0 :       break;
     884             :     }
     885             :     case nsISupportsPrimitive::TYPE_PRUINT16 : {
     886           0 :       nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
     887           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     888             : 
     889             :       uint16_t data;
     890             : 
     891           0 :       p->GetData(&data);
     892             : 
     893           0 :       aArgv->setInt32(data);
     894             : 
     895           0 :       break;
     896             :     }
     897             :     case nsISupportsPrimitive::TYPE_PRUINT32 : {
     898           0 :       nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
     899           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     900             : 
     901             :       uint32_t data;
     902             : 
     903           0 :       p->GetData(&data);
     904             : 
     905           0 :       aArgv->setInt32(data);
     906             : 
     907           0 :       break;
     908             :     }
     909             :     case nsISupportsPrimitive::TYPE_CHAR : {
     910           0 :       nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
     911           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     912             : 
     913             :       char data;
     914             : 
     915           0 :       p->GetData(&data);
     916             : 
     917           0 :       JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
     918           0 :       NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
     919             : 
     920           0 :       aArgv->setString(str);
     921             : 
     922           0 :       break;
     923             :     }
     924             :     case nsISupportsPrimitive::TYPE_PRINT16 : {
     925           0 :       nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
     926           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     927             : 
     928             :       int16_t data;
     929             : 
     930           0 :       p->GetData(&data);
     931             : 
     932           0 :       aArgv->setInt32(data);
     933             : 
     934           0 :       break;
     935             :     }
     936             :     case nsISupportsPrimitive::TYPE_PRINT32 : {
     937           0 :       nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
     938           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     939             : 
     940             :       int32_t data;
     941             : 
     942           0 :       p->GetData(&data);
     943             : 
     944           0 :       aArgv->setInt32(data);
     945             : 
     946           0 :       break;
     947             :     }
     948             :     case nsISupportsPrimitive::TYPE_FLOAT : {
     949           0 :       nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
     950           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     951             : 
     952             :       float data;
     953             : 
     954           0 :       p->GetData(&data);
     955             : 
     956           0 :       *aArgv = ::JS_NumberValue(data);
     957             : 
     958           0 :       break;
     959             :     }
     960             :     case nsISupportsPrimitive::TYPE_DOUBLE : {
     961           0 :       nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
     962           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     963             : 
     964             :       double data;
     965             : 
     966           0 :       p->GetData(&data);
     967             : 
     968           0 :       *aArgv = ::JS_NumberValue(data);
     969             : 
     970           0 :       break;
     971             :     }
     972             :     case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
     973           0 :       nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
     974           0 :       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
     975             : 
     976           0 :       nsCOMPtr<nsISupports> data;
     977           0 :       nsIID *iid = nullptr;
     978             : 
     979           0 :       p->GetData(getter_AddRefs(data));
     980           0 :       p->GetDataIID(&iid);
     981           0 :       NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
     982             : 
     983           0 :       AutoFree iidGuard(iid); // Free iid upon destruction.
     984             : 
     985           0 :       JS::Rooted<JSObject*> scope(cx, GetWindowProxy());
     986           0 :       JS::Rooted<JS::Value> v(cx);
     987           0 :       JSAutoRealm ar(cx, scope);
     988           0 :       nsresult rv = nsContentUtils::WrapNative(cx, data, iid, &v);
     989           0 :       NS_ENSURE_SUCCESS(rv, rv);
     990             : 
     991           0 :       *aArgv = v;
     992             : 
     993           0 :       break;
     994             :     }
     995             :     case nsISupportsPrimitive::TYPE_ID :
     996             :     case nsISupportsPrimitive::TYPE_PRUINT64 :
     997             :     case nsISupportsPrimitive::TYPE_PRINT64 :
     998             :     case nsISupportsPrimitive::TYPE_PRTIME : {
     999           0 :       NS_WARNING("Unsupported primitive type used");
    1000             :       aArgv->setNull();
    1001             :       break;
    1002             :     }
    1003             :     default : {
    1004           0 :       NS_WARNING("Unknown primitive type used");
    1005             :       aArgv->setNull();
    1006             :       break;
    1007             :     }
    1008             :   }
    1009             :   return NS_OK;
    1010             : }
    1011             : 
    1012             : #ifdef MOZ_JPROF
    1013             : 
    1014             : #include <signal.h>
    1015             : 
    1016             : inline bool
    1017             : IsJProfAction(struct sigaction *action)
    1018             : {
    1019             :     return (action->sa_sigaction &&
    1020             :             (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
    1021             : }
    1022             : 
    1023             : void NS_JProfStartProfiling();
    1024             : void NS_JProfStopProfiling();
    1025             : void NS_JProfClearCircular();
    1026             : 
    1027             : static bool
    1028             : JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
    1029             : {
    1030             :   NS_JProfStartProfiling();
    1031             :   return true;
    1032             : }
    1033             : 
    1034             : void NS_JProfStartProfiling()
    1035             : {
    1036             :     // Figure out whether we're dealing with SIGPROF, SIGALRM, or
    1037             :     // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
    1038             :     // JP_RTC_HZ)
    1039             :     struct sigaction action;
    1040             : 
    1041             :     // Must check ALRM before PROF since both are enabled for real-time
    1042             :     sigaction(SIGALRM, nullptr, &action);
    1043             :     //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
    1044             :     if (IsJProfAction(&action)) {
    1045             :         //printf("Beginning real-time jprof profiling.\n");
    1046             :         raise(SIGALRM);
    1047             :         return;
    1048             :     }
    1049             : 
    1050             :     sigaction(SIGPROF, nullptr, &action);
    1051             :     //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
    1052             :     if (IsJProfAction(&action)) {
    1053             :         //printf("Beginning process-time jprof profiling.\n");
    1054             :         raise(SIGPROF);
    1055             :         return;
    1056             :     }
    1057             : 
    1058             :     sigaction(SIGPOLL, nullptr, &action);
    1059             :     //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
    1060             :     if (IsJProfAction(&action)) {
    1061             :         //printf("Beginning rtc-based jprof profiling.\n");
    1062             :         raise(SIGPOLL);
    1063             :         return;
    1064             :     }
    1065             : 
    1066             :     printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
    1067             : }
    1068             : 
    1069             : static bool
    1070             : JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
    1071             : {
    1072             :   NS_JProfStopProfiling();
    1073             :   return true;
    1074             : }
    1075             : 
    1076             : void
    1077             : NS_JProfStopProfiling()
    1078             : {
    1079             :     raise(SIGUSR1);
    1080             :     //printf("Stopped jprof profiling.\n");
    1081             : }
    1082             : 
    1083             : static bool
    1084             : JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
    1085             : {
    1086             :   NS_JProfClearCircular();
    1087             :   return true;
    1088             : }
    1089             : 
    1090             : void
    1091             : NS_JProfClearCircular()
    1092             : {
    1093             :     raise(SIGUSR2);
    1094             :     //printf("cleared jprof buffer\n");
    1095             : }
    1096             : 
    1097             : static bool
    1098             : JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
    1099             : {
    1100             :   // Not ideal...
    1101             :   NS_JProfStopProfiling();
    1102             :   NS_JProfStartProfiling();
    1103             :   return true;
    1104             : }
    1105             : 
    1106             : static const JSFunctionSpec JProfFunctions[] = {
    1107             :     JS_FN("JProfStartProfiling",        JProfStartProfilingJS,      0, 0),
    1108             :     JS_FN("JProfStopProfiling",         JProfStopProfilingJS,       0, 0),
    1109             :     JS_FN("JProfClearCircular",         JProfClearCircularJS,       0, 0),
    1110             :     JS_FN("JProfSaveCircular",          JProfSaveCircularJS,        0, 0),
    1111             :     JS_FS_END
    1112             : };
    1113             : 
    1114             : #endif /* defined(MOZ_JPROF) */
    1115             : 
    1116             : nsresult
    1117           0 : nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
    1118             : {
    1119           0 :   AutoJSAPI jsapi;
    1120           0 :   jsapi.Init();
    1121           0 :   JSContext* cx = jsapi.cx();
    1122           0 :   JSAutoRealm ar(cx, aGlobalObj);
    1123             : 
    1124             :   // Attempt to initialize profiling functions
    1125           0 :   ::JS_DefineProfilingFunctions(cx, aGlobalObj);
    1126             : 
    1127             : #ifdef MOZ_JPROF
    1128             :   // Attempt to initialize JProf functions
    1129             :   ::JS_DefineFunctions(cx, aGlobalObj, JProfFunctions);
    1130             : #endif
    1131             : 
    1132           0 :   return NS_OK;
    1133             : }
    1134             : 
    1135             : void
    1136           0 : nsJSContext::WillInitializeContext()
    1137             : {
    1138           0 :   mIsInitialized = false;
    1139           0 : }
    1140             : 
    1141             : void
    1142           0 : nsJSContext::DidInitializeContext()
    1143             : {
    1144           0 :   mIsInitialized = true;
    1145           0 : }
    1146             : 
    1147             : bool
    1148           0 : nsJSContext::IsContextInitialized()
    1149             : {
    1150           0 :   return mIsInitialized;
    1151             : }
    1152             : 
    1153             : bool
    1154           0 : nsJSContext::GetProcessingScriptTag()
    1155             : {
    1156           0 :   return mProcessingScriptTag;
    1157             : }
    1158             : 
    1159             : void
    1160           0 : nsJSContext::SetProcessingScriptTag(bool aFlag)
    1161             : {
    1162           0 :   mProcessingScriptTag = aFlag;
    1163           0 : }
    1164             : 
    1165             : void
    1166           0 : FullGCTimerFired(nsITimer* aTimer, void* aClosure)
    1167             : {
    1168           0 :   nsJSContext::KillFullGCTimer();
    1169           0 :   MOZ_ASSERT(!aClosure, "Don't pass a closure to FullGCTimerFired");
    1170             :   nsJSContext::GarbageCollectNow(JS::gcreason::FULL_GC_TIMER,
    1171           0 :                                  nsJSContext::IncrementalGC);
    1172           0 : }
    1173             : 
    1174             : //static
    1175             : void
    1176           0 : nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
    1177             :                                IsIncremental aIncremental,
    1178             :                                IsShrinking aShrinking,
    1179             :                                int64_t aSliceMillis)
    1180             : {
    1181           0 :   AUTO_PROFILER_LABEL_DYNAMIC_CSTR("nsJSContext::GarbageCollectNow", GCCC,
    1182             :                                    JS::gcreason::ExplainReason(aReason));
    1183             : 
    1184           0 :   MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
    1185             : 
    1186           0 :   KillGCTimer();
    1187             : 
    1188             :   // Reset sPendingLoadCount in case the timer that fired was a
    1189             :   // timer we scheduled due to a normal GC timer firing while
    1190             :   // documents were loading. If this happens we're waiting for a
    1191             :   // document that is taking a long time to load, and we effectively
    1192             :   // ignore the fact that the currently loading documents are still
    1193             :   // loading and move on as if they weren't.
    1194           0 :   sPendingLoadCount = 0;
    1195           0 :   sLoadingInProgress = false;
    1196             : 
    1197             :   // We use danger::GetJSContext() since AutoJSAPI will assert if the current
    1198             :   // thread's context is null (such as during shutdown).
    1199           0 :   JSContext* cx = danger::GetJSContext();
    1200             : 
    1201           0 :   if (!nsContentUtils::XPConnect() || !cx) {
    1202           0 :     return;
    1203             :   }
    1204             : 
    1205           0 :   if (sCCLockedOut && aIncremental == IncrementalGC) {
    1206             :     // We're in the middle of incremental GC. Do another slice.
    1207           0 :     JS::PrepareForIncrementalGC(cx);
    1208           0 :     JS::IncrementalGCSlice(cx, aReason, aSliceMillis);
    1209           0 :     return;
    1210             :   }
    1211             : 
    1212           0 :   JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
    1213             : 
    1214           0 :   if (aIncremental == NonIncrementalGC || aReason == JS::gcreason::FULL_GC_TIMER) {
    1215           0 :     sNeedsFullGC = true;
    1216             :   }
    1217             : 
    1218           0 :   if (sNeedsFullGC) {
    1219           0 :     JS::PrepareForFullGC(cx);
    1220             :   } else {
    1221           0 :     CycleCollectedJSRuntime::Get()->PrepareWaitingZonesForGC();
    1222             :   }
    1223             : 
    1224           0 :   if (aIncremental == IncrementalGC) {
    1225           0 :     JS::StartIncrementalGC(cx, gckind, aReason, aSliceMillis);
    1226             :   } else {
    1227           0 :     JS::NonIncrementalGC(cx, gckind, aReason);
    1228             :   }
    1229             : }
    1230             : 
    1231             : static void
    1232           0 : FinishAnyIncrementalGC()
    1233             : {
    1234           0 :   AUTO_PROFILER_LABEL("FinishAnyIncrementalGC", GCCC);
    1235             : 
    1236           0 :   if (sCCLockedOut) {
    1237           0 :     AutoJSAPI jsapi;
    1238           0 :     jsapi.Init();
    1239             : 
    1240             :     // We're in the middle of an incremental GC, so finish it.
    1241           0 :     JS::PrepareForIncrementalGC(jsapi.cx());
    1242           0 :     JS::FinishIncrementalGC(jsapi.cx(), JS::gcreason::CC_FORCED);
    1243             :   }
    1244           0 : }
    1245             : 
    1246             : static void
    1247           0 : FireForgetSkippable(uint32_t aSuspected, bool aRemoveChildless,
    1248             :                     TimeStamp aDeadline)
    1249             : {
    1250           0 :   AUTO_PROFILER_TRACING("CC", aDeadline.IsNull() ? "ForgetSkippable"
    1251             :                                                  : "IdleForgetSkippable");
    1252           0 :   PRTime startTime = PR_Now();
    1253           0 :   TimeStamp startTimeStamp = TimeStamp::Now();
    1254           0 :   FinishAnyIncrementalGC();
    1255             :   bool earlyForgetSkippable =
    1256           0 :     sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS;
    1257             : 
    1258           0 :   int64_t budgetMs = aDeadline.IsNull() ?
    1259             :     kForgetSkippableSliceDuration :
    1260           0 :     int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
    1261           0 :   js::SliceBudget budget = js::SliceBudget(js::TimeBudget(budgetMs));
    1262           0 :   nsCycleCollector_forgetSkippable(budget, aRemoveChildless, earlyForgetSkippable);
    1263             : 
    1264           0 :   sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
    1265           0 :   ++sCleanupsSinceLastGC;
    1266           0 :   PRTime delta = PR_Now() - startTime;
    1267           0 :   if (sMinForgetSkippableTime > delta) {
    1268           0 :     sMinForgetSkippableTime = delta;
    1269             :   }
    1270           0 :   if (sMaxForgetSkippableTime < delta) {
    1271           0 :     sMaxForgetSkippableTime = delta;
    1272             :   }
    1273           0 :   sTotalForgetSkippableTime += delta;
    1274           0 :   sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
    1275           0 :   ++sForgetSkippableBeforeCC;
    1276             : 
    1277           0 :   TimeStamp now = TimeStamp::Now();
    1278           0 :   TimeDuration duration = now - startTimeStamp;
    1279           0 :   if (duration.ToSeconds()) {
    1280           0 :     TimeDuration idleDuration;
    1281           0 :     if (!aDeadline.IsNull()) {
    1282           0 :       if (aDeadline < now) {
    1283             :         // This slice overflowed the idle period.
    1284           0 :         if (aDeadline > startTimeStamp) {
    1285             :           idleDuration = aDeadline - startTimeStamp;
    1286           0 :         }
    1287             :       } else {
    1288             :         idleDuration = duration;
    1289             :       }
    1290             :     }
    1291           0 : 
    1292           0 :     uint32_t percent =
    1293             :       uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
    1294           0 :     Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_DURING_IDLE, percent);
    1295             :   }
    1296             : }
    1297             : 
    1298           0 : MOZ_ALWAYS_INLINE
    1299             : static uint32_t
    1300           0 : TimeBetween(TimeStamp start, TimeStamp end)
    1301           0 : {
    1302             :   MOZ_ASSERT(end >= start);
    1303             :   return (uint32_t) ((end - start).ToMilliseconds());
    1304             : }
    1305           0 : 
    1306             : static uint32_t
    1307           0 : TimeUntilNow(TimeStamp start)
    1308             : {
    1309             :   if (start.IsNull()) {
    1310           0 :     return 0;
    1311             :   }
    1312             :   return TimeBetween(start, TimeStamp::Now());
    1313             : }
    1314             : 
    1315             : struct CycleCollectorStats
    1316             : {
    1317             :   constexpr CycleCollectorStats() :
    1318             :     mMaxGCDuration(0), mRanSyncForgetSkippable(false), mSuspected(0),
    1319             :     mMaxSkippableDuration(0), mMaxSliceTime(0), mMaxSliceTimeSinceClear(0),
    1320             :     mTotalSliceTime(0), mAnyLockedOut(false), mFile(nullptr)
    1321           0 :   {}
    1322             : 
    1323           0 :   void Init()
    1324           0 :   {
    1325             :     Clear();
    1326           0 :     mMaxSliceTimeSinceClear = 0;
    1327           0 : 
    1328             :     char* env = getenv("MOZ_CCTIMER");
    1329             :     if (!env) {
    1330           0 :       return;
    1331           0 :     }
    1332           0 :     if (strcmp(env, "none") == 0) {
    1333           0 :       mFile = nullptr;
    1334           0 :     } else if (strcmp(env, "stdout") == 0) {
    1335           0 :       mFile = stdout;
    1336             :     } else if (strcmp(env, "stderr") == 0) {
    1337           0 :       mFile = stderr;
    1338           0 :     } else {
    1339           0 :       mFile = fopen(env, "a");
    1340             :       if (!mFile) {
    1341             :         MOZ_CRASH("Failed to open MOZ_CCTIMER log file.");
    1342             :       }
    1343             :     }
    1344           0 :   }
    1345             : 
    1346           0 :   void Clear()
    1347           0 :   {
    1348             :     if (mFile && mFile != stdout && mFile != stderr) {
    1349           0 :       fclose(mFile);
    1350           0 :     }
    1351           0 :     mBeginSliceTime = TimeStamp();
    1352           0 :     mEndSliceTime = TimeStamp();
    1353           0 :     mBeginTime = TimeStamp();
    1354           0 :     mMaxGCDuration = 0;
    1355           0 :     mRanSyncForgetSkippable = false;
    1356           0 :     mSuspected = 0;
    1357           0 :     mMaxSkippableDuration = 0;
    1358           0 :     mMaxSliceTime = 0;
    1359           0 :     mTotalSliceTime = 0;
    1360             :     mAnyLockedOut = false;
    1361             :   }
    1362             : 
    1363           0 :   void PrepareForCycleCollectionSlice(TimeStamp aDeadline = TimeStamp());
    1364             : 
    1365           0 :   void FinishCycleCollectionSlice()
    1366             :   {
    1367           0 :     if (mBeginSliceTime.IsNull()) {
    1368             :       // We already called this method from EndCycleCollectionCallback for this slice.
    1369             :       return;
    1370           0 :     }
    1371           0 : 
    1372             :     mEndSliceTime = TimeStamp::Now();
    1373           0 :     TimeDuration duration = mEndSliceTime - mBeginSliceTime;
    1374           0 : 
    1375           0 :     if (duration.ToSeconds()) {
    1376           0 :       TimeDuration idleDuration;
    1377             :       if (!mIdleDeadline.IsNull()) {
    1378           0 :         if (mIdleDeadline < mEndSliceTime) {
    1379             :           // This slice overflowed the idle period.
    1380           0 :           idleDuration = mIdleDeadline - mBeginSliceTime;
    1381             :         } else {
    1382             :           idleDuration = duration;
    1383             :         }
    1384             :       }
    1385           0 : 
    1386             :       uint32_t percent =
    1387           0 :         uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
    1388             :       Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE,
    1389             :                             percent);
    1390           0 :     }
    1391           0 : 
    1392           0 :     uint32_t sliceTime = TimeBetween(mBeginSliceTime, mEndSliceTime);
    1393           0 :     mMaxSliceTime = std::max(mMaxSliceTime, sliceTime);
    1394           0 :     mMaxSliceTimeSinceClear = std::max(mMaxSliceTimeSinceClear, sliceTime);
    1395             :     mTotalSliceTime += sliceTime;
    1396             :     mBeginSliceTime = TimeStamp();
    1397             :   }
    1398             : 
    1399             :   void RunForgetSkippable();
    1400             : 
    1401             :   // Time the current slice began, including any GC finishing.
    1402             :   TimeStamp mBeginSliceTime;
    1403             : 
    1404             :   // Time the previous slice of the current CC ended.
    1405             :   TimeStamp mEndSliceTime;
    1406             : 
    1407             :   // Time the current cycle collection began.
    1408             :   TimeStamp mBeginTime;
    1409             : 
    1410             :   // The longest GC finishing duration for any slice of the current CC.
    1411             :   uint32_t mMaxGCDuration;
    1412             : 
    1413             :   // True if we ran sync forget skippable in any slice of the current CC.
    1414             :   bool mRanSyncForgetSkippable;
    1415             : 
    1416             :   // Number of suspected objects at the start of the current CC.
    1417             :   uint32_t mSuspected;
    1418             : 
    1419             :   // The longest duration spent on sync forget skippable in any slice of the
    1420             :   // current CC.
    1421             :   uint32_t mMaxSkippableDuration;
    1422             : 
    1423             :   // The longest pause of any slice in the current CC.
    1424             :   uint32_t mMaxSliceTime;
    1425             : 
    1426             :   // The longest slice time since ClearMaxCCSliceTime() was called.
    1427             :   uint32_t mMaxSliceTimeSinceClear;
    1428             : 
    1429             :   // The total amount of time spent actually running the current CC.
    1430             :   uint32_t mTotalSliceTime;
    1431             : 
    1432             :   // True if we were locked out by the GC in any slice of the current CC.
    1433             :   bool mAnyLockedOut;
    1434             : 
    1435             :   // A file to dump CC activity to; set by MOZ_CCTIMER environment variable.
    1436             :   FILE* mFile;
    1437             : 
    1438             :   // In case CC slice was triggered during idle time, set to the end of the idle
    1439             :   // period.
    1440             :   TimeStamp mIdleDeadline;
    1441             : };
    1442             : 
    1443             : CycleCollectorStats gCCStats;
    1444           0 : 
    1445             : void
    1446           0 : CycleCollectorStats::PrepareForCycleCollectionSlice(TimeStamp aDeadline)
    1447           0 : {
    1448             :   mBeginSliceTime = TimeStamp::Now();
    1449             :   mIdleDeadline = aDeadline;
    1450           0 : 
    1451           0 :   // Before we begin the cycle collection, make sure there is no active GC.
    1452           0 :   if (sCCLockedOut) {
    1453           0 :     mAnyLockedOut = true;
    1454           0 :     FinishAnyIncrementalGC();
    1455             :     uint32_t gcTime = TimeBetween(mBeginSliceTime, TimeStamp::Now());
    1456           0 :     mMaxGCDuration = std::max(mMaxGCDuration, gcTime);
    1457             :   }
    1458             : }
    1459           0 : 
    1460             : void
    1461             : CycleCollectorStats::RunForgetSkippable()
    1462             : {
    1463           0 :   // Run forgetSkippable synchronously to reduce the size of the CC graph. This
    1464           0 :   // is particularly useful if we recently finished a GC.
    1465           0 :   TimeStamp beginForgetSkippable = TimeStamp::Now();
    1466           0 :   bool ranSyncForgetSkippable = false;
    1467           0 :   while (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
    1468             :     FireForgetSkippable(nsCycleCollector_suspectedCount(), false, TimeStamp());
    1469             :     ranSyncForgetSkippable = true;
    1470           0 :   }
    1471           0 : 
    1472           0 :   if (ranSyncForgetSkippable) {
    1473           0 :     mMaxSkippableDuration =
    1474             :       std::max(mMaxSkippableDuration, TimeUntilNow(beginForgetSkippable));
    1475           0 :     mRanSyncForgetSkippable = true;
    1476             :   }
    1477             : }
    1478             : 
    1479           0 : //static
    1480             : void
    1481           0 : nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener)
    1482           0 : {
    1483             :   if (!NS_IsMainThread()) {
    1484             :     return;
    1485           0 :   }
    1486             : 
    1487           0 :   AUTO_PROFILER_LABEL("nsJSContext::CycleCollectNow", GCCC);
    1488           0 : 
    1489           0 :   gCCStats.PrepareForCycleCollectionSlice(TimeStamp());
    1490             :   nsCycleCollector_collect(aListener);
    1491             :   gCCStats.FinishCycleCollectionSlice();
    1492             : }
    1493             : 
    1494           0 : //static
    1495             : void
    1496           0 : nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
    1497           0 : {
    1498             :   if (!NS_IsMainThread()) {
    1499             :     return;
    1500           0 :   }
    1501             : 
    1502           0 :   AUTO_PROFILER_TRACING("CC", aDeadline.IsNull() ? "CCSlice" : "IdleCCSlice");
    1503             : 
    1504           0 :   AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorSlice", GCCC);
    1505             : 
    1506             :   gCCStats.PrepareForCycleCollectionSlice(aDeadline);
    1507             : 
    1508             :   // Decide how long we want to budget for this slice. By default,
    1509             :   // use an unlimited budget.
    1510           0 :   js::SliceBudget budget = js::SliceBudget::unlimited();
    1511           0 : 
    1512           0 :   if (sIncrementalCC) {
    1513           0 :     int64_t baseBudget = kICCSliceBudget;
    1514             :     if (!aDeadline.IsNull()) {
    1515             :       baseBudget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
    1516           0 :     }
    1517             : 
    1518           0 :     if (gCCStats.mBeginTime.IsNull()) {
    1519             :       // If no CC is in progress, use the standard slice time.
    1520           0 :       budget = js::SliceBudget(js::TimeBudget(baseBudget));
    1521             :     } else {
    1522             :       TimeStamp now = TimeStamp::Now();
    1523           0 : 
    1524           0 :       // Only run a limited slice if we're within the max running time.
    1525           0 :       uint32_t runningTime = TimeBetween(gCCStats.mBeginTime, now);
    1526             :       if (runningTime < kMaxICCDuration) {
    1527             :         const float maxSlice = MainThreadIdlePeriod::GetLongIdlePeriod();
    1528             : 
    1529           0 :         // Try to make up for a delay in running this slice.
    1530             :         float sliceDelayMultiplier =
    1531           0 :           TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
    1532             :         float delaySliceBudget =
    1533             :           std::min(baseBudget * sliceDelayMultiplier, maxSlice);
    1534             : 
    1535           0 :         // Increase slice budgets up to |maxSlice| as we approach
    1536           0 :         // half way through the ICC, to avoid large sync CCs.
    1537             :         float percentToHalfDone = std::min(2.0f * runningTime / kMaxICCDuration, 1.0f);
    1538           0 :         float laterSliceBudget = maxSlice * percentToHalfDone;
    1539             : 
    1540             :         budget = js::SliceBudget(js::TimeBudget(std::max({delaySliceBudget,
    1541             :                   laterSliceBudget, (float)baseBudget})));
    1542             :       }
    1543             :     }
    1544           0 :   }
    1545           0 : 
    1546           0 :   nsCycleCollector_collectSlice(budget,
    1547           0 :                                 aDeadline.IsNull() ||
    1548             :                                 (aDeadline - TimeStamp::Now()).ToMilliseconds() <
    1549           0 :                                   kICCSliceBudget);
    1550             : 
    1551             :   gCCStats.FinishCycleCollectionSlice();
    1552             : }
    1553             : 
    1554           0 : //static
    1555             : void
    1556           0 : nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget)
    1557           0 : {
    1558             :   if (!NS_IsMainThread()) {
    1559             :     return;
    1560           0 :   }
    1561             : 
    1562           0 :   AUTO_PROFILER_LABEL("nsJSContext::RunCycleCollectorWorkSlice", GCCC);
    1563             : 
    1564           0 :   gCCStats.PrepareForCycleCollectionSlice();
    1565           0 : 
    1566             :   js::SliceBudget budget = js::SliceBudget(js::WorkBudget(aWorkBudget));
    1567           0 :   nsCycleCollector_collectSlice(budget);
    1568             : 
    1569             :   gCCStats.FinishCycleCollectionSlice();
    1570             : }
    1571           0 : 
    1572             : void
    1573           0 : nsJSContext::ClearMaxCCSliceTime()
    1574           0 : {
    1575             :   gCCStats.mMaxSliceTimeSinceClear = 0;
    1576             : }
    1577           0 : 
    1578             : uint32_t
    1579           0 : nsJSContext::GetMaxCCSliceTimeSinceClear()
    1580             : {
    1581             :   return gCCStats.mMaxSliceTimeSinceClear;
    1582             : }
    1583           0 : 
    1584             : static bool
    1585           0 : ICCRunnerFired(TimeStamp aDeadline)
    1586             : {
    1587             :   if (sDidShutdown) {
    1588             :     return false;
    1589             :   }
    1590             : 
    1591             :   // Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
    1592           0 :   // to synchronously finish the GC, which is bad.
    1593           0 : 
    1594           0 :   if (sCCLockedOut) {
    1595           0 :     PRTime now = PR_Now();
    1596           0 :     if (sCCLockedOutTime == 0) {
    1597             :       sCCLockedOutTime = now;
    1598           0 :       return false;
    1599             :     }
    1600             :     if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
    1601             :       return false;
    1602             :     }
    1603           0 :   }
    1604           0 : 
    1605             :   nsJSContext::RunCycleCollectorSlice(aDeadline);
    1606             :   return true;
    1607             : }
    1608             : 
    1609           0 : //static
    1610             : void
    1611           0 : nsJSContext::BeginCycleCollectionCallback()
    1612             : {
    1613           0 :   MOZ_ASSERT(NS_IsMainThread());
    1614           0 : 
    1615             :   gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
    1616           0 :   gCCStats.mSuspected = nsCycleCollector_suspectedCount();
    1617             : 
    1618           0 :   KillCCRunner();
    1619             : 
    1620           0 :   gCCStats.RunForgetSkippable();
    1621             : 
    1622           0 :   MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
    1623             : 
    1624             :   if (sShuttingDown) {
    1625             :     return;
    1626             :   }
    1627             : 
    1628           0 :   // Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
    1629             :   // an incremental collection, and we want to be sure to finish it.
    1630             :   sICCRunner = IdleTaskRunner::Create(ICCRunnerFired,
    1631             :                                       "BeginCycleCollectionCallback::ICCRunnerFired",
    1632             :                                       kICCIntersliceDelay,
    1633           0 :                                       kIdleICCSliceBudget,
    1634           0 :                                       true,
    1635             :                                       []{ return sShuttingDown; },
    1636             :                                       TaskCategory::GarbageCollection);
    1637             : }
    1638             : 
    1639             : static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
    1640             : 
    1641           0 : //static
    1642             : void
    1643           0 : nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
    1644             : {
    1645           0 :   MOZ_ASSERT(NS_IsMainThread());
    1646             : 
    1647             :   nsJSContext::KillICCRunner();
    1648             : 
    1649             :   // Update timing information for the current slice before we log it, if
    1650           0 :   // we previously called PrepareForCycleCollectionSlice(). During shutdown
    1651             :   // CCs, this won't happen.
    1652           0 :   gCCStats.FinishCycleCollectionSlice();
    1653           0 : 
    1654             :   sCCollectedWaitingForGC += aResults.mFreedGCed;
    1655           0 :   sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
    1656           0 : 
    1657             :   TimeStamp endCCTimeStamp = TimeStamp::Now();
    1658           0 :   uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
    1659           0 : 
    1660           0 :   if (NeedsGCAfterCC()) {
    1661             :     PokeGC(JS::gcreason::CC_WAITING, nullptr,
    1662             :            NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
    1663             :   }
    1664           0 : 
    1665           0 :   // Log information about the CC via telemetry, JSON and the console.
    1666           0 :   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC, gCCStats.mAnyLockedOut);
    1667           0 :   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE, gCCStats.mRanSyncForgetSkippable);
    1668             :   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL, ccNowDuration);
    1669           0 :   Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE, gCCStats.mMaxSliceTime);
    1670             : 
    1671           0 :   if (!sLastCCEndTime.IsNull()) {
    1672           0 :     // TimeBetween returns milliseconds, but we want to report seconds.
    1673             :     uint32_t timeBetween = TimeBetween(sLastCCEndTime, gCCStats.mBeginTime) / 1000;
    1674           0 :     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
    1675             :   }
    1676           0 :   sLastCCEndTime = endCCTimeStamp;
    1677           0 : 
    1678             :   Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
    1679           0 :                         sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
    1680             : 
    1681           0 :   PRTime delta = GetCollectionTimeDelta();
    1682           0 : 
    1683           0 :   uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
    1684             :   uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX)
    1685           0 :     ? 0 : sMinForgetSkippableTime;
    1686           0 : 
    1687           0 :   if (StaticPrefs::javascript_options_mem_log() || gCCStats.mFile) {
    1688           0 :     nsCString mergeMsg;
    1689             :     if (aResults.mMergedZones) {
    1690             :       mergeMsg.AssignLiteral(" merged");
    1691           0 :     }
    1692           0 : 
    1693           0 :     nsCString gcMsg;
    1694             :     if (aResults.mForcedGC) {
    1695             :       gcMsg.AssignLiteral(", forced a GC");
    1696             :     }
    1697             : 
    1698           0 :     const char16_t *kFmt =
    1699           0 :       u"CC(T+%.1f)[%s-%i] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n"
    1700           0 :       u"ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu";
    1701             :     nsString msg;
    1702             :     nsTextFormatter::ssprintf(msg, kFmt, double(delta) / PR_USEC_PER_SEC,
    1703             :                               ProcessNameForCollectorLog(), getpid(),
    1704             :                               gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
    1705             :                               aResults.mNumSlices, gCCStats.mSuspected,
    1706             :                               aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
    1707             :                               aResults.mFreedRefCounted, aResults.mFreedGCed,
    1708             :                               sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
    1709           0 :                               gcMsg.get(),
    1710           0 :                               sForgetSkippableBeforeCC,
    1711           0 :                               minForgetSkippableTime / PR_USEC_PER_MSEC,
    1712             :                               sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
    1713           0 :                               (sTotalForgetSkippableTime / cleanups) /
    1714           0 :                               PR_USEC_PER_MSEC,
    1715           0 :                               sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
    1716             :                               gCCStats.mMaxSkippableDuration, sRemovedPurples);
    1717           0 :     if (StaticPrefs::javascript_options_mem_log()) {
    1718           0 :       nsCOMPtr<nsIConsoleService> cs =
    1719           0 :         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    1720             :       if (cs) {
    1721             :         cs->LogStringMessage(msg.get());
    1722           0 :       }
    1723           0 :     }
    1724             :     if (gCCStats.mFile) {
    1725             :       fprintf(gCCStats.mFile, "%s\n", NS_ConvertUTF16toUTF8(msg).get());
    1726             :     }
    1727           0 :   }
    1728             : 
    1729             :   if (StaticPrefs::javascript_options_mem_notify()) {
    1730             :     const char16_t* kJSONFmt =
    1731             :        u"{ \"timestamp\": %llu, "
    1732             :          u"\"duration\": %lu, "
    1733             :          u"\"max_slice_pause\": %lu, "
    1734             :          u"\"total_slice_pause\": %lu, "
    1735             :          u"\"max_finish_gc_duration\": %lu, "
    1736             :          u"\"max_sync_skippable_duration\": %lu, "
    1737             :          u"\"suspected\": %lu, "
    1738             :          u"\"visited\": { "
    1739             :              u"\"RCed\": %lu, "
    1740             :              u"\"GCed\": %lu }, "
    1741             :          u"\"collected\": { "
    1742             :              u"\"RCed\": %lu, "
    1743             :              u"\"GCed\": %lu }, "
    1744             :          u"\"waiting_for_gc\": %lu, "
    1745             :          u"\"zones_waiting_for_gc\": %lu, "
    1746             :          u"\"short_living_objects_waiting_for_gc\": %lu, "
    1747             :          u"\"forced_gc\": %d, "
    1748             :          u"\"forget_skippable\": { "
    1749             :              u"\"times_before_cc\": %lu, "
    1750             :              u"\"min\": %lu, "
    1751             :              u"\"max\": %lu, "
    1752             :              u"\"avg\": %lu, "
    1753           0 :              u"\"total\": %lu, "
    1754             :              u"\"removed\": %lu } "
    1755           0 :        u"}";
    1756           0 : 
    1757             :     nsString json;
    1758             :     nsTextFormatter::ssprintf(json, kJSONFmt, PR_Now(), ccNowDuration,
    1759             :                               gCCStats.mMaxSliceTime,
    1760             :                               gCCStats.mTotalSliceTime,
    1761             :                               gCCStats.mMaxGCDuration,
    1762             :                               gCCStats.mMaxSkippableDuration,
    1763             :                               gCCStats.mSuspected,
    1764             :                               aResults.mVisitedRefCounted, aResults.mVisitedGCed,
    1765             :                               aResults.mFreedRefCounted, aResults.mFreedGCed,
    1766             :                               sCCollectedWaitingForGC,
    1767           0 :                               sCCollectedZonesWaitingForGC,
    1768             :                               sLikelyShortLivingObjectsNeedingGC,
    1769           0 :                               aResults.mForcedGC,
    1770           0 :                               sForgetSkippableBeforeCC,
    1771           0 :                               minForgetSkippableTime / PR_USEC_PER_MSEC,
    1772             :                               sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
    1773           0 :                               (sTotalForgetSkippableTime / cleanups) /
    1774           0 :                               PR_USEC_PER_MSEC,
    1775           0 :                               sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
    1776           0 :                               sRemovedPurples);
    1777           0 :     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
    1778             :     if (observerService) {
    1779             :       observerService->NotifyObservers(nullptr, "cycle-collection-statistics", json.get());
    1780             :     }
    1781             :   }
    1782           0 : 
    1783           0 :   // Update global state to indicate we have just run a cycle collection.
    1784           0 :   sMinForgetSkippableTime = UINT32_MAX;
    1785           0 :   sMaxForgetSkippableTime = 0;
    1786           0 :   sTotalForgetSkippableTime = 0;
    1787           0 :   sRemovedPurples = 0;
    1788           0 :   sForgetSkippableBeforeCC = 0;
    1789           0 :   sNeedsFullCC = false;
    1790           0 :   sNeedsGCAfterCC = false;
    1791             :   gCCStats.Clear();
    1792             : }
    1793             : 
    1794           0 : // static
    1795             : bool
    1796           0 : InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
    1797           0 : {
    1798             :   nsJSContext::KillInterSliceGCRunner();
    1799             :   MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
    1800             :   // We use longer budgets when the CC has been locked out but the CC has tried
    1801           0 :   // to run since that means we may have significant amount garbage to collect
    1802           0 :   // and better to GC in several longer slices than in a very long one.
    1803           0 :   int64_t budget = aDeadline.IsNull() ?
    1804           0 :     int64_t(sActiveIntersliceGCBudget * 2) :
    1805           0 :     int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
    1806           0 :   if (sCCLockedOut && sCCLockedOutTime) {
    1807             :     int64_t lockedTime = PR_Now() - sCCLockedOutTime;
    1808           0 :     int32_t maxSliceGCBudget = sActiveIntersliceGCBudget * 10;
    1809           0 :     double percentOfLockedTime =
    1810             :       std::min((double)lockedTime / NS_MAX_CC_LOCKEDOUT_TIME, 1.0);
    1811           0 :     budget =
    1812             :       static_cast<int64_t>(
    1813             :         std::max((double)budget, percentOfLockedTime * maxSliceGCBudget));
    1814           0 :   }
    1815           0 : 
    1816           0 :   TimeStamp startTimeStamp = TimeStamp::Now();
    1817           0 :   TimeDuration duration = sGCUnnotifiedTotalTime;
    1818             :   uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
    1819             :   nsJSContext::GarbageCollectNow(aData ?
    1820             :                                    static_cast<JS::gcreason::Reason>(reason) :
    1821             :                                    JS::gcreason::INTER_SLICE_GC,
    1822           0 :                                  nsJSContext::IncrementalGC,
    1823             :                                  nsJSContext::NonShrinkingGC,
    1824           0 :                                  budget);
    1825           0 : 
    1826           0 :   sGCUnnotifiedTotalTime = TimeDuration();
    1827           0 :   TimeStamp now = TimeStamp::Now();
    1828           0 :   TimeDuration sliceDuration = now - startTimeStamp;
    1829           0 :   duration += sliceDuration;
    1830           0 :   if (duration.ToSeconds()) {
    1831           0 :     TimeDuration idleDuration;
    1832             :     if (!aDeadline.IsNull()) {
    1833           0 :       if (aDeadline < now) {
    1834             :         // This slice overflowed the idle period.
    1835             :         idleDuration = aDeadline - startTimeStamp;
    1836             :       } else {
    1837           0 :         // Note, we don't want to use duration here, since it may contain
    1838             :         // data also from JS engine triggered GC slices.
    1839             :         idleDuration = sliceDuration;
    1840             :       }
    1841             :     }
    1842           0 : 
    1843           0 :     uint32_t percent =
    1844             :       uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
    1845           0 :     Telemetry::Accumulate(Telemetry::GC_SLICE_DURING_IDLE, percent);
    1846             :   }
    1847             :   return true;
    1848             : }
    1849             : 
    1850           0 : // static
    1851             : void
    1852           0 : GCTimerFired(nsITimer *aTimer, void *aClosure)
    1853           0 : {
    1854           0 :   nsJSContext::KillGCTimer();
    1855             :   nsJSContext::KillInterSliceGCRunner();
    1856             :   if (sShuttingDown) {
    1857             :     return;
    1858             :   }
    1859           0 : 
    1860             :   // Now start the actual GC after initial timer has fired.
    1861           0 :   sInterSliceGCRunner = IdleTaskRunner::Create([aClosure](TimeStamp aDeadline) {
    1862             :     return InterSliceGCRunnerFired(aDeadline, aClosure);
    1863             :   }, "GCTimerFired::InterSliceGCRunnerFired",
    1864             :      NS_INTERSLICE_GC_DELAY,
    1865           0 :      sActiveIntersliceGCBudget,
    1866           0 :      false,
    1867             :      []{ return sShuttingDown; },
    1868             :      TaskCategory::GarbageCollection);
    1869             : }
    1870             : 
    1871           0 : // static
    1872             : void
    1873           0 : ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
    1874           0 : {
    1875             :   nsJSContext::KillShrinkingGCTimer();
    1876             :   sIsCompactingOnUserInactive = true;
    1877           0 :   nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
    1878           0 :                                  nsJSContext::IncrementalGC,
    1879             :                                  nsJSContext::ShrinkingGC);
    1880             : }
    1881           0 : 
    1882             : static bool
    1883           0 : ShouldTriggerCC(uint32_t aSuspected)
    1884           0 : {
    1885           0 :   return sNeedsFullCC ||
    1886           0 :          aSuspected > NS_CC_PURPLE_LIMIT ||
    1887             :          (aSuspected > NS_CC_FORCED_PURPLE_LIMIT &&
    1888             :           TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
    1889             : }
    1890           0 : 
    1891             : static bool
    1892           0 : CCRunnerFired(TimeStamp aDeadline)
    1893             : {
    1894             :   if (sDidShutdown) {
    1895             :     return false;
    1896             :   }
    1897           0 : 
    1898           0 :   static uint32_t ccDelay = NS_CC_DELAY;
    1899             :   if (sCCLockedOut) {
    1900           0 :     ccDelay = NS_CC_DELAY / 3;
    1901           0 : 
    1902             :     PRTime now = PR_Now();
    1903             :     if (sCCLockedOutTime == 0) {
    1904             :       // Reset sCCRunnerFireCount so that we run forgetSkippable
    1905             :       // often enough before CC. Because of reduced ccDelay
    1906             :       // forgetSkippable will be called just a few times.
    1907           0 :       // NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
    1908           0 :       // forgetSkippable and CycleCollectNow eventually.
    1909           0 :       sCCRunnerFireCount = 0;
    1910             :       sCCLockedOutTime = now;
    1911           0 :       return false;
    1912             :     }
    1913             :     if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
    1914             :       return false;
    1915             :     }
    1916           0 :   }
    1917             : 
    1918           0 :   ++sCCRunnerFireCount;
    1919             : 
    1920             :   bool didDoWork = false;
    1921             : 
    1922             :   // During early timer fires, we only run forgetSkippable. During the first
    1923             :   // late timer fire, we decide if we are going to have a second and final
    1924           0 :   // late timer fire, where we may begin to run the CC. Should run at least one
    1925           0 :   // early timer fire to allow cleanup before the CC.
    1926           0 :   int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1);
    1927           0 :   bool isLateTimerFire = sCCRunnerFireCount > numEarlyTimerFires;
    1928           0 :   uint32_t suspected = nsCycleCollector_suspectedCount();
    1929           0 :   if (isLateTimerFire && ShouldTriggerCC(suspected)) {
    1930           0 :     if (sCCRunnerFireCount == numEarlyTimerFires + 1) {
    1931           0 :       FireForgetSkippable(suspected, true, aDeadline);
    1932             :       didDoWork = true;
    1933             :       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
    1934             :         // Our efforts to avoid a CC have failed, so we return to let the
    1935           0 :         // timer fire once more to trigger a CC.
    1936             : 
    1937           0 :         if (!aDeadline.IsNull() && TimeStamp::Now() < aDeadline) {
    1938             :           // Clear content unbinder before the first CC slice.
    1939           0 :           Element::ClearContentUnbinder();
    1940             : 
    1941           0 :           if (TimeStamp::Now() < aDeadline) {
    1942             :             // And trigger deferred deletion too.
    1943             :             nsCycleCollector_doDeferredDeletion();
    1944             :           }
    1945             :         }
    1946             :         return didDoWork;
    1947             :       }
    1948             :     } else {
    1949             :       // We are in the final timer fire and still meet the conditions for
    1950           0 :       // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
    1951           0 :       // any because that will allow us to include the GC time in the CC pause.
    1952             :       nsJSContext::RunCycleCollectorSlice(aDeadline);
    1953           0 :       didDoWork = true;
    1954           0 :     }
    1955             :   } else if (((sPreviousSuspectedCount + 100) <= suspected) ||
    1956             :              (sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
    1957           0 :       // Only do a forget skippable if there are more than a few new objects
    1958           0 :       // or we're doing the initial forget skippables.
    1959             :       FireForgetSkippable(suspected, false, aDeadline);
    1960             :       didDoWork = true;
    1961           0 :   }
    1962           0 : 
    1963             :   if (isLateTimerFire) {
    1964             :     ccDelay = NS_CC_DELAY;
    1965             : 
    1966           0 :     // We have either just run the CC or decided we don't want to run the CC
    1967           0 :     // next time, so kill the timer.
    1968             :     sPreviousSuspectedCount = 0;
    1969             :     nsJSContext::KillCCRunner();
    1970             :   }
    1971             : 
    1972             :   return didDoWork;
    1973             : }
    1974             : 
    1975           0 : // static
    1976             : uint32_t
    1977           0 : nsJSContext::CleanupsSinceLastGC()
    1978             : {
    1979             :   return sCleanupsSinceLastGC;
    1980             : }
    1981             : 
    1982           0 : // static
    1983             : void
    1984           0 : nsJSContext::LoadStart()
    1985           0 : {
    1986           0 :   sLoadingInProgress = true;
    1987             :   ++sPendingLoadCount;
    1988             : }
    1989             : 
    1990           0 : // static
    1991             : void
    1992           0 : nsJSContext::LoadEnd()
    1993             : {
    1994             :   if (!sLoadingInProgress)
    1995             :     return;
    1996             : 
    1997           0 :   // sPendingLoadCount is not a well managed load counter (and doesn't
    1998           0 :   // need to be), so make sure we don't make it wrap backwards here.
    1999           0 :   if (sPendingLoadCount > 0) {
    2000             :     --sPendingLoadCount;
    2001             :     return;
    2002           0 :   }
    2003             : 
    2004             :   sLoadingInProgress = false;
    2005             : }
    2006             : 
    2007             : // Check all of the various collector timers/runners and see if they are waiting to fire.
    2008             : // This does not check sFullGCTimer, as that's a more expensive collection we run
    2009             : // on a long timer.
    2010             : 
    2011           0 : // static
    2012             : void
    2013             : nsJSContext::RunNextCollectorTimer(JS::gcreason::Reason aReason,
    2014           0 :                                    mozilla::TimeStamp aDeadline)
    2015           0 : {
    2016             :   if (sShuttingDown) {
    2017             :     return;
    2018           0 :   }
    2019           0 : 
    2020           0 :   if (sGCTimer) {
    2021             :     GCTimerFired(nullptr, reinterpret_cast<void*>(aReason));
    2022             :     return;
    2023           0 :   }
    2024           0 : 
    2025           0 :   nsCOMPtr<nsIRunnable> runnable;
    2026           0 :   if (sInterSliceGCRunner) {
    2027             :     sInterSliceGCRunner->SetDeadline(aDeadline);
    2028             :     runnable = sInterSliceGCRunner;
    2029             :   } else {
    2030           0 :     // Check the CC timers after the GC timers, because the CC timers won't do
    2031             :     // anything if a GC is in progress.
    2032             :     MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
    2033           0 :   }
    2034           0 : 
    2035           0 :   if (sCCRunner) {
    2036             :     sCCRunner->SetDeadline(aDeadline);
    2037             :     runnable = sCCRunner;
    2038           0 :   }
    2039           0 : 
    2040           0 :   if (sICCRunner) {
    2041             :     sICCRunner->SetDeadline(aDeadline);
    2042             :     runnable = sICCRunner;
    2043           0 :   }
    2044           0 : 
    2045             :   if (runnable) {
    2046             :     runnable->Run();
    2047             :   }
    2048             : }
    2049             : 
    2050           0 : // static
    2051             : void
    2052             : nsJSContext::MaybeRunNextCollectorSlice(nsIDocShell* aDocShell,
    2053           0 :                                         JS::gcreason::Reason aReason)
    2054           0 : {
    2055             :   if (!aDocShell || !XRE_IsContentProcess()) {
    2056             :     return;
    2057           0 :   }
    2058           0 : 
    2059           0 :   nsCOMPtr<nsIDocShellTreeItem> root;
    2060             :   aDocShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
    2061           0 :   if (root == aDocShell) {
    2062             :     // We don't want to run collectors when loading the top level page.
    2063             :     return;
    2064           0 :   }
    2065           0 : 
    2066           0 :   nsIDocument* rootDocument = root->GetDocument();
    2067           0 :   if (!rootDocument ||
    2068             :       rootDocument->GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE ||
    2069             :       rootDocument->IsInBackgroundWindow()) {
    2070             :     return;
    2071           0 :   }
    2072           0 : 
    2073             :   nsIPresShell* presShell = rootDocument->GetShell();
    2074             :   if (!presShell) {
    2075             :     return;
    2076           0 :   }
    2077           0 : 
    2078             :   nsViewManager* vm = presShell->GetViewManager();
    2079             :   if (!vm) {
    2080             :     return;
    2081             :   }
    2082           0 : 
    2083           0 :   // GetLastUserEventTime returns microseconds.
    2084             :   uint32_t lastEventTime = 0;
    2085           0 :   vm->GetLastUserEventTime(lastEventTime);
    2086             :   uint32_t currentTime =
    2087             :     PR_IntervalToMicroseconds(PR_IntervalNow());
    2088           0 :   // Only try to trigger collectors more often if user hasn't interacted with
    2089             :   // the page for awhile.
    2090           0 :   if ((currentTime - lastEventTime) >
    2091             :       (NS_USER_INTERACTION_INTERVAL * PR_USEC_PER_MSEC)) {
    2092             :     Maybe<TimeStamp> next = nsRefreshDriver::GetNextTickHint();
    2093           0 :     // Try to not delay the next RefreshDriver tick, so give a reasonable
    2094           0 :     // deadline for collectors.
    2095             :     if (next.isSome()) {
    2096             :       nsJSContext::RunNextCollectorTimer(aReason, next.value());
    2097             :     }
    2098             :   }
    2099             : }
    2100             : 
    2101           0 : // static
    2102             : void
    2103             : nsJSContext::PokeGC(JS::gcreason::Reason aReason,
    2104             :                     JSObject* aObj,
    2105           0 :                     int aDelay)
    2106             : {
    2107             :   if (sShuttingDown) {
    2108             :     return;
    2109           0 :   }
    2110           0 : 
    2111           0 :   if (aObj) {
    2112           0 :     JS::Zone* zone = JS::GetObjectZone(aObj);
    2113           0 :     CycleCollectedJSRuntime::Get()->AddZoneWaitingForGC(zone);
    2114             :   } else if (aReason != JS::gcreason::CC_WAITING) {
    2115             :     sNeedsFullGC = true;
    2116           0 :   }
    2117             : 
    2118             :   if (sGCTimer || sInterSliceGCRunner) {
    2119             :     // There's already a timer for GC'ing, just return
    2120             :     return;
    2121           0 :   }
    2122             : 
    2123           0 :   if (sCCRunner) {
    2124             :     // Make sure CC is called...
    2125           0 :     sNeedsFullCC = true;
    2126           0 :     // and GC after it.
    2127             :     sNeedsGCAfterCC = true;
    2128             :     return;
    2129           0 :   }
    2130             : 
    2131             :   if (sICCRunner) {
    2132           0 :     // Make sure GC is called after the current CC completes.
    2133           0 :     // No need to set sNeedsFullCC because we are currently running a CC.
    2134             :     sNeedsGCAfterCC = true;
    2135             :     return;
    2136             :   }
    2137             : 
    2138           0 :   static bool first = true;
    2139             : 
    2140             :   NS_NewTimerWithFuncCallback(&sGCTimer,
    2141             :                               GCTimerFired,
    2142           0 :                               reinterpret_cast<void *>(aReason),
    2143             :                               aDelay
    2144             :                               ? aDelay
    2145             :                               : (first
    2146             :                                  ? NS_FIRST_GC_DELAY
    2147             :                                  : NS_GC_DELAY),
    2148           0 :                               nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
    2149             :                               "GCTimerFired",
    2150           0 :                               SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
    2151             : 
    2152             :   first = false;
    2153             : }
    2154             : 
    2155           0 : // static
    2156             : void
    2157           0 : nsJSContext::PokeShrinkingGC()
    2158             : {
    2159             :   if (sShrinkingGCTimer || sShuttingDown) {
    2160             :     return;
    2161           1 :   }
    2162             : 
    2163             :   NS_NewTimerWithFuncCallback(&sShrinkingGCTimer,
    2164             :                               ShrinkingGCTimerFired, nullptr,
    2165             :                               StaticPrefs::javascript_options_compact_on_user_inactive_delay(),
    2166           0 :                               nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
    2167             :                               "ShrinkingGCTimerFired",
    2168             :                               SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
    2169             : }
    2170             : 
    2171           0 : // static
    2172             : void
    2173           0 : nsJSContext::MaybePokeCC()
    2174             : {
    2175             :   if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
    2176             :     return;
    2177           0 :   }
    2178           0 : 
    2179             :   uint32_t sinceLastCCEnd = TimeUntilNow(sLastCCEndTime);
    2180             :   if (sinceLastCCEnd && sinceLastCCEnd < NS_CC_DELAY) {
    2181             :     return;
    2182           0 :   }
    2183           0 : 
    2184             :   if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
    2185             :     sCCRunnerFireCount = 0;
    2186           0 : 
    2187             :     // We can kill some objects before running forgetSkippable.
    2188             :     nsCycleCollector_dispatchDeferredDeletion();
    2189           0 : 
    2190             :     sCCRunner =
    2191             :       IdleTaskRunner::Create(CCRunnerFired,
    2192             :                              "MaybePokeCC::CCRunnerFired",
    2193           0 :                              NS_CC_SKIPPABLE_DELAY,
    2194           0 :                              kForgetSkippableSliceDuration, true,
    2195             :                              []{ return sShuttingDown; },
    2196             :                              TaskCategory::GarbageCollection);
    2197             :   }
    2198             : }
    2199             : 
    2200           0 : //static
    2201             : void
    2202           0 : nsJSContext::KillGCTimer()
    2203           0 : {
    2204           0 :   if (sGCTimer) {
    2205             :     sGCTimer->Cancel();
    2206           0 :     NS_RELEASE(sGCTimer);
    2207             :   }
    2208             : }
    2209           0 : 
    2210             : void
    2211           0 : nsJSContext::KillFullGCTimer()
    2212           0 : {
    2213           0 :   if (sFullGCTimer) {
    2214             :     sFullGCTimer->Cancel();
    2215           0 :     NS_RELEASE(sFullGCTimer);
    2216             :   }
    2217             : }
    2218           0 : 
    2219             : void
    2220           0 : nsJSContext::KillInterSliceGCRunner()
    2221           0 : {
    2222             :   if (sInterSliceGCRunner) {
    2223             :     sInterSliceGCRunner->Cancel();
    2224           0 :     sInterSliceGCRunner = nullptr;
    2225             :   }
    2226             : }
    2227             : 
    2228           0 : //static
    2229             : void
    2230           0 : nsJSContext::KillShrinkingGCTimer()
    2231           0 : {
    2232           0 :   if (sShrinkingGCTimer) {
    2233             :     sShrinkingGCTimer->Cancel();
    2234           0 :     NS_RELEASE(sShrinkingGCTimer);
    2235             :   }
    2236             : }
    2237             : 
    2238           0 : //static
    2239             : void
    2240           0 : nsJSContext::KillCCRunner()
    2241           0 : {
    2242           0 :   sCCLockedOutTime = 0;
    2243             :   if (sCCRunner) {
    2244             :     sCCRunner->Cancel();
    2245           0 :     sCCRunner = nullptr;
    2246             :   }
    2247             : }
    2248             : 
    2249           0 : //static
    2250             : void
    2251           0 : nsJSContext::KillICCRunner()
    2252             : {
    2253           0 :   sCCLockedOutTime = 0;
    2254           0 : 
    2255             :   if (sICCRunner) {
    2256             :     sICCRunner->Cancel();
    2257           0 :     sICCRunner = nullptr;
    2258             :   }
    2259           0 : }
    2260             : 
    2261             : class NotifyGCEndRunnable : public Runnable
    2262             : {
    2263             :   nsString mMessage;
    2264           0 : 
    2265           0 : public:
    2266           0 :   explicit NotifyGCEndRunnable(nsString&& aMessage)
    2267             :     : mozilla::Runnable("NotifyGCEndRunnable")
    2268           0 :     , mMessage(std::move(aMessage))
    2269             :   {
    2270             :   }
    2271             : 
    2272             :   NS_DECL_NSIRUNNABLE
    2273             : };
    2274           0 : 
    2275             : NS_IMETHODIMP
    2276           0 : NotifyGCEndRunnable::Run()
    2277             : {
    2278           0 :   MOZ_ASSERT(NS_IsMainThread());
    2279           0 : 
    2280             :   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
    2281             :   if (!observerService) {
    2282             :     return NS_OK;
    2283           0 :   }
    2284           0 : 
    2285           0 :   const char16_t oomMsg[3] = { '{', '}', 0 };
    2286             :   const char16_t *toSend = mMessage.get() ? mMessage.get() : oomMsg;
    2287           0 :   observerService->NotifyObservers(nullptr, "garbage-collection-statistics", toSend);
    2288             : 
    2289             :   return NS_OK;
    2290             : }
    2291           0 : 
    2292             : static void
    2293           0 : DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescription &aDesc)
    2294             : {
    2295           0 :   NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
    2296             : 
    2297             :   switch (aProgress) {
    2298           0 :     case JS::GC_CYCLE_BEGIN: {
    2299           0 :       // Prevent cycle collections and shrinking during incremental GC.
    2300             :       sCCLockedOut = true;
    2301             :       break;
    2302             :     }
    2303           0 : 
    2304             :     case JS::GC_CYCLE_END: {
    2305           0 :       PRTime delta = GetCollectionTimeDelta();
    2306           0 : 
    2307           0 :       if (StaticPrefs::javascript_options_mem_log()) {
    2308           0 :         nsString gcstats;
    2309           0 :         gcstats.Adopt(aDesc.formatSummaryMessage(aCx));
    2310           0 :         nsAutoString prefix;
    2311           0 :         nsTextFormatter::ssprintf(prefix, u"GC(T+%.1f)[%s-%i] ",
    2312           0 :                                   double(delta) / PR_USEC_PER_SEC,
    2313           0 :                                   ProcessNameForCollectorLog(), getpid());
    2314           0 :         nsString msg = prefix + gcstats;
    2315           0 :         nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    2316             :         if (cs) {
    2317             :           cs->LogStringMessage(msg.get());
    2318             :         }
    2319           0 :       }
    2320           0 : 
    2321           0 :       if (!sShuttingDown) {
    2322           0 :         if (StaticPrefs::javascript_options_mem_notify() ||
    2323           0 :             Telemetry::CanRecordExtended()) {
    2324           0 :           nsString json;
    2325           0 :           json.Adopt(aDesc.formatJSON(aCx, PR_Now()));
    2326             :           RefPtr<NotifyGCEndRunnable> notify = new NotifyGCEndRunnable(std::move(json));
    2327             :           SystemGroup::Dispatch(TaskCategory::GarbageCollection, notify.forget());
    2328             :         }
    2329           0 :       }
    2330           0 : 
    2331             :       sCCLockedOut = false;
    2332             :       sIsCompactingOnUserInactive = false;
    2333           0 : 
    2334             :       // May need to kill the inter-slice GC runner
    2335           0 :       nsJSContext::KillInterSliceGCRunner();
    2336           0 : 
    2337           0 :       sCCollectedWaitingForGC = 0;
    2338           0 :       sCCollectedZonesWaitingForGC = 0;
    2339           0 :       sLikelyShortLivingObjectsNeedingGC = 0;
    2340           0 :       sCleanupsSinceLastGC = 0;
    2341           0 :       sNeedsFullCC = true;
    2342             :       sHasRunGC = true;
    2343           0 :       nsJSContext::MaybePokeCC();
    2344           0 : 
    2345             :       if (aDesc.isZone_) {
    2346             :         if (!sFullGCTimer && !sShuttingDown) {
    2347             :           NS_NewTimerWithFuncCallback(&sFullGCTimer,
    2348             :                                       FullGCTimerFired,
    2349             :                                       nullptr,
    2350             :                                       NS_FULL_GC_DELAY,
    2351           0 :                                       nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
    2352             :                                       "FullGCTimerFired",
    2353             :                                       SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
    2354           0 :         }
    2355             :       } else {
    2356             :         nsJSContext::KillFullGCTimer();
    2357           0 :       }
    2358           0 : 
    2359             :       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
    2360             :         nsCycleCollector_dispatchDeferredDeletion();
    2361           0 :       }
    2362           0 : 
    2363             :       if (!aDesc.isZone_) {
    2364             :         sNeedsFullGC = false;
    2365             :       }
    2366             : 
    2367             :       break;
    2368             :     }
    2369             : 
    2370             :     case JS::GC_SLICE_BEGIN:
    2371             :       break;
    2372             : 
    2373           0 :     case JS::GC_SLICE_END:
    2374             :       sGCUnnotifiedTotalTime +=
    2375             :         aDesc.lastSliceEnd(aCx) - aDesc.lastSliceStart(aCx);
    2376           0 : 
    2377           0 :       // Schedule another GC slice if the GC has more work to do.
    2378             :       nsJSContext::KillInterSliceGCRunner();
    2379           0 :       if (!sShuttingDown && !aDesc.isComplete_) {
    2380             :         sInterSliceGCRunner =
    2381           0 :           IdleTaskRunner::Create([](TimeStamp aDeadline) {
    2382             :             return InterSliceGCRunnerFired(aDeadline, nullptr);
    2383             :           }, "DOMGCSliceCallback::InterSliceGCRunnerFired",
    2384             :              NS_INTERSLICE_GC_DELAY,
    2385           0 :              sActiveIntersliceGCBudget,
    2386           0 :              false,
    2387             :              []{ return sShuttingDown; },
    2388             :              TaskCategory::GarbageCollection);
    2389           0 :       }
    2390           0 : 
    2391             :       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
    2392             :         nsCycleCollector_dispatchDeferredDeletion();
    2393           0 :       }
    2394           0 : 
    2395           0 :       if (StaticPrefs::javascript_options_mem_log()) {
    2396           0 :         nsString gcstats;
    2397           0 :         gcstats.Adopt(aDesc.formatSliceMessage(aCx));
    2398           0 :         nsAutoString prefix;
    2399           0 :         nsTextFormatter::ssprintf(prefix, u"[%s-%i] ",
    2400           0 :                                   ProcessNameForCollectorLog(), getpid());
    2401           0 :         nsString msg = prefix + gcstats;
    2402           0 :         nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    2403             :         if (cs) {
    2404             :           cs->LogStringMessage(msg.get());
    2405             :         }
    2406             :       }
    2407             : 
    2408             :       break;
    2409           0 : 
    2410             :     default:
    2411             :       MOZ_CRASH("Unexpected GCProgress value");
    2412           0 :   }
    2413           0 : 
    2414             :   if (sPrevGCSliceCallback) {
    2415             :     (*sPrevGCSliceCallback)(aCx, aProgress, aDesc);
    2416           0 :   }
    2417             : 
    2418             : }
    2419           0 : 
    2420             : void
    2421           0 : nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
    2422           0 : {
    2423             :   mWindowProxy = aWindowProxy;
    2424             : }
    2425           0 : 
    2426             : JSObject*
    2427           0 : nsJSContext::GetWindowProxy()
    2428             : {
    2429             :   return mWindowProxy;
    2430             : }
    2431           0 : 
    2432             : void
    2433           0 : nsJSContext::LikelyShortLivingObjectCreated()
    2434           0 : {
    2435             :   ++sLikelyShortLivingObjectsNeedingGC;
    2436             : }
    2437           0 : 
    2438             : void
    2439             : mozilla::dom::StartupJSEnvironment()
    2440           0 : {
    2441           0 :   // initialize all our statics, so that we can restart XPCOM
    2442           0 :   sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
    2443           0 :   sCCLockedOut = false;
    2444           0 :   sCCLockedOutTime = 0;
    2445           0 :   sLastCCEndTime = TimeStamp();
    2446           0 :   sHasRunGC = false;
    2447           0 :   sPendingLoadCount = 0;
    2448           0 :   sLoadingInProgress = false;
    2449           0 :   sCCollectedWaitingForGC = 0;
    2450           0 :   sCCollectedZonesWaitingForGC = 0;
    2451           0 :   sLikelyShortLivingObjectsNeedingGC = 0;
    2452           0 :   sNeedsFullCC = false;
    2453           0 :   sNeedsFullGC = true;
    2454           0 :   sNeedsGCAfterCC = false;
    2455           0 :   sIsInitialized = false;
    2456           0 :   sDidShutdown = false;
    2457           0 :   sShuttingDown = false;
    2458             :   gCCStats.Init();
    2459             : }
    2460           0 : 
    2461             : static void
    2462           0 : SetGCParameter(JSGCParamKey aParam, uint32_t aValue)
    2463           0 : {
    2464           0 :   AutoJSAPI jsapi;
    2465           0 :   jsapi.Init();
    2466             :   JS_SetGCParameter(jsapi.cx(), aParam, aValue);
    2467             : }
    2468           0 : 
    2469             : static void
    2470           0 : ResetGCParameter(JSGCParamKey aParam)
    2471           0 : {
    2472           0 :   AutoJSAPI jsapi;
    2473           0 :   jsapi.Init();
    2474             :   JS_ResetGCParameter(jsapi.cx(), aParam);
    2475             : }
    2476           0 : 
    2477             : static void
    2478           0 : SetMemoryPrefChangedCallbackMB(const char* aPrefName, void* aClosure)
    2479             : {
    2480           0 :   int32_t prefMB = Preferences::GetInt(aPrefName, -1);
    2481           0 :   // handle overflow and negative pref values
    2482           0 :   CheckedInt<int32_t> prefB = CheckedInt<int32_t>(prefMB) * 1024 * 1024;
    2483             :   if (prefB.isValid() && prefB.value() >= 0) {
    2484           0 :     SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, prefB.value());
    2485             :   } else {
    2486           0 :     ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
    2487             :   }
    2488             : }
    2489           0 : 
    2490             : static void
    2491           0 : SetMemoryNurseryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
    2492             : {
    2493           0 :   int32_t prefMB = Preferences::GetInt(aPrefName, -1);
    2494           0 :   // handle overflow and negative pref values
    2495           0 :   CheckedInt<int32_t> prefB = CheckedInt<int32_t>(prefMB) * 1024;
    2496             :   if (prefB.isValid() && prefB.value() >= 0) {
    2497           0 :     SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, prefB.value());
    2498             :   } else {
    2499           0 :     ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
    2500             :   }
    2501             : }
    2502           0 : 
    2503             : static void
    2504           0 : SetMemoryPrefChangedCallbackInt(const char* aPrefName, void* aClosure)
    2505             : {
    2506           0 :   int32_t pref = Preferences::GetInt(aPrefName, -1);
    2507           0 :   // handle overflow and negative pref values
    2508             :   if (pref >= 0 && pref < 10000) {
    2509           0 :     SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, pref);
    2510             :   } else {
    2511           0 :     ResetGCParameter((JSGCParamKey)(uintptr_t)aClosure);
    2512             :   }
    2513             : }
    2514           0 : 
    2515             : static void
    2516           0 : SetMemoryPrefChangedCallbackBool(const char* aPrefName, void* aClosure)
    2517           0 : {
    2518           0 :   bool pref = Preferences::GetBool(aPrefName);
    2519             :   SetGCParameter((JSGCParamKey)(uintptr_t)aClosure, pref);
    2520             : }
    2521           0 : 
    2522             : static void
    2523           0 : SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
    2524           0 : {
    2525             :   bool enableZoneGC = Preferences::GetBool("javascript.options.mem.gc_per_zone");
    2526           0 :   bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
    2527             :   JSGCMode mode;
    2528           0 :   if (enableIncrementalGC) {
    2529             :     mode = JSGC_MODE_INCREMENTAL;
    2530             :   } else if (enableZoneGC) {
    2531           0 :     mode = JSGC_MODE_ZONE;
    2532             :   } else {
    2533             :     mode = JSGC_MODE_GLOBAL;
    2534           0 :   }
    2535           0 : 
    2536             :   SetGCParameter(JSGC_MODE, mode);
    2537             : }
    2538           0 : 
    2539             : static void
    2540           0 : SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
    2541             : {
    2542           0 :   int32_t pref = Preferences::GetInt(aPrefName, -1);
    2543           0 :   // handle overflow and negative pref values
    2544           0 :   if (pref > 0 && pref < 100000) {
    2545             :     sActiveIntersliceGCBudget = pref;
    2546           0 :     SetGCParameter(JSGC_SLICE_TIME_BUDGET, pref);
    2547             :   } else {
    2548           0 :     ResetGCParameter(JSGC_SLICE_TIME_BUDGET);
    2549             :   }
    2550             : }
    2551           0 : 
    2552             : static void
    2553           0 : SetIncrementalCCPrefChangedCallback(const char* aPrefName, void* aClosure)
    2554           0 : {
    2555           0 :   bool pref = Preferences::GetBool(aPrefName);
    2556             :   sIncrementalCC = pref;
    2557             : }
    2558           0 : 
    2559             : static bool
    2560             : AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
    2561             :                            const char16_t* aBegin,
    2562             :                            const char16_t* aLimit,
    2563             :                            size_t* aSize,
    2564             :                            const uint8_t** aMemory,
    2565             :                            intptr_t *aHandle)
    2566           0 : {
    2567             :   nsIPrincipal* principal =
    2568           0 :     nsJSPrincipals::get(JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal)));
    2569             :   return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory,
    2570             :                                       aHandle);
    2571             : }
    2572           0 : 
    2573             : static JS::AsmJSCacheResult
    2574             : AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
    2575             :                             const char16_t* aBegin,
    2576             :                             const char16_t* aEnd,
    2577             :                             size_t aSize,
    2578             :                             uint8_t** aMemory,
    2579             :                             intptr_t* aHandle)
    2580           0 : {
    2581             :   nsIPrincipal* principal =
    2582           0 :     nsJSPrincipals::get(JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal)));
    2583             :   return asmjscache::OpenEntryForWrite(principal, aBegin, aEnd, aSize, aMemory,
    2584             :                                        aHandle);
    2585             : }
    2586             : 
    2587           0 : class JSDispatchableRunnable final : public Runnable
    2588           0 : {
    2589           0 :   ~JSDispatchableRunnable()
    2590           0 :   {
    2591             :     MOZ_ASSERT(!mDispatchable);
    2592             :   }
    2593           0 : 
    2594           0 : public:
    2595           0 :   explicit JSDispatchableRunnable(JS::Dispatchable* aDispatchable)
    2596             :     : mozilla::Runnable("JSDispatchableRunnable")
    2597           0 :     , mDispatchable(aDispatchable)
    2598           0 :   {
    2599             :     MOZ_ASSERT(mDispatchable);
    2600             :   }
    2601           0 : 
    2602             : protected:
    2603           0 :   NS_IMETHOD Run() override
    2604             :   {
    2605           0 :     MOZ_ASSERT(NS_IsMainThread());
    2606           0 : 
    2607             :     AutoJSAPI jsapi;
    2608             :     jsapi.Init();
    2609           0 : 
    2610             :     JS::Dispatchable::MaybeShuttingDown maybeShuttingDown =
    2611           0 :       sShuttingDown ? JS::Dispatchable::ShuttingDown : JS::Dispatchable::NotShuttingDown;
    2612           0 : 
    2613             :     mDispatchable->run(jsapi.cx(), maybeShuttingDown);
    2614           0 :     mDispatchable = nullptr;  // mDispatchable may delete itself
    2615             : 
    2616             :     return NS_OK;
    2617             :   }
    2618             : 
    2619             : private:
    2620             :   JS::Dispatchable* mDispatchable;
    2621             : };
    2622           0 : 
    2623             : static bool
    2624           0 : DispatchToEventLoop(void* closure, JS::Dispatchable* aDispatchable)
    2625             : {
    2626             :   MOZ_ASSERT(!closure);
    2627             : 
    2628             :   // This callback may execute either on the main thread or a random JS-internal
    2629             :   // helper thread. This callback can be called during shutdown so we cannot
    2630             :   // simply NS_DispatchToMainThread. Failure during shutdown is expected and
    2631           0 :   // properly handled by the JS engine.
    2632           0 : 
    2633             :   nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
    2634             :   if (!mainTarget) {
    2635             :     return false;
    2636           0 :   }
    2637           0 : 
    2638             :   RefPtr<JSDispatchableRunnable> r = new JSDispatchableRunnable(aDispatchable);
    2639             :   MOZ_ALWAYS_SUCCEEDS(mainTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
    2640             :   return true;
    2641             : }
    2642           0 : 
    2643             : static bool
    2644             : ConsumeStream(JSContext* aCx,
    2645             :               JS::HandleObject aObj,
    2646             :               JS::MimeType aMimeType,
    2647           0 :               JS::StreamConsumer* aConsumer)
    2648             : {
    2649             :   return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, nullptr);
    2650             : }
    2651           0 : 
    2652             : void
    2653           0 : nsJSContext::EnsureStatics()
    2654           0 : {
    2655           0 :   if (sIsInitialized) {
    2656             :     if (!nsContentUtils::XPConnect()) {
    2657           0 :       MOZ_CRASH();
    2658             :     }
    2659             :     return;
    2660             :   }
    2661           0 : 
    2662             :   // Let's make sure that our main thread is the same as the xpcom main thread.
    2663           0 :   MOZ_ASSERT(NS_IsMainThread());
    2664           0 : 
    2665             :   AutoJSAPI jsapi;
    2666           0 :   jsapi.Init();
    2667             : 
    2668             :   sPrevGCSliceCallback = JS::SetGCSliceCallback(jsapi.cx(), DOMGCSliceCallback);
    2669             : 
    2670             :   // Set up the asm.js cache callbacks
    2671             :   static const JS::AsmJSCacheOps asmJSCacheOps = {
    2672             :     AsmJSCacheOpenEntryForRead,
    2673             :     asmjscache::CloseEntryForRead,
    2674             :     AsmJSCacheOpenEntryForWrite,
    2675           0 :     asmjscache::CloseEntryForWrite
    2676             :   };
    2677           0 :   JS::SetAsmJSCacheOps(jsapi.cx(), &asmJSCacheOps);
    2678           0 : 
    2679             :   JS::InitDispatchToEventLoop(jsapi.cx(), DispatchToEventLoop, nullptr);
    2680             :   JS::InitConsumeStreamCallback(jsapi.cx(), ConsumeStream);
    2681             : 
    2682             :   // Set these global xpconnect options...
    2683           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB,
    2684             :                                        "javascript.options.mem.high_water_mark",
    2685             :                                        (void*)JSGC_MAX_MALLOC_BYTES);
    2686             : 
    2687           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB,
    2688             :                                        "javascript.options.mem.max",
    2689             :                                        (void*)JSGC_MAX_BYTES);
    2690           0 :   Preferences::RegisterCallbackAndCall(SetMemoryNurseryMaxPrefChangedCallback,
    2691             :                                        "javascript.options.mem.nursery.max_kb",
    2692             :                                        (void*)JSGC_MAX_NURSERY_BYTES);
    2693           0 : 
    2694             :   Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
    2695             :                                        "javascript.options.mem.gc_per_zone");
    2696           0 : 
    2697             :   Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
    2698             :                                        "javascript.options.mem.gc_incremental");
    2699           0 : 
    2700             :   Preferences::RegisterCallbackAndCall(SetMemoryGCSliceTimePrefChangedCallback,
    2701             :                                        "javascript.options.mem.gc_incremental_slice_ms");
    2702             : 
    2703           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool,
    2704             :                                        "javascript.options.mem.gc_compacting",
    2705             :                                        (void *)JSGC_COMPACTING_ENABLED);
    2706             : 
    2707           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2708             :                                        "javascript.options.mem.gc_high_frequency_time_limit_ms",
    2709             :                                        (void *)JSGC_HIGH_FREQUENCY_TIME_LIMIT);
    2710             : 
    2711           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool,
    2712             :                                        "javascript.options.mem.gc_dynamic_mark_slice",
    2713             :                                        (void *)JSGC_DYNAMIC_MARK_SLICE);
    2714             : 
    2715           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackBool,
    2716             :                                        "javascript.options.mem.gc_dynamic_heap_growth",
    2717             :                                        (void *)JSGC_DYNAMIC_HEAP_GROWTH);
    2718             : 
    2719           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2720             :                                        "javascript.options.mem.gc_low_frequency_heap_growth",
    2721             :                                        (void *)JSGC_LOW_FREQUENCY_HEAP_GROWTH);
    2722             : 
    2723           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2724             :                                        "javascript.options.mem.gc_high_frequency_heap_growth_min",
    2725             :                                        (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
    2726             : 
    2727           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2728             :                                        "javascript.options.mem.gc_high_frequency_heap_growth_max",
    2729             :                                        (void *)JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
    2730             : 
    2731           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2732             :                                        "javascript.options.mem.gc_high_frequency_low_limit_mb",
    2733             :                                        (void *)JSGC_HIGH_FREQUENCY_LOW_LIMIT);
    2734             : 
    2735           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2736             :                                        "javascript.options.mem.gc_high_frequency_high_limit_mb",
    2737             :                                        (void *)JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
    2738             : 
    2739           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2740             :                                        "javascript.options.mem.gc_allocation_threshold_mb",
    2741             :                                        (void *)JSGC_ALLOCATION_THRESHOLD);
    2742           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2743             :                                        "javascript.options.mem.gc_allocation_threshold_factor",
    2744             :                                        (void *)JSGC_ALLOCATION_THRESHOLD_FACTOR);
    2745           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2746             :                                        "javascript.options.mem.gc_allocation_threshold_factor_avoid_interrupt",
    2747             :                                        (void *)JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT);
    2748           0 : 
    2749             :   Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback,
    2750             :                                        "dom.cycle_collector.incremental");
    2751             : 
    2752           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2753             :                                        "javascript.options.mem.gc_min_empty_chunk_count",
    2754             :                                        (void *)JSGC_MIN_EMPTY_CHUNK_COUNT);
    2755             : 
    2756           0 :   Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackInt,
    2757             :                                        "javascript.options.mem.gc_max_empty_chunk_count",
    2758           0 :                                        (void *)JSGC_MAX_EMPTY_CHUNK_COUNT);
    2759           0 : 
    2760           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    2761             :   if (!obs) {
    2762             :     MOZ_CRASH();
    2763           0 :   }
    2764           0 : 
    2765           0 :   nsIObserver* observer = new nsJSEnvironmentObserver();
    2766           0 :   obs->AddObserver(observer, "memory-pressure", false);
    2767           0 :   obs->AddObserver(observer, "user-interaction-inactive", false);
    2768           0 :   obs->AddObserver(observer, "user-interaction-active", false);
    2769             :   obs->AddObserver(observer, "quit-application", false);
    2770           0 :   obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
    2771             : 
    2772             :   sIsInitialized = true;
    2773             : }
    2774           0 : 
    2775             : void
    2776           0 : mozilla::dom::ShutdownJSEnvironment()
    2777             : {
    2778           0 :   KillTimers();
    2779           0 : 
    2780           0 :   sShuttingDown = true;
    2781             :   sDidShutdown = true;
    2782             : }
    2783             : 
    2784             : // A fast-array class for JS.  This class supports both nsIJSScriptArray and
    2785             : // nsIArray.  If it is JS itself providing and consuming this class, all work
    2786             : // can be done via nsIJSScriptArray, and avoid the conversion of elements
    2787             : // to/from nsISupports.
    2788             : // When consumed by non-JS (eg, another script language), conversion is done
    2789             : // on-the-fly.
    2790             : class nsJSArgArray final : public nsIJSArgArray {
    2791             : public:
    2792             :   nsJSArgArray(JSContext *aContext, uint32_t argc, const JS::Value* argv,
    2793             :                nsresult *prv);
    2794             : 
    2795           0 :   // nsISupports
    2796             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    2797             :   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
    2798             :                                                          nsIJSArgArray)
    2799             : 
    2800             :   // nsIArray
    2801             :   NS_DECL_NSIARRAY
    2802             : 
    2803             :   // nsIJSArgArray
    2804             :   nsresult GetArgs(uint32_t* argc, void** argv) override;
    2805             : 
    2806             :   void ReleaseJSObjects();
    2807             : 
    2808             : protected:
    2809             :   ~nsJSArgArray();
    2810             :   JSContext *mContext;
    2811             :   JS::Heap<JS::Value> *mArgv;
    2812             :   uint32_t mArgc;
    2813           0 : };
    2814           0 : 
    2815             : nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc,
    2816             :                            const JS::Value* argv, nsresult *prv)
    2817           0 :   : mContext(aContext)
    2818             :   , mArgv(nullptr)
    2819             :   , mArgc(argc)
    2820             : {
    2821           0 :   // copy the array - we don't know its lifetime, and ours is tied to xpcom
    2822           0 :   // refcounting.
    2823           0 :   if (argc) {
    2824           0 :     mArgv = new (fallible) JS::Heap<JS::Value>[argc];
    2825           0 :     if (!mArgv) {
    2826             :       *prv = NS_ERROR_OUT_OF_MEMORY;
    2827             :       return;
    2828             :     }
    2829             :   }
    2830             : 
    2831           0 :   // Callers are allowed to pass in a null argv even for argc > 0. They can
    2832           0 :   // then use GetArgs to initialize the values.
    2833           0 :   if (argv) {
    2834             :     for (uint32_t i = 0; i < argc; ++i)
    2835             :       mArgv[i] = argv[i];
    2836           0 :   }
    2837             : 
    2838             :   if (argc > 0) {
    2839             :     mozilla::HoldJSObjects(this);
    2840           0 :   }
    2841             : 
    2842             :   *prv = NS_OK;
    2843           0 : }
    2844             : 
    2845           0 : nsJSArgArray::~nsJSArgArray()
    2846           0 : {
    2847             :   ReleaseJSObjects();
    2848             : }
    2849           0 : 
    2850             : void
    2851           0 : nsJSArgArray::ReleaseJSObjects()
    2852           0 : {
    2853             :   if (mArgv) {
    2854           0 :     delete [] mArgv;
    2855           0 :   }
    2856             :   if (mArgc > 0) {
    2857             :     mArgc = 0;
    2858           0 :     mozilla::DropJSObjects(this);
    2859             :   }
    2860             : }
    2861             : 
    2862             : // QueryInterface implementation for nsJSArgArray
    2863           0 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
    2864           0 : 
    2865           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
    2866           0 :   tmp->ReleaseJSObjects();
    2867           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    2868             : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
    2869           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    2870           0 : 
    2871           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
    2872           0 :   if (tmp->mArgv) {
    2873             :     for (uint32_t i = 0; i < tmp->mArgc; ++i) {
    2874             :       NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv[i])
    2875           0 :     }
    2876             :   }
    2877           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
    2878           0 : 
    2879           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
    2880           0 :   NS_INTERFACE_MAP_ENTRY(nsIArray)
    2881           0 :   NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
    2882             :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
    2883           0 : NS_INTERFACE_MAP_END
    2884           0 : 
    2885             : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
    2886             : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
    2887           0 : 
    2888             : nsresult
    2889           0 : nsJSArgArray::GetArgs(uint32_t *argc, void **argv)
    2890           0 : {
    2891           0 :   *argv = (void *)mArgv;
    2892             :   *argc = mArgc;
    2893             :   return NS_OK;
    2894             : }
    2895           0 : 
    2896             : // nsIArray impl
    2897           0 : NS_IMETHODIMP nsJSArgArray::GetLength(uint32_t *aLength)
    2898           0 : {
    2899             :   *aLength = mArgc;
    2900             :   return NS_OK;
    2901           0 : }
    2902             : 
    2903           0 : NS_IMETHODIMP nsJSArgArray::QueryElementAt(uint32_t index, const nsIID & uuid, void * *result)
    2904           0 : {
    2905             :   *result = nullptr;
    2906             :   if (index >= mArgc)
    2907           0 :     return NS_ERROR_INVALID_ARG;
    2908             : 
    2909           0 :   if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
    2910           0 :     // Have to copy a Heap into a Rooted to work with it.
    2911           0 :     JS::Rooted<JS::Value> val(mContext, mArgv[index]);
    2912             :     return nsContentUtils::XPConnect()->JSToVariant(mContext, val,
    2913           0 :                                                     (nsIVariant **)result);
    2914           0 :   }
    2915             :   NS_WARNING("nsJSArgArray only handles nsIVariant");
    2916             :   return NS_ERROR_NO_INTERFACE;
    2917           0 : }
    2918             : 
    2919           0 : NS_IMETHODIMP nsJSArgArray::IndexOf(uint32_t startIndex, nsISupports *element, uint32_t *_retval)
    2920             : {
    2921             :   return NS_ERROR_NOT_IMPLEMENTED;
    2922           0 : }
    2923             : 
    2924           0 : NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
    2925             : {
    2926             :   return NS_ERROR_NOT_IMPLEMENTED;
    2927             : }
    2928           0 : 
    2929             : // The factory function
    2930             : nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc,
    2931             :                          const JS::Value* argv, nsIJSArgArray **aArray)
    2932           0 : {
    2933           0 :   nsresult rv;
    2934             :   nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc, argv, &rv);
    2935             :   if (NS_FAILED(rv)) {
    2936           0 :     return rv;
    2937           0 :   }
    2938             :   ret.forget(aArray);
    2939             :   return NS_OK;
    2940             : }

Generated by: LCOV version 1.13-14-ga5dd952