LCOV - code coverage report
Current view: top level - js/xpconnect/src - XPCJSContext.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 160 463 34.6 %
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: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
       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             : /* Per JSContext object */
       8             : 
       9             : #include "mozilla/MemoryReporting.h"
      10             : #include "mozilla/UniquePtr.h"
      11             : 
      12             : #include "xpcprivate.h"
      13             : #include "xpcpublic.h"
      14             : #include "XPCWrapper.h"
      15             : #include "XPCJSMemoryReporter.h"
      16             : #include "WrapperFactory.h"
      17             : #include "mozJSComponentLoader.h"
      18             : #include "nsAutoPtr.h"
      19             : #include "nsNetUtil.h"
      20             : #include "nsThreadUtils.h"
      21             : 
      22             : #include "nsIMemoryInfoDumper.h"
      23             : #include "nsIMemoryReporter.h"
      24             : #include "nsIObserverService.h"
      25             : #include "nsIDebug2.h"
      26             : #include "nsIDocShell.h"
      27             : #include "nsIRunnable.h"
      28             : #include "nsPIDOMWindow.h"
      29             : #include "nsPrintfCString.h"
      30             : #include "mozilla/Preferences.h"
      31             : #include "mozilla/Telemetry.h"
      32             : #include "mozilla/Services.h"
      33             : #include "mozilla/dom/ScriptSettings.h"
      34             : 
      35             : #include "nsContentUtils.h"
      36             : #include "nsCCUncollectableMarker.h"
      37             : #include "nsCycleCollectionNoteRootCallback.h"
      38             : #include "nsCycleCollector.h"
      39             : #include "jsapi.h"
      40             : #include "js/MemoryMetrics.h"
      41             : #include "mozilla/dom/BindingUtils.h"
      42             : #include "mozilla/dom/Element.h"
      43             : #include "mozilla/dom/ScriptLoader.h"
      44             : #include "mozilla/dom/WindowBinding.h"
      45             : #include "mozilla/extensions/WebExtensionPolicy.h"
      46             : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
      47             : #include "mozilla/Atomics.h"
      48             : #include "mozilla/Attributes.h"
      49             : #include "mozilla/ProcessHangMonitor.h"
      50             : #include "mozilla/Sprintf.h"
      51             : #include "mozilla/ThreadLocal.h"
      52             : #include "mozilla/UniquePtrExtensions.h"
      53             : #include "mozilla/Unused.h"
      54             : #include "AccessCheck.h"
      55             : #include "nsGlobalWindow.h"
      56             : #include "nsAboutProtocolUtils.h"
      57             : 
      58             : #include "GeckoProfiler.h"
      59             : #include "nsIInputStream.h"
      60             : #include "nsIXULRuntime.h"
      61             : #include "nsJSPrincipals.h"
      62             : #include "ExpandedPrincipal.h"
      63             : #include "SystemPrincipal.h"
      64             : 
      65             : #if defined(XP_LINUX) && !defined(ANDROID)
      66             : // For getrlimit and min/max.
      67             : #include <algorithm>
      68             : #include <sys/resource.h>
      69             : #endif
      70             : 
      71             : #ifdef XP_WIN
      72             : #include <windows.h>
      73             : #endif
      74             : 
      75             : static MOZ_THREAD_LOCAL(XPCJSContext*) gTlsContext;
      76             : 
      77             : using namespace mozilla;
      78             : using namespace xpc;
      79             : using namespace JS;
      80             : using mozilla::dom::AutoEntryScript;
      81             : 
      82             : static void WatchdogMain(void* arg);
      83             : class Watchdog;
      84             : class WatchdogManager;
      85             : class MOZ_RAII AutoLockWatchdog final
      86             : {
      87             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
      88             :     Watchdog* const mWatchdog;
      89             : 
      90             :   public:
      91             :     explicit AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
      92             :     ~AutoLockWatchdog();
      93             : };
      94             : 
      95             : class Watchdog
      96             : {
      97             :   public:
      98           1 :     explicit Watchdog(WatchdogManager* aManager)
      99           1 :       : mManager(aManager)
     100             :       , mLock(nullptr)
     101             :       , mWakeup(nullptr)
     102             :       , mThread(nullptr)
     103             :       , mHibernating(false)
     104             :       , mInitialized(false)
     105             :       , mShuttingDown(false)
     106           2 :       , mMinScriptRunTimeSeconds(1)
     107           1 :     {}
     108           0 :     ~Watchdog() { MOZ_ASSERT(!Initialized()); }
     109             : 
     110             :     WatchdogManager* Manager() { return mManager; }
     111             :     bool Initialized() { return mInitialized; }
     112             :     bool ShuttingDown() { return mShuttingDown; }
     113             :     PRLock* GetLock() { return mLock; }
     114             :     bool Hibernating() { return mHibernating; }
     115           0 :     void WakeUp()
     116             :     {
     117           0 :         MOZ_ASSERT(Initialized());
     118           0 :         MOZ_ASSERT(Hibernating());
     119           0 :         mHibernating = false;
     120           0 :         PR_NotifyCondVar(mWakeup);
     121           0 :     }
     122             : 
     123             :     //
     124             :     // Invoked by the main thread only.
     125             :     //
     126             : 
     127           1 :     void Init()
     128             :     {
     129           0 :         MOZ_ASSERT(NS_IsMainThread());
     130           1 :         mLock = PR_NewLock();
     131           0 :         if (!mLock)
     132           0 :             MOZ_CRASH("PR_NewLock failed.");
     133             : 
     134           1 :         mWakeup = PR_NewCondVar(mLock);
     135           1 :         if (!mWakeup)
     136           0 :             MOZ_CRASH("PR_NewCondVar failed.");
     137             : 
     138             :         {
     139           2 :             AutoLockWatchdog lock(this);
     140             : 
     141             :             // Gecko uses thread private for accounting and has to clean up at thread exit.
     142             :             // Therefore, even though we don't have a return value from the watchdog, we need to
     143             :             // join it on shutdown.
     144           1 :             mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
     145             :                                       PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
     146             :                                       PR_JOINABLE_THREAD, 0);
     147           1 :             if (!mThread)
     148           0 :                 MOZ_CRASH("PR_CreateThread failed!");
     149             : 
     150             :             // WatchdogMain acquires the lock and then asserts mInitialized. So
     151             :             // make sure to set mInitialized before releasing the lock here so
     152             :             // that it's atomic with the creation of the thread.
     153           1 :             mInitialized = true;
     154             :         }
     155           0 :     }
     156             : 
     157           0 :     void Shutdown()
     158             :     {
     159           0 :         MOZ_ASSERT(NS_IsMainThread());
     160           0 :         MOZ_ASSERT(Initialized());
     161             :         {   // Scoped lock.
     162           0 :             AutoLockWatchdog lock(this);
     163             : 
     164             :             // Signal to the watchdog thread that it's time to shut down.
     165           0 :             mShuttingDown = true;
     166             : 
     167             :             // Wake up the watchdog, and wait for it to call us back.
     168           0 :             PR_NotifyCondVar(mWakeup);
     169             :         }
     170             : 
     171           0 :         PR_JoinThread(mThread);
     172             : 
     173             :         // The thread sets mShuttingDown to false as it exits.
     174           0 :         MOZ_ASSERT(!mShuttingDown);
     175             : 
     176             :         // Destroy state.
     177           0 :         mThread = nullptr;
     178           0 :         PR_DestroyCondVar(mWakeup);
     179           0 :         mWakeup = nullptr;
     180           0 :         PR_DestroyLock(mLock);
     181           0 :         mLock = nullptr;
     182             : 
     183             :         // All done.
     184           0 :         mInitialized = false;
     185           0 :     }
     186             : 
     187           0 :     void SetMinScriptRunTimeSeconds(int32_t seconds)
     188             :     {
     189             :         // This variable is atomic, and is set from the main thread without
     190             :         // locking.
     191           3 :         MOZ_ASSERT(seconds > 0);
     192           6 :         mMinScriptRunTimeSeconds = seconds;
     193           0 :     }
     194             : 
     195             :     //
     196             :     // Invoked by the watchdog thread only.
     197             :     //
     198             : 
     199           0 :     void Hibernate()
     200             :     {
     201           0 :         MOZ_ASSERT(!NS_IsMainThread());
     202           0 :         mHibernating = true;
     203           0 :         Sleep(PR_INTERVAL_NO_TIMEOUT);
     204           0 :     }
     205           0 :     void Sleep(PRIntervalTime timeout)
     206             :     {
     207           0 :         MOZ_ASSERT(!NS_IsMainThread());
     208          12 :         MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
     209           0 :     }
     210           0 :     void Finished()
     211             :     {
     212           0 :         MOZ_ASSERT(!NS_IsMainThread());
     213           0 :         mShuttingDown = false;
     214           0 :     }
     215             : 
     216             :     int32_t MinScriptRunTimeSeconds()
     217             :     {
     218          20 :         return mMinScriptRunTimeSeconds;
     219             :     }
     220             : 
     221             :   private:
     222             :     WatchdogManager* mManager;
     223             : 
     224             :     PRLock* mLock;
     225             :     PRCondVar* mWakeup;
     226             :     PRThread* mThread;
     227             :     bool mHibernating;
     228             :     bool mInitialized;
     229             :     bool mShuttingDown;
     230             :     mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
     231             : };
     232             : 
     233             : #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
     234             : #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
     235             : #define PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT "dom.max_ext_content_script_run_time"
     236             : 
     237             : class WatchdogManager : public nsIObserver
     238             : {
     239             :   public:
     240             : 
     241             :     NS_DECL_ISUPPORTS
     242           1 :     explicit WatchdogManager()
     243           6 :     {
     244             :         // All the timestamps start at zero.
     245           0 :         PodArrayZero(mTimestamps);
     246             : 
     247             :         // Register ourselves as an observer to get updates on the pref.
     248           1 :         mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
     249           1 :         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
     250           0 :         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
     251           0 :         mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
     252           0 :     }
     253             : 
     254             :   protected:
     255             : 
     256           0 :     virtual ~WatchdogManager()
     257           0 :     {
     258             :         // Shutting down the watchdog requires context-switching to the watchdog
     259             :         // thread, which isn't great to do in a destructor. So we require
     260             :         // consumers to shut it down manually before releasing it.
     261           0 :         MOZ_ASSERT(!mWatchdog);
     262           0 :     }
     263             : 
     264             :   public:
     265             : 
     266           0 :     void Shutdown()
     267             :     {
     268           0 :         mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
     269           0 :         mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
     270           0 :         mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
     271           0 :         mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
     272           0 :     }
     273             : 
     274           0 :     NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
     275             :                        const char16_t* aData) override
     276             :     {
     277           2 :         RefreshWatchdog();
     278           2 :         return NS_OK;
     279             :     }
     280             : 
     281             :     void
     282           1 :     RegisterContext(XPCJSContext* aContext)
     283             :     {
     284           0 :         MOZ_ASSERT(NS_IsMainThread());
     285           3 :         AutoLockWatchdog lock(mWatchdog);
     286             : 
     287           0 :         if (aContext->mActive == XPCJSContext::CONTEXT_ACTIVE) {
     288           0 :             mActiveContexts.insertBack(aContext);
     289             :         } else {
     290           1 :             mInactiveContexts.insertBack(aContext);
     291             :         }
     292             : 
     293             :         // Enable the watchdog, if appropriate.
     294           1 :         RefreshWatchdog();
     295           1 :     }
     296             : 
     297             :     void
     298           0 :     UnregisterContext(XPCJSContext* aContext)
     299             :     {
     300           0 :         MOZ_ASSERT(NS_IsMainThread());
     301           0 :         AutoLockWatchdog lock(mWatchdog);
     302             : 
     303             :         // aContext must be in one of our two lists, simply remove it.
     304           0 :         aContext->LinkedListElement<XPCJSContext>::remove();
     305             : 
     306             : #ifdef DEBUG
     307             :         // If this was the last context, we should have already shut down
     308             :         // the watchdog.
     309           0 :         if (mActiveContexts.isEmpty() && mInactiveContexts.isEmpty()) {
     310           0 :             MOZ_ASSERT(!mWatchdog);
     311             :         }
     312             : #endif
     313           0 :     }
     314             : 
     315             :     // Context statistics. These live on the watchdog manager, are written
     316             :     // from the main thread, and are read from the watchdog thread (holding
     317             :     // the lock in each case).
     318        2334 :     void RecordContextActivity(XPCJSContext* aContext, bool active)
     319             :     {
     320             :         // The watchdog reads this state, so acquire the lock before writing it.
     321        2334 :         MOZ_ASSERT(NS_IsMainThread());
     322        7002 :         AutoLockWatchdog lock(mWatchdog);
     323             : 
     324             :         // Write state.
     325        2334 :         aContext->mLastStateChange = PR_Now();
     326        2334 :         aContext->mActive = active ? XPCJSContext::CONTEXT_ACTIVE :
     327             :             XPCJSContext::CONTEXT_INACTIVE;
     328           0 :         UpdateContextLists(aContext);
     329             : 
     330             :         // The watchdog may be hibernating, waiting for the context to go
     331             :         // active. Wake it up if necessary.
     332        3501 :         if (active && mWatchdog && mWatchdog->Hibernating())
     333           0 :             mWatchdog->WakeUp();
     334           0 :     }
     335             : 
     336           0 :     bool IsAnyContextActive()
     337             :     {
     338           0 :         return !mActiveContexts.isEmpty();
     339             :     }
     340           0 :     PRTime TimeSinceLastActiveContext()
     341             :     {
     342             :         // Must be called on the watchdog thread with the lock held.
     343           2 :         MOZ_ASSERT(!NS_IsMainThread());
     344           2 :         PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
     345           0 :         MOZ_ASSERT(mActiveContexts.isEmpty());
     346           0 :         MOZ_ASSERT(!mInactiveContexts.isEmpty());
     347             : 
     348             :         // We store inactive contexts with the most recently added inactive
     349             :         // context at the end of the list.
     350           2 :         return PR_Now() - mInactiveContexts.getLast()->mLastStateChange;
     351             :     }
     352             : 
     353          11 :     void RecordTimestamp(WatchdogTimestampCategory aCategory)
     354             :     {
     355             :         // Must be called on the watchdog thread with the lock held.
     356          11 :         MOZ_ASSERT(!NS_IsMainThread());
     357          11 :         PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
     358           0 :         MOZ_ASSERT(aCategory != TimestampContextStateChange,
     359             :                    "Use RecordContextActivity to update this");
     360             : 
     361          11 :         mTimestamps[aCategory] = PR_Now();
     362          11 :     }
     363             : 
     364             :     PRTime GetContextTimestamp(XPCJSContext* aContext,
     365             :                                const AutoLockWatchdog& aProofOfLock)
     366             :     {
     367             :         return aContext->mLastStateChange;
     368             :     }
     369             : 
     370           0 :     PRTime GetTimestamp(WatchdogTimestampCategory aCategory,
     371             :                         const AutoLockWatchdog& aProofOfLock)
     372             :     {
     373           0 :         MOZ_ASSERT(aCategory != TimestampContextStateChange,
     374             :                    "Use GetContextTimestamp to retrieve this");
     375           0 :         return mTimestamps[aCategory];
     376             :     }
     377             : 
     378           0 :     Watchdog* GetWatchdog() { return mWatchdog; }
     379             : 
     380           0 :     void RefreshWatchdog()
     381             :     {
     382           0 :         bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
     383           6 :         if (wantWatchdog != !!mWatchdog) {
     384           0 :             if (wantWatchdog)
     385           0 :                 StartWatchdog();
     386             :             else
     387           0 :                 StopWatchdog();
     388             :         }
     389             : 
     390           6 :         if (mWatchdog) {
     391           3 :             int32_t contentTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CONTENT, 10);
     392           0 :             if (contentTime <= 0)
     393           0 :                 contentTime = INT32_MAX;
     394           0 :             int32_t chromeTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CHROME, 20);
     395           0 :             if (chromeTime <= 0)
     396           0 :                 chromeTime = INT32_MAX;
     397           0 :             int32_t extTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT, 5);
     398           0 :             if (extTime <= 0)
     399           0 :                 extTime = INT32_MAX;
     400           0 :             mWatchdog->SetMinScriptRunTimeSeconds(std::min({contentTime, chromeTime, extTime}));
     401             :         }
     402           0 :     }
     403             : 
     404           0 :     void StartWatchdog()
     405             :     {
     406           0 :         MOZ_ASSERT(!mWatchdog);
     407           2 :         mWatchdog = new Watchdog(this);
     408           0 :         mWatchdog->Init();
     409           0 :     }
     410             : 
     411           0 :     void StopWatchdog()
     412             :     {
     413           0 :         MOZ_ASSERT(mWatchdog);
     414           0 :         mWatchdog->Shutdown();
     415           0 :         mWatchdog = nullptr;
     416           0 :     }
     417             : 
     418             :     template<class Callback>
     419          10 :     void ForAllActiveContexts(Callback&& aCallback)
     420             :     {
     421             :         // This function must be called on the watchdog thread with the lock held.
     422          10 :         MOZ_ASSERT(!NS_IsMainThread());
     423          10 :         PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
     424             : 
     425           0 :         for (auto* context = mActiveContexts.getFirst(); context;
     426           1 :              context = context->LinkedListElement<XPCJSContext>::getNext()) {
     427           0 :             if (!aCallback(context)) {
     428             :                 return;
     429             :             }
     430             :         }
     431             :     }
     432             : 
     433             :   private:
     434        2334 :     void UpdateContextLists(XPCJSContext* aContext)
     435             :     {
     436             :         // Given aContext whose activity state or timestamp has just changed,
     437             :         // put it back in the proper position in the proper list.
     438        2334 :         aContext->LinkedListElement<XPCJSContext>::remove();
     439        2334 :         auto& list = aContext->mActive == XPCJSContext::CONTEXT_ACTIVE ?
     440           0 :             mActiveContexts : mInactiveContexts;
     441             : 
     442             :         // Either the new list is empty or aContext must be more recent than
     443             :         // the existing last element.
     444        2334 :         MOZ_ASSERT_IF(!list.isEmpty(),
     445             :                       list.getLast()->mLastStateChange < aContext->mLastStateChange);
     446           0 :         list.insertBack(aContext);
     447        2334 :     }
     448             : 
     449             :     LinkedList<XPCJSContext> mActiveContexts;
     450             :     LinkedList<XPCJSContext> mInactiveContexts;
     451             :     nsAutoPtr<Watchdog> mWatchdog;
     452             : 
     453             :     // We store ContextStateChange on the contexts themselves.
     454             :     PRTime mTimestamps[kWatchdogTimestampCategoryCount - 1];
     455             : };
     456             : 
     457         142 : NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
     458             : 
     459           0 : AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     460        4674 :   : mWatchdog(aWatchdog)
     461             : {
     462           0 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     463        2337 :     if (mWatchdog) {
     464           0 :         PR_Lock(mWatchdog->GetLock());
     465             :     }
     466           0 : }
     467             : 
     468           0 : AutoLockWatchdog::~AutoLockWatchdog()
     469             : {
     470           0 :     if (mWatchdog) {
     471        2335 :         PR_Unlock(mWatchdog->GetLock());
     472             :     }
     473           0 : }
     474             : 
     475             : static void
     476           1 : WatchdogMain(void* arg)
     477             : {
     478           0 :     AUTO_PROFILER_REGISTER_THREAD("JS Watchdog");
     479           1 :     NS_SetCurrentThreadName("JS Watchdog");
     480             : 
     481           0 :     Watchdog* self = static_cast<Watchdog*>(arg);
     482           1 :     WatchdogManager* manager = self->Manager();
     483             : 
     484             :     // Lock lasts until we return
     485           1 :     AutoLockWatchdog lock(self);
     486             : 
     487           0 :     MOZ_ASSERT(self->Initialized());
     488          12 :     while (!self->ShuttingDown()) {
     489             :         // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
     490           0 :         if (manager->IsAnyContextActive() ||
     491           2 :             manager->TimeSinceLastActiveContext() <= PRTime(2*PR_USEC_PER_SEC))
     492             :         {
     493           0 :             self->Sleep(PR_TicksPerSecond());
     494             :         } else {
     495           0 :             manager->RecordTimestamp(TimestampWatchdogHibernateStart);
     496           0 :             self->Hibernate();
     497           0 :             manager->RecordTimestamp(TimestampWatchdogHibernateStop);
     498             :         }
     499             : 
     500             :         // Rise and shine.
     501          11 :         manager->RecordTimestamp(TimestampWatchdogWakeup);
     502             : 
     503             :         // Don't request an interrupt callback unless the current script has
     504             :         // been running long enough that we might show the slow script dialog.
     505             :         // Triggering the callback from off the main thread can be expensive.
     506             : 
     507             :         // We want to avoid showing the slow script dialog if the user's laptop
     508             :         // goes to sleep in the middle of running a script. To ensure this, we
     509             :         // invoke the interrupt callback after only half the timeout has
     510             :         // elapsed. The callback simply records the fact that it was called in
     511             :         // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
     512             :         // seconds and invoke the callback again. This time around it sees
     513             :         // mSlowScriptSecondHalf is set and so it shows the slow script
     514             :         // dialog. If the computer is put to sleep during one of the (timeout/2)
     515             :         // periods, the script still has the other (timeout/2) seconds to
     516             :         // finish.
     517          11 :         if (!self->ShuttingDown() && manager->IsAnyContextActive()) {
     518          10 :             bool debuggerAttached = false;
     519           0 :             nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
     520           0 :             if (dbg)
     521           0 :                 dbg->GetIsDebuggerAttached(&debuggerAttached);
     522           0 :             if (debuggerAttached) {
     523             :                 // We won't be interrupting these scripts anyway.
     524           0 :                 continue;
     525             :             }
     526             : 
     527          10 :             PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
     528          50 :             manager->ForAllActiveContexts([usecs, manager, &lock](XPCJSContext* aContext) -> bool {
     529           0 :                 auto timediff = PR_Now() - manager->GetContextTimestamp(aContext, lock);
     530           0 :                 if (timediff > usecs) {
     531           0 :                     JS_RequestInterruptCallback(aContext->Context());
     532             :                     return true;
     533             :                 }
     534             :                 return false;
     535          10 :             });
     536             :         }
     537             :     }
     538             : 
     539             :     // Tell the manager that we've shut down.
     540           0 :     self->Finished();
     541           0 : }
     542             : 
     543             : PRTime
     544           0 : XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
     545             : {
     546           0 :     AutoLockWatchdog lock(mWatchdogManager->GetWatchdog());
     547           0 :     return aCategory == TimestampContextStateChange ?
     548           0 :         mWatchdogManager->GetContextTimestamp(this, lock) :
     549           0 :         mWatchdogManager->GetTimestamp(aCategory, lock);
     550             : }
     551             : 
     552             : void
     553           0 : xpc::SimulateActivityCallback(bool aActive)
     554             : {
     555           0 :     XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive);
     556           0 : }
     557             : 
     558             : // static
     559             : void
     560        2334 : XPCJSContext::ActivityCallback(void* arg, bool active)
     561             : {
     562           0 :     if (!active) {
     563        1167 :         ProcessHangMonitor::ClearHang();
     564             :     }
     565             : 
     566        2334 :     XPCJSContext* self = static_cast<XPCJSContext*>(arg);
     567        2334 :     self->mWatchdogManager->RecordContextActivity(self, active);
     568           0 : }
     569             : 
     570             : // static
     571             : bool
     572           1 : XPCJSContext::InterruptCallback(JSContext* cx)
     573             : {
     574           0 :     XPCJSContext* self = XPCJSContext::Get();
     575             : 
     576             :     // Now is a good time to turn on profiling if it's pending.
     577           1 :     PROFILER_JS_INTERRUPT_CALLBACK();
     578             : 
     579             :     // Normally we record mSlowScriptCheckpoint when we start to process an
     580             :     // event. However, we can run JS outside of event handlers. This code takes
     581             :     // care of that case.
     582           2 :     if (self->mSlowScriptCheckpoint.IsNull()) {
     583           1 :         self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
     584           0 :         self->mSlowScriptSecondHalf = false;
     585           0 :         self->mSlowScriptActualWait = mozilla::TimeDuration();
     586           0 :         self->mTimeoutAccumulated = false;
     587           0 :         return true;
     588             :     }
     589             : 
     590             :     // Sometimes we get called back during XPConnect initialization, before Gecko
     591             :     // has finished bootstrapping. Avoid crashing in nsContentUtils below.
     592           0 :     if (!nsContentUtils::IsInitialized())
     593             :         return true;
     594             : 
     595             :     // This is at least the second interrupt callback we've received since
     596             :     // returning to the event loop. See how long it's been, and what the limit
     597             :     // is.
     598           0 :     TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
     599             :     int32_t limit;
     600             : 
     601           0 :     nsString addonId;
     602             :     const char* prefName;
     603             : 
     604           0 :     auto principal = BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(cx));
     605           0 :     bool chrome = principal->Is<SystemPrincipal>();
     606           0 :     if (chrome) {
     607           0 :         prefName = PREF_MAX_SCRIPT_RUN_TIME_CHROME;
     608           0 :         limit = Preferences::GetInt(prefName, 20);
     609           0 :     } else if (auto policy = principal->ContentScriptAddonPolicy()) {
     610           0 :         policy->GetId(addonId);
     611           0 :         prefName = PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT;
     612           0 :         limit = Preferences::GetInt(prefName, 5);
     613             :     } else {
     614           0 :         prefName = PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
     615           0 :         limit = Preferences::GetInt(prefName, 10);
     616             :     }
     617             : 
     618             :     // If there's no limit, or we're within the limit, let it go.
     619           0 :     if (limit == 0 || duration.ToSeconds() < limit / 2.0)
     620             :         return true;
     621             : 
     622           0 :     self->mSlowScriptActualWait += duration;
     623             : 
     624             :     // In order to guard against time changes or laptops going to sleep, we
     625             :     // don't trigger the slow script warning until (limit/2) seconds have
     626             :     // elapsed twice.
     627           0 :     if (!self->mSlowScriptSecondHalf) {
     628           0 :         self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
     629           0 :         self->mSlowScriptSecondHalf = true;
     630           0 :         return true;
     631             :     }
     632             : 
     633             :     //
     634             :     // This has gone on long enough! Time to take action. ;-)
     635             :     //
     636             : 
     637             :     // Get the DOM window associated with the running script. If the script is
     638             :     // running in a non-DOM scope, we have to just let it keep running.
     639           0 :     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     640           0 :     RefPtr<nsGlobalWindowInner> win = WindowOrNull(global);
     641           0 :     if (!win && IsSandbox(global)) {
     642             :         // If this is a sandbox associated with a DOMWindow via a
     643             :         // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
     644             :         // and JetPack content scripts.
     645           0 :         JS::Rooted<JSObject*> proto(cx);
     646           0 :         if (!JS_GetPrototype(cx, global, &proto))
     647           0 :             return false;
     648           0 :         if (proto && xpc::IsSandboxPrototypeProxy(proto) &&
     649           0 :             (proto = js::CheckedUnwrap(proto, /* stopAtWindowProxy = */ false)))
     650             :         {
     651           0 :             win = WindowGlobalOrNull(proto);
     652             :         }
     653             :     }
     654             : 
     655           0 :     if (!win) {
     656           0 :         NS_WARNING("No active window");
     657           0 :         return true;
     658             :     }
     659             : 
     660           0 :     if (win->IsDying()) {
     661             :         // The window is being torn down. When that happens we try to prevent
     662             :         // the dispatch of new runnables, so it also makes sense to kill any
     663             :         // long-running script. The user is primarily interested in this page
     664             :         // going away.
     665             :         return false;
     666             :     }
     667             : 
     668             :     // Accumulate slow script invokation delay.
     669           0 :     if (!chrome && !self->mTimeoutAccumulated) {
     670           0 :       uint32_t delay = uint32_t(self->mSlowScriptActualWait.ToMilliseconds() - (limit * 1000.0));
     671           0 :       Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTIFY_DELAY, delay);
     672           0 :       self->mTimeoutAccumulated = true;
     673             :     }
     674             : 
     675             :     // Show the prompt to the user, and kill if requested.
     676           0 :     nsGlobalWindowInner::SlowScriptResponse response = win->ShowSlowScriptDialog(addonId);
     677           0 :     if (response == nsGlobalWindowInner::KillSlowScript) {
     678           0 :         if (Preferences::GetBool("dom.global_stop_script", true))
     679           0 :             xpc::Scriptability::Get(global).Block();
     680             :         return false;
     681             :     }
     682           0 :     if (response == nsGlobalWindowInner::KillScriptGlobal) {
     683           0 :         nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     684             : 
     685           0 :         if (!IsSandbox(global) || !obs)
     686             :             return false;
     687             : 
     688             :         // Notify the extensions framework that the sandbox should be killed.
     689           0 :         nsIXPConnect* xpc = nsContentUtils::XPConnect();
     690           0 :         JS::RootedObject wrapper(cx, JS_NewPlainObject(cx));
     691           0 :         nsCOMPtr<nsISupports> supports;
     692             : 
     693             :         // Store the sandbox object on the wrappedJSObject property of the
     694             :         // subject so that JS recipients can access the JS value directly.
     695           0 :         if (!wrapper ||
     696           0 :             !JS_DefineProperty(cx, wrapper, "wrappedJSObject", global, JSPROP_ENUMERATE) ||
     697           0 :             NS_FAILED(xpc->WrapJS(cx, wrapper, NS_GET_IID(nsISupports), getter_AddRefs(supports)))) {
     698             :             return false;
     699             :         }
     700             : 
     701           0 :         obs->NotifyObservers(supports, "kill-content-script-sandbox", nullptr);
     702           0 :         return false;
     703             :     }
     704             : 
     705             :     // The user chose to continue the script. Reset the timer, and disable this
     706             :     // machinery with a pref of the user opted out of future slow-script dialogs.
     707           0 :     if (response != nsGlobalWindowInner::ContinueSlowScriptAndKeepNotifying)
     708           0 :         self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
     709             : 
     710           0 :     if (response == nsGlobalWindowInner::AlwaysContinueSlowScript)
     711           0 :         Preferences::SetInt(prefName, 0);
     712             : 
     713             :     return true;
     714             : }
     715             : 
     716             : #define JS_OPTIONS_DOT_STR "javascript.options."
     717             : 
     718             : static mozilla::Atomic<bool> sDiscardSystemSource(false);
     719             : 
     720             : bool
     721          23 : xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
     722             : 
     723             : #ifdef DEBUG
     724             : static mozilla::Atomic<bool> sExtraWarningsForSystemJS(false);
     725          22 : bool xpc::ExtraWarningsForSystemJS() { return sExtraWarningsForSystemJS; }
     726             : #else
     727             : bool xpc::ExtraWarningsForSystemJS() { return false; }
     728             : #endif
     729             : 
     730             : static mozilla::Atomic<bool> sSharedMemoryEnabled(false);
     731             : 
     732             : bool
     733          44 : xpc::SharedMemoryEnabled() { return sSharedMemoryEnabled; }
     734             : 
     735             : static void
     736           2 : ReloadPrefsCallback(const char* pref, void* data)
     737             : {
     738           0 :     XPCJSContext* xpccx = static_cast<XPCJSContext*>(data);
     739           2 :     JSContext* cx = xpccx->Context();
     740             : 
     741           0 :     bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit");
     742           2 :     bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion");
     743           0 :     bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs");
     744           0 :     bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm");
     745           0 :     bool useWasmIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_ionjit");
     746           0 :     bool useWasmBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit");
     747             : #ifdef ENABLE_WASM_GC
     748           0 :     bool useWasmGc = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_gc");
     749             : #endif
     750             :     bool throwOnAsmJSValidationFailure = Preferences::GetBool(JS_OPTIONS_DOT_STR
     751           2 :                                                               "throw_on_asmjs_validation_failure");
     752           2 :     bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp");
     753             : 
     754           0 :     bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing");
     755             :     bool offthreadIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
     756           0 :                                                        "ion.offthread_compilation");
     757             :     bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
     758           0 :                                                  "baselinejit.unsafe_eager_compilation");
     759           2 :     bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
     760             : #ifdef DEBUG
     761           0 :     bool fullJitDebugChecks = Preferences::GetBool(JS_OPTIONS_DOT_STR "jit.full_debug_checks");
     762             : #endif
     763             : 
     764           2 :     int32_t baselineThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR "baselinejit.threshold", -1);
     765           2 :     int32_t ionThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR "ion.threshold", -1);
     766             : 
     767           0 :     sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
     768             : 
     769           0 :     bool useAsyncStack = Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncstack");
     770             : 
     771             :     bool throwOnDebuggeeWouldRun = Preferences::GetBool(JS_OPTIONS_DOT_STR
     772           2 :                                                         "throw_on_debuggee_would_run");
     773             : 
     774             :     bool dumpStackOnDebuggeeWouldRun = Preferences::GetBool(JS_OPTIONS_DOT_STR
     775           2 :                                                             "dump_stack_on_debuggee_would_run");
     776             : 
     777           0 :     bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror");
     778             : 
     779           0 :     bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
     780             : 
     781           0 :     bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
     782             : 
     783           0 :     bool spectreIndexMasking = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.index_masking");
     784             :     bool spectreObjectMitigationsBarriers =
     785           0 :         Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.object_mitigations.barriers");
     786             :     bool spectreObjectMitigationsMisc =
     787           0 :         Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.object_mitigations.misc");
     788             :     bool spectreStringMitigations =
     789           0 :         Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.string_mitigations");
     790           2 :     bool spectreValueMasking = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.value_masking");
     791           0 :     bool spectreJitToCxxCalls = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.jit_to_C++_calls");
     792             : 
     793           0 :     sSharedMemoryEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory");
     794             : 
     795             : #ifdef DEBUG
     796           4 :     sExtraWarningsForSystemJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict.debug");
     797             : #endif
     798             : 
     799             : #ifdef JS_GC_ZEAL
     800           2 :     int32_t zeal = Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal", -1);
     801             :     int32_t zeal_frequency =
     802             :         Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal.frequency",
     803           2 :                             JS_DEFAULT_ZEAL_FREQ);
     804           2 :     if (zeal >= 0) {
     805           0 :         JS_SetGCZeal(cx, (uint8_t)zeal, zeal_frequency);
     806             :     }
     807             : #endif // JS_GC_ZEAL
     808             : 
     809             : #ifdef FUZZING
     810             :     bool fuzzingEnabled = Preferences::GetBool("fuzzing.enabled");
     811             : #endif
     812             : 
     813           2 :     bool arrayProtoValues = Preferences::GetBool(JS_OPTIONS_DOT_STR "array_prototype_values");
     814             : 
     815           0 :     JS::ContextOptionsRef(cx).setBaseline(useBaseline)
     816           2 :                              .setIon(useIon)
     817           0 :                              .setAsmJS(useAsmJS)
     818           0 :                              .setWasm(useWasm)
     819           0 :                              .setWasmIon(useWasmIon)
     820           0 :                              .setWasmBaseline(useWasmBaseline)
     821             : #ifdef ENABLE_WASM_GC
     822           0 :                              .setWasmGc(useWasmGc)
     823             : #endif
     824           0 :                              .setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure)
     825           2 :                              .setNativeRegExp(useNativeRegExp)
     826           0 :                              .setAsyncStack(useAsyncStack)
     827           0 :                              .setThrowOnDebuggeeWouldRun(throwOnDebuggeeWouldRun)
     828           0 :                              .setDumpStackOnDebuggeeWouldRun(dumpStackOnDebuggeeWouldRun)
     829           0 :                              .setWerror(werror)
     830             : #ifdef FUZZING
     831             :                              .setFuzzing(fuzzingEnabled)
     832             : #endif
     833           2 :                              .setStreams(streams)
     834           2 :                              .setExtraWarnings(extraWarnings)
     835           0 :                              .setArrayProtoValues(arrayProtoValues);
     836             : 
     837           0 :     nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
     838           2 :     if (xr) {
     839           0 :         bool safeMode = false;
     840           0 :         xr->GetInSafeMode(&safeMode);
     841           0 :         if (safeMode) {
     842           0 :             JS::ContextOptionsRef(cx).disableOptionsForSafeMode();
     843             :         }
     844             :     }
     845             : 
     846           2 :     JS_SetParallelParsingEnabled(cx, parallelParsing);
     847           2 :     JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
     848           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
     849           0 :                                   useBaselineEager ? 0 : baselineThreshold);
     850           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER,
     851           0 :                                   useIonEager ? 0 : ionThreshold);
     852             : #ifdef DEBUG
     853           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_FULL_DEBUG_CHECKS, fullJitDebugChecks);
     854             : #endif
     855             : 
     856           2 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_INDEX_MASKING, spectreIndexMasking);
     857           2 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS_BARRIERS,
     858           0 :                                   spectreObjectMitigationsBarriers);
     859           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS_MISC,
     860           0 :                                   spectreObjectMitigationsMisc);
     861           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_STRING_MITIGATIONS,
     862           0 :                                   spectreStringMitigations);
     863           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_VALUE_MASKING, spectreValueMasking);
     864           0 :     JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_JIT_TO_CXX_CALLS,
     865           0 :                                   spectreJitToCxxCalls);
     866           0 : }
     867             : 
     868           0 : XPCJSContext::~XPCJSContext()
     869             : {
     870           0 :     MOZ_COUNT_DTOR_INHERITED(XPCJSContext, CycleCollectedJSContext);
     871             :     // Elsewhere we abort immediately if XPCJSContext initialization fails.
     872             :     // Therefore the context must be non-null.
     873           0 :     MOZ_ASSERT(MaybeContext());
     874             : 
     875             :     Preferences::UnregisterPrefixCallback(ReloadPrefsCallback,
     876             :                                           JS_OPTIONS_DOT_STR,
     877           0 :                                           this);
     878             : 
     879             : #ifdef FUZZING
     880             :     Preferences::UnregisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
     881             : #endif
     882             : 
     883           0 :     js::SetActivityCallback(Context(), nullptr, nullptr);
     884             : 
     885             :     // Clear any pending exception.  It might be an XPCWrappedJS, and if we try
     886             :     // to destroy it later we will crash.
     887           0 :     SetPendingException(nullptr);
     888             : 
     889             :     // If we're the last XPCJSContext around, clean up the watchdog manager.
     890           0 :     if (--sInstanceCount == 0) {
     891           0 :         if (mWatchdogManager->GetWatchdog()) {
     892           0 :             mWatchdogManager->StopWatchdog();
     893             :         }
     894             : 
     895           0 :         mWatchdogManager->UnregisterContext(this);
     896           0 :         mWatchdogManager->Shutdown();
     897             :         sWatchdogInstance = nullptr;
     898             :     } else {
     899             :         // Otherwise, simply remove ourselves from the list.
     900           0 :         mWatchdogManager->UnregisterContext(this);
     901             :     }
     902             : 
     903           0 :     if (mCallContext)
     904           0 :         mCallContext->SystemIsBeingShutDown();
     905             : 
     906           0 :     PROFILER_CLEAR_JS_CONTEXT();
     907             : 
     908           0 :     gTlsContext.set(nullptr);
     909           0 : }
     910             : 
     911           1 : XPCJSContext::XPCJSContext()
     912             :  : mCallContext(nullptr),
     913             :    mAutoRoots(nullptr),
     914             :    mResolveName(JSID_VOID),
     915             :    mResolvingWrapper(nullptr),
     916           1 :    mWatchdogManager(GetWatchdogManager()),
     917             :    mSlowScriptSecondHalf(false),
     918             :    mTimeoutAccumulated(false),
     919             :    mPendingResult(NS_OK),
     920             :    mActive(CONTEXT_INACTIVE),
     921           5 :    mLastStateChange(PR_Now())
     922             : {
     923           1 :     MOZ_COUNT_CTOR_INHERITED(XPCJSContext, CycleCollectedJSContext);
     924           1 :     MOZ_RELEASE_ASSERT(!gTlsContext.get());
     925           1 :     MOZ_ASSERT(mWatchdogManager);
     926           1 :     ++sInstanceCount;
     927           0 :     mWatchdogManager->RegisterContext(this);
     928           1 :     gTlsContext.set(this);
     929           0 : }
     930             : 
     931             : /* static */ XPCJSContext*
     932           0 : XPCJSContext::Get()
     933             : {
     934           0 :     return gTlsContext.get();
     935             : }
     936             : 
     937             : #ifdef XP_WIN
     938             : static size_t
     939             : GetWindowsStackSize()
     940             : {
     941             :     // First, get the stack base. Because the stack grows down, this is the top
     942             :     // of the stack.
     943             :     const uint8_t* stackTop;
     944             : #ifdef _WIN64
     945             :     PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
     946             :     stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
     947             : #else
     948             :     PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
     949             :     stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
     950             : #endif
     951             : 
     952             :     // Now determine the stack bottom. Note that we can't use tib->StackLimit,
     953             :     // because that's the size of the committed area and we're also interested
     954             :     // in the reserved pages below that.
     955             :     MEMORY_BASIC_INFORMATION mbi;
     956             :     if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
     957             :         MOZ_CRASH("VirtualQuery failed");
     958             : 
     959             :     const uint8_t* stackBottom = reinterpret_cast<const uint8_t*>(mbi.AllocationBase);
     960             : 
     961             :     // Do some sanity checks.
     962             :     size_t stackSize = size_t(stackTop - stackBottom);
     963             :     MOZ_RELEASE_ASSERT(stackSize >= 1 * 1024 * 1024);
     964             :     MOZ_RELEASE_ASSERT(stackSize <= 32 * 1024 * 1024);
     965             : 
     966             :     // Subtract 40 KB (Win32) or 80 KB (Win64) to account for things like
     967             :     // the guard page and large PGO stack frames.
     968             :     return stackSize - 10 * sizeof(uintptr_t) * 1024;
     969             : }
     970             : #endif
     971             : 
     972             : XPCJSRuntime*
     973       10186 : XPCJSContext::Runtime() const
     974             : {
     975       10186 :     return static_cast<XPCJSRuntime*>(CycleCollectedJSContext::Runtime());
     976             : }
     977             : 
     978             : CycleCollectedJSRuntime*
     979           0 : XPCJSContext::CreateRuntime(JSContext* aCx)
     980             : {
     981           0 :     return new XPCJSRuntime(aCx);
     982             : }
     983             : 
     984             : nsresult
     985           0 : XPCJSContext::Initialize(XPCJSContext* aPrimaryContext)
     986             : {
     987             :     nsresult rv;
     988           1 :     if (aPrimaryContext) {
     989           0 :         rv = CycleCollectedJSContext::InitializeNonPrimary(aPrimaryContext);
     990             :     } else {
     991           0 :         rv = CycleCollectedJSContext::Initialize(nullptr,
     992             :                                                  JS::DefaultHeapMaxBytes,
     993           1 :                                                  JS::DefaultNurseryBytes);
     994             :     }
     995           1 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     996             :       return rv;
     997             :     }
     998             : 
     999           0 :     MOZ_ASSERT(Context());
    1000           1 :     JSContext* cx = Context();
    1001             : 
    1002             :     // The JS engine permits us to set different stack limits for system code,
    1003             :     // trusted script, and untrusted script. We have tests that ensure that
    1004             :     // we can always execute 10 "heavy" (eval+with) stack frames deeper in
    1005             :     // privileged code. Our stack sizes vary greatly in different configurations,
    1006             :     // so satisfying those tests requires some care. Manual measurements of the
    1007             :     // number of heavy stack frames achievable gives us the following rough data,
    1008             :     // ordered by the effective categories in which they are grouped in the
    1009             :     // JS_SetNativeStackQuota call (which predates this analysis).
    1010             :     //
    1011             :     // The following "Stack Frames" numbers come from `chromeLimit` in
    1012             :     // js/xpconnect/tests/chrome/test_bug732665.xul
    1013             :     //
    1014             :     //  Platform   | Build | Stack Quota | Stack Frames | Stack Frame Size
    1015             :     // ------------+-------+-------------+--------------+------------------
    1016             :     //  OSX 64     | Opt   | 7MB         | 1331         | ~5.4k
    1017             :     //  OSX 64     | Debug | 7MB         | 1202         | ~6.0k
    1018             :     // ------------+-------+-------------+--------------+------------------
    1019             :     //  Linux 32   | Opt   | 7.875MB     | 2513         | ~3.2k
    1020             :     //  Linux 32   | Debug | 7.875MB     | 2146         | ~3.8k
    1021             :     // ------------+-------+-------------+--------------+------------------
    1022             :     //  Linux 64   | Opt   | 7.875MB     | 1360         | ~5.9k
    1023             :     //  Linux 64   | Debug | 7.875MB     | 1180         | ~6.8k
    1024             :     //  Linux 64   | ASan  | 7.875MB     | 473          | ~17.0k
    1025             :     // ------------+-------+-------------+--------------+------------------
    1026             :     //  Windows 32 | Opt   | 984k        | 188          | ~5.2k
    1027             :     //  Windows 32 | Debug | 984k        | 208          | ~4.7k
    1028             :     // ------------+-------+-------------+--------------+------------------
    1029             :     //  Windows 64 | Opt   | 1.922MB     | 189          | ~10.4k
    1030             :     //  Windows 64 | Debug | 1.922MB     | 175          | ~11.2k
    1031             :     //
    1032             :     // We tune the trusted/untrusted quotas for each configuration to achieve our
    1033             :     // invariants while attempting to minimize overhead. In contrast, our buffer
    1034             :     // between system code and trusted script is a very unscientific 10k.
    1035           1 :     const size_t kSystemCodeBuffer = 10 * 1024;
    1036             : 
    1037             :     // Our "default" stack is what we use in configurations where we don't have
    1038             :     // a compelling reason to do things differently. This is effectively 512KB
    1039             :     // on 32-bit platforms and 1MB on 64-bit platforms.
    1040           1 :     const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
    1041             : 
    1042             :     // Set stack sizes for different configurations. It's probably not great for
    1043             :     // the web to base this decision primarily on the default stack size that the
    1044             :     // underlying platform makes available, but that seems to be what we do. :-(
    1045             : 
    1046             : #if defined(XP_MACOSX) || defined(DARWIN)
    1047             :     // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
    1048             :     // and give trusted script 180k extra. The stack is huge on mac anyway.
    1049             :     const size_t kStackQuota = 7 * 1024 * 1024;
    1050             :     const size_t kTrustedScriptBuffer = 180 * 1024;
    1051             : #elif defined(XP_LINUX) && !defined(ANDROID)
    1052             :     // Most Linux distributions set default stack size to 8MB.  Use it as the
    1053             :     // maximum value.
    1054           1 :     const size_t kStackQuotaMax = 8 * 1024 * 1024;
    1055             : #  if defined(MOZ_ASAN) || defined(DEBUG)
    1056             :     // Bug 803182: account for the 4x difference in the size of js::Interpret
    1057             :     // between optimized and debug builds.  We use 2x since the JIT part
    1058             :     // doesn't increase much.
    1059             :     // See the standalone MOZ_ASAN branch below for the ASan case.
    1060           1 :     const size_t kStackQuotaMin = 2 * kDefaultStackQuota;
    1061             : #  else
    1062             :     const size_t kStackQuotaMin = kDefaultStackQuota;
    1063             : #  endif
    1064             :     // Allocate 128kB margin for the safe space.
    1065           1 :     const size_t kStackSafeMargin = 128 * 1024;
    1066             : 
    1067             :     struct rlimit rlim;
    1068             :     const size_t kStackQuota =
    1069           1 :         getrlimit(RLIMIT_STACK, &rlim) == 0
    1070           0 :         ? std::max(std::min(size_t(rlim.rlim_cur - kStackSafeMargin),
    1071           1 :                             kStackQuotaMax - kStackSafeMargin),
    1072           2 :                    kStackQuotaMin)
    1073           1 :         : kStackQuotaMin;
    1074             : #  if defined(MOZ_ASAN)
    1075             :     // See the standalone MOZ_ASAN branch below for the ASan case.
    1076             :     const size_t kTrustedScriptBuffer = 450 * 1024;
    1077             : #  else
    1078           1 :     const size_t kTrustedScriptBuffer = 180 * 1024;
    1079             : #  endif
    1080             : #elif defined(MOZ_ASAN)
    1081             :     // ASan requires more stack space due to red-zones, so give it double the
    1082             :     // default (1MB on 32-bit, 2MB on 64-bit). ASAN stack frame measurements
    1083             :     // were not taken at the time of this writing, so we hazard a guess that
    1084             :     // ASAN builds have roughly thrice the stack overhead as normal builds.
    1085             :     // On normal builds, the largest stack frame size we might encounter is
    1086             :     // 9.0k (see above), so let's use a buffer of 9.0 * 5 * 10 = 450k.
    1087             :     //
    1088             :     // FIXME: Does this branch make sense for Windows and Android?
    1089             :     // (See bug 1415195)
    1090             :     const size_t kStackQuota =  2 * kDefaultStackQuota;
    1091             :     const size_t kTrustedScriptBuffer = 450 * 1024;
    1092             : #elif defined(XP_WIN)
    1093             :     // 1MB is the default stack size on Windows. We use the -STACK linker flag
    1094             :     // (see WIN32_EXE_LDFLAGS in config/config.mk) to request a larger stack,
    1095             :     // so we determine the stack size at runtime.
    1096             :     const size_t kStackQuota = GetWindowsStackSize();
    1097             :     const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 180 * 1024   //win64
    1098             :                                                               : 120 * 1024;  //win32
    1099             : #elif defined(ANDROID)
    1100             :     // Android appears to have 1MB stacks. Allow the use of 3/4 of that size
    1101             :     // (768KB on 32-bit), since otherwise we can crash with a stack overflow
    1102             :     // when nearing the 1MB limit.
    1103             :     const size_t kStackQuota = kDefaultStackQuota + kDefaultStackQuota / 2;
    1104             :     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
    1105             : #else
    1106             :     // Catch-all configuration for other environments.
    1107             : #  if defined(DEBUG)
    1108             :     const size_t kStackQuota = 2 * kDefaultStackQuota;
    1109             : #  else
    1110             :     const size_t kStackQuota = kDefaultStackQuota;
    1111             : #  endif
    1112             :     // Given the numbers above, we use 50k and 100k trusted buffers on 32-bit
    1113             :     // and 64-bit respectively.
    1114             :     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
    1115             : #endif
    1116             : 
    1117             :     // Avoid an unused variable warning on platforms where we don't use the
    1118             :     // default.
    1119             :     (void) kDefaultStackQuota;
    1120             : 
    1121           1 :     JS_SetNativeStackQuota(cx,
    1122             :                            kStackQuota,
    1123             :                            kStackQuota - kSystemCodeBuffer,
    1124           1 :                            kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
    1125             : 
    1126           1 :     PROFILER_SET_JS_CONTEXT(cx);
    1127             : 
    1128           1 :     js::SetActivityCallback(cx, ActivityCallback, this);
    1129           1 :     JS_AddInterruptCallback(cx, InterruptCallback);
    1130             : 
    1131           0 :     if (!aPrimaryContext) {
    1132           1 :         Runtime()->Initialize(cx);
    1133             :     }
    1134             : 
    1135             :     // Watch for the JS boolean options.
    1136           0 :     ReloadPrefsCallback(nullptr, this);
    1137             :     Preferences::RegisterPrefixCallback(ReloadPrefsCallback,
    1138             :                                         JS_OPTIONS_DOT_STR,
    1139           0 :                                         this);
    1140             : 
    1141             : #ifdef FUZZING
    1142             :     Preferences::RegisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
    1143             : #endif
    1144             : 
    1145           1 :     return NS_OK;
    1146             : }
    1147             : 
    1148             : // static
    1149             : uint32_t
    1150             : XPCJSContext::sInstanceCount;
    1151             : 
    1152             : // static
    1153             : StaticRefPtr<WatchdogManager>
    1154           1 : XPCJSContext::sWatchdogInstance;
    1155             : 
    1156             : // static
    1157             : WatchdogManager*
    1158           1 : XPCJSContext::GetWatchdogManager()
    1159             : {
    1160           1 :     if (sWatchdogInstance) {
    1161             :         return sWatchdogInstance;
    1162             :     }
    1163             : 
    1164           0 :     MOZ_ASSERT(sInstanceCount == 0);
    1165           2 :     sWatchdogInstance = new WatchdogManager();
    1166           1 :     return sWatchdogInstance;
    1167             : }
    1168             : 
    1169             : // static
    1170             : void
    1171           1 : XPCJSContext::InitTLS()
    1172             : {
    1173           1 :     MOZ_RELEASE_ASSERT(gTlsContext.init());
    1174           0 : }
    1175             : 
    1176             : // static
    1177             : XPCJSContext*
    1178           1 : XPCJSContext::NewXPCJSContext(XPCJSContext* aPrimaryContext)
    1179             : {
    1180           1 :     XPCJSContext* self = new XPCJSContext();
    1181           0 :     nsresult rv = self->Initialize(aPrimaryContext);
    1182           1 :     if (NS_FAILED(rv)) {
    1183           0 :         MOZ_CRASH("new XPCJSContext failed to initialize.");
    1184             :     }
    1185             : 
    1186           1 :     if (self->Context())
    1187           1 :         return self;
    1188             : 
    1189           0 :     MOZ_CRASH("new XPCJSContext failed to initialize.");
    1190             : }
    1191             : 
    1192             : void
    1193           1 : XPCJSContext::BeforeProcessTask(bool aMightBlock)
    1194             : {
    1195        1626 :     MOZ_ASSERT(NS_IsMainThread());
    1196             : 
    1197             :     // Start the slow script timer.
    1198        1626 :     mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
    1199           1 :     mSlowScriptSecondHalf = false;
    1200        1626 :     mSlowScriptActualWait = mozilla::TimeDuration();
    1201        1626 :     mTimeoutAccumulated = false;
    1202             : 
    1203             :     // As we may be entering a nested event loop, we need to
    1204             :     // cancel any ongoing performance measurement.
    1205           0 :     js::ResetPerformanceMonitoring(Context());
    1206             : 
    1207        1626 :     CycleCollectedJSContext::BeforeProcessTask(aMightBlock);
    1208           0 : }
    1209             : 
    1210             : void
    1211           0 : XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
    1212             : {
    1213             :     // Now that we're back to the event loop, reset the slow script checkpoint.
    1214        1625 :     mSlowScriptCheckpoint = mozilla::TimeStamp();
    1215           0 :     mSlowScriptSecondHalf = false;
    1216             : 
    1217             :     // Call cycle collector occasionally.
    1218           0 :     MOZ_ASSERT(NS_IsMainThread());
    1219        1625 :     nsJSContext::MaybePokeCC();
    1220             : 
    1221           0 :     CycleCollectedJSContext::AfterProcessTask(aNewRecursionDepth);
    1222             : 
    1223             :     // Now that we are certain that the event is complete,
    1224             :     // we can flush any ongoing performance measurement.
    1225           0 :     js::FlushPerformanceMonitoring(Context());
    1226             : 
    1227        1625 :     mozilla::jsipc::AfterProcessTask();
    1228           0 : }
    1229             : 
    1230             : bool
    1231           0 : XPCJSContext::IsSystemCaller() const
    1232             : {
    1233         345 :     return nsContentUtils::IsSystemCaller(Context());
    1234             : }

Generated by: LCOV version 1.13-14-ga5dd952