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 "nsJSPrincipals.h"
13 namespace mozilla::dom
{
15 SerializedStackHolder::SerializedStackHolder()
16 : mHolder(StructuredCloneHolder::CloningSupported
,
17 StructuredCloneHolder::TransferringNotSupported
,
18 StructuredCloneHolder::StructuredCloneScope::SameProcess
) {}
20 void SerializedStackHolder::WriteStack(JSContext
* aCx
,
21 JS::HandleObject aStack
) {
22 JS::RootedValue
stackValue(aCx
, JS::ObjectValue(*aStack
));
23 mHolder
.Write(aCx
, stackValue
, IgnoreErrors());
25 // StructuredCloneHolder::Write can leave a pending exception on the context.
26 JS_ClearPendingException(aCx
);
29 void SerializedStackHolder::SerializeMainThreadOrWorkletStack(
30 JSContext
* aCx
, JS::HandleObject aStack
) {
31 MOZ_ASSERT(!IsCurrentThreadRunningWorker());
32 WriteStack(aCx
, aStack
);
35 void SerializedStackHolder::SerializeWorkerStack(JSContext
* aCx
,
36 WorkerPrivate
* aWorkerPrivate
,
37 JS::HandleObject aStack
) {
38 MOZ_ASSERT(aWorkerPrivate
->IsOnCurrentThread());
40 RefPtr
<StrongWorkerRef
> workerRef
=
41 StrongWorkerRef::Create(aWorkerPrivate
, "WorkerErrorReport");
43 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
45 // Don't write the stack if we can't create a ref to the worker.
49 WriteStack(aCx
, aStack
);
52 void SerializedStackHolder::SerializeCurrentStack(JSContext
* aCx
) {
53 JS::RootedObject
stack(aCx
);
54 if (JS::CurrentGlobalOrNull(aCx
) && !JS::CaptureCurrentStack(aCx
, &stack
)) {
55 JS_ClearPendingException(aCx
);
60 if (NS_IsMainThread()) {
61 SerializeMainThreadOrWorkletStack(aCx
, stack
);
63 WorkerPrivate
* currentWorker
= GetCurrentThreadWorkerPrivate();
64 SerializeWorkerStack(aCx
, currentWorker
, stack
);
69 JSObject
* SerializedStackHolder::ReadStack(JSContext
* aCx
) {
70 MOZ_ASSERT(NS_IsMainThread());
71 if (!mHolder
.HasData()) {
75 JS::RootedValue
stackValue(aCx
);
78 Maybe
<nsJSPrincipals::AutoSetActiveWorkerPrincipal
> set
;
80 set
.emplace(mWorkerRef
->Private()->GetPrincipal());
83 mHolder
.Read(xpc::CurrentNativeGlobal(aCx
), aCx
, &stackValue
,
87 return stackValue
.isObject() ? &stackValue
.toObject() : nullptr;
90 UniquePtr
<SerializedStackHolder
> GetCurrentStackForNetMonitor(JSContext
* aCx
) {
91 MOZ_ASSERT_IF(!NS_IsMainThread(),
92 GetCurrentThreadWorkerPrivate()->IsWatchedByDevTools());
94 return GetCurrentStack(aCx
);
97 UniquePtr
<SerializedStackHolder
> GetCurrentStack(JSContext
* aCx
) {
98 UniquePtr
<SerializedStackHolder
> stack
= MakeUnique
<SerializedStackHolder
>();
99 stack
->SerializeCurrentStack(aCx
);
103 void NotifyNetworkMonitorAlternateStack(
104 nsISupports
* aChannel
, UniquePtr
<SerializedStackHolder
> aStackHolder
) {
109 nsString stackString
;
110 ConvertSerializedStackToJSON(std::move(aStackHolder
), stackString
);
112 if (!stackString
.IsEmpty()) {
113 NotifyNetworkMonitorAlternateStack(aChannel
, stackString
);
117 void ConvertSerializedStackToJSON(UniquePtr
<SerializedStackHolder
> aStackHolder
,
118 nsAString
& aStackString
) {
119 // We need a JSContext to be able to stringify the SavedFrame stack.
120 // This will not run any scripts. A privileged scope is needed to fully
121 // inspect all stack frames we find.
123 DebugOnly
<bool> ok
= jsapi
.Init(xpc::PrivilegedJunkScope());
124 JSContext
* cx
= jsapi
.cx();
126 JS::RootedObject
savedFrame(cx
, aStackHolder
->ReadStack(cx
));
131 JS::RootedObject
converted(cx
);
132 converted
= JS::ConvertSavedFrameToPlainObject(
133 cx
, savedFrame
, JS::SavedFrameSelfHosted::Exclude
);
135 JS_ClearPendingException(cx
);
139 JS::RootedValue
convertedValue(cx
, JS::ObjectValue(*converted
));
140 if (!nsContentUtils::StringifyJSON(cx
, &convertedValue
, aStackString
)) {
141 JS_ClearPendingException(cx
);
146 void NotifyNetworkMonitorAlternateStack(nsISupports
* aChannel
,
147 const nsAString
& aStackJSON
) {
148 nsCOMPtr
<nsIObserverService
> obsService
= services::GetObserverService();
153 obsService
->NotifyObservers(aChannel
, "network-monitor-alternate-stack",
154 PromiseFlatString(aStackJSON
).get());
157 } // namespace mozilla::dom