Bug 1750871 - run mochitest-remote on fission everywhere. r=releng-reviewers,aki
[gecko.git] / dom / base / SerializedStackHolder.cpp
blob76352832a62223e444efc1f4afe74fe7ba0c24ee
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");
42 if (workerRef) {
43 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
44 } else {
45 // Don't write the stack if we can't create a ref to the worker.
46 return;
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);
56 return;
59 if (stack) {
60 if (NS_IsMainThread()) {
61 SerializeMainThreadOrWorkletStack(aCx, stack);
62 } else {
63 WorkerPrivate* currentWorker = GetCurrentThreadWorkerPrivate();
64 SerializeWorkerStack(aCx, currentWorker, stack);
69 JSObject* SerializedStackHolder::ReadStack(JSContext* aCx) {
70 MOZ_ASSERT(NS_IsMainThread());
71 if (!mHolder.HasData()) {
72 return nullptr;
75 JS::RootedValue stackValue(aCx);
78 Maybe<nsJSPrincipals::AutoSetActiveWorkerPrincipal> set;
79 if (mWorkerRef) {
80 set.emplace(mWorkerRef->Private()->GetPrincipal());
83 mHolder.Read(xpc::CurrentNativeGlobal(aCx), aCx, &stackValue,
84 IgnoreErrors());
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);
100 return stack;
103 void NotifyNetworkMonitorAlternateStack(
104 nsISupports* aChannel, UniquePtr<SerializedStackHolder> aStackHolder) {
105 if (!aStackHolder) {
106 return;
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.
122 AutoJSAPI jsapi;
123 DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
124 JSContext* cx = jsapi.cx();
126 JS::RootedObject savedFrame(cx, aStackHolder->ReadStack(cx));
127 if (!savedFrame) {
128 return;
131 JS::RootedObject converted(cx);
132 converted = JS::ConvertSavedFrameToPlainObject(
133 cx, savedFrame, JS::SavedFrameSelfHosted::Exclude);
134 if (!converted) {
135 JS_ClearPendingException(cx);
136 return;
139 JS::RootedValue convertedValue(cx, JS::ObjectValue(*converted));
140 if (!nsContentUtils::StringifyJSON(cx, &convertedValue, aStackString)) {
141 JS_ClearPendingException(cx);
142 return;
146 void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
147 const nsAString& aStackJSON) {
148 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
149 if (!obsService) {
150 return;
153 obsService->NotifyObservers(aChannel, "network-monitor-alternate-stack",
154 PromiseFlatString(aStackJSON).get());
157 } // namespace mozilla::dom