1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/Console.h"
7 #include "mozilla/dom/ConsoleBinding.h"
9 #include "mozilla/dom/Exceptions.h"
10 #include "mozilla/dom/ToJSValue.h"
11 #include "mozilla/Maybe.h"
12 #include "nsCycleCollectionParticipant.h"
13 #include "nsDocument.h"
14 #include "nsDOMNavigationTiming.h"
15 #include "nsGlobalWindow.h"
16 #include "nsJSUtils.h"
17 #include "nsPerformance.h"
18 #include "ScriptSettings.h"
19 #include "WorkerPrivate.h"
20 #include "WorkerRunnable.h"
21 #include "xpcprivate.h"
22 #include "nsContentUtils.h"
23 #include "nsDocShell.h"
25 #include "nsIConsoleAPIStorage.h"
26 #include "nsIDOMWindowUtils.h"
27 #include "nsIInterfaceRequestorUtils.h"
28 #include "nsILoadContext.h"
29 #include "nsIServiceManager.h"
30 #include "nsISupportsPrimitives.h"
31 #include "nsIWebNavigation.h"
32 #include "nsIXPConnect.h"
34 // The maximum allowed number of concurrent timers per page.
35 #define MAX_PAGE_TIMERS 10000
37 // The maximum allowed number of concurrent counters per page.
38 #define MAX_PAGE_COUNTERS 10000
40 // The maximum stacktrace depth when populating the stacktrace array used for
42 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
44 // This tag is used in the Structured Clone Algorithm to move js values from
45 // worker thread to main thread
46 #define CONSOLE_TAG JS_SCTAG_USER_MIN
48 using namespace mozilla::dom::exceptions
;
49 using namespace mozilla::dom::workers
;
55 * Console API in workers uses the Structured Clone Algorithm to move any value
56 * from the worker thread to the main-thread. Some object cannot be moved and,
57 * in these cases, we convert them to strings.
58 * It's not the best, but at least we are able to show something.
61 // This method is called by the Structured Clone Algorithm when some data has
64 ConsoleStructuredCloneCallbacksRead(JSContext
* aCx
,
65 JSStructuredCloneReader
* /* unused */,
66 uint32_t aTag
, uint32_t aData
,
69 AssertIsOnMainThread();
71 if (aTag
!= CONSOLE_TAG
) {
75 nsTArray
<nsString
>* strings
= static_cast<nsTArray
<nsString
>*>(aClosure
);
76 MOZ_ASSERT(strings
->Length() > aData
);
78 JS::Rooted
<JS::Value
> value(aCx
);
79 if (!xpc::StringToJsval(aCx
, strings
->ElementAt(aData
), &value
)) {
83 JS::Rooted
<JSObject
*> obj(aCx
);
84 if (!JS_ValueToObject(aCx
, value
, &obj
)) {
91 // This method is called by the Structured Clone Algorithm when some data has
94 ConsoleStructuredCloneCallbacksWrite(JSContext
* aCx
,
95 JSStructuredCloneWriter
* aWriter
,
96 JS::Handle
<JSObject
*> aObj
,
99 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectOrNullValue(aObj
));
100 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
105 nsAutoJSString string
;
106 if (!string
.init(aCx
, jsString
)) {
110 nsTArray
<nsString
>* strings
= static_cast<nsTArray
<nsString
>*>(aClosure
);
112 if (!JS_WriteUint32Pair(aWriter
, CONSOLE_TAG
, strings
->Length())) {
116 strings
->AppendElement(string
);
122 ConsoleStructuredCloneCallbacksError(JSContext
* /* aCx */,
123 uint32_t /* aErrorId */)
125 NS_WARNING("Failed to clone data for the Console API in workers.");
128 static const JSStructuredCloneCallbacks gConsoleCallbacks
= {
129 ConsoleStructuredCloneCallbacksRead
,
130 ConsoleStructuredCloneCallbacksWrite
,
131 ConsoleStructuredCloneCallbacksError
134 class ConsoleCallData MOZ_FINAL
138 : mMethodName(Console::MethodLog
)
140 , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC
)
147 Initialize(JSContext
* aCx
, Console::MethodName aName
,
148 const nsAString
& aString
, const Sequence
<JS::Value
>& aArguments
)
150 mGlobal
= JS::CurrentGlobalOrNull(aCx
);
152 mMethodString
= aString
;
154 for (uint32_t i
= 0; i
< aArguments
.Length(); ++i
) {
155 mArguments
.AppendElement(aArguments
[i
]);
160 SetIDs(uint64_t aOuterID
, uint64_t aInnerID
)
162 MOZ_ASSERT(mIDType
== eUnknown
);
164 mOuterIDNumber
= aOuterID
;
165 mInnerIDNumber
= aInnerID
;
170 SetIDs(const nsAString
& aOuterID
, const nsAString
& aInnerID
)
172 MOZ_ASSERT(mIDType
== eUnknown
);
174 mOuterIDString
= aOuterID
;
175 mInnerIDString
= aInnerID
;
186 JS::Heap
<JSObject
*> mGlobal
;
188 Console::MethodName mMethodName
;
191 DOMHighResTimeStamp mMonotonicTimer
;
193 // The concept of outerID and innerID is misleading because when a
194 // ConsoleCallData is created from a window, these are the window IDs, but
195 // when the object is created from a SharedWorker, a ServiceWorker or a
196 // subworker of a ChromeWorker these IDs are the type of worker and the
197 // filename of the callee.
198 // In Console.jsm the ID is 'jsm'.
205 uint64_t mOuterIDNumber
;
206 nsString mOuterIDString
;
208 uint64_t mInnerIDNumber
;
209 nsString mInnerIDString
;
211 nsString mMethodString
;
212 nsTArray
<JS::Heap
<JS::Value
>> mArguments
;
214 // Stack management is complicated, because we want to do it as
215 // lazily as possible. Therefore, we have the following behavior:
216 // 1) mTopStackFrame is initialized whenever we have any JS on the stack
217 // 2) mReifiedStack is initialized if we're created in a worker.
218 // 3) mStack is set (possibly to null if there is no JS on the stack) if
219 // we're created on main thread.
220 Maybe
<ConsoleStackEntry
> mTopStackFrame
;
221 Maybe
<nsTArray
<ConsoleStackEntry
>> mReifiedStack
;
222 nsCOMPtr
<nsIStackFrame
> mStack
;
225 // This class is used to clear any exception at the end of this method.
229 explicit ClearException(JSContext
* aCx
)
236 JS_ClearPendingException(mCx
);
243 class ConsoleRunnable
: public nsRunnable
246 explicit ConsoleRunnable(Console
* aConsole
)
247 : mWorkerPrivate(GetCurrentThreadWorkerPrivate())
250 MOZ_ASSERT(mWorkerPrivate
);
261 mWorkerPrivate
->AssertIsOnWorkerThread();
263 JSContext
* cx
= mWorkerPrivate
->GetJSContext();
265 if (!PreDispatch(cx
)) {
269 AutoSyncLoopHolder
syncLoop(mWorkerPrivate
);
270 mSyncLoopTarget
= syncLoop
.EventTarget();
272 if (NS_FAILED(NS_DispatchToMainThread(this))) {
274 "Failed to dispatch to main thread for the Console API!");
278 return syncLoop
.Run();
284 AssertIsOnMainThread();
286 // Walk up to our containing page
287 WorkerPrivate
* wp
= mWorkerPrivate
;
288 while (wp
->GetParent()) {
289 wp
= wp
->GetParent();
292 nsPIDOMWindow
* window
= wp
->GetWindow();
296 RunWithWindow(window
);
299 nsRefPtr
<MainThreadStopSyncLoopRunnable
> response
=
300 new MainThreadStopSyncLoopRunnable(mWorkerPrivate
,
301 mSyncLoopTarget
.forget(),
303 if (!response
->Dispatch(nullptr)) {
304 NS_WARNING("Failed to dispatch response!");
311 RunWithWindow(nsPIDOMWindow
* aWindow
)
316 nsRefPtr
<nsGlobalWindow
> win
= static_cast<nsGlobalWindow
*>(aWindow
);
317 if (NS_WARN_IF(!jsapi
.Init(win
))) {
321 MOZ_ASSERT(aWindow
->IsInnerWindow());
322 nsPIDOMWindow
* outerWindow
= aWindow
->GetOuterWindow();
323 MOZ_ASSERT(outerWindow
);
325 RunConsole(jsapi
.cx(), outerWindow
, aWindow
);
331 WorkerPrivate
* wp
= mWorkerPrivate
;
332 while (wp
->GetParent()) {
333 wp
= wp
->GetParent();
336 MOZ_ASSERT(!wp
->GetWindow());
338 AutoSafeJSContext cx
;
340 nsCOMPtr
<nsIXPConnectJSObjectHolder
> sandbox
=
341 mConsole
->GetOrCreateSandbox(cx
, wp
->GetPrincipal());
342 if (NS_WARN_IF(!sandbox
)) {
346 JS::Rooted
<JSObject
*> global(cx
, sandbox
->GetJSObject());
347 if (NS_WARN_IF(!global
)) {
351 // The CreateSandbox call returns a proxy to the actual sandbox object. We
352 // don't need a proxy here.
353 global
= js::UncheckedUnwrap(global
);
355 JSAutoCompartment
ac(cx
, global
);
357 RunConsole(cx
, nullptr, nullptr);
362 PreDispatch(JSContext
* aCx
) = 0;
365 RunConsole(JSContext
* aCx
, nsPIDOMWindow
* aOuterWindow
,
366 nsPIDOMWindow
* aInnerWindow
) = 0;
368 WorkerPrivate
* mWorkerPrivate
;
370 // Raw pointer because this method is async and this object is kept alive by
375 nsCOMPtr
<nsIEventTarget
> mSyncLoopTarget
;
378 // This runnable appends a CallData object into the Console queue running on
380 class ConsoleCallDataRunnable MOZ_FINAL
: public ConsoleRunnable
383 ConsoleCallDataRunnable(Console
* aConsole
,
384 ConsoleCallData
* aCallData
)
385 : ConsoleRunnable(aConsole
)
386 , mCallData(aCallData
)
390 ~ConsoleCallDataRunnable()
394 PreDispatch(JSContext
* aCx
) MOZ_OVERRIDE
396 mWorkerPrivate
->AssertIsOnWorkerThread();
398 ClearException
ce(aCx
);
399 JSAutoCompartment
ac(aCx
, mCallData
->mGlobal
);
401 JS::Rooted
<JSObject
*> arguments(aCx
,
402 JS_NewArrayObject(aCx
, mCallData
->mArguments
.Length()));
407 JS::Rooted
<JS::Value
> arg(aCx
);
408 for (uint32_t i
= 0; i
< mCallData
->mArguments
.Length(); ++i
) {
409 arg
= mCallData
->mArguments
[i
];
410 if (!JS_DefineElement(aCx
, arguments
, i
, arg
, JSPROP_ENUMERATE
)) {
415 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectValue(*arguments
));
417 if (!mArguments
.write(aCx
, value
, &gConsoleCallbacks
, &mStrings
)) {
421 mCallData
->CleanupJSObjects();
426 RunConsole(JSContext
* aCx
, nsPIDOMWindow
* aOuterWindow
,
427 nsPIDOMWindow
* aInnerWindow
) MOZ_OVERRIDE
429 MOZ_ASSERT(NS_IsMainThread());
431 // The windows have to run in parallel.
432 MOZ_ASSERT(!!aOuterWindow
== !!aInnerWindow
);
435 mCallData
->SetIDs(aOuterWindow
->WindowID(), aInnerWindow
->WindowID());
437 ConsoleStackEntry frame
;
438 if (mCallData
->mTopStackFrame
) {
439 frame
= *mCallData
->mTopStackFrame
;
443 if (mWorkerPrivate
->IsSharedWorker()) {
444 id
= NS_LITERAL_STRING("SharedWorker");
445 } else if (mWorkerPrivate
->IsServiceWorker()) {
446 id
= NS_LITERAL_STRING("ServiceWorker");
448 id
= NS_LITERAL_STRING("Worker");
451 mCallData
->SetIDs(id
, frame
.mFilename
);
454 ProcessCallData(aCx
);
455 mCallData
->CleanupJSObjects();
460 ProcessCallData(JSContext
* aCx
)
462 ClearException
ce(aCx
);
464 JS::Rooted
<JS::Value
> argumentsValue(aCx
);
465 if (!mArguments
.read(aCx
, &argumentsValue
, &gConsoleCallbacks
, &mStrings
)) {
469 MOZ_ASSERT(argumentsValue
.isObject());
470 JS::Rooted
<JSObject
*> argumentsObj(aCx
, &argumentsValue
.toObject());
471 MOZ_ASSERT(JS_IsArrayObject(aCx
, argumentsObj
));
474 if (!JS_GetArrayLength(aCx
, argumentsObj
, &length
)) {
478 for (uint32_t i
= 0; i
< length
; ++i
) {
479 JS::Rooted
<JS::Value
> value(aCx
);
481 if (!JS_GetElement(aCx
, argumentsObj
, i
, &value
)) {
485 mCallData
->mArguments
.AppendElement(value
);
488 MOZ_ASSERT(mCallData
->mArguments
.Length() == length
);
490 mCallData
->mGlobal
= JS::CurrentGlobalOrNull(aCx
);
491 mConsole
->ProcessCallData(mCallData
);
494 ConsoleCallData
* mCallData
;
496 JSAutoStructuredCloneBuffer mArguments
;
497 nsTArray
<nsString
> mStrings
;
500 // This runnable calls ProfileMethod() on the console on the main-thread.
501 class ConsoleProfileRunnable MOZ_FINAL
: public ConsoleRunnable
504 ConsoleProfileRunnable(Console
* aConsole
, const nsAString
& aAction
,
505 const Sequence
<JS::Value
>& aArguments
)
506 : ConsoleRunnable(aConsole
)
508 , mArguments(aArguments
)
510 MOZ_ASSERT(aConsole
);
515 PreDispatch(JSContext
* aCx
) MOZ_OVERRIDE
517 ClearException
ce(aCx
);
519 JS::Rooted
<JSObject
*> global(aCx
, JS::CurrentGlobalOrNull(aCx
));
524 JSAutoCompartment
ac(aCx
, global
);
526 JS::Rooted
<JSObject
*> arguments(aCx
,
527 JS_NewArrayObject(aCx
, mArguments
.Length()));
532 JS::Rooted
<JS::Value
> arg(aCx
);
533 for (uint32_t i
= 0; i
< mArguments
.Length(); ++i
) {
535 if (!JS_DefineElement(aCx
, arguments
, i
, arg
, JSPROP_ENUMERATE
)) {
540 JS::Rooted
<JS::Value
> value(aCx
, JS::ObjectValue(*arguments
));
542 if (!mBuffer
.write(aCx
, value
, &gConsoleCallbacks
, &mStrings
)) {
550 RunConsole(JSContext
* aCx
, nsPIDOMWindow
* aOuterWindow
,
551 nsPIDOMWindow
* aInnerWindow
) MOZ_OVERRIDE
553 ClearException
ce(aCx
);
555 JS::Rooted
<JS::Value
> argumentsValue(aCx
);
556 if (!mBuffer
.read(aCx
, &argumentsValue
, &gConsoleCallbacks
, &mStrings
)) {
560 MOZ_ASSERT(argumentsValue
.isObject());
561 JS::Rooted
<JSObject
*> argumentsObj(aCx
, &argumentsValue
.toObject());
562 MOZ_ASSERT(JS_IsArrayObject(aCx
, argumentsObj
));
565 if (!JS_GetArrayLength(aCx
, argumentsObj
, &length
)) {
569 Sequence
<JS::Value
> arguments
;
571 for (uint32_t i
= 0; i
< length
; ++i
) {
572 JS::Rooted
<JS::Value
> value(aCx
);
574 if (!JS_GetElement(aCx
, argumentsObj
, i
, &value
)) {
578 arguments
.AppendElement(value
);
581 mConsole
->ProfileMethod(aCx
, mAction
, arguments
);
585 Sequence
<JS::Value
> mArguments
;
587 JSAutoStructuredCloneBuffer mBuffer
;
588 nsTArray
<nsString
> mStrings
;
591 NS_IMPL_CYCLE_COLLECTION_CLASS(Console
)
593 // We don't need to traverse/unlink mStorage and mSanbox because they are not
594 // CCed objects and they are only used on the main thread, even when this
595 // Console object is used on workers.
597 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console
)
598 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow
)
599 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
600 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
602 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console
)
603 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow
)
604 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
605 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
607 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console
)
608 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
609 NS_IMPL_CYCLE_COLLECTION_TRACE_END
611 NS_IMPL_CYCLE_COLLECTING_ADDREF(Console
)
612 NS_IMPL_CYCLE_COLLECTING_RELEASE(Console
)
614 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console
)
615 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
616 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
617 NS_INTERFACE_MAP_ENTRY(nsISupports
)
620 Console::Console(nsPIDOMWindow
* aWindow
)
626 MOZ_ASSERT(mWindow
->IsInnerWindow());
627 mInnerID
= mWindow
->WindowID();
629 nsPIDOMWindow
* outerWindow
= mWindow
->GetOuterWindow();
630 MOZ_ASSERT(outerWindow
);
631 mOuterID
= outerWindow
->WindowID();
634 if (NS_IsMainThread()) {
635 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
637 obs
->AddObserver(this, "inner-window-destroyed", false);
641 mozilla::HoldJSObjects(this);
646 if (!NS_IsMainThread()) {
647 nsCOMPtr
<nsIThread
> mainThread
;
648 NS_GetMainThread(getter_AddRefs(mainThread
));
651 nsIConsoleAPIStorage
* storage
;
652 mStorage
.forget(&storage
);
653 NS_ProxyRelease(mainThread
, storage
, false);
657 nsIXPConnectJSObjectHolder
* sandbox
;
658 mSandbox
.forget(&sandbox
);
659 NS_ProxyRelease(mainThread
, sandbox
, false);
663 mozilla::DropJSObjects(this);
667 Console::Observe(nsISupports
* aSubject
, const char* aTopic
,
668 const char16_t
* aData
)
670 MOZ_ASSERT(NS_IsMainThread());
672 if (strcmp(aTopic
, "inner-window-destroyed")) {
676 nsCOMPtr
<nsISupportsPRUint64
> wrapper
= do_QueryInterface(aSubject
);
677 NS_ENSURE_TRUE(wrapper
, NS_ERROR_FAILURE
);
680 nsresult rv
= wrapper
->GetData(&innerID
);
681 NS_ENSURE_SUCCESS(rv
, rv
);
683 if (innerID
== mInnerID
) {
684 nsCOMPtr
<nsIObserverService
> obs
=
685 do_GetService("@mozilla.org/observer-service;1");
687 obs
->RemoveObserver(this, "inner-window-destroyed");
690 mTimerRegistry
.Clear();
697 Console::WrapObject(JSContext
* aCx
)
699 return ConsoleBinding::Wrap(aCx
, this);
702 #define METHOD(name, string) \
704 Console::name(JSContext* aCx, const Sequence<JS::Value>& aData) \
706 Method(aCx, Method##name, NS_LITERAL_STRING(string), aData); \
712 METHOD(Error
, "error")
713 METHOD(Exception
, "exception")
714 METHOD(Debug
, "debug")
715 METHOD(Table
, "table")
718 Console::Trace(JSContext
* aCx
)
720 const Sequence
<JS::Value
> data
;
721 Method(aCx
, MethodTrace
, NS_LITERAL_STRING("trace"), data
);
724 // Displays an interactive listing of all the properties of an object.
727 METHOD(Group
, "group")
728 METHOD(GroupCollapsed
, "groupCollapsed")
729 METHOD(GroupEnd
, "groupEnd")
732 Console::Time(JSContext
* aCx
, const JS::Handle
<JS::Value
> aTime
)
734 Sequence
<JS::Value
> data
;
735 SequenceRooter
<JS::Value
> rooter(aCx
, &data
);
737 if (!aTime
.isUndefined()) {
738 data
.AppendElement(aTime
);
741 Method(aCx
, MethodTime
, NS_LITERAL_STRING("time"), data
);
745 Console::TimeEnd(JSContext
* aCx
, const JS::Handle
<JS::Value
> aTime
)
747 Sequence
<JS::Value
> data
;
748 SequenceRooter
<JS::Value
> rooter(aCx
, &data
);
750 if (!aTime
.isUndefined()) {
751 data
.AppendElement(aTime
);
754 Method(aCx
, MethodTimeEnd
, NS_LITERAL_STRING("timeEnd"), data
);
758 Console::Profile(JSContext
* aCx
, const Sequence
<JS::Value
>& aData
)
760 ProfileMethod(aCx
, NS_LITERAL_STRING("profile"), aData
);
764 Console::ProfileEnd(JSContext
* aCx
, const Sequence
<JS::Value
>& aData
)
766 ProfileMethod(aCx
, NS_LITERAL_STRING("profileEnd"), aData
);
770 Console::ProfileMethod(JSContext
* aCx
, const nsAString
& aAction
,
771 const Sequence
<JS::Value
>& aData
)
773 if (!NS_IsMainThread()) {
774 // Here we are in a worker thread.
775 nsRefPtr
<ConsoleProfileRunnable
> runnable
=
776 new ConsoleProfileRunnable(this, aAction
, aData
);
777 runnable
->Dispatch();
781 ClearException
ce(aCx
);
783 RootedDictionary
<ConsoleProfileEvent
> event(aCx
);
784 event
.mAction
= aAction
;
786 event
.mArguments
.Construct();
787 Sequence
<JS::Value
>& sequence
= event
.mArguments
.Value();
789 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
790 sequence
.AppendElement(aData
[i
]);
793 JS::Rooted
<JS::Value
> eventValue(aCx
);
794 if (!ToJSValue(aCx
, event
, &eventValue
)) {
798 JS::Rooted
<JSObject
*> eventObj(aCx
, &eventValue
.toObject());
799 MOZ_ASSERT(eventObj
);
801 if (!JS_DefineProperty(aCx
, eventObj
, "wrappedJSObject", eventValue
,
806 nsXPConnect
* xpc
= nsXPConnect::XPConnect();
807 nsCOMPtr
<nsISupports
> wrapper
;
808 const nsIID
& iid
= NS_GET_IID(nsISupports
);
810 if (NS_FAILED(xpc
->WrapJS(aCx
, eventObj
, iid
, getter_AddRefs(wrapper
)))) {
814 nsCOMPtr
<nsIObserverService
> obs
=
815 do_GetService("@mozilla.org/observer-service;1");
817 obs
->NotifyObservers(wrapper
, "console-api-profiler", nullptr);
822 Console::Assert(JSContext
* aCx
, bool aCondition
,
823 const Sequence
<JS::Value
>& aData
)
826 Method(aCx
, MethodAssert
, NS_LITERAL_STRING("assert"), aData
);
830 METHOD(Count
, "count")
833 Console::__noSuchMethod__()
840 StackFrameToStackEntry(nsIStackFrame
* aStackFrame
,
841 ConsoleStackEntry
& aStackEntry
,
844 MOZ_ASSERT(aStackFrame
);
846 nsresult rv
= aStackFrame
->GetFilename(aStackEntry
.mFilename
);
847 NS_ENSURE_SUCCESS(rv
, rv
);
850 rv
= aStackFrame
->GetLineNumber(&lineNumber
);
851 NS_ENSURE_SUCCESS(rv
, rv
);
853 aStackEntry
.mLineNumber
= lineNumber
;
855 int32_t columnNumber
;
856 rv
= aStackFrame
->GetColumnNumber(&columnNumber
);
857 NS_ENSURE_SUCCESS(rv
, rv
);
859 aStackEntry
.mColumnNumber
= columnNumber
;
861 rv
= aStackFrame
->GetName(aStackEntry
.mFunctionName
);
862 NS_ENSURE_SUCCESS(rv
, rv
);
864 aStackEntry
.mLanguage
= aLanguage
;
870 ReifyStack(nsIStackFrame
* aStack
, nsTArray
<ConsoleStackEntry
>& aRefiedStack
)
872 nsCOMPtr
<nsIStackFrame
> stack(aStack
);
876 nsresult rv
= stack
->GetLanguage(&language
);
877 NS_ENSURE_SUCCESS(rv
, rv
);
879 if (language
== nsIProgrammingLanguage::JAVASCRIPT
||
880 language
== nsIProgrammingLanguage::JAVASCRIPT2
) {
881 ConsoleStackEntry
& data
= *aRefiedStack
.AppendElement();
882 rv
= StackFrameToStackEntry(stack
, data
, language
);
883 NS_ENSURE_SUCCESS(rv
, rv
);
886 nsCOMPtr
<nsIStackFrame
> caller
;
887 rv
= stack
->GetCaller(getter_AddRefs(caller
));
888 NS_ENSURE_SUCCESS(rv
, rv
);
896 class ConsoleTimelineMarker
: public TimelineMarker
899 ConsoleTimelineMarker(nsDocShell
* aDocShell
,
900 TracingMetadata aMetaData
,
901 const nsAString
& aCause
)
902 : TimelineMarker(aDocShell
, "ConsoleTime", aMetaData
, aCause
)
904 if (aMetaData
== TRACING_INTERVAL_END
) {
909 virtual bool Equals(const TimelineMarker
* aOther
)
911 if (!TimelineMarker::Equals(aOther
)) {
914 // Console markers must have matching causes as well.
915 return GetCause() == aOther
->GetCause();
918 virtual void AddDetails(mozilla::dom::ProfileTimelineMarker
& aMarker
)
920 if (GetMetaData() == TRACING_INTERVAL_START
) {
921 aMarker
.mCauseName
.Construct(GetCause());
923 aMarker
.mEndStack
= GetStack();
928 // Queue a call to a console method. See the CALL_DELAY constant.
930 Console::Method(JSContext
* aCx
, MethodName aMethodName
,
931 const nsAString
& aMethodString
,
932 const Sequence
<JS::Value
>& aData
)
934 nsAutoPtr
<ConsoleCallData
> callData(new ConsoleCallData());
936 ClearException
ce(aCx
);
938 callData
->Initialize(aCx
, aMethodName
, aMethodString
, aData
);
941 nsCOMPtr
<nsIWebNavigation
> webNav
= do_GetInterface(mWindow
);
946 nsCOMPtr
<nsILoadContext
> loadContext
= do_QueryInterface(webNav
);
947 MOZ_ASSERT(loadContext
);
949 loadContext
->GetUsePrivateBrowsing(&callData
->mPrivate
);
952 uint32_t maxDepth
= ShouldIncludeStackTrace(aMethodName
) ?
953 DEFAULT_MAX_STACKTRACE_DEPTH
: 1;
954 nsCOMPtr
<nsIStackFrame
> stack
= CreateStack(aCx
, maxDepth
);
960 // Walk up to the first JS stack frame and save it if we find it.
963 nsresult rv
= stack
->GetLanguage(&language
);
968 if (language
== nsIProgrammingLanguage::JAVASCRIPT
||
969 language
== nsIProgrammingLanguage::JAVASCRIPT2
) {
970 callData
->mTopStackFrame
.emplace();
971 nsresult rv
= StackFrameToStackEntry(stack
,
972 *callData
->mTopStackFrame
,
981 nsCOMPtr
<nsIStackFrame
> caller
;
982 rv
= stack
->GetCaller(getter_AddRefs(caller
));
990 if (NS_IsMainThread()) {
991 callData
->mStack
= stack
;
993 // nsIStackFrame is not threadsafe, so we need to snapshot it now,
994 // before we post our runnable to the main thread.
995 callData
->mReifiedStack
.emplace();
996 nsresult rv
= ReifyStack(stack
, *callData
->mReifiedStack
);
997 if (NS_WARN_IF(NS_FAILED(rv
))) {
1002 // Monotonic timer for 'time' and 'timeEnd'
1003 if ((aMethodName
== MethodTime
|| aMethodName
== MethodTimeEnd
)) {
1005 nsGlobalWindow
*win
= static_cast<nsGlobalWindow
*>(mWindow
.get());
1008 nsRefPtr
<nsPerformance
> performance
= win
->GetPerformance();
1013 callData
->mMonotonicTimer
= performance
->Now();
1015 // 'time' and 'timeEnd' are displayed in the devtools timeline if active.
1016 bool isTimelineRecording
= false;
1017 nsDocShell
* docShell
= static_cast<nsDocShell
*>(mWindow
->GetDocShell());
1019 docShell
->GetRecordProfileTimelineMarkers(&isTimelineRecording
);
1022 if (isTimelineRecording
&& aData
.Length() == 1) {
1023 JS::Rooted
<JS::Value
> value(aCx
, aData
[0]);
1024 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
1027 if (key
.init(aCx
, jsString
)) {
1028 mozilla::UniquePtr
<TimelineMarker
> marker
=
1029 MakeUnique
<ConsoleTimelineMarker
>(docShell
,
1030 aMethodName
== MethodTime
? TRACING_INTERVAL_START
: TRACING_INTERVAL_END
,
1032 docShell
->AddProfileTimelineMarker(marker
);
1038 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1039 MOZ_ASSERT(workerPrivate
);
1041 TimeDuration duration
=
1042 mozilla::TimeStamp::Now() - workerPrivate
->CreationTimeStamp();
1044 callData
->mMonotonicTimer
= duration
.ToMilliseconds();
1048 if (NS_IsMainThread()) {
1049 callData
->SetIDs(mOuterID
, mInnerID
);
1050 ProcessCallData(callData
);
1054 // Note: we can pass the reference of callData because this runnable calls
1055 // ProcessCallData() synchronously.
1056 nsRefPtr
<ConsoleCallDataRunnable
> runnable
=
1057 new ConsoleCallDataRunnable(this, callData
);
1058 runnable
->Dispatch();
1061 // We store information to lazily compute the stack in the reserved slots of
1062 // LazyStackGetter. The first slot always stores a JS object: it's either the
1063 // JS wrapper of the nsIStackFrame or the actual reified stack representation.
1064 // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
1065 // reified the stack yet, or an UndefinedValue() otherwise.
1072 LazyStackGetter(JSContext
* aCx
, unsigned aArgc
, JS::Value
* aVp
)
1074 JS::CallArgs args
= CallArgsFromVp(aArgc
, aVp
);
1075 JS::Rooted
<JSObject
*> callee(aCx
, &args
.callee());
1077 JS::Value v
= js::GetFunctionNativeReserved(&args
.callee(), SLOT_RAW_STACK
);
1078 if (v
.isUndefined()) {
1080 args
.rval().set(js::GetFunctionNativeReserved(callee
, SLOT_STACKOBJ
));
1084 nsIStackFrame
* stack
= reinterpret_cast<nsIStackFrame
*>(v
.toPrivate());
1085 nsTArray
<ConsoleStackEntry
> reifiedStack
;
1086 nsresult rv
= ReifyStack(stack
, reifiedStack
);
1087 if (NS_FAILED(rv
)) {
1092 JS::Rooted
<JS::Value
> stackVal(aCx
);
1093 if (!ToJSValue(aCx
, reifiedStack
, &stackVal
)) {
1097 MOZ_ASSERT(stackVal
.isObject());
1099 js::SetFunctionNativeReserved(callee
, SLOT_STACKOBJ
, stackVal
);
1100 js::SetFunctionNativeReserved(callee
, SLOT_RAW_STACK
, JS::UndefinedValue());
1102 args
.rval().set(stackVal
);
1107 Console::ProcessCallData(ConsoleCallData
* aData
)
1110 MOZ_ASSERT(NS_IsMainThread());
1112 ConsoleStackEntry frame
;
1113 if (aData
->mTopStackFrame
) {
1114 frame
= *aData
->mTopStackFrame
;
1117 AutoSafeJSContext cx
;
1118 ClearException
ce(cx
);
1119 RootedDictionary
<ConsoleEvent
> event(cx
);
1121 JSAutoCompartment
ac(cx
, aData
->mGlobal
);
1123 event
.mID
.Construct();
1124 event
.mInnerID
.Construct();
1126 MOZ_ASSERT(aData
->mIDType
!= ConsoleCallData::eUnknown
);
1127 if (aData
->mIDType
== ConsoleCallData::eString
) {
1128 event
.mID
.Value().SetAsString() = aData
->mOuterIDString
;
1129 event
.mInnerID
.Value().SetAsString() = aData
->mInnerIDString
;
1131 MOZ_ASSERT(aData
->mIDType
== ConsoleCallData::eNumber
);
1132 event
.mID
.Value().SetAsUnsignedLong() = aData
->mOuterIDNumber
;
1133 event
.mInnerID
.Value().SetAsUnsignedLong() = aData
->mInnerIDNumber
;
1136 event
.mLevel
= aData
->mMethodString
;
1137 event
.mFilename
= frame
.mFilename
;
1138 event
.mLineNumber
= frame
.mLineNumber
;
1139 event
.mColumnNumber
= frame
.mColumnNumber
;
1140 event
.mFunctionName
= frame
.mFunctionName
;
1141 event
.mTimeStamp
= aData
->mTimeStamp
;
1142 event
.mPrivate
= aData
->mPrivate
;
1144 switch (aData
->mMethodName
) {
1149 case MethodException
:
1152 event
.mArguments
.Construct();
1153 event
.mStyles
.Construct();
1154 ProcessArguments(cx
, aData
->mArguments
, event
.mArguments
.Value(),
1155 event
.mStyles
.Value());
1159 event
.mArguments
.Construct();
1160 ArgumentsToValueList(aData
->mArguments
, event
.mArguments
.Value());
1163 if (aData
->mMethodName
== MethodGroup
||
1164 aData
->mMethodName
== MethodGroupCollapsed
||
1165 aData
->mMethodName
== MethodGroupEnd
) {
1166 ComposeGroupName(cx
, aData
->mArguments
, event
.mGroupName
);
1169 else if (aData
->mMethodName
== MethodTime
&& !aData
->mArguments
.IsEmpty()) {
1170 event
.mTimer
= StartTimer(cx
, aData
->mArguments
[0], aData
->mMonotonicTimer
);
1173 else if (aData
->mMethodName
== MethodTimeEnd
&& !aData
->mArguments
.IsEmpty()) {
1174 event
.mTimer
= StopTimer(cx
, aData
->mArguments
[0], aData
->mMonotonicTimer
);
1177 else if (aData
->mMethodName
== MethodCount
) {
1178 event
.mCounter
= IncreaseCounter(cx
, frame
, aData
->mArguments
);
1181 // We want to create a console event object and pass it to our
1182 // nsIConsoleAPIStorage implementation. We want to define some accessor
1183 // properties on this object, and those will need to keep an nsIStackFrame
1184 // alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
1185 // further, passing untrusted objects to system code is likely to run afoul of
1186 // Object Xrays. So we want to wrap in a system-principal scope here. But
1187 // which one? We could cheat and try to get the underlying JSObject* of
1188 // mStorage, but that's a bit fragile. Instead, we just use the junk scope,
1189 // with explicit permission from the XPConnect module owner. If you're
1190 // tempted to do that anywhere else, talk to said module owner first.
1191 JSAutoCompartment
ac2(cx
, xpc::PrivilegedJunkScope());
1193 JS::Rooted
<JS::Value
> eventValue(cx
);
1194 if (!ToJSValue(cx
, event
, &eventValue
)) {
1198 JS::Rooted
<JSObject
*> eventObj(cx
, &eventValue
.toObject());
1199 MOZ_ASSERT(eventObj
);
1201 if (!JS_DefineProperty(cx
, eventObj
, "wrappedJSObject", eventValue
, JSPROP_ENUMERATE
)) {
1205 if (ShouldIncludeStackTrace(aData
->mMethodName
)) {
1206 // Now define the "stacktrace" property on eventObj. There are two cases
1207 // here. Either we came from a worker and have a reified stack, or we want
1208 // to define a getter that will lazily reify the stack.
1209 if (aData
->mReifiedStack
) {
1210 JS::Rooted
<JS::Value
> stacktrace(cx
);
1211 if (!ToJSValue(cx
, *aData
->mReifiedStack
, &stacktrace
) ||
1212 !JS_DefineProperty(cx
, eventObj
, "stacktrace", stacktrace
,
1213 JSPROP_ENUMERATE
)) {
1217 JSFunction
* fun
= js::NewFunctionWithReserved(cx
, LazyStackGetter
, 0, 0,
1218 eventObj
, "stacktrace");
1223 JS::Rooted
<JSObject
*> funObj(cx
, JS_GetFunctionObject(fun
));
1225 // We want to store our stack in the function and have it stay alive. But
1226 // we also need sane access to the C++ nsIStackFrame. So store both a JS
1227 // wrapper and the raw pointer: the former will keep the latter alive.
1228 JS::Rooted
<JS::Value
> stackVal(cx
);
1229 nsresult rv
= nsContentUtils::WrapNative(cx
, aData
->mStack
,
1231 if (NS_FAILED(rv
)) {
1235 js::SetFunctionNativeReserved(funObj
, SLOT_STACKOBJ
, stackVal
);
1236 js::SetFunctionNativeReserved(funObj
, SLOT_RAW_STACK
,
1237 JS::PrivateValue(aData
->mStack
.get()));
1239 if (!JS_DefineProperty(cx
, eventObj
, "stacktrace",
1240 JS::UndefinedHandleValue
,
1241 JSPROP_ENUMERATE
| JSPROP_SHARED
| JSPROP_GETTER
|
1243 JS_DATA_TO_FUNC_PTR(JSNative
, funObj
.get()),
1251 mStorage
= do_GetService("@mozilla.org/consoleAPI-storage;1");
1255 NS_WARNING("Failed to get the ConsoleAPIStorage service.");
1259 nsAutoString innerID
, outerID
;
1261 MOZ_ASSERT(aData
->mIDType
!= ConsoleCallData::eUnknown
);
1262 if (aData
->mIDType
== ConsoleCallData::eString
) {
1263 outerID
= aData
->mOuterIDString
;
1264 innerID
= aData
->mInnerIDString
;
1266 MOZ_ASSERT(aData
->mIDType
== ConsoleCallData::eNumber
);
1267 outerID
.AppendInt(aData
->mOuterIDNumber
);
1268 innerID
.AppendInt(aData
->mInnerIDNumber
);
1271 if (NS_FAILED(mStorage
->RecordPendingEvent(innerID
, outerID
, eventValue
))) {
1272 NS_WARNING("Failed to record a console event.");
1277 Console::ProcessArguments(JSContext
* aCx
,
1278 const nsTArray
<JS::Heap
<JS::Value
>>& aData
,
1279 Sequence
<JS::Value
>& aSequence
,
1280 Sequence
<JS::Value
>& aStyles
)
1282 if (aData
.IsEmpty()) {
1286 if (aData
.Length() == 1 || !aData
[0].isString()) {
1287 ArgumentsToValueList(aData
, aSequence
);
1291 JS::Rooted
<JS::Value
> format(aCx
, aData
[0]);
1292 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, format
));
1297 nsAutoJSString string
;
1298 if (!string
.init(aCx
, jsString
)) {
1302 nsString::const_iterator start
, end
;
1303 string
.BeginReading(start
);
1304 string
.EndReading(end
);
1309 while (start
!= end
) {
1310 if (*start
!= '%') {
1311 output
.Append(*start
);
1322 if (*start
== '%') {
1323 output
.Append(*start
);
1331 int32_t integer
= -1;
1332 int32_t mantissa
= -1;
1334 // Let's parse %<number>.<number> for %d and %f
1335 if (*start
>= '0' && *start
<= '9') {
1339 integer
= integer
* 10 + *start
- '0';
1342 } while (*start
>= '0' && *start
<= '9' && start
!= end
);
1350 if (*start
== '.') {
1359 // '.' must be followed by a number.
1360 if (*start
< '0' || *start
> '9') {
1368 mantissa
= mantissa
* 10 + *start
- '0';
1371 } while (*start
>= '0' && *start
<= '9' && start
!= end
);
1387 if (!output
.IsEmpty()) {
1388 JS::Rooted
<JSString
*> str(aCx
, JS_NewUCStringCopyN(aCx
,
1395 aSequence
.AppendElement(JS::StringValue(str
));
1399 JS::Rooted
<JS::Value
> v(aCx
);
1400 if (index
< aData
.Length()) {
1404 aSequence
.AppendElement(v
);
1410 if (!output
.IsEmpty()) {
1411 JS::Rooted
<JSString
*> str(aCx
, JS_NewUCStringCopyN(aCx
,
1418 aSequence
.AppendElement(JS::StringValue(str
));
1422 if (index
< aData
.Length()) {
1423 JS::Rooted
<JS::Value
> v(aCx
, aData
[index
++]);
1424 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, v
));
1429 int32_t diff
= aSequence
.Length() - aStyles
.Length();
1431 for (int32_t i
= 0; i
< diff
; i
++) {
1432 aStyles
.AppendElement(JS::NullValue());
1435 aStyles
.AppendElement(JS::StringValue(jsString
));
1441 if (index
< aData
.Length()) {
1442 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
1443 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
1449 if (!v
.init(aCx
, jsString
)) {
1459 if (index
< aData
.Length()) {
1460 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
1463 if (!JS::ToInt32(aCx
, value
, &v
)) {
1468 MakeFormatString(format
, integer
, mantissa
, 'd');
1469 output
.AppendPrintf(format
.get(), v
);
1474 if (index
< aData
.Length()) {
1475 JS::Rooted
<JS::Value
> value(aCx
, aData
[index
++]);
1478 if (!JS::ToNumber(aCx
, value
, &v
)) {
1483 MakeFormatString(format
, integer
, mantissa
, 'f');
1484 output
.AppendPrintf(format
.get(), v
);
1494 if (!output
.IsEmpty()) {
1495 JS::Rooted
<JSString
*> str(aCx
, JS_NewUCStringCopyN(aCx
, output
.get(),
1501 aSequence
.AppendElement(JS::StringValue(str
));
1504 // The rest of the array, if unused by the format string.
1505 for (; index
< aData
.Length(); ++index
) {
1506 aSequence
.AppendElement(aData
[index
]);
1511 Console::MakeFormatString(nsCString
& aFormat
, int32_t aInteger
,
1512 int32_t aMantissa
, char aCh
)
1514 aFormat
.Append('%');
1515 if (aInteger
>= 0) {
1516 aFormat
.AppendInt(aInteger
);
1519 if (aMantissa
>= 0) {
1520 aFormat
.Append('.');
1521 aFormat
.AppendInt(aMantissa
);
1524 aFormat
.Append(aCh
);
1528 Console::ComposeGroupName(JSContext
* aCx
,
1529 const nsTArray
<JS::Heap
<JS::Value
>>& aData
,
1532 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
1534 aName
.AppendASCII(" ");
1537 JS::Rooted
<JS::Value
> value(aCx
, aData
[i
]);
1538 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, value
));
1543 nsAutoJSString string
;
1544 if (!string
.init(aCx
, jsString
)) {
1548 aName
.Append(string
);
1553 Console::StartTimer(JSContext
* aCx
, const JS::Value
& aName
,
1554 DOMHighResTimeStamp aTimestamp
)
1556 if (mTimerRegistry
.Count() >= MAX_PAGE_TIMERS
) {
1557 RootedDictionary
<ConsoleTimerError
> error(aCx
);
1559 JS::Rooted
<JS::Value
> value(aCx
);
1560 if (!ToJSValue(aCx
, error
, &value
)) {
1561 return JS::UndefinedValue();
1567 RootedDictionary
<ConsoleTimerStart
> timer(aCx
);
1569 JS::Rooted
<JS::Value
> name(aCx
, aName
);
1570 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, name
));
1572 return JS::UndefinedValue();
1576 if (!key
.init(aCx
, jsString
)) {
1577 return JS::UndefinedValue();
1582 DOMHighResTimeStamp entry
;
1583 if (!mTimerRegistry
.Get(key
, &entry
)) {
1584 mTimerRegistry
.Put(key
, aTimestamp
);
1589 timer
.mStarted
= aTimestamp
;
1591 JS::Rooted
<JS::Value
> value(aCx
);
1592 if (!ToJSValue(aCx
, timer
, &value
)) {
1593 return JS::UndefinedValue();
1600 Console::StopTimer(JSContext
* aCx
, const JS::Value
& aName
,
1601 DOMHighResTimeStamp aTimestamp
)
1603 JS::Rooted
<JS::Value
> name(aCx
, aName
);
1604 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, name
));
1606 return JS::UndefinedValue();
1610 if (!key
.init(aCx
, jsString
)) {
1611 return JS::UndefinedValue();
1614 DOMHighResTimeStamp entry
;
1615 if (!mTimerRegistry
.Get(key
, &entry
)) {
1616 return JS::UndefinedValue();
1619 mTimerRegistry
.Remove(key
);
1621 RootedDictionary
<ConsoleTimerEnd
> timer(aCx
);
1623 timer
.mDuration
= aTimestamp
- entry
;
1625 JS::Rooted
<JS::Value
> value(aCx
);
1626 if (!ToJSValue(aCx
, timer
, &value
)) {
1627 return JS::UndefinedValue();
1634 Console::ArgumentsToValueList(const nsTArray
<JS::Heap
<JS::Value
>>& aData
,
1635 Sequence
<JS::Value
>& aSequence
)
1637 for (uint32_t i
= 0; i
< aData
.Length(); ++i
) {
1638 aSequence
.AppendElement(aData
[i
]);
1643 Console::IncreaseCounter(JSContext
* aCx
, const ConsoleStackEntry
& aFrame
,
1644 const nsTArray
<JS::Heap
<JS::Value
>>& aArguments
)
1646 ClearException
ce(aCx
);
1651 if (!aArguments
.IsEmpty()) {
1652 JS::Rooted
<JS::Value
> labelValue(aCx
, aArguments
[0]);
1653 JS::Rooted
<JSString
*> jsString(aCx
, JS::ToString(aCx
, labelValue
));
1655 nsAutoJSString string
;
1656 if (jsString
&& string
.init(aCx
, jsString
)) {
1662 if (key
.IsEmpty()) {
1663 key
.Append(aFrame
.mFilename
);
1665 key
.AppendInt(aFrame
.mLineNumber
);
1669 if (!mCounterRegistry
.Get(key
, &count
)) {
1670 if (mCounterRegistry
.Count() >= MAX_PAGE_COUNTERS
) {
1671 RootedDictionary
<ConsoleCounterError
> error(aCx
);
1673 JS::Rooted
<JS::Value
> value(aCx
);
1674 if (!ToJSValue(aCx
, error
, &value
)) {
1675 return JS::UndefinedValue();
1683 mCounterRegistry
.Put(key
, count
);
1685 RootedDictionary
<ConsoleCounter
> data(aCx
);
1686 data
.mLabel
= label
;
1687 data
.mCount
= count
;
1689 JS::Rooted
<JS::Value
> value(aCx
);
1690 if (!ToJSValue(aCx
, data
, &value
)) {
1691 return JS::UndefinedValue();
1698 Console::ShouldIncludeStackTrace(MethodName aMethodName
)
1700 switch (aMethodName
) {
1702 case MethodException
:
1711 nsIXPConnectJSObjectHolder
*
1712 Console::GetOrCreateSandbox(JSContext
* aCx
, nsIPrincipal
* aPrincipal
)
1714 MOZ_ASSERT(NS_IsMainThread());
1717 nsIXPConnect
* xpc
= nsContentUtils::XPConnect();
1718 MOZ_ASSERT(xpc
, "This should never be null!");
1720 nsresult rv
= xpc
->CreateSandbox(aCx
, aPrincipal
,
1721 getter_AddRefs(mSandbox
));
1722 if (NS_WARN_IF(NS_FAILED(rv
))) {
1731 } // namespace mozilla