Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/ipc/jsactor/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 18 kB image not shown  

Quelle  JSActor.cpp

  Sprache: C
 

/* 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 "mozilla/dom/JSActor.h"

#include "chrome/common/ipc_channel.h"
#include "js/Promise.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/dom/AutoEntryScript.h"
#include "mozilla/dom/ClonedErrorHolder.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/JSActorBinding.h"
#include "mozilla/dom/JSActorManager.h"
#include "mozilla/dom/JSIPCValue.h"
#include "mozilla/dom/JSIPCValueUtils.h"
#include "mozilla/dom/MessageManagerBinding.h"
#include "mozilla/dom/PWindowGlobal.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "nsFrameMessageManager.h"
#include "nsICrashReporter.h"
#include "xpcprivate.h"

namespace mozilla::dom {

struct JSActorMessageMarker {
  static constexpr Span<const char> MarkerTypeName() {
    return MakeStringSpan("JSActorMessage");
  }
  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
                                   const ProfilerString8View& aActorName,
                                   const ProfilerString16View& aMessageName) {
    aWriter.StringProperty("actor", aActorName);
    aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(aMessageName));
  }
  static MarkerSchema MarkerTypeDisplay() {
    using MS = MarkerSchema;
    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    schema.AddKeyLabelFormat("actor""Actor Name", MS::Format::String);
    schema.AddKeyLabelFormat("name""Message Name", MS::Format::String);
    schema.SetTooltipLabel("JSActor - {marker.name}");
    schema.SetTableLabel("[{marker.data.actor}] {marker.data.name}");
    return schema;
  }
};

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSActor)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(JSActor)
NS_IMPL_CYCLE_COLLECTING_RELEASE(JSActor)

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(JSActor)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSActor)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWrappedJS)
  tmp->mPendingQueries.Clear();
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSActor)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWrappedJS)
  for (const auto& query : tmp->mPendingQueries.Values()) {
    CycleCollectionNoteChild(cb, query.mPromise.get(), "Pending Query Promise");
  }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

JSActor::JSActor(nsISupports* aGlobal) {
  mGlobal = do_QueryInterface(aGlobal);
  if (!mGlobal) {
    mGlobal = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
  }
}

void JSActor::StartDestroy() { mCanSend = false; }

void JSActor::AfterDestroy() {
  mCanSend = false;

  // Take our queries out, in case somehow rejecting promises can trigger
  // additions or removals.
  const nsTHashMap<nsUint64HashKey, PendingQuery> pendingQueries =
      std::move(mPendingQueries);
  for (const auto& entry : pendingQueries.Values()) {
    nsPrintfCString message(
        "Actor '%s' destroyed before query '%s' was resolved", mName.get(),
        NS_LossyConvertUTF16toASCII(entry.mMessageName).get());
    entry.mPromise->MaybeRejectWithAbortError(message);
  }

  InvokeCallback(CallbackFunction::DidDestroy);
  ClearManager();
}

void JSActor::InvokeCallback(CallbackFunction callback) {
  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());

  AutoEntryScript aes(GetParentObject(), "JSActor destroy callback");
  JSContext* cx = aes.cx();
  MozJSActorCallbacks callbacksHolder;
  JS::Rooted<JS::Value> val(cx, JS::ObjectOrNullValue(GetWrapper()));
  if (NS_WARN_IF(!callbacksHolder.Init(cx, val))) {
    return;
  }

  // Destroy callback is optional.
  if (callback == CallbackFunction::DidDestroy) {
    if (callbacksHolder.mDidDestroy.WasPassed()) {
      callbacksHolder.mDidDestroy.Value()->Call(this);
    }
  } else {
    if (callbacksHolder.mActorCreated.WasPassed()) {
      callbacksHolder.mActorCreated.Value()->Call(this);
    }
  }
}

nsresult JSActor::QueryInterfaceActor(const nsIID& aIID, void** aPtr) {
  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
  if (!GetWrapperPreserveColor()) {
    // If we have no preserved wrapper, we won't implement any interfaces.
    return NS_NOINTERFACE;
  }

  if (!mWrappedJS) {
    AutoEntryScript aes(GetParentObject(), "JSActor query interface");
    JSContext* cx = aes.cx();

    JS::Rooted<JSObject*> self(cx, GetWrapper());
    JSAutoRealm ar(cx, self);

    RefPtr<nsXPCWrappedJS> wrappedJS;
    nsresult rv = nsXPCWrappedJS::GetNewOrUsed(
        cx, self, NS_GET_IID(nsISupports), getter_AddRefs(wrappedJS));
    NS_ENSURE_SUCCESS(rv, rv);

    mWrappedJS = do_QueryInterface(wrappedJS);
    MOZ_ASSERT(mWrappedJS);
  }

  return mWrappedJS->QueryInterface(aIID, aPtr);
}

void JSActor::Init(const nsACString& aName, bool aSendTyped) {
  MOZ_ASSERT(mName.IsEmpty(), "Cannot set name twice!");
  mName = aName;
  mSendTyped = aSendTyped;
  InvokeCallback(CallbackFunction::ActorCreated);
}

void JSActor::ThrowStateErrorForGetter(const char* aName,
                                       ErrorResult& aRv) const {
  if (mName.IsEmpty()) {
    aRv.ThrowInvalidStateError(nsPrintfCString(
        "Cannot access property '%s' before actor is initialized", aName));
  } else {
    aRv.ThrowInvalidStateError(nsPrintfCString(
        "Cannot access property '%s' after actor '%s' has been destroyed",
        aName, mName.get()));
  }
}

static RefPtr<ipc::StructuredCloneData> TryClone(JSContext* aCx,
                                                 JS::Handle<JS::Value> aValue) {
  auto data = MakeRefPtr<ipc::StructuredCloneData>(
      JS::StructuredCloneScope::DifferentProcess,
      StructuredCloneHolder::TransferringNotSupported);

  // Try to directly serialize the passed-in data, and return it to our caller.
  IgnoredErrorResult rv;
  data->Write(aCx, aValue, rv);
  if (rv.Failed()) {
    // Serialization failed, return null instead.
    JS_ClearPendingException(aCx);
    data = nullptr;
  }
  return data;
}

static RefPtr<ipc::StructuredCloneData> CloneJSStack(
    JSContext* aCx, JS::Handle<JSObject*> aStack) {
  JS::Rooted<JS::Value> stackVal(aCx, JS::ObjectOrNullValue(aStack));
  return TryClone(aCx, stackVal);
}

static RefPtr<ipc::StructuredCloneData> CaptureJSStack(JSContext* aCx) {
  JS::Rooted<JSObject*> stack(aCx, nullptr);
  if (JS::IsAsyncStackCaptureEnabledForRealm(aCx) &&
      !JS::CaptureCurrentStack(aCx, &stack)) {
    JS_ClearPendingException(aCx);
  }

  return CloneJSStack(aCx, stack);
}

void JSActor::SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
                               JS::Handle<JS::Value> aObj,
                               JS::Handle<JS::Value> aTransfers,
                               ErrorResult& aRv) {
  profiler_add_marker("SendAsyncMessage", geckoprofiler::category::IPC, {},
                      JSActorMessageMarker{}, mName, aMessageName);
  JSIPCValueUtils::Context cx(aCx, /* aStrict = */ false);
  IgnoredErrorResult error;
  auto data =
      JSIPCValueUtils::FromJSVal(cx, aObj, aTransfers, mSendTyped, error);
  if (error.Failed()) {
    aRv.ThrowDataCloneError(nsPrintfCString(
        "Failed to serialize message '%s::%s'",
        NS_LossyConvertUTF16toASCII(aMessageName).get(), mName.get()));
    return;
  }

  JSActorMessageMeta meta;
  meta.actorName() = mName;
  meta.messageName() = aMessageName;
  meta.kind() = JSActorMessageKind::Message;

  auto stack = CaptureJSStack(aCx);

  SendRawMessage(meta, std::move(data), stack, aRv);
}

already_AddRefed<Promise> JSActor::SendQuery(JSContext* aCx,
                                             const nsAString& aMessageName,
                                             JS::Handle<JS::Value> aObj,
                                             ErrorResult& aRv) {
  profiler_add_marker("SendQuery", geckoprofiler::category::IPC, {},
                      JSActorMessageMarker{}, mName, aMessageName);
  JSIPCValueUtils::Context cx(aCx, /* aStrict = */ false);
  IgnoredErrorResult error;
  auto data = JSIPCValueUtils::FromJSVal(cx, aObj, mSendTyped, error);
  if (error.Failed()) {
    aRv.ThrowDataCloneError(nsPrintfCString(
        "Failed to serialize message '%s::%s'",
        NS_LossyConvertUTF16toASCII(aMessageName).get(), mName.get()));
    return nullptr;
  }

  nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
  if (NS_WARN_IF(!global)) {
    aRv.ThrowUnknownError("Unable to get current native global");
    return nullptr;
  }

  RefPtr<Promise> promise = Promise::Create(global, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  JSActorMessageMeta meta;
  meta.actorName() = mName;
  meta.messageName() = aMessageName;
  meta.queryId() = mNextQueryId++;
  meta.kind() = JSActorMessageKind::Query;

  auto stack = CaptureJSStack(aCx);

  SendRawMessage(meta, std::move(data), stack, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  mPendingQueries.InsertOrUpdate(meta.queryId(),
                                 PendingQuery{promise, meta.messageName()});

  return promise.forget();
}

void JSActor::CallReceiveMessage(JSContext* aCx,
                                 const JSActorMessageMeta& aMetadata,
                                 JS::Handle<JS::Value> aData,
                                 JS::MutableHandle<JS::Value> aRetVal,
                                 ErrorResult& aRv) {
  // The argument which we want to pass to IPC.
  RootedDictionary<ReceiveMessageArgument> argument(aCx);
  argument.mTarget = this;
  argument.mName = aMetadata.messageName();
  argument.mData = aData;
  argument.mJson = aData;
  argument.mSync = false;

  if (GetWrapperPreserveColor()) {
    // Invoke the actual callback.
    JS::Rooted<JSObject*> global(aCx, JS::GetNonCCWObjectGlobal(GetWrapper()));
    RefPtr<MessageListener> messageListener =
        new MessageListener(GetWrapper(), global, nullptr, nullptr);
    messageListener->ReceiveMessage(argument, aRetVal, aRv,
                                    "JSActor receive message",
                                    MessageListener::eRethrowExceptions);
  } else {
    aRv.ThrowTypeError<MSG_NOT_CALLABLE>("Property 'receiveMessage'");
  }
}

void JSActor::ReceiveMessage(JSContext* aCx,
                             const JSActorMessageMeta& aMetadata,
                             JS::Handle<JS::Value> aData, ErrorResult& aRv) {
  MOZ_ASSERT(aMetadata.kind() == JSActorMessageKind::Message);
  profiler_add_marker("ReceiveMessage", geckoprofiler::category::IPC, {},
                      JSActorMessageMarker{}, mName, aMetadata.messageName());

  JS::Rooted<JS::Value> retval(aCx);
  CallReceiveMessage(aCx, aMetadata, aData, &retval, aRv);
}

void JSActor::ReceiveQuery(JSContext* aCx, const JSActorMessageMeta& aMetadata,
                           JS::Handle<JS::Value> aData, ErrorResult& aRv) {
  MOZ_ASSERT(aMetadata.kind() == JSActorMessageKind::Query);
  profiler_add_marker("ReceiveQuery", geckoprofiler::category::IPC, {},
                      JSActorMessageMarker{}, mName, aMetadata.messageName());

  // This promise will be resolved or rejected once the listener has been
  // called. Our listener on this promise will then send the reply.
  RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return;
  }

  RefPtr<QueryHandler> handler = new QueryHandler(this, aMetadata, promise);
  promise->AppendNativeHandler(handler);

  ErrorResult error;
  JS::Rooted<JS::Value> retval(aCx);
  CallReceiveMessage(aCx, aMetadata, aData, &retval, error);

  // If we have a promise, resolve or reject it respectively.
  if (error.Failed()) {
    if (error.IsUncatchableException()) {
      promise->MaybeRejectWithTimeoutError(
          "Message handler threw uncatchable exception");
    } else {
      promise->MaybeReject(std::move(error));
    }
  } else {
    promise->MaybeResolve(retval);
  }
  error.SuppressException();
}

void JSActor::ReceiveQueryReply(JSContext* aCx,
                                const JSActorMessageMeta& aMetadata,
                                JS::Handle<JS::Value> aData, ErrorResult& aRv) {
  if (NS_WARN_IF(aMetadata.actorName() != mName)) {
    aRv.ThrowUnknownError("Mismatched actor name for query reply");
    return;
  }

  Maybe<PendingQuery> query = mPendingQueries.Extract(aMetadata.queryId());
  if (NS_WARN_IF(!query)) {
    aRv.ThrowUnknownError("Received reply for non-pending query");
    return;
  }

  profiler_add_marker("ReceiveQueryReply", geckoprofiler::category::IPC, {},
                      JSActorMessageMarker{}, mName, aMetadata.messageName());

  Promise* promise = query->mPromise;
  JSAutoRealm ar(aCx, promise->PromiseObj());
  JS::RootedValue data(aCx, aData);
  if (NS_WARN_IF(!JS_WrapValue(aCx, &data))) {
    aRv.NoteJSContextException(aCx);
    return;
  }

  if (aMetadata.kind() == JSActorMessageKind::QueryResolve) {
    promise->MaybeResolve(data);
  } else {
    promise->MaybeReject(data);
  }
}

void JSActor::SendRawMessageInProcess(const JSActorMessageMeta& aMeta,
                                      JSIPCValue&& aData,
                                      ipc::StructuredCloneData* aStack,
                                      OtherSideCallback&& aGetOtherSide) {
  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
  NS_DispatchToMainThread(NS_NewRunnableFunction(
      "JSActor Async Message",
      [aMeta, data{std::move(aData)}, stack = RefPtr{aStack},
       getOtherSide{std::move(aGetOtherSide)}]() mutable {
        if (RefPtr<JSActorManager> otherSide = getOtherSide()) {
          otherSide->ReceiveRawMessage(aMeta, std::move(data), stack);
        }
      }));
}

// Native handler for our generated promise which is used to handle Queries and
// send the reply when their promises have been resolved.
JSActor::QueryHandler::QueryHandler(JSActor* aActor,
                                    const JSActorMessageMeta& aMetadata,
                                    Promise* aPromise)
    : mActor(aActor),
      mPromise(aPromise),
      mMessageName(aMetadata.messageName()),
      mQueryId(aMetadata.queryId()) {}

void JSActor::QueryHandler::RejectedCallback(JSContext* aCx,
                                             JS::Handle<JS::Value> aValue,
                                             ErrorResult& aRv) {
  if (!mActor) {
    // Make sure that this rejection is reported. See comment below.
    if (!JS::CallOriginalPromiseReject(aCx, aValue)) {
      JS_ClearPendingException(aCx);
    }
    return;
  }

  JS::Rooted<JS::Value> value(aCx, aValue);
  if (value.isObject()) {
    JS::Rooted<JSObject*> error(aCx, &value.toObject());
    if (UniquePtr<ClonedErrorHolder> ceh =
            ClonedErrorHolder::Create(aCx, error, IgnoreErrors())) {
      if (!ToJSValue(aCx, std::move(ceh), &value)) {
        JS_ClearPendingException(aCx);
      }
    } else {
      JS_ClearPendingException(aCx);
    }
  }

  // The only valid type a QueryReject message can have is "any", so serialize
  // it as an untyped value, and never log anything for it. Ideally, we would
  // require that this is an error object (bug 1907175).
  JSIPCValueUtils::Context cx(aCx);
  IgnoredErrorResult error;
  auto data =
      JSIPCValueUtils::FromJSVal(cx, value, /* aSendTyped = */ false, error);

  if (error.Failed()) {
    // Failed to clone the rejection value. Make sure that this
    // rejection is reported, despite being "handled". This is done by
    // creating a new promise in the rejected state, and throwing it
    // away. This will be reported as an unhandled rejected promise.
    if (!JS::CallOriginalPromiseReject(aCx, aValue)) {
      JS_ClearPendingException(aCx);
    }

    // Unlike other cases, we want to send a reject reply message even if
    // serialization failed, so send the JS value undefined, rather than
    // returning.
    data = JSIPCValue(void_t());
  }

  const JSActorMessageKind kind = JSActorMessageKind::QueryReject;
  SendReply(aCx, kind, std::move(data));
}

void JSActor::QueryHandler::ResolvedCallback(JSContext* aCx,
                                             JS::Handle<JS::Value> aValue,
                                             ErrorResult& aRv) {
  if (!mActor) {
    return;
  }

  JSIPCValueUtils::Context cx(aCx);
  IgnoredErrorResult error;
  auto data = JSIPCValueUtils::FromJSVal(cx, aValue, mActor->mSendTyped, error);
  if (error.Failed()) {
    nsAutoCString msg;
    msg.Append(mActor->Name());
    msg.Append(':');
    msg.Append(NS_LossyConvertUTF16toASCII(mMessageName));
    msg.AppendLiteral(": message reply cannot be cloned.");

    auto exc = MakeRefPtr<Exception>(msg, NS_ERROR_FAILURE, "DataCloneError"_ns,
                                     nullptr, nullptr);

    JS::Rooted<JS::Value> val(aCx);
    if (ToJSValue(aCx, exc, &val)) {
      RejectedCallback(aCx, val, aRv);
    } else {
      JS_ClearPendingException(aCx);
    }
    return;
  }

  const JSActorMessageKind kind = JSActorMessageKind::QueryResolve;
  SendReply(aCx, kind, std::move(data));
}

void JSActor::QueryHandler::SendReply(JSContext* aCx, JSActorMessageKind aKind,
                                      JSIPCValue&& aData) {
  MOZ_ASSERT(mActor);
  profiler_add_marker("SendQueryReply", geckoprofiler::category::IPC, {},
                      JSActorMessageMarker{}, mActor->Name(), mMessageName);

  JSActorMessageMeta meta;
  meta.actorName() = mActor->Name();
  meta.messageName() = mMessageName;
  meta.queryId() = mQueryId;
  meta.kind() = aKind;

  JS::Rooted<JSObject*> promise(aCx, mPromise->PromiseObj());
  JS::Rooted<JSObject*> jsStack(aCx, JS::GetPromiseResolutionSite(promise));

  auto stack = CloneJSStack(aCx, jsStack);

  mActor->SendRawMessage(meta, std::move(aData), stack, IgnoreErrors());
  mActor = nullptr;
  mPromise = nullptr;
}

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSActor::QueryHandler)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(JSActor::QueryHandler)
NS_IMPL_CYCLE_COLLECTING_RELEASE(JSActor::QueryHandler)

NS_IMPL_CYCLE_COLLECTION(JSActor::QueryHandler, mActor, mPromise)

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.