Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / base / SerializedStackHolder.cpp
blob199879e22640c471d3fd6fa1d5fd125a2ce0ae3a
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");
46 if (workerRef) {
47 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
48 } else {
49 // Don't write the stack if we can't create a ref to the worker.
50 return;
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);
60 return;
63 if (stack) {
64 if (NS_IsMainThread()) {
65 SerializeMainThreadOrWorkletStack(aCx, stack);
66 } else {
67 WorkerPrivate* currentWorker = GetCurrentThreadWorkerPrivate();
68 SerializeWorkerStack(aCx, currentWorker, stack);
73 JSObject* SerializedStackHolder::ReadStack(JSContext* aCx) {
74 MOZ_ASSERT(NS_IsMainThread());
75 if (!mHolder.HasData()) {
76 return nullptr;
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);
96 return stack;
99 void NotifyNetworkMonitorAlternateStack(
100 nsISupports* aChannel, UniquePtr<SerializedStackHolder> aStackHolder) {
101 if (!aStackHolder) {
102 return;
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.
118 AutoJSAPI jsapi;
119 DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
120 JSContext* cx = jsapi.cx();
122 JS::Rooted<JSObject*> savedFrame(cx, aStackHolder->ReadStack(cx));
123 if (!savedFrame) {
124 return;
127 JS::Rooted<JSObject*> converted(cx);
128 converted = JS::ConvertSavedFrameToPlainObject(
129 cx, savedFrame, JS::SavedFrameSelfHosted::Exclude);
130 if (!converted) {
131 JS_ClearPendingException(cx);
132 return;
135 JS::Rooted<JS::Value> convertedValue(cx, JS::ObjectValue(*converted));
136 if (!nsContentUtils::StringifyJSON(cx, convertedValue, aStackString,
137 UndefinedIsNullStringLiteral)) {
138 JS_ClearPendingException(cx);
139 return;
143 void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
144 const nsAString& aStackJSON) {
145 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
146 if (!obsService) {
147 return;
150 obsService->NotifyObservers(aChannel, "network-monitor-alternate-stack",
151 PromiseFlatString(aStackJSON).get());
154 } // namespace mozilla::dom