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/Preferences.h"
37 #include "mozilla/StaticPrefs_devtools.h"
38 #include "mozilla/StaticPrefs_dom.h"
39 #include "nsCycleCollectionParticipant.h"
40 #include "nsDOMNavigationTiming.h"
41 #include "nsGlobalWindowInner.h"
42 #include "nsJSUtils.h"
43 #include "nsNetUtil.h"
44 #include "xpcpublic.h"
45 #include "nsContentUtils.h"
46 #include "nsDocShell.h"
47 #include "nsProxyRelease.h"
48 #include "nsReadableUtils.h"
50 #include "nsIConsoleAPIStorage.h"
51 #include "nsIException.h" // for nsIStackFrame
52 #include "nsIInterfaceRequestorUtils.h"
53 #include "nsILoadContext.h"
54 #include "nsISensitiveInfoHiddenURI.h"
55 #include "nsISupportsPrimitives.h"
56 #include "nsIWebNavigation.h"
57 #include "nsIXPConnect.h"
59 // The maximum allowed number of concurrent timers per page.
60 #define MAX_PAGE_TIMERS 10000
62 // The maximum allowed number of concurrent counters per page.
63 #define MAX_PAGE_COUNTERS 10000
65 // The maximum stacktrace depth when populating the stacktrace array used for
67 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
69 // This tags are used in the Structured Clone Algorithm to move js values from
70 // worker thread to main thread
71 #define CONSOLE_TAG_BLOB JS_SCTAG_USER_MIN
73 // This value is taken from ConsoleAPIStorage.js
74 #define STORAGE_MAX_EVENTS 1000
76 using namespace mozilla::dom::exceptions
;
78 namespace mozilla::dom
{
80 struct ConsoleStructuredCloneData
{
81 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
82 nsTArray
<RefPtr
<BlobImpl
>> mBlobs
;
85 static void ComposeAndStoreGroupName(JSContext
* aCx
,
86 const Sequence
<JS::Value
>& aData
,
88 nsTArray
<nsString
>* aGroupStack
);
89 static bool UnstoreGroupName(nsAString
& aName
, nsTArray
<nsString
>* aGroupStack
);
91 static bool ProcessArguments(JSContext
* aCx
, const Sequence
<JS::Value
>& aData
,
92 Sequence
<JS::Value
>& aSequence
,
93 Sequence
<nsString
>& aStyles
);
95 static JS::Value
CreateCounterOrResetCounterValue(JSContext
* aCx
,
96 const nsAString
& aCountLabel
,
97 uint32_t aCountValue
);
100 * Console API in workers uses the Structured Clone Algorithm to move any value
101 * from the worker thread to the main-thread. Some object cannot be moved and,
102 * in these cases, we convert them to strings.
103 * It's not the best, but at least we are able to show something.
106 class ConsoleCallData final
{
108 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConsoleCallData
)
110 ConsoleCallData(Console::MethodName aName
, const nsAString
& aString
,
112 : mConsoleID(aConsole
->mConsoleID
),
113 mPrefix(aConsole
->mPrefix
),
115 mMicroSecondTimeStamp(JS_Now()),
117 mStartTimerStatus(Console::eTimerUnknown
),
118 mLogTimerDuration(0),
119 mLogTimerStatus(Console::eTimerUnknown
),
120 mCountValue(MAX_PAGE_COUNTERS
),
124 mMethodString(aString
) {}
126 void SetIDs(uint64_t aOuterID
, uint64_t aInnerID
) {
127 MOZ_ASSERT(mIDType
== eUnknown
);
129 mOuterIDNumber
= aOuterID
;
130 mInnerIDNumber
= aInnerID
;
134 void SetIDs(const nsAString
& aOuterID
, const nsAString
& aInnerID
) {
135 MOZ_ASSERT(mIDType
== eUnknown
);
137 mOuterIDString
= aOuterID
;
138 mInnerIDString
= aInnerID
;
142 void SetOriginAttributes(const OriginAttributes
& aOriginAttributes
) {
143 mOriginAttributes
= aOriginAttributes
;
146 void SetAddonId(nsIPrincipal
* aPrincipal
) {
147 nsAutoString addonId
;
148 aPrincipal
->GetAddonId(addonId
);
153 void AssertIsOnOwningThread() const {
154 NS_ASSERT_OWNINGTHREAD(ConsoleCallData
);
157 const nsString mConsoleID
;
158 const nsString mPrefix
;
160 const Console::MethodName mMethodName
;
161 int64_t mMicroSecondTimeStamp
;
163 // These values are set in the owning thread and they contain the timestamp of
164 // when the new timer has started, the name of it and the status of the
165 // creation of it. If status is false, something went wrong. User
166 // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
167 // monotonicTimer from Performance.now();
168 // They will be set on the owning thread and never touched again on that
169 // thread. They will be used in order to create a ConsoleTimerStart dictionary
170 // when console.time() is used.
171 DOMHighResTimeStamp mStartTimerValue
;
172 nsString mStartTimerLabel
;
173 Console::TimerStatus mStartTimerStatus
;
175 // These values are set in the owning thread and they contain the duration,
176 // the name and the status of the LogTimer method. If status is false,
177 // something went wrong. They will be set on the owning thread and never
178 // touched again on that thread. They will be used in order to create a
179 // ConsoleTimerLogOrEnd dictionary. This members are set when
180 // console.timeEnd() or console.timeLog() are called.
181 double mLogTimerDuration
;
182 nsString mLogTimerLabel
;
183 Console::TimerStatus mLogTimerStatus
;
185 // These 2 values are set by IncreaseCounter or ResetCounter on the owning
186 // thread and they are used by CreateCounterOrResetCounterValue.
187 // These members are set when console.count() or console.countReset() are
189 nsString mCountLabel
;
190 uint32_t mCountValue
;
192 // The concept of outerID and innerID is misleading because when a
193 // ConsoleCallData is created from a window, these are the window IDs, but
194 // when the object is created from a SharedWorker, a ServiceWorker or a
195 // subworker of a ChromeWorker these IDs are the type of worker and the
196 // filename of the callee.
197 // In Console.sys.mjs the ID is 'jsm'.
198 enum { eString
, eNumber
, eUnknown
} mIDType
;
200 uint64_t mOuterIDNumber
;
201 nsString mOuterIDString
;
203 uint64_t mInnerIDNumber
;
204 nsString mInnerIDString
;
206 OriginAttributes mOriginAttributes
;
210 const nsString mMethodString
;
212 // Stack management is complicated, because we want to do it as
213 // lazily as possible. Therefore, we have the following behavior:
214 // 1) mTopStackFrame is initialized whenever we have any JS on the stack
215 // 2) mReifiedStack is initialized if we're created in a worker.
216 // 3) mStack is set (possibly to null if there is no JS on the stack) if
217 // we're created on main thread.
218 Maybe
<ConsoleStackEntry
> mTopStackFrame
;
219 Maybe
<nsTArray
<ConsoleStackEntry
>> mReifiedStack
;
220 nsCOMPtr
<nsIStackFrame
> mStack
;
223 ~ConsoleCallData() = default;
225 NS_DECL_OWNINGTHREAD
;
228 // MainThreadConsoleData instances are created on the Console thread and
229 // referenced from both main and Console threads in order to provide the same
230 // object for any ConsoleRunnables relating to the same Console. A Console
231 // owns a MainThreadConsoleData; MainThreadConsoleData does not keep its
233 class MainThreadConsoleData final
{
234 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MainThreadConsoleData
);
236 JSObject
* GetOrCreateSandbox(JSContext
* aCx
, nsIPrincipal
* aPrincipal
);
237 // This method must receive aCx and aArguments in the same JS::Compartment.
238 void ProcessCallData(JSContext
* aCx
, ConsoleCallData
* aData
,
239 const Sequence
<JS::Value
>& aArguments
);
242 ~MainThreadConsoleData() {
243 NS_ReleaseOnMainThread("MainThreadConsoleData::mStorage",
245 NS_ReleaseOnMainThread("MainThreadConsoleData::mSandbox",
249 // All members, except for mRefCnt, are accessed only on the main thread,
250 // except in MainThreadConsoleData destruction, at which point there are no
252 nsCOMPtr
<nsIConsoleAPIStorage
> mStorage
;
253 RefPtr
<JSObjectHolder
> mSandbox
;
254 nsTArray
<nsString
> mGroupStack
;
257 // This base class must be extended for Worker and for Worklet.
258 class ConsoleRunnable
: public StructuredCloneHolderBase
{
260 ~ConsoleRunnable() override
{
261 MOZ_ASSERT(!mClonedData
.mGlobal
,
262 "mClonedData.mGlobal is set and cleared in a main thread scope");
263 // Clear the StructuredCloneHolderBase class.
268 JSObject
* CustomReadHandler(JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
269 const JS::CloneDataPolicy
& aCloneDataPolicy
,
270 uint32_t aTag
, uint32_t aIndex
) override
{
271 AssertIsOnMainThread();
273 if (aTag
== CONSOLE_TAG_BLOB
) {
274 MOZ_ASSERT(mClonedData
.mBlobs
.Length() > aIndex
);
276 JS::Rooted
<JS::Value
> val(aCx
);
278 nsCOMPtr
<nsIGlobalObject
> global
= mClonedData
.mGlobal
;
280 Blob::Create(global
, mClonedData
.mBlobs
.ElementAt(aIndex
));
281 if (!ToJSValue(aCx
, blob
, &val
)) {
286 return &val
.toObject();
289 MOZ_CRASH("No other tags are supported.");
293 bool CustomWriteHandler(JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
,
294 JS::Handle
<JSObject
*> aObj
,
295 bool* aSameProcessScopeRequired
) override
{
297 if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob
, aObj
, blob
))) {
298 if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter
, CONSOLE_TAG_BLOB
,
299 mClonedData
.mBlobs
.Length()))) {
303 mClonedData
.mBlobs
.AppendElement(blob
->Impl());
307 if (!JS_ObjectNotWritten(aWriter
, aObj
)) {
311 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectOrNullValue(aObj
));
312 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
313 if (NS_WARN_IF(!jsString
)) {
317 if (NS_WARN_IF(!JS_WriteString(aWriter
, jsString
))) {
324 // Helper method for CallData
325 void ProcessCallData(JSContext
* aCx
, MainThreadConsoleData
* aConsoleData
,
326 ConsoleCallData
* aCallData
) {
327 AssertIsOnMainThread();
329 ConsoleCommon::ClearException
ce(aCx
);
331 // This is the same policy as when writing from the other side, in
333 JS::CloneDataPolicy cloneDataPolicy
;
334 cloneDataPolicy
.allowIntraClusterClonableSharedObjects();
335 cloneDataPolicy
.allowSharedMemoryObjects();
337 JS::Rooted
<JS::Value
> argumentsValue(aCx
);
338 if (!Read(aCx
, &argumentsValue
, cloneDataPolicy
)) {
342 MOZ_ASSERT(argumentsValue
.isObject());
344 JS::Rooted
<JSObject
*> argumentsObj(aCx
, &argumentsValue
.toObject());
347 if (!JS::GetArrayLength(aCx
, argumentsObj
, &length
)) {
351 Sequence
<JS::Value
> values
;
352 SequenceRooter
<JS::Value
> arguments(aCx
, &values
);
354 for (uint32_t i
= 0; i
< length
; ++i
) {
355 JS::Rooted
<JS::Value
> value(aCx
);
357 if (!JS_GetElement(aCx
, argumentsObj
, i
, &value
)) {
361 if (!values
.AppendElement(value
, fallible
)) {
366 MOZ_ASSERT(values
.Length() == length
);
368 aConsoleData
->ProcessCallData(aCx
, aCallData
, values
);
372 bool WriteArguments(JSContext
* aCx
, const Sequence
<JS::Value
>& aArguments
) {
373 ConsoleCommon::ClearException
ce(aCx
);
375 JS::Rooted
<JSObject
*> arguments(
376 aCx
, JS::NewArrayObject(aCx
, aArguments
.Length()));
377 if (NS_WARN_IF(!arguments
)) {
381 JS::Rooted
<JS::Value
> arg(aCx
);
382 for (uint32_t i
= 0; i
< aArguments
.Length(); ++i
) {
385 !JS_DefineElement(aCx
, arguments
, i
, arg
, JSPROP_ENUMERATE
))) {
390 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectValue(*arguments
));
391 return WriteData(aCx
, value
);
394 // Helper method for Profile calls
395 void ProcessProfileData(JSContext
* aCx
, Console::MethodName aMethodName
,
396 const nsAString
& aAction
) {
397 AssertIsOnMainThread();
399 ConsoleCommon::ClearException
ce(aCx
);
401 JS::Rooted
<JS::Value
> argumentsValue(aCx
);
402 bool ok
= Read(aCx
, &argumentsValue
);
403 mClonedData
.mGlobal
= nullptr;
409 MOZ_ASSERT(argumentsValue
.isObject());
410 JS::Rooted
<JSObject
*> argumentsObj(aCx
, &argumentsValue
.toObject());
411 if (NS_WARN_IF(!argumentsObj
)) {
416 if (!JS::GetArrayLength(aCx
, argumentsObj
, &length
)) {
420 Sequence
<JS::Value
> arguments
;
422 for (uint32_t i
= 0; i
< length
; ++i
) {
423 JS::Rooted
<JS::Value
> value(aCx
);
425 if (!JS_GetElement(aCx
, argumentsObj
, i
, &value
)) {
429 if (!arguments
.AppendElement(value
, fallible
)) {
434 Console::ProfileMethodMainthread(aCx
, aAction
, arguments
);
437 bool WriteData(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
438 // We use structuredClone to send the JSValue to the main-thread, in order
439 // to store it into the Console API Service. The consumer will be the
440 // console panel in the devtools and, because of this, we want to allow the
441 // cloning of sharedArrayBuffers and WASM modules.
442 JS::CloneDataPolicy cloneDataPolicy
;
443 cloneDataPolicy
.allowIntraClusterClonableSharedObjects();
444 cloneDataPolicy
.allowSharedMemoryObjects();
447 !Write(aCx
, aValue
, JS::UndefinedHandleValue
, cloneDataPolicy
))) {
448 // Ignore the message.
455 ConsoleStructuredCloneData mClonedData
;
458 class ConsoleWorkletRunnable
: public Runnable
, public ConsoleRunnable
{
460 explicit ConsoleWorkletRunnable(Console
* aConsole
)
461 : Runnable("dom::console::ConsoleWorkletRunnable"),
462 mConsoleData(aConsole
->GetOrCreateMainThreadData()) {
463 WorkletThread::AssertIsOnWorkletThread();
464 nsCOMPtr
<WorkletGlobalScope
> global
= do_QueryInterface(aConsole
->mGlobal
);
466 mWorkletImpl
= global
->Impl();
467 MOZ_ASSERT(mWorkletImpl
);
470 ~ConsoleWorkletRunnable() override
= default;
473 RefPtr
<MainThreadConsoleData
> mConsoleData
;
475 RefPtr
<WorkletImpl
> mWorkletImpl
;
478 // This runnable appends a CallData object into the Console queue running on
480 class ConsoleCallDataWorkletRunnable final
: public ConsoleWorkletRunnable
{
482 static already_AddRefed
<ConsoleCallDataWorkletRunnable
> Create(
483 JSContext
* aCx
, Console
* aConsole
, ConsoleCallData
* aConsoleData
,
484 const Sequence
<JS::Value
>& aArguments
) {
485 WorkletThread::AssertIsOnWorkletThread();
487 RefPtr
<ConsoleCallDataWorkletRunnable
> runnable
=
488 new ConsoleCallDataWorkletRunnable(aConsole
, aConsoleData
);
490 if (!runnable
->WriteArguments(aCx
, aArguments
)) {
494 return runnable
.forget();
498 ConsoleCallDataWorkletRunnable(Console
* aConsole
, ConsoleCallData
* aCallData
)
499 : ConsoleWorkletRunnable(aConsole
), mCallData(aCallData
) {
500 WorkletThread::AssertIsOnWorkletThread();
501 MOZ_ASSERT(aCallData
);
502 aCallData
->AssertIsOnOwningThread();
504 const WorkletLoadInfo
& loadInfo
= mWorkletImpl
->LoadInfo();
505 mCallData
->SetIDs(loadInfo
.OuterWindowID(), loadInfo
.InnerWindowID());
508 ~ConsoleCallDataWorkletRunnable() override
= default;
510 NS_IMETHOD
Run() override
{
511 AssertIsOnMainThread();
514 JSContext
* cx
= jsapi
.cx();
517 mConsoleData
->GetOrCreateSandbox(cx
, mWorkletImpl
->Principal());
518 JS::Rooted
<JSObject
*> global(cx
, sandbox
);
519 if (NS_WARN_IF(!global
)) {
520 return NS_ERROR_FAILURE
;
523 // The CreateSandbox call returns a proxy to the actual sandbox object. We
524 // don't need a proxy here.
525 global
= js::UncheckedUnwrap(global
);
527 JSAutoRealm
ar(cx
, global
);
529 // We don't need to set a parent object in mCallData bacause there are not
530 // DOM objects exposed to worklet.
532 ProcessCallData(cx
, mConsoleData
, mCallData
);
537 RefPtr
<ConsoleCallData
> mCallData
;
540 class ConsoleWorkerRunnable
: public WorkerProxyToMainThreadRunnable
,
541 public ConsoleRunnable
{
543 explicit ConsoleWorkerRunnable(Console
* aConsole
)
544 : mConsoleData(aConsole
->GetOrCreateMainThreadData()) {}
546 ~ConsoleWorkerRunnable() override
= default;
548 bool Dispatch(JSContext
* aCx
, const Sequence
<JS::Value
>& aArguments
) {
549 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
550 MOZ_ASSERT(workerPrivate
);
552 if (NS_WARN_IF(!WriteArguments(aCx
, aArguments
))) {
553 RunBackOnWorkerThreadForCleanup(workerPrivate
);
557 if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch(workerPrivate
))) {
558 // RunBackOnWorkerThreadForCleanup() will be called by
559 // WorkerProxyToMainThreadRunnable::Dispatch().
567 void RunOnMainThread(WorkerPrivate
* aWorkerPrivate
) override
{
568 MOZ_ASSERT(aWorkerPrivate
);
569 AssertIsOnMainThread();
571 // Walk up to our containing page
572 WorkerPrivate
* wp
= aWorkerPrivate
;
573 while (wp
->GetParent()) {
574 wp
= wp
->GetParent();
577 nsCOMPtr
<nsPIDOMWindowInner
> window
= wp
->GetWindow();
579 RunWindowless(aWorkerPrivate
);
581 RunWithWindow(aWorkerPrivate
, window
);
585 void RunWithWindow(WorkerPrivate
* aWorkerPrivate
,
586 nsPIDOMWindowInner
* aWindow
) {
587 MOZ_ASSERT(aWorkerPrivate
);
588 AssertIsOnMainThread();
593 RefPtr
<nsGlobalWindowInner
> win
= nsGlobalWindowInner::Cast(aWindow
);
594 if (NS_WARN_IF(!jsapi
.Init(win
))) {
598 nsCOMPtr
<nsPIDOMWindowOuter
> outerWindow
= aWindow
->GetOuterWindow();
599 if (NS_WARN_IF(!outerWindow
)) {
603 RunConsole(jsapi
.cx(), aWindow
->AsGlobal(), aWorkerPrivate
, outerWindow
,
607 void RunWindowless(WorkerPrivate
* aWorkerPrivate
) {
608 MOZ_ASSERT(aWorkerPrivate
);
609 AssertIsOnMainThread();
611 WorkerPrivate
* wp
= aWorkerPrivate
;
612 while (wp
->GetParent()) {
613 wp
= wp
->GetParent();
616 MOZ_ASSERT(!wp
->GetWindow());
621 JSContext
* cx
= jsapi
.cx();
623 JS::Rooted
<JSObject
*> global(
624 cx
, mConsoleData
->GetOrCreateSandbox(cx
, wp
->GetPrincipal()));
625 if (NS_WARN_IF(!global
)) {
629 // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
630 // We don't need a proxy here.
631 global
= js::UncheckedUnwrap(global
);
633 JSAutoRealm
ar(cx
, global
);
635 nsCOMPtr
<nsIGlobalObject
> globalObject
= xpc::NativeGlobal(global
);
636 if (NS_WARN_IF(!globalObject
)) {
640 RunConsole(cx
, globalObject
, aWorkerPrivate
, nullptr, nullptr);
643 void RunBackOnWorkerThreadForCleanup(WorkerPrivate
* aWorkerPrivate
) override
{
644 MOZ_ASSERT(aWorkerPrivate
);
645 aWorkerPrivate
->AssertIsOnWorkerThread();
648 // This method is called in the main-thread.
649 virtual void RunConsole(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
650 WorkerPrivate
* aWorkerPrivate
,
651 nsPIDOMWindowOuter
* aOuterWindow
,
652 nsPIDOMWindowInner
* aInnerWindow
) = 0;
654 bool ForMessaging() const override
{ return true; }
656 RefPtr
<MainThreadConsoleData
> mConsoleData
;
659 // This runnable appends a CallData object into the Console queue running on
661 class ConsoleCallDataWorkerRunnable final
: public ConsoleWorkerRunnable
{
663 ConsoleCallDataWorkerRunnable(Console
* aConsole
, ConsoleCallData
* aCallData
)
664 : ConsoleWorkerRunnable(aConsole
), mCallData(aCallData
) {
665 MOZ_ASSERT(aCallData
);
666 mCallData
->AssertIsOnOwningThread();
670 ~ConsoleCallDataWorkerRunnable() override
= default;
672 void RunConsole(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
673 WorkerPrivate
* aWorkerPrivate
,
674 nsPIDOMWindowOuter
* aOuterWindow
,
675 nsPIDOMWindowInner
* aInnerWindow
) override
{
677 MOZ_ASSERT(aWorkerPrivate
);
678 AssertIsOnMainThread();
680 // The windows have to run in parallel.
681 MOZ_ASSERT(!!aOuterWindow
== !!aInnerWindow
);
684 mCallData
->SetIDs(aOuterWindow
->WindowID(), aInnerWindow
->WindowID());
686 ConsoleStackEntry frame
;
687 if (mCallData
->mTopStackFrame
) {
688 frame
= *mCallData
->mTopStackFrame
;
691 nsString id
= frame
.mFilename
;
693 if (aWorkerPrivate
->IsSharedWorker()) {
694 innerID
= u
"SharedWorker"_ns
;
695 } else if (aWorkerPrivate
->IsServiceWorker()) {
696 innerID
= u
"ServiceWorker"_ns
;
697 // Use scope as ID so the webconsole can decide if the message should
699 CopyASCIItoUTF16(aWorkerPrivate
->ServiceWorkerScope(), id
);
701 innerID
= u
"Worker"_ns
;
704 mCallData
->SetIDs(id
, innerID
);
707 mClonedData
.mGlobal
= aGlobal
;
709 ProcessCallData(aCx
, mConsoleData
, mCallData
);
711 mClonedData
.mGlobal
= nullptr;
714 RefPtr
<ConsoleCallData
> mCallData
;
717 // This runnable calls ProfileMethod() on the console on the main-thread.
718 class ConsoleProfileWorkletRunnable final
: public ConsoleWorkletRunnable
{
720 static already_AddRefed
<ConsoleProfileWorkletRunnable
> Create(
721 JSContext
* aCx
, Console
* aConsole
, Console::MethodName aName
,
722 const nsAString
& aAction
, const Sequence
<JS::Value
>& aArguments
) {
723 WorkletThread::AssertIsOnWorkletThread();
725 RefPtr
<ConsoleProfileWorkletRunnable
> runnable
=
726 new ConsoleProfileWorkletRunnable(aConsole
, aName
, aAction
);
728 if (!runnable
->WriteArguments(aCx
, aArguments
)) {
732 return runnable
.forget();
736 ConsoleProfileWorkletRunnable(Console
* aConsole
, Console::MethodName aName
,
737 const nsAString
& aAction
)
738 : ConsoleWorkletRunnable(aConsole
), mName(aName
), mAction(aAction
) {
739 MOZ_ASSERT(aConsole
);
742 NS_IMETHOD
Run() override
{
743 AssertIsOnMainThread();
747 JSContext
* cx
= jsapi
.cx();
750 mConsoleData
->GetOrCreateSandbox(cx
, mWorkletImpl
->Principal());
751 JS::Rooted
<JSObject
*> global(cx
, sandbox
);
752 if (NS_WARN_IF(!global
)) {
753 return NS_ERROR_FAILURE
;
756 // The CreateSandbox call returns a proxy to the actual sandbox object. We
757 // don't need a proxy here.
758 global
= js::UncheckedUnwrap(global
);
760 JSAutoRealm
ar(cx
, global
);
762 // We don't need to set a parent object in mCallData bacause there are not
763 // DOM objects exposed to worklet.
764 ProcessProfileData(cx
, mName
, mAction
);
769 Console::MethodName mName
;
773 // This runnable calls ProfileMethod() on the console on the main-thread.
774 class ConsoleProfileWorkerRunnable final
: public ConsoleWorkerRunnable
{
776 ConsoleProfileWorkerRunnable(Console
* aConsole
, Console::MethodName aName
,
777 const nsAString
& aAction
)
778 : ConsoleWorkerRunnable(aConsole
), mName(aName
), mAction(aAction
) {
779 MOZ_ASSERT(aConsole
);
783 void RunConsole(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
784 WorkerPrivate
* aWorkerPrivate
,
785 nsPIDOMWindowOuter
* aOuterWindow
,
786 nsPIDOMWindowInner
* aInnerWindow
) override
{
787 AssertIsOnMainThread();
790 mClonedData
.mGlobal
= aGlobal
;
792 ProcessProfileData(aCx
, mName
, mAction
);
794 mClonedData
.mGlobal
= nullptr;
797 Console::MethodName mName
;
801 NS_IMPL_CYCLE_COLLECTION_CLASS(Console
)
803 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console
)
804 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal
)
805 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsoleEventNotifier
)
806 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDumpFunction
)
807 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
809 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
811 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console
)
812 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal
)
813 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsoleEventNotifier
)
814 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDumpFunction
)
815 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
817 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console
)
818 for (uint32_t i
= 0; i
< tmp
->mArgumentStorage
.length(); ++i
) {
819 tmp
->mArgumentStorage
[i
].Trace(aCallbacks
, aClosure
);
821 NS_IMPL_CYCLE_COLLECTION_TRACE_END
823 NS_IMPL_CYCLE_COLLECTING_ADDREF(Console
)
824 NS_IMPL_CYCLE_COLLECTING_RELEASE(Console
)
826 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console
)
827 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
828 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIObserver
)
829 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
833 already_AddRefed
<Console
> Console::Create(JSContext
* aCx
,
834 nsPIDOMWindowInner
* aWindow
,
836 MOZ_ASSERT_IF(NS_IsMainThread(), aWindow
);
838 uint64_t outerWindowID
= 0;
839 uint64_t innerWindowID
= 0;
842 innerWindowID
= aWindow
->WindowID();
844 // Without outerwindow any console message coming from this object will not
845 // shown in the devtools webconsole. But this should be fine because
846 // probably we are shutting down, or the window is CCed/GCed.
847 nsPIDOMWindowOuter
* outerWindow
= aWindow
->GetOuterWindow();
849 outerWindowID
= outerWindow
->WindowID();
853 RefPtr
<Console
> console
= new Console(aCx
, nsGlobalWindowInner::Cast(aWindow
),
854 outerWindowID
, innerWindowID
);
855 console
->Initialize(aRv
);
856 if (NS_WARN_IF(aRv
.Failed())) {
860 return console
.forget();
864 already_AddRefed
<Console
> Console::CreateForWorklet(JSContext
* aCx
,
865 nsIGlobalObject
* aGlobal
,
866 uint64_t aOuterWindowID
,
867 uint64_t aInnerWindowID
,
869 WorkletThread::AssertIsOnWorkletThread();
871 RefPtr
<Console
> console
=
872 new Console(aCx
, aGlobal
, aOuterWindowID
, aInnerWindowID
);
873 console
->Initialize(aRv
);
874 if (NS_WARN_IF(aRv
.Failed())) {
878 return console
.forget();
881 Console::Console(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
882 uint64_t aOuterWindowID
, uint64_t aInnerWindowID
)
884 mOuterID(aOuterWindowID
),
885 mInnerID(aInnerWindowID
),
886 mDumpToStdout(false),
887 mChromeInstance(false),
888 mCurrentLogLevel(WebIDLLogLevelToInteger(ConsoleLogLevel::All
)),
890 mCreationTimeStamp(TimeStamp::Now()) {
891 // Let's enable the dumping to stdout by default for chrome.
892 if (nsContentUtils::ThreadsafeIsSystemCaller(aCx
)) {
893 mDumpToStdout
= StaticPrefs::devtools_console_stdout_chrome();
895 mDumpToStdout
= StaticPrefs::devtools_console_stdout_content();
898 mozilla::HoldJSObjects(this);
901 Console::~Console() {
902 AssertIsOnOwningThread();
904 mozilla::DropJSObjects(this);
907 void Console::Initialize(ErrorResult
& aRv
) {
908 AssertIsOnOwningThread();
909 MOZ_ASSERT(mStatus
== eUnknown
);
911 if (NS_IsMainThread()) {
912 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
913 if (NS_WARN_IF(!obs
)) {
914 aRv
.Throw(NS_ERROR_FAILURE
);
919 aRv
= obs
->AddObserver(this, "inner-window-destroyed", true);
920 if (NS_WARN_IF(aRv
.Failed())) {
925 aRv
= obs
->AddObserver(this, "memory-pressure", true);
926 if (NS_WARN_IF(aRv
.Failed())) {
931 mStatus
= eInitialized
;
934 void Console::Shutdown() {
935 AssertIsOnOwningThread();
937 if (mStatus
== eUnknown
|| mStatus
== eShuttingDown
) {
941 if (NS_IsMainThread()) {
942 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
944 obs
->RemoveObserver(this, "inner-window-destroyed");
945 obs
->RemoveObserver(this, "memory-pressure");
949 mTimerRegistry
.Clear();
950 mCounterRegistry
.Clear();
953 mCallDataStorage
.Clear();
955 mStatus
= eShuttingDown
;
959 Console::Observe(nsISupports
* aSubject
, const char* aTopic
,
960 const char16_t
* aData
) {
961 AssertIsOnMainThread();
963 if (!strcmp(aTopic
, "inner-window-destroyed")) {
964 nsCOMPtr
<nsISupportsPRUint64
> wrapper
= do_QueryInterface(aSubject
);
965 NS_ENSURE_TRUE(wrapper
, NS_ERROR_FAILURE
);
968 nsresult rv
= wrapper
->GetData(&innerID
);
969 NS_ENSURE_SUCCESS(rv
, rv
);
971 if (innerID
== mInnerID
) {
978 if (!strcmp(aTopic
, "memory-pressure")) {
986 void Console::ClearStorage() {
987 mCallDataStorage
.Clear();
988 mArgumentStorage
.clearAndFree();
991 #define METHOD(name, string) \
992 /* static */ void Console::name(const GlobalObject& aGlobal, \
993 const Sequence<JS::Value>& aData) { \
994 Method(aGlobal, Method##name, nsLiteralString(string), aData); \
998 METHOD(Info
, u
"info")
999 METHOD(Warn
, u
"warn")
1000 METHOD(Error
, u
"error")
1001 METHOD(Exception
, u
"exception")
1002 METHOD(Debug
, u
"debug")
1003 METHOD(Table
, u
"table")
1004 METHOD(Trace
, u
"trace")
1006 // Displays an interactive listing of all the properties of an object.
1007 METHOD(Dir
, u
"dir");
1008 METHOD(Dirxml
, u
"dirxml");
1010 METHOD(Group
, u
"group")
1011 METHOD(GroupCollapsed
, u
"groupCollapsed")
1016 void Console::Clear(const GlobalObject
& aGlobal
) {
1017 const Sequence
<JS::Value
> data
;
1018 Method(aGlobal
, MethodClear
, u
"clear"_ns
, data
);
1022 void Console::GroupEnd(const GlobalObject
& aGlobal
) {
1023 const Sequence
<JS::Value
> data
;
1024 Method(aGlobal
, MethodGroupEnd
, u
"groupEnd"_ns
, data
);
1028 void Console::Time(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1029 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodTime
, u
"time"_ns
);
1033 void Console::TimeEnd(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1034 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodTimeEnd
,
1039 void Console::TimeLog(const GlobalObject
& aGlobal
, const nsAString
& aLabel
,
1040 const Sequence
<JS::Value
>& aData
) {
1041 StringMethod(aGlobal
, aLabel
, aData
, MethodTimeLog
, u
"timeLog"_ns
);
1045 void Console::StringMethod(const GlobalObject
& aGlobal
, const nsAString
& aLabel
,
1046 const Sequence
<JS::Value
>& aData
,
1047 MethodName aMethodName
,
1048 const nsAString
& aMethodString
) {
1049 RefPtr
<Console
> console
= GetConsole(aGlobal
);
1054 console
->StringMethodInternal(aGlobal
.Context(), aLabel
, aData
, aMethodName
,
1058 void Console::StringMethodInternal(JSContext
* aCx
, const nsAString
& aLabel
,
1059 const Sequence
<JS::Value
>& aData
,
1060 MethodName aMethodName
,
1061 const nsAString
& aMethodString
) {
1062 ConsoleCommon::ClearException
ce(aCx
);
1064 Sequence
<JS::Value
> data
;
1065 SequenceRooter
<JS::Value
> rooter(aCx
, &data
);
1067 JS::Rooted
<JS::Value
> value(aCx
);
1068 if (!dom::ToJSValue(aCx
, aLabel
, &value
)) {
1072 if (!data
.AppendElement(value
, fallible
)) {
1076 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
1077 if (!data
.AppendElement(aData
[i
], fallible
)) {
1082 MethodInternal(aCx
, aMethodName
, aMethodString
, data
);
1086 void Console::TimeStamp(const GlobalObject
& aGlobal
,
1087 const JS::Handle
<JS::Value
> aData
) {
1088 JSContext
* cx
= aGlobal
.Context();
1090 ConsoleCommon::ClearException
ce(cx
);
1092 Sequence
<JS::Value
> data
;
1093 SequenceRooter
<JS::Value
> rooter(cx
, &data
);
1095 if (aData
.isString() && !data
.AppendElement(aData
, fallible
)) {
1099 Method(aGlobal
, MethodTimeStamp
, u
"timeStamp"_ns
, data
);
1103 void Console::Profile(const GlobalObject
& aGlobal
,
1104 const Sequence
<JS::Value
>& aData
) {
1105 ProfileMethod(aGlobal
, MethodProfile
, u
"profile"_ns
, aData
);
1109 void Console::ProfileEnd(const GlobalObject
& aGlobal
,
1110 const Sequence
<JS::Value
>& aData
) {
1111 ProfileMethod(aGlobal
, MethodProfileEnd
, u
"profileEnd"_ns
, aData
);
1115 void Console::ProfileMethod(const GlobalObject
& aGlobal
, MethodName aName
,
1116 const nsAString
& aAction
,
1117 const Sequence
<JS::Value
>& aData
) {
1118 RefPtr
<Console
> console
= GetConsole(aGlobal
);
1123 JSContext
* cx
= aGlobal
.Context();
1124 console
->ProfileMethodInternal(cx
, aName
, aAction
, aData
);
1127 void Console::ProfileMethodInternal(JSContext
* aCx
, MethodName aMethodName
,
1128 const nsAString
& aAction
,
1129 const Sequence
<JS::Value
>& aData
) {
1130 if (!ShouldProceed(aMethodName
)) {
1134 MaybeExecuteDumpFunction(aCx
, aAction
, aData
, nullptr);
1136 if (WorkletThread::IsOnWorkletThread()) {
1137 RefPtr
<ConsoleProfileWorkletRunnable
> runnable
=
1138 ConsoleProfileWorkletRunnable::Create(aCx
, this, aMethodName
, aAction
,
1144 NS_DispatchToMainThread(runnable
.forget());
1148 if (!NS_IsMainThread()) {
1149 // Here we are in a worker thread.
1150 RefPtr
<ConsoleProfileWorkerRunnable
> runnable
=
1151 new ConsoleProfileWorkerRunnable(this, aMethodName
, aAction
);
1153 runnable
->Dispatch(aCx
, aData
);
1157 ProfileMethodMainthread(aCx
, aAction
, aData
);
1161 void Console::ProfileMethodMainthread(JSContext
* aCx
, const nsAString
& aAction
,
1162 const Sequence
<JS::Value
>& aData
) {
1163 MOZ_ASSERT(NS_IsMainThread());
1164 ConsoleCommon::ClearException
ce(aCx
);
1166 RootedDictionary
<ConsoleProfileEvent
> event(aCx
);
1167 event
.mAction
= aAction
;
1168 event
.mChromeContext
= nsContentUtils::ThreadsafeIsSystemCaller(aCx
);
1170 event
.mArguments
.Construct();
1171 Sequence
<JS::Value
>& sequence
= event
.mArguments
.Value();
1173 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
1174 if (!sequence
.AppendElement(aData
[i
], fallible
)) {
1179 JS::Rooted
<JS::Value
> eventValue(aCx
);
1180 if (!ToJSValue(aCx
, event
, &eventValue
)) {
1184 JS::Rooted
<JSObject
*> eventObj(aCx
, &eventValue
.toObject());
1185 MOZ_ASSERT(eventObj
);
1187 if (!JS_DefineProperty(aCx
, eventObj
, "wrappedJSObject", eventValue
,
1188 JSPROP_ENUMERATE
)) {
1192 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
1193 nsCOMPtr
<nsISupports
> wrapper
;
1194 const nsIID
& iid
= NS_GET_IID(nsISupports
);
1196 if (NS_FAILED(xpc
->WrapJS(aCx
, eventObj
, iid
, getter_AddRefs(wrapper
)))) {
1200 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1202 obs
->NotifyObservers(wrapper
, "console-api-profiler", nullptr);
1207 void Console::Assert(const GlobalObject
& aGlobal
, bool aCondition
,
1208 const Sequence
<JS::Value
>& aData
) {
1210 Method(aGlobal
, MethodAssert
, u
"assert"_ns
, aData
);
1215 void Console::Count(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1216 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodCount
,
1221 void Console::CountReset(const GlobalObject
& aGlobal
, const nsAString
& aLabel
) {
1222 StringMethod(aGlobal
, aLabel
, Sequence
<JS::Value
>(), MethodCountReset
,
1228 void StackFrameToStackEntry(JSContext
* aCx
, nsIStackFrame
* aStackFrame
,
1229 ConsoleStackEntry
& aStackEntry
) {
1230 MOZ_ASSERT(aStackFrame
);
1232 aStackFrame
->GetFilename(aCx
, aStackEntry
.mFilename
);
1234 aStackEntry
.mSourceId
= aStackFrame
->GetSourceId(aCx
);
1235 aStackEntry
.mLineNumber
= aStackFrame
->GetLineNumber(aCx
);
1236 aStackEntry
.mColumnNumber
= aStackFrame
->GetColumnNumber(aCx
);
1238 aStackFrame
->GetName(aCx
, aStackEntry
.mFunctionName
);
1241 aStackFrame
->GetAsyncCause(aCx
, cause
);
1242 if (!cause
.IsEmpty()) {
1243 aStackEntry
.mAsyncCause
.Construct(cause
);
1247 void ReifyStack(JSContext
* aCx
, nsIStackFrame
* aStack
,
1248 nsTArray
<ConsoleStackEntry
>& aRefiedStack
) {
1249 nsCOMPtr
<nsIStackFrame
> stack(aStack
);
1252 ConsoleStackEntry
& data
= *aRefiedStack
.AppendElement();
1253 StackFrameToStackEntry(aCx
, stack
, data
);
1255 nsCOMPtr
<nsIStackFrame
> caller
= stack
->GetCaller(aCx
);
1258 caller
= stack
->GetAsyncCaller(aCx
);
1264 } // anonymous namespace
1266 // Queue a call to a console method. See the CALL_DELAY constant.
1268 void Console::Method(const GlobalObject
& aGlobal
, MethodName aMethodName
,
1269 const nsAString
& aMethodString
,
1270 const Sequence
<JS::Value
>& aData
) {
1271 RefPtr
<Console
> console
= GetConsole(aGlobal
);
1276 console
->MethodInternal(aGlobal
.Context(), aMethodName
, aMethodString
, aData
);
1279 void Console::MethodInternal(JSContext
* aCx
, MethodName aMethodName
,
1280 const nsAString
& aMethodString
,
1281 const Sequence
<JS::Value
>& aData
) {
1282 if (!ShouldProceed(aMethodName
)) {
1286 AssertIsOnOwningThread();
1288 ConsoleCommon::ClearException
ce(aCx
);
1290 RefPtr
<ConsoleCallData
> callData
=
1291 new ConsoleCallData(aMethodName
, aMethodString
, this);
1292 if (!StoreCallData(aCx
, callData
, aData
)) {
1296 OriginAttributes oa
;
1298 if (NS_IsMainThread()) {
1300 // Save the principal's OriginAttributes in the console event data
1301 // so that we will be able to filter messages by origin attributes.
1302 nsCOMPtr
<nsIScriptObjectPrincipal
> sop
= do_QueryInterface(mGlobal
);
1303 if (NS_WARN_IF(!sop
)) {
1307 nsCOMPtr
<nsIPrincipal
> principal
= sop
->GetPrincipal();
1308 if (NS_WARN_IF(!principal
)) {
1312 oa
= principal
->OriginAttributesRef();
1313 callData
->SetAddonId(principal
);
1316 if (!principal
->IsSystemPrincipal()) {
1317 nsCOMPtr
<nsIWebNavigation
> webNav
= do_GetInterface(mGlobal
);
1319 nsCOMPtr
<nsILoadContext
> loadContext
= do_QueryInterface(webNav
);
1320 MOZ_ASSERT(loadContext
);
1323 if (NS_SUCCEEDED(loadContext
->GetUsePrivateBrowsing(&pb
))) {
1324 MOZ_ASSERT(pb
== !!oa
.mPrivateBrowsingId
);
1330 } else if (WorkletThread::IsOnWorkletThread()) {
1331 nsCOMPtr
<WorkletGlobalScope
> global
= do_QueryInterface(mGlobal
);
1333 oa
= global
->Impl()->OriginAttributesRef();
1335 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1336 MOZ_ASSERT(workerPrivate
);
1337 oa
= workerPrivate
->GetOriginAttributes();
1340 callData
->SetOriginAttributes(oa
);
1342 JS::StackCapture captureMode
=
1343 ShouldIncludeStackTrace(aMethodName
)
1344 ? JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH
))
1345 : JS::StackCapture(JS::FirstSubsumedFrame(aCx
));
1346 nsCOMPtr
<nsIStackFrame
> stack
= CreateStack(aCx
, std::move(captureMode
));
1349 callData
->mTopStackFrame
.emplace();
1350 StackFrameToStackEntry(aCx
, stack
, *callData
->mTopStackFrame
);
1353 if (NS_IsMainThread()) {
1354 callData
->mStack
= stack
;
1356 // nsIStackFrame is not threadsafe, so we need to snapshot it now,
1357 // before we post our runnable to the main thread.
1358 callData
->mReifiedStack
.emplace();
1359 ReifyStack(aCx
, stack
, *callData
->mReifiedStack
);
1362 DOMHighResTimeStamp monotonicTimer
;
1364 // Monotonic timer for 'time', 'timeLog' and 'timeEnd'
1365 if ((aMethodName
== MethodTime
|| aMethodName
== MethodTimeLog
||
1366 aMethodName
== MethodTimeEnd
|| aMethodName
== MethodTimeStamp
) &&
1367 !MonotonicTimer(aCx
, aMethodName
, aData
, &monotonicTimer
)) {
1371 if (aMethodName
== MethodTime
&& !aData
.IsEmpty()) {
1372 callData
->mStartTimerStatus
=
1373 StartTimer(aCx
, aData
[0], monotonicTimer
, callData
->mStartTimerLabel
,
1374 &callData
->mStartTimerValue
);
1377 else if (aMethodName
== MethodTimeEnd
&& !aData
.IsEmpty()) {
1378 callData
->mLogTimerStatus
=
1379 LogTimer(aCx
, aData
[0], monotonicTimer
, callData
->mLogTimerLabel
,
1380 &callData
->mLogTimerDuration
, true /* Cancel timer */);
1383 else if (aMethodName
== MethodTimeLog
&& !aData
.IsEmpty()) {
1384 callData
->mLogTimerStatus
=
1385 LogTimer(aCx
, aData
[0], monotonicTimer
, callData
->mLogTimerLabel
,
1386 &callData
->mLogTimerDuration
, false /* Cancel timer */);
1389 else if (aMethodName
== MethodCount
) {
1390 callData
->mCountValue
= IncreaseCounter(aCx
, aData
, callData
->mCountLabel
);
1391 if (!callData
->mCountValue
) {
1396 else if (aMethodName
== MethodCountReset
) {
1397 callData
->mCountValue
= ResetCounter(aCx
, aData
, callData
->mCountLabel
);
1398 if (callData
->mCountLabel
.IsEmpty()) {
1403 // Before processing this CallData differently, it's time to call the dump
1405 if (aMethodName
== MethodTrace
|| aMethodName
== MethodAssert
) {
1406 MaybeExecuteDumpFunction(aCx
, aMethodString
, aData
, stack
);
1407 } else if ((aMethodName
== MethodTime
|| aMethodName
== MethodTimeEnd
) &&
1409 MaybeExecuteDumpFunctionForTime(aCx
, aMethodName
, aMethodString
,
1410 monotonicTimer
, aData
[0]);
1412 MaybeExecuteDumpFunction(aCx
, aMethodString
, aData
, nullptr);
1415 if (NS_IsMainThread()) {
1417 callData
->SetIDs(mOuterID
, mInnerID
);
1418 } else if (!mPassedInnerID
.IsEmpty()) {
1419 callData
->SetIDs(u
"jsm"_ns
, mPassedInnerID
);
1421 nsAutoString filename
;
1422 if (callData
->mTopStackFrame
.isSome()) {
1423 filename
= callData
->mTopStackFrame
->mFilename
;
1426 callData
->SetIDs(u
"jsm"_ns
, filename
);
1429 GetOrCreateMainThreadData()->ProcessCallData(aCx
, callData
, aData
);
1431 // Just because we don't want to expose
1432 // retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
1433 // cleanup the mCallDataStorage:
1434 UnstoreCallData(callData
);
1438 if (WorkletThread::IsOnWorkletThread()) {
1439 RefPtr
<ConsoleCallDataWorkletRunnable
> runnable
=
1440 ConsoleCallDataWorkletRunnable::Create(aCx
, this, callData
, aData
);
1445 NS_DispatchToMainThread(runnable
);
1449 // We do this only in workers for now.
1450 NotifyHandler(aCx
, aData
, callData
);
1452 if (StaticPrefs::dom_worker_console_dispatch_events_to_main_thread()) {
1453 RefPtr
<ConsoleCallDataWorkerRunnable
> runnable
=
1454 new ConsoleCallDataWorkerRunnable(this, callData
);
1455 Unused
<< NS_WARN_IF(!runnable
->Dispatch(aCx
, aData
));
1459 MainThreadConsoleData
* Console::GetOrCreateMainThreadData() {
1460 AssertIsOnOwningThread();
1462 if (!mMainThreadData
) {
1463 mMainThreadData
= new MainThreadConsoleData();
1466 return mMainThreadData
;
1469 // We store information to lazily compute the stack in the reserved slots of
1470 // LazyStackGetter. The first slot always stores a JS object: it's either the
1471 // JS wrapper of the nsIStackFrame or the actual reified stack representation.
1472 // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
1473 // reified the stack yet, or an UndefinedValue() otherwise.
1474 enum { SLOT_STACKOBJ
, SLOT_RAW_STACK
};
1476 bool LazyStackGetter(JSContext
* aCx
, unsigned aArgc
, JS::Value
* aVp
) {
1477 JS::CallArgs args
= CallArgsFromVp(aArgc
, aVp
);
1478 JS::Rooted
<JSObject
*> callee(aCx
, &args
.callee());
1480 JS::Value v
= js::GetFunctionNativeReserved(&args
.callee(), SLOT_RAW_STACK
);
1481 if (v
.isUndefined()) {
1483 args
.rval().set(js::GetFunctionNativeReserved(callee
, SLOT_STACKOBJ
));
1487 nsIStackFrame
* stack
= reinterpret_cast<nsIStackFrame
*>(v
.toPrivate());
1488 nsTArray
<ConsoleStackEntry
> reifiedStack
;
1489 ReifyStack(aCx
, stack
, reifiedStack
);
1491 JS::Rooted
<JS::Value
> stackVal(aCx
);
1492 if (NS_WARN_IF(!ToJSValue(aCx
, reifiedStack
, &stackVal
))) {
1496 MOZ_ASSERT(stackVal
.isObject());
1498 js::SetFunctionNativeReserved(callee
, SLOT_STACKOBJ
, stackVal
);
1499 js::SetFunctionNativeReserved(callee
, SLOT_RAW_STACK
, JS::UndefinedValue());
1501 args
.rval().set(stackVal
);
1505 void MainThreadConsoleData::ProcessCallData(
1506 JSContext
* aCx
, ConsoleCallData
* aData
,
1507 const Sequence
<JS::Value
>& aArguments
) {
1508 AssertIsOnMainThread();
1511 JS::Rooted
<JS::Value
> eventValue(aCx
);
1513 // We want to create a console event object and pass it to our
1514 // nsIConsoleAPIStorage implementation. We want to define some accessor
1515 // properties on this object, and those will need to keep an nsIStackFrame
1516 // alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
1517 // further, passing untrusted objects to system code is likely to run afoul of
1518 // Object Xrays. So we want to wrap in a system-principal scope here. But
1519 // which one? We could cheat and try to get the underlying JSObject* of
1520 // mStorage, but that's a bit fragile. Instead, we just use the junk scope,
1521 // with explicit permission from the XPConnect module owner. If you're
1522 // tempted to do that anywhere else, talk to said module owner first.
1524 // aCx and aArguments are in the same compartment.
1525 JS::Rooted
<JSObject
*> targetScope(aCx
, xpc::PrivilegedJunkScope());
1526 if (NS_WARN_IF(!Console::PopulateConsoleNotificationInTheTargetScope(
1527 aCx
, aArguments
, targetScope
, &eventValue
, aData
, &mGroupStack
))) {
1532 mStorage
= do_GetService("@mozilla.org/consoleAPI-storage;1");
1536 NS_WARNING("Failed to get the ConsoleAPIStorage service.");
1540 nsAutoString innerID
;
1542 MOZ_ASSERT(aData
->mIDType
!= ConsoleCallData::eUnknown
);
1543 if (aData
->mIDType
== ConsoleCallData::eString
) {
1544 innerID
= aData
->mInnerIDString
;
1546 MOZ_ASSERT(aData
->mIDType
== ConsoleCallData::eNumber
);
1547 innerID
.AppendInt(aData
->mInnerIDNumber
);
1550 if (aData
->mMethodName
== Console::MethodClear
) {
1551 DebugOnly
<nsresult
> rv
= mStorage
->ClearEvents(innerID
);
1552 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "ClearEvents failed");
1555 if (NS_FAILED(mStorage
->RecordEvent(innerID
, eventValue
))) {
1556 NS_WARNING("Failed to record a console event.");
1561 bool Console::PopulateConsoleNotificationInTheTargetScope(
1562 JSContext
* aCx
, const Sequence
<JS::Value
>& aArguments
,
1563 JS::Handle
<JSObject
*> aTargetScope
,
1564 JS::MutableHandle
<JS::Value
> aEventValue
, ConsoleCallData
* aData
,
1565 nsTArray
<nsString
>* aGroupStack
) {
1568 MOZ_ASSERT(aTargetScope
);
1569 MOZ_ASSERT(JS_IsGlobalObject(aTargetScope
));
1571 ConsoleStackEntry frame
;
1572 if (aData
->mTopStackFrame
) {
1573 frame
= *aData
->mTopStackFrame
;
1576 ConsoleCommon::ClearException
ce(aCx
);
1577 RootedDictionary
<ConsoleEvent
> event(aCx
);
1579 event
.mAddonId
= aData
->mAddonId
;
1581 event
.mID
.Construct();
1582 event
.mInnerID
.Construct();
1584 event
.mChromeContext
= nsContentUtils::ThreadsafeIsSystemCaller(aCx
);
1586 if (aData
->mIDType
== ConsoleCallData::eString
) {
1587 event
.mID
.Value().SetAsString() = aData
->mOuterIDString
;
1588 event
.mInnerID
.Value().SetAsString() = aData
->mInnerIDString
;
1589 } else if (aData
->mIDType
== ConsoleCallData::eNumber
) {
1590 event
.mID
.Value().SetAsUnsignedLongLong() = aData
->mOuterIDNumber
;
1591 event
.mInnerID
.Value().SetAsUnsignedLongLong() = aData
->mInnerIDNumber
;
1593 // aData->mIDType can be eUnknown when we dispatch notifications via
1594 // mConsoleEventNotifier.
1595 event
.mID
.Value().SetAsUnsignedLongLong() = 0;
1596 event
.mInnerID
.Value().SetAsUnsignedLongLong() = 0;
1599 event
.mConsoleID
= aData
->mConsoleID
;
1600 event
.mLevel
= aData
->mMethodString
;
1601 event
.mFilename
= frame
.mFilename
;
1602 event
.mPrefix
= aData
->mPrefix
;
1604 nsCOMPtr
<nsIURI
> filenameURI
;
1606 if (NS_IsMainThread() &&
1607 NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI
), frame
.mFilename
)) &&
1608 NS_SUCCEEDED(filenameURI
->GetPassword(pass
)) && !pass
.IsEmpty()) {
1609 nsCOMPtr
<nsISensitiveInfoHiddenURI
> safeURI
=
1610 do_QueryInterface(filenameURI
);
1612 if (safeURI
&& NS_SUCCEEDED(safeURI
->GetSensitiveInfoHiddenSpec(spec
))) {
1613 CopyUTF8toUTF16(spec
, event
.mFilename
);
1617 event
.mSourceId
= frame
.mSourceId
;
1618 event
.mLineNumber
= frame
.mLineNumber
;
1619 event
.mColumnNumber
= frame
.mColumnNumber
;
1620 event
.mFunctionName
= frame
.mFunctionName
;
1621 event
.mTimeStamp
= aData
->mMicroSecondTimeStamp
/ PR_USEC_PER_MSEC
;
1622 event
.mMicroSecondTimeStamp
= aData
->mMicroSecondTimeStamp
;
1623 event
.mPrivate
= !!aData
->mOriginAttributes
.mPrivateBrowsingId
;
1625 switch (aData
->mMethodName
) {
1630 case MethodException
:
1634 case MethodGroupCollapsed
:
1636 event
.mArguments
.Construct();
1637 event
.mStyles
.Construct();
1638 if (NS_WARN_IF(!ProcessArguments(aCx
, aArguments
,
1639 event
.mArguments
.Value(),
1640 event
.mStyles
.Value()))) {
1647 event
.mArguments
.Construct();
1649 !event
.mArguments
.Value().AppendElements(aArguments
, fallible
))) {
1654 if (aData
->mMethodName
== MethodGroup
||
1655 aData
->mMethodName
== MethodGroupCollapsed
) {
1656 ComposeAndStoreGroupName(aCx
, event
.mArguments
.Value(), event
.mGroupName
,
1660 else if (aData
->mMethodName
== MethodGroupEnd
) {
1661 if (!UnstoreGroupName(event
.mGroupName
, aGroupStack
)) {
1666 else if (aData
->mMethodName
== MethodTime
&& !aArguments
.IsEmpty()) {
1667 event
.mTimer
= CreateStartTimerValue(aCx
, aData
->mStartTimerLabel
,
1668 aData
->mStartTimerStatus
);
1671 else if ((aData
->mMethodName
== MethodTimeEnd
||
1672 aData
->mMethodName
== MethodTimeLog
) &&
1673 !aArguments
.IsEmpty()) {
1674 event
.mTimer
= CreateLogOrEndTimerValue(aCx
, aData
->mLogTimerLabel
,
1675 aData
->mLogTimerDuration
,
1676 aData
->mLogTimerStatus
);
1679 else if (aData
->mMethodName
== MethodCount
||
1680 aData
->mMethodName
== MethodCountReset
) {
1681 event
.mCounter
= CreateCounterOrResetCounterValue(aCx
, aData
->mCountLabel
,
1682 aData
->mCountValue
);
1685 JSAutoRealm
ar2(aCx
, aTargetScope
);
1687 if (NS_WARN_IF(!ToJSValue(aCx
, event
, aEventValue
))) {
1691 JS::Rooted
<JSObject
*> eventObj(aCx
, &aEventValue
.toObject());
1692 if (NS_WARN_IF(!JS_DefineProperty(aCx
, eventObj
, "wrappedJSObject", eventObj
,
1693 JSPROP_ENUMERATE
))) {
1697 if (ShouldIncludeStackTrace(aData
->mMethodName
)) {
1698 // Now define the "stacktrace" property on eventObj. There are two cases
1699 // here. Either we came from a worker and have a reified stack, or we want
1700 // to define a getter that will lazily reify the stack.
1701 if (aData
->mReifiedStack
) {
1702 JS::Rooted
<JS::Value
> stacktrace(aCx
);
1703 if (NS_WARN_IF(!ToJSValue(aCx
, *aData
->mReifiedStack
, &stacktrace
)) ||
1704 NS_WARN_IF(!JS_DefineProperty(aCx
, eventObj
, "stacktrace", stacktrace
,
1705 JSPROP_ENUMERATE
))) {
1710 js::NewFunctionWithReserved(aCx
, LazyStackGetter
, 0, 0, "stacktrace");
1711 if (NS_WARN_IF(!fun
)) {
1715 JS::Rooted
<JSObject
*> funObj(aCx
, JS_GetFunctionObject(fun
));
1717 // We want to store our stack in the function and have it stay alive. But
1718 // we also need sane access to the C++ nsIStackFrame. So store both a JS
1719 // wrapper and the raw pointer: the former will keep the latter alive.
1720 JS::Rooted
<JS::Value
> stackVal(aCx
);
1721 nsresult rv
= nsContentUtils::WrapNative(aCx
, aData
->mStack
, &stackVal
);
1722 if (NS_WARN_IF(NS_FAILED(rv
))) {
1726 js::SetFunctionNativeReserved(funObj
, SLOT_STACKOBJ
, stackVal
);
1727 js::SetFunctionNativeReserved(funObj
, SLOT_RAW_STACK
,
1728 JS::PrivateValue(aData
->mStack
.get()));
1730 if (NS_WARN_IF(!JS_DefineProperty(aCx
, eventObj
, "stacktrace", funObj
,
1731 nullptr, JSPROP_ENUMERATE
))) {
1742 // Helper method for ProcessArguments. Flushes output, if non-empty, to
1744 bool FlushOutput(JSContext
* aCx
, Sequence
<JS::Value
>& aSequence
,
1745 nsString
& aOutput
) {
1746 if (!aOutput
.IsEmpty()) {
1747 JS::Rooted
<JSString
*> str(
1748 aCx
, JS_NewUCStringCopyN(aCx
, aOutput
.get(), aOutput
.Length()));
1749 if (NS_WARN_IF(!str
)) {
1753 if (NS_WARN_IF(!aSequence
.AppendElement(JS::StringValue(str
), fallible
))) {
1765 static void MakeFormatString(nsCString
& aFormat
, int32_t aInteger
,
1766 int32_t aMantissa
, char aCh
) {
1767 aFormat
.Append('%');
1768 if (aInteger
>= 0) {
1769 aFormat
.AppendInt(aInteger
);
1772 if (aMantissa
>= 0) {
1773 aFormat
.Append('.');
1774 aFormat
.AppendInt(aMantissa
);
1777 aFormat
.Append(aCh
);
1780 // If the first JS::Value of the array is a string, this method uses it to
1781 // format a string. The supported sequences are:
1785 // %o,%O - a JS object.
1786 // %c - style string.
1787 // The output is an array where any object is a separated item, the rest is
1788 // unified in a format string.
1789 // Example if the input is:
1790 // "string: %s, integer: %d, object: %o, double: %f", 's', 1, window, 0.9
1791 // The output will be:
1792 // [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
1794 // The aStyles array is populated with the style strings that the function
1795 // finds based the format string. The index of the styles matches the indexes
1796 // of elements that need the custom styling from aSequence. For elements with
1797 // no custom styling the array is padded with null elements.
1798 static bool ProcessArguments(JSContext
* aCx
, const Sequence
<JS::Value
>& aData
,
1799 Sequence
<JS::Value
>& aSequence
,
1800 Sequence
<nsString
>& aStyles
) {
1801 // This method processes the arguments as format strings (%d, %i, %s...)
1802 // only if the first element of them is a valid and not-empty string.
1804 if (aData
.IsEmpty()) {
1808 if (aData
.Length() == 1 || !aData
[0].isString()) {
1809 return aSequence
.AppendElements(aData
, fallible
);
1812 JS::Rooted
<JS::Value
> format(aCx
, aData
[0]);
1813 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, format
));
1814 if (NS_WARN_IF(!jsString
)) {
1818 nsAutoJSString string
;
1819 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
1823 if (string
.IsEmpty()) {
1824 return aSequence
.AppendElements(aData
, fallible
);
1827 nsString::const_iterator start
, end
;
1828 string
.BeginReading(start
);
1829 string
.EndReading(end
);
1834 while (start
!= end
) {
1835 if (*start
!= '%') {
1836 output
.Append(*start
);
1847 if (*start
== '%') {
1848 output
.Append(*start
);
1856 int32_t integer
= -1;
1857 int32_t mantissa
= -1;
1859 // Let's parse %<number>.<number> for %d and %f
1860 if (*start
>= '0' && *start
<= '9') {
1864 integer
= integer
* 10 + *start
- '0';
1867 } while (*start
>= '0' && *start
<= '9' && start
!= end
);
1875 if (*start
== '.') {
1884 // '.' must be followed by a number.
1885 if (*start
< '0' || *start
> '9') {
1893 mantissa
= mantissa
* 10 + *start
- '0';
1896 } while (*start
>= '0' && *start
<= '9' && start
!= end
);
1911 if (NS_WARN_IF(!FlushOutput(aCx
, aSequence
, output
))) {
1915 JS::Rooted
<JS::Value
> v(aCx
);
1916 if (index
< aData
.Length()) {
1920 if (NS_WARN_IF(!aSequence
.AppendElement(v
, fallible
))) {
1928 // If there isn't any output but there's already a style, then
1929 // discard the previous style and use the next one instead.
1930 if (output
.IsEmpty() && !aStyles
.IsEmpty()) {
1931 aStyles
.RemoveLastElement();
1934 if (NS_WARN_IF(!FlushOutput(aCx
, aSequence
, output
))) {
1938 if (index
< aData
.Length()) {
1939 JS::Rooted
<JS::Value
> v(aCx
, aData
[index
++]);
1940 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, v
));
1941 if (NS_WARN_IF(!jsString
)) {
1945 int32_t diff
= aSequence
.Length() - aStyles
.Length();
1947 for (int32_t i
= 0; i
< diff
; i
++) {
1948 if (NS_WARN_IF(!aStyles
.AppendElement(VoidString(), fallible
))) {
1954 nsAutoJSString string
;
1955 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
1959 if (NS_WARN_IF(!aStyles
.AppendElement(string
, fallible
))) {
1967 if (index
< aData
.Length()) {
1968 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
1969 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
1970 if (NS_WARN_IF(!jsString
)) {
1975 if (NS_WARN_IF(!v
.init(aCx
, jsString
))) {
1985 if (index
< aData
.Length()) {
1986 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
1988 if (value
.isBigInt()) {
1989 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
1990 if (NS_WARN_IF(!jsString
)) {
1995 if (NS_WARN_IF(!v
.init(aCx
, jsString
))) {
2003 if (NS_WARN_IF(!JS::ToInt32(aCx
, value
, &v
))) {
2008 MakeFormatString(format
, integer
, mantissa
, 'd');
2009 output
.AppendPrintf(format
.get(), v
);
2014 if (index
< aData
.Length()) {
2015 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
2018 if (NS_WARN_IF(!JS::ToNumber(aCx
, value
, &v
))) {
2022 // nspr returns "nan", but we want to expose it as "NaN"
2023 if (std::isnan(v
)) {
2024 output
.AppendFloat(v
);
2027 MakeFormatString(format
, integer
, mantissa
, 'f');
2028 output
.AppendPrintf(format
.get(), v
);
2039 if (NS_WARN_IF(!FlushOutput(aCx
, aSequence
, output
))) {
2043 // Discard trailing style element if there is no output to apply it to.
2044 if (aStyles
.Length() > aSequence
.Length()) {
2045 aStyles
.TruncateLength(aSequence
.Length());
2048 // The rest of the array, if unused by the format string.
2049 for (; index
< aData
.Length(); ++index
) {
2050 if (NS_WARN_IF(!aSequence
.AppendElement(aData
[index
], fallible
))) {
2058 // Stringify and Concat all the JS::Value in a single string using ' ' as
2059 // separator. The new group name will be stored in aGroupStack array.
2060 static void ComposeAndStoreGroupName(JSContext
* aCx
,
2061 const Sequence
<JS::Value
>& aData
,
2063 nsTArray
<nsString
>* aGroupStack
) {
2065 aName
, u
" "_ns
, aData
, [aCx
](nsAString
& dest
, const JS::Value
& valueRef
) {
2066 JS::Rooted
<JS::Value
> value(aCx
, valueRef
);
2067 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
2072 nsAutoJSString string
;
2073 if (!string
.init(aCx
, jsString
)) {
2077 dest
.Append(string
);
2080 aGroupStack
->AppendElement(aName
);
2083 // Remove the last group name and return that name. It returns false if
2084 // aGroupStack is empty.
2085 static bool UnstoreGroupName(nsAString
& aName
,
2086 nsTArray
<nsString
>* aGroupStack
) {
2087 if (aGroupStack
->IsEmpty()) {
2091 aName
= aGroupStack
->PopLastElement();
2095 Console::TimerStatus
Console::StartTimer(JSContext
* aCx
, const JS::Value
& aName
,
2096 DOMHighResTimeStamp aTimestamp
,
2097 nsAString
& aTimerLabel
,
2098 DOMHighResTimeStamp
* aTimerValue
) {
2099 AssertIsOnOwningThread();
2100 MOZ_ASSERT(aTimerValue
);
2104 if (NS_WARN_IF(mTimerRegistry
.Count() >= MAX_PAGE_TIMERS
)) {
2105 return eTimerMaxReached
;
2108 JS::Rooted
<JS::Value
> name(aCx
, aName
);
2109 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, name
));
2110 if (NS_WARN_IF(!jsString
)) {
2111 return eTimerJSException
;
2114 nsAutoJSString label
;
2115 if (NS_WARN_IF(!label
.init(aCx
, jsString
))) {
2116 return eTimerJSException
;
2119 aTimerLabel
= label
;
2121 if (mTimerRegistry
.WithEntryHandle(label
, [&](auto&& entry
) {
2125 entry
.Insert(aTimestamp
);
2128 return eTimerAlreadyExists
;
2131 *aTimerValue
= aTimestamp
;
2136 JS::Value
Console::CreateStartTimerValue(JSContext
* aCx
,
2137 const nsAString
& aTimerLabel
,
2138 TimerStatus aTimerStatus
) {
2139 MOZ_ASSERT(aTimerStatus
!= eTimerUnknown
);
2141 if (aTimerStatus
!= eTimerDone
) {
2142 return CreateTimerError(aCx
, aTimerLabel
, aTimerStatus
);
2145 RootedDictionary
<ConsoleTimerStart
> timer(aCx
);
2147 timer
.mName
= aTimerLabel
;
2149 JS::Rooted
<JS::Value
> value(aCx
);
2150 if (!ToJSValue(aCx
, timer
, &value
)) {
2151 return JS::UndefinedValue();
2157 Console::TimerStatus
Console::LogTimer(JSContext
* aCx
, const JS::Value
& aName
,
2158 DOMHighResTimeStamp aTimestamp
,
2159 nsAString
& aTimerLabel
,
2160 double* aTimerDuration
,
2161 bool aCancelTimer
) {
2162 AssertIsOnOwningThread();
2163 MOZ_ASSERT(aTimerDuration
);
2165 *aTimerDuration
= 0;
2167 JS::Rooted
<JS::Value
> name(aCx
, aName
);
2168 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, name
));
2169 if (NS_WARN_IF(!jsString
)) {
2170 return eTimerJSException
;
2174 if (NS_WARN_IF(!key
.init(aCx
, jsString
))) {
2175 return eTimerJSException
;
2180 DOMHighResTimeStamp value
= 0;
2183 if (!mTimerRegistry
.Remove(key
, &value
)) {
2184 NS_WARNING("mTimerRegistry entry not found");
2185 return eTimerDoesntExist
;
2188 if (!mTimerRegistry
.Get(key
, &value
)) {
2189 NS_WARNING("mTimerRegistry entry not found");
2190 return eTimerDoesntExist
;
2194 *aTimerDuration
= aTimestamp
- value
;
2199 JS::Value
Console::CreateLogOrEndTimerValue(JSContext
* aCx
,
2200 const nsAString
& aLabel
,
2202 TimerStatus aStatus
) {
2203 if (aStatus
!= eTimerDone
) {
2204 return CreateTimerError(aCx
, aLabel
, aStatus
);
2207 RootedDictionary
<ConsoleTimerLogOrEnd
> timer(aCx
);
2208 timer
.mName
= aLabel
;
2209 timer
.mDuration
= aDuration
;
2211 JS::Rooted
<JS::Value
> value(aCx
);
2212 if (!ToJSValue(aCx
, timer
, &value
)) {
2213 return JS::UndefinedValue();
2220 JS::Value
Console::CreateTimerError(JSContext
* aCx
, const nsAString
& aLabel
,
2221 TimerStatus aStatus
) {
2222 MOZ_ASSERT(aStatus
!= eTimerUnknown
&& aStatus
!= eTimerDone
);
2224 RootedDictionary
<ConsoleTimerError
> error(aCx
);
2226 error
.mName
= aLabel
;
2229 case eTimerAlreadyExists
:
2230 error
.mError
.AssignLiteral("timerAlreadyExists");
2233 case eTimerDoesntExist
:
2234 error
.mError
.AssignLiteral("timerDoesntExist");
2237 case eTimerJSException
:
2238 error
.mError
.AssignLiteral("timerJSError");
2241 case eTimerMaxReached
:
2242 error
.mError
.AssignLiteral("maxTimersExceeded");
2246 MOZ_CRASH("Unsupported status");
2250 JS::Rooted
<JS::Value
> value(aCx
);
2251 if (!ToJSValue(aCx
, error
, &value
)) {
2252 return JS::UndefinedValue();
2258 uint32_t Console::IncreaseCounter(JSContext
* aCx
,
2259 const Sequence
<JS::Value
>& aArguments
,
2260 nsAString
& aCountLabel
) {
2261 AssertIsOnOwningThread();
2263 ConsoleCommon::ClearException
ce(aCx
);
2265 MOZ_ASSERT(!aArguments
.IsEmpty());
2267 JS::Rooted
<JS::Value
> labelValue(aCx
, aArguments
[0]);
2268 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, labelValue
));
2270 return 0; // We cannot continue.
2273 nsAutoJSString string
;
2274 if (!string
.init(aCx
, jsString
)) {
2275 return 0; // We cannot continue.
2278 aCountLabel
= string
;
2280 const bool maxCountersReached
= mCounterRegistry
.Count() >= MAX_PAGE_COUNTERS
;
2281 return mCounterRegistry
.WithEntryHandle(
2282 aCountLabel
, [maxCountersReached
](auto&& entry
) -> uint32_t {
2286 if (maxCountersReached
) {
2287 return MAX_PAGE_COUNTERS
;
2291 return entry
.Data();
2295 uint32_t Console::ResetCounter(JSContext
* aCx
,
2296 const Sequence
<JS::Value
>& aArguments
,
2297 nsAString
& aCountLabel
) {
2298 AssertIsOnOwningThread();
2300 ConsoleCommon::ClearException
ce(aCx
);
2302 MOZ_ASSERT(!aArguments
.IsEmpty());
2304 JS::Rooted
<JS::Value
> labelValue(aCx
, aArguments
[0]);
2305 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, labelValue
));
2307 return 0; // We cannot continue.
2310 nsAutoJSString string
;
2311 if (!string
.init(aCx
, jsString
)) {
2312 return 0; // We cannot continue.
2315 aCountLabel
= string
;
2317 if (mCounterRegistry
.Remove(aCountLabel
)) {
2321 // Let's return something different than 0 if the key doesn't exist.
2322 return MAX_PAGE_COUNTERS
;
2325 // This method generates a ConsoleCounter dictionary as JS::Value. If
2326 // aCountValue is == MAX_PAGE_COUNTERS it generates a ConsoleCounterError
2327 // instead. See IncreaseCounter.
2328 // * aCx - this is the context that will root the returned value.
2329 // * aCountLabel - this label must be what IncreaseCounter received as
2331 // * aCountValue - the return value of IncreaseCounter.
2332 static JS::Value
CreateCounterOrResetCounterValue(JSContext
* aCx
,
2333 const nsAString
& aCountLabel
,
2334 uint32_t aCountValue
) {
2335 ConsoleCommon::ClearException
ce(aCx
);
2337 if (aCountValue
== MAX_PAGE_COUNTERS
) {
2338 RootedDictionary
<ConsoleCounterError
> error(aCx
);
2339 error
.mLabel
= aCountLabel
;
2340 error
.mError
.AssignLiteral("counterDoesntExist");
2342 JS::Rooted
<JS::Value
> value(aCx
);
2343 if (!ToJSValue(aCx
, error
, &value
)) {
2344 return JS::UndefinedValue();
2350 RootedDictionary
<ConsoleCounter
> data(aCx
);
2351 data
.mLabel
= aCountLabel
;
2352 data
.mCount
= aCountValue
;
2354 JS::Rooted
<JS::Value
> value(aCx
);
2355 if (!ToJSValue(aCx
, data
, &value
)) {
2356 return JS::UndefinedValue();
2363 bool Console::ShouldIncludeStackTrace(MethodName aMethodName
) {
2364 switch (aMethodName
) {
2366 case MethodException
:
2375 JSObject
* MainThreadConsoleData::GetOrCreateSandbox(JSContext
* aCx
,
2376 nsIPrincipal
* aPrincipal
) {
2377 AssertIsOnMainThread();
2380 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
2381 MOZ_ASSERT(xpc
, "This should never be null!");
2383 JS::Rooted
<JSObject
*> sandbox(aCx
);
2384 nsresult rv
= xpc
->CreateSandbox(aCx
, aPrincipal
, sandbox
.address());
2385 if (NS_WARN_IF(NS_FAILED(rv
))) {
2389 mSandbox
= new JSObjectHolder(aCx
, sandbox
);
2392 return mSandbox
->GetJSObject();
2395 bool Console::StoreCallData(JSContext
* aCx
, ConsoleCallData
* aCallData
,
2396 const Sequence
<JS::Value
>& aArguments
) {
2397 AssertIsOnOwningThread();
2399 if (NS_WARN_IF(!mArgumentStorage
.growBy(1))) {
2402 if (!mArgumentStorage
.end()[-1].Initialize(aCx
, aArguments
)) {
2403 mArgumentStorage
.shrinkBy(1);
2407 MOZ_ASSERT(aCallData
);
2408 MOZ_ASSERT(!mCallDataStorage
.Contains(aCallData
));
2410 mCallDataStorage
.AppendElement(aCallData
);
2412 MOZ_ASSERT(mCallDataStorage
.Length() == mArgumentStorage
.length());
2414 if (mCallDataStorage
.Length() > STORAGE_MAX_EVENTS
) {
2415 mCallDataStorage
.RemoveElementAt(0);
2416 mArgumentStorage
.erase(&mArgumentStorage
[0]);
2421 void Console::UnstoreCallData(ConsoleCallData
* aCallData
) {
2422 AssertIsOnOwningThread();
2424 MOZ_ASSERT(aCallData
);
2425 MOZ_ASSERT(mCallDataStorage
.Length() == mArgumentStorage
.length());
2427 size_t index
= mCallDataStorage
.IndexOf(aCallData
);
2428 // It can be that mCallDataStorage has been already cleaned in case the
2429 // processing of the argument of some Console methods triggers the
2431 if (index
== mCallDataStorage
.NoIndex
) {
2435 mCallDataStorage
.RemoveElementAt(index
);
2436 mArgumentStorage
.erase(&mArgumentStorage
[index
]);
2439 void Console::NotifyHandler(JSContext
* aCx
,
2440 const Sequence
<JS::Value
>& aArguments
,
2441 ConsoleCallData
* aCallData
) {
2442 AssertIsOnOwningThread();
2443 MOZ_ASSERT(!NS_IsMainThread());
2444 MOZ_ASSERT(aCallData
);
2446 if (!mConsoleEventNotifier
) {
2450 JS::Rooted
<JS::Value
> value(aCx
);
2452 JS::Rooted
<JSObject
*> callableGlobal(
2453 aCx
, mConsoleEventNotifier
->CallbackGlobalOrNull());
2454 if (NS_WARN_IF(!callableGlobal
)) {
2458 // aCx and aArguments are in the same compartment because this method is
2459 // called directly when a Console.something() runs.
2460 // mConsoleEventNotifier->CallbackGlobal() is the scope where value will be
2462 if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
2463 aCx
, aArguments
, callableGlobal
, &value
, aCallData
, &mGroupStack
))) {
2467 JS::Rooted
<JS::Value
> ignored(aCx
);
2468 RefPtr
<AnyCallback
> notifier(mConsoleEventNotifier
);
2469 notifier
->Call(value
, &ignored
);
2472 void Console::RetrieveConsoleEvents(JSContext
* aCx
,
2473 nsTArray
<JS::Value
>& aEvents
,
2475 AssertIsOnOwningThread();
2477 // We don't want to expose this functionality to main-thread yet.
2478 MOZ_ASSERT(!NS_IsMainThread());
2480 JS::Rooted
<JSObject
*> targetScope(aCx
, JS::CurrentGlobalOrNull(aCx
));
2482 for (uint32_t i
= 0; i
< mArgumentStorage
.length(); ++i
) {
2483 JS::Rooted
<JS::Value
> value(aCx
);
2485 JS::Rooted
<JSObject
*> sequenceScope(aCx
, mArgumentStorage
[i
].Global());
2486 JSAutoRealm
ar(aCx
, sequenceScope
);
2488 Sequence
<JS::Value
> sequence
;
2489 SequenceRooter
<JS::Value
> arguments(aCx
, &sequence
);
2491 if (!mArgumentStorage
[i
].PopulateArgumentsSequence(sequence
)) {
2492 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2496 // Here we have aCx and sequence in the same compartment.
2497 // targetScope is the destination scope and value will be populated in its
2499 if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(
2500 aCx
, sequence
, targetScope
, &value
, mCallDataStorage
[i
],
2502 aRv
.Throw(NS_ERROR_FAILURE
);
2506 aEvents
.AppendElement(value
);
2510 void Console::SetConsoleEventHandler(AnyCallback
* aHandler
) {
2511 AssertIsOnOwningThread();
2513 // We don't want to expose this functionality to main-thread yet.
2514 MOZ_ASSERT(!NS_IsMainThread());
2516 mConsoleEventNotifier
= aHandler
;
2519 void Console::AssertIsOnOwningThread() const {
2520 NS_ASSERT_OWNINGTHREAD(Console
);
2523 bool Console::IsShuttingDown() const {
2524 MOZ_ASSERT(mStatus
!= eUnknown
);
2525 return mStatus
== eShuttingDown
;
2529 already_AddRefed
<Console
> Console::GetConsole(const GlobalObject
& aGlobal
) {
2531 RefPtr
<Console
> console
= GetConsoleInternal(aGlobal
, rv
);
2532 if (NS_WARN_IF(rv
.Failed()) || !console
) {
2533 rv
.SuppressException();
2537 console
->AssertIsOnOwningThread();
2539 if (console
->IsShuttingDown()) {
2543 return console
.forget();
2547 already_AddRefed
<Console
> Console::GetConsoleInternal(
2548 const GlobalObject
& aGlobal
, ErrorResult
& aRv
) {
2550 if (NS_IsMainThread()) {
2551 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
=
2552 do_QueryInterface(aGlobal
.GetAsSupports());
2554 // we are probably running a chrome script.
2556 RefPtr
<Console
> console
= new Console(aGlobal
.Context(), nullptr, 0, 0);
2557 console
->Initialize(aRv
);
2558 if (NS_WARN_IF(aRv
.Failed())) {
2562 return console
.forget();
2565 nsGlobalWindowInner
* window
= nsGlobalWindowInner::Cast(innerWindow
);
2566 return window
->GetConsole(aGlobal
.Context(), aRv
);
2570 nsCOMPtr
<WorkletGlobalScope
> workletScope
=
2571 do_QueryInterface(aGlobal
.GetAsSupports());
2573 WorkletThread::AssertIsOnWorkletThread();
2574 return workletScope
->GetConsole(aGlobal
.Context(), aRv
);
2578 MOZ_ASSERT(!NS_IsMainThread());
2580 JSContext
* cx
= aGlobal
.Context();
2581 WorkerPrivate
* workerPrivate
= GetWorkerPrivateFromContext(cx
);
2582 MOZ_ASSERT(workerPrivate
);
2584 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
2585 if (NS_WARN_IF(!global
)) {
2589 WorkerGlobalScope
* scope
= workerPrivate
->GlobalScope();
2592 // Normal worker scope.
2593 if (scope
== global
) {
2594 return scope
->GetConsole(aRv
);
2597 // Debugger worker scope
2599 WorkerDebuggerGlobalScope
* debuggerScope
=
2600 workerPrivate
->DebuggerGlobalScope();
2601 MOZ_ASSERT(debuggerScope
);
2602 MOZ_ASSERT(debuggerScope
== global
, "Which kind of global do we have?");
2604 return debuggerScope
->GetConsole(aRv
);
2607 bool Console::MonotonicTimer(JSContext
* aCx
, MethodName aMethodName
,
2608 const Sequence
<JS::Value
>& aData
,
2609 DOMHighResTimeStamp
* aTimeStamp
) {
2610 if (nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
= do_QueryInterface(mGlobal
)) {
2611 nsGlobalWindowInner
* win
= nsGlobalWindowInner::Cast(innerWindow
);
2614 RefPtr
<Performance
> performance
= win
->GetPerformance();
2619 *aTimeStamp
= performance
->Now();
2623 if (NS_IsMainThread()) {
2624 *aTimeStamp
= (TimeStamp::Now() - mCreationTimeStamp
).ToMilliseconds();
2628 if (nsCOMPtr
<WorkletGlobalScope
> workletGlobal
= do_QueryInterface(mGlobal
)) {
2629 *aTimeStamp
= workletGlobal
->TimeStampToDOMHighRes(TimeStamp::Now());
2633 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2634 MOZ_ASSERT(workerPrivate
);
2636 *aTimeStamp
= workerPrivate
->TimeStampToDOMHighRes(TimeStamp::Now());
2641 already_AddRefed
<ConsoleInstance
> Console::CreateInstance(
2642 const GlobalObject
& aGlobal
, const ConsoleInstanceOptions
& aOptions
) {
2643 RefPtr
<ConsoleInstance
> console
=
2644 new ConsoleInstance(aGlobal
.Context(), aOptions
);
2645 return console
.forget();
2648 void Console::StringifyElement(Element
* aElement
, nsAString
& aOut
) {
2649 aOut
.AppendLiteral("<");
2650 aOut
.Append(aElement
->LocalName());
2651 uint32_t attrCount
= aElement
->GetAttrCount();
2652 nsAutoString idAttr
;
2653 nsAutoString classAttr
;
2654 nsAutoString nameAttr
;
2655 nsAutoString otherAttrs
;
2656 for (uint32_t i
= 0; i
< attrCount
; i
++) {
2657 BorrowedAttrInfo attrInfo
= aElement
->GetAttrInfoAt(i
);
2658 nsAutoString attrValue
;
2659 attrInfo
.mValue
->ToString(attrValue
);
2661 const nsAttrName
* attrName
= attrInfo
.mName
;
2662 if (attrName
->Equals(nsGkAtoms::id
)) {
2663 idAttr
.AppendLiteral(" id=\"");
2664 idAttr
.Append(attrValue
);
2665 idAttr
.AppendLiteral("\"");
2666 } else if (attrName
->Equals(nsGkAtoms::_class
)) {
2667 classAttr
.AppendLiteral(" class=\"");
2668 classAttr
.Append(attrValue
);
2669 classAttr
.AppendLiteral("\"");
2670 } else if (attrName
->Equals(nsGkAtoms::name
)) {
2671 nameAttr
.AppendLiteral(" name=\"");
2672 nameAttr
.Append(attrValue
);
2673 nameAttr
.AppendLiteral("\"");
2675 nsAutoString attrNameStr
;
2676 attrName
->GetQualifiedName(attrNameStr
);
2677 otherAttrs
.AppendLiteral(" ");
2678 otherAttrs
.Append(attrNameStr
);
2679 otherAttrs
.AppendLiteral("=\"");
2680 otherAttrs
.Append(attrValue
);
2681 otherAttrs
.AppendLiteral("\"");
2684 if (!idAttr
.IsEmpty()) {
2685 aOut
.Append(idAttr
);
2687 if (!classAttr
.IsEmpty()) {
2688 aOut
.Append(classAttr
);
2690 if (!nameAttr
.IsEmpty()) {
2691 aOut
.Append(nameAttr
);
2693 if (!otherAttrs
.IsEmpty()) {
2694 aOut
.Append(otherAttrs
);
2696 aOut
.AppendLiteral(">");
2699 void Console::MaybeExecuteDumpFunction(JSContext
* aCx
,
2700 const nsAString
& aMethodName
,
2701 const Sequence
<JS::Value
>& aData
,
2702 nsIStackFrame
* aStack
) {
2703 if (!mDumpFunction
&& !mDumpToStdout
) {
2707 nsAutoString message
;
2708 message
.AssignLiteral("console.");
2709 message
.Append(aMethodName
);
2710 message
.AppendLiteral(": ");
2712 if (!mPrefix
.IsEmpty()) {
2713 message
.Append(mPrefix
);
2714 message
.AppendLiteral(": ");
2717 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
2718 JS::Rooted
<JS::Value
> v(aCx
, aData
[i
]);
2720 Element
* element
= nullptr;
2721 if (NS_SUCCEEDED(UNWRAP_OBJECT(Element
, &v
, element
))) {
2723 message
.AppendLiteral(" ");
2725 StringifyElement(element
, message
);
2730 JS::Rooted
<JSString
*> jsString(aCx
, JS_ValueToSource(aCx
, v
));
2735 nsAutoJSString string
;
2736 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
2741 message
.AppendLiteral(" ");
2744 message
.Append(string
);
2747 message
.AppendLiteral("\n");
2749 // aStack can be null.
2751 nsCOMPtr
<nsIStackFrame
> stack(aStack
);
2754 nsAutoString filename
;
2755 stack
->GetFilename(aCx
, filename
);
2757 message
.Append(filename
);
2758 message
.AppendLiteral(" ");
2760 message
.AppendInt(stack
->GetLineNumber(aCx
));
2761 message
.AppendLiteral(" ");
2763 nsAutoString functionName
;
2764 stack
->GetName(aCx
, functionName
);
2766 message
.Append(functionName
);
2767 message
.AppendLiteral("\n");
2769 nsCOMPtr
<nsIStackFrame
> caller
= stack
->GetCaller(aCx
);
2772 caller
= stack
->GetAsyncCaller(aCx
);
2778 ExecuteDumpFunction(message
);
2781 void Console::MaybeExecuteDumpFunctionForTime(JSContext
* aCx
,
2782 MethodName aMethodName
,
2783 const nsAString
& aMethodString
,
2784 uint64_t aMonotonicTimer
,
2785 const JS::Value
& aData
) {
2786 if (!mDumpFunction
&& !mDumpToStdout
) {
2790 nsAutoString message
;
2791 message
.AssignLiteral("console.");
2792 message
.Append(aMethodString
);
2793 message
.AppendLiteral(": ");
2795 if (!mPrefix
.IsEmpty()) {
2796 message
.Append(mPrefix
);
2797 message
.AppendLiteral(": ");
2800 JS::Rooted
<JS::Value
> v(aCx
, aData
);
2801 JS::Rooted
<JSString
*> jsString(aCx
, JS_ValueToSource(aCx
, v
));
2806 nsAutoJSString string
;
2807 if (NS_WARN_IF(!string
.init(aCx
, jsString
))) {
2811 message
.Append(string
);
2812 message
.AppendLiteral(" @ ");
2813 message
.AppendInt(aMonotonicTimer
);
2815 message
.AppendLiteral("\n");
2816 ExecuteDumpFunction(message
);
2819 void Console::ExecuteDumpFunction(const nsAString
& aMessage
) {
2820 if (mDumpFunction
) {
2821 RefPtr
<ConsoleInstanceDumpCallback
> dumpFunction(mDumpFunction
);
2822 dumpFunction
->Call(aMessage
);
2826 NS_ConvertUTF16toUTF8
str(aMessage
);
2827 MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug
, ("%s", str
.get()));
2829 __android_log_print(ANDROID_LOG_INFO
, "Gecko", "%s", str
.get());
2831 fputs(str
.get(), stdout
);
2835 bool Console::ShouldProceed(MethodName aName
) const {
2836 return mCurrentLogLevel
<= InternalLogLevelToInteger(aName
);
2839 uint32_t Console::WebIDLLogLevelToInteger(ConsoleLogLevel aLevel
) const {
2841 case ConsoleLogLevel::All
:
2843 case ConsoleLogLevel::Debug
:
2845 case ConsoleLogLevel::Log
:
2847 case ConsoleLogLevel::Info
:
2849 case ConsoleLogLevel::Clear
:
2851 case ConsoleLogLevel::Trace
:
2853 case ConsoleLogLevel::TimeLog
:
2855 case ConsoleLogLevel::TimeEnd
:
2857 case ConsoleLogLevel::Time
:
2859 case ConsoleLogLevel::Group
:
2861 case ConsoleLogLevel::GroupEnd
:
2863 case ConsoleLogLevel::Profile
:
2865 case ConsoleLogLevel::ProfileEnd
:
2867 case ConsoleLogLevel::Dir
:
2869 case ConsoleLogLevel::Dirxml
:
2871 case ConsoleLogLevel::Warn
:
2873 case ConsoleLogLevel::Error
:
2875 case ConsoleLogLevel::Off
:
2879 "ConsoleLogLevel is out of sync with the Console implementation!");
2884 uint32_t Console::InternalLogLevelToInteger(MethodName aName
) const {
2894 case MethodException
:
2908 case MethodGroupCollapsed
:
2910 case MethodGroupEnd
:
2918 case MethodTimeStamp
:
2924 case MethodCountReset
:
2930 case MethodProfileEnd
:
2933 MOZ_CRASH("MethodName is out of sync with the Console implementation!");
2938 bool Console::ArgumentData::Initialize(JSContext
* aCx
,
2939 const Sequence
<JS::Value
>& aArguments
) {
2940 mGlobal
= JS::CurrentGlobalOrNull(aCx
);
2942 if (NS_WARN_IF(!mArguments
.AppendElements(aArguments
, fallible
))) {
2949 void Console::ArgumentData::Trace(const TraceCallbacks
& aCallbacks
,
2951 ArgumentData
* tmp
= this;
2952 for (uint32_t i
= 0; i
< mArguments
.Length(); ++i
) {
2953 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArguments
[i
])
2956 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal
)
2959 bool Console::ArgumentData::PopulateArgumentsSequence(
2960 Sequence
<JS::Value
>& aSequence
) const {
2961 AssertIsOnOwningThread();
2963 for (uint32_t i
= 0; i
< mArguments
.Length(); ++i
) {
2964 if (NS_WARN_IF(!aSequence
.AppendElement(mArguments
[i
], fallible
))) {
2972 } // namespace mozilla::dom