tor-browser

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

Console.cpp (91377B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/Console.h"
      8 
      9 #include "ConsoleCommon.h"
     10 #include "js/Array.h"               // JS::GetArrayLength, JS::NewArrayObject
     11 #include "js/PropertyAndElement.h"  // JS_DefineElement, JS_DefineProperty, JS_GetElement
     12 #include "mozilla/BasePrincipal.h"
     13 #include "mozilla/HoldDropJSObjects.h"
     14 #include "mozilla/JSObjectHolder.h"
     15 #include "mozilla/Maybe.h"
     16 #include "mozilla/Mutex.h"
     17 #include "mozilla/Preferences.h"
     18 #include "mozilla/StaticPrefs_devtools.h"
     19 #include "mozilla/StaticPrefs_dom.h"
     20 #include "mozilla/dom/BlobBinding.h"
     21 #include "mozilla/dom/BlobImpl.h"
     22 #include "mozilla/dom/ConsoleBinding.h"
     23 #include "mozilla/dom/ConsoleInstance.h"
     24 #include "mozilla/dom/Document.h"
     25 #include "mozilla/dom/ElementBinding.h"
     26 #include "mozilla/dom/Exceptions.h"
     27 #include "mozilla/dom/File.h"
     28 #include "mozilla/dom/FunctionBinding.h"
     29 #include "mozilla/dom/Performance.h"
     30 #include "mozilla/dom/PromiseBinding.h"
     31 #include "mozilla/dom/RootedDictionary.h"
     32 #include "mozilla/dom/ScriptSettings.h"
     33 #include "mozilla/dom/StructuredCloneHolder.h"
     34 #include "mozilla/dom/ToJSValue.h"
     35 #include "mozilla/dom/WorkerRunnable.h"
     36 #include "mozilla/dom/WorkerScope.h"
     37 #include "mozilla/dom/WorkletGlobalScope.h"
     38 #include "mozilla/dom/WorkletImpl.h"
     39 #include "mozilla/dom/WorkletThread.h"
     40 #include "nsContentUtils.h"
     41 #include "nsCycleCollectionParticipant.h"
     42 #include "nsDOMNavigationTiming.h"
     43 #include "nsDocShell.h"
     44 #include "nsGlobalWindowInner.h"
     45 #include "nsIConsoleAPIStorage.h"
     46 #include "nsIException.h"  // for nsIStackFrame
     47 #include "nsIInterfaceRequestorUtils.h"
     48 #include "nsILoadContext.h"
     49 #include "nsISensitiveInfoHiddenURI.h"
     50 #include "nsISupportsPrimitives.h"
     51 #include "nsIWebNavigation.h"
     52 #include "nsIXPConnect.h"
     53 #include "nsJSUtils.h"
     54 #include "nsNetUtil.h"
     55 #include "nsProxyRelease.h"
     56 #include "nsReadableUtils.h"
     57 #include "xpcpublic.h"
     58 
     59 // The maximum allowed number of concurrent timers per page.
     60 #define MAX_PAGE_TIMERS 10000
     61 
     62 // The maximum allowed number of concurrent counters per page.
     63 #define MAX_PAGE_COUNTERS 10000
     64 
     65 // The maximum stacktrace depth when populating the stacktrace array used for
     66 // console.trace().
     67 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
     68 
     69 // This tags are used in the Structured Clone Algorithm to move js values from
     70 // worker thread to main thread
     71 #define CONSOLE_TAG_BLOB JS_SCTAG_USER_MIN
     72 
     73 // This value is taken from ConsoleAPIStorage.js
     74 #define STORAGE_MAX_EVENTS 1000
     75 
     76 using namespace mozilla::dom::exceptions;
     77 
     78 namespace mozilla::dom {
     79 
     80 struct ConsoleStructuredCloneData {
     81  nsCOMPtr<nsIGlobalObject> mGlobal;
     82  nsTArray<RefPtr<BlobImpl>> mBlobs;
     83 };
     84 
     85 static void ComposeAndStoreGroupName(JSContext* aCx,
     86                                     const Sequence<JS::Value>& aData,
     87                                     nsAString& aName,
     88                                     nsTArray<nsString>* aGroupStack);
     89 static bool UnstoreGroupName(nsAString& aName, nsTArray<nsString>* aGroupStack);
     90 
     91 static bool ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData,
     92                             Sequence<JS::Value>& aSequence,
     93                             Sequence<nsString>& aStyles);
     94 
     95 static JS::Value CreateCounterOrResetCounterValue(JSContext* aCx,
     96                                                  const nsAString& aCountLabel,
     97                                                  uint32_t aCountValue);
     98 
     99 /**
    100 * Console API in workers uses the Structured Clone Algorithm to move any value
    101 * from the worker thread to the main-thread. Some object cannot be moved and,
    102 * in these cases, we convert them to strings.
    103 * It's not the best, but at least we are able to show something.
    104 */
    105 
    106 class ConsoleCallData final {
    107 public:
    108  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConsoleCallData)
    109 
    110  ConsoleCallData(Console::MethodName aName, const nsAString& aString,
    111                  Console* aConsole)
    112      : mMutex("ConsoleCallData"),
    113        mConsoleID(aConsole->mConsoleID),
    114        mPrefix(aConsole->mPrefix),
    115        mMethodName(aName),
    116        mMicroSecondTimeStamp(JS_Now()),
    117        mStartTimerValue(0),
    118        mStartTimerStatus(Console::eTimerUnknown),
    119        mLogTimerDuration(0),
    120        mLogTimerStatus(Console::eTimerUnknown),
    121        mCountValue(MAX_PAGE_COUNTERS),
    122        mIDType(eUnknown),
    123        mOuterIDNumber(0),
    124        mInnerIDNumber(0),
    125        mMethodString(aString) {}
    126 
    127  void SetIDs(uint64_t aOuterID, uint64_t aInnerID) MOZ_REQUIRES(mMutex) {
    128    MOZ_ASSERT(mIDType == eUnknown);
    129 
    130    mOuterIDNumber = aOuterID;
    131    mInnerIDNumber = aInnerID;
    132    mIDType = eNumber;
    133  }
    134 
    135  void SetIDs(const nsAString& aOuterID, const nsAString& aInnerID)
    136      MOZ_REQUIRES(mMutex) {
    137    MOZ_ASSERT(mIDType == eUnknown);
    138 
    139    mOuterIDString = aOuterID;
    140    mInnerIDString = aInnerID;
    141    mIDType = eString;
    142  }
    143 
    144  void SetOriginAttributes(const OriginAttributes& aOriginAttributes)
    145      MOZ_REQUIRES(mMutex) {
    146    mOriginAttributes = aOriginAttributes;
    147  }
    148 
    149  void SetAddonId(nsIPrincipal* aPrincipal) MOZ_REQUIRES(mMutex) {
    150    nsAutoString addonId;
    151    aPrincipal->GetAddonId(addonId);
    152 
    153    mAddonId = addonId;
    154  }
    155 
    156  void AssertIsOnOwningThread() const {
    157    NS_ASSERT_OWNINGTHREAD(ConsoleCallData);
    158  }
    159 
    160  Mutex mMutex;
    161 
    162  const nsString mConsoleID MOZ_GUARDED_BY(mMutex);
    163  const nsString mPrefix MOZ_GUARDED_BY(mMutex);
    164 
    165  const Console::MethodName mMethodName MOZ_GUARDED_BY(mMutex);
    166  int64_t mMicroSecondTimeStamp MOZ_GUARDED_BY(mMutex);
    167 
    168  // These values are set in the owning thread and they contain the timestamp of
    169  // when the new timer has started, the name of it and the status of the
    170  // creation of it. If status is false, something went wrong. User
    171  // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
    172  // monotonicTimer from Performance.now();
    173  // They will be set on the owning thread and never touched again on that
    174  // thread. They will be used in order to create a ConsoleTimerStart dictionary
    175  // when console.time() is used.
    176  DOMHighResTimeStamp mStartTimerValue MOZ_GUARDED_BY(mMutex);
    177  nsString mStartTimerLabel MOZ_GUARDED_BY(mMutex);
    178  Console::TimerStatus mStartTimerStatus MOZ_GUARDED_BY(mMutex);
    179 
    180  // These values are set in the owning thread and they contain the duration,
    181  // the name and the status of the LogTimer method. If status is false,
    182  // something went wrong. They will be set on the owning thread and never
    183  // touched again on that thread. They will be used in order to create a
    184  // ConsoleTimerLogOrEnd dictionary. This members are set when
    185  // console.timeEnd() or console.timeLog() are called.
    186  double mLogTimerDuration MOZ_GUARDED_BY(mMutex);
    187  nsString mLogTimerLabel MOZ_GUARDED_BY(mMutex);
    188  Console::TimerStatus mLogTimerStatus MOZ_GUARDED_BY(mMutex);
    189 
    190  // These 2 values are set by IncreaseCounter or ResetCounter on the owning
    191  // thread and they are used by CreateCounterOrResetCounterValue.
    192  // These members are set when console.count() or console.countReset() are
    193  // called.
    194  nsString mCountLabel MOZ_GUARDED_BY(mMutex);
    195  uint32_t mCountValue MOZ_GUARDED_BY(mMutex);
    196 
    197  // The concept of outerID and innerID is misleading because when a
    198  // ConsoleCallData is created from a window, these are the window IDs, but
    199  // when the object is created from a SharedWorker, a ServiceWorker or a
    200  // subworker of a ChromeWorker these IDs are the type of worker and the
    201  // filename of the callee.
    202  // In Console.sys.mjs the ID is 'jsm'.
    203  enum { eString, eNumber, eUnknown } mIDType MOZ_GUARDED_BY(mMutex);
    204 
    205  uint64_t mOuterIDNumber MOZ_GUARDED_BY(mMutex);
    206  nsString mOuterIDString MOZ_GUARDED_BY(mMutex);
    207 
    208  uint64_t mInnerIDNumber MOZ_GUARDED_BY(mMutex);
    209  nsString mInnerIDString MOZ_GUARDED_BY(mMutex);
    210 
    211  OriginAttributes mOriginAttributes MOZ_GUARDED_BY(mMutex);
    212 
    213  nsString mAddonId MOZ_GUARDED_BY(mMutex);
    214 
    215  const nsString mMethodString MOZ_GUARDED_BY(mMutex);
    216 
    217  // Stack management is complicated, because we want to do it as
    218  // lazily as possible.  Therefore, we have the following behavior:
    219  // 1)  mTopStackFrame is initialized whenever we have any JS on the stack
    220  // 2)  mReifiedStack is initialized if we're created in a worker.
    221  // 3)  mStack is set (possibly to null if there is no JS on the stack) if
    222  //     we're created on main thread.
    223  Maybe<ConsoleStackEntry> mTopStackFrame MOZ_GUARDED_BY(mMutex);
    224  Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack MOZ_GUARDED_BY(mMutex);
    225  nsCOMPtr<nsIStackFrame> mStack MOZ_GUARDED_BY(mMutex);
    226 
    227 private:
    228  ~ConsoleCallData() = default;
    229 
    230  NS_DECL_OWNINGTHREAD;
    231 };
    232 
    233 // MainThreadConsoleData instances are created on the Console thread and
    234 // referenced from both main and Console threads in order to provide the same
    235 // object for any ConsoleRunnables relating to the same Console.  A Console
    236 // owns a MainThreadConsoleData; MainThreadConsoleData does not keep its
    237 // Console alive.
    238 class MainThreadConsoleData final {
    239  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MainThreadConsoleData);
    240 
    241  JSObject* GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal);
    242  // This method must receive aCx and aArguments in the same JS::Compartment.
    243  void ProcessCallData(JSContext* aCx, ConsoleCallData* aData,
    244                       const Sequence<JS::Value>& aArguments);
    245 
    246 private:
    247  ~MainThreadConsoleData() {
    248    NS_ReleaseOnMainThread("MainThreadConsoleData::mStorage",
    249                           mStorage.forget());
    250    NS_ReleaseOnMainThread("MainThreadConsoleData::mSandbox",
    251                           mSandbox.forget());
    252  }
    253 
    254  // All members, except for mRefCnt, are accessed only on the main thread,
    255  // except in MainThreadConsoleData destruction, at which point there are no
    256  // other references.
    257  nsCOMPtr<nsIConsoleAPIStorage> mStorage;
    258  RefPtr<JSObjectHolder> mSandbox;
    259  nsTArray<nsString> mGroupStack;
    260 };
    261 
    262 // This base class must be extended for Worker and for Worklet.
    263 class ConsoleRunnable : public StructuredCloneHolderBase {
    264 public:
    265  ~ConsoleRunnable() override {
    266    MOZ_ASSERT(!mClonedData.mGlobal,
    267               "mClonedData.mGlobal is set and cleared in a main thread scope");
    268    // Clear the StructuredCloneHolderBase class.
    269    Clear();
    270  }
    271 
    272 protected:
    273  JSObject* CustomReadHandler(JSContext* aCx, JSStructuredCloneReader* aReader,
    274                              const JS::CloneDataPolicy& aCloneDataPolicy,
    275                              uint32_t aTag, uint32_t aIndex) override {
    276    AssertIsOnMainThread();
    277 
    278    if (aTag == CONSOLE_TAG_BLOB) {
    279      MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
    280 
    281      JS::Rooted<JS::Value> val(aCx);
    282      {
    283        nsCOMPtr<nsIGlobalObject> global = mClonedData.mGlobal;
    284        RefPtr<Blob> blob =
    285            Blob::Create(global, mClonedData.mBlobs.ElementAt(aIndex));
    286        if (!ToJSValue(aCx, blob, &val)) {
    287          return nullptr;
    288        }
    289      }
    290 
    291      return &val.toObject();
    292    }
    293 
    294    MOZ_CRASH("No other tags are supported.");
    295    return nullptr;
    296  }
    297 
    298  bool CustomWriteHandler(JSContext* aCx, JSStructuredCloneWriter* aWriter,
    299                          JS::Handle<JSObject*> aObj,
    300                          bool* aSameProcessScopeRequired) override {
    301    RefPtr<Blob> blob;
    302    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
    303      if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
    304                                         mClonedData.mBlobs.Length()))) {
    305        return false;
    306      }
    307 
    308      mClonedData.mBlobs.AppendElement(blob->Impl());
    309      return true;
    310    }
    311 
    312    if (!JS_ObjectNotWritten(aWriter, aObj)) {
    313      return false;
    314    }
    315 
    316    JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
    317    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
    318    if (NS_WARN_IF(!jsString)) {
    319      return false;
    320    }
    321 
    322    if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
    323      return false;
    324    }
    325 
    326    return true;
    327  }
    328 
    329  // Helper method for CallData
    330  void ProcessCallData(JSContext* aCx, MainThreadConsoleData* aConsoleData,
    331                       ConsoleCallData* aCallData) {
    332    AssertIsOnMainThread();
    333 
    334    ConsoleCommon::ClearException ce(aCx);
    335 
    336    // This is the same policy as when writing from the other side, in
    337    // WriteData.
    338    JS::CloneDataPolicy cloneDataPolicy;
    339    cloneDataPolicy.allowIntraClusterClonableSharedObjects();
    340    cloneDataPolicy.allowSharedMemoryObjects();
    341 
    342    JS::Rooted<JS::Value> argumentsValue(aCx);
    343    if (!Read(aCx, &argumentsValue, cloneDataPolicy)) {
    344      return;
    345    }
    346 
    347    MOZ_ASSERT(argumentsValue.isObject());
    348 
    349    JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
    350 
    351    uint32_t length;
    352    if (!JS::GetArrayLength(aCx, argumentsObj, &length)) {
    353      return;
    354    }
    355 
    356    Sequence<JS::Value> values;
    357    SequenceRooter<JS::Value> arguments(aCx, &values);
    358 
    359    for (uint32_t i = 0; i < length; ++i) {
    360      JS::Rooted<JS::Value> value(aCx);
    361 
    362      if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
    363        return;
    364      }
    365 
    366      if (!values.AppendElement(value, fallible)) {
    367        return;
    368      }
    369    }
    370 
    371    MOZ_ASSERT(values.Length() == length);
    372 
    373    aConsoleData->ProcessCallData(aCx, aCallData, values);
    374  }
    375 
    376  // Generic
    377  bool WriteArguments(JSContext* aCx, const Sequence<JS::Value>& aArguments) {
    378    ConsoleCommon::ClearException ce(aCx);
    379 
    380    JS::Rooted<JSObject*> arguments(
    381        aCx, JS::NewArrayObject(aCx, aArguments.Length()));
    382    if (NS_WARN_IF(!arguments)) {
    383      return false;
    384    }
    385 
    386    JS::Rooted<JS::Value> arg(aCx);
    387    for (uint32_t i = 0; i < aArguments.Length(); ++i) {
    388      arg = aArguments[i];
    389      if (NS_WARN_IF(
    390              !JS_DefineElement(aCx, arguments, i, arg, JSPROP_ENUMERATE))) {
    391        return false;
    392      }
    393    }
    394 
    395    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
    396    return WriteData(aCx, value);
    397  }
    398 
    399  // Helper method for Profile calls
    400  void ProcessProfileData(JSContext* aCx, Console::MethodName aMethodName,
    401                          const nsAString& aAction) {
    402    AssertIsOnMainThread();
    403 
    404    ConsoleCommon::ClearException ce(aCx);
    405 
    406    JS::Rooted<JS::Value> argumentsValue(aCx);
    407    bool ok = Read(aCx, &argumentsValue);
    408    mClonedData.mGlobal = nullptr;
    409 
    410    if (!ok) {
    411      return;
    412    }
    413 
    414    MOZ_ASSERT(argumentsValue.isObject());
    415    JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
    416    if (NS_WARN_IF(!argumentsObj)) {
    417      return;
    418    }
    419 
    420    uint32_t length;
    421    if (!JS::GetArrayLength(aCx, argumentsObj, &length)) {
    422      return;
    423    }
    424 
    425    Sequence<JS::Value> arguments;
    426 
    427    for (uint32_t i = 0; i < length; ++i) {
    428      JS::Rooted<JS::Value> value(aCx);
    429 
    430      if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
    431        return;
    432      }
    433 
    434      if (!arguments.AppendElement(value, fallible)) {
    435        return;
    436      }
    437    }
    438 
    439    Console::ProfileMethodMainthread(aCx, aAction, arguments);
    440  }
    441 
    442  bool WriteData(JSContext* aCx, JS::Handle<JS::Value> aValue) {
    443    // We use structuredClone to send the JSValue to the main-thread, in order
    444    // to store it into the Console API Service. The consumer will be the
    445    // console panel in the devtools and, because of this, we want to allow the
    446    // cloning of sharedArrayBuffers and WASM modules.
    447    JS::CloneDataPolicy cloneDataPolicy;
    448    cloneDataPolicy.allowIntraClusterClonableSharedObjects();
    449    cloneDataPolicy.allowSharedMemoryObjects();
    450 
    451    if (NS_WARN_IF(
    452            !Write(aCx, aValue, JS::UndefinedHandleValue, cloneDataPolicy))) {
    453      // Ignore the message.
    454      return false;
    455    }
    456 
    457    return true;
    458  }
    459 
    460  ConsoleStructuredCloneData mClonedData;
    461 };
    462 
    463 class ConsoleWorkletRunnable : public Runnable, public ConsoleRunnable {
    464 protected:
    465  explicit ConsoleWorkletRunnable(Console* aConsole)
    466      : Runnable("dom::console::ConsoleWorkletRunnable"),
    467        mConsoleData(aConsole->GetOrCreateMainThreadData()) {
    468    WorkletThread::AssertIsOnWorkletThread();
    469    nsCOMPtr<WorkletGlobalScope> global = do_QueryInterface(aConsole->mGlobal);
    470    MOZ_ASSERT(global);
    471    mWorkletImpl = global->Impl();
    472    MOZ_ASSERT(mWorkletImpl);
    473  }
    474 
    475  ~ConsoleWorkletRunnable() override = default;
    476 
    477 protected:
    478  RefPtr<MainThreadConsoleData> mConsoleData;
    479 
    480  RefPtr<WorkletImpl> mWorkletImpl;
    481 };
    482 
    483 // This runnable appends a CallData object into the Console queue running on
    484 // the main-thread.
    485 class ConsoleCallDataWorkletRunnable final : public ConsoleWorkletRunnable {
    486 public:
    487  static already_AddRefed<ConsoleCallDataWorkletRunnable> Create(
    488      JSContext* aCx, Console* aConsole, ConsoleCallData* aConsoleData,
    489      const Sequence<JS::Value>& aArguments) {
    490    WorkletThread::AssertIsOnWorkletThread();
    491 
    492    RefPtr<ConsoleCallDataWorkletRunnable> runnable =
    493        new ConsoleCallDataWorkletRunnable(aConsole, aConsoleData);
    494 
    495    if (!runnable->WriteArguments(aCx, aArguments)) {
    496      return nullptr;
    497    }
    498 
    499    return runnable.forget();
    500  }
    501 
    502 private:
    503  ConsoleCallDataWorkletRunnable(Console* aConsole, ConsoleCallData* aCallData)
    504      : ConsoleWorkletRunnable(aConsole), mCallData(aCallData) {
    505    WorkletThread::AssertIsOnWorkletThread();
    506    MOZ_ASSERT(aCallData);
    507    aCallData->AssertIsOnOwningThread();
    508 
    509    const WorkletLoadInfo& loadInfo = mWorkletImpl->LoadInfo();
    510    mCallData->SetIDs(loadInfo.OuterWindowID(), loadInfo.InnerWindowID());
    511  }
    512 
    513  ~ConsoleCallDataWorkletRunnable() override = default;
    514 
    515  NS_IMETHOD Run() override {
    516    AssertIsOnMainThread();
    517    AutoJSAPI jsapi;
    518    jsapi.Init();
    519    JSContext* cx = jsapi.cx();
    520 
    521    {
    522      MutexAutoLock lock(mCallData->mMutex);
    523 
    524      JSObject* sandbox =
    525          mConsoleData->GetOrCreateSandbox(cx, mWorkletImpl->Principal());
    526      JS::Rooted<JSObject*> global(cx, sandbox);
    527      if (NS_WARN_IF(!global)) {
    528        return NS_ERROR_FAILURE;
    529      }
    530 
    531      // The CreateSandbox call returns a proxy to the actual sandbox object. We
    532      // don't need a proxy here.
    533      global = js::UncheckedUnwrap(global);
    534      JSAutoRealm ar(cx, global);
    535 
    536      // We don't need to set a parent object in mCallData bacause there are not
    537      // DOM objects exposed to worklet.
    538 
    539      ProcessCallData(cx, mConsoleData, mCallData);
    540    }
    541 
    542    return NS_OK;
    543  }
    544 
    545  RefPtr<ConsoleCallData> mCallData;
    546 };
    547 
    548 class ConsoleWorkerRunnable : public WorkerProxyToMainThreadRunnable,
    549                              public ConsoleRunnable {
    550 public:
    551  explicit ConsoleWorkerRunnable(Console* aConsole)
    552      : mConsoleData(aConsole->GetOrCreateMainThreadData()) {}
    553 
    554  ~ConsoleWorkerRunnable() override = default;
    555 
    556  bool Dispatch(JSContext* aCx, const Sequence<JS::Value>& aArguments) {
    557    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    558    MOZ_ASSERT(workerPrivate);
    559 
    560    if (NS_WARN_IF(!WriteArguments(aCx, aArguments))) {
    561      RunBackOnWorkerThreadForCleanup(workerPrivate);
    562      return false;
    563    }
    564 
    565    if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch(workerPrivate))) {
    566      // RunBackOnWorkerThreadForCleanup() will be called by
    567      // WorkerProxyToMainThreadRunnable::Dispatch().
    568      return false;
    569    }
    570 
    571    return true;
    572  }
    573 
    574 protected:
    575  void RunOnMainThread(WorkerPrivate* aWorkerPrivate) override {
    576    MOZ_ASSERT(aWorkerPrivate);
    577    AssertIsOnMainThread();
    578 
    579    // Walk up to our containing page
    580    WorkerPrivate* wp = aWorkerPrivate->GetTopLevelWorker();
    581 
    582    nsCOMPtr<nsPIDOMWindowInner> window = wp->GetWindow();
    583    if (!window) {
    584      RunWindowless(aWorkerPrivate);
    585    } else {
    586      RunWithWindow(aWorkerPrivate, window);
    587    }
    588  }
    589 
    590  void RunWithWindow(WorkerPrivate* aWorkerPrivate,
    591                     nsPIDOMWindowInner* aWindow) {
    592    MOZ_ASSERT(aWorkerPrivate);
    593    AssertIsOnMainThread();
    594 
    595    AutoJSAPI jsapi;
    596    MOZ_ASSERT(aWindow);
    597 
    598    RefPtr<nsGlobalWindowInner> win = nsGlobalWindowInner::Cast(aWindow);
    599    if (NS_WARN_IF(!jsapi.Init(win))) {
    600      return;
    601    }
    602 
    603    nsCOMPtr<nsPIDOMWindowOuter> outerWindow = aWindow->GetOuterWindow();
    604    if (NS_WARN_IF(!outerWindow)) {
    605      return;
    606    }
    607 
    608    RunConsole(jsapi.cx(), aWindow->AsGlobal(), aWorkerPrivate, outerWindow,
    609               aWindow);
    610  }
    611 
    612  void RunWindowless(WorkerPrivate* aWorkerPrivate) {
    613    MOZ_ASSERT(aWorkerPrivate);
    614    AssertIsOnMainThread();
    615 
    616    WorkerPrivate* wp = aWorkerPrivate->GetTopLevelWorker();
    617 
    618    MOZ_ASSERT(!wp->GetWindow());
    619 
    620    AutoJSAPI jsapi;
    621    jsapi.Init();
    622 
    623    JSContext* cx = jsapi.cx();
    624 
    625    JS::Rooted<JSObject*> global(
    626        cx, mConsoleData->GetOrCreateSandbox(cx, wp->GetPrincipal()));
    627    if (NS_WARN_IF(!global)) {
    628      return;
    629    }
    630 
    631    // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
    632    // We don't need a proxy here.
    633    global = js::UncheckedUnwrap(global);
    634 
    635    JSAutoRealm ar(cx, global);
    636 
    637    nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(global);
    638    if (NS_WARN_IF(!globalObject)) {
    639      return;
    640    }
    641 
    642    RunConsole(cx, globalObject, aWorkerPrivate, nullptr, nullptr);
    643  }
    644 
    645  void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override {
    646    MOZ_ASSERT(aWorkerPrivate);
    647    aWorkerPrivate->AssertIsOnWorkerThread();
    648  }
    649 
    650  // This method is called in the main-thread.
    651  virtual void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal,
    652                          WorkerPrivate* aWorkerPrivate,
    653                          nsPIDOMWindowOuter* aOuterWindow,
    654                          nsPIDOMWindowInner* aInnerWindow) = 0;
    655 
    656  bool ForMessaging() const override { return true; }
    657 
    658  RefPtr<MainThreadConsoleData> mConsoleData;
    659 };
    660 
    661 // This runnable appends a CallData object into the Console queue running on
    662 // the main-thread.
    663 class ConsoleCallDataWorkerRunnable final : public ConsoleWorkerRunnable {
    664 public:
    665  ConsoleCallDataWorkerRunnable(Console* aConsole, ConsoleCallData* aCallData)
    666      : ConsoleWorkerRunnable(aConsole), mCallData(aCallData) {
    667    MOZ_ASSERT(aCallData);
    668    mCallData->AssertIsOnOwningThread();
    669  }
    670 
    671 private:
    672  ~ConsoleCallDataWorkerRunnable() override = default;
    673 
    674  void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal,
    675                  WorkerPrivate* aWorkerPrivate,
    676                  nsPIDOMWindowOuter* aOuterWindow,
    677                  nsPIDOMWindowInner* aInnerWindow) override {
    678    MOZ_ASSERT(aGlobal);
    679    MOZ_ASSERT(aWorkerPrivate);
    680    AssertIsOnMainThread();
    681 
    682    // The windows have to run in parallel.
    683    MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
    684 
    685    {
    686      MutexAutoLock lock(mCallData->mMutex);
    687      if (aOuterWindow) {
    688        mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
    689      } else {
    690        ConsoleStackEntry frame;
    691        if (mCallData->mTopStackFrame) {
    692          frame = *mCallData->mTopStackFrame;
    693        }
    694 
    695        nsCString id = frame.mFilename;
    696        nsString innerID;
    697        if (aWorkerPrivate->IsSharedWorker()) {
    698          innerID = u"SharedWorker"_ns;
    699        } else if (aWorkerPrivate->IsServiceWorker()) {
    700          innerID = u"ServiceWorker"_ns;
    701          // Use scope as ID so the webconsole can decide if the message should
    702          // show up per tab
    703          id = aWorkerPrivate->ServiceWorkerScope();
    704        } else {
    705          innerID = u"Worker"_ns;
    706        }
    707 
    708        mCallData->SetIDs(NS_ConvertUTF8toUTF16(id), innerID);
    709      }
    710 
    711      mClonedData.mGlobal = aGlobal;
    712 
    713      ProcessCallData(aCx, mConsoleData, mCallData);
    714 
    715      mClonedData.mGlobal = nullptr;
    716    }
    717  }
    718 
    719  RefPtr<ConsoleCallData> mCallData;
    720 };
    721 
    722 // This runnable calls ProfileMethod() on the console on the main-thread.
    723 class ConsoleProfileWorkletRunnable final : public ConsoleWorkletRunnable {
    724 public:
    725  static already_AddRefed<ConsoleProfileWorkletRunnable> Create(
    726      JSContext* aCx, Console* aConsole, Console::MethodName aName,
    727      const nsAString& aAction, const Sequence<JS::Value>& aArguments) {
    728    WorkletThread::AssertIsOnWorkletThread();
    729 
    730    RefPtr<ConsoleProfileWorkletRunnable> runnable =
    731        new ConsoleProfileWorkletRunnable(aConsole, aName, aAction);
    732 
    733    if (!runnable->WriteArguments(aCx, aArguments)) {
    734      return nullptr;
    735    }
    736 
    737    return runnable.forget();
    738  }
    739 
    740 private:
    741  ConsoleProfileWorkletRunnable(Console* aConsole, Console::MethodName aName,
    742                                const nsAString& aAction)
    743      : ConsoleWorkletRunnable(aConsole), mName(aName), mAction(aAction) {
    744    MOZ_ASSERT(aConsole);
    745  }
    746 
    747  NS_IMETHOD Run() override {
    748    AssertIsOnMainThread();
    749 
    750    AutoJSAPI jsapi;
    751    jsapi.Init();
    752    JSContext* cx = jsapi.cx();
    753 
    754    JSObject* sandbox =
    755        mConsoleData->GetOrCreateSandbox(cx, mWorkletImpl->Principal());
    756    JS::Rooted<JSObject*> global(cx, sandbox);
    757    if (NS_WARN_IF(!global)) {
    758      return NS_ERROR_FAILURE;
    759    }
    760 
    761    // The CreateSandbox call returns a proxy to the actual sandbox object. We
    762    // don't need a proxy here.
    763    global = js::UncheckedUnwrap(global);
    764 
    765    JSAutoRealm ar(cx, global);
    766 
    767    // We don't need to set a parent object in mCallData bacause there are not
    768    // DOM objects exposed to worklet.
    769    ProcessProfileData(cx, mName, mAction);
    770 
    771    return NS_OK;
    772  }
    773 
    774  Console::MethodName mName;
    775  nsString mAction;
    776 };
    777 
    778 // This runnable calls ProfileMethod() on the console on the main-thread.
    779 class ConsoleProfileWorkerRunnable final : public ConsoleWorkerRunnable {
    780 public:
    781  ConsoleProfileWorkerRunnable(Console* aConsole, Console::MethodName aName,
    782                               const nsAString& aAction)
    783      : ConsoleWorkerRunnable(aConsole), mName(aName), mAction(aAction) {
    784    MOZ_ASSERT(aConsole);
    785  }
    786 
    787 private:
    788  void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal,
    789                  WorkerPrivate* aWorkerPrivate,
    790                  nsPIDOMWindowOuter* aOuterWindow,
    791                  nsPIDOMWindowInner* aInnerWindow) override {
    792    AssertIsOnMainThread();
    793    MOZ_ASSERT(aGlobal);
    794 
    795    mClonedData.mGlobal = aGlobal;
    796 
    797    ProcessProfileData(aCx, mName, mAction);
    798 
    799    mClonedData.mGlobal = nullptr;
    800  }
    801 
    802  Console::MethodName mName;
    803  nsString mAction;
    804 };
    805 
    806 NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
    807 
    808 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
    809  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
    810  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsoleEventNotifier)
    811  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDumpFunction)
    812  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
    813  tmp->Shutdown();
    814  tmp->mArgumentStorage.clearAndFree();
    815 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    816 
    817 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
    818  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
    819  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsoleEventNotifier)
    820  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDumpFunction)
    821 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    822 
    823 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
    824  for (uint32_t i = 0; i < tmp->mArgumentStorage.length(); ++i) {
    825    tmp->mArgumentStorage[i].Trace(aCallbacks, aClosure);
    826  }
    827 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    828 
    829 NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
    830 NS_IMPL_CYCLE_COLLECTING_RELEASE(Console)
    831 
    832 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
    833  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    834  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
    835  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    836 NS_INTERFACE_MAP_END
    837 
    838 /* static */
    839 already_AddRefed<Console> Console::Create(JSContext* aCx,
    840                                          nsPIDOMWindowInner* aWindow,
    841                                          ErrorResult& aRv) {
    842  MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
    843 
    844  uint64_t outerWindowID = 0;
    845  uint64_t innerWindowID = 0;
    846 
    847  if (aWindow) {
    848    innerWindowID = aWindow->WindowID();
    849 
    850    // Without outerwindow any console message coming from this object will not
    851    // shown in the devtools webconsole. But this should be fine because
    852    // probably we are shutting down, or the window is CCed/GCed.
    853    nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
    854    if (outerWindow) {
    855      outerWindowID = outerWindow->WindowID();
    856    }
    857  }
    858 
    859  RefPtr<Console> console = new Console(aCx, nsGlobalWindowInner::Cast(aWindow),
    860                                        outerWindowID, innerWindowID);
    861  console->Initialize(aRv);
    862  if (NS_WARN_IF(aRv.Failed())) {
    863    return nullptr;
    864  }
    865 
    866  return console.forget();
    867 }
    868 
    869 /* static */
    870 already_AddRefed<Console> Console::CreateForWorklet(JSContext* aCx,
    871                                                    nsIGlobalObject* aGlobal,
    872                                                    uint64_t aOuterWindowID,
    873                                                    uint64_t aInnerWindowID,
    874                                                    ErrorResult& aRv) {
    875  WorkletThread::AssertIsOnWorkletThread();
    876 
    877  RefPtr<Console> console =
    878      new Console(aCx, aGlobal, aOuterWindowID, aInnerWindowID);
    879  console->Initialize(aRv);
    880  if (NS_WARN_IF(aRv.Failed())) {
    881    return nullptr;
    882  }
    883 
    884  return console.forget();
    885 }
    886 
    887 Console::Console(JSContext* aCx, nsIGlobalObject* aGlobal,
    888                 uint64_t aOuterWindowID, uint64_t aInnerWindowID,
    889                 const nsAString& aPrefix)
    890    : mGlobal(aGlobal),
    891      mOuterID(aOuterWindowID),
    892      mInnerID(aInnerWindowID),
    893      mDumpToStdout(false),
    894      mLogModule(nullptr),
    895      mPrefix(aPrefix),
    896      mChromeInstance(false),
    897      mCurrentLogLevel(WebIDLLogLevelToInteger(ConsoleLogLevel::All)),
    898      mStatus(eUnknown),
    899      mCreationTimeStamp(TimeStamp::Now()) {
    900  // Let's enable the dumping to stdout by default for chrome.
    901  if (nsContentUtils::ThreadsafeIsSystemCaller(aCx)) {
    902    mDumpToStdout = StaticPrefs::devtools_console_stdout_chrome();
    903  } else {
    904    mDumpToStdout = StaticPrefs::devtools_console_stdout_content();
    905  }
    906 
    907  // By default, the console uses "console" MOZ_LOG module name,
    908  // but ConsoleInstance may pass a custom prefix which we will use a module
    909  // name.
    910  mLogModule = mPrefix.IsEmpty()
    911                   ? LogModule::Get("console")
    912                   : LogModule::Get(NS_ConvertUTF16toUTF8(mPrefix).get());
    913 
    914  mozilla::HoldJSObjects(this);
    915 }
    916 
    917 Console::~Console() {
    918  AssertIsOnOwningThread();
    919  Shutdown();
    920  mozilla::DropJSObjects(this);
    921 }
    922 
    923 void Console::Initialize(ErrorResult& aRv) {
    924  AssertIsOnOwningThread();
    925  MOZ_ASSERT(mStatus == eUnknown);
    926 
    927  if (NS_IsMainThread()) {
    928    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    929    if (NS_WARN_IF(!obs)) {
    930      aRv.Throw(NS_ERROR_FAILURE);
    931      return;
    932    }
    933 
    934    if (mInnerID) {
    935      aRv = obs->AddObserver(this, "inner-window-destroyed", true);
    936      if (NS_WARN_IF(aRv.Failed())) {
    937        return;
    938      }
    939    }
    940 
    941    aRv = obs->AddObserver(this, "memory-pressure", true);
    942    if (NS_WARN_IF(aRv.Failed())) {
    943      return;
    944    }
    945  }
    946 
    947  mStatus = eInitialized;
    948 }
    949 
    950 void Console::Shutdown() {
    951  AssertIsOnOwningThread();
    952 
    953  if (mStatus == eUnknown || mStatus == eShuttingDown) {
    954    return;
    955  }
    956 
    957  if (NS_IsMainThread()) {
    958    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    959    if (obs) {
    960      obs->RemoveObserver(this, "inner-window-destroyed");
    961      obs->RemoveObserver(this, "memory-pressure");
    962    }
    963  }
    964 
    965  mTimerRegistry.Clear();
    966  mCounterRegistry.Clear();
    967 
    968  ClearStorage();
    969  mCallDataStorage.Clear();
    970 
    971  mStatus = eShuttingDown;
    972 }
    973 
    974 NS_IMETHODIMP
    975 Console::Observe(nsISupports* aSubject, const char* aTopic,
    976                 const char16_t* aData) {
    977  AssertIsOnMainThread();
    978 
    979  if (!strcmp(aTopic, "inner-window-destroyed")) {
    980    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
    981    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
    982 
    983    uint64_t innerID;
    984    nsresult rv = wrapper->GetData(&innerID);
    985    NS_ENSURE_SUCCESS(rv, rv);
    986 
    987    if (innerID == mInnerID) {
    988      Shutdown();
    989    }
    990 
    991    return NS_OK;
    992  }
    993 
    994  if (!strcmp(aTopic, "memory-pressure")) {
    995    ClearStorage();
    996    return NS_OK;
    997  }
    998 
    999  return NS_OK;
   1000 }
   1001 
   1002 void Console::ClearStorage() {
   1003  mCallDataStorage.Clear();
   1004  mArgumentStorage.clearAndFree();
   1005 }
   1006 
   1007 #define METHOD(name, string)                                          \
   1008  /* static */ void Console::name(const GlobalObject& aGlobal,        \
   1009                                  const Sequence<JS::Value>& aData) { \
   1010    Method(aGlobal, Method##name, nsLiteralString(string), aData);    \
   1011  }
   1012 
   1013 METHOD(Log, u"log")
   1014 METHOD(Info, u"info")
   1015 METHOD(Warn, u"warn")
   1016 METHOD(Error, u"error")
   1017 METHOD(Exception, u"exception")
   1018 METHOD(Debug, u"debug")
   1019 METHOD(Table, u"table")
   1020 METHOD(Trace, u"trace")
   1021 
   1022 // Displays an interactive listing of all the properties of an object.
   1023 METHOD(Dir, u"dir");
   1024 METHOD(Dirxml, u"dirxml");
   1025 
   1026 METHOD(Group, u"group")
   1027 METHOD(GroupCollapsed, u"groupCollapsed")
   1028 
   1029 #undef METHOD
   1030 
   1031 /* static */
   1032 void Console::Clear(const GlobalObject& aGlobal) {
   1033  const Sequence<JS::Value> data;
   1034  Method(aGlobal, MethodClear, u"clear"_ns, data);
   1035 }
   1036 
   1037 /* static */
   1038 void Console::GroupEnd(const GlobalObject& aGlobal) {
   1039  const Sequence<JS::Value> data;
   1040  Method(aGlobal, MethodGroupEnd, u"groupEnd"_ns, data);
   1041 }
   1042 
   1043 /* static */
   1044 void Console::Time(const GlobalObject& aGlobal, const nsAString& aLabel) {
   1045  StringMethod(aGlobal, aLabel, Sequence<JS::Value>(), MethodTime, u"time"_ns);
   1046 }
   1047 
   1048 /* static */
   1049 void Console::TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel) {
   1050  StringMethod(aGlobal, aLabel, Sequence<JS::Value>(), MethodTimeEnd,
   1051               u"timeEnd"_ns);
   1052 }
   1053 
   1054 /* static */
   1055 void Console::TimeLog(const GlobalObject& aGlobal, const nsAString& aLabel,
   1056                      const Sequence<JS::Value>& aData) {
   1057  StringMethod(aGlobal, aLabel, aData, MethodTimeLog, u"timeLog"_ns);
   1058 }
   1059 
   1060 /* static */
   1061 void Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
   1062                           const Sequence<JS::Value>& aData,
   1063                           MethodName aMethodName,
   1064                           const nsAString& aMethodString) {
   1065  RefPtr<Console> console = GetConsole(aGlobal);
   1066  if (!console) {
   1067    return;
   1068  }
   1069 
   1070  console->StringMethodInternal(aGlobal.Context(), aLabel, aData, aMethodName,
   1071                                aMethodString);
   1072 }
   1073 
   1074 void Console::StringMethodInternal(JSContext* aCx, const nsAString& aLabel,
   1075                                   const Sequence<JS::Value>& aData,
   1076                                   MethodName aMethodName,
   1077                                   const nsAString& aMethodString) {
   1078  ConsoleCommon::ClearException ce(aCx);
   1079 
   1080  Sequence<JS::Value> data;
   1081  SequenceRooter<JS::Value> rooter(aCx, &data);
   1082 
   1083  JS::Rooted<JS::Value> value(aCx);
   1084  if (!dom::ToJSValue(aCx, aLabel, &value)) {
   1085    return;
   1086  }
   1087 
   1088  if (!data.AppendElement(value, fallible)) {
   1089    return;
   1090  }
   1091 
   1092  for (uint32_t i = 0; i < aData.Length(); ++i) {
   1093    if (!data.AppendElement(aData[i], fallible)) {
   1094      return;
   1095    }
   1096  }
   1097 
   1098  MethodInternal(aCx, aMethodName, aMethodString, data);
   1099 }
   1100 
   1101 /* static */
   1102 void Console::TimeStamp(const GlobalObject& aGlobal,
   1103                        const JS::Handle<JS::Value> aData) {
   1104  JSContext* cx = aGlobal.Context();
   1105 
   1106  ConsoleCommon::ClearException ce(cx);
   1107 
   1108  Sequence<JS::Value> data;
   1109  SequenceRooter<JS::Value> rooter(cx, &data);
   1110 
   1111  if (aData.isString() && !data.AppendElement(aData, fallible)) {
   1112    return;
   1113  }
   1114 
   1115  Method(aGlobal, MethodTimeStamp, u"timeStamp"_ns, data);
   1116 }
   1117 
   1118 /* static */
   1119 void Console::Profile(const GlobalObject& aGlobal,
   1120                      const Sequence<JS::Value>& aData) {
   1121  ProfileMethod(aGlobal, MethodProfile, u"profile"_ns, aData);
   1122 }
   1123 
   1124 /* static */
   1125 void Console::ProfileEnd(const GlobalObject& aGlobal,
   1126                         const Sequence<JS::Value>& aData) {
   1127  ProfileMethod(aGlobal, MethodProfileEnd, u"profileEnd"_ns, aData);
   1128 }
   1129 
   1130 /* static */
   1131 void Console::ProfileMethod(const GlobalObject& aGlobal, MethodName aName,
   1132                            const nsAString& aAction,
   1133                            const Sequence<JS::Value>& aData) {
   1134  RefPtr<Console> console = GetConsole(aGlobal);
   1135  if (!console) {
   1136    return;
   1137  }
   1138 
   1139  JSContext* cx = aGlobal.Context();
   1140  console->ProfileMethodInternal(cx, aName, aAction, aData);
   1141 }
   1142 
   1143 void Console::ProfileMethodInternal(JSContext* aCx, MethodName aMethodName,
   1144                                    const nsAString& aAction,
   1145                                    const Sequence<JS::Value>& aData) {
   1146  if (!ShouldProceed(aMethodName)) {
   1147    return;
   1148  }
   1149 
   1150  MaybeExecuteDumpFunction(aCx, aMethodName, aAction, aData, nullptr,
   1151                           DOMHighResTimeStamp(0.0));
   1152 
   1153  if (WorkletThread::IsOnWorkletThread()) {
   1154    RefPtr<ConsoleProfileWorkletRunnable> runnable =
   1155        ConsoleProfileWorkletRunnable::Create(aCx, this, aMethodName, aAction,
   1156                                              aData);
   1157    if (!runnable) {
   1158      return;
   1159    }
   1160 
   1161    NS_DispatchToMainThread(runnable.forget());
   1162    return;
   1163  }
   1164 
   1165  if (!NS_IsMainThread()) {
   1166    // Here we are in a worker thread.
   1167    RefPtr<ConsoleProfileWorkerRunnable> runnable =
   1168        new ConsoleProfileWorkerRunnable(this, aMethodName, aAction);
   1169 
   1170    runnable->Dispatch(aCx, aData);
   1171    return;
   1172  }
   1173 
   1174  ProfileMethodMainthread(aCx, aAction, aData);
   1175 }
   1176 
   1177 // static
   1178 void Console::ProfileMethodMainthread(JSContext* aCx, const nsAString& aAction,
   1179                                      const Sequence<JS::Value>& aData) {
   1180  MOZ_ASSERT(NS_IsMainThread());
   1181  ConsoleCommon::ClearException ce(aCx);
   1182 
   1183  RootedDictionary<ConsoleProfileEvent> event(aCx);
   1184  event.mAction = aAction;
   1185  event.mChromeContext = nsContentUtils::ThreadsafeIsSystemCaller(aCx);
   1186 
   1187  event.mArguments.Construct();
   1188  Sequence<JS::Value>& sequence = event.mArguments.Value();
   1189 
   1190  for (uint32_t i = 0; i < aData.Length(); ++i) {
   1191    if (!sequence.AppendElement(aData[i], fallible)) {
   1192      return;
   1193    }
   1194  }
   1195 
   1196  JS::Rooted<JS::Value> eventValue(aCx);
   1197  if (!ToJSValue(aCx, event, &eventValue)) {
   1198    return;
   1199  }
   1200 
   1201  JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
   1202  MOZ_ASSERT(eventObj);
   1203 
   1204  if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
   1205                         JSPROP_ENUMERATE)) {
   1206    return;
   1207  }
   1208 
   1209  nsIXPConnect* xpc = nsContentUtils::XPConnect();
   1210  nsCOMPtr<nsISupports> wrapper;
   1211  const nsIID& iid = NS_GET_IID(nsISupports);
   1212 
   1213  if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
   1214    return;
   1215  }
   1216 
   1217  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1218  if (obs) {
   1219    obs->NotifyObservers(wrapper, "console-api-profiler", nullptr);
   1220  }
   1221 }
   1222 
   1223 /* static */
   1224 void Console::Assert(const GlobalObject& aGlobal, bool aCondition,
   1225                     const Sequence<JS::Value>& aData) {
   1226  if (!aCondition) {
   1227    Method(aGlobal, MethodAssert, u"assert"_ns, aData);
   1228  }
   1229 }
   1230 
   1231 /* static */
   1232 void Console::Count(const GlobalObject& aGlobal, const nsAString& aLabel) {
   1233  StringMethod(aGlobal, aLabel, Sequence<JS::Value>(), MethodCount,
   1234               u"count"_ns);
   1235 }
   1236 
   1237 /* static */
   1238 void Console::CountReset(const GlobalObject& aGlobal, const nsAString& aLabel) {
   1239  StringMethod(aGlobal, aLabel, Sequence<JS::Value>(), MethodCountReset,
   1240               u"countReset"_ns);
   1241 }
   1242 
   1243 namespace {
   1244 
   1245 void StackFrameToStackEntry(JSContext* aCx, nsIStackFrame* aStackFrame,
   1246                            ConsoleStackEntry& aStackEntry) {
   1247  MOZ_ASSERT(aStackFrame);
   1248 
   1249  aStackFrame->GetFilename(aCx, aStackEntry.mFilename);
   1250  aStackEntry.mSourceId = aStackFrame->GetSourceId(aCx);
   1251  aStackEntry.mLineNumber = aStackFrame->GetLineNumber(aCx);
   1252  aStackEntry.mColumnNumber = aStackFrame->GetColumnNumber(aCx);
   1253 
   1254  aStackFrame->GetName(aCx, aStackEntry.mFunctionName);
   1255 
   1256  nsString cause;
   1257  aStackFrame->GetAsyncCause(aCx, cause);
   1258  if (!cause.IsEmpty()) {
   1259    aStackEntry.mAsyncCause.Construct(cause);
   1260  }
   1261 }
   1262 
   1263 void ReifyStack(JSContext* aCx, nsIStackFrame* aStack,
   1264                nsTArray<ConsoleStackEntry>& aRefiedStack) {
   1265  nsCOMPtr<nsIStackFrame> stack(aStack);
   1266 
   1267  while (stack) {
   1268    ConsoleStackEntry& data = *aRefiedStack.AppendElement();
   1269    StackFrameToStackEntry(aCx, stack, data);
   1270 
   1271    nsCOMPtr<nsIStackFrame> caller = stack->GetCaller(aCx);
   1272 
   1273    if (!caller) {
   1274      caller = stack->GetAsyncCaller(aCx);
   1275    }
   1276    stack.swap(caller);
   1277  }
   1278 }
   1279 
   1280 }  // anonymous namespace
   1281 
   1282 // Queue a call to a console method. See the CALL_DELAY constant.
   1283 /* static */
   1284 void Console::Method(const GlobalObject& aGlobal, MethodName aMethodName,
   1285                     const nsAString& aMethodString,
   1286                     const Sequence<JS::Value>& aData) {
   1287  RefPtr<Console> console = GetConsole(aGlobal);
   1288  if (!console) {
   1289    return;
   1290  }
   1291 
   1292  console->MethodInternal(aGlobal.Context(), aMethodName, aMethodString, aData);
   1293 }
   1294 
   1295 void Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
   1296                             const nsAString& aMethodString,
   1297                             const Sequence<JS::Value>& aData) {
   1298  if (!ShouldProceed(aMethodName)) {
   1299    return;
   1300  }
   1301 
   1302  AssertIsOnOwningThread();
   1303 
   1304  ConsoleCommon::ClearException ce(aCx);
   1305 
   1306  RefPtr<ConsoleCallData> callData =
   1307      new ConsoleCallData(aMethodName, aMethodString, this);
   1308 
   1309  MutexAutoLock lock(callData->mMutex);
   1310 
   1311  if (!StoreCallData(aCx, callData, aData)) {
   1312    return;
   1313  }
   1314 
   1315  OriginAttributes oa;
   1316 
   1317  if (NS_IsMainThread()) {
   1318    if (mGlobal) {
   1319      // Save the principal's OriginAttributes in the console event data
   1320      // so that we will be able to filter messages by origin attributes.
   1321      nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mGlobal);
   1322      if (NS_WARN_IF(!sop)) {
   1323        return;
   1324      }
   1325 
   1326      nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   1327      if (NS_WARN_IF(!principal)) {
   1328        return;
   1329      }
   1330 
   1331      oa = principal->OriginAttributesRef();
   1332      callData->SetAddonId(principal);
   1333 
   1334 #ifdef DEBUG
   1335      if (!principal->IsSystemPrincipal()) {
   1336        nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mGlobal);
   1337        if (webNav) {
   1338          nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
   1339          MOZ_ASSERT(loadContext);
   1340 
   1341          bool pb;
   1342          if (NS_SUCCEEDED(loadContext->GetUsePrivateBrowsing(&pb))) {
   1343            MOZ_ASSERT(pb == oa.IsPrivateBrowsing());
   1344          }
   1345        }
   1346      }
   1347 #endif
   1348    }
   1349  } else if (WorkletThread::IsOnWorkletThread()) {
   1350    nsCOMPtr<WorkletGlobalScope> global = do_QueryInterface(mGlobal);
   1351    MOZ_ASSERT(global);
   1352    oa = global->Impl()->OriginAttributesRef();
   1353  } else {
   1354    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1355    MOZ_ASSERT(workerPrivate);
   1356    oa = workerPrivate->GetOriginAttributes();
   1357  }
   1358 
   1359  callData->SetOriginAttributes(oa);
   1360 
   1361  JS::StackCapture captureMode =
   1362      ShouldIncludeStackTrace(aMethodName)
   1363          ? JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH))
   1364          : JS::StackCapture(JS::FirstSubsumedFrame(aCx));
   1365  nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, std::move(captureMode));
   1366 
   1367  if (stack) {
   1368    callData->mTopStackFrame.emplace();
   1369    StackFrameToStackEntry(aCx, stack, *callData->mTopStackFrame);
   1370  }
   1371 
   1372  if (NS_IsMainThread()) {
   1373    callData->mStack = stack;
   1374  } else {
   1375    // nsIStackFrame is not threadsafe, so we need to snapshot it now,
   1376    // before we post our runnable to the main thread.
   1377    callData->mReifiedStack.emplace();
   1378    ReifyStack(aCx, stack, *callData->mReifiedStack);
   1379  }
   1380 
   1381  DOMHighResTimeStamp monotonicTimer = 0.0;
   1382 
   1383  // Monotonic timer for 'time', 'timeLog' and 'timeEnd'
   1384  if ((aMethodName == MethodTime || aMethodName == MethodTimeLog ||
   1385       aMethodName == MethodTimeEnd || aMethodName == MethodTimeStamp) &&
   1386      !MonotonicTimer(aCx, aMethodName, aData, &monotonicTimer)) {
   1387    return;
   1388  }
   1389 
   1390  if (aMethodName == MethodTime && !aData.IsEmpty()) {
   1391    callData->mStartTimerStatus =
   1392        StartTimer(aCx, aData[0], monotonicTimer, callData->mStartTimerLabel,
   1393                   &callData->mStartTimerValue);
   1394  }
   1395 
   1396  else if (aMethodName == MethodTimeEnd && !aData.IsEmpty()) {
   1397    callData->mLogTimerStatus =
   1398        LogTimer(aCx, aData[0], monotonicTimer, callData->mLogTimerLabel,
   1399                 &callData->mLogTimerDuration, true /* Cancel timer */);
   1400  }
   1401 
   1402  else if (aMethodName == MethodTimeLog && !aData.IsEmpty()) {
   1403    callData->mLogTimerStatus =
   1404        LogTimer(aCx, aData[0], monotonicTimer, callData->mLogTimerLabel,
   1405                 &callData->mLogTimerDuration, false /* Cancel timer */);
   1406  }
   1407 
   1408  else if (aMethodName == MethodCount) {
   1409    callData->mCountValue = IncreaseCounter(aCx, aData, callData->mCountLabel);
   1410    if (!callData->mCountValue) {
   1411      return;
   1412    }
   1413  }
   1414 
   1415  else if (aMethodName == MethodCountReset) {
   1416    callData->mCountValue = ResetCounter(aCx, aData, callData->mCountLabel);
   1417    if (callData->mCountLabel.IsEmpty()) {
   1418      return;
   1419    }
   1420  }
   1421 
   1422  // Before processing this CallData differently, it's time to call the dump
   1423  // function.
   1424  //
   1425  // Only log the stack trace for console.trace() and console.assert()
   1426  if (aMethodName == MethodTrace || aMethodName == MethodAssert) {
   1427    MaybeExecuteDumpFunction(aCx, aMethodName, aMethodString, aData, stack,
   1428                             monotonicTimer);
   1429  } else {
   1430    MaybeExecuteDumpFunction(aCx, aMethodName, aMethodString, aData, nullptr,
   1431                             monotonicTimer);
   1432  }
   1433 
   1434  if (NS_IsMainThread()) {
   1435    if (mInnerID) {
   1436      callData->SetIDs(mOuterID, mInnerID);
   1437    } else if (!mPassedInnerID.IsEmpty()) {
   1438      callData->SetIDs(u"jsm"_ns, mPassedInnerID);
   1439    } else {
   1440      nsAutoCString filename;
   1441      if (callData->mTopStackFrame.isSome()) {
   1442        filename = callData->mTopStackFrame->mFilename;
   1443      }
   1444      callData->SetIDs(u"jsm"_ns, NS_ConvertUTF8toUTF16(filename));
   1445    }
   1446 
   1447    GetOrCreateMainThreadData()->ProcessCallData(aCx, callData, aData);
   1448 
   1449    // Just because we don't want to expose
   1450    // retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
   1451    // cleanup the mCallDataStorage:
   1452    UnstoreCallData(callData);
   1453    return;
   1454  }
   1455 
   1456  if (WorkletThread::IsOnWorkletThread()) {
   1457    RefPtr<ConsoleCallDataWorkletRunnable> runnable =
   1458        ConsoleCallDataWorkletRunnable::Create(aCx, this, callData, aData);
   1459    if (!runnable) {
   1460      return;
   1461    }
   1462 
   1463    NS_DispatchToMainThread(runnable);
   1464    return;
   1465  }
   1466 
   1467  // We do this only in workers for now.
   1468  NotifyHandler(aCx, aData, callData);
   1469 
   1470  if (StaticPrefs::dom_worker_console_dispatch_events_to_main_thread()) {
   1471    RefPtr<ConsoleCallDataWorkerRunnable> runnable =
   1472        new ConsoleCallDataWorkerRunnable(this, callData);
   1473    (void)NS_WARN_IF(!runnable->Dispatch(aCx, aData));
   1474  }
   1475 }
   1476 
   1477 MainThreadConsoleData* Console::GetOrCreateMainThreadData() {
   1478  AssertIsOnOwningThread();
   1479 
   1480  if (!mMainThreadData) {
   1481    mMainThreadData = new MainThreadConsoleData();
   1482  }
   1483 
   1484  return mMainThreadData;
   1485 }
   1486 
   1487 // We store information to lazily compute the stack in the reserved slots of
   1488 // LazyStackGetter.  The first slot always stores a JS object: it's either the
   1489 // JS wrapper of the nsIStackFrame or the actual reified stack representation.
   1490 // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
   1491 // reified the stack yet, or an UndefinedValue() otherwise.
   1492 enum { SLOT_STACKOBJ, SLOT_RAW_STACK };
   1493 
   1494 bool LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
   1495  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
   1496  JS::Rooted<JSObject*> callee(aCx, &args.callee());
   1497 
   1498  JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
   1499  if (v.isUndefined()) {
   1500    // Already reified.
   1501    args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
   1502    return true;
   1503  }
   1504 
   1505  nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
   1506  nsTArray<ConsoleStackEntry> reifiedStack;
   1507  ReifyStack(aCx, stack, reifiedStack);
   1508 
   1509  JS::Rooted<JS::Value> stackVal(aCx);
   1510  if (NS_WARN_IF(!ToJSValue(aCx, reifiedStack, &stackVal))) {
   1511    return false;
   1512  }
   1513 
   1514  MOZ_ASSERT(stackVal.isObject());
   1515 
   1516  js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
   1517  js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
   1518 
   1519  args.rval().set(stackVal);
   1520  return true;
   1521 }
   1522 
   1523 void MainThreadConsoleData::ProcessCallData(
   1524    JSContext* aCx, ConsoleCallData* aData,
   1525    const Sequence<JS::Value>& aArguments) {
   1526  AssertIsOnMainThread();
   1527  MOZ_ASSERT(aData);
   1528  aData->mMutex.AssertCurrentThreadOwns();
   1529 
   1530  JS::Rooted<JS::Value> eventValue(aCx);
   1531 
   1532  // We want to create a console event object and pass it to our
   1533  // nsIConsoleAPIStorage implementation.  We want to define some accessor
   1534  // properties on this object, and those will need to keep an nsIStackFrame
   1535  // alive.  But nsIStackFrame cannot be wrapped in an untrusted scope.  And
   1536  // further, passing untrusted objects to system code is likely to run afoul of
   1537  // Object Xrays.  So we want to wrap in a system-principal scope here.  But
   1538  // which one?  We could cheat and try to get the underlying JSObject* of
   1539  // mStorage, but that's a bit fragile.  Instead, we just use the junk scope,
   1540  // with explicit permission from the XPConnect module owner.  If you're
   1541  // tempted to do that anywhere else, talk to said module owner first.
   1542 
   1543  // aCx and aArguments are in the same compartment.
   1544  JS::Rooted<JSObject*> targetScope(aCx, xpc::PrivilegedJunkScope());
   1545  if (NS_WARN_IF(!Console::PopulateConsoleNotificationInTheTargetScope(
   1546          aCx, aArguments, targetScope, &eventValue, aData, &mGroupStack))) {
   1547    return;
   1548  }
   1549 
   1550  if (!mStorage) {
   1551    mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
   1552  }
   1553 
   1554  if (!mStorage) {
   1555    NS_WARNING("Failed to get the ConsoleAPIStorage service.");
   1556    return;
   1557  }
   1558 
   1559  nsAutoString innerID;
   1560 
   1561  MOZ_ASSERT(aData->mIDType != ConsoleCallData::eUnknown);
   1562  if (aData->mIDType == ConsoleCallData::eString) {
   1563    innerID = aData->mInnerIDString;
   1564  } else {
   1565    MOZ_ASSERT(aData->mIDType == ConsoleCallData::eNumber);
   1566    innerID.AppendInt(aData->mInnerIDNumber);
   1567  }
   1568 
   1569  if (aData->mMethodName == Console::MethodClear) {
   1570    DebugOnly<nsresult> rv = mStorage->ClearEvents(innerID);
   1571    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ClearEvents failed");
   1572  }
   1573 
   1574  if (NS_FAILED(mStorage->RecordEvent(innerID, eventValue))) {
   1575    NS_WARNING("Failed to record a console event.");
   1576  }
   1577 }
   1578 
   1579 /* static */
   1580 bool Console::PopulateConsoleNotificationInTheTargetScope(
   1581    JSContext* aCx, const Sequence<JS::Value>& aArguments,
   1582    JS::Handle<JSObject*> aTargetScope,
   1583    JS::MutableHandle<JS::Value> aEventValue, ConsoleCallData* aData,
   1584    nsTArray<nsString>* aGroupStack) {
   1585  MOZ_ASSERT(aCx);
   1586  MOZ_ASSERT(aData);
   1587  MOZ_ASSERT(aTargetScope);
   1588  MOZ_ASSERT(JS_IsGlobalObject(aTargetScope));
   1589 
   1590  aData->mMutex.AssertCurrentThreadOwns();
   1591 
   1592  ConsoleStackEntry frame;
   1593  if (aData->mTopStackFrame) {
   1594    frame = *aData->mTopStackFrame;
   1595  }
   1596 
   1597  ConsoleCommon::ClearException ce(aCx);
   1598  RootedDictionary<ConsoleEvent> event(aCx);
   1599 
   1600  event.mAddonId = aData->mAddonId;
   1601 
   1602  event.mID.Construct();
   1603  event.mInnerID.Construct();
   1604 
   1605  event.mChromeContext = nsContentUtils::ThreadsafeIsSystemCaller(aCx);
   1606 
   1607  if (aData->mIDType == ConsoleCallData::eString) {
   1608    event.mID.Value().SetAsString() = aData->mOuterIDString;
   1609    event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
   1610  } else if (aData->mIDType == ConsoleCallData::eNumber) {
   1611    event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
   1612    event.mInnerID.Value().SetAsUnsignedLongLong() = aData->mInnerIDNumber;
   1613  } else {
   1614    // aData->mIDType can be eUnknown when we dispatch notifications via
   1615    // mConsoleEventNotifier.
   1616    event.mID.Value().SetAsUnsignedLongLong() = 0;
   1617    event.mInnerID.Value().SetAsUnsignedLongLong() = 0;
   1618  }
   1619 
   1620  event.mConsoleID = aData->mConsoleID;
   1621  event.mLevel = aData->mMethodString;
   1622  event.mFilename = frame.mFilename;
   1623  event.mPrefix = aData->mPrefix;
   1624 
   1625  nsCOMPtr<nsIURI> filenameURI;
   1626  nsAutoCString pass;
   1627  if (NS_IsMainThread() &&
   1628      NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI), frame.mFilename)) &&
   1629      NS_SUCCEEDED(filenameURI->GetPassword(pass)) && !pass.IsEmpty()) {
   1630    nsCOMPtr<nsISensitiveInfoHiddenURI> safeURI =
   1631        do_QueryInterface(filenameURI);
   1632    nsAutoCString spec;
   1633    if (safeURI && NS_SUCCEEDED(safeURI->GetSensitiveInfoHiddenSpec(spec))) {
   1634      event.mFilename = spec;
   1635    }
   1636  }
   1637 
   1638  event.mSourceId = frame.mSourceId;
   1639  event.mLineNumber = frame.mLineNumber;
   1640  event.mColumnNumber = frame.mColumnNumber;
   1641  event.mFunctionName = frame.mFunctionName;
   1642  event.mTimeStamp = aData->mMicroSecondTimeStamp / PR_USEC_PER_MSEC;
   1643  event.mMicroSecondTimeStamp = aData->mMicroSecondTimeStamp;
   1644  event.mPrivate = aData->mOriginAttributes.IsPrivateBrowsing();
   1645 
   1646  switch (aData->mMethodName) {
   1647    case MethodLog:
   1648    case MethodInfo:
   1649    case MethodWarn:
   1650    case MethodError:
   1651    case MethodException:
   1652    case MethodDebug:
   1653    case MethodAssert:
   1654    case MethodGroup:
   1655    case MethodGroupCollapsed:
   1656    case MethodTrace:
   1657      event.mArguments.Construct();
   1658      event.mStyles.Construct();
   1659      if (NS_WARN_IF(!ProcessArguments(aCx, aArguments,
   1660                                       event.mArguments.Value(),
   1661                                       event.mStyles.Value()))) {
   1662        return false;
   1663      }
   1664 
   1665      break;
   1666 
   1667    default:
   1668      event.mArguments.Construct();
   1669      if (NS_WARN_IF(
   1670              !event.mArguments.Value().AppendElements(aArguments, fallible))) {
   1671        return false;
   1672      }
   1673  }
   1674 
   1675  if (aData->mMethodName == MethodGroup ||
   1676      aData->mMethodName == MethodGroupCollapsed) {
   1677    ComposeAndStoreGroupName(aCx, event.mArguments.Value(), event.mGroupName,
   1678                             aGroupStack);
   1679  }
   1680 
   1681  else if (aData->mMethodName == MethodGroupEnd) {
   1682    if (!UnstoreGroupName(event.mGroupName, aGroupStack)) {
   1683      return false;
   1684    }
   1685  }
   1686 
   1687  else if (aData->mMethodName == MethodTime && !aArguments.IsEmpty()) {
   1688    event.mTimer = CreateStartTimerValue(aCx, aData->mStartTimerLabel,
   1689                                         aData->mStartTimerStatus);
   1690  }
   1691 
   1692  else if ((aData->mMethodName == MethodTimeEnd ||
   1693            aData->mMethodName == MethodTimeLog) &&
   1694           !aArguments.IsEmpty()) {
   1695    event.mTimer = CreateLogOrEndTimerValue(aCx, aData->mLogTimerLabel,
   1696                                            aData->mLogTimerDuration,
   1697                                            aData->mLogTimerStatus);
   1698  }
   1699 
   1700  else if (aData->mMethodName == MethodCount ||
   1701           aData->mMethodName == MethodCountReset) {
   1702    event.mCounter = CreateCounterOrResetCounterValue(aCx, aData->mCountLabel,
   1703                                                      aData->mCountValue);
   1704  }
   1705 
   1706  JSAutoRealm ar2(aCx, aTargetScope);
   1707 
   1708  if (NS_WARN_IF(!ToJSValue(aCx, event, aEventValue))) {
   1709    return false;
   1710  }
   1711 
   1712  JS::Rooted<JSObject*> eventObj(aCx, &aEventValue.toObject());
   1713  if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventObj,
   1714                                    JSPROP_ENUMERATE))) {
   1715    return false;
   1716  }
   1717 
   1718  if (ShouldIncludeStackTrace(aData->mMethodName)) {
   1719    // Now define the "stacktrace" property on eventObj.  There are two cases
   1720    // here.  Either we came from a worker and have a reified stack, or we want
   1721    // to define a getter that will lazily reify the stack.
   1722    if (aData->mReifiedStack) {
   1723      JS::Rooted<JS::Value> stacktrace(aCx);
   1724      if (NS_WARN_IF(!ToJSValue(aCx, *aData->mReifiedStack, &stacktrace)) ||
   1725          NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace", stacktrace,
   1726                                        JSPROP_ENUMERATE))) {
   1727        return false;
   1728      }
   1729    } else {
   1730      JSFunction* fun =
   1731          js::NewFunctionWithReserved(aCx, LazyStackGetter, 0, 0, "stacktrace");
   1732      if (NS_WARN_IF(!fun)) {
   1733        return false;
   1734      }
   1735 
   1736      JS::Rooted<JSObject*> funObj(aCx, JS_GetFunctionObject(fun));
   1737 
   1738      // We want to store our stack in the function and have it stay alive.  But
   1739      // we also need sane access to the C++ nsIStackFrame.  So store both a JS
   1740      // wrapper and the raw pointer: the former will keep the latter alive.
   1741      JS::Rooted<JS::Value> stackVal(aCx);
   1742      nsresult rv = nsContentUtils::WrapNative(aCx, aData->mStack, &stackVal);
   1743      if (NS_WARN_IF(NS_FAILED(rv))) {
   1744        return false;
   1745      }
   1746 
   1747      js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
   1748      js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
   1749                                    JS::PrivateValue(aData->mStack.get()));
   1750 
   1751      if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace", funObj,
   1752                                        nullptr, JSPROP_ENUMERATE))) {
   1753        return false;
   1754      }
   1755    }
   1756  }
   1757 
   1758  return true;
   1759 }
   1760 
   1761 namespace {
   1762 
   1763 // Helper method for ProcessArguments. Flushes output, if non-empty, to
   1764 // aSequence.
   1765 bool FlushOutput(JSContext* aCx, Sequence<JS::Value>& aSequence,
   1766                 nsString& aOutput) {
   1767  if (!aOutput.IsEmpty()) {
   1768    JS::Rooted<JSString*> str(
   1769        aCx, JS_NewUCStringCopyN(aCx, aOutput.get(), aOutput.Length()));
   1770    if (NS_WARN_IF(!str)) {
   1771      return false;
   1772    }
   1773 
   1774    if (NS_WARN_IF(!aSequence.AppendElement(JS::StringValue(str), fallible))) {
   1775      return false;
   1776    }
   1777 
   1778    aOutput.Truncate();
   1779  }
   1780 
   1781  return true;
   1782 }
   1783 
   1784 }  // namespace
   1785 
   1786 static void MakeFormatString(nsCString& aFormat, int32_t aInteger,
   1787                             int32_t aMantissa, char aCh) {
   1788  aFormat.Append('%');
   1789  if (aInteger >= 0) {
   1790    aFormat.AppendInt(aInteger);
   1791  }
   1792 
   1793  if (aMantissa >= 0) {
   1794    aFormat.Append('.');
   1795    aFormat.AppendInt(aMantissa);
   1796  }
   1797 
   1798  aFormat.Append(aCh);
   1799 }
   1800 
   1801 // If the first JS::Value of the array is a string, this method uses it to
   1802 // format a string. The supported sequences are:
   1803 //   %s    - string
   1804 //   %d,%i - integer
   1805 //   %f    - double
   1806 //   %o,%O - a JS object.
   1807 //   %c    - style string.
   1808 // The output is an array where any object is a separated item, the rest is
   1809 // unified in a format string.
   1810 // Example if the input is:
   1811 //   "string: %s, integer: %d, object: %o, double: %f", 's', 1, window, 0.9
   1812 // The output will be:
   1813 //   [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
   1814 //
   1815 // The aStyles array is populated with the style strings that the function
   1816 // finds based the format string. The index of the styles matches the indexes
   1817 // of elements that need the custom styling from aSequence. For elements with
   1818 // no custom styling the array is padded with null elements.
   1819 static bool ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData,
   1820                             Sequence<JS::Value>& aSequence,
   1821                             Sequence<nsString>& aStyles) {
   1822  // This method processes the arguments as format strings (%d, %i, %s...)
   1823  // only if the first element of them is a valid and not-empty string.
   1824 
   1825  if (aData.IsEmpty()) {
   1826    return true;
   1827  }
   1828 
   1829  if (aData.Length() == 1 || !aData[0].isString()) {
   1830    return aSequence.AppendElements(aData, fallible);
   1831  }
   1832 
   1833  JS::Rooted<JS::Value> format(aCx, aData[0]);
   1834  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, format));
   1835  if (NS_WARN_IF(!jsString)) {
   1836    return false;
   1837  }
   1838 
   1839  nsAutoJSString string;
   1840  if (NS_WARN_IF(!string.init(aCx, jsString))) {
   1841    return false;
   1842  }
   1843 
   1844  if (string.IsEmpty()) {
   1845    return aSequence.AppendElements(aData, fallible);
   1846  }
   1847 
   1848  nsString::const_iterator start, end;
   1849  string.BeginReading(start);
   1850  string.EndReading(end);
   1851 
   1852  nsString output;
   1853  uint32_t index = 1;
   1854 
   1855  while (start != end) {
   1856    if (*start != '%') {
   1857      output.Append(*start);
   1858      ++start;
   1859      continue;
   1860    }
   1861 
   1862    ++start;
   1863    if (start == end) {
   1864      output.Append('%');
   1865      break;
   1866    }
   1867 
   1868    if (*start == '%') {
   1869      output.Append(*start);
   1870      ++start;
   1871      continue;
   1872    }
   1873 
   1874    nsAutoString tmp;
   1875    tmp.Append('%');
   1876 
   1877    int32_t integer = -1;
   1878    int32_t mantissa = -1;
   1879 
   1880    // Let's parse %<number>.<number> for %d and %f
   1881    if (*start >= '0' && *start <= '9') {
   1882      integer = 0;
   1883 
   1884      do {
   1885        integer = integer * 10 + *start - '0';
   1886        tmp.Append(*start);
   1887        ++start;
   1888      } while (*start >= '0' && *start <= '9' && start != end);
   1889    }
   1890 
   1891    if (start == end) {
   1892      output.Append(tmp);
   1893      break;
   1894    }
   1895 
   1896    if (*start == '.') {
   1897      tmp.Append(*start);
   1898      ++start;
   1899 
   1900      if (start == end) {
   1901        output.Append(tmp);
   1902        break;
   1903      }
   1904 
   1905      // '.' must be followed by a number.
   1906      if (*start < '0' || *start > '9') {
   1907        output.Append(tmp);
   1908        continue;
   1909      }
   1910 
   1911      mantissa = 0;
   1912 
   1913      do {
   1914        mantissa = mantissa * 10 + *start - '0';
   1915        tmp.Append(*start);
   1916        ++start;
   1917      } while (*start >= '0' && *start <= '9' && start != end);
   1918 
   1919      if (start == end) {
   1920        output.Append(tmp);
   1921        break;
   1922      }
   1923    }
   1924 
   1925    char ch = *start;
   1926    tmp.Append(ch);
   1927    ++start;
   1928 
   1929    switch (ch) {
   1930      case 'o':
   1931      case 'O': {
   1932        if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
   1933          return false;
   1934        }
   1935 
   1936        JS::Rooted<JS::Value> v(aCx);
   1937        if (index < aData.Length()) {
   1938          v = aData[index++];
   1939        }
   1940 
   1941        if (NS_WARN_IF(!aSequence.AppendElement(v, fallible))) {
   1942          return false;
   1943        }
   1944 
   1945        break;
   1946      }
   1947 
   1948      case 'c': {
   1949        // If there isn't any output but there's already a style, then
   1950        // discard the previous style and use the next one instead.
   1951        if (output.IsEmpty() && !aStyles.IsEmpty()) {
   1952          aStyles.RemoveLastElement();
   1953        }
   1954 
   1955        if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
   1956          return false;
   1957        }
   1958 
   1959        if (index < aData.Length()) {
   1960          JS::Rooted<JS::Value> v(aCx, aData[index++]);
   1961          JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, v));
   1962          if (NS_WARN_IF(!jsString)) {
   1963            return false;
   1964          }
   1965 
   1966          int32_t diff = aSequence.Length() - aStyles.Length();
   1967          if (diff > 0) {
   1968            for (int32_t i = 0; i < diff; i++) {
   1969              if (NS_WARN_IF(!aStyles.AppendElement(VoidString(), fallible))) {
   1970                return false;
   1971              }
   1972            }
   1973          }
   1974 
   1975          nsAutoJSString string;
   1976          if (NS_WARN_IF(!string.init(aCx, jsString))) {
   1977            return false;
   1978          }
   1979 
   1980          if (NS_WARN_IF(!aStyles.AppendElement(string, fallible))) {
   1981            return false;
   1982          }
   1983        }
   1984        break;
   1985      }
   1986 
   1987      case 's':
   1988        if (index < aData.Length()) {
   1989          JS::Rooted<JS::Value> value(aCx, aData[index++]);
   1990          JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
   1991          if (NS_WARN_IF(!jsString)) {
   1992            return false;
   1993          }
   1994 
   1995          nsAutoJSString v;
   1996          if (NS_WARN_IF(!v.init(aCx, jsString))) {
   1997            return false;
   1998          }
   1999 
   2000          output.Append(v);
   2001        }
   2002        break;
   2003 
   2004      case 'd':
   2005      case 'i':
   2006        if (index < aData.Length()) {
   2007          JS::Rooted<JS::Value> value(aCx, aData[index++]);
   2008 
   2009          if (value.isBigInt()) {
   2010            JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
   2011            if (NS_WARN_IF(!jsString)) {
   2012              return false;
   2013            }
   2014 
   2015            nsAutoJSString v;
   2016            if (NS_WARN_IF(!v.init(aCx, jsString))) {
   2017              return false;
   2018            }
   2019            output.Append(v);
   2020            break;
   2021          }
   2022 
   2023          int32_t v;
   2024          if (NS_WARN_IF(!JS::ToInt32(aCx, value, &v))) {
   2025            return false;
   2026          }
   2027 
   2028          nsCString format;
   2029          MakeFormatString(format, integer, mantissa, 'd');
   2030          output.AppendPrintf(format.get(), v);
   2031        }
   2032        break;
   2033 
   2034      case 'f':
   2035        if (index < aData.Length()) {
   2036          JS::Rooted<JS::Value> value(aCx, aData[index++]);
   2037 
   2038          double v;
   2039          if (NS_WARN_IF(!JS::ToNumber(aCx, value, &v))) {
   2040            return false;
   2041          }
   2042 
   2043          // nspr returns "nan", but we want to expose it as "NaN"
   2044          if (std::isnan(v)) {
   2045            output.AppendFloat(v);
   2046          } else {
   2047            nsCString format;
   2048            MakeFormatString(format, integer, std::min(mantissa, 15), 'f');
   2049            output.AppendPrintf(format.get(), v);
   2050          }
   2051        }
   2052        break;
   2053 
   2054      default:
   2055        output.Append(tmp);
   2056        break;
   2057    }
   2058  }
   2059 
   2060  if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
   2061    return false;
   2062  }
   2063 
   2064  // Discard trailing style element if there is no output to apply it to.
   2065  if (aStyles.Length() > aSequence.Length()) {
   2066    aStyles.TruncateLength(aSequence.Length());
   2067  }
   2068 
   2069  // The rest of the array, if unused by the format string.
   2070  for (; index < aData.Length(); ++index) {
   2071    if (NS_WARN_IF(!aSequence.AppendElement(aData[index], fallible))) {
   2072      return false;
   2073    }
   2074  }
   2075 
   2076  return true;
   2077 }
   2078 
   2079 // Stringify and Concat all the JS::Value in a single string using ' ' as
   2080 // separator. The new group name will be stored in aGroupStack array.
   2081 static void ComposeAndStoreGroupName(JSContext* aCx,
   2082                                     const Sequence<JS::Value>& aData,
   2083                                     nsAString& aName,
   2084                                     nsTArray<nsString>* aGroupStack) {
   2085  StringJoinAppend(
   2086      aName, u" "_ns, aData, [aCx](nsAString& dest, const JS::Value& valueRef) {
   2087        JS::Rooted<JS::Value> value(aCx, valueRef);
   2088        JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
   2089        if (!jsString) {
   2090          return;
   2091        }
   2092 
   2093        nsAutoJSString string;
   2094        if (!string.init(aCx, jsString)) {
   2095          return;
   2096        }
   2097 
   2098        dest.Append(string);
   2099      });
   2100 
   2101  aGroupStack->AppendElement(aName);
   2102 }
   2103 
   2104 // Remove the last group name and return that name. It returns false if
   2105 // aGroupStack is empty.
   2106 static bool UnstoreGroupName(nsAString& aName,
   2107                             nsTArray<nsString>* aGroupStack) {
   2108  if (aGroupStack->IsEmpty()) {
   2109    return false;
   2110  }
   2111 
   2112  aName = aGroupStack->PopLastElement();
   2113  return true;
   2114 }
   2115 
   2116 Console::TimerStatus Console::StartTimer(JSContext* aCx, const JS::Value& aName,
   2117                                         DOMHighResTimeStamp aTimestamp,
   2118                                         nsAString& aTimerLabel,
   2119                                         DOMHighResTimeStamp* aTimerValue) {
   2120  AssertIsOnOwningThread();
   2121  MOZ_ASSERT(aTimerValue);
   2122 
   2123  *aTimerValue = 0;
   2124 
   2125  if (NS_WARN_IF(mTimerRegistry.Count() >= MAX_PAGE_TIMERS)) {
   2126    return eTimerMaxReached;
   2127  }
   2128 
   2129  JS::Rooted<JS::Value> name(aCx, aName);
   2130  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
   2131  if (NS_WARN_IF(!jsString)) {
   2132    return eTimerJSException;
   2133  }
   2134 
   2135  nsAutoJSString label;
   2136  if (NS_WARN_IF(!label.init(aCx, jsString))) {
   2137    return eTimerJSException;
   2138  }
   2139 
   2140  aTimerLabel = label;
   2141 
   2142  if (mTimerRegistry.WithEntryHandle(label, [&](auto&& entry) {
   2143        if (entry) {
   2144          return true;
   2145        }
   2146        entry.Insert(aTimestamp);
   2147        return false;
   2148      })) {
   2149    return eTimerAlreadyExists;
   2150  }
   2151 
   2152  *aTimerValue = aTimestamp;
   2153  return eTimerDone;
   2154 }
   2155 
   2156 /* static */
   2157 JS::Value Console::CreateStartTimerValue(JSContext* aCx,
   2158                                         const nsAString& aTimerLabel,
   2159                                         TimerStatus aTimerStatus) {
   2160  MOZ_ASSERT(aTimerStatus != eTimerUnknown);
   2161 
   2162  if (aTimerStatus != eTimerDone) {
   2163    return CreateTimerError(aCx, aTimerLabel, aTimerStatus);
   2164  }
   2165 
   2166  RootedDictionary<ConsoleTimerStart> timer(aCx);
   2167 
   2168  timer.mName = aTimerLabel;
   2169 
   2170  JS::Rooted<JS::Value> value(aCx);
   2171  if (!ToJSValue(aCx, timer, &value)) {
   2172    return JS::UndefinedValue();
   2173  }
   2174 
   2175  return value;
   2176 }
   2177 
   2178 Console::TimerStatus Console::LogTimer(JSContext* aCx, const JS::Value& aName,
   2179                                       DOMHighResTimeStamp aTimestamp,
   2180                                       nsAString& aTimerLabel,
   2181                                       double* aTimerDuration,
   2182                                       bool aCancelTimer) {
   2183  AssertIsOnOwningThread();
   2184  MOZ_ASSERT(aTimerDuration);
   2185 
   2186  *aTimerDuration = 0;
   2187 
   2188  JS::Rooted<JS::Value> name(aCx, aName);
   2189  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
   2190  if (NS_WARN_IF(!jsString)) {
   2191    return eTimerJSException;
   2192  }
   2193 
   2194  nsAutoJSString key;
   2195  if (NS_WARN_IF(!key.init(aCx, jsString))) {
   2196    return eTimerJSException;
   2197  }
   2198 
   2199  aTimerLabel = key;
   2200 
   2201  DOMHighResTimeStamp value = 0;
   2202 
   2203  if (aCancelTimer) {
   2204    if (!mTimerRegistry.Remove(key, &value)) {
   2205      NS_WARNING("mTimerRegistry entry not found");
   2206      return eTimerDoesntExist;
   2207    }
   2208  } else {
   2209    if (!mTimerRegistry.Get(key, &value)) {
   2210      NS_WARNING("mTimerRegistry entry not found");
   2211      return eTimerDoesntExist;
   2212    }
   2213  }
   2214 
   2215  *aTimerDuration = aTimestamp - value;
   2216  return eTimerDone;
   2217 }
   2218 
   2219 /* static */
   2220 JS::Value Console::CreateLogOrEndTimerValue(JSContext* aCx,
   2221                                            const nsAString& aLabel,
   2222                                            double aDuration,
   2223                                            TimerStatus aStatus) {
   2224  if (aStatus != eTimerDone) {
   2225    return CreateTimerError(aCx, aLabel, aStatus);
   2226  }
   2227 
   2228  RootedDictionary<ConsoleTimerLogOrEnd> timer(aCx);
   2229  timer.mName = aLabel;
   2230  timer.mDuration = aDuration;
   2231 
   2232  JS::Rooted<JS::Value> value(aCx);
   2233  if (!ToJSValue(aCx, timer, &value)) {
   2234    return JS::UndefinedValue();
   2235  }
   2236 
   2237  return value;
   2238 }
   2239 
   2240 /* static */
   2241 JS::Value Console::CreateTimerError(JSContext* aCx, const nsAString& aLabel,
   2242                                    TimerStatus aStatus) {
   2243  MOZ_ASSERT(aStatus != eTimerUnknown && aStatus != eTimerDone);
   2244 
   2245  RootedDictionary<ConsoleTimerError> error(aCx);
   2246 
   2247  error.mName = aLabel;
   2248 
   2249  switch (aStatus) {
   2250    case eTimerAlreadyExists:
   2251      error.mError.AssignLiteral("timerAlreadyExists");
   2252      break;
   2253 
   2254    case eTimerDoesntExist:
   2255      error.mError.AssignLiteral("timerDoesntExist");
   2256      break;
   2257 
   2258    case eTimerJSException:
   2259      error.mError.AssignLiteral("timerJSError");
   2260      break;
   2261 
   2262    case eTimerMaxReached:
   2263      error.mError.AssignLiteral("maxTimersExceeded");
   2264      break;
   2265 
   2266    default:
   2267      MOZ_CRASH("Unsupported status");
   2268      break;
   2269  }
   2270 
   2271  JS::Rooted<JS::Value> value(aCx);
   2272  if (!ToJSValue(aCx, error, &value)) {
   2273    return JS::UndefinedValue();
   2274  }
   2275 
   2276  return value;
   2277 }
   2278 
   2279 uint32_t Console::IncreaseCounter(JSContext* aCx,
   2280                                  const Sequence<JS::Value>& aArguments,
   2281                                  nsAString& aCountLabel) {
   2282  AssertIsOnOwningThread();
   2283 
   2284  ConsoleCommon::ClearException ce(aCx);
   2285 
   2286  MOZ_ASSERT(!aArguments.IsEmpty());
   2287 
   2288  JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
   2289  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
   2290  if (!jsString) {
   2291    return 0;  // We cannot continue.
   2292  }
   2293 
   2294  nsAutoJSString string;
   2295  if (!string.init(aCx, jsString)) {
   2296    return 0;  // We cannot continue.
   2297  }
   2298 
   2299  aCountLabel = string;
   2300 
   2301  const bool maxCountersReached = mCounterRegistry.Count() >= MAX_PAGE_COUNTERS;
   2302  return mCounterRegistry.WithEntryHandle(
   2303      aCountLabel, [maxCountersReached](auto&& entry) -> uint32_t {
   2304        if (entry) {
   2305          ++entry.Data();
   2306        } else {
   2307          if (maxCountersReached) {
   2308            return MAX_PAGE_COUNTERS;
   2309          }
   2310          entry.Insert(1);
   2311        }
   2312        return entry.Data();
   2313      });
   2314 }
   2315 
   2316 uint32_t Console::ResetCounter(JSContext* aCx,
   2317                               const Sequence<JS::Value>& aArguments,
   2318                               nsAString& aCountLabel) {
   2319  AssertIsOnOwningThread();
   2320 
   2321  ConsoleCommon::ClearException ce(aCx);
   2322 
   2323  MOZ_ASSERT(!aArguments.IsEmpty());
   2324 
   2325  JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
   2326  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
   2327  if (!jsString) {
   2328    return 0;  // We cannot continue.
   2329  }
   2330 
   2331  nsAutoJSString string;
   2332  if (!string.init(aCx, jsString)) {
   2333    return 0;  // We cannot continue.
   2334  }
   2335 
   2336  aCountLabel = string;
   2337 
   2338  if (mCounterRegistry.Remove(aCountLabel)) {
   2339    return 0;
   2340  }
   2341 
   2342  // Let's return something different than 0 if the key doesn't exist.
   2343  return MAX_PAGE_COUNTERS;
   2344 }
   2345 
   2346 // This method generates a ConsoleCounter dictionary as JS::Value. If
   2347 // aCountValue is == MAX_PAGE_COUNTERS it generates a ConsoleCounterError
   2348 // instead. See IncreaseCounter.
   2349 // * aCx - this is the context that will root the returned value.
   2350 // * aCountLabel - this label must be what IncreaseCounter received as
   2351 //                 aTimerLabel.
   2352 // * aCountValue - the return value of IncreaseCounter.
   2353 static JS::Value CreateCounterOrResetCounterValue(JSContext* aCx,
   2354                                                  const nsAString& aCountLabel,
   2355                                                  uint32_t aCountValue) {
   2356  ConsoleCommon::ClearException ce(aCx);
   2357 
   2358  if (aCountValue == MAX_PAGE_COUNTERS) {
   2359    RootedDictionary<ConsoleCounterError> error(aCx);
   2360    error.mLabel = aCountLabel;
   2361    error.mError.AssignLiteral("counterDoesntExist");
   2362 
   2363    JS::Rooted<JS::Value> value(aCx);
   2364    if (!ToJSValue(aCx, error, &value)) {
   2365      return JS::UndefinedValue();
   2366    }
   2367 
   2368    return value;
   2369  }
   2370 
   2371  RootedDictionary<ConsoleCounter> data(aCx);
   2372  data.mLabel = aCountLabel;
   2373  data.mCount = aCountValue;
   2374 
   2375  JS::Rooted<JS::Value> value(aCx);
   2376  if (!ToJSValue(aCx, data, &value)) {
   2377    return JS::UndefinedValue();
   2378  }
   2379 
   2380  return value;
   2381 }
   2382 
   2383 /* static */
   2384 bool Console::ShouldIncludeStackTrace(MethodName aMethodName) {
   2385  switch (aMethodName) {
   2386    case MethodError:
   2387    case MethodException:
   2388    case MethodAssert:
   2389    case MethodTrace:
   2390      return true;
   2391    default:
   2392      return false;
   2393  }
   2394 }
   2395 
   2396 JSObject* MainThreadConsoleData::GetOrCreateSandbox(JSContext* aCx,
   2397                                                    nsIPrincipal* aPrincipal) {
   2398  AssertIsOnMainThread();
   2399 
   2400  if (!mSandbox) {
   2401    nsIXPConnect* xpc = nsContentUtils::XPConnect();
   2402    MOZ_ASSERT(xpc, "This should never be null!");
   2403 
   2404    JS::Rooted<JSObject*> sandbox(aCx);
   2405    nsresult rv = xpc->CreateSandbox(aCx, aPrincipal, sandbox.address());
   2406    if (NS_WARN_IF(NS_FAILED(rv))) {
   2407      return nullptr;
   2408    }
   2409 
   2410    mSandbox = new JSObjectHolder(aCx, sandbox);
   2411  }
   2412 
   2413  return mSandbox->GetJSObject();
   2414 }
   2415 
   2416 bool Console::StoreCallData(JSContext* aCx, ConsoleCallData* aCallData,
   2417                            const Sequence<JS::Value>& aArguments) {
   2418  AssertIsOnOwningThread();
   2419 
   2420  if (NS_WARN_IF(!mArgumentStorage.growBy(1))) {
   2421    return false;
   2422  }
   2423  if (!mArgumentStorage.end()[-1].Initialize(aCx, aArguments)) {
   2424    mArgumentStorage.shrinkBy(1);
   2425    return false;
   2426  }
   2427 
   2428  MOZ_ASSERT(aCallData);
   2429  MOZ_ASSERT(!mCallDataStorage.Contains(aCallData));
   2430 
   2431  mCallDataStorage.AppendElement(aCallData);
   2432 
   2433  MOZ_ASSERT(mCallDataStorage.Length() == mArgumentStorage.length());
   2434 
   2435  if (mCallDataStorage.Length() > STORAGE_MAX_EVENTS) {
   2436    mCallDataStorage.RemoveElementAt(0);
   2437    mArgumentStorage.erase(&mArgumentStorage[0]);
   2438  }
   2439  return true;
   2440 }
   2441 
   2442 void Console::UnstoreCallData(ConsoleCallData* aCallData) {
   2443  AssertIsOnOwningThread();
   2444 
   2445  MOZ_ASSERT(aCallData);
   2446  MOZ_ASSERT(mCallDataStorage.Length() == mArgumentStorage.length());
   2447 
   2448  size_t index = mCallDataStorage.IndexOf(aCallData);
   2449  // It can be that mCallDataStorage has been already cleaned in case the
   2450  // processing of the argument of some Console methods triggers the
   2451  // window.close().
   2452  if (index == mCallDataStorage.NoIndex) {
   2453    return;
   2454  }
   2455 
   2456  mCallDataStorage.RemoveElementAt(index);
   2457  mArgumentStorage.erase(&mArgumentStorage[index]);
   2458 }
   2459 
   2460 void Console::NotifyHandler(JSContext* aCx,
   2461                            const Sequence<JS::Value>& aArguments,
   2462                            ConsoleCallData* aCallData) {
   2463  AssertIsOnOwningThread();
   2464  MOZ_ASSERT(!NS_IsMainThread());
   2465  MOZ_ASSERT(aCallData);
   2466 
   2467  if (!mConsoleEventNotifier) {
   2468    return;
   2469  }
   2470 
   2471  JS::Rooted<JS::Value> value(aCx);
   2472 
   2473  JS::Rooted<JSObject*> callableGlobal(
   2474      aCx, mConsoleEventNotifier->CallbackGlobalOrNull());
   2475  if (NS_WARN_IF(!callableGlobal)) {
   2476    return;
   2477  }
   2478 
   2479  // aCx and aArguments are in the same compartment because this method is
   2480  // called directly when a Console.something() runs.
   2481  // mConsoleEventNotifier->CallbackGlobal() is the scope where value will be
   2482  // sent to.
   2483  if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
   2484          aCx, aArguments, callableGlobal, &value, aCallData, &mGroupStack))) {
   2485    return;
   2486  }
   2487 
   2488  JS::Rooted<JS::Value> ignored(aCx);
   2489  RefPtr<AnyCallback> notifier(mConsoleEventNotifier);
   2490  notifier->Call(value, &ignored);
   2491 }
   2492 
   2493 void Console::RetrieveConsoleEvents(JSContext* aCx,
   2494                                    nsTArray<JS::Value>& aEvents,
   2495                                    ErrorResult& aRv) {
   2496  AssertIsOnOwningThread();
   2497 
   2498  // We don't want to expose this functionality to main-thread yet.
   2499  MOZ_ASSERT(!NS_IsMainThread());
   2500 
   2501  JS::Rooted<JSObject*> targetScope(aCx, JS::CurrentGlobalOrNull(aCx));
   2502 
   2503  for (uint32_t i = 0; i < mArgumentStorage.length(); ++i) {
   2504    JS::Rooted<JS::Value> value(aCx);
   2505 
   2506    JS::Rooted<JSObject*> sequenceScope(aCx, mArgumentStorage[i].Global());
   2507    JSAutoRealm ar(aCx, sequenceScope);
   2508 
   2509    Sequence<JS::Value> sequence;
   2510    SequenceRooter<JS::Value> arguments(aCx, &sequence);
   2511 
   2512    if (!mArgumentStorage[i].PopulateArgumentsSequence(sequence)) {
   2513      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   2514      return;
   2515    }
   2516 
   2517    // Here we have aCx and sequence in the same compartment.
   2518    // targetScope is the destination scope and value will be populated in its
   2519    // compartment.
   2520    {
   2521      MutexAutoLock lock(mCallDataStorage[i]->mMutex);
   2522      if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
   2523              aCx, sequence, targetScope, &value, mCallDataStorage[i],
   2524              &mGroupStack))) {
   2525        aRv.Throw(NS_ERROR_FAILURE);
   2526        return;
   2527      }
   2528    }
   2529 
   2530    aEvents.AppendElement(value);
   2531  }
   2532 }
   2533 
   2534 void Console::SetConsoleEventHandler(AnyCallback* aHandler) {
   2535  AssertIsOnOwningThread();
   2536 
   2537  // We don't want to expose this functionality to main-thread yet.
   2538  MOZ_ASSERT(!NS_IsMainThread());
   2539 
   2540  mConsoleEventNotifier = aHandler;
   2541 }
   2542 
   2543 void Console::AssertIsOnOwningThread() const {
   2544  NS_ASSERT_OWNINGTHREAD(Console);
   2545 }
   2546 
   2547 bool Console::IsShuttingDown() const {
   2548  MOZ_ASSERT(mStatus != eUnknown);
   2549  return mStatus == eShuttingDown;
   2550 }
   2551 
   2552 /* static */
   2553 already_AddRefed<Console> Console::GetConsole(const GlobalObject& aGlobal) {
   2554  ErrorResult rv;
   2555  RefPtr<Console> console = GetConsoleInternal(aGlobal, rv);
   2556  if (NS_WARN_IF(rv.Failed()) || !console) {
   2557    rv.SuppressException();
   2558    return nullptr;
   2559  }
   2560 
   2561  console->AssertIsOnOwningThread();
   2562 
   2563  if (console->IsShuttingDown()) {
   2564    return nullptr;
   2565  }
   2566 
   2567  return console.forget();
   2568 }
   2569 
   2570 /* static */
   2571 already_AddRefed<Console> Console::GetConsoleInternal(
   2572    const GlobalObject& aGlobal, ErrorResult& aRv) {
   2573  // Window
   2574  if (NS_IsMainThread()) {
   2575    nsCOMPtr<nsPIDOMWindowInner> innerWindow =
   2576        do_QueryInterface(aGlobal.GetAsSupports());
   2577 
   2578    // we are probably running a chrome script.
   2579    if (!innerWindow) {
   2580      RefPtr<Console> console = new Console(aGlobal.Context(), nullptr, 0, 0);
   2581      console->Initialize(aRv);
   2582      if (NS_WARN_IF(aRv.Failed())) {
   2583        return nullptr;
   2584      }
   2585 
   2586      return console.forget();
   2587    }
   2588 
   2589    nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(innerWindow);
   2590    return window->GetConsole(aGlobal.Context(), aRv);
   2591  }
   2592 
   2593  // Worklet
   2594  nsCOMPtr<WorkletGlobalScope> workletScope =
   2595      do_QueryInterface(aGlobal.GetAsSupports());
   2596  if (workletScope) {
   2597    WorkletThread::AssertIsOnWorkletThread();
   2598    return workletScope->GetConsole(aGlobal.Context(), aRv);
   2599  }
   2600 
   2601  // Workers
   2602  MOZ_ASSERT(!NS_IsMainThread());
   2603 
   2604  JSContext* cx = aGlobal.Context();
   2605  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
   2606  MOZ_ASSERT(workerPrivate);
   2607 
   2608  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
   2609  if (NS_WARN_IF(!global)) {
   2610    return nullptr;
   2611  }
   2612 
   2613  WorkerGlobalScope* scope = workerPrivate->GlobalScope();
   2614  MOZ_ASSERT(scope);
   2615 
   2616  // Normal worker scope.
   2617  if (scope == global) {
   2618    return scope->GetConsole(aRv);
   2619  }
   2620 
   2621  // Debugger worker scope
   2622 
   2623  WorkerDebuggerGlobalScope* debuggerScope =
   2624      workerPrivate->DebuggerGlobalScope();
   2625  MOZ_ASSERT(debuggerScope);
   2626  MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
   2627 
   2628  return debuggerScope->GetConsole(aRv);
   2629 }
   2630 
   2631 bool Console::MonotonicTimer(JSContext* aCx, MethodName aMethodName,
   2632                             const Sequence<JS::Value>& aData,
   2633                             DOMHighResTimeStamp* aTimeStamp) {
   2634  if (nsCOMPtr<nsPIDOMWindowInner> innerWindow = do_QueryInterface(mGlobal)) {
   2635    nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(innerWindow);
   2636    MOZ_ASSERT(win);
   2637 
   2638    RefPtr<Performance> performance = win->GetPerformance();
   2639    if (!performance) {
   2640      return false;
   2641    }
   2642 
   2643    *aTimeStamp = performance->Now();
   2644    return true;
   2645  }
   2646 
   2647  if (NS_IsMainThread()) {
   2648    *aTimeStamp = (TimeStamp::Now() - mCreationTimeStamp).ToMilliseconds();
   2649    return true;
   2650  }
   2651 
   2652  if (nsCOMPtr<WorkletGlobalScope> workletGlobal = do_QueryInterface(mGlobal)) {
   2653    *aTimeStamp = workletGlobal->TimeStampToDOMHighRes(TimeStamp::Now());
   2654    return true;
   2655  }
   2656 
   2657  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   2658  MOZ_ASSERT(workerPrivate);
   2659 
   2660  *aTimeStamp = workerPrivate->TimeStampToDOMHighRes(TimeStamp::Now());
   2661  return true;
   2662 }
   2663 
   2664 /* static */
   2665 already_AddRefed<ConsoleInstance> Console::CreateInstance(
   2666    const GlobalObject& aGlobal, const ConsoleInstanceOptions& aOptions) {
   2667  RefPtr<ConsoleInstance> console =
   2668      new ConsoleInstance(aGlobal.Context(), aOptions);
   2669  return console.forget();
   2670 }
   2671 
   2672 void Console::StringifyElement(Element* aElement, nsAString& aOut) {
   2673  aOut.AppendLiteral("<");
   2674  aOut.Append(aElement->LocalName());
   2675  uint32_t attrCount = aElement->GetAttrCount();
   2676  nsAutoString idAttr;
   2677  nsAutoString classAttr;
   2678  nsAutoString nameAttr;
   2679  nsAutoString otherAttrs;
   2680  for (uint32_t i = 0; i < attrCount; i++) {
   2681    BorrowedAttrInfo attrInfo = aElement->GetAttrInfoAt(i);
   2682    nsAutoString attrValue;
   2683    attrInfo.mValue->ToString(attrValue);
   2684 
   2685    const nsAttrName* attrName = attrInfo.mName;
   2686    if (attrName->Equals(nsGkAtoms::id)) {
   2687      idAttr.AppendLiteral(" id=\"");
   2688      idAttr.Append(attrValue);
   2689      idAttr.AppendLiteral("\"");
   2690    } else if (attrName->Equals(nsGkAtoms::_class)) {
   2691      classAttr.AppendLiteral(" class=\"");
   2692      classAttr.Append(attrValue);
   2693      classAttr.AppendLiteral("\"");
   2694    } else if (attrName->Equals(nsGkAtoms::name)) {
   2695      nameAttr.AppendLiteral(" name=\"");
   2696      nameAttr.Append(attrValue);
   2697      nameAttr.AppendLiteral("\"");
   2698    } else {
   2699      nsAutoString attrNameStr;
   2700      attrName->GetQualifiedName(attrNameStr);
   2701      otherAttrs.AppendLiteral(" ");
   2702      otherAttrs.Append(attrNameStr);
   2703      otherAttrs.AppendLiteral("=\"");
   2704      otherAttrs.Append(attrValue);
   2705      otherAttrs.AppendLiteral("\"");
   2706    }
   2707  }
   2708  if (!idAttr.IsEmpty()) {
   2709    aOut.Append(idAttr);
   2710  }
   2711  if (!classAttr.IsEmpty()) {
   2712    aOut.Append(classAttr);
   2713  }
   2714  if (!nameAttr.IsEmpty()) {
   2715    aOut.Append(nameAttr);
   2716  }
   2717  if (!otherAttrs.IsEmpty()) {
   2718    aOut.Append(otherAttrs);
   2719  }
   2720  aOut.AppendLiteral(">");
   2721 }
   2722 
   2723 void Console::MaybeExecuteDumpFunction(JSContext* aCx, MethodName aMethodName,
   2724                                       const nsAString& aMethodString,
   2725                                       const Sequence<JS::Value>& aData,
   2726                                       nsIStackFrame* aStack,
   2727                                       DOMHighResTimeStamp aMonotonicTimer) {
   2728  if (mLogModule->ShouldLog(InternalLogLevelToMozLog(aMethodName))) {
   2729    nsString message = GetDumpMessage(aCx, aMethodName, aMethodString, aData,
   2730                                      aStack, aMonotonicTimer, true);
   2731 
   2732    MOZ_LOG(mLogModule, InternalLogLevelToMozLog(aMethodName),
   2733            ("%s", NS_ConvertUTF16toUTF8(message).get()));
   2734  }
   2735 
   2736  if (!mDumpFunction && !mDumpToStdout) {
   2737    return;
   2738  }
   2739  nsString message = GetDumpMessage(aCx, aMethodName, aMethodString, aData,
   2740                                    aStack, aMonotonicTimer, false);
   2741 
   2742  ExecuteDumpFunction(message);
   2743 }
   2744 
   2745 nsString Console::GetDumpMessage(JSContext* aCx, MethodName aMethodName,
   2746                                 const nsAString& aMethodString,
   2747                                 const Sequence<JS::Value>& aData,
   2748                                 nsIStackFrame* aStack,
   2749                                 DOMHighResTimeStamp aMonotonicTimer,
   2750                                 bool aIsForMozLog) {
   2751  nsString message;
   2752  // MOZ_LOG already logs either console or the prefix
   2753  if (!aIsForMozLog) {
   2754    message.AssignLiteral("console.");
   2755  } else {
   2756    message.AssignLiteral("");
   2757  }
   2758  message.Append(aMethodString);
   2759  message.AppendLiteral(": ");
   2760 
   2761  if (!aIsForMozLog && !mPrefix.IsEmpty()) {
   2762    message.Append(mPrefix);
   2763    message.AppendLiteral(": ");
   2764  }
   2765 
   2766  for (uint32_t i = 0; i < aData.Length(); ++i) {
   2767    JS::Rooted<JS::Value> v(aCx, aData[i]);
   2768    if (v.isObject()) {
   2769      Element* element = nullptr;
   2770      if (NS_SUCCEEDED(UNWRAP_OBJECT(Element, &v, element))) {
   2771        if (i != 0) {
   2772          message.AppendLiteral(" ");
   2773        }
   2774        StringifyElement(element, message);
   2775        continue;
   2776      }
   2777    }
   2778 
   2779    JS::Rooted<JSString*> jsString(aCx, JS_ValueToSource(aCx, v));
   2780    if (!jsString) {
   2781      continue;
   2782    }
   2783 
   2784    nsAutoJSString string;
   2785    if (NS_WARN_IF(!string.init(aCx, jsString))) {
   2786      return message;
   2787    }
   2788 
   2789    if (i != 0) {
   2790      message.AppendLiteral(" ");
   2791    }
   2792 
   2793    if (string.EqualsLiteral("({})")) {
   2794      // ({}) is a generic serialization, possibly from XPC_WN_Shared_ToSource.
   2795      // Such a serialization is rather unhelpful, so try .toString() instead.
   2796      // When the input is a Components.Exception instance, the result will
   2797      // contain the error message, code and stack, which eases debugging.
   2798      JS::Rooted<JSString*> jsString2(aCx, JS::ToString(aCx, v));
   2799      nsAutoJSString string2;
   2800      if (jsString2 && string2.init(aCx, jsString2)) {
   2801        message.Append(string2);
   2802        continue;
   2803      }
   2804    }
   2805    message.Append(string);
   2806  }
   2807 
   2808  if (aMethodName == MethodTime || aMethodName == MethodTimeEnd) {
   2809    message.AppendLiteral(" @ ");
   2810    message.AppendFloat(aMonotonicTimer);
   2811  }
   2812 
   2813  message.AppendLiteral("\n");
   2814 
   2815  // aStack can be null.
   2816 
   2817  nsCOMPtr<nsIStackFrame> stack(aStack);
   2818 
   2819  while (stack) {
   2820    nsAutoCString filename;
   2821    stack->GetFilename(aCx, filename);
   2822 
   2823    AppendUTF8toUTF16(filename, message);
   2824    message.AppendLiteral(" ");
   2825 
   2826    message.AppendInt(stack->GetLineNumber(aCx));
   2827    message.AppendLiteral(" ");
   2828 
   2829    nsAutoString functionName;
   2830    stack->GetName(aCx, functionName);
   2831 
   2832    message.Append(functionName);
   2833    message.AppendLiteral("\n");
   2834 
   2835    nsCOMPtr<nsIStackFrame> caller = stack->GetCaller(aCx);
   2836 
   2837    if (!caller) {
   2838      caller = stack->GetAsyncCaller(aCx);
   2839    }
   2840 
   2841    stack.swap(caller);
   2842  }
   2843 
   2844  return message;
   2845 }
   2846 
   2847 void Console::ExecuteDumpFunction(const nsAString& aMessage) {
   2848  if (mDumpFunction) {
   2849    RefPtr<ConsoleInstanceDumpCallback> dumpFunction(mDumpFunction);
   2850    dumpFunction->Call(aMessage);
   2851    return;
   2852  }
   2853 
   2854  NS_ConvertUTF16toUTF8 str(aMessage);
   2855  MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("%s", str.get()));
   2856 #ifdef ANDROID
   2857  __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
   2858 #endif
   2859  fputs(str.get(), stdout);
   2860  fflush(stdout);
   2861 }
   2862 
   2863 bool Console::ShouldProceed(MethodName aName) const {
   2864  return mCurrentLogLevel <= InternalLogLevelToInteger(aName);
   2865 }
   2866 
   2867 uint32_t Console::WebIDLLogLevelToInteger(ConsoleLogLevel aLevel) const {
   2868  switch (aLevel) {
   2869    case ConsoleLogLevel::All:
   2870      return 0;
   2871    case ConsoleLogLevel::Debug:
   2872      return 2;
   2873    case ConsoleLogLevel::Log:
   2874      return 3;
   2875    case ConsoleLogLevel::Info:
   2876      return 3;
   2877    case ConsoleLogLevel::Clear:
   2878      return 3;
   2879    case ConsoleLogLevel::Trace:
   2880      return 3;
   2881    case ConsoleLogLevel::TimeLog:
   2882      return 3;
   2883    case ConsoleLogLevel::TimeEnd:
   2884      return 3;
   2885    case ConsoleLogLevel::Time:
   2886      return 3;
   2887    case ConsoleLogLevel::Group:
   2888      return 3;
   2889    case ConsoleLogLevel::GroupEnd:
   2890      return 3;
   2891    case ConsoleLogLevel::Profile:
   2892      return 3;
   2893    case ConsoleLogLevel::ProfileEnd:
   2894      return 3;
   2895    case ConsoleLogLevel::Dir:
   2896      return 3;
   2897    case ConsoleLogLevel::Dirxml:
   2898      return 3;
   2899    case ConsoleLogLevel::Warn:
   2900      return 4;
   2901    case ConsoleLogLevel::Error:
   2902      return 5;
   2903    case ConsoleLogLevel::Off:
   2904      return UINT32_MAX;
   2905    default:
   2906      MOZ_CRASH(
   2907          "ConsoleLogLevel is out of sync with the Console implementation!");
   2908      return 0;
   2909  }
   2910 }
   2911 
   2912 uint32_t Console::InternalLogLevelToInteger(MethodName aName) const {
   2913  switch (aName) {
   2914    case MethodLog:
   2915      return 3;
   2916    case MethodInfo:
   2917      return 3;
   2918    case MethodWarn:
   2919      return 4;
   2920    case MethodError:
   2921      return 5;
   2922    case MethodException:
   2923      return 5;
   2924    case MethodDebug:
   2925      return 2;
   2926    case MethodTable:
   2927      return 3;
   2928    case MethodTrace:
   2929      return 3;
   2930    case MethodDir:
   2931      return 3;
   2932    case MethodDirxml:
   2933      return 3;
   2934    case MethodGroup:
   2935      return 3;
   2936    case MethodGroupCollapsed:
   2937      return 3;
   2938    case MethodGroupEnd:
   2939      return 3;
   2940    case MethodTime:
   2941      return 3;
   2942    case MethodTimeLog:
   2943      return 3;
   2944    case MethodTimeEnd:
   2945      return 3;
   2946    case MethodTimeStamp:
   2947      return 3;
   2948    case MethodAssert:
   2949      return 3;
   2950    case MethodCount:
   2951      return 3;
   2952    case MethodCountReset:
   2953      return 3;
   2954    case MethodClear:
   2955      return 3;
   2956    case MethodProfile:
   2957      return 3;
   2958    case MethodProfileEnd:
   2959      return 3;
   2960    default:
   2961      MOZ_CRASH("MethodName is out of sync with the Console implementation!");
   2962      return 0;
   2963  }
   2964 }
   2965 
   2966 LogLevel Console::InternalLogLevelToMozLog(MethodName aName) const {
   2967  switch (aName) {
   2968    case MethodLog:
   2969      return LogLevel::Info;
   2970    case MethodInfo:
   2971      return LogLevel::Info;
   2972    case MethodWarn:
   2973      return LogLevel::Warning;
   2974    case MethodError:
   2975      return LogLevel::Error;
   2976    case MethodException:
   2977      return LogLevel::Error;
   2978    case MethodDebug:
   2979      return LogLevel::Debug;
   2980    case MethodTable:
   2981      return LogLevel::Info;
   2982    case MethodTrace:
   2983      return LogLevel::Info;
   2984    case MethodDir:
   2985      return LogLevel::Info;
   2986    case MethodDirxml:
   2987      return LogLevel::Info;
   2988    case MethodGroup:
   2989      return LogLevel::Info;
   2990    case MethodGroupCollapsed:
   2991      return LogLevel::Info;
   2992    case MethodGroupEnd:
   2993      return LogLevel::Info;
   2994    case MethodTime:
   2995      return LogLevel::Info;
   2996    case MethodTimeLog:
   2997      return LogLevel::Info;
   2998    case MethodTimeEnd:
   2999      return LogLevel::Info;
   3000    case MethodTimeStamp:
   3001      return LogLevel::Info;
   3002    case MethodAssert:
   3003      return LogLevel::Error;
   3004    case MethodCount:
   3005      return LogLevel::Info;
   3006    case MethodCountReset:
   3007      return LogLevel::Info;
   3008    case MethodClear:
   3009      return LogLevel::Info;
   3010    case MethodProfile:
   3011      return LogLevel::Info;
   3012    case MethodProfileEnd:
   3013      return LogLevel::Info;
   3014    default:
   3015      MOZ_CRASH("MethodName is out of sync with the Console implementation!");
   3016      return LogLevel::Disabled;
   3017  }
   3018 }
   3019 
   3020 bool Console::ArgumentData::Initialize(JSContext* aCx,
   3021                                       const Sequence<JS::Value>& aArguments) {
   3022  mGlobal = JS::CurrentGlobalOrNull(aCx);
   3023 
   3024  if (NS_WARN_IF(!mArguments.AppendElements(aArguments, fallible))) {
   3025    return false;
   3026  }
   3027 
   3028  return true;
   3029 }
   3030 
   3031 void Console::ArgumentData::Trace(const TraceCallbacks& aCallbacks,
   3032                                  void* aClosure) {
   3033  ArgumentData* tmp = this;
   3034  for (uint32_t i = 0; i < mArguments.Length(); ++i) {
   3035    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArguments[i])
   3036  }
   3037 
   3038  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
   3039 }
   3040 
   3041 bool Console::ArgumentData::PopulateArgumentsSequence(
   3042    Sequence<JS::Value>& aSequence) const {
   3043  AssertIsOnOwningThread();
   3044 
   3045  for (uint32_t i = 0; i < mArguments.Length(); ++i) {
   3046    if (NS_WARN_IF(!aSequence.AppendElement(mArguments[i], fallible))) {
   3047      return false;
   3048    }
   3049  }
   3050 
   3051  return true;
   3052 }
   3053 
   3054 }  // namespace mozilla::dom