tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XPCJSContext.cpp (54464B)


      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 /* Per JSContext object */
      8 
      9 #include "mozilla/UniquePtr.h"
     10 
     11 #include "xpcprivate.h"
     12 #include "xpcpublic.h"
     13 #include "XPCWrapper.h"
     14 #include "XPCJSMemoryReporter.h"
     15 #include "XPCSelfHostedShmem.h"
     16 #include "WrapperFactory.h"
     17 #include "mozJSModuleLoader.h"
     18 #include "nsNetUtil.h"
     19 #include "nsThreadUtils.h"
     20 #include "ExecutionTracerIntegration.h"
     21 
     22 #include "nsIObserverService.h"
     23 #include "nsIDebug2.h"
     24 #include "nsPIDOMWindow.h"
     25 #include "nsPrintfCString.h"
     26 #include "mozilla/AppShutdown.h"
     27 #include "mozilla/Preferences.h"
     28 #include "mozilla/MemoryTelemetry.h"
     29 #include "mozilla/Services.h"
     30 #ifdef FUZZING
     31 #  include "mozilla/StaticPrefs_fuzzing.h"
     32 #endif
     33 #include "mozilla/StaticPrefs_dom.h"
     34 #include "mozilla/StaticPrefs_browser.h"
     35 #include "mozilla/StaticPrefs_javascript.h"
     36 #include "mozilla/dom/ScriptSettings.h"
     37 #include "mozilla/glean/JsXpconnectMetrics.h"
     38 #include "mozilla/scache/StartupCache.h"
     39 
     40 #include "nsContentUtils.h"
     41 #include "nsCCUncollectableMarker.h"
     42 #include "nsCycleCollectionNoteRootCallback.h"
     43 #include "nsCycleCollector.h"
     44 #include "nsINode.h"
     45 #include "nsJSEnvironment.h"
     46 #include "jsapi.h"
     47 #include "js/ArrayBuffer.h"
     48 #include "js/ContextOptions.h"
     49 #include "js/DOMEventDispatch.h"
     50 #include "js/experimental/LoggingInterface.h"
     51 #include "js/HelperThreadAPI.h"
     52 #include "js/Initialization.h"
     53 #include "js/MemoryMetrics.h"
     54 #include "js/Prefs.h"
     55 #include "js/WasmFeatures.h"
     56 #include "fmt/format.h"
     57 #include "mozilla/dom/BindingUtils.h"
     58 #include "mozilla/dom/ContentChild.h"
     59 #include "mozilla/dom/Document.h"
     60 #include "mozilla/dom/Element.h"
     61 #include "mozilla/dom/ScriptLoader.h"
     62 #include "mozilla/dom/WindowBinding.h"
     63 #include "mozilla/dom/WakeLockBinding.h"
     64 #include "mozilla/extensions/WebExtensionPolicy.h"
     65 #include "mozilla/AsyncEventDispatcher.h"
     66 #include "mozilla/Atomics.h"
     67 #include "mozilla/Attributes.h"
     68 #include "mozilla/ProcessHangMonitor.h"
     69 #include "mozilla/Sprintf.h"
     70 #include "mozilla/SystemPrincipal.h"
     71 #include "mozilla/TaskController.h"
     72 #include "mozilla/UniquePtrExtensions.h"
     73 #include "AccessCheck.h"
     74 #include "nsGlobalWindowInner.h"
     75 #include "nsAboutProtocolUtils.h"
     76 
     77 #include "GeckoProfiler.h"
     78 #include "nsIXULRuntime.h"
     79 #include "nsJSPrincipals.h"
     80 #include "ExpandedPrincipal.h"
     81 
     82 #if defined(XP_LINUX) && !defined(ANDROID)
     83 // For getrlimit and min/max.
     84 #  include <algorithm>
     85 #  include <sys/resource.h>
     86 #endif
     87 
     88 #ifdef XP_WIN
     89 // For min.
     90 #  include <algorithm>
     91 #  include <windows.h>
     92 #endif
     93 
     94 using namespace mozilla;
     95 using namespace mozilla::dom;
     96 using namespace xpc;
     97 using namespace JS;
     98 
     99 // Callback for JIT trace events to dispatch DOM events to global target
    100 static void DispatchJitEventToDOM(JSContext* cx, const char* eventType) {
    101  // Check if test interfaces are enabled
    102  if (!StaticPrefs::dom_expose_test_interfaces()) {
    103    return;
    104  }
    105 
    106  if (!cx) {
    107    return;
    108  }
    109 
    110  // Get the global object from the context
    111  JSObject* globalObj = JS::CurrentGlobalOrNull(cx);
    112  if (!globalObj) {
    113    return;
    114  }
    115 
    116  nsIGlobalObject* global = xpc::NativeGlobal(globalObj);
    117  if (!global) {
    118    return;
    119  }
    120 
    121  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
    122  if (!window) {
    123    return;
    124  }
    125 
    126  mozilla::dom::Document* doc = window->GetDoc();
    127  if (!doc) {
    128    return;
    129  }
    130 
    131  nsCOMPtr<nsINode> target = doc;
    132  if (!target) {
    133    return;
    134  }
    135 
    136  // Convert event type to nsString and dispatch to document
    137  NS_ConvertUTF8toUTF16 eventTypeStr(eventType);
    138  RefPtr<AsyncEventDispatcher> dispatcher = new AsyncEventDispatcher(
    139      target, eventTypeStr, CanBubble::eYes, ChromeOnlyDispatch::eNo);
    140  dispatcher->PostDOMEvent();
    141 }
    142 
    143 // We will clamp to reasonable values if this isn't set.
    144 #if !defined(PTHREAD_STACK_MIN)
    145 #  define PTHREAD_STACK_MIN 0
    146 #endif
    147 
    148 static void WatchdogMain(void* arg);
    149 class Watchdog;
    150 class WatchdogManager;
    151 class MOZ_RAII AutoLockWatchdog final {
    152  Watchdog* const mWatchdog;
    153 
    154 public:
    155  explicit AutoLockWatchdog(Watchdog* aWatchdog);
    156  ~AutoLockWatchdog();
    157 };
    158 
    159 class Watchdog {
    160 public:
    161  explicit Watchdog(WatchdogManager* aManager)
    162      : mManager(aManager),
    163        mLock(nullptr),
    164        mWakeup(nullptr),
    165        mThread(nullptr),
    166        mHibernating(false),
    167        mInitialized(false),
    168        mShuttingDown(false),
    169        mMinScriptRunTimeSeconds(1) {}
    170  ~Watchdog() { MOZ_ASSERT(!Initialized()); }
    171 
    172  WatchdogManager* Manager() { return mManager; }
    173  bool Initialized() { return mInitialized; }
    174  bool ShuttingDown() { return mShuttingDown; }
    175  PRLock* GetLock() { return mLock; }
    176  bool Hibernating() { return mHibernating; }
    177  void WakeUp() {
    178    MOZ_ASSERT(Initialized());
    179    MOZ_ASSERT(Hibernating());
    180    mHibernating = false;
    181    PR_NotifyCondVar(mWakeup);
    182  }
    183 
    184  //
    185  // Invoked by the main thread only.
    186  //
    187 
    188  void Init() {
    189    MOZ_ASSERT(NS_IsMainThread());
    190    mLock = PR_NewLock();
    191    if (!mLock) {
    192      MOZ_CRASH("PR_NewLock failed.");
    193    }
    194 
    195    mWakeup = PR_NewCondVar(mLock);
    196    if (!mWakeup) {
    197      MOZ_CRASH("PR_NewCondVar failed.");
    198    }
    199 
    200    {
    201      // Make sure the debug service is instantiated before we create the
    202      // watchdog thread, since we intentionally try to keep the thread's stack
    203      // segment as small as possible. It isn't always large enough to
    204      // instantiate a new service, and even when it is, we don't want fault in
    205      // extra pages if we can avoid it.
    206      nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
    207      (void)dbg;
    208    }
    209 
    210    {
    211      AutoLockWatchdog lock(this);
    212 
    213      // The watchdog thread loop is pretty trivial, and should not
    214      // require much stack space to do its job. So only give it 32KiB
    215      // or the platform minimum. On modern Linux libc this might resolve to
    216      // a runtime call.
    217      size_t watchdogStackSize = PTHREAD_STACK_MIN;
    218      watchdogStackSize = std::max<size_t>(32 * 1024, watchdogStackSize);
    219 
    220      // Gecko uses thread private for accounting and has to clean up at thread
    221      // exit. Therefore, even though we don't have a return value from the
    222      // watchdog, we need to join it on shutdown.
    223      mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
    224                                PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
    225                                PR_JOINABLE_THREAD, watchdogStackSize);
    226      if (!mThread) {
    227        MOZ_CRASH("PR_CreateThread failed!");
    228      }
    229 
    230      // WatchdogMain acquires the lock and then asserts mInitialized. So
    231      // make sure to set mInitialized before releasing the lock here so
    232      // that it's atomic with the creation of the thread.
    233      mInitialized = true;
    234    }
    235  }
    236 
    237  void Shutdown() {
    238    MOZ_ASSERT(NS_IsMainThread());
    239    MOZ_ASSERT(Initialized());
    240    {  // Scoped lock.
    241      AutoLockWatchdog lock(this);
    242 
    243      // Signal to the watchdog thread that it's time to shut down.
    244      mShuttingDown = true;
    245 
    246      // Wake up the watchdog, and wait for it to call us back.
    247      PR_NotifyCondVar(mWakeup);
    248    }
    249 
    250    PR_JoinThread(mThread);
    251 
    252    // The thread sets mShuttingDown to false as it exits.
    253    MOZ_ASSERT(!mShuttingDown);
    254 
    255    // Destroy state.
    256    mThread = nullptr;
    257    PR_DestroyCondVar(mWakeup);
    258    mWakeup = nullptr;
    259    PR_DestroyLock(mLock);
    260    mLock = nullptr;
    261 
    262    // All done.
    263    mInitialized = false;
    264  }
    265 
    266  void SetMinScriptRunTimeSeconds(int32_t seconds) {
    267    // This variable is atomic, and is set from the main thread without
    268    // locking.
    269    MOZ_ASSERT(seconds > 0);
    270    mMinScriptRunTimeSeconds = seconds;
    271  }
    272 
    273  //
    274  // Invoked by the watchdog thread only.
    275  //
    276 
    277  void Hibernate() {
    278    MOZ_ASSERT(!NS_IsMainThread());
    279    mHibernating = true;
    280    Sleep(PR_INTERVAL_NO_TIMEOUT);
    281  }
    282  void Sleep(PRIntervalTime timeout) {
    283    MOZ_ASSERT(!NS_IsMainThread());
    284    AUTO_PROFILER_THREAD_SLEEP;
    285    MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
    286  }
    287  void Finished() {
    288    MOZ_ASSERT(!NS_IsMainThread());
    289    mShuttingDown = false;
    290  }
    291 
    292  int32_t MinScriptRunTimeSeconds() { return mMinScriptRunTimeSeconds; }
    293 
    294 private:
    295  WatchdogManager* mManager;
    296 
    297  PRLock* mLock;
    298  PRCondVar* mWakeup;
    299  PRThread* mThread;
    300  bool mHibernating;
    301  bool mInitialized;
    302  bool mShuttingDown;
    303  mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
    304 };
    305 
    306 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
    307 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
    308 #define PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT \
    309  "dom.max_ext_content_script_run_time"
    310 
    311 static const char* gCallbackPrefs[] = {
    312    "dom.use_watchdog",
    313    PREF_MAX_SCRIPT_RUN_TIME_CONTENT,
    314    PREF_MAX_SCRIPT_RUN_TIME_CHROME,
    315    PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT,
    316    nullptr,
    317 };
    318 
    319 class WatchdogManager {
    320 public:
    321  explicit WatchdogManager() {
    322    // All the timestamps start at zero.
    323    PodArrayZero(mTimestamps);
    324 
    325    // Register ourselves as an observer to get updates on the pref.
    326    Preferences::RegisterCallbacks(PrefsChanged, gCallbackPrefs, this);
    327  }
    328 
    329  virtual ~WatchdogManager() {
    330    // Shutting down the watchdog requires context-switching to the watchdog
    331    // thread, which isn't great to do in a destructor. So we require
    332    // consumers to shut it down manually before releasing it.
    333    MOZ_ASSERT(!mWatchdog);
    334  }
    335 
    336 private:
    337  static void PrefsChanged(const char* aPref, void* aSelf) {
    338    static_cast<WatchdogManager*>(aSelf)->RefreshWatchdog();
    339  }
    340 
    341 public:
    342  void Shutdown() {
    343    Preferences::UnregisterCallbacks(PrefsChanged, gCallbackPrefs, this);
    344  }
    345 
    346  void RegisterContext(XPCJSContext* aContext) {
    347    MOZ_ASSERT(NS_IsMainThread());
    348    AutoLockWatchdog lock(mWatchdog.get());
    349 
    350    if (aContext->mActive == XPCJSContext::CONTEXT_ACTIVE) {
    351      mActiveContexts.insertBack(aContext);
    352    } else {
    353      mInactiveContexts.insertBack(aContext);
    354    }
    355 
    356    // Enable the watchdog, if appropriate.
    357    RefreshWatchdog();
    358  }
    359 
    360  void UnregisterContext(XPCJSContext* aContext) {
    361    MOZ_ASSERT(NS_IsMainThread());
    362    AutoLockWatchdog lock(mWatchdog.get());
    363 
    364    // aContext must be in one of our two lists, simply remove it.
    365    aContext->LinkedListElement<XPCJSContext>::remove();
    366 
    367 #ifdef DEBUG
    368    // If this was the last context, we should have already shut down
    369    // the watchdog.
    370    if (mActiveContexts.isEmpty() && mInactiveContexts.isEmpty()) {
    371      MOZ_ASSERT(!mWatchdog);
    372    }
    373 #endif
    374  }
    375 
    376  // Context statistics. These live on the watchdog manager, are written
    377  // from the main thread, and are read from the watchdog thread (holding
    378  // the lock in each case).
    379  void RecordContextActivity(XPCJSContext* aContext, bool active) {
    380    // The watchdog reads this state, so acquire the lock before writing it.
    381    MOZ_ASSERT(NS_IsMainThread());
    382    AutoLockWatchdog lock(mWatchdog.get());
    383 
    384    // Write state.
    385    aContext->mLastStateChange = PR_Now();
    386    aContext->mActive =
    387        active ? XPCJSContext::CONTEXT_ACTIVE : XPCJSContext::CONTEXT_INACTIVE;
    388    UpdateContextLists(aContext);
    389 
    390    // The watchdog may be hibernating, waiting for the context to go
    391    // active. Wake it up if necessary.
    392    if (active && mWatchdog && mWatchdog->Hibernating()) {
    393      mWatchdog->WakeUp();
    394    }
    395  }
    396 
    397  bool IsAnyContextActive() { return !mActiveContexts.isEmpty(); }
    398  PRTime TimeSinceLastActiveContext() {
    399    // Must be called on the watchdog thread with the lock held.
    400    MOZ_ASSERT(!NS_IsMainThread());
    401    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
    402    MOZ_ASSERT(mActiveContexts.isEmpty());
    403    MOZ_ASSERT(!mInactiveContexts.isEmpty());
    404 
    405    // We store inactive contexts with the most recently added inactive
    406    // context at the end of the list.
    407    return PR_Now() - mInactiveContexts.getLast()->mLastStateChange;
    408  }
    409 
    410  void RecordTimestamp(WatchdogTimestampCategory aCategory) {
    411    // Must be called on the watchdog thread with the lock held.
    412    MOZ_ASSERT(!NS_IsMainThread());
    413    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
    414    MOZ_ASSERT(aCategory != TimestampContextStateChange,
    415               "Use RecordContextActivity to update this");
    416 
    417    mTimestamps[aCategory] = PR_Now();
    418  }
    419 
    420  PRTime GetContextTimestamp(XPCJSContext* aContext,
    421                             const AutoLockWatchdog& aProofOfLock) {
    422    return aContext->mLastStateChange;
    423  }
    424 
    425  PRTime GetTimestamp(WatchdogTimestampCategory aCategory,
    426                      const AutoLockWatchdog& aProofOfLock) {
    427    MOZ_ASSERT(aCategory != TimestampContextStateChange,
    428               "Use GetContextTimestamp to retrieve this");
    429    return mTimestamps[aCategory];
    430  }
    431 
    432  Watchdog* GetWatchdog() { return mWatchdog.get(); }
    433 
    434  void RefreshWatchdog() {
    435    bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
    436    if (wantWatchdog != !!mWatchdog) {
    437      if (wantWatchdog) {
    438        StartWatchdog();
    439      } else {
    440        StopWatchdog();
    441      }
    442    }
    443 
    444    if (mWatchdog) {
    445      int32_t contentTime = StaticPrefs::dom_max_script_run_time();
    446      if (contentTime <= 0) {
    447        contentTime = INT32_MAX;
    448      }
    449      int32_t chromeTime = StaticPrefs::dom_max_chrome_script_run_time();
    450      if (chromeTime <= 0) {
    451        chromeTime = INT32_MAX;
    452      }
    453      int32_t extTime = StaticPrefs::dom_max_ext_content_script_run_time();
    454      if (extTime <= 0) {
    455        extTime = INT32_MAX;
    456      }
    457      mWatchdog->SetMinScriptRunTimeSeconds(
    458          std::min({contentTime, chromeTime, extTime}));
    459    }
    460  }
    461 
    462  void StartWatchdog() {
    463    MOZ_ASSERT(!mWatchdog);
    464    mWatchdog = mozilla::MakeUnique<Watchdog>(this);
    465    mWatchdog->Init();
    466  }
    467 
    468  void StopWatchdog() {
    469    MOZ_ASSERT(mWatchdog);
    470    mWatchdog->Shutdown();
    471    mWatchdog = nullptr;
    472  }
    473 
    474  template <class Callback>
    475  void ForAllActiveContexts(Callback&& aCallback) {
    476    // This function must be called on the watchdog thread with the lock held.
    477    MOZ_ASSERT(!NS_IsMainThread());
    478    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
    479 
    480    for (auto* context = mActiveContexts.getFirst(); context;
    481         context = context->LinkedListElement<XPCJSContext>::getNext()) {
    482      if (!aCallback(context)) {
    483        return;
    484      }
    485    }
    486  }
    487 
    488 private:
    489  void UpdateContextLists(XPCJSContext* aContext) {
    490    // Given aContext whose activity state or timestamp has just changed,
    491    // put it back in the proper position in the proper list.
    492    aContext->LinkedListElement<XPCJSContext>::remove();
    493    auto& list = aContext->mActive == XPCJSContext::CONTEXT_ACTIVE
    494                     ? mActiveContexts
    495                     : mInactiveContexts;
    496 
    497    // Either the new list is empty or aContext must be more recent than
    498    // the existing last element.
    499    MOZ_ASSERT_IF(!list.isEmpty(), list.getLast()->mLastStateChange <
    500                                       aContext->mLastStateChange);
    501    list.insertBack(aContext);
    502  }
    503 
    504  LinkedList<XPCJSContext> mActiveContexts;
    505  LinkedList<XPCJSContext> mInactiveContexts;
    506  mozilla::UniquePtr<Watchdog> mWatchdog;
    507 
    508  // We store ContextStateChange on the contexts themselves.
    509  PRTime mTimestamps[kWatchdogTimestampCategoryCount - 1];
    510 };
    511 
    512 AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog) : mWatchdog(aWatchdog) {
    513  if (mWatchdog) {
    514    PR_Lock(mWatchdog->GetLock());
    515  }
    516 }
    517 
    518 AutoLockWatchdog::~AutoLockWatchdog() {
    519  if (mWatchdog) {
    520    PR_Unlock(mWatchdog->GetLock());
    521  }
    522 }
    523 
    524 static void WatchdogMain(void* arg) {
    525  AUTO_PROFILER_REGISTER_THREAD("JS Watchdog");
    526  // Create an nsThread wrapper for the thread and register it with the thread
    527  // manager.
    528  (void)NS_GetCurrentThread();
    529  NS_SetCurrentThreadName("JS Watchdog");
    530 
    531  Watchdog* self = static_cast<Watchdog*>(arg);
    532  WatchdogManager* manager = self->Manager();
    533 
    534  // Lock lasts until we return
    535  AutoLockWatchdog lock(self);
    536 
    537  MOZ_ASSERT(self->Initialized());
    538  while (!self->ShuttingDown()) {
    539    // Sleep only 1 second if recently (or currently) active; otherwise,
    540    // hibernate
    541    if (manager->IsAnyContextActive() ||
    542        manager->TimeSinceLastActiveContext() <= PRTime(2 * PR_USEC_PER_SEC)) {
    543      self->Sleep(PR_TicksPerSecond());
    544    } else {
    545      manager->RecordTimestamp(TimestampWatchdogHibernateStart);
    546      self->Hibernate();
    547      manager->RecordTimestamp(TimestampWatchdogHibernateStop);
    548    }
    549 
    550    // Rise and shine.
    551    manager->RecordTimestamp(TimestampWatchdogWakeup);
    552 
    553    // Don't request an interrupt callback unless the current script has
    554    // been running long enough that we might show the slow script dialog.
    555    // Triggering the callback from off the main thread can be expensive.
    556 
    557    // We want to avoid showing the slow script dialog if the user's laptop
    558    // goes to sleep in the middle of running a script. To ensure this, we
    559    // invoke the interrupt callback after only half the timeout has
    560    // elapsed. The callback simply records the fact that it was called in
    561    // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
    562    // seconds and invoke the callback again. This time around it sees
    563    // mSlowScriptSecondHalf is set and so it shows the slow script
    564    // dialog. If the computer is put to sleep during one of the (timeout/2)
    565    // periods, the script still has the other (timeout/2) seconds to
    566    // finish.
    567    if (!self->ShuttingDown() && manager->IsAnyContextActive()) {
    568      bool debuggerAttached = false;
    569      nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
    570      if (dbg) {
    571        dbg->GetIsDebuggerAttached(&debuggerAttached);
    572      }
    573      if (debuggerAttached) {
    574        // We won't be interrupting these scripts anyway.
    575        continue;
    576      }
    577 
    578      PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
    579      manager->ForAllActiveContexts([usecs, manager,
    580                                     &lock](XPCJSContext* aContext) -> bool {
    581        auto timediff = PR_Now() - manager->GetContextTimestamp(aContext, lock);
    582        if (timediff > usecs) {
    583          JS_RequestInterruptCallback(aContext->Context());
    584          return true;
    585        }
    586        return false;
    587      });
    588    }
    589  }
    590 
    591  // Tell the manager that we've shut down.
    592  self->Finished();
    593 }
    594 
    595 PRTime XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory) {
    596  AutoLockWatchdog lock(mWatchdogManager->GetWatchdog());
    597  return aCategory == TimestampContextStateChange
    598             ? mWatchdogManager->GetContextTimestamp(this, lock)
    599             : mWatchdogManager->GetTimestamp(aCategory, lock);
    600 }
    601 
    602 // static
    603 bool XPCJSContext::RecordScriptActivity(bool aActive) {
    604  MOZ_ASSERT(NS_IsMainThread());
    605 
    606  XPCJSContext* xpccx = XPCJSContext::Get();
    607  if (!xpccx) {
    608    // mozilla::SpinEventLoopUntil may use AutoScriptActivity(false) after
    609    // we destroyed the XPCJSContext.
    610    MOZ_ASSERT(!aActive);
    611    return false;
    612  }
    613 
    614  bool oldValue = xpccx->SetHasScriptActivity(aActive);
    615  if (aActive == oldValue) {
    616    // Nothing to do.
    617    return oldValue;
    618  }
    619 
    620  if (!aActive) {
    621    ProcessHangMonitor::ClearHang();
    622  }
    623  xpccx->mWatchdogManager->RecordContextActivity(xpccx, aActive);
    624 
    625  return oldValue;
    626 }
    627 
    628 AutoScriptActivity::AutoScriptActivity(bool aActive)
    629    : mActive(aActive),
    630      mOldValue(XPCJSContext::RecordScriptActivity(aActive)) {}
    631 
    632 AutoScriptActivity::~AutoScriptActivity() {
    633  MOZ_ALWAYS_TRUE(mActive == XPCJSContext::RecordScriptActivity(mOldValue));
    634 }
    635 
    636 static const double sChromeSlowScriptTelemetryCutoff(10.0);
    637 
    638 // static
    639 bool XPCJSContext::InterruptCallback(JSContext* cx) {
    640  XPCJSContext* self = XPCJSContext::Get();
    641 
    642  // Now is a good time to turn on profiling if it's pending.
    643  PROFILER_JS_INTERRUPT_CALLBACK();
    644 
    645  if (profiler_thread_is_being_profiled_for_markers()) {
    646    nsDependentCString filename("unknown file");
    647    JS::AutoFilename scriptFilename;
    648    // Computing the line number can be very expensive (see bug 1330231 for
    649    // example), so don't request it here.
    650    if (JS::DescribeScriptedCaller(&scriptFilename, cx)) {
    651      if (const char* file = scriptFilename.get()) {
    652        filename.Assign(file, strlen(file));
    653      }
    654      PROFILER_MARKER_TEXT("JS::InterruptCallback", JS, {}, filename);
    655    }
    656  }
    657 
    658  // Normally we record mSlowScriptCheckpoint when we start to process an
    659  // event. However, we can run JS outside of event handlers. This code takes
    660  // care of that case.
    661  if (self->mSlowScriptCheckpoint.IsNull()) {
    662    self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
    663    self->mSlowScriptSecondHalf = false;
    664    self->mSlowScriptActualWait = mozilla::TimeDuration();
    665    self->mTimeoutAccumulated = false;
    666    self->mExecutedChromeScript = false;
    667    return true;
    668  }
    669 
    670  // Sometimes we get called back during XPConnect initialization, before Gecko
    671  // has finished bootstrapping. Avoid crashing in nsContentUtils below.
    672  if (!nsContentUtils::IsInitialized()) {
    673    return true;
    674  }
    675 
    676  // This is at least the second interrupt callback we've received since
    677  // returning to the event loop. See how long it's been, and what the limit
    678  // is.
    679  TimeStamp now = TimeStamp::NowLoRes();
    680  TimeDuration duration = now - self->mSlowScriptCheckpoint;
    681  int32_t limit;
    682 
    683  nsString addonId;
    684  const char* prefName;
    685  auto principal = BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(cx));
    686  bool chrome = principal->Is<SystemPrincipal>();
    687  if (chrome) {
    688    prefName = PREF_MAX_SCRIPT_RUN_TIME_CHROME;
    689    limit = StaticPrefs::dom_max_chrome_script_run_time();
    690    self->mExecutedChromeScript = true;
    691  } else if (auto policy = principal->ContentScriptAddonPolicy()) {
    692    policy->GetId(addonId);
    693    prefName = PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT;
    694    limit = StaticPrefs::dom_max_ext_content_script_run_time();
    695  } else {
    696    prefName = PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
    697    limit = StaticPrefs::dom_max_script_run_time();
    698  }
    699 
    700  // When the parent process slow script dialog is disabled, we still want
    701  // to be able to track things for telemetry, so set `mSlowScriptSecondHalf`
    702  // to true in that case:
    703  if (limit == 0 && chrome &&
    704      duration.ToSeconds() > sChromeSlowScriptTelemetryCutoff / 2.0) {
    705    self->mSlowScriptSecondHalf = true;
    706    return true;
    707  }
    708  // If there's no limit, or we're within the limit, let it go.
    709  if (limit == 0 || duration.ToSeconds() < limit / 2.0) {
    710    return true;
    711  }
    712 
    713  self->mSlowScriptCheckpoint = now;
    714  self->mSlowScriptActualWait += duration;
    715 
    716  // In order to guard against time changes or laptops going to sleep, we
    717  // don't trigger the slow script warning until (limit/2) seconds have
    718  // elapsed twice.
    719  if (!self->mSlowScriptSecondHalf) {
    720    self->mSlowScriptSecondHalf = true;
    721    return true;
    722  }
    723 
    724  // For scripts in content processes, we only want to show the slow script
    725  // dialogue if the user is actually trying to perform an important
    726  // interaction. In theory this could be a chrome script running in the
    727  // content process, which we probably don't want to give the user the ability
    728  // to terminate. However, if this is the case we won't be able to map the
    729  // script global to a window and we'll bail out below.
    730  if (XRE_IsContentProcess() &&
    731      StaticPrefs::dom_max_script_run_time_require_critical_input()) {
    732    // Call possibly slow PeekMessages after the other common early returns in
    733    // this method.
    734    ContentChild* contentChild = ContentChild::GetSingleton();
    735    mozilla::ipc::MessageChannel* channel =
    736        contentChild ? contentChild->GetIPCChannel() : nullptr;
    737    if (channel) {
    738      bool foundInputEvent = false;
    739      channel->PeekMessages(
    740          [&foundInputEvent](const IPC::Message& aMsg) -> bool {
    741            if (nsContentUtils::IsMessageCriticalInputEvent(aMsg)) {
    742              foundInputEvent = true;
    743              return false;
    744            }
    745            return true;
    746          });
    747      if (!foundInputEvent) {
    748        return true;
    749      }
    750    }
    751  }
    752 
    753  // We use a fixed value of 2 from browser_parent_process_hang_telemetry.js
    754  // to check if the telemetry events work. Do not interrupt it with a dialog.
    755  if (chrome && limit == 2 && xpc::IsInAutomation()) {
    756    return true;
    757  }
    758 
    759  //
    760  // This has gone on long enough! Time to take action. ;-)
    761  //
    762 
    763  // Get the DOM window associated with the running script. If the script is
    764  // running in a non-DOM scope, we have to just let it keep running.
    765  RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
    766  RefPtr<nsGlobalWindowInner> win = WindowOrNull(global);
    767  if (!win) {
    768    // If this is a sandbox associated with a DOMWindow via a
    769    // sandboxPrototype, use that DOMWindow. This supports WebExtension
    770    // content scripts.
    771    win = SandboxWindowOrNull(global, cx);
    772  }
    773 
    774  if (!win) {
    775    NS_WARNING("No active window");
    776    return true;
    777  }
    778 
    779  if (win->IsDying()) {
    780    // The window is being torn down. When that happens we try to prevent
    781    // the dispatch of new runnables, so it also makes sense to kill any
    782    // long-running script. The user is primarily interested in this page
    783    // going away.
    784    return false;
    785  }
    786 
    787  // Accumulate slow script invokation delay.
    788  if (!chrome && !self->mTimeoutAccumulated) {
    789    TimeDuration delay =
    790        self->mSlowScriptActualWait - TimeDuration::FromSeconds(limit);
    791    glean::slow_script_warning::notify_delay.AccumulateRawDuration(delay);
    792    self->mTimeoutAccumulated = true;
    793  }
    794 
    795  // Show the prompt to the user, and kill if requested.
    796  nsGlobalWindowInner::SlowScriptResponse response = win->ShowSlowScriptDialog(
    797      cx, addonId, self->mSlowScriptActualWait.ToMilliseconds());
    798  if (response == nsGlobalWindowInner::KillSlowScript) {
    799    if (Preferences::GetBool("dom.global_stop_script", true)) {
    800      xpc::Scriptability::Get(global).Block();
    801    }
    802    if (nsCOMPtr<Document> doc = win->GetExtantDoc()) {
    803      doc->UnlockAllWakeLocks(WakeLockType::Screen);
    804    }
    805    return false;
    806  }
    807 
    808  // The user chose to continue the script. Reset the timer, and disable this
    809  // machinery with a pref if the user opted out of future slow-script dialogs.
    810  if (response != nsGlobalWindowInner::ContinueSlowScriptAndKeepNotifying) {
    811    self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
    812  }
    813 
    814  if (response == nsGlobalWindowInner::AlwaysContinueSlowScript) {
    815    Preferences::SetInt(prefName, 0);
    816  }
    817 
    818  return true;
    819 }
    820 
    821 #define JS_OPTIONS_DOT_STR "javascript.options."
    822 
    823 static mozilla::Atomic<bool> sDiscardSystemSource(false);
    824 
    825 bool xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
    826 
    827 static mozilla::Atomic<bool> sSharedMemoryEnabled(false);
    828 static mozilla::Atomic<bool> sStreamsEnabled(false);
    829 
    830 void xpc::SetPrefableRealmOptions(JS::RealmOptions& options) {
    831  options.creationOptions()
    832      .setSharedMemoryAndAtomicsEnabled(sSharedMemoryEnabled)
    833      .setCoopAndCoepEnabled(
    834          StaticPrefs::browser_tabs_remote_useCrossOriginOpenerPolicy() &&
    835          StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy());
    836 }
    837 
    838 void xpc::SetPrefableCompileOptions(JS::PrefableCompileOptions& options) {
    839  options.setSourcePragmas(StaticPrefs::javascript_options_source_pragmas())
    840      .setAsmJS(StaticPrefs::javascript_options_asmjs())
    841      .setThrowOnAsmJSValidationFailure(
    842          StaticPrefs::javascript_options_throw_on_asmjs_validation_failure());
    843 }
    844 
    845 void xpc::SetPrefableContextOptions(JS::ContextOptions& options) {
    846  options
    847 #ifdef FUZZING
    848      .setFuzzing(Preferences::GetBool(JS_OPTIONS_DOT_STR "fuzzing.enabled"))
    849 #endif
    850      .setWasm(Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm"))
    851      .setWasmForTrustedPrinciples(
    852          Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_trustedprincipals"))
    853      .setWasmIon(Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_optimizingjit"))
    854      .setWasmBaseline(
    855          Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit"))
    856      .setAsyncStack(Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncstack"))
    857      .setAsyncStackCaptureDebuggeeOnly(Preferences::GetBool(
    858          JS_OPTIONS_DOT_STR "asyncstack_capture_debuggee_only"));
    859 
    860  SetPrefableCompileOptions(options.compileOptions());
    861 }
    862 
    863 static void LoadStartupJSPrefs(XPCJSContext* xpccx) {
    864  // Prefs that require a restart are handled here. This includes the
    865  // process-wide JIT options because toggling these at runtime can easily cause
    866  // races or get us into an inconsistent state.
    867  //
    868  // 'Live' prefs are handled by ReloadPrefsCallback below.
    869 
    870  // Note: JS::Prefs are set earlier in startup, in InitializeJS in
    871  // XPCOMInit.cpp.
    872 
    873  JSContext* cx = xpccx->Context();
    874 
    875  // Some prefs are unlisted in all.js / StaticPrefs (and thus are invisible in
    876  // about:config). Make sure we use explicit defaults here.
    877  bool useJitForTrustedPrincipals =
    878      Preferences::GetBool(JS_OPTIONS_DOT_STR "jit_trustedprincipals", false);
    879 
    880  bool safeMode = false;
    881  nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
    882  if (xr) {
    883    xr->GetInSafeMode(&safeMode);
    884  }
    885 
    886  // NOTE: Baseline Interpreter is still used in safe-mode. This gives a big
    887  //       perf gain and is our simplest JIT so we make a tradeoff.
    888  JS_SetGlobalJitCompilerOption(
    889      cx, JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
    890      StaticPrefs::javascript_options_blinterp_DoNotUseDirectly());
    891 
    892  // Disable most JITs in Safe-Mode.
    893  if (safeMode) {
    894    JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_ENABLE, false);
    895    JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_ENABLE, false);
    896    JS_SetGlobalJitCompilerOption(
    897        cx, JSJITCOMPILER_JIT_TRUSTEDPRINCIPALS_ENABLE, false);
    898    JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
    899                                  false);
    900    JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_JIT_HINTS_ENABLE, false);
    901    xpc::SelfHostedShmem::SetSelfHostedUseSharedMemory(false);
    902  } else {
    903    JS_SetGlobalJitCompilerOption(
    904        cx, JSJITCOMPILER_BASELINE_ENABLE,
    905        StaticPrefs::javascript_options_baselinejit_DoNotUseDirectly());
    906    JS_SetGlobalJitCompilerOption(
    907        cx, JSJITCOMPILER_ION_ENABLE,
    908        StaticPrefs::javascript_options_ion_DoNotUseDirectly());
    909    JS_SetGlobalJitCompilerOption(cx,
    910                                  JSJITCOMPILER_JIT_TRUSTEDPRINCIPALS_ENABLE,
    911                                  useJitForTrustedPrincipals);
    912    JS_SetGlobalJitCompilerOption(
    913        cx, JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
    914        StaticPrefs::javascript_options_native_regexp_DoNotUseDirectly());
    915    // Only enable the jit hints cache for the content process to avoid
    916    // any possible jank or delays on the parent process.
    917    JS_SetGlobalJitCompilerOption(
    918        cx, JSJITCOMPILER_JIT_HINTS_ENABLE,
    919        XRE_IsContentProcess()
    920            ? StaticPrefs::javascript_options_jithints_DoNotUseDirectly()
    921            : false);
    922    xpc::SelfHostedShmem::SetSelfHostedUseSharedMemory(
    923        StaticPrefs::
    924            javascript_options_self_hosted_use_shared_memory_DoNotUseDirectly());
    925  }
    926 
    927  uint32_t strategyIndex = StaticPrefs::
    928      javascript_options_baselinejit_offthread_compilation_strategy();
    929  bool onDemandOMTBaselineEnabled = strategyIndex == 1 || strategyIndex == 3;
    930  JS_SetOffthreadBaselineCompilationEnabled(cx, onDemandOMTBaselineEnabled);
    931 
    932  JS_SetOffthreadIonCompilationEnabled(
    933      cx, StaticPrefs::
    934              javascript_options_ion_offthread_compilation_DoNotUseDirectly());
    935 
    936  JS_SetGlobalJitCompilerOption(
    937      cx, JSJITCOMPILER_BASELINE_INTERPRETER_WARMUP_TRIGGER,
    938      StaticPrefs::javascript_options_blinterp_threshold_DoNotUseDirectly());
    939  JS_SetGlobalJitCompilerOption(
    940      cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
    941      StaticPrefs::javascript_options_baselinejit_threshold_DoNotUseDirectly());
    942  JS_SetGlobalJitCompilerOption(
    943      cx, JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
    944      StaticPrefs::javascript_options_ion_threshold_DoNotUseDirectly());
    945  JS_SetGlobalJitCompilerOption(
    946      cx, JSJITCOMPILER_ION_FREQUENT_BAILOUT_THRESHOLD,
    947      StaticPrefs::
    948          javascript_options_ion_frequent_bailout_threshold_DoNotUseDirectly());
    949  JS_SetGlobalJitCompilerOption(
    950      cx, JSJITCOMPILER_INLINING_BYTECODE_MAX_LENGTH,
    951      StaticPrefs::
    952          javascript_options_inlining_bytecode_max_length_DoNotUseDirectly());
    953 
    954 #ifdef DEBUG
    955  JS_SetGlobalJitCompilerOption(
    956      cx, JSJITCOMPILER_FULL_DEBUG_CHECKS,
    957      StaticPrefs::javascript_options_jit_full_debug_checks_DoNotUseDirectly());
    958 #endif
    959 
    960 #if !defined(JS_CODEGEN_MIPS64) && !defined(JS_CODEGEN_RISCV64) && \
    961    !defined(JS_CODEGEN_LOONG64)
    962  JS_SetGlobalJitCompilerOption(
    963      cx, JSJITCOMPILER_SPECTRE_INDEX_MASKING,
    964      StaticPrefs::javascript_options_spectre_index_masking_DoNotUseDirectly());
    965  JS_SetGlobalJitCompilerOption(
    966      cx, JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS,
    967      StaticPrefs::
    968          javascript_options_spectre_object_mitigations_DoNotUseDirectly());
    969  JS_SetGlobalJitCompilerOption(
    970      cx, JSJITCOMPILER_SPECTRE_STRING_MITIGATIONS,
    971      StaticPrefs::
    972          javascript_options_spectre_string_mitigations_DoNotUseDirectly());
    973  JS_SetGlobalJitCompilerOption(
    974      cx, JSJITCOMPILER_SPECTRE_VALUE_MASKING,
    975      StaticPrefs::javascript_options_spectre_value_masking_DoNotUseDirectly());
    976  JS_SetGlobalJitCompilerOption(
    977      cx, JSJITCOMPILER_SPECTRE_JIT_TO_CXX_CALLS,
    978      StaticPrefs::
    979          javascript_options_spectre_jit_to_cxx_calls_DoNotUseDirectly());
    980 #endif
    981 
    982  bool writeProtectCode = true;
    983  if (XRE_IsContentProcess()) {
    984    writeProtectCode =
    985        StaticPrefs::javascript_options_content_process_write_protect_code();
    986  }
    987  JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_WRITE_PROTECT_CODE,
    988                                writeProtectCode);
    989 }
    990 
    991 static void ReloadPrefsCallback(const char* pref, void* aXpccx) {
    992  // Note: Prefs that require a restart are handled in LoadStartupJSPrefs above.
    993 
    994  // Update all non-startup JS::Prefs.
    995  SET_NON_STARTUP_JS_PREFS_FROM_BROWSER_PREFS;
    996 
    997  auto xpccx = static_cast<XPCJSContext*>(aXpccx);
    998  JSContext* cx = xpccx->Context();
    999 
   1000  sDiscardSystemSource =
   1001      Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
   1002  sSharedMemoryEnabled =
   1003      Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory");
   1004  sStreamsEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
   1005 
   1006 #ifdef JS_GC_ZEAL
   1007  int32_t zeal = Preferences::GetInt(JS_OPTIONS_DOT_STR "mem.gc_zeal.mode", -1);
   1008  int32_t zeal_frequency =
   1009      Preferences::GetInt(JS_OPTIONS_DOT_STR "mem.gc_zeal.frequency",
   1010                          JS::BrowserDefaultGCZealFrequency);
   1011  if (zeal >= 0) {
   1012    JS::SetGCZeal(cx, (uint8_t)zeal, zeal_frequency);
   1013  }
   1014 #endif  // JS_GC_ZEAL
   1015 
   1016  auto& contextOptions = JS::ContextOptionsRef(cx);
   1017  SetPrefableContextOptions(contextOptions);
   1018 
   1019  // Set options not shared with workers.
   1020  contextOptions
   1021      .setThrowOnDebuggeeWouldRun(Preferences::GetBool(
   1022          JS_OPTIONS_DOT_STR "throw_on_debuggee_would_run"))
   1023      .setDumpStackOnDebuggeeWouldRun(Preferences::GetBool(
   1024          JS_OPTIONS_DOT_STR "dump_stack_on_debuggee_would_run"));
   1025 
   1026  nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
   1027  if (xr) {
   1028    bool safeMode = false;
   1029    xr->GetInSafeMode(&safeMode);
   1030    if (safeMode) {
   1031      contextOptions.disableOptionsForSafeMode();
   1032    }
   1033  }
   1034 
   1035  // Set up the callback for DOM event dispatch
   1036  if (StaticPrefs::dom_expose_test_interfaces()) {
   1037    JS::SetDispatchDOMEventCallback(cx, DispatchJitEventToDOM);
   1038  } else {
   1039    JS::SetDispatchDOMEventCallback(cx, nullptr);
   1040  }
   1041 }
   1042 
   1043 XPCJSContext::~XPCJSContext() {
   1044  MOZ_COUNT_DTOR_INHERITED(XPCJSContext, CycleCollectedJSContext);
   1045  // Elsewhere we abort immediately if XPCJSContext initialization fails.
   1046  // Therefore the context must be non-null.
   1047  MOZ_ASSERT(MaybeContext());
   1048 
   1049  Preferences::UnregisterPrefixCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR,
   1050                                        this);
   1051 
   1052 #ifdef FUZZING
   1053  Preferences::UnregisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
   1054 #endif
   1055 
   1056  // Clear any pending exception.  It might be an XPCWrappedJS, and if we try
   1057  // to destroy it later we will crash.
   1058  SetPendingException(nullptr);
   1059 
   1060  // If we're the last XPCJSContext around, clean up the watchdog manager.
   1061  if (--sInstanceCount == 0) {
   1062    if (mWatchdogManager->GetWatchdog()) {
   1063      mWatchdogManager->StopWatchdog();
   1064    }
   1065 
   1066    mWatchdogManager->UnregisterContext(this);
   1067    mWatchdogManager->Shutdown();
   1068    sWatchdogInstance = nullptr;
   1069  } else {
   1070    // Otherwise, simply remove ourselves from the list.
   1071    mWatchdogManager->UnregisterContext(this);
   1072  }
   1073 
   1074  if (mCallContext) {
   1075    mCallContext->SystemIsBeingShutDown();
   1076  }
   1077 
   1078  PROFILER_CLEAR_JS_CONTEXT();
   1079 }
   1080 
   1081 XPCJSContext::XPCJSContext()
   1082    : mCallContext(nullptr),
   1083      mAutoRoots(nullptr),
   1084      mResolveName(JS::PropertyKey::Void()),
   1085      mResolvingWrapper(nullptr),
   1086      mWatchdogManager(GetWatchdogManager()),
   1087      mSlowScriptSecondHalf(false),
   1088      mTimeoutAccumulated(false),
   1089      mExecutedChromeScript(false),
   1090      mHasScriptActivity(false),
   1091      mPendingResult(NS_OK),
   1092      mActive(CONTEXT_INACTIVE),
   1093      mLastStateChange(PR_Now()) {
   1094  MOZ_COUNT_CTOR_INHERITED(XPCJSContext, CycleCollectedJSContext);
   1095  MOZ_ASSERT(mWatchdogManager);
   1096  ++sInstanceCount;
   1097  mWatchdogManager->RegisterContext(this);
   1098 }
   1099 
   1100 /* static */
   1101 XPCJSContext* XPCJSContext::Get() {
   1102  // Do an explicit null check, because this can get called from a process that
   1103  // does not run JS.
   1104  nsXPConnect* xpc = static_cast<nsXPConnect*>(nsXPConnect::XPConnect());
   1105  return xpc ? xpc->GetContext() : nullptr;
   1106 }
   1107 
   1108 #ifdef XP_WIN
   1109 static size_t GetWindowsStackSize() {
   1110  // First, get the stack base. Because the stack grows down, this is the top
   1111  // of the stack.
   1112  const uint8_t* stackTop;
   1113 #  ifdef _WIN64
   1114  PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
   1115  stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
   1116 #  else
   1117  PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
   1118  stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
   1119 #  endif
   1120 
   1121  // Now determine the stack bottom. Note that we can't use tib->StackLimit,
   1122  // because that's the size of the committed area and we're also interested
   1123  // in the reserved pages below that.
   1124  MEMORY_BASIC_INFORMATION mbi;
   1125  if (!VirtualQuery(&mbi, &mbi, sizeof(mbi))) {
   1126    MOZ_CRASH("VirtualQuery failed");
   1127  }
   1128 
   1129  const uint8_t* stackBottom =
   1130      reinterpret_cast<const uint8_t*>(mbi.AllocationBase);
   1131 
   1132  // Do some sanity checks.
   1133  size_t stackSize = size_t(stackTop - stackBottom);
   1134  MOZ_RELEASE_ASSERT(stackSize >= 1 * 1024 * 1024);
   1135  MOZ_RELEASE_ASSERT(stackSize <= 32 * 1024 * 1024);
   1136 
   1137  // Subtract 40 KB (Win32) or 80 KB (Win64) to account for things like
   1138  // the guard page and large PGO stack frames.
   1139  return stackSize - 10 * sizeof(uintptr_t) * 1024;
   1140 }
   1141 #endif
   1142 
   1143 XPCJSRuntime* XPCJSContext::Runtime() const {
   1144  return static_cast<XPCJSRuntime*>(CycleCollectedJSContext::Runtime());
   1145 }
   1146 
   1147 CycleCollectedJSRuntime* XPCJSContext::CreateRuntime(JSContext* aCx) {
   1148  return new XPCJSRuntime(aCx);
   1149 }
   1150 
   1151 class HelperThreadTaskHandler : public Task {
   1152  JS::HelperThreadTask* mTask;
   1153 
   1154 public:
   1155  explicit HelperThreadTaskHandler(JS::HelperThreadTask* aTask)
   1156      : Task(Kind::OffMainThreadOnly, EventQueuePriority::Normal),
   1157        mTask(aTask) {
   1158    // Bug 1703185: Currently all tasks are run at the same priority.
   1159  }
   1160 
   1161  TaskResult Run() override {
   1162    JS::RunHelperThreadTask(mTask);
   1163    return TaskResult::Complete;
   1164  }
   1165 
   1166 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
   1167  bool GetName(nsACString& aName) override {
   1168    const char* taskName = JS::GetHelperThreadTaskName(mTask);
   1169    aName.AssignLiteral(taskName, strlen(taskName));
   1170    return true;
   1171  }
   1172 #endif
   1173 
   1174 private:
   1175  ~HelperThreadTaskHandler() = default;
   1176 };
   1177 
   1178 static void DispatchOffThreadTask(JS::HelperThreadTask* aTask) {
   1179  TaskController::Get()->AddTask(MakeAndAddRef<HelperThreadTaskHandler>(aTask));
   1180 }
   1181 
   1182 // Name of entry in mozilla::scache::StartupCache to use for SpiderMonkey
   1183 // self-hosted JS precompiled bytecode.
   1184 static constexpr char kSelfHostCacheKey[] = "js.self-hosted";
   1185 
   1186 static bool CreateSelfHostedSharedMemory(JSContext* aCx,
   1187                                         JS::SelfHostedCache aBuf) {
   1188  // Record the data to the "StartupCache" for future restarts to use to
   1189  // initialize the shmem with.
   1190  if (auto* sc = scache::StartupCache::GetSingleton()) {
   1191    UniqueFreePtr<char[]> copy(static_cast<char*>(malloc(aBuf.LengthBytes())));
   1192    if (copy) {
   1193      memcpy(copy.get(), aBuf.Elements(), aBuf.LengthBytes());
   1194      sc->PutBuffer(kSelfHostCacheKey, std::move(copy), aBuf.LengthBytes());
   1195    }
   1196  }
   1197 
   1198  auto& shm = xpc::SelfHostedShmem::GetSingleton();
   1199  MOZ_RELEASE_ASSERT(shm.Content().IsEmpty());
   1200  // Failures within InitFromParent output warnings but do not cause
   1201  // unrecoverable failures.
   1202  shm.InitFromParent(aBuf);
   1203  return true;
   1204 }
   1205 
   1206 static JS::OpaqueLogger GetLoggerByName(const char* name) {
   1207  LogModule* tmp = LogModule::Get(name);
   1208  return static_cast<JS::OpaqueLogger>(tmp);
   1209 }
   1210 
   1211 MOZ_FORMAT_PRINTF(3, 0)
   1212 static void LogPrintVA(JS::OpaqueLogger aLogger, mozilla::LogLevel level,
   1213                       const char* aFmt, va_list ap) {
   1214  LogModule* logmod = static_cast<LogModule*>(aLogger);
   1215 
   1216  logmod->Printv(level, aFmt, ap);
   1217 }
   1218 
   1219 static void LogPrintFMT(JS::OpaqueLogger aLogger, mozilla::LogLevel level,
   1220                        fmt::string_view fmt, fmt::format_args args) {
   1221  LogModule* logmod = static_cast<LogModule*>(aLogger);
   1222 
   1223  logmod->PrintvFmt(level, fmt, args);
   1224 }
   1225 
   1226 static AtomicLogLevel& GetLevelRef(JS::OpaqueLogger aLogger) {
   1227  LogModule* logmod = static_cast<LogModule*>(aLogger);
   1228  return logmod->LevelRef();
   1229 }
   1230 
   1231 static JS::LoggingInterface loggingInterface = {GetLoggerByName, LogPrintVA,
   1232                                                LogPrintFMT, GetLevelRef};
   1233 
   1234 nsresult XPCJSContext::Initialize() {
   1235  if (StaticPrefs::javascript_options_external_thread_pool_DoNotUseDirectly()) {
   1236    size_t threadCount = TaskController::GetPoolThreadCount();
   1237    size_t stackSize = TaskController::GetThreadStackSize();
   1238    SetHelperThreadTaskCallback(&DispatchOffThreadTask, threadCount, stackSize);
   1239  }
   1240 
   1241  if (!JS::SetLoggingInterface(loggingInterface)) {
   1242    MOZ_CRASH("Failed to install logging interface");
   1243  }
   1244 
   1245  nsresult rv =
   1246      CycleCollectedJSContext::Initialize(nullptr, JS::DefaultHeapMaxBytes);
   1247  if (NS_WARN_IF(NS_FAILED(rv))) {
   1248    return rv;
   1249  }
   1250 
   1251  MOZ_ASSERT(Context());
   1252  JSContext* cx = Context();
   1253 
   1254  // The JS engine permits us to set different stack limits for system code,
   1255  // trusted script, and untrusted script. We have tests that ensure that
   1256  // we can always execute 10 "heavy" (eval+with) stack frames deeper in
   1257  // privileged code. Our stack sizes vary greatly in different configurations,
   1258  // so satisfying those tests requires some care. Manual measurements of the
   1259  // number of heavy stack frames achievable gives us the following rough data,
   1260  // ordered by the effective categories in which they are grouped in the
   1261  // JS_SetNativeStackQuota call (which predates this analysis).
   1262  //
   1263  // The following "Stack Frames" numbers come from `chromeLimit` in
   1264  // js/xpconnect/tests/chrome/test_bug732665.xul
   1265  //
   1266  //  Platform   | Build | Stack Quota | Stack Frames | Stack Frame Size
   1267  // ------------+-------+-------------+--------------+------------------
   1268  //  OSX 64     | Opt   | 7MB         | 1331         | ~5.4k
   1269  //  OSX 64     | Debug | 7MB         | 1202         | ~6.0k
   1270  // ------------+-------+-------------+--------------+------------------
   1271  //  Linux 32   | Opt   | 7.875MB     | 2513         | ~3.2k
   1272  //  Linux 32   | Debug | 7.875MB     | 2146         | ~3.8k
   1273  // ------------+-------+-------------+--------------+------------------
   1274  //  Linux 64   | Opt   | 7.875MB     | 1360         | ~5.9k
   1275  //  Linux 64   | Debug | 7.875MB     | 1180         | ~6.8k
   1276  //  Linux 64   | ASan  | 7.875MB     | 473          | ~17.0k
   1277  // ------------+-------+-------------+--------------+------------------
   1278  //  Windows 32 | Opt   | 984k        | 188          | ~5.2k
   1279  //  Windows 32 | Debug | 984k        | 208          | ~4.7k
   1280  // ------------+-------+-------------+--------------+------------------
   1281  //  Windows 64 | Opt   | 1.922MB     | 189          | ~10.4k
   1282  //  Windows 64 | Debug | 1.922MB     | 175          | ~11.2k
   1283  //
   1284  // We tune the trusted/untrusted quotas for each configuration to achieve our
   1285  // invariants while attempting to minimize overhead. In contrast, our buffer
   1286  // between system code and trusted script is a very unscientific 10k.
   1287  const size_t kSystemCodeBuffer = 10 * 1024;
   1288 
   1289  // Our "default" stack is what we use in configurations where we don't have
   1290  // a compelling reason to do things differently. This is effectively 512KB
   1291  // on 32-bit platforms and 1MB on 64-bit platforms.
   1292  const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
   1293 
   1294  // Set maximum stack size for different configurations. This value is then
   1295  // capped below because huge stacks are not web-compatible.
   1296 
   1297 #if defined(XP_MACOSX) || defined(DARWIN)
   1298  // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
   1299  // and give trusted script 180k extra. The stack is huge on mac anyway.
   1300  const size_t kUncappedStackQuota = 7 * 1024 * 1024;
   1301  const size_t kTrustedScriptBuffer = 180 * 1024;
   1302 #elif defined(XP_LINUX) && !defined(ANDROID)
   1303  // Most Linux distributions set default stack size to 8MB.  Use it as the
   1304  // maximum value.
   1305  const size_t kStackQuotaMax = 8 * 1024 * 1024;
   1306 #  if defined(MOZ_ASAN) || defined(DEBUG)
   1307  // Bug 803182: account for the 4x difference in the size of js::Interpret
   1308  // between optimized and debug builds.  We use 2x since the JIT part
   1309  // doesn't increase much.
   1310  // See the standalone MOZ_ASAN branch below for the ASan case.
   1311  const size_t kStackQuotaMin = 2 * kDefaultStackQuota;
   1312 #  else
   1313  const size_t kStackQuotaMin = kDefaultStackQuota;
   1314 #  endif
   1315  // Allocate 128kB margin for the safe space.
   1316  const size_t kStackSafeMargin = 128 * 1024;
   1317 
   1318  struct rlimit rlim;
   1319  const size_t kUncappedStackQuota =
   1320      getrlimit(RLIMIT_STACK, &rlim) == 0
   1321          ? std::clamp(size_t(rlim.rlim_cur - kStackSafeMargin), kStackQuotaMin,
   1322                       kStackQuotaMax - kStackSafeMargin)
   1323          : kStackQuotaMin;
   1324 #  if defined(MOZ_ASAN)
   1325  // See the standalone MOZ_ASAN branch below for the ASan case.
   1326  const size_t kTrustedScriptBuffer = 450 * 1024;
   1327 #  else
   1328  const size_t kTrustedScriptBuffer = 180 * 1024;
   1329 #  endif
   1330 #elif defined(XP_WIN)
   1331  // 1MB is the default stack size on Windows. We use the -STACK linker flag
   1332  // (see WIN32_EXE_LDFLAGS in config/config.mk) to request a larger stack, so
   1333  // we determine the stack size at runtime.
   1334  const size_t kUncappedStackQuota = GetWindowsStackSize();
   1335 #  if defined(MOZ_ASAN)
   1336  // See the standalone MOZ_ASAN branch below for the ASan case.
   1337  const size_t kTrustedScriptBuffer = 450 * 1024;
   1338 #  else
   1339  const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8)
   1340                                          ? 180 * 1024   // win64
   1341                                          : 120 * 1024;  // win32
   1342 #  endif
   1343 #elif defined(MOZ_ASAN)
   1344  // ASan requires more stack space due to red-zones, so give it double the
   1345  // default (1MB on 32-bit, 2MB on 64-bit). ASAN stack frame measurements
   1346  // were not taken at the time of this writing, so we hazard a guess that
   1347  // ASAN builds have roughly thrice the stack overhead as normal builds.
   1348  // On normal builds, the largest stack frame size we might encounter is
   1349  // 9.0k (see above), so let's use a buffer of 9.0 * 5 * 10 = 450k.
   1350  //
   1351  // FIXME: Does this branch make sense for Windows and Android?
   1352  // (See bug 1415195)
   1353  const size_t kUncappedStackQuota = 2 * kDefaultStackQuota;
   1354  const size_t kTrustedScriptBuffer = 450 * 1024;
   1355 #elif defined(ANDROID)
   1356  // Android appears to have 1MB stacks. Allow the use of 3/4 of that size
   1357  // (768KB on 32-bit), since otherwise we can crash with a stack overflow
   1358  // when nearing the 1MB limit.
   1359  const size_t kUncappedStackQuota =
   1360      kDefaultStackQuota + kDefaultStackQuota / 2;
   1361  const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
   1362 #else
   1363  // Catch-all configuration for other environments.
   1364 #  if defined(DEBUG)
   1365  const size_t kUncappedStackQuota = 2 * kDefaultStackQuota;
   1366 #  else
   1367  const size_t kUncappedStackQuota = kDefaultStackQuota;
   1368 #  endif
   1369  // Given the numbers above, we use 50k and 100k trusted buffers on 32-bit
   1370  // and 64-bit respectively.
   1371  const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
   1372 #endif
   1373 
   1374  // Avoid an unused variable warning on platforms where we don't use the
   1375  // default.
   1376  (void)kDefaultStackQuota;
   1377 
   1378  // Large stacks are not web-compatible so cap to a smaller value.
   1379  // See bug 1537609 and bug 1562700.
   1380  const size_t kStackQuotaCap =
   1381      StaticPrefs::javascript_options_main_thread_stack_quota_cap();
   1382  const size_t kStackQuota = std::min(kUncappedStackQuota, kStackQuotaCap);
   1383 
   1384  JS_SetNativeStackQuota(
   1385      cx, kStackQuota, kStackQuota - kSystemCodeBuffer,
   1386      kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
   1387 
   1388  PROFILER_SET_JS_CONTEXT(this);
   1389 
   1390  JS_AddInterruptCallback(cx, InterruptCallback);
   1391 
   1392  Runtime()->Initialize(cx);
   1393 
   1394  LoadStartupJSPrefs(this);
   1395 
   1396  // Watch for the JS boolean options.
   1397  ReloadPrefsCallback(nullptr, this);
   1398  Preferences::RegisterPrefixCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR,
   1399                                      this);
   1400 
   1401 #ifdef FUZZING
   1402  Preferences::RegisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
   1403 #endif
   1404 
   1405  // Initialize the MIME type used for the bytecode cache, after calling
   1406  // SetProcessBuildIdOp and loading JS prefs.
   1407  if (!nsContentUtils::InitJSBytecodeMimeType()) {
   1408    NS_ABORT_OOM(0);  // Size is unknown.
   1409  }
   1410 
   1411  // The self-hosted bytecode can be shared with child processes and also stored
   1412  // in startupcache. Only the parent process may initialize the data.
   1413  auto& shm = xpc::SelfHostedShmem::GetSingleton();
   1414  JS::SelfHostedWriter writer = nullptr;
   1415  if (XRE_IsParentProcess() &&
   1416      xpc::SelfHostedShmem::SelfHostedUseSharedMemory()) {
   1417    // Check the startup cache for a copy of the bytecode.
   1418    if (auto* sc = scache::StartupCache::GetSingleton()) {
   1419      const char* buf = nullptr;
   1420      uint32_t len = 0;
   1421      if (NS_SUCCEEDED(sc->GetBuffer(kSelfHostCacheKey, &buf, &len))) {
   1422        shm.InitFromParent(AsBytes(mozilla::Span(buf, len)));
   1423      }
   1424    }
   1425 
   1426    // If we have no data then the InitSelfHostedCode call below will parse from
   1427    // scratch and invoke this callback with the results. That callback data can
   1428    // then be used in initialize cache and SelfHostedShmem.
   1429    if (shm.Content().IsEmpty()) {
   1430      writer = CreateSelfHostedSharedMemory;
   1431    }
   1432  }
   1433 
   1434  if (!JS::InitSelfHostedCode(cx, shm.Content(), writer)) {
   1435    // Note: If no exception is pending, failure is due to OOM.
   1436    if (!JS_IsExceptionPending(cx) || JS_IsThrowingOutOfMemory(cx)) {
   1437      NS_ABORT_OOM(0);  // Size is unknown.
   1438    }
   1439 
   1440    // Failed to execute self-hosted JavaScript! Uh oh.
   1441    MOZ_CRASH("InitSelfHostedCode failed");
   1442  }
   1443 
   1444 #ifdef MOZ_EXECUTION_TRACING
   1445  JS_SetCustomObjectSummaryCallback(cx, ExecutionTracerIntegration::Callback);
   1446 #endif
   1447 
   1448  MOZ_RELEASE_ASSERT(Runtime()->InitializeStrings(cx),
   1449                     "InitializeStrings failed");
   1450 
   1451  return NS_OK;
   1452 }
   1453 
   1454 // static
   1455 uint32_t XPCJSContext::sInstanceCount;
   1456 
   1457 // static
   1458 StaticAutoPtr<WatchdogManager> XPCJSContext::sWatchdogInstance;
   1459 
   1460 // static
   1461 WatchdogManager* XPCJSContext::GetWatchdogManager() {
   1462  if (sWatchdogInstance) {
   1463    return sWatchdogInstance;
   1464  }
   1465 
   1466  MOZ_ASSERT(sInstanceCount == 0);
   1467  sWatchdogInstance = new WatchdogManager();
   1468  return sWatchdogInstance;
   1469 }
   1470 
   1471 // static
   1472 XPCJSContext* XPCJSContext::NewXPCJSContext() {
   1473  XPCJSContext* self = new XPCJSContext();
   1474  nsresult rv = self->Initialize();
   1475  if (rv == NS_ERROR_OUT_OF_MEMORY) {
   1476    mozalloc_handle_oom(0);
   1477  } else if (NS_FAILED(rv)) {
   1478    MOZ_CRASH("new XPCJSContext failed to initialize.");
   1479  }
   1480 
   1481  if (self->Context()) {
   1482    return self;
   1483  }
   1484 
   1485  MOZ_CRASH("new XPCJSContext failed to initialize.");
   1486 }
   1487 
   1488 void XPCJSContext::BeforeProcessTask(bool aMightBlock) {
   1489  MOZ_ASSERT(NS_IsMainThread());
   1490 
   1491  // Start the slow script timer.
   1492  mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
   1493  mSlowScriptSecondHalf = false;
   1494  mSlowScriptActualWait = mozilla::TimeDuration();
   1495  mTimeoutAccumulated = false;
   1496  mExecutedChromeScript = false;
   1497  CycleCollectedJSContext::BeforeProcessTask(aMightBlock);
   1498 }
   1499 
   1500 void XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth) {
   1501  // Record hangs in the parent process for telemetry.
   1502  if (mSlowScriptSecondHalf && XRE_IsE10sParentProcess()) {
   1503    double hangDuration = (mozilla::TimeStamp::NowLoRes() -
   1504                           mSlowScriptCheckpoint + mSlowScriptActualWait)
   1505                              .ToSeconds();
   1506    // We use the pref to test this code.
   1507    double limit = sChromeSlowScriptTelemetryCutoff;
   1508    if (xpc::IsInAutomation()) {
   1509      double prefLimit = StaticPrefs::dom_max_chrome_script_run_time();
   1510      if (prefLimit > 0) {
   1511        limit = std::min(prefLimit, sChromeSlowScriptTelemetryCutoff);
   1512      }
   1513    }
   1514    if (hangDuration > limit) {
   1515      // Use AppendFloat to avoid printf-type APIs using locale-specific
   1516      // decimal separators, when we definitely want a `.`.
   1517      nsCString durationStr;
   1518      durationStr.AppendFloat(hangDuration);
   1519 
   1520      glean::slow_script_warning::ShownBrowserExtra extra = {
   1521          .hangDuration = Some(durationStr),
   1522          .uriType = Some(mExecutedChromeScript ? "browser"_ns : "content"_ns),
   1523      };
   1524      glean::slow_script_warning::shown_browser.Record(Some(extra));
   1525    }
   1526  }
   1527 
   1528  // Now that we're back to the event loop, reset the slow script checkpoint.
   1529  mSlowScriptCheckpoint = mozilla::TimeStamp();
   1530  mSlowScriptSecondHalf = false;
   1531 
   1532  // Call cycle collector occasionally.
   1533  MOZ_ASSERT(NS_IsMainThread());
   1534  nsJSContext::MaybePokeCC();
   1535  CycleCollectedJSContext::AfterProcessTask(aNewRecursionDepth);
   1536 
   1537  // Poke the memory telemetry reporter
   1538  if (AppShutdown::GetCurrentShutdownPhase() == ShutdownPhase::NotInShutdown) {
   1539    MemoryTelemetry::Get().Poke();
   1540  }
   1541 
   1542  // This exception might have been set if we called an XPCWrappedJS that threw,
   1543  // but now we're returning to the event loop, so nothing is going to look at
   1544  // this value again. Clear it to prevent leaks.
   1545  SetPendingException(nullptr);
   1546 }
   1547 
   1548 void XPCJSContext::MaybePokeGC() { nsJSContext::MaybePokeGC(); }
   1549 
   1550 bool XPCJSContext::IsSystemCaller() const {
   1551  return nsContentUtils::IsSystemCaller(Context());
   1552 }