tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SerializedStackHolder.cpp (5047B)


      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/. */
      6 
      7 #include "SerializedStackHolder.h"
      8 
      9 #include "js/SavedFrameAPI.h"
     10 #include "mozilla/Services.h"
     11 #include "mozilla/dom/ScriptSettings.h"
     12 #include "mozilla/dom/WorkerPrivate.h"
     13 #include "nsIObserverService.h"
     14 #include "nsJSPrincipals.h"
     15 #include "xpcpublic.h"
     16 
     17 namespace mozilla::dom {
     18 
     19 SerializedStackHolder::SerializedStackHolder()
     20    : mHolder(StructuredCloneHolder::CloningSupported,
     21              StructuredCloneHolder::TransferringNotSupported,
     22              StructuredCloneHolder::StructuredCloneScope::SameProcess) {}
     23 
     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());
     28 
     29  // StructuredCloneHolder::Write can leave a pending exception on the context.
     30  JS_ClearPendingException(aCx);
     31 }
     32 
     33 void SerializedStackHolder::SerializeMainThreadOrWorkletStack(
     34    JSContext* aCx, JS::Handle<JSObject*> aStack) {
     35  MOZ_ASSERT(!IsCurrentThreadRunningWorker());
     36  WriteStack(aCx, aStack);
     37 }
     38 
     39 void SerializedStackHolder::SerializeWorkerStack(JSContext* aCx,
     40                                                 WorkerPrivate* aWorkerPrivate,
     41                                                 JS::Handle<JSObject*> aStack) {
     42  MOZ_ASSERT(aWorkerPrivate->IsOnCurrentThread());
     43 
     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;
     51  }
     52 
     53  WriteStack(aCx, aStack);
     54 }
     55 
     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;
     61  }
     62 
     63  if (stack) {
     64    if (NS_IsMainThread()) {
     65      SerializeMainThreadOrWorkletStack(aCx, stack);
     66    } else {
     67      WorkerPrivate* currentWorker = GetCurrentThreadWorkerPrivate();
     68      SerializeWorkerStack(aCx, currentWorker, stack);
     69    }
     70  }
     71 }
     72 
     73 JSObject* SerializedStackHolder::ReadStack(JSContext* aCx) {
     74  MOZ_ASSERT(NS_IsMainThread());
     75  if (!mHolder.HasData()) {
     76    return nullptr;
     77  }
     78 
     79  JS::Rooted<JS::Value> stackValue(aCx);
     80 
     81  mHolder.Read(xpc::CurrentNativeGlobal(aCx), aCx, &stackValue, IgnoreErrors());
     82 
     83  return stackValue.isObject() ? &stackValue.toObject() : nullptr;
     84 }
     85 
     86 UniquePtr<SerializedStackHolder> GetCurrentStackForNetMonitor(JSContext* aCx) {
     87  MOZ_ASSERT_IF(!NS_IsMainThread(),
     88                GetCurrentThreadWorkerPrivate()->IsWatchedByDevTools());
     89 
     90  return GetCurrentStack(aCx);
     91 }
     92 
     93 UniquePtr<SerializedStackHolder> GetCurrentStack(JSContext* aCx) {
     94  UniquePtr<SerializedStackHolder> stack = MakeUnique<SerializedStackHolder>();
     95  stack->SerializeCurrentStack(aCx);
     96  return stack;
     97 }
     98 
     99 void NotifyNetworkMonitorAlternateStack(
    100    nsISupports* aChannel, UniquePtr<SerializedStackHolder> aStackHolder) {
    101  if (!aStackHolder) {
    102    return;
    103  }
    104 
    105  nsString stackString;
    106  ConvertSerializedStackToJSON(std::move(aStackHolder), stackString);
    107 
    108  if (!stackString.IsEmpty()) {
    109    NotifyNetworkMonitorAlternateStack(aChannel, stackString);
    110  }
    111 }
    112 
    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();
    121 
    122  JS::Rooted<JSObject*> savedFrame(cx, aStackHolder->ReadStack(cx));
    123  if (!savedFrame) {
    124    return;
    125  }
    126 
    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;
    133  }
    134 
    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;
    140  }
    141 }
    142 
    143 void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
    144                                        const nsAString& aStackJSON) {
    145  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
    146  if (!obsService) {
    147    return;
    148  }
    149 
    150  obsService->NotifyObservers(aChannel, "network-monitor-alternate-stack",
    151                              PromiseFlatString(aStackJSON).get());
    152 }
    153 
    154 }  // namespace mozilla::dom