diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/base/SerializedStackHolder.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/base/SerializedStackHolder.cpp')
-rw-r--r-- | dom/base/SerializedStackHolder.cpp | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/dom/base/SerializedStackHolder.cpp b/dom/base/SerializedStackHolder.cpp new file mode 100644 index 0000000000..fa52de9ba2 --- /dev/null +++ b/dom/base/SerializedStackHolder.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SerializedStackHolder.h" + +#include "js/SavedFrameAPI.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "nsJSPrincipals.h" + +namespace mozilla::dom { + +SerializedStackHolder::SerializedStackHolder() + : mHolder(StructuredCloneHolder::CloningSupported, + StructuredCloneHolder::TransferringNotSupported, + StructuredCloneHolder::StructuredCloneScope::SameProcess) {} + +void SerializedStackHolder::WriteStack(JSContext* aCx, + JS::HandleObject aStack) { + JS::RootedValue stackValue(aCx, JS::ObjectValue(*aStack)); + mHolder.Write(aCx, stackValue, IgnoreErrors()); + + // StructuredCloneHolder::Write can leave a pending exception on the context. + JS_ClearPendingException(aCx); +} + +void SerializedStackHolder::SerializeMainThreadOrWorkletStack( + JSContext* aCx, JS::HandleObject aStack) { + MOZ_ASSERT(!IsCurrentThreadRunningWorker()); + WriteStack(aCx, aStack); +} + +void SerializedStackHolder::SerializeWorkerStack(JSContext* aCx, + WorkerPrivate* aWorkerPrivate, + JS::HandleObject aStack) { + MOZ_ASSERT(aWorkerPrivate->IsOnCurrentThread()); + + RefPtr<StrongWorkerRef> workerRef = + StrongWorkerRef::Create(aWorkerPrivate, "WorkerErrorReport"); + if (workerRef) { + mWorkerRef = new ThreadSafeWorkerRef(workerRef); + } else { + // Don't write the stack if we can't create a ref to the worker. + return; + } + + WriteStack(aCx, aStack); +} + +void SerializedStackHolder::SerializeCurrentStack(JSContext* aCx) { + JS::RootedObject stack(aCx); + if (JS::CurrentGlobalOrNull(aCx) && !JS::CaptureCurrentStack(aCx, &stack)) { + JS_ClearPendingException(aCx); + return; + } + + if (stack) { + if (NS_IsMainThread()) { + SerializeMainThreadOrWorkletStack(aCx, stack); + } else { + WorkerPrivate* currentWorker = GetCurrentThreadWorkerPrivate(); + SerializeWorkerStack(aCx, currentWorker, stack); + } + } +} + +JSObject* SerializedStackHolder::ReadStack(JSContext* aCx) { + MOZ_ASSERT(NS_IsMainThread()); + if (!mHolder.HasData()) { + return nullptr; + } + + JS::RootedValue stackValue(aCx); + + { + Maybe<nsJSPrincipals::AutoSetActiveWorkerPrincipal> set; + if (mWorkerRef) { + set.emplace(mWorkerRef->Private()->GetPrincipal()); + } + + mHolder.Read(xpc::CurrentNativeGlobal(aCx), aCx, &stackValue, + IgnoreErrors()); + } + + return stackValue.isObject() ? &stackValue.toObject() : nullptr; +} + +UniquePtr<SerializedStackHolder> GetCurrentStackForNetMonitor(JSContext* aCx) { + MOZ_ASSERT_IF(!NS_IsMainThread(), + GetCurrentThreadWorkerPrivate()->IsWatchedByDevTools()); + + UniquePtr<SerializedStackHolder> stack = MakeUnique<SerializedStackHolder>(); + stack->SerializeCurrentStack(aCx); + return stack; +} + +void NotifyNetworkMonitorAlternateStack( + nsISupports* aChannel, UniquePtr<SerializedStackHolder> aStackHolder) { + if (!aStackHolder) { + return; + } + + nsString stackString; + ConvertSerializedStackToJSON(std::move(aStackHolder), stackString); + + if (!stackString.IsEmpty()) { + NotifyNetworkMonitorAlternateStack(aChannel, stackString); + } +} + +void ConvertSerializedStackToJSON(UniquePtr<SerializedStackHolder> aStackHolder, + nsAString& aStackString) { + // We need a JSContext to be able to stringify the SavedFrame stack. + // This will not run any scripts. A privileged scope is needed to fully + // inspect all stack frames we find. + AutoJSAPI jsapi; + DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope()); + JSContext* cx = jsapi.cx(); + + JS::RootedObject savedFrame(cx, aStackHolder->ReadStack(cx)); + if (!savedFrame) { + return; + } + + JS::RootedObject converted(cx); + converted = JS::ConvertSavedFrameToPlainObject( + cx, savedFrame, JS::SavedFrameSelfHosted::Exclude); + if (!converted) { + JS_ClearPendingException(cx); + return; + } + + JS::RootedValue convertedValue(cx, JS::ObjectValue(*converted)); + if (!nsContentUtils::StringifyJSON(cx, &convertedValue, aStackString)) { + JS_ClearPendingException(cx); + return; + } +} + +void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel, + const nsAString& aStackJSON) { + nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); + if (!obsService) { + return; + } + + obsService->NotifyObservers(aChannel, "network-monitor-alternate-stack", + PromiseFlatString(aStackJSON).get()); +} + +} // namespace mozilla::dom |