LCOV - code coverage report
Current view: top level - dom/console - Console.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 263 1140 23.1 %
Date: 2017-07-14 16:53:18 Functions: 30 111 27.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/Console.h"
       8             : #include "mozilla/dom/ConsoleBinding.h"
       9             : 
      10             : #include "mozilla/dom/BlobBinding.h"
      11             : #include "mozilla/dom/Exceptions.h"
      12             : #include "mozilla/dom/File.h"
      13             : #include "mozilla/dom/FunctionBinding.h"
      14             : #include "mozilla/dom/Performance.h"
      15             : #include "mozilla/dom/ScriptSettings.h"
      16             : #include "mozilla/dom/StructuredCloneHolder.h"
      17             : #include "mozilla/dom/ToJSValue.h"
      18             : #include "mozilla/dom/WorkletGlobalScope.h"
      19             : #include "mozilla/Maybe.h"
      20             : #include "nsCycleCollectionParticipant.h"
      21             : #include "nsDocument.h"
      22             : #include "nsDOMNavigationTiming.h"
      23             : #include "nsGlobalWindow.h"
      24             : #include "nsJSUtils.h"
      25             : #include "nsNetUtil.h"
      26             : #include "WorkerPrivate.h"
      27             : #include "WorkerRunnable.h"
      28             : #include "WorkerScope.h"
      29             : #include "xpcpublic.h"
      30             : #include "nsContentUtils.h"
      31             : #include "nsDocShell.h"
      32             : #include "nsProxyRelease.h"
      33             : #include "mozilla/ConsoleTimelineMarker.h"
      34             : #include "mozilla/TimestampTimelineMarker.h"
      35             : 
      36             : #include "nsIConsoleAPIStorage.h"
      37             : #include "nsIDOMWindowUtils.h"
      38             : #include "nsIInterfaceRequestorUtils.h"
      39             : #include "nsILoadContext.h"
      40             : #include "nsIProgrammingLanguage.h"
      41             : #include "nsISensitiveInfoHiddenURI.h"
      42             : #include "nsIServiceManager.h"
      43             : #include "nsISupportsPrimitives.h"
      44             : #include "nsIWebNavigation.h"
      45             : #include "nsIXPConnect.h"
      46             : 
      47             : // The maximum allowed number of concurrent timers per page.
      48             : #define MAX_PAGE_TIMERS 10000
      49             : 
      50             : // The maximum allowed number of concurrent counters per page.
      51             : #define MAX_PAGE_COUNTERS 10000
      52             : 
      53             : // The maximum stacktrace depth when populating the stacktrace array used for
      54             : // console.trace().
      55             : #define DEFAULT_MAX_STACKTRACE_DEPTH 200
      56             : 
      57             : // This tags are used in the Structured Clone Algorithm to move js values from
      58             : // worker thread to main thread
      59             : #define CONSOLE_TAG_BLOB   JS_SCTAG_USER_MIN
      60             : 
      61             : // This value is taken from ConsoleAPIStorage.js
      62             : #define STORAGE_MAX_EVENTS 1000
      63             : 
      64             : using namespace mozilla::dom::exceptions;
      65             : using namespace mozilla::dom::workers;
      66             : 
      67             : namespace mozilla {
      68             : namespace dom {
      69             : 
      70             : struct
      71           0 : ConsoleStructuredCloneData
      72             : {
      73             :   nsCOMPtr<nsISupports> mParent;
      74             :   nsTArray<RefPtr<BlobImpl>> mBlobs;
      75             : };
      76             : 
      77             : /**
      78             :  * Console API in workers uses the Structured Clone Algorithm to move any value
      79             :  * from the worker thread to the main-thread. Some object cannot be moved and,
      80             :  * in these cases, we convert them to strings.
      81             :  * It's not the best, but at least we are able to show something.
      82             :  */
      83             : 
      84             : class ConsoleCallData final
      85             : {
      86             : public:
      87           5 :   NS_INLINE_DECL_REFCOUNTING(ConsoleCallData)
      88             : 
      89           1 :   ConsoleCallData()
      90           1 :     : mMethodName(Console::MethodLog)
      91           1 :     , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
      92             :     , mStartTimerValue(0)
      93             :     , mStartTimerStatus(Console::eTimerUnknown)
      94             :     , mStopTimerDuration(0)
      95             :     , mStopTimerStatus(Console::eTimerUnknown)
      96             :     , mCountValue(MAX_PAGE_COUNTERS)
      97             :     , mIDType(eUnknown)
      98             :     , mOuterIDNumber(0)
      99             :     , mInnerIDNumber(0)
     100           2 :     , mStatus(eUnused)
     101           1 :   {}
     102             : 
     103             :   bool
     104           1 :   Initialize(JSContext* aCx, Console::MethodName aName,
     105             :              const nsAString& aString,
     106             :              const Sequence<JS::Value>& aArguments,
     107             :              Console* aConsole)
     108             :   {
     109           1 :     AssertIsOnOwningThread();
     110           1 :     MOZ_ASSERT(aConsole);
     111             : 
     112             :     // We must be registered before doing any JS operation otherwise it can
     113             :     // happen that mCopiedArguments are not correctly traced.
     114           1 :     aConsole->StoreCallData(this);
     115             : 
     116           1 :     mMethodName = aName;
     117           1 :     mMethodString = aString;
     118             : 
     119           1 :     mGlobal = JS::CurrentGlobalOrNull(aCx);
     120             : 
     121           2 :     for (uint32_t i = 0; i < aArguments.Length(); ++i) {
     122           1 :       if (NS_WARN_IF(!mCopiedArguments.AppendElement(aArguments[i]))) {
     123           0 :         aConsole->UnstoreCallData(this);
     124           0 :         return false;
     125             :       }
     126             :     }
     127             : 
     128           1 :     return true;
     129             :   }
     130             : 
     131             :   void
     132           1 :   SetIDs(uint64_t aOuterID, uint64_t aInnerID)
     133             :   {
     134           1 :     MOZ_ASSERT(mIDType == eUnknown);
     135             : 
     136           1 :     mOuterIDNumber = aOuterID;
     137           1 :     mInnerIDNumber = aInnerID;
     138           1 :     mIDType = eNumber;
     139           1 :   }
     140             : 
     141             :   void
     142           0 :   SetIDs(const nsAString& aOuterID, const nsAString& aInnerID)
     143             :   {
     144           0 :     MOZ_ASSERT(mIDType == eUnknown);
     145             : 
     146           0 :     mOuterIDString = aOuterID;
     147           0 :     mInnerIDString = aInnerID;
     148           0 :     mIDType = eString;
     149           0 :   }
     150             : 
     151             :   void
     152           1 :   SetOriginAttributes(const OriginAttributes& aOriginAttributes)
     153             :   {
     154           1 :     mOriginAttributes = aOriginAttributes;
     155           1 :   }
     156             : 
     157             :   void
     158           1 :   SetAddonId(nsIPrincipal* aPrincipal)
     159             :   {
     160           2 :     nsAutoString addonId;
     161           1 :     aPrincipal->GetAddonId(addonId);
     162             : 
     163           1 :     mAddonId = addonId;
     164           1 :   }
     165             : 
     166             :   bool
     167           0 :   PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const
     168             :   {
     169           0 :     AssertIsOnOwningThread();
     170             : 
     171           0 :     for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
     172           0 :       if (NS_WARN_IF(!aSequence.AppendElement(mCopiedArguments[i],
     173             :                                               fallible))) {
     174           0 :         return false;
     175             :       }
     176             :     }
     177             : 
     178           0 :     return true;
     179             :   }
     180             : 
     181             :   void
     182           0 :   Trace(const TraceCallbacks& aCallbacks, void* aClosure)
     183             :   {
     184           0 :     ConsoleCallData* tmp = this;
     185           0 :     for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
     186           0 :       NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i])
     187             :     }
     188             : 
     189           0 :     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal);
     190           0 :   }
     191             : 
     192             :   void
     193           2 :   AssertIsOnOwningThread() const
     194             :   {
     195           2 :     NS_ASSERT_OWNINGTHREAD(ConsoleCallData);
     196           2 :   }
     197             : 
     198             :   JS::Heap<JSObject*> mGlobal;
     199             : 
     200             :   // This is a copy of the arguments we received from the DOM bindings. Console
     201             :   // object traces them because this ConsoleCallData calls
     202             :   // RegisterConsoleCallData() in the Initialize().
     203             :   nsTArray<JS::Heap<JS::Value>> mCopiedArguments;
     204             : 
     205             :   Console::MethodName mMethodName;
     206             :   int64_t mTimeStamp;
     207             : 
     208             :   // These values are set in the owning thread and they contain the timestamp of
     209             :   // when the new timer has started, the name of it and the status of the
     210             :   // creation of it. If status is false, something went wrong. User
     211             :   // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
     212             :   // monotonicTimer from Performance.now();
     213             :   // They will be set on the owning thread and never touched again on that
     214             :   // thread. They will be used in order to create a ConsoleTimerStart dictionary
     215             :   // when console.time() is used.
     216             :   DOMHighResTimeStamp mStartTimerValue;
     217             :   nsString mStartTimerLabel;
     218             :   Console::TimerStatus mStartTimerStatus;
     219             : 
     220             :   // These values are set in the owning thread and they contain the duration,
     221             :   // the name and the status of the StopTimer method. If status is false,
     222             :   // something went wrong. They will be set on the owning thread and never
     223             :   // touched again on that thread. They will be used in order to create a
     224             :   // ConsoleTimerEnd dictionary. This members are set when
     225             :   // console.timeEnd() is called.
     226             :   double mStopTimerDuration;
     227             :   nsString mStopTimerLabel;
     228             :   Console::TimerStatus mStopTimerStatus;
     229             : 
     230             :   // These 2 values are set by IncreaseCounter on the owning thread and they are
     231             :   // used CreateCounterValue. These members are set when console.count() is
     232             :   // called.
     233             :   nsString mCountLabel;
     234             :   uint32_t mCountValue;
     235             : 
     236             :   // The concept of outerID and innerID is misleading because when a
     237             :   // ConsoleCallData is created from a window, these are the window IDs, but
     238             :   // when the object is created from a SharedWorker, a ServiceWorker or a
     239             :   // subworker of a ChromeWorker these IDs are the type of worker and the
     240             :   // filename of the callee.
     241             :   // In Console.jsm the ID is 'jsm'.
     242             :   enum {
     243             :     eString,
     244             :     eNumber,
     245             :     eUnknown
     246             :   } mIDType;
     247             : 
     248             :   uint64_t mOuterIDNumber;
     249             :   nsString mOuterIDString;
     250             : 
     251             :   uint64_t mInnerIDNumber;
     252             :   nsString mInnerIDString;
     253             : 
     254             :   OriginAttributes mOriginAttributes;
     255             : 
     256             :   nsString mAddonId;
     257             : 
     258             :   nsString mMethodString;
     259             : 
     260             :   // Stack management is complicated, because we want to do it as
     261             :   // lazily as possible.  Therefore, we have the following behavior:
     262             :   // 1)  mTopStackFrame is initialized whenever we have any JS on the stack
     263             :   // 2)  mReifiedStack is initialized if we're created in a worker.
     264             :   // 3)  mStack is set (possibly to null if there is no JS on the stack) if
     265             :   //     we're created on main thread.
     266             :   Maybe<ConsoleStackEntry> mTopStackFrame;
     267             :   Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
     268             :   nsCOMPtr<nsIStackFrame> mStack;
     269             : 
     270             :   // mStatus is about the lifetime of this object. Console must take care of
     271             :   // keep it alive or not following this enumeration.
     272             :   enum {
     273             :     // If the object is created but it is owned by some runnable, this is its
     274             :     // status. It can be deleted at any time.
     275             :     eUnused,
     276             : 
     277             :     // When a runnable takes ownership of a ConsoleCallData and send it to
     278             :     // different thread, this is its status. Console cannot delete it at this
     279             :     // time.
     280             :     eInUse,
     281             : 
     282             :     // When a runnable owns this ConsoleCallData, we can't delete it directly.
     283             :     // instead, we mark it with this new status and we move it in
     284             :     // mCallDataStoragePending list in order to keep it alive an trace it
     285             :     // correctly. Once the runnable finishs its task, it will delete this object
     286             :     // calling ReleaseCallData().
     287             :     eToBeDeleted
     288             :   } mStatus;
     289             : 
     290             : private:
     291           1 :   ~ConsoleCallData()
     292           1 :   {
     293           1 :     AssertIsOnOwningThread();
     294           1 :     MOZ_ASSERT(mStatus != eInUse);
     295           1 :   }
     296             : };
     297             : 
     298             : // This class is used to clear any exception at the end of this method.
     299             : class ClearException
     300             : {
     301             : public:
     302           0 :   explicit ClearException(JSContext* aCx)
     303           0 :     : mCx(aCx)
     304             :   {
     305           0 :   }
     306             : 
     307           0 :   ~ClearException()
     308           0 :   {
     309           0 :     JS_ClearPendingException(mCx);
     310           0 :   }
     311             : 
     312             : private:
     313             :   JSContext* mCx;
     314             : };
     315             : 
     316             : class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
     317             :                       , public StructuredCloneHolderBase
     318             : {
     319             : public:
     320           0 :   explicit ConsoleRunnable(Console* aConsole)
     321           0 :     : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
     322           0 :     , mConsole(aConsole)
     323           0 :   {}
     324             : 
     325             :   virtual
     326           0 :   ~ConsoleRunnable()
     327           0 :   {
     328             :     // Clear the StructuredCloneHolderBase class.
     329           0 :     Clear();
     330           0 :   }
     331             : 
     332             :   bool
     333           0 :   Dispatch(JSContext* aCx)
     334             :   {
     335           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     336             : 
     337           0 :     if (NS_WARN_IF(!PreDispatch(aCx))) {
     338           0 :       RunBackOnWorkerThreadForCleanup();
     339           0 :       return false;
     340             :     }
     341             : 
     342           0 :     if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch())) {
     343             :       // RunBackOnWorkerThreadForCleanup() will be called by
     344             :       // WorkerProxyToMainThreadRunnable::Dispatch().
     345           0 :       return false;
     346             :     }
     347             : 
     348           0 :     return true;
     349             :   }
     350             : 
     351             : protected:
     352             :   void
     353           0 :   RunOnMainThread() override
     354             :   {
     355           0 :     AssertIsOnMainThread();
     356             : 
     357             :     // Walk up to our containing page
     358           0 :     WorkerPrivate* wp = mWorkerPrivate;
     359           0 :     while (wp->GetParent()) {
     360           0 :       wp = wp->GetParent();
     361             :     }
     362             : 
     363           0 :     nsPIDOMWindowInner* window = wp->GetWindow();
     364           0 :     if (!window) {
     365           0 :       RunWindowless();
     366             :     } else {
     367           0 :       RunWithWindow(window);
     368             :     }
     369           0 :   }
     370             : 
     371             :   void
     372           0 :   RunWithWindow(nsPIDOMWindowInner* aWindow)
     373             :   {
     374           0 :     AssertIsOnMainThread();
     375             : 
     376           0 :     AutoJSAPI jsapi;
     377           0 :     MOZ_ASSERT(aWindow);
     378             : 
     379           0 :     RefPtr<nsGlobalWindow> win = nsGlobalWindow::Cast(aWindow);
     380           0 :     if (NS_WARN_IF(!jsapi.Init(win))) {
     381           0 :       return;
     382             :     }
     383             : 
     384           0 :     MOZ_ASSERT(aWindow->IsInnerWindow());
     385           0 :     nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
     386           0 :     if (NS_WARN_IF(!outerWindow)) {
     387           0 :       return;
     388             :     }
     389             : 
     390           0 :     RunConsole(jsapi.cx(), outerWindow, aWindow);
     391             :   }
     392             : 
     393             :   void
     394           0 :   RunWindowless()
     395             :   {
     396           0 :     AssertIsOnMainThread();
     397             : 
     398           0 :     WorkerPrivate* wp = mWorkerPrivate;
     399           0 :     while (wp->GetParent()) {
     400           0 :       wp = wp->GetParent();
     401             :     }
     402             : 
     403           0 :     MOZ_ASSERT(!wp->GetWindow());
     404             : 
     405           0 :     AutoSafeJSContext cx;
     406             : 
     407           0 :     JS::Rooted<JSObject*> global(cx, mConsole->GetOrCreateSandbox(cx, wp->GetPrincipal()));
     408           0 :     if (NS_WARN_IF(!global)) {
     409           0 :       return;
     410             :     }
     411             : 
     412             :     // The CreateSandbox call returns a proxy to the actual sandbox object. We
     413             :     // don't need a proxy here.
     414           0 :     global = js::UncheckedUnwrap(global);
     415             : 
     416           0 :     JSAutoCompartment ac(cx, global);
     417             : 
     418           0 :     RunConsole(cx, nullptr, nullptr);
     419             :   }
     420             : 
     421             :   void
     422           0 :   RunBackOnWorkerThreadForCleanup() override
     423             :   {
     424           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     425           0 :     ReleaseData();
     426           0 :     mConsole = nullptr;
     427           0 :   }
     428             : 
     429             :   // This method is called in the owning thread of the Console object.
     430             :   virtual bool
     431             :   PreDispatch(JSContext* aCx) = 0;
     432             : 
     433             :   // This method is called in the main-thread.
     434             :   virtual void
     435             :   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
     436             :              nsPIDOMWindowInner* aInnerWindow) = 0;
     437             : 
     438             :   // This method is called in the owning thread of the Console object.
     439             :   virtual void
     440             :   ReleaseData() = 0;
     441             : 
     442           0 :   virtual JSObject* CustomReadHandler(JSContext* aCx,
     443             :                                       JSStructuredCloneReader* aReader,
     444             :                                       uint32_t aTag,
     445             :                                       uint32_t aIndex) override
     446             :   {
     447           0 :     AssertIsOnMainThread();
     448             : 
     449           0 :     if (aTag == CONSOLE_TAG_BLOB) {
     450           0 :       MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
     451             : 
     452           0 :       JS::Rooted<JS::Value> val(aCx);
     453             :       {
     454             :         RefPtr<Blob> blob =
     455           0 :           Blob::Create(mClonedData.mParent, mClonedData.mBlobs.ElementAt(aIndex));
     456           0 :         if (!ToJSValue(aCx, blob, &val)) {
     457           0 :           return nullptr;
     458             :         }
     459             :       }
     460             : 
     461           0 :       return &val.toObject();
     462             :     }
     463             : 
     464           0 :     MOZ_CRASH("No other tags are supported.");
     465             :     return nullptr;
     466             :   }
     467             : 
     468           0 :   virtual bool CustomWriteHandler(JSContext* aCx,
     469             :                                   JSStructuredCloneWriter* aWriter,
     470             :                                   JS::Handle<JSObject*> aObj) override
     471             :   {
     472           0 :     RefPtr<Blob> blob;
     473           0 :     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
     474           0 :         blob->Impl()->MayBeClonedToOtherThreads()) {
     475           0 :       if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
     476             :                                          mClonedData.mBlobs.Length()))) {
     477           0 :         return false;
     478             :       }
     479             : 
     480           0 :       mClonedData.mBlobs.AppendElement(blob->Impl());
     481           0 :       return true;
     482             :     }
     483             : 
     484           0 :     if (!JS_ObjectNotWritten(aWriter, aObj)) {
     485           0 :       return false;
     486             :     }
     487             : 
     488           0 :     JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
     489           0 :     JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
     490           0 :     if (NS_WARN_IF(!jsString)) {
     491           0 :       return false;
     492             :     }
     493             : 
     494           0 :     if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
     495           0 :       return false;
     496             :     }
     497             : 
     498           0 :     return true;
     499             :   }
     500             : 
     501             :   // This must be released on the worker thread.
     502             :   RefPtr<Console> mConsole;
     503             : 
     504             :   ConsoleStructuredCloneData mClonedData;
     505             : };
     506             : 
     507             : // This runnable appends a CallData object into the Console queue running on
     508             : // the main-thread.
     509             : class ConsoleCallDataRunnable final : public ConsoleRunnable
     510             : {
     511             : public:
     512           0 :   ConsoleCallDataRunnable(Console* aConsole,
     513             :                           ConsoleCallData* aCallData)
     514           0 :     : ConsoleRunnable(aConsole)
     515           0 :     , mCallData(aCallData)
     516             :   {
     517           0 :     MOZ_ASSERT(aCallData);
     518           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     519           0 :     mCallData->AssertIsOnOwningThread();
     520           0 :   }
     521             : 
     522             : private:
     523           0 :   ~ConsoleCallDataRunnable()
     524           0 :   {
     525           0 :     MOZ_ASSERT(!mCallData);
     526           0 :   }
     527             : 
     528             :   bool
     529           0 :   PreDispatch(JSContext* aCx) override
     530             :   {
     531           0 :     mWorkerPrivate->AssertIsOnWorkerThread();
     532           0 :     mCallData->AssertIsOnOwningThread();
     533             : 
     534           0 :     ClearException ce(aCx);
     535             : 
     536             :     JS::Rooted<JSObject*> arguments(aCx,
     537           0 :       JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
     538           0 :     if (NS_WARN_IF(!arguments)) {
     539           0 :       return false;
     540             :     }
     541             : 
     542           0 :     JS::Rooted<JS::Value> arg(aCx);
     543           0 :     for (uint32_t i = 0; i < mCallData->mCopiedArguments.Length(); ++i) {
     544           0 :       arg = mCallData->mCopiedArguments[i];
     545           0 :       if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
     546             :                                        JSPROP_ENUMERATE))) {
     547           0 :         return false;
     548             :       }
     549             :     }
     550             : 
     551           0 :     JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
     552             : 
     553           0 :     if (NS_WARN_IF(!Write(aCx, value))) {
     554           0 :       return false;
     555             :     }
     556             : 
     557           0 :     mCallData->mStatus = ConsoleCallData::eInUse;
     558           0 :     return true;
     559             :   }
     560             : 
     561             :   void
     562           0 :   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
     563             :              nsPIDOMWindowInner* aInnerWindow) override
     564             :   {
     565           0 :     AssertIsOnMainThread();
     566             : 
     567             :     // The windows have to run in parallel.
     568           0 :     MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
     569             : 
     570           0 :     if (aOuterWindow) {
     571           0 :       mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
     572             :     } else {
     573           0 :       ConsoleStackEntry frame;
     574           0 :       if (mCallData->mTopStackFrame) {
     575           0 :         frame = *mCallData->mTopStackFrame;
     576             :       }
     577             : 
     578           0 :       nsString id = frame.mFilename;
     579           0 :       nsString innerID;
     580           0 :       if (mWorkerPrivate->IsSharedWorker()) {
     581           0 :         innerID = NS_LITERAL_STRING("SharedWorker");
     582           0 :       } else if (mWorkerPrivate->IsServiceWorker()) {
     583           0 :         innerID = NS_LITERAL_STRING("ServiceWorker");
     584             :         // Use scope as ID so the webconsole can decide if the message should
     585             :         // show up per tab
     586           0 :         id.AssignWithConversion(mWorkerPrivate->ServiceWorkerScope());
     587             :       } else {
     588           0 :         innerID = NS_LITERAL_STRING("Worker");
     589             :       }
     590             : 
     591           0 :       mCallData->SetIDs(id, innerID);
     592             :     }
     593             : 
     594             :     // Now we could have the correct window (if we are not window-less).
     595           0 :     mClonedData.mParent = aInnerWindow;
     596             : 
     597           0 :     ProcessCallData(aCx);
     598             : 
     599           0 :     mClonedData.mParent = nullptr;
     600           0 :   }
     601             : 
     602             :   virtual void
     603           0 :   ReleaseData() override
     604             :   {
     605           0 :     mConsole->AssertIsOnOwningThread();
     606             : 
     607           0 :     if (mCallData->mStatus == ConsoleCallData::eToBeDeleted) {
     608           0 :       mConsole->ReleaseCallData(mCallData);
     609             :     } else {
     610           0 :       MOZ_ASSERT(mCallData->mStatus == ConsoleCallData::eInUse);
     611           0 :       mCallData->mStatus = ConsoleCallData::eUnused;
     612             :     }
     613             : 
     614           0 :     mCallData = nullptr;
     615           0 :   }
     616             : 
     617             :   void
     618           0 :   ProcessCallData(JSContext* aCx)
     619             :   {
     620           0 :     AssertIsOnMainThread();
     621             : 
     622           0 :     ClearException ce(aCx);
     623             : 
     624           0 :     JS::Rooted<JS::Value> argumentsValue(aCx);
     625           0 :     if (!Read(aCx, &argumentsValue)) {
     626           0 :       return;
     627             :     }
     628             : 
     629           0 :     MOZ_ASSERT(argumentsValue.isObject());
     630             : 
     631           0 :     JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
     632             : 
     633             :     uint32_t length;
     634           0 :     if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
     635           0 :       return;
     636             :     }
     637             : 
     638           0 :     Sequence<JS::Value> values;
     639           0 :     SequenceRooter<JS::Value> arguments(aCx, &values);
     640             : 
     641           0 :     for (uint32_t i = 0; i < length; ++i) {
     642           0 :       JS::Rooted<JS::Value> value(aCx);
     643             : 
     644           0 :       if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
     645           0 :         return;
     646             :       }
     647             : 
     648           0 :       if (!values.AppendElement(value, fallible)) {
     649           0 :         return;
     650             :       }
     651             :     }
     652             : 
     653           0 :     MOZ_ASSERT(values.Length() == length);
     654             : 
     655           0 :     mConsole->ProcessCallData(aCx, mCallData, values);
     656             :   }
     657             : 
     658             :   RefPtr<ConsoleCallData> mCallData;
     659             : };
     660             : 
     661             : // This runnable calls ProfileMethod() on the console on the main-thread.
     662           0 : class ConsoleProfileRunnable final : public ConsoleRunnable
     663             : {
     664             : public:
     665           0 :   ConsoleProfileRunnable(Console* aConsole, const nsAString& aAction,
     666             :                          const Sequence<JS::Value>& aArguments)
     667           0 :     : ConsoleRunnable(aConsole)
     668             :     , mAction(aAction)
     669           0 :     , mArguments(aArguments)
     670             :   {
     671           0 :     MOZ_ASSERT(aConsole);
     672           0 :   }
     673             : 
     674             : private:
     675             :   bool
     676           0 :   PreDispatch(JSContext* aCx) override
     677             :   {
     678           0 :     ClearException ce(aCx);
     679             : 
     680             :     JS::Rooted<JSObject*> arguments(aCx,
     681           0 :       JS_NewArrayObject(aCx, mArguments.Length()));
     682           0 :     if (NS_WARN_IF(!arguments)) {
     683           0 :       return false;
     684             :     }
     685             : 
     686           0 :     JS::Rooted<JS::Value> arg(aCx);
     687           0 :     for (uint32_t i = 0; i < mArguments.Length(); ++i) {
     688           0 :       arg = mArguments[i];
     689           0 :       if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
     690             :                                        JSPROP_ENUMERATE))) {
     691           0 :         return false;
     692             :       }
     693             :     }
     694             : 
     695           0 :     JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
     696             : 
     697           0 :     if (NS_WARN_IF(!Write(aCx, value))) {
     698           0 :       return false;
     699             :     }
     700             : 
     701           0 :     return true;
     702             :   }
     703             : 
     704             :   void
     705           0 :   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
     706             :              nsPIDOMWindowInner* aInnerWindow) override
     707             :   {
     708           0 :     AssertIsOnMainThread();
     709             : 
     710           0 :     ClearException ce(aCx);
     711             : 
     712             :     // Now we could have the correct window (if we are not window-less).
     713           0 :     mClonedData.mParent = aInnerWindow;
     714             : 
     715           0 :     JS::Rooted<JS::Value> argumentsValue(aCx);
     716           0 :     bool ok = Read(aCx, &argumentsValue);
     717           0 :     mClonedData.mParent = nullptr;
     718             : 
     719           0 :     if (!ok) {
     720           0 :       return;
     721             :     }
     722             : 
     723           0 :     MOZ_ASSERT(argumentsValue.isObject());
     724           0 :     JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
     725           0 :     if (NS_WARN_IF(!argumentsObj)) {
     726           0 :       return;
     727             :     }
     728             : 
     729             :     uint32_t length;
     730           0 :     if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
     731           0 :       return;
     732             :     }
     733             : 
     734           0 :     Sequence<JS::Value> arguments;
     735             : 
     736           0 :     for (uint32_t i = 0; i < length; ++i) {
     737           0 :       JS::Rooted<JS::Value> value(aCx);
     738             : 
     739           0 :       if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
     740           0 :         return;
     741             :       }
     742             : 
     743           0 :       if (!arguments.AppendElement(value, fallible)) {
     744           0 :         return;
     745             :       }
     746             :     }
     747             : 
     748           0 :     mConsole->ProfileMethodInternal(aCx, mAction, arguments);
     749             :   }
     750             : 
     751             :   virtual void
     752           0 :   ReleaseData() override
     753           0 :   {}
     754             : 
     755             :   nsString mAction;
     756             : 
     757             :   // This is a reference of the sequence of arguments we receive from the DOM
     758             :   // bindings and it's rooted by them. It's only used on the owning thread in
     759             :   // PreDispatch().
     760             :   const Sequence<JS::Value>& mArguments;
     761             : };
     762             : 
     763             : NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
     764             : 
     765             : // We don't need to traverse/unlink mStorage and mSandbox because they are not
     766             : // CCed objects and they are only used on the main thread, even when this
     767             : // Console object is used on workers.
     768             : 
     769           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
     770           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
     771           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsoleEventNotifier)
     772           0 :   tmp->Shutdown();
     773           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     774             : 
     775           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
     776           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
     777           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsoleEventNotifier)
     778           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     779             : 
     780           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
     781           0 :   for (uint32_t i = 0; i < tmp->mCallDataStorage.Length(); ++i) {
     782           0 :     tmp->mCallDataStorage[i]->Trace(aCallbacks, aClosure);
     783             :   }
     784             : 
     785           0 :   for (uint32_t i = 0; i < tmp->mCallDataStoragePending.Length(); ++i) {
     786           0 :     tmp->mCallDataStoragePending[i]->Trace(aCallbacks, aClosure);
     787             :   }
     788             : 
     789           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     790             : 
     791           4 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
     792           3 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Console)
     793             : 
     794           5 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
     795           2 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     796           2 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
     797           2 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     798           0 : NS_INTERFACE_MAP_END
     799             : 
     800             : /* static */ already_AddRefed<Console>
     801           1 : Console::Create(nsPIDOMWindowInner* aWindow, ErrorResult& aRv)
     802             : {
     803           1 :   MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
     804             : 
     805           2 :   RefPtr<Console> console = new Console(aWindow);
     806           1 :   console->Initialize(aRv);
     807           1 :   if (NS_WARN_IF(aRv.Failed())) {
     808           0 :     return nullptr;
     809             :   }
     810             : 
     811           1 :   return console.forget();
     812             : }
     813             : 
     814           1 : Console::Console(nsPIDOMWindowInner* aWindow)
     815             :   : mWindow(aWindow)
     816             :   , mOuterID(0)
     817             :   , mInnerID(0)
     818           1 :   , mStatus(eUnknown)
     819             : {
     820           1 :   MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
     821             : 
     822           1 :   if (mWindow) {
     823           1 :     MOZ_ASSERT(mWindow->IsInnerWindow());
     824           1 :     mInnerID = mWindow->WindowID();
     825             : 
     826             :     // Without outerwindow any console message coming from this object will not
     827             :     // shown in the devtools webconsole. But this should be fine because
     828             :     // probably we are shutting down, or the window is CCed/GCed.
     829           1 :     nsPIDOMWindowOuter* outerWindow = mWindow->GetOuterWindow();
     830           1 :     if (outerWindow) {
     831           1 :       mOuterID = outerWindow->WindowID();
     832             :     }
     833             :   }
     834             : 
     835           1 :   mozilla::HoldJSObjects(this);
     836           1 : }
     837             : 
     838           0 : Console::~Console()
     839             : {
     840           0 :   AssertIsOnOwningThread();
     841           0 :   Shutdown();
     842           0 :   mozilla::DropJSObjects(this);
     843           0 : }
     844             : 
     845             : void
     846           1 : Console::Initialize(ErrorResult& aRv)
     847             : {
     848           1 :   AssertIsOnOwningThread();
     849           1 :   MOZ_ASSERT(mStatus == eUnknown);
     850             : 
     851           1 :   if (NS_IsMainThread()) {
     852           2 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     853           1 :     if (NS_WARN_IF(!obs)) {
     854           0 :       aRv.Throw(NS_ERROR_FAILURE);
     855           0 :       return;
     856             :     }
     857             : 
     858           1 :     aRv = obs->AddObserver(this, "inner-window-destroyed", true);
     859           1 :     if (NS_WARN_IF(aRv.Failed())) {
     860           0 :       return;
     861             :     }
     862             : 
     863           1 :     aRv = obs->AddObserver(this, "memory-pressure", true);
     864           1 :     if (NS_WARN_IF(aRv.Failed())) {
     865           0 :       return;
     866             :     }
     867             :   }
     868             : 
     869           1 :   mStatus = eInitialized;
     870             : }
     871             : 
     872             : void
     873           0 : Console::Shutdown()
     874             : {
     875           0 :   AssertIsOnOwningThread();
     876             : 
     877           0 :   if (mStatus == eUnknown || mStatus == eShuttingDown) {
     878           0 :     return;
     879             :   }
     880             : 
     881           0 :   if (NS_IsMainThread()) {
     882           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     883           0 :     if (obs) {
     884           0 :       obs->RemoveObserver(this, "inner-window-destroyed");
     885           0 :       obs->RemoveObserver(this, "memory-pressure");
     886             :     }
     887             :   }
     888             : 
     889             :   NS_ReleaseOnMainThread(
     890           0 :     "Console::mStorage", mStorage.forget());
     891             :   NS_ReleaseOnMainThread(
     892           0 :     "Console::mSandbox", mSandbox.forget());
     893             : 
     894           0 :   mTimerRegistry.Clear();
     895           0 :   mCounterRegistry.Clear();
     896             : 
     897           0 :   mCallDataStorage.Clear();
     898           0 :   mCallDataStoragePending.Clear();
     899             : 
     900           0 :   mStatus = eShuttingDown;
     901             : }
     902             : 
     903             : NS_IMETHODIMP
     904           0 : Console::Observe(nsISupports* aSubject, const char* aTopic,
     905             :                  const char16_t* aData)
     906             : {
     907           0 :   AssertIsOnMainThread();
     908             : 
     909           0 :   if (!strcmp(aTopic, "inner-window-destroyed")) {
     910           0 :     nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
     911           0 :     NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
     912             : 
     913             :     uint64_t innerID;
     914           0 :     nsresult rv = wrapper->GetData(&innerID);
     915           0 :     NS_ENSURE_SUCCESS(rv, rv);
     916             : 
     917           0 :     if (innerID == mInnerID) {
     918           0 :       Shutdown();
     919             :     }
     920             : 
     921           0 :     return NS_OK;
     922             :   }
     923             : 
     924           0 :   if (!strcmp(aTopic, "memory-pressure")) {
     925           0 :     ClearStorage();
     926           0 :     return NS_OK;
     927             :   }
     928             : 
     929           0 :   return NS_OK;
     930             : }
     931             : 
     932             : void
     933           0 : Console::ClearStorage()
     934             : {
     935           0 :   mCallDataStorage.Clear();
     936           0 : }
     937             : 
     938             : #define METHOD(name, string)                                                   \
     939             :   /* static */ void                                                            \
     940             :   Console::name(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData) \
     941             :   {                                                                            \
     942             :     Method(aGlobal, Method##name, NS_LITERAL_STRING(string), aData);           \
     943             :   }
     944             : 
     945           0 : METHOD(Log, "log")
     946           0 : METHOD(Info, "info")
     947           0 : METHOD(Warn, "warn")
     948           1 : METHOD(Error, "error")
     949           0 : METHOD(Exception, "exception")
     950           0 : METHOD(Debug, "debug")
     951           0 : METHOD(Table, "table")
     952           0 : METHOD(Trace, "trace")
     953             : 
     954             : /* static */ void
     955           0 : Console::Clear(const GlobalObject& aGlobal)
     956             : {
     957           0 :   const Sequence<JS::Value> data;
     958           0 :   Method(aGlobal, MethodClear, NS_LITERAL_STRING("clear"), data);
     959           0 : }
     960             : 
     961             : // Displays an interactive listing of all the properties of an object.
     962           0 : METHOD(Dir, "dir");
     963           0 : METHOD(Dirxml, "dirxml");
     964             : 
     965           0 : METHOD(Group, "group")
     966           0 : METHOD(GroupCollapsed, "groupCollapsed")
     967             : 
     968             : /* static */ void
     969           0 : Console::GroupEnd(const GlobalObject& aGlobal)
     970             : {
     971           0 :   const Sequence<JS::Value> data;
     972           0 :   Method(aGlobal, MethodGroupEnd, NS_LITERAL_STRING("groupEnd"), data);
     973           0 : }
     974             : 
     975             : /* static */ void
     976           0 : Console::Time(const GlobalObject& aGlobal, const nsAString& aLabel)
     977             : {
     978           0 :   StringMethod(aGlobal, aLabel, MethodTime, NS_LITERAL_STRING("time"));
     979           0 : }
     980             : 
     981             : /* static */ void
     982           0 : Console::TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel)
     983             : {
     984           0 :   StringMethod(aGlobal, aLabel, MethodTimeEnd, NS_LITERAL_STRING("timeEnd"));
     985           0 : }
     986             : 
     987             : /* static */ void
     988           0 : Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
     989             :                       MethodName aMethodName, const nsAString& aMethodString)
     990             : {
     991           0 :   JSContext* cx = aGlobal.Context();
     992             : 
     993           0 :   ClearException ce(cx);
     994             : 
     995           0 :   Sequence<JS::Value> data;
     996           0 :   SequenceRooter<JS::Value> rooter(cx, &data);
     997             : 
     998           0 :   JS::Rooted<JS::Value> value(cx);
     999           0 :   if (!dom::ToJSValue(cx, aLabel, &value)) {
    1000           0 :     return;
    1001             :   }
    1002             : 
    1003           0 :   if (!data.AppendElement(value, fallible)) {
    1004           0 :     return;
    1005             :   }
    1006             : 
    1007           0 :   Method(aGlobal, aMethodName, aMethodString, data);
    1008             : }
    1009             : 
    1010             : /* static */ void
    1011           0 : Console::TimeStamp(const GlobalObject& aGlobal,
    1012             :                    const JS::Handle<JS::Value> aData)
    1013             : {
    1014           0 :   JSContext* cx = aGlobal.Context();
    1015             : 
    1016           0 :   ClearException ce(cx);
    1017             : 
    1018           0 :   Sequence<JS::Value> data;
    1019           0 :   SequenceRooter<JS::Value> rooter(cx, &data);
    1020             : 
    1021           0 :   if (aData.isString() && !data.AppendElement(aData, fallible)) {
    1022           0 :     return;
    1023             :   }
    1024             : 
    1025           0 :   Method(aGlobal, MethodTimeStamp, NS_LITERAL_STRING("timeStamp"), data);
    1026             : }
    1027             : 
    1028             : /* static */ void
    1029           0 : Console::Profile(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData)
    1030             : {
    1031           0 :   ProfileMethod(aGlobal, NS_LITERAL_STRING("profile"), aData);
    1032           0 : }
    1033             : 
    1034             : /* static */ void
    1035           0 : Console::ProfileEnd(const GlobalObject& aGlobal,
    1036             :                     const Sequence<JS::Value>& aData)
    1037             : {
    1038           0 :   ProfileMethod(aGlobal, NS_LITERAL_STRING("profileEnd"), aData);
    1039           0 : }
    1040             : 
    1041             : /* static */ void
    1042           0 : Console::ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
    1043             :                        const Sequence<JS::Value>& aData)
    1044             : {
    1045           0 :   RefPtr<Console> console = GetConsole(aGlobal);
    1046           0 :   if (!console) {
    1047           0 :     return;
    1048             :   }
    1049             : 
    1050           0 :   JSContext* cx = aGlobal.Context();
    1051           0 :   console->ProfileMethodInternal(cx, aAction, aData);
    1052             : }
    1053             : 
    1054             : void
    1055           0 : Console::ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
    1056             :                                const Sequence<JS::Value>& aData)
    1057             : {
    1058           0 :   if (!NS_IsMainThread()) {
    1059             :     // Here we are in a worker thread.
    1060             :     RefPtr<ConsoleProfileRunnable> runnable =
    1061           0 :       new ConsoleProfileRunnable(this, aAction, aData);
    1062             : 
    1063           0 :     runnable->Dispatch(aCx);
    1064           0 :     return;
    1065             :   }
    1066             : 
    1067           0 :   ClearException ce(aCx);
    1068             : 
    1069           0 :   RootedDictionary<ConsoleProfileEvent> event(aCx);
    1070           0 :   event.mAction = aAction;
    1071             : 
    1072           0 :   event.mArguments.Construct();
    1073           0 :   Sequence<JS::Value>& sequence = event.mArguments.Value();
    1074             : 
    1075           0 :   for (uint32_t i = 0; i < aData.Length(); ++i) {
    1076           0 :     if (!sequence.AppendElement(aData[i], fallible)) {
    1077           0 :       return;
    1078             :     }
    1079             :   }
    1080             : 
    1081           0 :   JS::Rooted<JS::Value> eventValue(aCx);
    1082           0 :   if (!ToJSValue(aCx, event, &eventValue)) {
    1083           0 :     return;
    1084             :   }
    1085             : 
    1086           0 :   JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
    1087           0 :   MOZ_ASSERT(eventObj);
    1088             : 
    1089           0 :   if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
    1090             :       JSPROP_ENUMERATE)) {
    1091           0 :     return;
    1092             :   }
    1093             : 
    1094           0 :   nsIXPConnect* xpc = nsContentUtils::XPConnect();
    1095           0 :   nsCOMPtr<nsISupports> wrapper;
    1096           0 :   const nsIID& iid = NS_GET_IID(nsISupports);
    1097             : 
    1098           0 :   if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
    1099           0 :     return;
    1100             :   }
    1101             : 
    1102           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    1103           0 :   if (obs) {
    1104           0 :     obs->NotifyObservers(wrapper, "console-api-profiler", nullptr);
    1105             :   }
    1106             : }
    1107             : 
    1108             : /* static */ void
    1109           0 : Console::Assert(const GlobalObject& aGlobal, bool aCondition,
    1110             :                 const Sequence<JS::Value>& aData)
    1111             : {
    1112           0 :   if (!aCondition) {
    1113           0 :     Method(aGlobal, MethodAssert, NS_LITERAL_STRING("assert"), aData);
    1114             :   }
    1115           0 : }
    1116             : 
    1117             : /* static */ void
    1118           0 : Console::Count(const GlobalObject& aGlobal, const nsAString& aLabel)
    1119             : {
    1120           0 :   StringMethod(aGlobal, aLabel, MethodCount, NS_LITERAL_STRING("count"));
    1121           0 : }
    1122             : 
    1123             : namespace {
    1124             : 
    1125             : nsresult
    1126           1 : StackFrameToStackEntry(JSContext* aCx, nsIStackFrame* aStackFrame,
    1127             :                        ConsoleStackEntry& aStackEntry)
    1128             : {
    1129           1 :   MOZ_ASSERT(aStackFrame);
    1130             : 
    1131           1 :   nsresult rv = aStackFrame->GetFilename(aCx, aStackEntry.mFilename);
    1132           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1133             : 
    1134             :   int32_t lineNumber;
    1135           1 :   rv = aStackFrame->GetLineNumber(aCx, &lineNumber);
    1136           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1137             : 
    1138           1 :   aStackEntry.mLineNumber = lineNumber;
    1139             : 
    1140             :   int32_t columnNumber;
    1141           1 :   rv = aStackFrame->GetColumnNumber(aCx, &columnNumber);
    1142           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1143             : 
    1144           1 :   aStackEntry.mColumnNumber = columnNumber;
    1145             : 
    1146           1 :   rv = aStackFrame->GetName(aCx, aStackEntry.mFunctionName);
    1147           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1148             : 
    1149           2 :   nsString cause;
    1150           1 :   rv = aStackFrame->GetAsyncCause(aCx, cause);
    1151           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1152           1 :   if (!cause.IsEmpty()) {
    1153           0 :     aStackEntry.mAsyncCause.Construct(cause);
    1154             :   }
    1155             : 
    1156           1 :   aStackEntry.mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
    1157           1 :   return NS_OK;
    1158             : }
    1159             : 
    1160             : nsresult
    1161           0 : ReifyStack(JSContext* aCx, nsIStackFrame* aStack,
    1162             :            nsTArray<ConsoleStackEntry>& aRefiedStack)
    1163             : {
    1164           0 :   nsCOMPtr<nsIStackFrame> stack(aStack);
    1165             : 
    1166           0 :   while (stack) {
    1167           0 :     ConsoleStackEntry& data = *aRefiedStack.AppendElement();
    1168           0 :     nsresult rv = StackFrameToStackEntry(aCx, stack, data);
    1169           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1170             : 
    1171           0 :     nsCOMPtr<nsIStackFrame> caller;
    1172           0 :     rv = stack->GetCaller(aCx, getter_AddRefs(caller));
    1173           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1174             : 
    1175           0 :     if (!caller) {
    1176           0 :       rv = stack->GetAsyncCaller(aCx, getter_AddRefs(caller));
    1177           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1178             :     }
    1179           0 :     stack.swap(caller);
    1180             :   }
    1181             : 
    1182           0 :   return NS_OK;
    1183             : }
    1184             : 
    1185             : } // anonymous namespace
    1186             : 
    1187             : // Queue a call to a console method. See the CALL_DELAY constant.
    1188             : /* static */ void
    1189           1 : Console::Method(const GlobalObject& aGlobal, MethodName aMethodName,
    1190             :                 const nsAString& aMethodString,
    1191             :                 const Sequence<JS::Value>& aData)
    1192             : {
    1193           2 :   RefPtr<Console> console = GetConsole(aGlobal);
    1194           1 :   if (!console) {
    1195           0 :     return;
    1196             :   }
    1197             : 
    1198           1 :   console->MethodInternal(aGlobal.Context(), aMethodName, aMethodString,
    1199           1 :                           aData);
    1200             : }
    1201             : 
    1202             : void
    1203           1 : Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
    1204             :                         const nsAString& aMethodString,
    1205             :                         const Sequence<JS::Value>& aData)
    1206             : {
    1207           1 :   AssertIsOnOwningThread();
    1208             : 
    1209           1 :   RefPtr<ConsoleCallData> callData(new ConsoleCallData());
    1210             : 
    1211           1 :   ClearException ce(aCx);
    1212             : 
    1213           1 :   if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
    1214             :                                        aData, this))) {
    1215           0 :     return;
    1216             :   }
    1217             : 
    1218           1 :   OriginAttributes oa;
    1219             : 
    1220           1 :   if (mWindow) {
    1221             :     // Save the principal's OriginAttributes in the console event data
    1222             :     // so that we will be able to filter messages by origin attributes.
    1223           2 :     nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
    1224           1 :     if (NS_WARN_IF(!sop)) {
    1225           0 :       return;
    1226             :     }
    1227             : 
    1228           2 :     nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
    1229           1 :     if (NS_WARN_IF(!principal)) {
    1230           0 :       return;
    1231             :     }
    1232             : 
    1233           1 :     oa = principal->OriginAttributesRef();
    1234           1 :     callData->SetAddonId(principal);
    1235             : 
    1236             : #ifdef DEBUG
    1237           1 :     if (!nsContentUtils::IsSystemPrincipal(principal)) {
    1238           2 :       nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
    1239           1 :       if (webNav) {
    1240           2 :         nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
    1241           1 :         MOZ_ASSERT(loadContext);
    1242             : 
    1243             :         bool pb;
    1244           1 :         if (NS_SUCCEEDED(loadContext->GetUsePrivateBrowsing(&pb))) {
    1245           1 :           MOZ_ASSERT(pb == !!oa.mPrivateBrowsingId);
    1246             :         }
    1247             :       }
    1248             :     }
    1249             : #endif
    1250             :   } else {
    1251           0 :     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    1252           0 :     MOZ_ASSERT(workerPrivate);
    1253           0 :     oa = workerPrivate->GetOriginAttributes();
    1254             :   }
    1255             : 
    1256           1 :   callData->SetOriginAttributes(oa);
    1257             : 
    1258           1 :   JS::StackCapture captureMode = ShouldIncludeStackTrace(aMethodName) ?
    1259           3 :     JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH)) :
    1260           2 :     JS::StackCapture(JS::FirstSubsumedFrame(aCx));
    1261           1 :   nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, mozilla::Move(captureMode));
    1262             : 
    1263           1 :   if (stack) {
    1264           1 :     callData->mTopStackFrame.emplace();
    1265           1 :     nsresult rv = StackFrameToStackEntry(aCx, stack,
    1266           2 :                                          *callData->mTopStackFrame);
    1267           1 :     if (NS_FAILED(rv)) {
    1268           0 :       return;
    1269             :     }
    1270             :   }
    1271             : 
    1272           1 :   if (NS_IsMainThread()) {
    1273           1 :     callData->mStack = stack;
    1274             :   } else {
    1275             :     // nsIStackFrame is not threadsafe, so we need to snapshot it now,
    1276             :     // before we post our runnable to the main thread.
    1277           0 :     callData->mReifiedStack.emplace();
    1278           0 :     nsresult rv = ReifyStack(aCx, stack, *callData->mReifiedStack);
    1279           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1280           0 :       return;
    1281             :     }
    1282             :   }
    1283             : 
    1284             :   DOMHighResTimeStamp monotonicTimer;
    1285             : 
    1286             :   // Monotonic timer for 'time' and 'timeEnd'
    1287           1 :   if (aMethodName == MethodTime ||
    1288           1 :       aMethodName == MethodTimeEnd ||
    1289             :       aMethodName == MethodTimeStamp) {
    1290           0 :     if (mWindow) {
    1291           0 :       nsGlobalWindow *win = nsGlobalWindow::Cast(mWindow);
    1292           0 :       MOZ_ASSERT(win);
    1293             : 
    1294           0 :       RefPtr<Performance> performance = win->GetPerformance();
    1295           0 :       if (!performance) {
    1296           0 :         return;
    1297             :       }
    1298             : 
    1299           0 :       monotonicTimer = performance->Now();
    1300             : 
    1301           0 :       nsDocShell* docShell = static_cast<nsDocShell*>(mWindow->GetDocShell());
    1302           0 :       RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
    1303           0 :       bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
    1304             : 
    1305             :       // The 'timeStamp' recordings do not need an argument; use empty string
    1306             :       // if no arguments passed in.
    1307           0 :       if (isTimelineRecording && aMethodName == MethodTimeStamp) {
    1308           0 :         JS::Rooted<JS::Value> value(aCx, aData.Length() == 0
    1309           0 :           ? JS_GetEmptyStringValue(aCx)
    1310           0 :           : aData[0]);
    1311           0 :         JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
    1312           0 :         if (!jsString) {
    1313           0 :           return;
    1314             :         }
    1315             : 
    1316           0 :         nsAutoJSString key;
    1317           0 :         if (!key.init(aCx, jsString)) {
    1318           0 :           return;
    1319             :         }
    1320             : 
    1321           0 :         timelines->AddMarkerForDocShell(docShell, Move(
    1322           0 :           MakeUnique<TimestampTimelineMarker>(key)));
    1323             :       }
    1324             :       // For `console.time(foo)` and `console.timeEnd(foo)`.
    1325           0 :       else if (isTimelineRecording && aData.Length() == 1) {
    1326           0 :         JS::Rooted<JS::Value> value(aCx, aData[0]);
    1327           0 :         JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
    1328           0 :         if (!jsString) {
    1329           0 :           return;
    1330             :         }
    1331             : 
    1332           0 :         nsAutoJSString key;
    1333           0 :         if (!key.init(aCx, jsString)) {
    1334           0 :           return;
    1335             :         }
    1336             : 
    1337           0 :         timelines->AddMarkerForDocShell(docShell, Move(
    1338           0 :           MakeUnique<ConsoleTimelineMarker>(
    1339           0 :             key, aMethodName == MethodTime ? MarkerTracingType::START
    1340           0 :                                            : MarkerTracingType::END)));
    1341             :       }
    1342             :     } else {
    1343           0 :       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    1344           0 :       MOZ_ASSERT(workerPrivate);
    1345             : 
    1346           0 :       monotonicTimer = workerPrivate->TimeStampToDOMHighRes(TimeStamp::Now());
    1347             :     }
    1348             :   }
    1349             : 
    1350           1 :   if (aMethodName == MethodTime && !aData.IsEmpty()) {
    1351           0 :     callData->mStartTimerStatus = StartTimer(aCx, aData[0],
    1352             :                                              monotonicTimer,
    1353           0 :                                              callData->mStartTimerLabel,
    1354           0 :                                              &callData->mStartTimerValue);
    1355             :   }
    1356             : 
    1357           1 :   else if (aMethodName == MethodTimeEnd && !aData.IsEmpty()) {
    1358           0 :     callData->mStopTimerStatus = StopTimer(aCx, aData[0],
    1359             :                                            monotonicTimer,
    1360           0 :                                            callData->mStopTimerLabel,
    1361           0 :                                            &callData->mStopTimerDuration);
    1362             :   }
    1363             : 
    1364           1 :   else if (aMethodName == MethodCount) {
    1365           0 :     callData->mCountValue = IncreaseCounter(aCx, aData, callData->mCountLabel);
    1366           0 :     if (!callData->mCountValue) {
    1367           0 :       return;
    1368             :     }
    1369             :   }
    1370             : 
    1371           1 :   if (NS_IsMainThread()) {
    1372           1 :     callData->SetIDs(mOuterID, mInnerID);
    1373           1 :     ProcessCallData(aCx, callData, aData);
    1374             : 
    1375             :     // Just because we don't want to expose
    1376             :     // retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
    1377             :     // cleanup the mCallDataStorage:
    1378           1 :     UnstoreCallData(callData);
    1379           1 :     return;
    1380             :   }
    1381             : 
    1382             :   // We do this only in workers for now.
    1383           0 :   NotifyHandler(aCx, aData, callData);
    1384             : 
    1385             :   RefPtr<ConsoleCallDataRunnable> runnable =
    1386           0 :     new ConsoleCallDataRunnable(this, callData);
    1387           0 :   Unused << NS_WARN_IF(!runnable->Dispatch(aCx));
    1388             : }
    1389             : 
    1390             : // We store information to lazily compute the stack in the reserved slots of
    1391             : // LazyStackGetter.  The first slot always stores a JS object: it's either the
    1392             : // JS wrapper of the nsIStackFrame or the actual reified stack representation.
    1393             : // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
    1394             : // reified the stack yet, or an UndefinedValue() otherwise.
    1395             : enum {
    1396             :   SLOT_STACKOBJ,
    1397             :   SLOT_RAW_STACK
    1398             : };
    1399             : 
    1400             : bool
    1401           0 : LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
    1402             : {
    1403           0 :   JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
    1404           0 :   JS::Rooted<JSObject*> callee(aCx, &args.callee());
    1405             : 
    1406           0 :   JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
    1407           0 :   if (v.isUndefined()) {
    1408             :     // Already reified.
    1409           0 :     args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
    1410           0 :     return true;
    1411             :   }
    1412             : 
    1413           0 :   nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
    1414           0 :   nsTArray<ConsoleStackEntry> reifiedStack;
    1415           0 :   nsresult rv = ReifyStack(aCx, stack, reifiedStack);
    1416           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1417           0 :     Throw(aCx, rv);
    1418           0 :     return false;
    1419             :   }
    1420             : 
    1421           0 :   JS::Rooted<JS::Value> stackVal(aCx);
    1422           0 :   if (NS_WARN_IF(!ToJSValue(aCx, reifiedStack, &stackVal))) {
    1423           0 :     return false;
    1424             :   }
    1425             : 
    1426           0 :   MOZ_ASSERT(stackVal.isObject());
    1427             : 
    1428           0 :   js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
    1429           0 :   js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
    1430             : 
    1431           0 :   args.rval().set(stackVal);
    1432           0 :   return true;
    1433             : }
    1434             : 
    1435             : void
    1436           1 : Console::ProcessCallData(JSContext* aCx, ConsoleCallData* aData,
    1437             :                          const Sequence<JS::Value>& aArguments)
    1438             : {
    1439           1 :   AssertIsOnMainThread();
    1440           1 :   MOZ_ASSERT(aData);
    1441             : 
    1442           2 :   JS::Rooted<JS::Value> eventValue(aCx);
    1443             : 
    1444             :   // We want to create a console event object and pass it to our
    1445             :   // nsIConsoleAPIStorage implementation.  We want to define some accessor
    1446             :   // properties on this object, and those will need to keep an nsIStackFrame
    1447             :   // alive.  But nsIStackFrame cannot be wrapped in an untrusted scope.  And
    1448             :   // further, passing untrusted objects to system code is likely to run afoul of
    1449             :   // Object Xrays.  So we want to wrap in a system-principal scope here.  But
    1450             :   // which one?  We could cheat and try to get the underlying JSObject* of
    1451             :   // mStorage, but that's a bit fragile.  Instead, we just use the junk scope,
    1452             :   // with explicit permission from the XPConnect module owner.  If you're
    1453             :   // tempted to do that anywhere else, talk to said module owner first.
    1454             : 
    1455             :   // aCx and aArguments are in the same compartment.
    1456           1 :   if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(aCx, aArguments,
    1457             :                                                               xpc::PrivilegedJunkScope(),
    1458             :                                                               &eventValue, aData))) {
    1459           0 :     return;
    1460             :   }
    1461             : 
    1462           1 :   if (!mStorage) {
    1463           1 :     mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
    1464             :   }
    1465             : 
    1466           1 :   if (!mStorage) {
    1467           0 :     NS_WARNING("Failed to get the ConsoleAPIStorage service.");
    1468           0 :     return;
    1469             :   }
    1470             : 
    1471           2 :   nsAutoString innerID, outerID;
    1472             : 
    1473           1 :   MOZ_ASSERT(aData->mIDType != ConsoleCallData::eUnknown);
    1474           1 :   if (aData->mIDType == ConsoleCallData::eString) {
    1475           0 :     outerID = aData->mOuterIDString;
    1476           0 :     innerID = aData->mInnerIDString;
    1477             :   } else {
    1478           1 :     MOZ_ASSERT(aData->mIDType == ConsoleCallData::eNumber);
    1479           1 :     outerID.AppendInt(aData->mOuterIDNumber);
    1480           1 :     innerID.AppendInt(aData->mInnerIDNumber);
    1481             :   }
    1482             : 
    1483           1 :   if (aData->mMethodName == MethodClear) {
    1484           0 :     DebugOnly<nsresult> rv = mStorage->ClearEvents(innerID);
    1485           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ClearEvents failed");
    1486             :   }
    1487             : 
    1488           1 :   if (NS_FAILED(mStorage->RecordEvent(innerID, outerID, eventValue))) {
    1489           0 :     NS_WARNING("Failed to record a console event.");
    1490             :   }
    1491             : }
    1492             : 
    1493             : bool
    1494           1 : Console::PopulateConsoleNotificationInTheTargetScope(JSContext* aCx,
    1495             :                                                      const Sequence<JS::Value>& aArguments,
    1496             :                                                      JSObject* aTargetScope,
    1497             :                                                      JS::MutableHandle<JS::Value> aEventValue,
    1498             :                                                      ConsoleCallData* aData)
    1499             : {
    1500           1 :   MOZ_ASSERT(aCx);
    1501           1 :   MOZ_ASSERT(aData);
    1502           1 :   MOZ_ASSERT(aTargetScope);
    1503             : 
    1504           2 :   JS::Rooted<JSObject*> targetScope(aCx, aTargetScope);
    1505             : 
    1506           2 :   ConsoleStackEntry frame;
    1507           1 :   if (aData->mTopStackFrame) {
    1508           1 :     frame = *aData->mTopStackFrame;
    1509             :   }
    1510             : 
    1511           2 :   ClearException ce(aCx);
    1512           2 :   RootedDictionary<ConsoleEvent> event(aCx);
    1513             : 
    1514             :   // Save the principal's OriginAttributes in the console event data
    1515             :   // so that we will be able to filter messages by origin attributes.
    1516           2 :   JS::Rooted<JS::Value> originAttributesValue(aCx);
    1517           1 :   if (ToJSValue(aCx, aData->mOriginAttributes, &originAttributesValue)) {
    1518           1 :     event.mOriginAttributes = originAttributesValue;
    1519             :   }
    1520             : 
    1521           1 :   event.mAddonId = aData->mAddonId;
    1522             : 
    1523           1 :   event.mID.Construct();
    1524           1 :   event.mInnerID.Construct();
    1525             : 
    1526           1 :   if (aData->mIDType == ConsoleCallData::eString) {
    1527           0 :     event.mID.Value().SetAsString() = aData->mOuterIDString;
    1528           0 :     event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
    1529           1 :   } else if (aData->mIDType == ConsoleCallData::eNumber) {
    1530           1 :     event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
    1531           1 :     event.mInnerID.Value().SetAsUnsignedLongLong() = aData->mInnerIDNumber;
    1532             :   } else {
    1533             :     // aData->mIDType can be eUnknown when we dispatch notifications via
    1534             :     // mConsoleEventNotifier.
    1535           0 :     event.mID.Value().SetAsUnsignedLongLong() = 0;
    1536           0 :     event.mInnerID.Value().SetAsUnsignedLongLong() = 0;
    1537             :   }
    1538             : 
    1539           1 :   event.mLevel = aData->mMethodString;
    1540           1 :   event.mFilename = frame.mFilename;
    1541             : 
    1542           2 :   nsCOMPtr<nsIURI> filenameURI;
    1543           2 :   nsAutoCString pass;
    1544           5 :   if (NS_IsMainThread() &&
    1545           5 :       NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI), frame.mFilename)) &&
    1546           4 :       NS_SUCCEEDED(filenameURI->GetPassword(pass)) && !pass.IsEmpty()) {
    1547           0 :     nsCOMPtr<nsISensitiveInfoHiddenURI> safeURI = do_QueryInterface(filenameURI);
    1548           0 :     nsAutoCString spec;
    1549           0 :     if (safeURI &&
    1550           0 :         NS_SUCCEEDED(safeURI->GetSensitiveInfoHiddenSpec(spec))) {
    1551           0 :       CopyUTF8toUTF16(spec, event.mFilename);
    1552             :     }
    1553             :   }
    1554             : 
    1555           1 :   event.mLineNumber = frame.mLineNumber;
    1556           1 :   event.mColumnNumber = frame.mColumnNumber;
    1557           1 :   event.mFunctionName = frame.mFunctionName;
    1558           1 :   event.mTimeStamp = aData->mTimeStamp;
    1559           1 :   event.mPrivate = !!aData->mOriginAttributes.mPrivateBrowsingId;
    1560             : 
    1561           1 :   switch (aData->mMethodName) {
    1562             :     case MethodLog:
    1563             :     case MethodInfo:
    1564             :     case MethodWarn:
    1565             :     case MethodError:
    1566             :     case MethodException:
    1567             :     case MethodDebug:
    1568             :     case MethodAssert:
    1569             :     case MethodGroup:
    1570             :     case MethodGroupCollapsed:
    1571           1 :       event.mArguments.Construct();
    1572           1 :       event.mStyles.Construct();
    1573           1 :       if (NS_WARN_IF(!ProcessArguments(aCx, aArguments,
    1574             :                                        event.mArguments.Value(),
    1575             :                                        event.mStyles.Value()))) {
    1576           0 :         return false;
    1577             :       }
    1578             : 
    1579           1 :       break;
    1580             : 
    1581             :     default:
    1582           0 :       event.mArguments.Construct();
    1583           0 :       if (NS_WARN_IF(!ArgumentsToValueList(aArguments,
    1584             :                                            event.mArguments.Value()))) {
    1585           0 :         return false;
    1586             :       }
    1587             :   }
    1588             : 
    1589           2 :   if (aData->mMethodName == MethodGroup ||
    1590           1 :       aData->mMethodName == MethodGroupCollapsed) {
    1591           0 :     ComposeAndStoreGroupName(aCx, event.mArguments.Value(), event.mGroupName);
    1592             :   }
    1593             : 
    1594           1 :   else if (aData->mMethodName == MethodGroupEnd) {
    1595           0 :     if (!UnstoreGroupName(event.mGroupName)) {
    1596           0 :       return false;
    1597             :     }
    1598             :   }
    1599             : 
    1600           1 :   else if (aData->mMethodName == MethodTime && !aArguments.IsEmpty()) {
    1601             :     event.mTimer = CreateStartTimerValue(aCx, aData->mStartTimerLabel,
    1602           0 :                                          aData->mStartTimerStatus);
    1603             :   }
    1604             : 
    1605           1 :   else if (aData->mMethodName == MethodTimeEnd && !aArguments.IsEmpty()) {
    1606             :     event.mTimer = CreateStopTimerValue(aCx, aData->mStopTimerLabel,
    1607             :                                         aData->mStopTimerDuration,
    1608           0 :                                         aData->mStopTimerStatus);
    1609             :   }
    1610             : 
    1611           1 :   else if (aData->mMethodName == MethodCount) {
    1612             :     event.mCounter = CreateCounterValue(aCx, aData->mCountLabel,
    1613           0 :                                         aData->mCountValue);
    1614             :   }
    1615             : 
    1616           2 :   JSAutoCompartment ac2(aCx, targetScope);
    1617             : 
    1618           1 :   if (NS_WARN_IF(!ToJSValue(aCx, event, aEventValue))) {
    1619           0 :     return false;
    1620             :   }
    1621             : 
    1622           2 :   JS::Rooted<JSObject*> eventObj(aCx, &aEventValue.toObject());
    1623           1 :   if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventObj,
    1624             :                                     JSPROP_ENUMERATE))) {
    1625           0 :     return false;
    1626             :   }
    1627             : 
    1628           1 :   if (ShouldIncludeStackTrace(aData->mMethodName)) {
    1629             :     // Now define the "stacktrace" property on eventObj.  There are two cases
    1630             :     // here.  Either we came from a worker and have a reified stack, or we want
    1631             :     // to define a getter that will lazily reify the stack.
    1632           1 :     if (aData->mReifiedStack) {
    1633           0 :       JS::Rooted<JS::Value> stacktrace(aCx);
    1634           0 :       if (NS_WARN_IF(!ToJSValue(aCx, *aData->mReifiedStack, &stacktrace)) ||
    1635           0 :           NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace", stacktrace,
    1636             :                                         JSPROP_ENUMERATE))) {
    1637           0 :         return false;
    1638             :       }
    1639             :     } else {
    1640           1 :       JSFunction* fun = js::NewFunctionWithReserved(aCx, LazyStackGetter, 0, 0,
    1641           1 :                                                     "stacktrace");
    1642           1 :       if (NS_WARN_IF(!fun)) {
    1643           0 :         return false;
    1644             :       }
    1645             : 
    1646           2 :       JS::Rooted<JSObject*> funObj(aCx, JS_GetFunctionObject(fun));
    1647             : 
    1648             :       // We want to store our stack in the function and have it stay alive.  But
    1649             :       // we also need sane access to the C++ nsIStackFrame.  So store both a JS
    1650             :       // wrapper and the raw pointer: the former will keep the latter alive.
    1651           2 :       JS::Rooted<JS::Value> stackVal(aCx);
    1652           3 :       nsresult rv = nsContentUtils::WrapNative(aCx, aData->mStack,
    1653           2 :                                                &stackVal);
    1654           1 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1655           0 :         return false;
    1656             :       }
    1657             : 
    1658           1 :       js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
    1659           1 :       js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
    1660           2 :                                     JS::PrivateValue(aData->mStack.get()));
    1661             : 
    1662           1 :       if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace",
    1663             :                                         JS::UndefinedHandleValue,
    1664             :                                         JSPROP_ENUMERATE | JSPROP_SHARED |
    1665             :                                         JSPROP_GETTER | JSPROP_SETTER,
    1666             :                                         JS_DATA_TO_FUNC_PTR(JSNative, funObj.get()),
    1667             :                                         nullptr))) {
    1668           0 :         return false;
    1669             :       }
    1670             :     }
    1671             :   }
    1672             : 
    1673           1 :   return true;
    1674             : }
    1675             : 
    1676             : namespace {
    1677             : 
    1678             : // Helper method for ProcessArguments. Flushes output, if non-empty, to aSequence.
    1679             : bool
    1680           0 : FlushOutput(JSContext* aCx, Sequence<JS::Value>& aSequence, nsString &aOutput)
    1681             : {
    1682           0 :   if (!aOutput.IsEmpty()) {
    1683           0 :     JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
    1684             :                                                        aOutput.get(),
    1685           0 :                                                        aOutput.Length()));
    1686           0 :     if (NS_WARN_IF(!str)) {
    1687           0 :       return false;
    1688             :     }
    1689             : 
    1690           0 :     if (NS_WARN_IF(!aSequence.AppendElement(JS::StringValue(str), fallible))) {
    1691           0 :       return false;
    1692             :     }
    1693             : 
    1694           0 :     aOutput.Truncate();
    1695             :   }
    1696             : 
    1697           0 :   return true;
    1698             : }
    1699             : 
    1700             : } // namespace
    1701             : 
    1702             : bool
    1703           1 : Console::ProcessArguments(JSContext* aCx,
    1704             :                           const Sequence<JS::Value>& aData,
    1705             :                           Sequence<JS::Value>& aSequence,
    1706             :                           Sequence<nsString>& aStyles) const
    1707             : {
    1708           1 :   if (aData.IsEmpty()) {
    1709           0 :     return true;
    1710             :   }
    1711             : 
    1712           1 :   if (aData.Length() == 1 || !aData[0].isString()) {
    1713           1 :     return ArgumentsToValueList(aData, aSequence);
    1714             :   }
    1715             : 
    1716           0 :   JS::Rooted<JS::Value> format(aCx, aData[0]);
    1717           0 :   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, format));
    1718           0 :   if (NS_WARN_IF(!jsString)) {
    1719           0 :     return false;
    1720             :   }
    1721             : 
    1722           0 :   nsAutoJSString string;
    1723           0 :   if (NS_WARN_IF(!string.init(aCx, jsString))) {
    1724           0 :     return false;
    1725             :   }
    1726             : 
    1727           0 :   nsString::const_iterator start, end;
    1728           0 :   string.BeginReading(start);
    1729           0 :   string.EndReading(end);
    1730             : 
    1731           0 :   nsString output;
    1732           0 :   uint32_t index = 1;
    1733             : 
    1734           0 :   while (start != end) {
    1735           0 :     if (*start != '%') {
    1736           0 :       output.Append(*start);
    1737           0 :       ++start;
    1738           0 :       continue;
    1739             :     }
    1740             : 
    1741           0 :     ++start;
    1742           0 :     if (start == end) {
    1743           0 :       output.Append('%');
    1744           0 :       break;
    1745             :     }
    1746             : 
    1747           0 :     if (*start == '%') {
    1748           0 :       output.Append(*start);
    1749           0 :       ++start;
    1750           0 :       continue;
    1751             :     }
    1752             : 
    1753           0 :     nsAutoString tmp;
    1754           0 :     tmp.Append('%');
    1755             : 
    1756           0 :     int32_t integer = -1;
    1757           0 :     int32_t mantissa = -1;
    1758             : 
    1759             :     // Let's parse %<number>.<number> for %d and %f
    1760           0 :     if (*start >= '0' && *start <= '9') {
    1761           0 :       integer = 0;
    1762             : 
    1763           0 :       do {
    1764           0 :         integer = integer * 10 + *start - '0';
    1765           0 :         tmp.Append(*start);
    1766           0 :         ++start;
    1767           0 :       } while (*start >= '0' && *start <= '9' && start != end);
    1768             :     }
    1769             : 
    1770           0 :     if (start == end) {
    1771           0 :       output.Append(tmp);
    1772           0 :       break;
    1773             :     }
    1774             : 
    1775           0 :     if (*start == '.') {
    1776           0 :       tmp.Append(*start);
    1777           0 :       ++start;
    1778             : 
    1779           0 :       if (start == end) {
    1780           0 :         output.Append(tmp);
    1781           0 :         break;
    1782             :       }
    1783             : 
    1784             :       // '.' must be followed by a number.
    1785           0 :       if (*start < '0' || *start > '9') {
    1786           0 :         output.Append(tmp);
    1787           0 :         continue;
    1788             :       }
    1789             : 
    1790           0 :       mantissa = 0;
    1791             : 
    1792           0 :       do {
    1793           0 :         mantissa = mantissa * 10 + *start - '0';
    1794           0 :         tmp.Append(*start);
    1795           0 :         ++start;
    1796           0 :       } while (*start >= '0' && *start <= '9' && start != end);
    1797             : 
    1798           0 :       if (start == end) {
    1799           0 :         output.Append(tmp);
    1800           0 :         break;
    1801             :       }
    1802             :     }
    1803             : 
    1804           0 :     char ch = *start;
    1805           0 :     tmp.Append(ch);
    1806           0 :     ++start;
    1807             : 
    1808           0 :     switch (ch) {
    1809             :       case 'o':
    1810             :       case 'O':
    1811             :       {
    1812           0 :         if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
    1813           0 :           return false;
    1814             :         }
    1815             : 
    1816           0 :         JS::Rooted<JS::Value> v(aCx);
    1817           0 :         if (index < aData.Length()) {
    1818           0 :           v = aData[index++];
    1819             :         }
    1820             : 
    1821           0 :         if (NS_WARN_IF(!aSequence.AppendElement(v, fallible))) {
    1822           0 :           return false;
    1823             :         }
    1824             : 
    1825           0 :         break;
    1826             :       }
    1827             : 
    1828             :       case 'c':
    1829             :       {
    1830             :         // If there isn't any output but there's already a style, then
    1831             :         // discard the previous style and use the next one instead.
    1832           0 :         if (output.IsEmpty() && !aStyles.IsEmpty()) {
    1833           0 :           aStyles.TruncateLength(aStyles.Length() - 1);
    1834             :         }
    1835             : 
    1836           0 :         if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
    1837           0 :           return false;
    1838             :         }
    1839             : 
    1840           0 :         if (index < aData.Length()) {
    1841           0 :           JS::Rooted<JS::Value> v(aCx, aData[index++]);
    1842           0 :           JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, v));
    1843           0 :           if (NS_WARN_IF(!jsString)) {
    1844           0 :             return false;
    1845             :           }
    1846             : 
    1847           0 :           int32_t diff = aSequence.Length() - aStyles.Length();
    1848           0 :           if (diff > 0) {
    1849           0 :             for (int32_t i = 0; i < diff; i++) {
    1850           0 :               if (NS_WARN_IF(!aStyles.AppendElement(NullString(), fallible))) {
    1851           0 :                 return false;
    1852             :               }
    1853             :             }
    1854             :           }
    1855             : 
    1856           0 :           nsAutoJSString string;
    1857           0 :           if (NS_WARN_IF(!string.init(aCx, jsString))) {
    1858           0 :             return false;
    1859             :           }
    1860             : 
    1861           0 :           if (NS_WARN_IF(!aStyles.AppendElement(string, fallible))) {
    1862           0 :             return false;
    1863             :           }
    1864             :         }
    1865           0 :         break;
    1866             :       }
    1867             : 
    1868             :       case 's':
    1869           0 :         if (index < aData.Length()) {
    1870           0 :           JS::Rooted<JS::Value> value(aCx, aData[index++]);
    1871           0 :           JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
    1872           0 :           if (NS_WARN_IF(!jsString)) {
    1873           0 :             return false;
    1874             :           }
    1875             : 
    1876           0 :           nsAutoJSString v;
    1877           0 :           if (NS_WARN_IF(!v.init(aCx, jsString))) {
    1878           0 :             return false;
    1879             :           }
    1880             : 
    1881           0 :           output.Append(v);
    1882             :         }
    1883           0 :         break;
    1884             : 
    1885             :       case 'd':
    1886             :       case 'i':
    1887           0 :         if (index < aData.Length()) {
    1888           0 :           JS::Rooted<JS::Value> value(aCx, aData[index++]);
    1889             : 
    1890             :           int32_t v;
    1891           0 :           if (NS_WARN_IF(!JS::ToInt32(aCx, value, &v))) {
    1892           0 :             return false;
    1893             :           }
    1894             : 
    1895           0 :           nsCString format;
    1896           0 :           MakeFormatString(format, integer, mantissa, 'd');
    1897           0 :           output.AppendPrintf(format.get(), v);
    1898             :         }
    1899           0 :         break;
    1900             : 
    1901             :       case 'f':
    1902           0 :         if (index < aData.Length()) {
    1903           0 :           JS::Rooted<JS::Value> value(aCx, aData[index++]);
    1904             : 
    1905             :           double v;
    1906           0 :           if (NS_WARN_IF(!JS::ToNumber(aCx, value, &v))) {
    1907           0 :             return false;
    1908             :           }
    1909             : 
    1910             :           // nspr returns "nan", but we want to expose it as "NaN"
    1911           0 :           if (std::isnan(v)) {
    1912           0 :             output.AppendFloat(v);
    1913             :           } else {
    1914           0 :             nsCString format;
    1915           0 :             MakeFormatString(format, integer, mantissa, 'f');
    1916           0 :             output.AppendPrintf(format.get(), v);
    1917             :           }
    1918             :         }
    1919           0 :         break;
    1920             : 
    1921             :       default:
    1922           0 :         output.Append(tmp);
    1923           0 :         break;
    1924             :     }
    1925             :   }
    1926             : 
    1927           0 :   if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
    1928           0 :     return false;
    1929             :   }
    1930             : 
    1931             :   // Discard trailing style element if there is no output to apply it to.
    1932           0 :   if (aStyles.Length() > aSequence.Length()) {
    1933           0 :     aStyles.TruncateLength(aSequence.Length());
    1934             :   }
    1935             : 
    1936             :   // The rest of the array, if unused by the format string.
    1937           0 :   for (; index < aData.Length(); ++index) {
    1938           0 :     if (NS_WARN_IF(!aSequence.AppendElement(aData[index], fallible))) {
    1939           0 :       return false;
    1940             :     }
    1941             :   }
    1942             : 
    1943           0 :   return true;
    1944             : }
    1945             : 
    1946             : void
    1947           0 : Console::MakeFormatString(nsCString& aFormat, int32_t aInteger,
    1948             :                           int32_t aMantissa, char aCh) const
    1949             : {
    1950           0 :   aFormat.Append('%');
    1951           0 :   if (aInteger >= 0) {
    1952           0 :     aFormat.AppendInt(aInteger);
    1953             :   }
    1954             : 
    1955           0 :   if (aMantissa >= 0) {
    1956           0 :     aFormat.Append('.');
    1957           0 :     aFormat.AppendInt(aMantissa);
    1958             :   }
    1959             : 
    1960           0 :   aFormat.Append(aCh);
    1961           0 : }
    1962             : 
    1963             : void
    1964           0 : Console::ComposeAndStoreGroupName(JSContext* aCx,
    1965             :                                   const Sequence<JS::Value>& aData,
    1966             :                                   nsAString& aName)
    1967             : {
    1968           0 :   for (uint32_t i = 0; i < aData.Length(); ++i) {
    1969           0 :     if (i != 0) {
    1970           0 :       aName.AppendASCII(" ");
    1971             :     }
    1972             : 
    1973           0 :     JS::Rooted<JS::Value> value(aCx, aData[i]);
    1974           0 :     JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
    1975           0 :     if (!jsString) {
    1976           0 :       return;
    1977             :     }
    1978             : 
    1979           0 :     nsAutoJSString string;
    1980           0 :     if (!string.init(aCx, jsString)) {
    1981           0 :       return;
    1982             :     }
    1983             : 
    1984           0 :     aName.Append(string);
    1985             :   }
    1986             : 
    1987           0 :   mGroupStack.AppendElement(aName);
    1988             : }
    1989             : 
    1990             : bool
    1991           0 : Console::UnstoreGroupName(nsAString& aName)
    1992             : {
    1993           0 :   if (mGroupStack.IsEmpty()) {
    1994           0 :     return false;
    1995             :   }
    1996             : 
    1997           0 :   uint32_t pos = mGroupStack.Length() - 1;
    1998           0 :   aName = mGroupStack[pos];
    1999           0 :   mGroupStack.RemoveElementAt(pos);
    2000           0 :   return true;
    2001             : }
    2002             : 
    2003             : Console::TimerStatus
    2004           0 : Console::StartTimer(JSContext* aCx, const JS::Value& aName,
    2005             :                     DOMHighResTimeStamp aTimestamp,
    2006             :                     nsAString& aTimerLabel,
    2007             :                     DOMHighResTimeStamp* aTimerValue)
    2008             : {
    2009           0 :   AssertIsOnOwningThread();
    2010           0 :   MOZ_ASSERT(aTimerValue);
    2011             : 
    2012           0 :   *aTimerValue = 0;
    2013             : 
    2014           0 :   if (NS_WARN_IF(mTimerRegistry.Count() >= MAX_PAGE_TIMERS)) {
    2015           0 :     return eTimerMaxReached;
    2016             :   }
    2017             : 
    2018           0 :   JS::Rooted<JS::Value> name(aCx, aName);
    2019           0 :   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
    2020           0 :   if (NS_WARN_IF(!jsString)) {
    2021           0 :     return eTimerJSException;
    2022             :   }
    2023             : 
    2024           0 :   nsAutoJSString label;
    2025           0 :   if (NS_WARN_IF(!label.init(aCx, jsString))) {
    2026           0 :     return eTimerJSException;
    2027             :   }
    2028             : 
    2029           0 :   aTimerLabel = label;
    2030             : 
    2031           0 :   auto entry = mTimerRegistry.LookupForAdd(label);
    2032           0 :   if (entry) {
    2033           0 :     return eTimerAlreadyExists;
    2034             :   }
    2035           0 :   entry.OrInsert([&aTimestamp](){ return aTimestamp; });
    2036             : 
    2037           0 :   *aTimerValue = aTimestamp;
    2038           0 :   return eTimerDone;
    2039             : }
    2040             : 
    2041             : JS::Value
    2042           0 : Console::CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
    2043             :                                TimerStatus aTimerStatus) const
    2044             : {
    2045           0 :   MOZ_ASSERT(aTimerStatus != eTimerUnknown);
    2046             : 
    2047           0 :   if (aTimerStatus != eTimerDone) {
    2048           0 :     return CreateTimerError(aCx, aTimerLabel, aTimerStatus);
    2049             :   }
    2050             : 
    2051           0 :   RootedDictionary<ConsoleTimerStart> timer(aCx);
    2052             : 
    2053           0 :   timer.mName = aTimerLabel;
    2054             : 
    2055           0 :   JS::Rooted<JS::Value> value(aCx);
    2056           0 :   if (!ToJSValue(aCx, timer, &value)) {
    2057           0 :     return JS::UndefinedValue();
    2058             :   }
    2059             : 
    2060           0 :   return value;
    2061             : }
    2062             : 
    2063             : Console::TimerStatus
    2064           0 : Console::StopTimer(JSContext* aCx, const JS::Value& aName,
    2065             :                    DOMHighResTimeStamp aTimestamp,
    2066             :                    nsAString& aTimerLabel,
    2067             :                    double* aTimerDuration)
    2068             : {
    2069           0 :   AssertIsOnOwningThread();
    2070           0 :   MOZ_ASSERT(aTimerDuration);
    2071             : 
    2072           0 :   *aTimerDuration = 0;
    2073             : 
    2074           0 :   JS::Rooted<JS::Value> name(aCx, aName);
    2075           0 :   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
    2076           0 :   if (NS_WARN_IF(!jsString)) {
    2077           0 :     return eTimerJSException;
    2078             :   }
    2079             : 
    2080           0 :   nsAutoJSString key;
    2081           0 :   if (NS_WARN_IF(!key.init(aCx, jsString))) {
    2082           0 :     return eTimerJSException;
    2083             :   }
    2084             : 
    2085           0 :   aTimerLabel = key;
    2086             : 
    2087           0 :   DOMHighResTimeStamp value = 0;
    2088           0 :   if (!mTimerRegistry.Remove(key, &value)) {
    2089           0 :     NS_WARNING("mTimerRegistry entry not found");
    2090           0 :     return eTimerDoesntExist;
    2091             :   }
    2092             : 
    2093           0 :   *aTimerDuration = aTimestamp - value;
    2094           0 :   return eTimerDone;
    2095             : }
    2096             : 
    2097             : JS::Value
    2098           0 : Console::CreateStopTimerValue(JSContext* aCx, const nsAString& aLabel,
    2099             :                               double aDuration, TimerStatus aStatus) const
    2100             : {
    2101           0 :   if (aStatus != eTimerDone) {
    2102           0 :     return CreateTimerError(aCx, aLabel, aStatus);
    2103             :   }
    2104             : 
    2105           0 :   RootedDictionary<ConsoleTimerEnd> timer(aCx);
    2106           0 :   timer.mName = aLabel;
    2107           0 :   timer.mDuration = aDuration;
    2108             : 
    2109           0 :   JS::Rooted<JS::Value> value(aCx);
    2110           0 :   if (!ToJSValue(aCx, timer, &value)) {
    2111           0 :     return JS::UndefinedValue();
    2112             :   }
    2113             : 
    2114           0 :   return value;
    2115             : }
    2116             : 
    2117             : JS::Value
    2118           0 : Console::CreateTimerError(JSContext* aCx, const nsAString& aLabel,
    2119             :                           TimerStatus aStatus) const
    2120             : {
    2121           0 :   MOZ_ASSERT(aStatus != eTimerUnknown && aStatus != eTimerDone);
    2122             : 
    2123           0 :   RootedDictionary<ConsoleTimerError> error(aCx);
    2124             : 
    2125           0 :   error.mName = aLabel;
    2126             : 
    2127           0 :   switch (aStatus) {
    2128             :   case eTimerAlreadyExists:
    2129           0 :     error.mError.AssignLiteral("timerAlreadyExists");
    2130           0 :     break;
    2131             : 
    2132             :   case eTimerDoesntExist:
    2133           0 :     error.mError.AssignLiteral("timerDoesntExist");
    2134           0 :     break;
    2135             : 
    2136             :   case eTimerJSException:
    2137           0 :     error.mError.AssignLiteral("timerJSError");
    2138           0 :     break;
    2139             : 
    2140             :   case eTimerMaxReached:
    2141           0 :     error.mError.AssignLiteral("maxTimersExceeded");
    2142           0 :     break;
    2143             : 
    2144             :   default:
    2145           0 :     MOZ_CRASH("Unsupported status");
    2146             :     break;
    2147             :   }
    2148             : 
    2149           0 :   JS::Rooted<JS::Value> value(aCx);
    2150           0 :   if (!ToJSValue(aCx, error, &value)) {
    2151           0 :     return JS::UndefinedValue();
    2152             :   }
    2153             : 
    2154           0 :   return value;
    2155             : }
    2156             : 
    2157             : bool
    2158           1 : Console::ArgumentsToValueList(const Sequence<JS::Value>& aData,
    2159             :                               Sequence<JS::Value>& aSequence) const
    2160             : {
    2161           2 :   for (uint32_t i = 0; i < aData.Length(); ++i) {
    2162           1 :     if (NS_WARN_IF(!aSequence.AppendElement(aData[i], fallible))) {
    2163           0 :       return false;
    2164             :     }
    2165             :   }
    2166             : 
    2167           1 :   return true;
    2168             : }
    2169             : 
    2170             : uint32_t
    2171           0 : Console::IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aArguments,
    2172             :                          nsAString& aCountLabel)
    2173             : {
    2174           0 :   AssertIsOnOwningThread();
    2175             : 
    2176           0 :   ClearException ce(aCx);
    2177             : 
    2178           0 :   MOZ_ASSERT(!aArguments.IsEmpty());
    2179             : 
    2180           0 :   JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
    2181           0 :   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
    2182           0 :   if (!jsString) {
    2183           0 :     return 0; // We cannot continue.
    2184             :   }
    2185             : 
    2186           0 :   nsAutoJSString string;
    2187           0 :   if (!string.init(aCx, jsString)) {
    2188           0 :     return 0; // We cannot continue.
    2189             :   }
    2190             : 
    2191           0 :   aCountLabel = string;
    2192             : 
    2193           0 :   const bool maxCountersReached = mCounterRegistry.Count() >= MAX_PAGE_COUNTERS;
    2194           0 :   auto entry = mCounterRegistry.LookupForAdd(aCountLabel);
    2195           0 :   if (entry) {
    2196           0 :     ++entry.Data();
    2197             :   } else {
    2198           0 :     entry.OrInsert([](){ return 1; });
    2199           0 :     if (maxCountersReached) {
    2200             :       // oops, we speculatively added an entry even though we shouldn't
    2201           0 :       mCounterRegistry.Remove(aCountLabel);
    2202           0 :       return MAX_PAGE_COUNTERS;
    2203             :     }
    2204             :   }
    2205           0 :   return entry.Data();
    2206             : }
    2207             : 
    2208             : JS::Value
    2209           0 : Console::CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
    2210             :                             uint32_t aCountValue) const
    2211             : {
    2212           0 :   ClearException ce(aCx);
    2213             : 
    2214           0 :   if (aCountValue == MAX_PAGE_COUNTERS) {
    2215           0 :     RootedDictionary<ConsoleCounterError> error(aCx);
    2216             : 
    2217           0 :     JS::Rooted<JS::Value> value(aCx);
    2218           0 :     if (!ToJSValue(aCx, error, &value)) {
    2219           0 :       return JS::UndefinedValue();
    2220             :     }
    2221             : 
    2222           0 :     return value;
    2223             :   }
    2224             : 
    2225           0 :   RootedDictionary<ConsoleCounter> data(aCx);
    2226           0 :   data.mLabel = aCountLabel;
    2227           0 :   data.mCount = aCountValue;
    2228             : 
    2229           0 :   JS::Rooted<JS::Value> value(aCx);
    2230           0 :   if (!ToJSValue(aCx, data, &value)) {
    2231           0 :     return JS::UndefinedValue();
    2232             :   }
    2233             : 
    2234           0 :   return value;
    2235             : }
    2236             : 
    2237             : bool
    2238           2 : Console::ShouldIncludeStackTrace(MethodName aMethodName) const
    2239             : {
    2240           2 :   switch (aMethodName) {
    2241             :     case MethodError:
    2242             :     case MethodException:
    2243             :     case MethodAssert:
    2244             :     case MethodTrace:
    2245           2 :       return true;
    2246             :     default:
    2247           0 :       return false;
    2248             :   }
    2249             : }
    2250             : 
    2251             : JSObject*
    2252           0 : Console::GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal)
    2253             : {
    2254           0 :   AssertIsOnMainThread();
    2255             : 
    2256           0 :   if (!mSandbox) {
    2257           0 :     nsIXPConnect* xpc = nsContentUtils::XPConnect();
    2258           0 :     MOZ_ASSERT(xpc, "This should never be null!");
    2259             : 
    2260           0 :     JS::Rooted<JSObject*> sandbox(aCx);
    2261           0 :     nsresult rv = xpc->CreateSandbox(aCx, aPrincipal, sandbox.address());
    2262           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2263           0 :       return nullptr;
    2264             :     }
    2265             : 
    2266           0 :     mSandbox = new JSObjectHolder(aCx, sandbox);
    2267             :   }
    2268             : 
    2269           0 :   return mSandbox->GetJSObject();
    2270             : }
    2271             : 
    2272             : void
    2273           1 : Console::StoreCallData(ConsoleCallData* aCallData)
    2274             : {
    2275           1 :   AssertIsOnOwningThread();
    2276             : 
    2277           1 :   MOZ_ASSERT(aCallData);
    2278           1 :   MOZ_ASSERT(!mCallDataStorage.Contains(aCallData));
    2279           1 :   MOZ_ASSERT(!mCallDataStoragePending.Contains(aCallData));
    2280             : 
    2281           1 :   mCallDataStorage.AppendElement(aCallData);
    2282             : 
    2283           1 :   if (mCallDataStorage.Length() > STORAGE_MAX_EVENTS) {
    2284           0 :     RefPtr<ConsoleCallData> callData = mCallDataStorage[0];
    2285           0 :     mCallDataStorage.RemoveElementAt(0);
    2286             : 
    2287           0 :     MOZ_ASSERT(callData->mStatus != ConsoleCallData::eToBeDeleted);
    2288             : 
    2289             :     // We cannot delete this object now because we have to trace its JSValues
    2290             :     // until the pending operation (ConsoleCallDataRunnable) is completed.
    2291           0 :     if (callData->mStatus == ConsoleCallData::eInUse) {
    2292           0 :       callData->mStatus = ConsoleCallData::eToBeDeleted;
    2293           0 :       mCallDataStoragePending.AppendElement(callData);
    2294             :     }
    2295             :   }
    2296           1 : }
    2297             : 
    2298             : void
    2299           1 : Console::UnstoreCallData(ConsoleCallData* aCallData)
    2300             : {
    2301           1 :   AssertIsOnOwningThread();
    2302             : 
    2303           1 :   MOZ_ASSERT(aCallData);
    2304             : 
    2305           1 :   MOZ_ASSERT(!mCallDataStoragePending.Contains(aCallData));
    2306             : 
    2307             :   // It can be that mCallDataStorage has been already cleaned in case the
    2308             :   // processing of the argument of some Console methods triggers the
    2309             :   // window.close().
    2310             : 
    2311           1 :   mCallDataStorage.RemoveElement(aCallData);
    2312           1 : }
    2313             : 
    2314             : void
    2315           0 : Console::ReleaseCallData(ConsoleCallData* aCallData)
    2316             : {
    2317           0 :   AssertIsOnOwningThread();
    2318           0 :   MOZ_ASSERT(aCallData);
    2319           0 :   MOZ_ASSERT(aCallData->mStatus == ConsoleCallData::eToBeDeleted);
    2320           0 :   MOZ_ASSERT(mCallDataStoragePending.Contains(aCallData));
    2321             : 
    2322           0 :   mCallDataStoragePending.RemoveElement(aCallData);
    2323           0 : }
    2324             : 
    2325             : void
    2326           0 : Console::NotifyHandler(JSContext* aCx, const Sequence<JS::Value>& aArguments,
    2327             :                        ConsoleCallData* aCallData)
    2328             : {
    2329           0 :   AssertIsOnOwningThread();
    2330           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2331           0 :   MOZ_ASSERT(aCallData);
    2332             : 
    2333           0 :   if (!mConsoleEventNotifier) {
    2334           0 :     return;
    2335             :   }
    2336             : 
    2337           0 :   JS::Rooted<JS::Value> value(aCx);
    2338             : 
    2339           0 :   JS::Rooted<JSObject*> callable(aCx, mConsoleEventNotifier->CallableOrNull());
    2340           0 :   if (NS_WARN_IF(!callable)) {
    2341           0 :     return;
    2342             :   }
    2343             : 
    2344             :   // aCx and aArguments are in the same compartment because this method is
    2345             :   // called directly when a Console.something() runs.
    2346             :   // mConsoleEventNotifier->Callable() is the scope where value will be sent to.
    2347           0 :   if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(aCx, aArguments,
    2348             :                                                               callable,
    2349             :                                                               &value,
    2350             :                                                               aCallData))) {
    2351           0 :     return;
    2352             :   }
    2353             : 
    2354           0 :   JS::Rooted<JS::Value> ignored(aCx);
    2355           0 :   mConsoleEventNotifier->Call(value, &ignored);
    2356             : }
    2357             : 
    2358             : void
    2359           0 : Console::RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
    2360             :                                ErrorResult& aRv)
    2361             : {
    2362           0 :   AssertIsOnOwningThread();
    2363             : 
    2364             :   // We don't want to expose this functionality to main-thread yet.
    2365           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2366             : 
    2367           0 :   JS::Rooted<JSObject*> targetScope(aCx, JS::CurrentGlobalOrNull(aCx));
    2368             : 
    2369           0 :   for (uint32_t i = 0; i < mCallDataStorage.Length(); ++i) {
    2370           0 :     JS::Rooted<JS::Value> value(aCx);
    2371             : 
    2372           0 :     JS::Rooted<JSObject*> sequenceScope(aCx, mCallDataStorage[i]->mGlobal);
    2373           0 :     JSAutoCompartment ac(aCx, sequenceScope);
    2374             : 
    2375           0 :     Sequence<JS::Value> sequence;
    2376           0 :     SequenceRooter<JS::Value> arguments(aCx, &sequence);
    2377             : 
    2378           0 :     if (!mCallDataStorage[i]->PopulateArgumentsSequence(sequence)) {
    2379           0 :       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    2380           0 :       return;
    2381             :     }
    2382             : 
    2383             :     // Here we have aCx and sequence in the same compartment.
    2384             :     // targetScope is the destination scope and value will be populated in its
    2385             :     // compartment.
    2386           0 :     if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(aCx, sequence,
    2387             :                                                                 targetScope,
    2388             :                                                                 &value,
    2389             :                                                                 mCallDataStorage[i]))) {
    2390           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2391           0 :       return;
    2392             :     }
    2393             : 
    2394           0 :     aEvents.AppendElement(value);
    2395             :   }
    2396             : }
    2397             : 
    2398             : void
    2399           0 : Console::SetConsoleEventHandler(AnyCallback* aHandler)
    2400             : {
    2401           0 :   AssertIsOnOwningThread();
    2402             : 
    2403             :   // We don't want to expose this functionality to main-thread yet.
    2404           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2405             : 
    2406           0 :   mConsoleEventNotifier = aHandler;
    2407           0 : }
    2408             : 
    2409             : void
    2410           5 : Console::AssertIsOnOwningThread() const
    2411             : {
    2412           5 :   NS_ASSERT_OWNINGTHREAD(Console);
    2413           5 : }
    2414             : 
    2415             : bool
    2416           1 : Console::IsShuttingDown() const
    2417             : {
    2418           1 :   MOZ_ASSERT(mStatus != eUnknown);
    2419           1 :   return mStatus == eShuttingDown;
    2420             : }
    2421             : 
    2422             : /* static */ already_AddRefed<Console>
    2423           1 : Console::GetConsole(const GlobalObject& aGlobal)
    2424             : {
    2425           2 :   ErrorResult rv;
    2426           2 :   RefPtr<Console> console = GetConsoleInternal(aGlobal, rv);
    2427           1 :   if (NS_WARN_IF(rv.Failed()) || !console) {
    2428           0 :     rv.SuppressException();
    2429           0 :     return nullptr;
    2430             :   }
    2431             : 
    2432           1 :   console->AssertIsOnOwningThread();
    2433             : 
    2434           1 :   if (console->IsShuttingDown()) {
    2435           0 :     return nullptr;
    2436             :   }
    2437             : 
    2438           1 :   return console.forget();
    2439             : }
    2440             : 
    2441             : /* static */ Console*
    2442           1 : Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
    2443             : {
    2444             :   // Worklet
    2445           1 :   if (NS_IsMainThread()) {
    2446             :     nsCOMPtr<WorkletGlobalScope> workletScope =
    2447           2 :       do_QueryInterface(aGlobal.GetAsSupports());
    2448           1 :     if (workletScope) {
    2449           0 :       return workletScope->GetConsole(aRv);
    2450             :     }
    2451             :   }
    2452             : 
    2453             :   // Window
    2454           1 :   if (NS_IsMainThread()) {
    2455             :     nsCOMPtr<nsPIDOMWindowInner> innerWindow =
    2456           2 :       do_QueryInterface(aGlobal.GetAsSupports());
    2457           1 :     if (NS_WARN_IF(!innerWindow)) {
    2458           0 :       return nullptr;
    2459             :     }
    2460             : 
    2461           1 :     nsGlobalWindow* window = nsGlobalWindow::Cast(innerWindow);
    2462           1 :     return window->GetConsole(aRv);
    2463             :   }
    2464             : 
    2465             :   // Workers
    2466           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2467             : 
    2468           0 :   JSContext* cx = aGlobal.Context();
    2469           0 :   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
    2470           0 :   MOZ_ASSERT(workerPrivate);
    2471             : 
    2472             :   nsCOMPtr<nsIGlobalObject> global =
    2473           0 :     do_QueryInterface(aGlobal.GetAsSupports());
    2474           0 :   if (NS_WARN_IF(!global)) {
    2475           0 :     return nullptr;
    2476             :   }
    2477             : 
    2478           0 :   WorkerGlobalScope* scope = workerPrivate->GlobalScope();
    2479           0 :   MOZ_ASSERT(scope);
    2480             : 
    2481             :   // Normal worker scope.
    2482           0 :   if (scope == global) {
    2483           0 :     return scope->GetConsole(aRv);
    2484             :   }
    2485             : 
    2486             :   // Debugger worker scope
    2487             :   else {
    2488             :     WorkerDebuggerGlobalScope* debuggerScope =
    2489           0 :       workerPrivate->DebuggerGlobalScope();
    2490           0 :     MOZ_ASSERT(debuggerScope);
    2491           0 :     MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
    2492             : 
    2493           0 :     return debuggerScope->GetConsole(aRv);
    2494             :   }
    2495             : }
    2496             : 
    2497             : } // namespace dom
    2498             : } // namespace mozilla

Generated by: LCOV version 1.13