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/. */
7 #include "mozilla/dom/Console.h"
8 #include "mozilla/dom/ConsoleInstance.h"
9 #include "mozilla/dom/ConsoleBinding.h"
10 #include "ConsoleCommon.h"
12 #include "js/Array.h" // JS::GetArrayLength, JS::NewArrayObject
13 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineProperty, JS_GetElement
14 #include "mozilla/dom/BlobBinding.h"
15 #include "mozilla/dom/BlobImpl.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/ElementBinding.h"
18 #include "mozilla/dom/Exceptions.h"
19 #include "mozilla/dom/File.h"
20 #include "mozilla/dom/FunctionBinding.h"
21 #include "mozilla/dom/Performance.h"
22 #include "mozilla/dom/PromiseBinding.h"
23 #include "mozilla/dom/ScriptSettings.h"
24 #include "mozilla/dom/StructuredCloneHolder.h"
25 #include "mozilla/dom/ToJSValue.h"
26 #include "mozilla/dom/WorkerRunnable.h"
27 #include "mozilla/dom/WorkerScope.h"
28 #include "mozilla/dom/WorkletGlobalScope.h"
29 #include "mozilla/dom/WorkletImpl.h"
30 #include "mozilla/dom/WorkletThread.h"
31 #include "mozilla/dom/RootedDictionary.h"
32 #include "mozilla/BasePrincipal.h"
33 #include "mozilla/HoldDropJSObjects.h"
34 #include "mozilla/JSObjectHolder.h"
35 #include "mozilla/Maybe.h"
36 #include "mozilla/Mutex.h"
37 #include "mozilla/Preferences.h"
38 #include "mozilla/StaticPrefs_devtools.h"
39 #include "mozilla/StaticPrefs_dom.h"
40 #include "nsCycleCollectionParticipant.h"
41 #include "nsDOMNavigationTiming.h"
42 #include "nsGlobalWindowInner.h"
43 #include "nsJSUtils.h"
44 #include "nsNetUtil.h"
45 #include "xpcpublic.h"
46 #include "nsContentUtils.h"
47 #include "nsDocShell.h"
48 #include "nsProxyRelease.h"
49 #include "nsReadableUtils.h"
51 #include "nsIConsoleAPIStorage.h"
52 #include "nsIException.h" // for nsIStackFrame
53 #include "nsIInterfaceRequestorUtils.h"
54 #include "nsILoadContext.h"
55 #include "nsISensitiveInfoHiddenURI.h"
56 #include "nsISupportsPrimitives.h"
57 #include "nsIWebNavigation.h"
58 #include "nsIXPConnect.h"
60 // The maximum allowed number of concurrent timers per page.
61 #define MAX_PAGE_TIMERS 10000
63 // The maximum allowed number of concurrent counters per page.
64 #define MAX_PAGE_COUNTERS 10000
66 // The maximum stacktrace depth when populating the stacktrace array used for
68 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
70 // This tags are used in the Structured Clone Algorithm to move js values from
71 // worker thread to main thread
72 #define CONSOLE_TAG_BLOB JS_SCTAG_USER_MIN
74 // This value is taken from ConsoleAPIStorage.js
75 #define STORAGE_MAX_EVENTS 1000
77 using namespace mozilla::dom::exceptions
;
79 namespace mozilla::dom
{
81 struct ConsoleStructuredCloneData
{
82 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
83 nsTArray
<RefPtr
<BlobImpl
>> mBlobs
;
86 static void ComposeAndStoreGroupName(JSContext
* aCx
,
87 const Sequence
<JS::Value
>& aData
,
89 nsTArray
<nsString
>* aGroupStack
);
90 static bool UnstoreGroupName(nsAString
& aName
, nsTArray
<nsString
>* aGroupStack
);
92 static bool ProcessArguments(JSContext
* aCx
, const Sequence
<JS::Value
>& aData
,
93 Sequence
<JS::Value
>& aSequence
,
94 Sequence
<nsString
>& aStyles
);
96 static JS::Value
CreateCounterOrResetCounterValue(JSContext
* aCx
,
97 const nsAString
& aCountLabel
,
98 uint32_t aCountValue
);
101 * Console API in workers uses the Structured Clone Algorithm to move any value
102 * from the worker thread to the main-thread. Some object cannot be moved and,
103 * in these cases, we convert them to strings.
104 * It's not the best, but at least we are able to show something.
107 class ConsoleCallData final
{
109 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConsoleCallData
)
111 ConsoleCallData(Console::MethodName aName
, const nsAString
& aString
,
113 : mMutex("ConsoleCallData"),
114 mConsoleID(aConsole
->mConsoleID
),
115 mPrefix(aConsole
->mPrefix
),
117 mMicroSecondTimeStamp(JS_Now()),
119 mStartTimerStatus(Console::eTimerUnknown
),
120 mLogTimerDuration(0),
121 mLogTimerStatus(Console::eTimerUnknown
),
122 mCountValue(MAX_PAGE_COUNTERS
),
126 mMethodString(aString
) {}
128 void SetIDs(uint64_t aOuterID
, uint64_t aInnerID
) MOZ_REQUIRES(mMutex
) {
129 MOZ_ASSERT(mIDType
== eUnknown
);
131 mOuterIDNumber
= aOuterID
;
132 mInnerIDNumber
= aInnerID
;
136 void SetIDs(const nsAString
& aOuterID
, const nsAString
& aInnerID
)
137 MOZ_REQUIRES(mMutex
) {
138 MOZ_ASSERT(mIDType
== eUnknown
);
140 mOuterIDString
= aOuterID
;
141 mInnerIDString
= aInnerID
;
145 void SetOriginAttributes(const OriginAttributes
& aOriginAttributes
)
146 MOZ_REQUIRES(mMutex
) {
147 mOriginAttributes
= aOriginAttributes
;
150 void SetAddonId(nsIPrincipal
* aPrincipal
) MOZ_REQUIRES(mMutex
) {
151 nsAutoString addonId
;
152 aPrincipal
->GetAddonId(addonId
);
157 void AssertIsOnOwningThread() const {
158 NS_ASSERT_OWNINGTHREAD(ConsoleCallData
);
163 const nsString mConsoleID
MOZ_GUARDED_BY(mMutex
);
164 const nsString mPrefix
MOZ_GUARDED_BY(mMutex
);
166 const Console::MethodName mMethodName
MOZ_GUARDED_BY(mMutex
);
167 int64_t mMicroSecondTimeStamp
MOZ_GUARDED_BY(mMutex
);
169 // These values are set in the owning thread and they contain the timestamp of
170 // when the new timer has started, the name of it and the status of the
171 // creation of it. If status is false, something went wrong. User
172 // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
173 // monotonicTimer from Performance.now();
174 // They will be set on the owning thread and never touched again on that
175 // thread. They will be used in order to create a ConsoleTimerStart dictionary
176 // when console.time() is used.
177 DOMHighResTimeStamp mStartTimerValue
MOZ_GUARDED_BY(mMutex
);
178 nsString mStartTimerLabel
MOZ_GUARDED_BY(mMutex
);
179 Console::TimerStatus mStartTimerStatus
MOZ_GUARDED_BY(mMutex
);
181 // These values are set in the owning thread and they contain the duration,
182 // the name and the status of the LogTimer method. If status is false,
183 // something went wrong. They will be set on the owning thread and never
184 // touched again on that thread. They will be used in order to create a
185 // ConsoleTimerLogOrEnd dictionary. This members are set when
186 // console.timeEnd() or console.timeLog() are called.
187 double mLogTimerDuration
MOZ_GUARDED_BY(mMutex
);
188 nsString mLogTimerLabel
MOZ_GUARDED_BY(mMutex
);
189 Console::TimerStatus mLogTimerStatus
MOZ_GUARDED_BY(mMutex
);
191 // These 2 values are set by IncreaseCounter or ResetCounter on the owning
192 // thread and they are used by CreateCounterOrResetCounterValue.
193 // These members are set when console.count() or console.countReset() are
195 nsString mCountLabel
MOZ_GUARDED_BY(mMutex
);
196 uint32_t mCountValue
MOZ_GUARDED_BY(mMutex
);
198 // The concept of outerID and innerID is misleading because when a
199 // ConsoleCallData is created from a window, these are the window IDs, but
200 // when the object is created from a SharedWorker, a ServiceWorker or a
201 // subworker of a ChromeWorker these IDs are the type of worker and the
202 // filename of the callee.
203 // In Console.sys.mjs the ID is 'jsm'.
204 enum { eString
, eNumber
, eUnknown
} mIDType
MOZ_GUARDED_BY(mMutex
);
206 uint64_t mOuterIDNumber
MOZ_GUARDED_BY(mMutex
);
207 nsString mOuterIDString
MOZ_GUARDED_BY(mMutex
);
209 uint64_t mInnerIDNumber
MOZ_GUARDED_BY(mMutex
);
210 nsString mInnerIDString
MOZ_GUARDED_BY(mMutex
);
212 OriginAttributes mOriginAttributes
MOZ_GUARDED_BY(mMutex
);
214 nsString mAddonId
MOZ_GUARDED_BY(mMutex
);
216 const nsString mMethodString
MOZ_GUARDED_BY(mMutex
);
218 // Stack management is complicated, because we want to do it as
219 // lazily as possible. Therefore, we have the following behavior:
220 // 1) mTopStackFrame is initialized whenever we have any JS on the stack
221 // 2) mReifiedStack is initialized if we're created in a worker.
222 // 3) mStack is set (possibly to null if there is no JS on the stack) if
223 // we're created on main thread.
224 Maybe
<ConsoleStackEntry
> mTopStackFrame
MOZ_GUARDED_BY(mMutex
);
225 Maybe
<nsTArray
<ConsoleStackEntry
>> mReifiedStack
MOZ_GUARDED_BY(mMutex
);
226 nsCOMPtr
<nsIStackFrame
> mStack
MOZ_GUARDED_BY(mMutex
);
229 ~ConsoleCallData() = default;
231 NS_DECL_OWNINGTHREAD
;
234 // MainThreadConsoleData instances are created on the Console thread and
235 // referenced from both main and Console threads in order to provide the same
236 // object for any ConsoleRunnables relating to the same Console. A Console
237 // owns a MainThreadConsoleData; MainThreadConsoleData does not keep its
239 class MainThreadConsoleData final
{
240 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MainThreadConsoleData
);
242 JSObject
* GetOrCreateSandbox(JSContext
* aCx
, nsIPrincipal
* aPrincipal
);
243 // This method must receive aCx and aArguments in the same JS::Compartment.
244 void ProcessCallData(JSContext
* aCx
, ConsoleCallData
* aData
,
245 const Sequence
<JS::Value
>& aArguments
);
248 ~MainThreadConsoleData() {
249 NS_ReleaseOnMainThread("MainThreadConsoleData::mStorage",
251 NS_ReleaseOnMainThread("MainThreadConsoleData::mSandbox",
255 // All members, except for mRefCnt, are accessed only on the main thread,
256 // except in MainThreadConsoleData destruction, at which point there are no
258 nsCOMPtr
<nsIConsoleAPIStorage
> mStorage
;
259 RefPtr
<JSObjectHolder
> mSandbox
;
260 nsTArray
<nsString
> mGroupStack
;
263 // This base class must be extended for Worker and for Worklet.
264 class ConsoleRunnable
: public StructuredCloneHolderBase
{
266 ~ConsoleRunnable() override
{
267 MOZ_ASSERT(!mClonedData
.mGlobal
,
268 "mClonedData.mGlobal is set and cleared in a main thread scope");
269 // Clear the StructuredCloneHolderBase class.
274 JSObject
* CustomReadHandler(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
275 const JS::CloneDataPolicy
& aCloneDataPolicy
,
276 uint32_t aTag
, uint32_t aIndex
) override
{
277 AssertIsOnMainThread();
279 if (aTag
== CONSOLE_TAG_BLOB
) {
280 MOZ_ASSERT(mClonedData
.mBlobs
.Length() > aIndex
);
282 JS::Rooted
<JS::Value
> val(aCx
);
284 nsCOMPtr
<nsIGlobalObject
> global
= mClonedData
.mGlobal
;
286 Blob::Create(global
, mClonedData
.mBlobs
.ElementAt(aIndex
));
287 if (!ToJSValue(aCx
, blob
, &val
)) {
292 return &val
.toObject();
295 MOZ_CRASH("No other tags are supported.");
299 bool CustomWriteHandler(JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
,
300 JS::Handle
<JSObject
*> aObj
,
301 bool* aSameProcessScopeRequired
) override
{
303 if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob
, aObj
, blob
))) {
304 if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter
, CONSOLE_TAG_BLOB
,
305 mClonedData
.mBlobs
.Length()))) {
309 mClonedData
.mBlobs
.AppendElement(blob
->Impl());
313 if (!JS_ObjectNotWritten(aWriter
, aObj
)) {
317 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectOrNullValue(aObj
));
318 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
319 if (NS_WARN_IF(!jsString
)) {
323 if (NS_WARN_IF(!JS_WriteString(aWriter
, jsString
))) {
330 // Helper method for CallData
331 void ProcessCallData(JSContext
* aCx
, MainThreadConsoleData
* aConsoleData
,
332 ConsoleCallData
* aCallData
) {
333 AssertIsOnMainThread();
335 ConsoleCommon::ClearException
ce(aCx
);
337 // This is the same policy as when writing from the other side, in
339 JS::CloneDataPolicy cloneDataPolicy
;
340 cloneDataPolicy
.allowIntraClusterClonableSharedObjects();
341 cloneDataPolicy
.allowSharedMemoryObjects();
343 JS::Rooted
<JS::Value
> argumentsValue(aCx
);
344 if (!Read(aCx
, &argumentsValue
, cloneDataPolicy
)) {
348 MOZ_ASSERT(argumentsValue
.isObject());
350 JS::Rooted
<JSObject
*> argumentsObj(aCx
, &argumentsValue
.toObject());
353 if (!JS::GetArrayLength(aCx
, argumentsObj
, &length
)) {
357 Sequence
<JS::Value
> values
;
358 SequenceRooter
<JS::Value
> arguments(aCx
, &values
);
360 for (uint32_t i
= 0; i
< length
; ++i
) {
361 JS::Rooted
<JS::Value
> value(aCx
);
363 if (!JS_GetElement(aCx
, argumentsObj
, i
, &value
)) {
367 if (!values
.AppendElement(value
, fallible
)) {
372 MOZ_ASSERT(values
.Length() == length
);
374 aConsoleData
->ProcessCallData(aCx
, aCallData
, values
);
378 bool WriteArguments(JSContext
* aCx
, const Sequence
<JS::Value
>& aArguments
) {
379 ConsoleCommon::ClearException
ce(aCx
);
381 JS::Rooted
<JSObject
*> arguments(
382 aCx
, JS::NewArrayObject(aCx
, aArguments
.Length()));
383 if (NS_WARN_IF(!arguments
)) {
387 JS::Rooted
<JS::Value
> arg(aCx
);
388 for (uint32_t i
= 0; i
< aArguments
.Length(); ++i
) {
391 !JS_DefineElement(aCx
, arguments
, i
, arg
, JSPROP_ENUMERATE
))) {
396 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectValue(*arguments
));
397 return WriteData(aCx
, value
);
400 // Helper method for Profile calls
401 void ProcessProfileData(JSContext
* aCx
, Console::MethodName aMethodName
,
402 const nsAString
& aAction
) {
403 AssertIsOnMainThread();
405 ConsoleCommon::ClearException
ce(aCx
);
407 JS::Rooted
<JS::Value
> argumentsValue(aCx
);
408 bool ok
= Read(aCx
, &argumentsValue
);
409 mClonedData
.mGlobal
= nullptr;
415 MOZ_ASSERT(argumentsValue
.isObject());
416 JS::Rooted
<JSObject
*> argumentsObj(aCx
, &argumentsValue
.toObject());
417 if (NS_WARN_IF(!argumentsObj
)) {
422 if (!JS::GetArrayLength(aCx
, argumentsObj
, &length
)) {
426 Sequence
<JS::Value
> arguments
;
428 for (uint32_t i
= 0; i
< length
; ++i
) {
429 JS::Rooted
<JS::Value
> value(aCx
);
431 if (!JS_GetElement(aCx
, argumentsObj
, i
, &value
)) {
435 if (!arguments
.AppendElement(value
, fallible
)) {
440 Console::ProfileMethodMainthread(aCx
, aAction
, arguments
);
443 bool WriteData(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
444 // We use structuredClone to send the JSValue to the main-thread, in order
445 // to store it into the Console API Service. The consumer will be the
446 // console panel in the devtools and, because of this, we want to allow the
447 // cloning of sharedArrayBuffers and WASM modules.
448 JS::CloneDataPolicy cloneDataPolicy
;
449 cloneDataPolicy
.allowIntraClusterClonableSharedObjects();
450 cloneDataPolicy
.allowSharedMemoryObjects();
453 !Write(aCx
, aValue
, JS::UndefinedHandleValue
, cloneDataPolicy
))) {
454 // Ignore the message.
461 ConsoleStructuredCloneData mClonedData
;
464 class ConsoleWorkletRunnable
: public Runnable
, public ConsoleRunnable
{
466 explicit ConsoleWorkletRunnable(Console
* aConsole
)
467 : Runnable("dom::console::ConsoleWorkletRunnable"),
468 mConsoleData(aConsole
->GetOrCreateMainThreadData()) {
469 WorkletThread::AssertIsOnWorkletThread();
470 nsCOMPtr
<WorkletGlobalScope
> global
= do_QueryInterface(aConsole
->mGlobal
);
472 mWorkletImpl
= global
->Impl();
473 MOZ_ASSERT(mWorkletImpl
);
476 ~ConsoleWorkletRunnable() override
= default;
479 RefPtr
<MainThreadConsoleData
> mConsoleData
;
481 RefPtr
<WorkletImpl
> mWorkletImpl
;
484 // This runnable appends a CallData object into the Console queue running on
486 class ConsoleCallDataWorkletRunnable final
: public ConsoleWorkletRunnable
{
488 static already_AddRefed
<ConsoleCallDataWorkletRunnable
> Create(
489 JSContext
* aCx
, Console
* aConsole
, ConsoleCallData
* aConsoleData
,
490 const Sequence
<JS::Value
>& aArguments
) {
491 WorkletThread::AssertIsOnWorkletThread();
493 RefPtr
<ConsoleCallDataWorkletRunnable
> runnable
=
494 new ConsoleCallDataWorkletRunnable(aConsole
, aConsoleData
);
496 if (!runnable
->WriteArguments(aCx
, aArguments
)) {
500 return runnable
.forget();
504 ConsoleCallDataWorkletRunnable(Console
* aConsole
, ConsoleCallData
* aCallData
)
505 : ConsoleWorkletRunnable(aConsole
), mCallData(aCallData
) {
506 WorkletThread::AssertIsOnWorkletThread();
507 MOZ_ASSERT(aCallData
);
508 aCallData
->AssertIsOnOwningThread();
510 const WorkletLoadInfo
& loadInfo
= mWorkletImpl
->LoadInfo();
511 mCallData
->SetIDs(loadInfo
.OuterWindowID(), loadInfo
.InnerWindowID());
514 ~ConsoleCallDataWorkletRunnable() override
= default;
516 NS_IMETHOD
Run() override
{
517 AssertIsOnMainThread();
520 JSContext
* cx
= jsapi
.cx();
523 MutexAutoLock
lock(mCallData
->mMutex
);
526 mConsoleData
->GetOrCreateSandbox(cx
, mWorkletImpl
->Principal());
527 JS::Rooted
<JSObject
*> global(cx
, sandbox
);
528 if (NS_WARN_IF(!global
)) {
529 return NS_ERROR_FAILURE
;
532 // The CreateSandbox call returns a proxy to the actual sandbox object. We
533 // don't need a proxy here.
534 global
= js::UncheckedUnwrap(global
);
535 JSAutoRealm
ar(cx
, global
);
537 // We don't need to set a parent object in mCallData bacause there are not
538 // DOM objects exposed to worklet.
540 ProcessCallData(cx
, mConsoleData
, mCallData
);
546 RefPtr
<ConsoleCallData
> mCallData
;
549 class ConsoleWorkerRunnable
: public WorkerProxyToMainThreadRunnable
,
550 public ConsoleRunnable
{
552 explicit ConsoleWorkerRunnable(Console
* aConsole
)
553 : mConsoleData(aConsole
->GetOrCreateMainThreadData()) {}
555 ~ConsoleWorkerRunnable() override
= default;
557 bool Dispatch(JSContext
* aCx
, const Sequence
<JS::Value
>& aArguments
) {
558 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
559 MOZ_ASSERT(workerPrivate
);
561 if (NS_WARN_IF(!WriteArguments(aCx
, aArguments
))) {
562 RunBackOnWorkerThreadForCleanup(workerPrivate
);
566 if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch(workerPrivate
))) {
567 // RunBackOnWorkerThreadForCleanup() will be called by
568 // WorkerProxyToMainThreadRunnable::Dispatch().
576 void RunOnMainThread(WorkerPrivate
* aWorkerPrivate
) override
{
577 MOZ_ASSERT(aWorkerPrivate
);
578 AssertIsOnMainThread();
580 // Walk up to our containing page
581 WorkerPrivate
* wp
= aWorkerPrivate
->GetTopLevelWorker();
583 nsCOMPtr
<nsPIDOMWindowInner
> window
= wp
->GetWindow();
585 RunWindowless(aWorkerPrivate
);
587 RunWithWindow(aWorkerPrivate
, window
);
591 void RunWithWindow(WorkerPrivate
* aWorkerPrivate
,
592 nsPIDOMWindowInner
* aWindow
) {
593 MOZ_ASSERT(aWorkerPrivate
);
594 AssertIsOnMainThread();
599 RefPtr
<nsGlobalWindowInner
> win
= nsGlobalWindowInner::Cast(aWindow
);
600 if (NS_WARN_IF(!jsapi
.Init(win
))) {
604 nsCOMPtr
<nsPIDOMWindowOuter
> outerWindow
= aWindow
->GetOuterWindow();
605 if (NS_WARN_IF(!outerWindow
)) {
609 RunConsole(jsapi
.cx(), aWindow
->AsGlobal(), aWorkerPrivate
, outerWindow
,
613 void RunWindowless(WorkerPrivate
* aWorkerPrivate
) {
614 MOZ_ASSERT(aWorkerPrivate
);
615 AssertIsOnMainThread();
617 WorkerPrivate
* wp
= aWorkerPrivate
->GetTopLevelWorker();
619 MOZ_ASSERT(!wp
->GetWindow());
624 JSContext
* cx
= jsapi
.cx();
626 JS::Rooted
<JSObject
*> global(
627 cx
, mConsoleData
->GetOrCreateSandbox(cx
, wp
->GetPrincipal()));
628 if (NS_WARN_IF(!global
)) {
632 // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
633 // We don't need a proxy here.
634 global
= js::UncheckedUnwrap(global
);
636 JSAutoRealm
ar(cx
, global
);
638 nsCOMPtr
<nsIGlobalObject
> globalObject
= xpc::NativeGlobal(global
);
639 if (NS_WARN_IF(!globalObject
)) {
643 RunConsole(cx
, globalObject
, aWorkerPrivate
, nullptr, nullptr);
646 void RunBackOnWorkerThreadForCleanup(WorkerPrivate
* aWorkerPrivate
) override
{
647 MOZ_ASSERT(aWorkerPrivate
);
648 aWorkerPrivate
->AssertIsOnWorkerThread();
651 // This method is called in the main-thread.
652 virtual void RunConsole(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
653 WorkerPrivate
* aWorkerPrivate
,
654 nsPIDOMWindowOuter
* aOuterWindow
,
655 nsPIDOMWindowInner
* aInnerWindow
) = 0;
657 bool ForMessaging() const override
{ return true; }
659 RefPtr
<MainThreadConsoleData
> mConsoleData
;
662 // This runnable appends a CallData object into the Console queue running on
664 class ConsoleCallDataWorkerRunnable final
: public ConsoleWorkerRunnable
{
666 ConsoleCallDataWorkerRunnable(Console
* aConsole
, ConsoleCallData
* aCallData
)
667 : ConsoleWorkerRunnable(aConsole
), mCallData(aCallData
) {
668 MOZ_ASSERT(aCallData
);
669 mCallData
->AssertIsOnOwningThread();
673 ~ConsoleCallDataWorkerRunnable() override
= default;
675 void RunConsole(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
676 WorkerPrivate
* aWorkerPrivate
,
677 nsPIDOMWindowOuter
* aOuterWindow
,
678 nsPIDOMWindowInner
* aInnerWindow
) override
{
680 MOZ_ASSERT(aWorkerPrivate
);
681 AssertIsOnMainThread();
683 // The windows have to run in parallel.
684 MOZ_ASSERT(!!aOuterWindow
== !!aInnerWindow
);
687 MutexAutoLock
lock(mCallData
->mMutex
);
689 mCallData
->SetIDs(aOuterWindow
->WindowID(), aInnerWindow
->WindowID());
691 ConsoleStackEntry frame
;
692 if (mCallData
->mTopStackFrame
) {
693 frame
= *mCallData
->mTopStackFrame
;
696 nsCString id
= frame
.mFilename
;
698 if (aWorkerPrivate
->IsSharedWorker()) {
699 innerID
= u
"SharedWorker"_ns
;
700 } else if (aWorkerPrivate
->IsServiceWorker()) {
701 innerID
= u
"ServiceWorker"_ns
;
702 // Use scope as ID so the webconsole can decide if the message should
704 id
= aWorkerPrivate
->ServiceWorkerScope();
706 innerID
= u
"Worker"_ns
;
709 mCallData
->SetIDs(NS_ConvertUTF8toUTF16(id
), innerID
);
712 mClonedData
.mGlobal
= aGlobal
;
714 ProcessCallData(aCx
, mConsoleData
, mCallData
);
716 mClonedData
.mGlobal
= nullptr;
720 RefPtr
<ConsoleCallData
> mCallData
;
723 // This runnable calls ProfileMethod() on the console on the main-thread.
724 class ConsoleProfileWorkletRunnable final
: public ConsoleWorkletRunnable
{
726 static already_AddRefed
<ConsoleProfileWorkletRunnable
> Create(
727 JSContext
* aCx
, Console
* aConsole
, Console::MethodName aName
,
728 const nsAString
& aAction
, const Sequence
<JS::Value
>& aArguments
) {
729 WorkletThread::AssertIsOnWorkletThread();
731 RefPtr
<ConsoleProfileWorkletRunnable
> runnable
=
732 new ConsoleProfileWorkletRunnable(aConsole
, aName
, aAction
);
734 if (!runnable
->WriteArguments(aCx
, aArguments
)) {
738 return runnable
.forget();
742 ConsoleProfileWorkletRunnable(Console
* aConsole
, Console::MethodName aName
,
743 const nsAString
& aAction
)
744 : ConsoleWorkletRunnable(aConsole
), mName(aName
), mAction(aAction
) {
745 MOZ_ASSERT(aConsole
);
748 NS_IMETHOD
Run() override
{
749 AssertIsOnMainThread();
753 JSContext
* cx
= jsapi
.cx();
756 mConsoleData
->GetOrCreateSandbox(cx
, mWorkletImpl
->Principal());
757 JS::Rooted
<JSObject
*> global(cx
, sandbox
);
758 if (NS_WARN_IF(!global
)) {
759 return NS_ERROR_FAILURE
;
762 // The CreateSandbox call returns a proxy to the actual sandbox object. We
763 // don't need a proxy here.
764 global
= js::UncheckedUnwrap(global
);
766 JSAutoRealm
ar(cx
, global
);
768 // We don't need to set a parent object in mCallData bacause there are not
769 // DOM objects exposed to worklet.
770 ProcessProfileData(cx
, mName
, mAction
);
775 Console::MethodName mName
;
779 // This runnable calls ProfileMethod() on the console on the main-thread.
780 class ConsoleProfileWorkerRunnable final
: public ConsoleWorkerRunnable
{
782 ConsoleProfileWorkerRunnable(Console
* aConsole
, Console::MethodName aName
,
783 const nsAString
& aAction
)
784 : ConsoleWorkerRunnable(aConsole
), mName(aName
), mAction(aAction
) {
785 MOZ_ASSERT(aConsole
);
789 void RunConsole(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
790 WorkerPrivate
* aWorkerPrivate
,
791 nsPIDOMWindowOuter
* aOuterWindow
,
792 nsPIDOMWindowInner
* aInnerWindow
) override
{
793 AssertIsOnMainThread();
796 mClonedData
.mGlobal
= aGlobal
;
798 ProcessProfileData(aCx
, mName
, mAction
);
800 mClonedData
.mGlobal
= nullptr;
803 Console::MethodName mName
;
807 NS_IMPL_CYCLE_COLLECTION_CLASS(Console
)
809 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console
)
810 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal
)
811 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsoleEventNotifier
)
812 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDumpFunction
)
813 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
815 tmp
->mArgumentStorage
.clearAndFree();
816 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
818 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console
)
819 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal
)
820 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsoleEventNotifier
)
821 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDumpFunction
)
822 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
824 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console
)
825 for (uint32_t i
= 0; i
< tmp
->mArgumentStorage
.length(); ++i
) {
826 tmp
->mArgumentStorage
[i
].Trace(aCallbacks
, aClosure
);
828 NS_IMPL_CYCLE_COLLECTION_TRACE_END
830 NS_IMPL_CYCLE_COLLECTING_ADDREF(Console
)
831 NS_IMPL_CYCLE_COLLECTING_RELEASE(Console
)
833 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console
)
834 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
835 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIObserver
)
836 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
840 already_AddRefed
<Console
> Console::Create(JSContext
* aCx
,
841 nsPIDOMWindowInner
* aWindow
,
843 MOZ_ASSERT_IF(NS_IsMainThread(), aWindow
);
845 uint64_t outerWindowID
= 0;
846 uint64_t innerWindowID
= 0;
849 innerWindowID
= aWindow
->WindowID();
851 // Without outerwindow any console message coming from this object will not
852 // shown in the devtools webconsole. But this should be fine because
853 // probably we are shutting down, or the window is CCed/GCed.
854 nsPIDOMWindowOuter
* outerWindow
= aWindow
->GetOuterWindow();
856 outerWindowID
= outerWindow
->WindowID();
860 RefPtr
<Console
> console
= new Console(aCx
, nsGlobalWindowInner::Cast(aWindow
),
861 outerWindowID
, innerWindowID
);
862 console
->Initialize(aRv
);
863 if (NS_WARN_IF(aRv
.Failed())) {
867 return console
.forget();
871 already_AddRefed
<Console
> Console::CreateForWorklet(JSContext
* aCx
,
872 nsIGlobalObject
* aGlobal
,
873 uint64_t aOuterWindowID
,
874 uint64_t aInnerWindowID
,
876 WorkletThread::AssertIsOnWorkletThread();
878 RefPtr
<Console
> console
=
879 new Console(aCx
, aGlobal
, aOuterWindowID
, aInnerWindowID
);
880 console
->Initialize(aRv
);
881 if (NS_WARN_IF(aRv
.Failed())) {
885 return console
.forget();
888 Console::Console(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
889 uint64_t aOuterWindowID
, uint64_t aInnerWindowID
,
890 const nsAString
& aPrefix
)
892 mOuterID(aOuterWindowID
),
893 mInnerID(aInnerWindowID
),
894 mDumpToStdout(false),
897 mChromeInstance(false),
898 mCurrentLogLevel(WebIDLLogLevelToInteger(ConsoleLogLevel::All
)),
900 mCreationTimeStamp(TimeStamp::Now()) {
901 // Let's enable the dumping to stdout by default for chrome.
902 if (nsContentUtils::ThreadsafeIsSystemCaller(aCx
)) {
903 mDumpToStdout
= StaticPrefs::devtools_console_stdout_chrome();
905 mDumpToStdout
= StaticPrefs::devtools_console_stdout_content();
908 // By default, the console uses "console" MOZ_LOG module name,
909 // but ConsoleInstance may pass a custom prefix which we will use a module
911 mLogModule
= mPrefix
.IsEmpty()
912 ? LogModule::Get("console")
913 : LogModule::Get(NS_ConvertUTF16toUTF8(mPrefix
).get());
915 mozilla::HoldJSObjects(this);
918 Console::~Console() {
919 AssertIsOnOwningThread();
921 mozilla::DropJSObjects(this);
924 void Console::Initialize(ErrorResult
& aRv
) {
925 AssertIsOnOwningThread();
926 MOZ_ASSERT(mStatus
== eUnknown
);
928 if (NS_IsMainThread()) {
929 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
930 if (NS_WARN_IF(!obs
)) {
931 aRv
.Throw(NS_ERROR_FAILURE
);
936 aRv
= obs
->AddObserver(this, "inner-window-destroyed", true);
937 if (NS_WARN_IF(aRv
.Failed())) {
942 aRv
= obs
->AddObserver(this, "memory-pressure", true);
943 if (NS_WARN_IF(aRv
.Failed())) {
948 mStatus
= eInitialized
;
951 void Console::Shutdown() {
952 AssertIsOnOwningThread();
954 if (mStatus
== eUnknown
|| mStatus
== eShuttingDown
) {
958 if (NS_IsMainThread()) {
959 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
961 obs
->RemoveObserver(this, "inner-window-destroyed");
962 obs
->RemoveObserver(this, "memory-pressure");
966 mTimerRegistry
.Clear();
967 mCounterRegistry
.Clear();
970 mCallDataStorage
.Clear();
972 mStatus
= eShuttingDown
;
976 Console::Observe(nsISupports
* aSubject
, const char* aTopic
,
977 const char16_t
* aData
) {
978 AssertIsOnMainThread();
980 if (!strcmp(aTopic
, "inner-window-destroyed")) {
981 nsCOMPtr
<nsISupportsPRUint64
> wrapper
= do_QueryInterface(aSubject
);
982 NS_ENSURE_TRUE(wrapper
, NS_ERROR_FAILURE
);
985 nsresult rv
= wrapper
->GetData(&innerID
);
986 NS_ENSURE_SUCCESS(rv
, rv
);
988 if (innerID
== mInnerID
) {
995 if (!strcmp(aTopic
, "memory-pressure")) {
1003 void Console::ClearStorage() {
1004 mCallDataStorage
.Clear();
1005 mArgumentStorage
.clearAndFree();
1008 #define METHOD(name, string) \
1009 /* static */ void Console::name(const GlobalObject& aGlobal, \
1010 const Sequence<JS::Value>& aData) { \
1011 Method(aGlobal, Method##name, nsLiteralString(string), aData); \
1015 METHOD(Info
, u
"info")
1016 METHOD(Warn
, u
"warn")
1017 METHOD(Error
, u
"error")
1018 METHOD(Exception
, u
"exception")
1019 METHOD(Debug
, u
"debug")
1020 METHOD(Table
, u
"table")
1021 METHOD(Trace
, u
"trace")
1023 // Displays an interactive listing of all the properties of an object.
1024 METHOD(Dir
, u
"dir");
1025 METHOD(Dirxml
, u
"dirxml");
1027 METHOD(Group
, u
"group")
1028 METHOD(GroupCollapsed
, u
"groupCollapsed")
1033 void Console::Clear(const GlobalObject
& aGlobal
) {
1034 const Sequence
<JS::Value
> data
;
1035 Method(aGlobal
, MethodClear
, u
"clear"_ns
, data
);
1039 void Console::GroupEnd(const GlobalObject
& aGlobal
) {
1040 const Sequence
<JS::Value
> data
;
1041 Method(aGlobal
, MethodGroupEnd
, u
"groupEnd"_ns
, data
);
1045 void Console::Time(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1046 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodTime
, u
"time"_ns
);
1050 void Console::TimeEnd(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1051 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodTimeEnd
,
1056 void Console::TimeLog(const GlobalObject
& aGlobal
, const nsAString
& aLabel
,
1057 const Sequence
<JS::Value
>& aData
) {
1058 StringMethod(aGlobal
, aLabel
, aData
, MethodTimeLog
, u
"timeLog"_ns
);
1062 void Console::StringMethod(const GlobalObject
& aGlobal
, const nsAString
& aLabel
,
1063 const Sequence
<JS::Value
>& aData
,
1064 MethodName aMethodName
,
1065 const nsAString
& aMethodString
) {
1066 RefPtr
<Console
> console
= GetConsole(aGlobal
);
1071 console
->StringMethodInternal(aGlobal
.Context(), aLabel
, aData
, aMethodName
,
1075 void Console::StringMethodInternal(JSContext
* aCx
, const nsAString
& aLabel
,
1076 const Sequence
<JS::Value
>& aData
,
1077 MethodName aMethodName
,
1078 const nsAString
& aMethodString
) {
1079 ConsoleCommon::ClearException
ce(aCx
);
1081 Sequence
<JS::Value
> data
;
1082 SequenceRooter
<JS::Value
> rooter(aCx
, &data
);
1084 JS::Rooted
<JS::Value
> value(aCx
);
1085 if (!dom::ToJSValue(aCx
, aLabel
, &value
)) {
1089 if (!data
.AppendElement(value
, fallible
)) {
1093 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
1094 if (!data
.AppendElement(aData
[i
], fallible
)) {
1099 MethodInternal(aCx
, aMethodName
, aMethodString
, data
);
1103 void Console::TimeStamp(const GlobalObject
& aGlobal
,
1104 const JS::Handle
<JS::Value
> aData
) {
1105 JSContext
* cx
= aGlobal
.Context();
1107 ConsoleCommon::ClearException
ce(cx
);
1109 Sequence
<JS::Value
> data
;
1110 SequenceRooter
<JS::Value
> rooter(cx
, &data
);
1112 if (aData
.isString() && !data
.AppendElement(aData
, fallible
)) {
1116 Method(aGlobal
, MethodTimeStamp
, u
"timeStamp"_ns
, data
);
1120 void Console::Profile(const GlobalObject
& aGlobal
,
1121 const Sequence
<JS::Value
>& aData
) {
1122 ProfileMethod(aGlobal
, MethodProfile
, u
"profile"_ns
, aData
);
1126 void Console::ProfileEnd(const GlobalObject
& aGlobal
,
1127 const Sequence
<JS::Value
>& aData
) {
1128 ProfileMethod(aGlobal
, MethodProfileEnd
, u
"profileEnd"_ns
, aData
);
1132 void Console::ProfileMethod(const GlobalObject
& aGlobal
, MethodName aName
,
1133 const nsAString
& aAction
,
1134 const Sequence
<JS::Value
>& aData
) {
1135 RefPtr
<Console
> console
= GetConsole(aGlobal
);
1140 JSContext
* cx
= aGlobal
.Context();
1141 console
->ProfileMethodInternal(cx
, aName
, aAction
, aData
);
1144 void Console::ProfileMethodInternal(JSContext
* aCx
, MethodName aMethodName
,
1145 const nsAString
& aAction
,
1146 const Sequence
<JS::Value
>& aData
) {
1147 if (!ShouldProceed(aMethodName
)) {
1151 MaybeExecuteDumpFunction(aCx
, aMethodName
, aAction
, aData
, nullptr,
1152 DOMHighResTimeStamp(0.0));
1154 if (WorkletThread::IsOnWorkletThread()) {
1155 RefPtr
<ConsoleProfileWorkletRunnable
> runnable
=
1156 ConsoleProfileWorkletRunnable::Create(aCx
, this, aMethodName
, aAction
,
1162 NS_DispatchToMainThread(runnable
.forget());
1166 if (!NS_IsMainThread()) {
1167 // Here we are in a worker thread.
1168 RefPtr
<ConsoleProfileWorkerRunnable
> runnable
=
1169 new ConsoleProfileWorkerRunnable(this, aMethodName
, aAction
);
1171 runnable
->Dispatch(aCx
, aData
);
1175 ProfileMethodMainthread(aCx
, aAction
, aData
);
1179 void Console::ProfileMethodMainthread(JSContext
* aCx
, const nsAString
& aAction
,
1180 const Sequence
<JS::Value
>& aData
) {
1181 MOZ_ASSERT(NS_IsMainThread());
1182 ConsoleCommon::ClearException
ce(aCx
);
1184 RootedDictionary
<ConsoleProfileEvent
> event(aCx
);
1185 event
.mAction
= aAction
;
1186 event
.mChromeContext
= nsContentUtils::ThreadsafeIsSystemCaller(aCx
);
1188 event
.mArguments
.Construct();
1189 Sequence
<JS::Value
>& sequence
= event
.mArguments
.Value();
1191 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
1192 if (!sequence
.AppendElement(aData
[i
], fallible
)) {
1197 JS::Rooted
<JS::Value
> eventValue(aCx
);
1198 if (!ToJSValue(aCx
, event
, &eventValue
)) {
1202 JS::Rooted
<JSObject
*> eventObj(aCx
, &eventValue
.toObject());
1203 MOZ_ASSERT(eventObj
);
1205 if (!JS_DefineProperty(aCx
, eventObj
, "wrappedJSObject", eventValue
,
1206 JSPROP_ENUMERATE
)) {
1210 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
1211 nsCOMPtr
<nsISupports
> wrapper
;
1212 const nsIID
& iid
= NS_GET_IID(nsISupports
);
1214 if (NS_FAILED(xpc
->WrapJS(aCx
, eventObj
, iid
, getter_AddRefs(wrapper
)))) {
1218 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1220 obs
->NotifyObservers(wrapper
, "console-api-profiler", nullptr);
1225 void Console::Assert(const GlobalObject
& aGlobal
, bool aCondition
,
1226 const Sequence
<JS::Value
>& aData
) {
1228 Method(aGlobal
, MethodAssert
, u
"assert"_ns
, aData
);
1233 void Console::Count(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1234 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodCount
,
1239 void Console::CountReset(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1240 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodCountReset
,
1246 void StackFrameToStackEntry(JSContext
* aCx
, nsIStackFrame
* aStackFrame
,
1247 ConsoleStackEntry
& aStackEntry
) {
1248 MOZ_ASSERT(aStackFrame
);
1250 aStackFrame
->GetFilename(aCx
, aStackEntry
.mFilename
);
1251 aStackEntry
.mSourceId
= aStackFrame
->GetSourceId(aCx
);
1252 aStackEntry
.mLineNumber
= aStackFrame
->GetLineNumber(aCx
);
1253 aStackEntry
.mColumnNumber
= aStackFrame
->GetColumnNumber(aCx
);
1255 aStackFrame
->GetName(aCx
, aStackEntry
.mFunctionName
);
1258 aStackFrame
->GetAsyncCause(aCx
, cause
);
1259 if (!cause
.IsEmpty()) {
1260 aStackEntry
.mAsyncCause
.Construct(cause
);
1264 void ReifyStack(JSContext
* aCx
, nsIStackFrame
* aStack
,
1265 nsTArray
<ConsoleStackEntry
>& aRefiedStack
) {
1266 nsCOMPtr
<nsIStackFrame
> stack(aStack
);
1269 ConsoleStackEntry
& data
= *aRefiedStack
.AppendElement();
1270 StackFrameToStackEntry(aCx
, stack
, data
);
1272 nsCOMPtr
<nsIStackFrame
> caller
= stack
->GetCaller(aCx
);
1275 caller
= stack
->GetAsyncCaller(aCx
);
1281 } // anonymous namespace
1283 // Queue a call to a console method. See the CALL_DELAY constant.
1285 void Console::Method(const GlobalObject
& aGlobal
, MethodName aMethodName
,
1286 const nsAString
& aMethodString
,
1287 const Sequence
<JS::Value
>& aData
) {
1288 RefPtr
<Console
> console
= GetConsole(aGlobal
);
1293 console
->MethodInternal(aGlobal
.Context(), aMethodName
, aMethodString
, aData
);
1296 void Console::MethodInternal(JSContext
* aCx
, MethodName aMethodName
,
1297 const nsAString
& aMethodString
,
1298 const Sequence
<JS::Value
>& aData
) {
1299 if (!ShouldProceed(aMethodName
)) {
1303 AssertIsOnOwningThread();
1305 ConsoleCommon::ClearException
ce(aCx
);
1307 RefPtr
<ConsoleCallData
> callData
=
1308 new ConsoleCallData(aMethodName
, aMethodString
, this);
1310 MutexAutoLock
lock(callData
->mMutex
);
1312 if (!StoreCallData(aCx
, callData
, aData
)) {
1316 OriginAttributes oa
;
1318 if (NS_IsMainThread()) {
1320 // Save the principal's OriginAttributes in the console event data
1321 // so that we will be able to filter messages by origin attributes.
1322 nsCOMPtr
<nsIScriptObjectPrincipal
> sop
= do_QueryInterface(mGlobal
);
1323 if (NS_WARN_IF(!sop
)) {
1327 nsCOMPtr
<nsIPrincipal
> principal
= sop
->GetPrincipal();
1328 if (NS_WARN_IF(!principal
)) {
1332 oa
= principal
->OriginAttributesRef();
1333 callData
->SetAddonId(principal
);
1336 if (!principal
->IsSystemPrincipal()) {
1337 nsCOMPtr
<nsIWebNavigation
> webNav
= do_GetInterface(mGlobal
);
1339 nsCOMPtr
<nsILoadContext
> loadContext
= do_QueryInterface(webNav
);
1340 MOZ_ASSERT(loadContext
);
1343 if (NS_SUCCEEDED(loadContext
->GetUsePrivateBrowsing(&pb
))) {
1344 MOZ_ASSERT(pb
== oa
.IsPrivateBrowsing());
1350 } else if (WorkletThread::IsOnWorkletThread()) {
1351 nsCOMPtr
<WorkletGlobalScope
> global
= do_QueryInterface(mGlobal
);
1353 oa
= global
->Impl()->OriginAttributesRef();
1355 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1356 MOZ_ASSERT(workerPrivate
);
1357 oa
= workerPrivate
->GetOriginAttributes();
1360 callData
->SetOriginAttributes(oa
);
1362 JS::StackCapture captureMode
=
1363 ShouldIncludeStackTrace(aMethodName
)
1364 ? JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH
))
1365 : JS::StackCapture(JS::FirstSubsumedFrame(aCx
));
1366 nsCOMPtr
<nsIStackFrame
> stack
= CreateStack(aCx
, std::move(captureMode
));
1369 callData
->mTopStackFrame
.emplace();
1370 StackFrameToStackEntry(aCx
, stack
, *callData
->mTopStackFrame
);
1373 if (NS_IsMainThread()) {
1374 callData
->mStack
= stack
;
1376 // nsIStackFrame is not threadsafe, so we need to snapshot it now,
1377 // before we post our runnable to the main thread.
1378 callData
->mReifiedStack
.emplace();
1379 ReifyStack(aCx
, stack
, *callData
->mReifiedStack
);
1382 DOMHighResTimeStamp monotonicTimer
= 0.0;
1384 // Monotonic timer for 'time', 'timeLog' and 'timeEnd'
1385 if ((aMethodName
== MethodTime
|| aMethodName
== MethodTimeLog
||
1386 aMethodName
== MethodTimeEnd
|| aMethodName
== MethodTimeStamp
) &&
1387 !MonotonicTimer(aCx
, aMethodName
, aData
, &monotonicTimer
)) {
1391 if (aMethodName
== MethodTime
&& !aData
.IsEmpty()) {
1392 callData
->mStartTimerStatus
=
1393 StartTimer(aCx
, aData
[0], monotonicTimer
, callData
->mStartTimerLabel
,
1394 &callData
->mStartTimerValue
);
1397 else if (aMethodName
== MethodTimeEnd
&& !aData
.IsEmpty()) {
1398 callData
->mLogTimerStatus
=
1399 LogTimer(aCx
, aData
[0], monotonicTimer
, callData
->mLogTimerLabel
,
1400 &callData
->mLogTimerDuration
, true /* Cancel timer */);
1403 else if (aMethodName
== MethodTimeLog
&& !aData
.IsEmpty()) {
1404 callData
->mLogTimerStatus
=
1405 LogTimer(aCx
, aData
[0], monotonicTimer
, callData
->mLogTimerLabel
,
1406 &callData
->mLogTimerDuration
, false /* Cancel timer */);
1409 else if (aMethodName
== MethodCount
) {
1410 callData
->mCountValue
= IncreaseCounter(aCx
, aData
, callData
->mCountLabel
);
1411 if (!callData
->mCountValue
) {
1416 else if (aMethodName
== MethodCountReset
) {
1417 callData
->mCountValue
= ResetCounter(aCx
, aData
, callData
->mCountLabel
);
1418 if (callData
->mCountLabel
.IsEmpty()) {
1423 // Before processing this CallData differently, it's time to call the dump
1426 // Only log the stack trace for console.trace() and console.assert()
1427 if (aMethodName
== MethodTrace
|| aMethodName
== MethodAssert
) {
1428 MaybeExecuteDumpFunction(aCx
, aMethodName
, aMethodString
, aData
, stack
,
1431 MaybeExecuteDumpFunction(aCx
, aMethodName
, aMethodString
, aData
, nullptr,
1435 if (NS_IsMainThread()) {
1437 callData
->SetIDs(mOuterID
, mInnerID
);
1438 } else if (!mPassedInnerID
.IsEmpty()) {
1439 callData
->SetIDs(u
"jsm"_ns
, mPassedInnerID
);
1441 nsAutoCString filename
;
1442 if (callData
->mTopStackFrame
.isSome()) {
1443 filename
= callData
->mTopStackFrame
->mFilename
;
1445 callData
->SetIDs(u
"jsm"_ns
, NS_ConvertUTF8toUTF16(filename
));
1448 GetOrCreateMainThreadData()->ProcessCallData(aCx
, callData
, aData
);
1450 // Just because we don't want to expose
1451 // retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
1452 // cleanup the mCallDataStorage:
1453 UnstoreCallData(callData
);
1457 if (WorkletThread::IsOnWorkletThread()) {
1458 RefPtr
<ConsoleCallDataWorkletRunnable
> runnable
=
1459 ConsoleCallDataWorkletRunnable::Create(aCx
, this, callData
, aData
);
1464 NS_DispatchToMainThread(runnable
);
1468 // We do this only in workers for now.
1469 NotifyHandler(aCx
, aData
, callData
);
1471 if (StaticPrefs::dom_worker_console_dispatch_events_to_main_thread()) {
1472 RefPtr
<ConsoleCallDataWorkerRunnable
> runnable
=
1473 new ConsoleCallDataWorkerRunnable(this, callData
);
1474 Unused
<< NS_WARN_IF(!runnable
->Dispatch(aCx
, aData
));
1478 MainThreadConsoleData
* Console::GetOrCreateMainThreadData() {
1479 AssertIsOnOwningThread();
1481 if (!mMainThreadData
) {
1482 mMainThreadData
= new MainThreadConsoleData();
1485 return mMainThreadData
;
1488 // We store information to lazily compute the stack in the reserved slots of
1489 // LazyStackGetter. The first slot always stores a JS object: it's either the
1490 // JS wrapper of the nsIStackFrame or the actual reified stack representation.
1491 // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
1492 // reified the stack yet, or an UndefinedValue() otherwise.
1493 enum { SLOT_STACKOBJ
, SLOT_RAW_STACK
};
1495 bool LazyStackGetter(JSContext
* aCx
, unsigned aArgc
, JS::Value
* aVp
) {
1496 JS::CallArgs args
= CallArgsFromVp(aArgc
, aVp
);
1497 JS::Rooted
<JSObject
*> callee(aCx
, &args
.callee());
1499 JS::Value v
= js::GetFunctionNativeReserved(&args
.callee(), SLOT_RAW_STACK
);
1500 if (v
.isUndefined()) {
1502 args
.rval().set(js::GetFunctionNativeReserved(callee
, SLOT_STACKOBJ
));
1506 nsIStackFrame
* stack
= reinterpret_cast<nsIStackFrame
*>(v
.toPrivate());
1507 nsTArray
<ConsoleStackEntry
> reifiedStack
;
1508 ReifyStack(aCx
, stack
, reifiedStack
);
1510 JS::Rooted
<JS::Value
> stackVal(aCx
);
1511 if (NS_WARN_IF(!ToJSValue(aCx
, reifiedStack
, &stackVal
))) {
1515 MOZ_ASSERT(stackVal
.isObject());
1517 js::SetFunctionNativeReserved(callee
, SLOT_STACKOBJ
, stackVal
);
1518 js::SetFunctionNativeReserved(callee
, SLOT_RAW_STACK
, JS::UndefinedValue());
1520 args
.rval().set(stackVal
);
1524 void MainThreadConsoleData::ProcessCallData(
1525 JSContext
* aCx
, ConsoleCallData
* aData
,
1526 const Sequence
<JS::Value
>& aArguments
) {
1527 AssertIsOnMainThread();
1529 aData
->mMutex
.AssertCurrentThreadOwns();
1531 JS::Rooted
<JS::Value
> eventValue(aCx
);
1533 // We want to create a console event object and pass it to our
1534 // nsIConsoleAPIStorage implementation. We want to define some accessor
1535 // properties on this object, and those will need to keep an nsIStackFrame
1536 // alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
1537 // further, passing untrusted objects to system code is likely to run afoul of
1538 // Object Xrays. So we want to wrap in a system-principal scope here. But
1539 // which one? We could cheat and try to get the underlying JSObject* of
1540 // mStorage, but that's a bit fragile. Instead, we just use the junk scope,
1541 // with explicit permission from the XPConnect module owner. If you're
1542 // tempted to do that anywhere else, talk to said module owner first.
1544 // aCx and aArguments are in the same compartment.
1545 JS::Rooted
<JSObject
*> targetScope(aCx
, xpc::PrivilegedJunkScope());
1546 if (NS_WARN_IF(!Console::PopulateConsoleNotificationInTheTargetScope(
1547 aCx
, aArguments
, targetScope
, &eventValue
, aData
, &mGroupStack
))) {
1552 mStorage
= do_GetService("@mozilla.org/consoleAPI-storage;1");
1556 NS_WARNING("Failed to get the ConsoleAPIStorage service.");
1560 nsAutoString innerID
;
1562 MOZ_ASSERT(aData
->mIDType
!= ConsoleCallData::eUnknown
);
1563 if (aData
->mIDType
== ConsoleCallData::eString
) {
1564 innerID
= aData
->mInnerIDString
;
1566 MOZ_ASSERT(aData
->mIDType
== ConsoleCallData::eNumber
);
1567 innerID
.AppendInt(aData
->mInnerIDNumber
);
1570 if (aData
->mMethodName
== Console::MethodClear
) {
1571 DebugOnly
<nsresult
> rv
= mStorage
->ClearEvents(innerID
);
1572 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "ClearEvents failed");
1575 if (NS_FAILED(mStorage
->RecordEvent(innerID
, eventValue
))) {
1576 NS_WARNING("Failed to record a console event.");
1581 bool Console::PopulateConsoleNotificationInTheTargetScope(
1582 JSContext
* aCx
, const Sequence
<JS::Value
>& aArguments
,
1583 JS::Handle
<JSObject
*> aTargetScope
,
1584 JS::MutableHandle
<JS::Value
> aEventValue
, ConsoleCallData
* aData
,
1585 nsTArray
<nsString
>* aGroupStack
) {
1588 MOZ_ASSERT(aTargetScope
);
1589 MOZ_ASSERT(JS_IsGlobalObject(aTargetScope
));
1591 aData
->mMutex
.AssertCurrentThreadOwns();
1593 ConsoleStackEntry frame
;
1594 if (aData
->mTopStackFrame
) {
1595 frame
= *aData
->mTopStackFrame
;
1598 ConsoleCommon::ClearException
ce(aCx
);
1599 RootedDictionary
<ConsoleEvent
> event(aCx
);
1601 event
.mAddonId
= aData
->mAddonId
;
1603 event
.mID
.Construct();
1604 event
.mInnerID
.Construct();
1606 event
.mChromeContext
= nsContentUtils::ThreadsafeIsSystemCaller(aCx
);
1608 if (aData
->mIDType
== ConsoleCallData::eString
) {
1609 event
.mID
.Value().SetAsString() = aData
->mOuterIDString
;
1610 event
.mInnerID
.Value().SetAsString() = aData
->mInnerIDString
;
1611 } else if (aData
->mIDType
== ConsoleCallData::eNumber
) {
1612 event
.mID
.Value().SetAsUnsignedLongLong() = aData
->mOuterIDNumber
;
1613 event
.mInnerID
.Value().SetAsUnsignedLongLong() = aData
->mInnerIDNumber
;
1615 // aData->mIDType can be eUnknown when we dispatch notifications via
1616 // mConsoleEventNotifier.
1617 event
.mID
.Value().SetAsUnsignedLongLong() = 0;
1618 event
.mInnerID
.Value().SetAsUnsignedLongLong() = 0;
1621 event
.mConsoleID
= aData
->mConsoleID
;
1622 event
.mLevel
= aData
->mMethodString
;
1623 event
.mFilename
= frame
.mFilename
;
1624 event
.mPrefix
= aData
->mPrefix
;
1626 nsCOMPtr
<nsIURI
> filenameURI
;
1628 if (NS_IsMainThread() &&
1629 NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI
), frame
.mFilename
)) &&
1630 NS_SUCCEEDED(filenameURI
->GetPassword(pass
)) && !pass
.IsEmpty()) {
1631 nsCOMPtr
<nsISensitiveInfoHiddenURI
> safeURI
=
1632 do_QueryInterface(filenameURI
);
1634 if (safeURI
&& NS_SUCCEEDED(safeURI
->GetSensitiveInfoHiddenSpec(spec
))) {
1635 event
.mFilename
= spec
;
1639 event
.mSourceId
= frame
.mSourceId
;
1640 event
.mLineNumber
= frame
.mLineNumber
;
1641 event
.mColumnNumber
= frame
.mColumnNumber
;
1642 event
.mFunctionName
= frame
.mFunctionName
;
1643 event
.mTimeStamp
= aData
->mMicroSecondTimeStamp
/ PR_USEC_PER_MSEC
;
1644 event
.mMicroSecondTimeStamp
= aData
->mMicroSecondTimeStamp
;
1645 event
.mPrivate
= aData
->mOriginAttributes
.IsPrivateBrowsing();
1647 switch (aData
->mMethodName
) {
1652 case MethodException
:
1656 case MethodGroupCollapsed
:
1658 event
.mArguments
.Construct();
1659 event
.mStyles
.Construct();
1660 if (NS_WARN_IF(!ProcessArguments(aCx
, aArguments
,
1661 event
.mArguments
.Value(),
1662 event
.mStyles
.Value()))) {
1669 event
.mArguments
.Construct();
1671 !event
.mArguments
.Value().AppendElements(aArguments
, fallible
))) {
1676 if (aData
->mMethodName
== MethodGroup
||
1677 aData
->mMethodName
== MethodGroupCollapsed
) {
1678 ComposeAndStoreGroupName(aCx
, event
.mArguments
.Value(), event
.mGroupName
,
1682 else if (aData
->mMethodName
== MethodGroupEnd
) {
1683 if (!UnstoreGroupName(event
.mGroupName
, aGroupStack
)) {
1688 else if (aData
->mMethodName
== MethodTime
&& !aArguments
.IsEmpty()) {
1689 event
.mTimer
= CreateStartTimerValue(aCx
, aData
->mStartTimerLabel
,
1690 aData
->mStartTimerStatus
);
1693 else if ((aData
->mMethodName
== MethodTimeEnd
||
1694 aData
->mMethodName
== MethodTimeLog
) &&
1695 !aArguments
.IsEmpty()) {
1696 event
.mTimer
= CreateLogOrEndTimerValue(aCx
, aData
->mLogTimerLabel
,
1697 aData
->mLogTimerDuration
,
1698 aData
->mLogTimerStatus
);
1701 else if (aData
->mMethodName
== MethodCount
||
1702 aData
->mMethodName
== MethodCountReset
) {
1703 event
.mCounter
= CreateCounterOrResetCounterValue(aCx
, aData
->mCountLabel
,
1704 aData
->mCountValue
);
1707 JSAutoRealm
ar2(aCx
, aTargetScope
);
1709 if (NS_WARN_IF(!ToJSValue(aCx
, event
, aEventValue
))) {
1713 JS::Rooted
<JSObject
*> eventObj(aCx
, &aEventValue
.toObject());
1714 if (NS_WARN_IF(!JS_DefineProperty(aCx
, eventObj
, "wrappedJSObject", eventObj
,
1715 JSPROP_ENUMERATE
))) {
1719 if (ShouldIncludeStackTrace(aData
->mMethodName
)) {
1720 // Now define the "stacktrace" property on eventObj. There are two cases
1721 // here. Either we came from a worker and have a reified stack, or we want
1722 // to define a getter that will lazily reify the stack.
1723 if (aData
->mReifiedStack
) {
1724 JS::Rooted
<JS::Value
> stacktrace(aCx
);
1725 if (NS_WARN_IF(!ToJSValue(aCx
, *aData
->mReifiedStack
, &stacktrace
)) ||
1726 NS_WARN_IF(!JS_DefineProperty(aCx
, eventObj
, "stacktrace", stacktrace
,
1727 JSPROP_ENUMERATE
))) {
1732 js::NewFunctionWithReserved(aCx
, LazyStackGetter
, 0, 0, "stacktrace");
1733 if (NS_WARN_IF(!fun
)) {
1737 JS::Rooted
<JSObject
*> funObj(aCx
, JS_GetFunctionObject(fun
));
1739 // We want to store our stack in the function and have it stay alive. But
1740 // we also need sane access to the C++ nsIStackFrame. So store both a JS
1741 // wrapper and the raw pointer: the former will keep the latter alive.
1742 JS::Rooted
<JS::Value
> stackVal(aCx
);
1743 nsresult rv
= nsContentUtils::WrapNative(aCx
, aData
->mStack
, &stackVal
);
1744 if (NS_WARN_IF(NS_FAILED(rv
))) {
1748 js::SetFunctionNativeReserved(funObj
, SLOT_STACKOBJ
, stackVal
);
1749 js::SetFunctionNativeReserved(funObj
, SLOT_RAW_STACK
,
1750 JS::PrivateValue(aData
->mStack
.get()));
1752 if (NS_WARN_IF(!JS_DefineProperty(aCx
, eventObj
, "stacktrace", funObj
,
1753 nullptr, JSPROP_ENUMERATE
))) {
1764 // Helper method for ProcessArguments. Flushes output, if non-empty, to
1766 bool FlushOutput(JSContext
* aCx
, Sequence
<JS::Value
>& aSequence
,
1767 nsString
& aOutput
) {
1768 if (!aOutput
.IsEmpty()) {
1769 JS::Rooted
<JSString
*> str(
1770 aCx
, JS_NewUCStringCopyN(aCx
, aOutput
.get(), aOutput
.Length()));
1771 if (NS_WARN_IF(!str
)) {
1775 if (NS_WARN_IF(!aSequence
.AppendElement(JS::StringValue(str
), fallible
))) {
1787 static void MakeFormatString(nsCString
& aFormat
, int32_t aInteger
,
1788 int32_t aMantissa
, char aCh
) {
1789 aFormat
.Append('%');
1790 if (aInteger
>= 0) {
1791 aFormat
.AppendInt(aInteger
);
1794 if (aMantissa
>= 0) {
1795 aFormat
.Append('.');
1796 aFormat
.AppendInt(aMantissa
);
1799 aFormat
.Append(aCh
);
1802 // If the first JS::Value of the array is a string, this method uses it to
1803 // format a string. The supported sequences are:
1807 // %o,%O - a JS object.
1808 // %c - style string.
1809 // The output is an array where any object is a separated item, the rest is
1810 // unified in a format string.
1811 // Example if the input is:
1812 // "string: %s, integer: %d, object: %o, double: %f", 's', 1, window, 0.9
1813 // The output will be:
1814 // [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
1816 // The aStyles array is populated with the style strings that the function
1817 // finds based the format string. The index of the styles matches the indexes
1818 // of elements that need the custom styling from aSequence. For elements with
1819 // no custom styling the array is padded with null elements.
1820 static bool ProcessArguments(JSContext
* aCx
, const Sequence
<JS::Value
>& aData
,
1821 Sequence
<JS::Value
>& aSequence
,
1822 Sequence
<nsString
>& aStyles
) {
1823 // This method processes the arguments as format strings (%d, %i, %s...)
1824 // only if the first element of them is a valid and not-empty string.
1826 if (aData
.IsEmpty()) {
1830 if (aData
.Length() == 1 || !aData
[0].isString()) {
1831 return aSequence
.AppendElements(aData
, fallible
);
1834 JS::Rooted
<JS::Value
> format(aCx
, aData
[0]);
1835 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, format
));
1836 if (NS_WARN_IF(!jsString
)) {
1840 nsAutoJSString string
;
1841 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
1845 if (string
.IsEmpty()) {
1846 return aSequence
.AppendElements(aData
, fallible
);
1849 nsString::const_iterator start
, end
;
1850 string
.BeginReading(start
);
1851 string
.EndReading(end
);
1856 while (start
!= end
) {
1857 if (*start
!= '%') {
1858 output
.Append(*start
);
1869 if (*start
== '%') {
1870 output
.Append(*start
);
1878 int32_t integer
= -1;
1879 int32_t mantissa
= -1;
1881 // Let's parse %<number>.<number> for %d and %f
1882 if (*start
>= '0' && *start
<= '9') {
1886 integer
= integer
* 10 + *start
- '0';
1889 } while (*start
>= '0' && *start
<= '9' && start
!= end
);
1897 if (*start
== '.') {
1906 // '.' must be followed by a number.
1907 if (*start
< '0' || *start
> '9') {
1915 mantissa
= mantissa
* 10 + *start
- '0';
1918 } while (*start
>= '0' && *start
<= '9' && start
!= end
);
1933 if (NS_WARN_IF(!FlushOutput(aCx
, aSequence
, output
))) {
1937 JS::Rooted
<JS::Value
> v(aCx
);
1938 if (index
< aData
.Length()) {
1942 if (NS_WARN_IF(!aSequence
.AppendElement(v
, fallible
))) {
1950 // If there isn't any output but there's already a style, then
1951 // discard the previous style and use the next one instead.
1952 if (output
.IsEmpty() && !aStyles
.IsEmpty()) {
1953 aStyles
.RemoveLastElement();
1956 if (NS_WARN_IF(!FlushOutput(aCx
, aSequence
, output
))) {
1960 if (index
< aData
.Length()) {
1961 JS::Rooted
<JS::Value
> v(aCx
, aData
[index
++]);
1962 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, v
));
1963 if (NS_WARN_IF(!jsString
)) {
1967 int32_t diff
= aSequence
.Length() - aStyles
.Length();
1969 for (int32_t i
= 0; i
< diff
; i
++) {
1970 if (NS_WARN_IF(!aStyles
.AppendElement(VoidString(), fallible
))) {
1976 nsAutoJSString string
;
1977 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
1981 if (NS_WARN_IF(!aStyles
.AppendElement(string
, fallible
))) {
1989 if (index
< aData
.Length()) {
1990 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
1991 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
1992 if (NS_WARN_IF(!jsString
)) {
1997 if (NS_WARN_IF(!v
.init(aCx
, jsString
))) {
2007 if (index
< aData
.Length()) {
2008 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
2010 if (value
.isBigInt()) {
2011 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
2012 if (NS_WARN_IF(!jsString
)) {
2017 if (NS_WARN_IF(!v
.init(aCx
, jsString
))) {
2025 if (NS_WARN_IF(!JS::ToInt32(aCx
, value
, &v
))) {
2030 MakeFormatString(format
, integer
, mantissa
, 'd');
2031 output
.AppendPrintf(format
.get(), v
);
2036 if (index
< aData
.Length()) {
2037 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
2040 if (NS_WARN_IF(!JS::ToNumber(aCx
, value
, &v
))) {
2044 // nspr returns "nan", but we want to expose it as "NaN"
2045 if (std::isnan(v
)) {
2046 output
.AppendFloat(v
);
2049 MakeFormatString(format
, integer
, mantissa
, 'f');
2050 output
.AppendPrintf(format
.get(), v
);
2061 if (NS_WARN_IF(!FlushOutput(aCx
, aSequence
, output
))) {
2065 // Discard trailing style element if there is no output to apply it to.
2066 if (aStyles
.Length() > aSequence
.Length()) {
2067 aStyles
.TruncateLength(aSequence
.Length());
2070 // The rest of the array, if unused by the format string.
2071 for (; index
< aData
.Length(); ++index
) {
2072 if (NS_WARN_IF(!aSequence
.AppendElement(aData
[index
], fallible
))) {
2080 // Stringify and Concat all the JS::Value in a single string using ' ' as
2081 // separator. The new group name will be stored in aGroupStack array.
2082 static void ComposeAndStoreGroupName(JSContext
* aCx
,
2083 const Sequence
<JS::Value
>& aData
,
2085 nsTArray
<nsString
>* aGroupStack
) {
2087 aName
, u
" "_ns
, aData
, [aCx
](nsAString
& dest
, const JS::Value
& valueRef
) {
2088 JS::Rooted
<JS::Value
> value(aCx
, valueRef
);
2089 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
2094 nsAutoJSString string
;
2095 if (!string
.init(aCx
, jsString
)) {
2099 dest
.Append(string
);
2102 aGroupStack
->AppendElement(aName
);
2105 // Remove the last group name and return that name. It returns false if
2106 // aGroupStack is empty.
2107 static bool UnstoreGroupName(nsAString
& aName
,
2108 nsTArray
<nsString
>* aGroupStack
) {
2109 if (aGroupStack
->IsEmpty()) {
2113 aName
= aGroupStack
->PopLastElement();
2117 Console::TimerStatus
Console::StartTimer(JSContext
* aCx
, const JS::Value
& aName
,
2118 DOMHighResTimeStamp aTimestamp
,
2119 nsAString
& aTimerLabel
,
2120 DOMHighResTimeStamp
* aTimerValue
) {
2121 AssertIsOnOwningThread();
2122 MOZ_ASSERT(aTimerValue
);
2126 if (NS_WARN_IF(mTimerRegistry
.Count() >= MAX_PAGE_TIMERS
)) {
2127 return eTimerMaxReached
;
2130 JS::Rooted
<JS::Value
> name(aCx
, aName
);
2131 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, name
));
2132 if (NS_WARN_IF(!jsString
)) {
2133 return eTimerJSException
;
2136 nsAutoJSString label
;
2137 if (NS_WARN_IF(!label
.init(aCx
, jsString
))) {
2138 return eTimerJSException
;
2141 aTimerLabel
= label
;
2143 if (mTimerRegistry
.WithEntryHandle(label
, [&](auto&& entry
) {
2147 entry
.Insert(aTimestamp
);
2150 return eTimerAlreadyExists
;
2153 *aTimerValue
= aTimestamp
;
2158 JS::Value
Console::CreateStartTimerValue(JSContext
* aCx
,
2159 const nsAString
& aTimerLabel
,
2160 TimerStatus aTimerStatus
) {
2161 MOZ_ASSERT(aTimerStatus
!= eTimerUnknown
);
2163 if (aTimerStatus
!= eTimerDone
) {
2164 return CreateTimerError(aCx
, aTimerLabel
, aTimerStatus
);
2167 RootedDictionary
<ConsoleTimerStart
> timer(aCx
);
2169 timer
.mName
= aTimerLabel
;
2171 JS::Rooted
<JS::Value
> value(aCx
);
2172 if (!ToJSValue(aCx
, timer
, &value
)) {
2173 return JS::UndefinedValue();
2179 Console::TimerStatus
Console::LogTimer(JSContext
* aCx
, const JS::Value
& aName
,
2180 DOMHighResTimeStamp aTimestamp
,
2181 nsAString
& aTimerLabel
,
2182 double* aTimerDuration
,
2183 bool aCancelTimer
) {
2184 AssertIsOnOwningThread();
2185 MOZ_ASSERT(aTimerDuration
);
2187 *aTimerDuration
= 0;
2189 JS::Rooted
<JS::Value
> name(aCx
, aName
);
2190 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, name
));
2191 if (NS_WARN_IF(!jsString
)) {
2192 return eTimerJSException
;
2196 if (NS_WARN_IF(!key
.init(aCx
, jsString
))) {
2197 return eTimerJSException
;
2202 DOMHighResTimeStamp value
= 0;
2205 if (!mTimerRegistry
.Remove(key
, &value
)) {
2206 NS_WARNING("mTimerRegistry entry not found");
2207 return eTimerDoesntExist
;
2210 if (!mTimerRegistry
.Get(key
, &value
)) {
2211 NS_WARNING("mTimerRegistry entry not found");
2212 return eTimerDoesntExist
;
2216 *aTimerDuration
= aTimestamp
- value
;
2221 JS::Value
Console::CreateLogOrEndTimerValue(JSContext
* aCx
,
2222 const nsAString
& aLabel
,
2224 TimerStatus aStatus
) {
2225 if (aStatus
!= eTimerDone
) {
2226 return CreateTimerError(aCx
, aLabel
, aStatus
);
2229 RootedDictionary
<ConsoleTimerLogOrEnd
> timer(aCx
);
2230 timer
.mName
= aLabel
;
2231 timer
.mDuration
= aDuration
;
2233 JS::Rooted
<JS::Value
> value(aCx
);
2234 if (!ToJSValue(aCx
, timer
, &value
)) {
2235 return JS::UndefinedValue();
2242 JS::Value
Console::CreateTimerError(JSContext
* aCx
, const nsAString
& aLabel
,
2243 TimerStatus aStatus
) {
2244 MOZ_ASSERT(aStatus
!= eTimerUnknown
&& aStatus
!= eTimerDone
);
2246 RootedDictionary
<ConsoleTimerError
> error(aCx
);
2248 error
.mName
= aLabel
;
2251 case eTimerAlreadyExists
:
2252 error
.mError
.AssignLiteral("timerAlreadyExists");
2255 case eTimerDoesntExist
:
2256 error
.mError
.AssignLiteral("timerDoesntExist");
2259 case eTimerJSException
:
2260 error
.mError
.AssignLiteral("timerJSError");
2263 case eTimerMaxReached
:
2264 error
.mError
.AssignLiteral("maxTimersExceeded");
2268 MOZ_CRASH("Unsupported status");
2272 JS::Rooted
<JS::Value
> value(aCx
);
2273 if (!ToJSValue(aCx
, error
, &value
)) {
2274 return JS::UndefinedValue();
2280 uint32_t Console::IncreaseCounter(JSContext
* aCx
,
2281 const Sequence
<JS::Value
>& aArguments
,
2282 nsAString
& aCountLabel
) {
2283 AssertIsOnOwningThread();
2285 ConsoleCommon::ClearException
ce(aCx
);
2287 MOZ_ASSERT(!aArguments
.IsEmpty());
2289 JS::Rooted
<JS::Value
> labelValue(aCx
, aArguments
[0]);
2290 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, labelValue
));
2292 return 0; // We cannot continue.
2295 nsAutoJSString string
;
2296 if (!string
.init(aCx
, jsString
)) {
2297 return 0; // We cannot continue.
2300 aCountLabel
= string
;
2302 const bool maxCountersReached
= mCounterRegistry
.Count() >= MAX_PAGE_COUNTERS
;
2303 return mCounterRegistry
.WithEntryHandle(
2304 aCountLabel
, [maxCountersReached
](auto&& entry
) -> uint32_t {
2308 if (maxCountersReached
) {
2309 return MAX_PAGE_COUNTERS
;
2313 return entry
.Data();
2317 uint32_t Console::ResetCounter(JSContext
* aCx
,
2318 const Sequence
<JS::Value
>& aArguments
,
2319 nsAString
& aCountLabel
) {
2320 AssertIsOnOwningThread();
2322 ConsoleCommon::ClearException
ce(aCx
);
2324 MOZ_ASSERT(!aArguments
.IsEmpty());
2326 JS::Rooted
<JS::Value
> labelValue(aCx
, aArguments
[0]);
2327 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, labelValue
));
2329 return 0; // We cannot continue.
2332 nsAutoJSString string
;
2333 if (!string
.init(aCx
, jsString
)) {
2334 return 0; // We cannot continue.
2337 aCountLabel
= string
;
2339 if (mCounterRegistry
.Remove(aCountLabel
)) {
2343 // Let's return something different than 0 if the key doesn't exist.
2344 return MAX_PAGE_COUNTERS
;
2347 // This method generates a ConsoleCounter dictionary as JS::Value. If
2348 // aCountValue is == MAX_PAGE_COUNTERS it generates a ConsoleCounterError
2349 // instead. See IncreaseCounter.
2350 // * aCx - this is the context that will root the returned value.
2351 // * aCountLabel - this label must be what IncreaseCounter received as
2353 // * aCountValue - the return value of IncreaseCounter.
2354 static JS::Value
CreateCounterOrResetCounterValue(JSContext
* aCx
,
2355 const nsAString
& aCountLabel
,
2356 uint32_t aCountValue
) {
2357 ConsoleCommon::ClearException
ce(aCx
);
2359 if (aCountValue
== MAX_PAGE_COUNTERS
) {
2360 RootedDictionary
<ConsoleCounterError
> error(aCx
);
2361 error
.mLabel
= aCountLabel
;
2362 error
.mError
.AssignLiteral("counterDoesntExist");
2364 JS::Rooted
<JS::Value
> value(aCx
);
2365 if (!ToJSValue(aCx
, error
, &value
)) {
2366 return JS::UndefinedValue();
2372 RootedDictionary
<ConsoleCounter
> data(aCx
);
2373 data
.mLabel
= aCountLabel
;
2374 data
.mCount
= aCountValue
;
2376 JS::Rooted
<JS::Value
> value(aCx
);
2377 if (!ToJSValue(aCx
, data
, &value
)) {
2378 return JS::UndefinedValue();
2385 bool Console::ShouldIncludeStackTrace(MethodName aMethodName
) {
2386 switch (aMethodName
) {
2388 case MethodException
:
2397 JSObject
* MainThreadConsoleData::GetOrCreateSandbox(JSContext
* aCx
,
2398 nsIPrincipal
* aPrincipal
) {
2399 AssertIsOnMainThread();
2402 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
2403 MOZ_ASSERT(xpc
, "This should never be null!");
2405 JS::Rooted
<JSObject
*> sandbox(aCx
);
2406 nsresult rv
= xpc
->CreateSandbox(aCx
, aPrincipal
, sandbox
.address());
2407 if (NS_WARN_IF(NS_FAILED(rv
))) {
2411 mSandbox
= new JSObjectHolder(aCx
, sandbox
);
2414 return mSandbox
->GetJSObject();
2417 bool Console::StoreCallData(JSContext
* aCx
, ConsoleCallData
* aCallData
,
2418 const Sequence
<JS::Value
>& aArguments
) {
2419 AssertIsOnOwningThread();
2421 if (NS_WARN_IF(!mArgumentStorage
.growBy(1))) {
2424 if (!mArgumentStorage
.end()[-1].Initialize(aCx
, aArguments
)) {
2425 mArgumentStorage
.shrinkBy(1);
2429 MOZ_ASSERT(aCallData
);
2430 MOZ_ASSERT(!mCallDataStorage
.Contains(aCallData
));
2432 mCallDataStorage
.AppendElement(aCallData
);
2434 MOZ_ASSERT(mCallDataStorage
.Length() == mArgumentStorage
.length());
2436 if (mCallDataStorage
.Length() > STORAGE_MAX_EVENTS
) {
2437 mCallDataStorage
.RemoveElementAt(0);
2438 mArgumentStorage
.erase(&mArgumentStorage
[0]);
2443 void Console::UnstoreCallData(ConsoleCallData
* aCallData
) {
2444 AssertIsOnOwningThread();
2446 MOZ_ASSERT(aCallData
);
2447 MOZ_ASSERT(mCallDataStorage
.Length() == mArgumentStorage
.length());
2449 size_t index
= mCallDataStorage
.IndexOf(aCallData
);
2450 // It can be that mCallDataStorage has been already cleaned in case the
2451 // processing of the argument of some Console methods triggers the
2453 if (index
== mCallDataStorage
.NoIndex
) {
2457 mCallDataStorage
.RemoveElementAt(index
);
2458 mArgumentStorage
.erase(&mArgumentStorage
[index
]);
2461 void Console::NotifyHandler(JSContext
* aCx
,
2462 const Sequence
<JS::Value
>& aArguments
,
2463 ConsoleCallData
* aCallData
) {
2464 AssertIsOnOwningThread();
2465 MOZ_ASSERT(!NS_IsMainThread());
2466 MOZ_ASSERT(aCallData
);
2468 if (!mConsoleEventNotifier
) {
2472 JS::Rooted
<JS::Value
> value(aCx
);
2474 JS::Rooted
<JSObject
*> callableGlobal(
2475 aCx
, mConsoleEventNotifier
->CallbackGlobalOrNull());
2476 if (NS_WARN_IF(!callableGlobal
)) {
2480 // aCx and aArguments are in the same compartment because this method is
2481 // called directly when a Console.something() runs.
2482 // mConsoleEventNotifier->CallbackGlobal() is the scope where value will be
2484 if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
2485 aCx
, aArguments
, callableGlobal
, &value
, aCallData
, &mGroupStack
))) {
2489 JS::Rooted
<JS::Value
> ignored(aCx
);
2490 RefPtr
<AnyCallback
> notifier(mConsoleEventNotifier
);
2491 notifier
->Call(value
, &ignored
);
2494 void Console::RetrieveConsoleEvents(JSContext
* aCx
,
2495 nsTArray
<JS::Value
>& aEvents
,
2497 AssertIsOnOwningThread();
2499 // We don't want to expose this functionality to main-thread yet.
2500 MOZ_ASSERT(!NS_IsMainThread());
2502 JS::Rooted
<JSObject
*> targetScope(aCx
, JS::CurrentGlobalOrNull(aCx
));
2504 for (uint32_t i
= 0; i
< mArgumentStorage
.length(); ++i
) {
2505 JS::Rooted
<JS::Value
> value(aCx
);
2507 JS::Rooted
<JSObject
*> sequenceScope(aCx
, mArgumentStorage
[i
].Global());
2508 JSAutoRealm
ar(aCx
, sequenceScope
);
2510 Sequence
<JS::Value
> sequence
;
2511 SequenceRooter
<JS::Value
> arguments(aCx
, &sequence
);
2513 if (!mArgumentStorage
[i
].PopulateArgumentsSequence(sequence
)) {
2514 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2518 // Here we have aCx and sequence in the same compartment.
2519 // targetScope is the destination scope and value will be populated in its
2522 MutexAutoLock
lock(mCallDataStorage
[i
]->mMutex
);
2523 if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
2524 aCx
, sequence
, targetScope
, &value
, mCallDataStorage
[i
],
2526 aRv
.Throw(NS_ERROR_FAILURE
);
2531 aEvents
.AppendElement(value
);
2535 void Console::SetConsoleEventHandler(AnyCallback
* aHandler
) {
2536 AssertIsOnOwningThread();
2538 // We don't want to expose this functionality to main-thread yet.
2539 MOZ_ASSERT(!NS_IsMainThread());
2541 mConsoleEventNotifier
= aHandler
;
2544 void Console::AssertIsOnOwningThread() const {
2545 NS_ASSERT_OWNINGTHREAD(Console
);
2548 bool Console::IsShuttingDown() const {
2549 MOZ_ASSERT(mStatus
!= eUnknown
);
2550 return mStatus
== eShuttingDown
;
2554 already_AddRefed
<Console
> Console::GetConsole(const GlobalObject
& aGlobal
) {
2556 RefPtr
<Console
> console
= GetConsoleInternal(aGlobal
, rv
);
2557 if (NS_WARN_IF(rv
.Failed()) || !console
) {
2558 rv
.SuppressException();
2562 console
->AssertIsOnOwningThread();
2564 if (console
->IsShuttingDown()) {
2568 return console
.forget();
2572 already_AddRefed
<Console
> Console::GetConsoleInternal(
2573 const GlobalObject
& aGlobal
, ErrorResult
& aRv
) {
2575 if (NS_IsMainThread()) {
2576 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
=
2577 do_QueryInterface(aGlobal
.GetAsSupports());
2579 // we are probably running a chrome script.
2581 RefPtr
<Console
> console
= new Console(aGlobal
.Context(), nullptr, 0, 0);
2582 console
->Initialize(aRv
);
2583 if (NS_WARN_IF(aRv
.Failed())) {
2587 return console
.forget();
2590 nsGlobalWindowInner
* window
= nsGlobalWindowInner::Cast(innerWindow
);
2591 return window
->GetConsole(aGlobal
.Context(), aRv
);
2595 nsCOMPtr
<WorkletGlobalScope
> workletScope
=
2596 do_QueryInterface(aGlobal
.GetAsSupports());
2598 WorkletThread::AssertIsOnWorkletThread();
2599 return workletScope
->GetConsole(aGlobal
.Context(), aRv
);
2603 MOZ_ASSERT(!NS_IsMainThread());
2605 JSContext
* cx
= aGlobal
.Context();
2606 WorkerPrivate
* workerPrivate
= GetWorkerPrivateFromContext(cx
);
2607 MOZ_ASSERT(workerPrivate
);
2609 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
2610 if (NS_WARN_IF(!global
)) {
2614 WorkerGlobalScope
* scope
= workerPrivate
->GlobalScope();
2617 // Normal worker scope.
2618 if (scope
== global
) {
2619 return scope
->GetConsole(aRv
);
2622 // Debugger worker scope
2624 WorkerDebuggerGlobalScope
* debuggerScope
=
2625 workerPrivate
->DebuggerGlobalScope();
2626 MOZ_ASSERT(debuggerScope
);
2627 MOZ_ASSERT(debuggerScope
== global
, "Which kind of global do we have?");
2629 return debuggerScope
->GetConsole(aRv
);
2632 bool Console::MonotonicTimer(JSContext
* aCx
, MethodName aMethodName
,
2633 const Sequence
<JS::Value
>& aData
,
2634 DOMHighResTimeStamp
* aTimeStamp
) {
2635 if (nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
= do_QueryInterface(mGlobal
)) {
2636 nsGlobalWindowInner
* win
= nsGlobalWindowInner::Cast(innerWindow
);
2639 RefPtr
<Performance
> performance
= win
->GetPerformance();
2644 *aTimeStamp
= performance
->Now();
2648 if (NS_IsMainThread()) {
2649 *aTimeStamp
= (TimeStamp::Now() - mCreationTimeStamp
).ToMilliseconds();
2653 if (nsCOMPtr
<WorkletGlobalScope
> workletGlobal
= do_QueryInterface(mGlobal
)) {
2654 *aTimeStamp
= workletGlobal
->TimeStampToDOMHighRes(TimeStamp::Now());
2658 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2659 MOZ_ASSERT(workerPrivate
);
2661 *aTimeStamp
= workerPrivate
->TimeStampToDOMHighRes(TimeStamp::Now());
2666 already_AddRefed
<ConsoleInstance
> Console::CreateInstance(
2667 const GlobalObject
& aGlobal
, const ConsoleInstanceOptions
& aOptions
) {
2668 RefPtr
<ConsoleInstance
> console
=
2669 new ConsoleInstance(aGlobal
.Context(), aOptions
);
2670 return console
.forget();
2673 void Console::StringifyElement(Element
* aElement
, nsAString
& aOut
) {
2674 aOut
.AppendLiteral("<");
2675 aOut
.Append(aElement
->LocalName());
2676 uint32_t attrCount
= aElement
->GetAttrCount();
2677 nsAutoString idAttr
;
2678 nsAutoString classAttr
;
2679 nsAutoString nameAttr
;
2680 nsAutoString otherAttrs
;
2681 for (uint32_t i
= 0; i
< attrCount
; i
++) {
2682 BorrowedAttrInfo attrInfo
= aElement
->GetAttrInfoAt(i
);
2683 nsAutoString attrValue
;
2684 attrInfo
.mValue
->ToString(attrValue
);
2686 const nsAttrName
* attrName
= attrInfo
.mName
;
2687 if (attrName
->Equals(nsGkAtoms::id
)) {
2688 idAttr
.AppendLiteral(" id=\"");
2689 idAttr
.Append(attrValue
);
2690 idAttr
.AppendLiteral("\"");
2691 } else if (attrName
->Equals(nsGkAtoms::_class
)) {
2692 classAttr
.AppendLiteral(" class=\"");
2693 classAttr
.Append(attrValue
);
2694 classAttr
.AppendLiteral("\"");
2695 } else if (attrName
->Equals(nsGkAtoms::name
)) {
2696 nameAttr
.AppendLiteral(" name=\"");
2697 nameAttr
.Append(attrValue
);
2698 nameAttr
.AppendLiteral("\"");
2700 nsAutoString attrNameStr
;
2701 attrName
->GetQualifiedName(attrNameStr
);
2702 otherAttrs
.AppendLiteral(" ");
2703 otherAttrs
.Append(attrNameStr
);
2704 otherAttrs
.AppendLiteral("=\"");
2705 otherAttrs
.Append(attrValue
);
2706 otherAttrs
.AppendLiteral("\"");
2709 if (!idAttr
.IsEmpty()) {
2710 aOut
.Append(idAttr
);
2712 if (!classAttr
.IsEmpty()) {
2713 aOut
.Append(classAttr
);
2715 if (!nameAttr
.IsEmpty()) {
2716 aOut
.Append(nameAttr
);
2718 if (!otherAttrs
.IsEmpty()) {
2719 aOut
.Append(otherAttrs
);
2721 aOut
.AppendLiteral(">");
2724 void Console::MaybeExecuteDumpFunction(JSContext
* aCx
, MethodName aMethodName
,
2725 const nsAString
& aMethodString
,
2726 const Sequence
<JS::Value
>& aData
,
2727 nsIStackFrame
* aStack
,
2728 DOMHighResTimeStamp aMonotonicTimer
) {
2729 if (mLogModule
->ShouldLog(InternalLogLevelToMozLog(aMethodName
))) {
2730 nsString message
= GetDumpMessage(aCx
, aMethodName
, aMethodString
, aData
,
2731 aStack
, aMonotonicTimer
, true);
2733 MOZ_LOG(mLogModule
, InternalLogLevelToMozLog(aMethodName
),
2734 ("%s", NS_ConvertUTF16toUTF8(message
).get()));
2737 if (!mDumpFunction
&& !mDumpToStdout
) {
2740 nsString message
= GetDumpMessage(aCx
, aMethodName
, aMethodString
, aData
,
2741 aStack
, aMonotonicTimer
, false);
2743 ExecuteDumpFunction(message
);
2746 nsString
Console::GetDumpMessage(JSContext
* aCx
, MethodName aMethodName
,
2747 const nsAString
& aMethodString
,
2748 const Sequence
<JS::Value
>& aData
,
2749 nsIStackFrame
* aStack
,
2750 DOMHighResTimeStamp aMonotonicTimer
,
2751 bool aIsForMozLog
) {
2753 // MOZ_LOG already logs either console or the prefix
2754 if (!aIsForMozLog
) {
2755 message
.AssignLiteral("console.");
2757 message
.AssignLiteral("");
2759 message
.Append(aMethodString
);
2760 message
.AppendLiteral(": ");
2762 if (!aIsForMozLog
&& !mPrefix
.IsEmpty()) {
2763 message
.Append(mPrefix
);
2764 message
.AppendLiteral(": ");
2767 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
2768 JS::Rooted
<JS::Value
> v(aCx
, aData
[i
]);
2770 Element
* element
= nullptr;
2771 if (NS_SUCCEEDED(UNWRAP_OBJECT(Element
, &v
, element
))) {
2773 message
.AppendLiteral(" ");
2775 StringifyElement(element
, message
);
2780 JS::Rooted
<JSString
*> jsString(aCx
, JS_ValueToSource(aCx
, v
));
2785 nsAutoJSString string
;
2786 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
2791 message
.AppendLiteral(" ");
2794 message
.Append(string
);
2797 if (aMethodName
== MethodTime
|| aMethodName
== MethodTimeEnd
) {
2798 message
.AppendLiteral(" @ ");
2799 message
.AppendFloat(aMonotonicTimer
);
2802 message
.AppendLiteral("\n");
2804 // aStack can be null.
2806 nsCOMPtr
<nsIStackFrame
> stack(aStack
);
2809 nsAutoCString filename
;
2810 stack
->GetFilename(aCx
, filename
);
2812 AppendUTF8toUTF16(filename
, message
);
2813 message
.AppendLiteral(" ");
2815 message
.AppendInt(stack
->GetLineNumber(aCx
));
2816 message
.AppendLiteral(" ");
2818 nsAutoString functionName
;
2819 stack
->GetName(aCx
, functionName
);
2821 message
.Append(functionName
);
2822 message
.AppendLiteral("\n");
2824 nsCOMPtr
<nsIStackFrame
> caller
= stack
->GetCaller(aCx
);
2827 caller
= stack
->GetAsyncCaller(aCx
);
2836 void Console::ExecuteDumpFunction(const nsAString
& aMessage
) {
2837 if (mDumpFunction
) {
2838 RefPtr
<ConsoleInstanceDumpCallback
> dumpFunction(mDumpFunction
);
2839 dumpFunction
->Call(aMessage
);
2843 NS_ConvertUTF16toUTF8
str(aMessage
);
2844 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug
, ("%s", str
.get()));
2846 __android_log_print(ANDROID_LOG_INFO
, "Gecko", "%s", str
.get());
2848 fputs(str
.get(), stdout
);
2852 bool Console::ShouldProceed(MethodName aName
) const {
2853 return mCurrentLogLevel
<= InternalLogLevelToInteger(aName
);
2856 uint32_t Console::WebIDLLogLevelToInteger(ConsoleLogLevel aLevel
) const {
2858 case ConsoleLogLevel::All
:
2860 case ConsoleLogLevel::Debug
:
2862 case ConsoleLogLevel::Log
:
2864 case ConsoleLogLevel::Info
:
2866 case ConsoleLogLevel::Clear
:
2868 case ConsoleLogLevel::Trace
:
2870 case ConsoleLogLevel::TimeLog
:
2872 case ConsoleLogLevel::TimeEnd
:
2874 case ConsoleLogLevel::Time
:
2876 case ConsoleLogLevel::Group
:
2878 case ConsoleLogLevel::GroupEnd
:
2880 case ConsoleLogLevel::Profile
:
2882 case ConsoleLogLevel::ProfileEnd
:
2884 case ConsoleLogLevel::Dir
:
2886 case ConsoleLogLevel::Dirxml
:
2888 case ConsoleLogLevel::Warn
:
2890 case ConsoleLogLevel::Error
:
2892 case ConsoleLogLevel::Off
:
2896 "ConsoleLogLevel is out of sync with the Console implementation!");
2901 uint32_t Console::InternalLogLevelToInteger(MethodName aName
) const {
2911 case MethodException
:
2925 case MethodGroupCollapsed
:
2927 case MethodGroupEnd
:
2935 case MethodTimeStamp
:
2941 case MethodCountReset
:
2947 case MethodProfileEnd
:
2950 MOZ_CRASH("MethodName is out of sync with the Console implementation!");
2955 LogLevel
Console::InternalLogLevelToMozLog(MethodName aName
) const {
2958 return LogLevel::Info
;
2960 return LogLevel::Info
;
2962 return LogLevel::Warning
;
2964 return LogLevel::Error
;
2965 case MethodException
:
2966 return LogLevel::Error
;
2968 return LogLevel::Debug
;
2970 return LogLevel::Info
;
2972 return LogLevel::Info
;
2974 return LogLevel::Info
;
2976 return LogLevel::Info
;
2978 return LogLevel::Info
;
2979 case MethodGroupCollapsed
:
2980 return LogLevel::Info
;
2981 case MethodGroupEnd
:
2982 return LogLevel::Info
;
2984 return LogLevel::Info
;
2986 return LogLevel::Info
;
2988 return LogLevel::Info
;
2989 case MethodTimeStamp
:
2990 return LogLevel::Info
;
2992 return LogLevel::Error
;
2994 return LogLevel::Info
;
2995 case MethodCountReset
:
2996 return LogLevel::Info
;
2998 return LogLevel::Info
;
3000 return LogLevel::Info
;
3001 case MethodProfileEnd
:
3002 return LogLevel::Info
;
3004 MOZ_CRASH("MethodName is out of sync with the Console implementation!");
3005 return LogLevel::Disabled
;
3009 bool Console::ArgumentData::Initialize(JSContext
* aCx
,
3010 const Sequence
<JS::Value
>& aArguments
) {
3011 mGlobal
= JS::CurrentGlobalOrNull(aCx
);
3013 if (NS_WARN_IF(!mArguments
.AppendElements(aArguments
, fallible
))) {
3020 void Console::ArgumentData::Trace(const TraceCallbacks
& aCallbacks
,
3022 ArgumentData
* tmp
= this;
3023 for (uint32_t i
= 0; i
< mArguments
.Length(); ++i
) {
3024 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArguments
[i
])
3027 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal
)
3030 bool Console::ArgumentData::PopulateArgumentsSequence(
3031 Sequence
<JS::Value
>& aSequence
) const {
3032 AssertIsOnOwningThread();
3034 for (uint32_t i
= 0; i
< mArguments
.Length(); ++i
) {
3035 if (NS_WARN_IF(!aSequence
.AppendElement(mArguments
[i
], fallible
))) {
3043 } // namespace mozilla::dom