tor-browser

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

JSActorManager.cpp (8849B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 "mozilla/dom/JSActorManager.h"
      8 
      9 #include "js/CallAndConstruct.h"    // JS::Construct
     10 #include "js/PropertyAndElement.h"  // JS_GetProperty
     11 #include "jsapi.h"
     12 #include "mozJSModuleLoader.h"
     13 #include "mozilla/AppShutdown.h"
     14 #include "mozilla/CycleCollectedJSRuntime.h"
     15 #include "mozilla/ScopeExit.h"
     16 #include "mozilla/dom/AutoEntryScript.h"
     17 #include "mozilla/dom/JSActorService.h"
     18 #include "mozilla/dom/JSIPCValue.h"
     19 #include "mozilla/dom/JSIPCValueUtils.h"
     20 #include "mozilla/dom/JSProcessActorProtocol.h"
     21 #include "mozilla/dom/JSWindowActorProtocol.h"
     22 #include "mozilla/dom/MessagePort.h"
     23 #include "mozilla/dom/PWindowGlobal.h"
     24 #include "mozilla/ipc/ProtocolUtils.h"
     25 #include "nsContentUtils.h"
     26 
     27 namespace mozilla::dom {
     28 
     29 already_AddRefed<JSActor> JSActorManager::GetActor(JSContext* aCx,
     30                                                   const nsACString& aName,
     31                                                   ErrorResult& aRv) {
     32  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
     33 
     34  // If our connection has been closed, return an error.
     35  mozilla::ipc::IProtocol* nativeActor = AsNativeActor();
     36  if (!nativeActor->CanSend()) {
     37    aRv.ThrowInvalidStateError(nsPrintfCString(
     38        "Cannot get actor '%s'. Native '%s' actor is destroyed.",
     39        PromiseFlatCString(aName).get(), nativeActor->GetProtocolName()));
     40    return nullptr;
     41  }
     42 
     43  // Check if this actor has already been created, and return it if it has.
     44  if (RefPtr<JSActor> actor = mJSActors.Get(aName)) {
     45    return actor.forget();
     46  }
     47 
     48  RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
     49  if (!actorSvc) {
     50    aRv.ThrowInvalidStateError("JSActorService hasn't been initialized");
     51    return nullptr;
     52  }
     53 
     54  // Check if this actor satisfies the requirements of the protocol
     55  // corresponding to `aName`, and get the module which implements it.
     56  RefPtr<JSActorProtocol> protocol =
     57      MatchingJSActorProtocol(actorSvc, aName, aRv);
     58  if (!protocol) {
     59    return nullptr;
     60  }
     61 
     62  auto& side = nativeActor->GetSide() == mozilla::ipc::ParentSide
     63                   ? protocol->Parent()
     64                   : protocol->Child();
     65 
     66  // Load the module using mozJSModuleLoader.
     67  // If the JSActor uses `loadInDevToolsLoader`, force loading in the DevTools
     68  // specific's loader.
     69  RefPtr loader = protocol->mLoadInDevToolsLoader
     70                      ? mozJSModuleLoader::GetOrCreateDevToolsLoader(aCx)
     71                      : mozJSModuleLoader::Get();
     72  MOZ_ASSERT(loader);
     73 
     74  // We're about to construct the actor, so make sure we're in the loader realm
     75  // while importing etc.
     76  JSAutoRealm ar(aCx, loader->GetSharedGlobal());
     77 
     78  // If a module URI was provided, use it to construct an instance of the actor.
     79  JS::Rooted<JSObject*> actorObj(aCx);
     80  if (side.mESModuleURI) {
     81    JS::Rooted<JSObject*> exports(aCx);
     82    aRv = loader->ImportESModule(aCx, side.mESModuleURI.ref(), &exports);
     83    if (aRv.Failed()) {
     84      return nullptr;
     85    }
     86    MOZ_ASSERT(exports, "null exports!");
     87 
     88    // Load the specific property from our module.
     89    JS::Rooted<JS::Value> ctor(aCx);
     90    nsAutoCString ctorName(aName);
     91    ctorName.Append(StringFromIPCSide(nativeActor->GetSide()));
     92    if (!JS_GetProperty(aCx, exports, ctorName.get(), &ctor)) {
     93      aRv.NoteJSContextException(aCx);
     94      return nullptr;
     95    }
     96 
     97    if (NS_WARN_IF(!ctor.isObject())) {
     98      aRv.ThrowNotFoundError(nsPrintfCString(
     99          "Could not find actor constructor '%s'", ctorName.get()));
    100      return nullptr;
    101    }
    102 
    103    // Invoke the constructor loaded from the module.
    104    if (!JS::Construct(aCx, ctor, JS::HandleValueArray::empty(), &actorObj)) {
    105      aRv.NoteJSContextException(aCx);
    106      return nullptr;
    107    }
    108  }
    109 
    110  // Initialize our newly-constructed actor, and return it.
    111  RefPtr<JSActor> actor = InitJSActor(actorObj, aName, aRv);
    112  if (aRv.Failed()) {
    113    return nullptr;
    114  }
    115  mJSActors.InsertOrUpdate(aName, RefPtr{actor});
    116  return actor.forget();
    117 }
    118 
    119 already_AddRefed<JSActor> JSActorManager::GetExistingActor(
    120    const nsACString& aName) {
    121  if (!AsNativeActor()->CanSend()) {
    122    return nullptr;
    123  }
    124  return mJSActors.Get(aName);
    125 }
    126 
    127 #define CHILD_DIAGNOSTIC_ASSERT(test, msg) \
    128  do {                                     \
    129    if (XRE_IsParentProcess()) {           \
    130      MOZ_ASSERT(test, msg);               \
    131    } else {                               \
    132      MOZ_DIAGNOSTIC_ASSERT(test, msg);    \
    133    }                                      \
    134  } while (0)
    135 
    136 void JSActorManager::ReceiveRawMessage(
    137    const JSActorMessageMeta& aMetadata, JSIPCValue&& aData,
    138    UniquePtr<ipc::StructuredCloneData> aStack) {
    139  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
    140 
    141  CrashReporter::AutoRecordAnnotation autoActorName(
    142      CrashReporter::Annotation::JSActorName, aMetadata.actorName());
    143  CrashReporter::AutoRecordAnnotation autoMessageName(
    144      CrashReporter::Annotation::JSActorMessage,
    145      NS_LossyConvertUTF16toASCII(aMetadata.messageName()));
    146 
    147  // We're going to be running JS. Enter the privileged junk realm so we can set
    148  // up our JS state correctly.
    149  AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSActor message handler");
    150  JSContext* cx = aes.cx();
    151 
    152  // Ensure any errors reported to `error` are set on the scope, so they're
    153  // reported.
    154  ErrorResult error;
    155  auto autoSetException =
    156      MakeScopeExit([&] { (void)error.MaybeSetPendingException(cx); });
    157 
    158  // If an async stack was provided, set up our async stack state.
    159  JS::Rooted<JSObject*> stack(cx);
    160  Maybe<JS::AutoSetAsyncStackForNewCalls> stackSetter;
    161  {
    162    JS::Rooted<JS::Value> stackVal(cx);
    163    if (aStack) {
    164      aStack->Read(cx, &stackVal, error);
    165      if (error.Failed()) {
    166        error.SuppressException();
    167        JS_ClearPendingException(cx);
    168        stackVal.setUndefined();
    169      }
    170    }
    171 
    172    if (stackVal.isObject()) {
    173      stack = &stackVal.toObject();
    174      if (!js::IsSavedFrame(stack)) {
    175        CHILD_DIAGNOSTIC_ASSERT(false, "Stack must be a SavedFrame object");
    176        error.ThrowDataError("Actor async stack must be a SavedFrame object");
    177        return;
    178      }
    179      stackSetter.emplace(cx, stack, "JSActor query");
    180    }
    181  }
    182 
    183  RefPtr<JSActor> actor = GetActor(cx, aMetadata.actorName(), error);
    184  if (error.Failed()) {
    185    return;
    186  }
    187 
    188 #ifdef DEBUG
    189  {
    190    RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
    191    RefPtr windowProtocol(
    192        actorSvc->GetJSWindowActorProtocol(aMetadata.actorName()));
    193    RefPtr processProtocol(
    194        actorSvc->GetJSProcessActorProtocol(aMetadata.actorName()));
    195    MOZ_ASSERT(windowProtocol || processProtocol,
    196               "The protocol of this actor should exist");
    197  }
    198 #endif  // DEBUG
    199 
    200  JS::Rooted<JS::Value> data(cx);
    201  JSIPCValueUtils::ToJSVal(cx, std::move(aData), &data, error);
    202  if (error.Failed()) {
    203    CHILD_DIAGNOSTIC_ASSERT(CycleCollectedJSRuntime::Get()->OOMReported(),
    204                            "Should not receive non-decodable data");
    205    return;
    206  }
    207 
    208  switch (aMetadata.kind()) {
    209    case JSActorMessageKind::QueryResolve:
    210    case JSActorMessageKind::QueryReject:
    211      actor->ReceiveQueryReply(cx, aMetadata, data, error);
    212      break;
    213 
    214    case JSActorMessageKind::Message:
    215      actor->ReceiveMessage(cx, aMetadata, data, error);
    216      break;
    217 
    218    case JSActorMessageKind::Query:
    219      actor->ReceiveQuery(cx, aMetadata, data, error);
    220      break;
    221 
    222    default:
    223      MOZ_ASSERT_UNREACHABLE();
    224  }
    225 }
    226 
    227 void JSActorManager::JSActorWillDestroy() {
    228  for (const auto& entry : mJSActors.Values()) {
    229    entry->StartDestroy();
    230  }
    231 }
    232 
    233 void JSActorManager::JSActorDidDestroy() {
    234  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
    235  CrashReporter::AutoRecordAnnotation autoMessageName(
    236      CrashReporter::Annotation::JSActorMessage, "<DidDestroy>"_ns);
    237 
    238  // Swap the table with `mJSActors` so that we don't invalidate it while
    239  // iterating.
    240  const nsRefPtrHashtable<nsCStringHashKey, JSActor> actors =
    241      std::move(mJSActors);
    242  for (const auto& entry : actors.Values()) {
    243    CrashReporter::AutoRecordAnnotation autoActorName(
    244        CrashReporter::Annotation::JSActorName, entry->Name());
    245    // Do not risk to run script very late in shutdown
    246    if (!AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
    247      entry->AfterDestroy();
    248    }
    249  }
    250 }
    251 
    252 void JSActorManager::JSActorUnregister(const nsACString& aName) {
    253  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
    254 
    255  RefPtr<JSActor> actor;
    256  if (mJSActors.Remove(aName, getter_AddRefs(actor))) {
    257    actor->AfterDestroy();
    258  }
    259 }
    260 
    261 }  // namespace mozilla::dom