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 "SerializedStackHolder.h"
9 #include "js/SavedFrameAPI.h"
10 #include "mozilla/dom/WorkerPrivate.h"
11 #include "mozilla/dom/ScriptSettings.h"
12 #include "mozilla/Services.h"
13 #include "nsJSPrincipals.h"
14 #include "nsIObserverService.h"
15 #include "xpcpublic.h"
17 namespace mozilla::dom
{
19 SerializedStackHolder::SerializedStackHolder()
20 : mHolder(StructuredCloneHolder::CloningSupported
,
21 StructuredCloneHolder::TransferringNotSupported
,
22 StructuredCloneHolder::StructuredCloneScope::SameProcess
) {}
24 void SerializedStackHolder::WriteStack(JSContext
* aCx
,
25 JS::Handle
<JSObject
*> aStack
) {
26 JS::Rooted
<JS::Value
> stackValue(aCx
, JS::ObjectValue(*aStack
));
27 mHolder
.Write(aCx
, stackValue
, IgnoreErrors());
29 // StructuredCloneHolder::Write can leave a pending exception on the context.
30 JS_ClearPendingException(aCx
);
33 void SerializedStackHolder::SerializeMainThreadOrWorkletStack(
34 JSContext
* aCx
, JS::Handle
<JSObject
*> aStack
) {
35 MOZ_ASSERT(!IsCurrentThreadRunningWorker());
36 WriteStack(aCx
, aStack
);
39 void SerializedStackHolder::SerializeWorkerStack(JSContext
* aCx
,
40 WorkerPrivate
* aWorkerPrivate
,
41 JS::Handle
<JSObject
*> aStack
) {
42 MOZ_ASSERT(aWorkerPrivate
->IsOnCurrentThread());
44 RefPtr
<StrongWorkerRef
> workerRef
=
45 StrongWorkerRef::Create(aWorkerPrivate
, "WorkerErrorReport");
47 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
49 // Don't write the stack if we can't create a ref to the worker.
53 WriteStack(aCx
, aStack
);
56 void SerializedStackHolder::SerializeCurrentStack(JSContext
* aCx
) {
57 JS::Rooted
<JSObject
*> stack(aCx
);
58 if (JS::CurrentGlobalOrNull(aCx
) && !JS::CaptureCurrentStack(aCx
, &stack
)) {
59 JS_ClearPendingException(aCx
);
64 if (NS_IsMainThread()) {
65 SerializeMainThreadOrWorkletStack(aCx
, stack
);
67 WorkerPrivate
* currentWorker
= GetCurrentThreadWorkerPrivate();
68 SerializeWorkerStack(aCx
, currentWorker
, stack
);
73 JSObject
* SerializedStackHolder::ReadStack(JSContext
* aCx
) {
74 MOZ_ASSERT(NS_IsMainThread());
75 if (!mHolder
.HasData()) {
79 JS::Rooted
<JS::Value
> stackValue(aCx
);
81 mHolder
.Read(xpc::CurrentNativeGlobal(aCx
), aCx
, &stackValue
, IgnoreErrors());
83 return stackValue
.isObject() ? &stackValue
.toObject() : nullptr;
86 UniquePtr
<SerializedStackHolder
> GetCurrentStackForNetMonitor(JSContext
* aCx
) {
87 MOZ_ASSERT_IF(!NS_IsMainThread(),
88 GetCurrentThreadWorkerPrivate()->IsWatchedByDevTools());
90 return GetCurrentStack(aCx
);
93 UniquePtr
<SerializedStackHolder
> GetCurrentStack(JSContext
* aCx
) {
94 UniquePtr
<SerializedStackHolder
> stack
= MakeUnique
<SerializedStackHolder
>();
95 stack
->SerializeCurrentStack(aCx
);
99 void NotifyNetworkMonitorAlternateStack(
100 nsISupports
* aChannel
, UniquePtr
<SerializedStackHolder
> aStackHolder
) {
105 nsString stackString
;
106 ConvertSerializedStackToJSON(std::move(aStackHolder
), stackString
);
108 if (!stackString
.IsEmpty()) {
109 NotifyNetworkMonitorAlternateStack(aChannel
, stackString
);
113 void ConvertSerializedStackToJSON(UniquePtr
<SerializedStackHolder
> aStackHolder
,
114 nsAString
& aStackString
) {
115 // We need a JSContext to be able to stringify the SavedFrame stack.
116 // This will not run any scripts. A privileged scope is needed to fully
117 // inspect all stack frames we find.
119 DebugOnly
<bool> ok
= jsapi
.Init(xpc::PrivilegedJunkScope());
120 JSContext
* cx
= jsapi
.cx();
122 JS::Rooted
<JSObject
*> savedFrame(cx
, aStackHolder
->ReadStack(cx
));
127 JS::Rooted
<JSObject
*> converted(cx
);
128 converted
= JS::ConvertSavedFrameToPlainObject(
129 cx
, savedFrame
, JS::SavedFrameSelfHosted::Exclude
);
131 JS_ClearPendingException(cx
);
135 JS::Rooted
<JS::Value
> convertedValue(cx
, JS::ObjectValue(*converted
));
136 if (!nsContentUtils::StringifyJSON(cx
, convertedValue
, aStackString
,
137 UndefinedIsNullStringLiteral
)) {
138 JS_ClearPendingException(cx
);
143 void NotifyNetworkMonitorAlternateStack(nsISupports
* aChannel
,
144 const nsAString
& aStackJSON
) {
145 nsCOMPtr
<nsIObserverService
> obsService
= services::GetObserverService();
150 obsService
->NotifyObservers(aChannel
, "network-monitor-alternate-stack",
151 PromiseFlatString(aStackJSON
).get());
154 } // namespace mozilla::dom