Add same-thread stream transfer support

This commit is contained in:
Basilisk-Dev 2026-05-08 21:43:14 -04:00 committed by wuggy
commit 58e0e6a192
3 changed files with 313 additions and 0 deletions

View file

@ -138,6 +138,120 @@ StructuredCloneCallbacksError(JSContext* aCx,
NS_WARNING("Failed to clone data.");
}
struct SameThreadStreamTransferData
{
SameThreadStreamTransferData(JSContext* aCx, JS::HandleObject aRecord)
: mRecord(aCx, aRecord)
{}
~SameThreadStreamTransferData()
{
mRecord.reset();
}
JS::PersistentRootedObject mRecord;
};
bool
CallStreamTransferHelper(JSContext* aCx,
const char* aHelperName,
JS::HandleValue aArgument,
JS::MutableHandleValue aResult)
{
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
if (!global) {
return false;
}
// Resolve the stream extras lazily before looking up the internal helper.
JS::Rooted<JS::Value> ignored(aCx);
if (!JS_GetProperty(aCx, global, "WritableStream", &ignored)) {
return false;
}
JS::Rooted<JS::Value> helper(aCx);
if (!JS_GetProperty(aCx, global, aHelperName, &helper)) {
return false;
}
if (!helper.isObject() || !JS::IsCallable(&helper.toObject())) {
aResult.setUndefined();
return true;
}
JS::Rooted<JS::Value> argument(aCx, aArgument);
if (!JS_WrapValue(aCx, &argument)) {
return false;
}
return JS::Call(aCx, JS::UndefinedHandleValue, helper,
JS::HandleValueArray(argument), aResult);
}
bool
TryWriteSameThreadStreamTransfer(JSContext* aCx,
JS::Handle<JSObject*> aObj,
const char* aHelperName,
uint32_t aTransferTag,
uint32_t* aTag,
JS::TransferableOwnership* aOwnership,
void** aContent,
uint64_t* aExtraData,
bool* aHandled)
{
*aHandled = false;
JS::Rooted<JS::Value> argument(aCx, JS::ObjectValue(*aObj));
JS::Rooted<JS::Value> transferRecord(aCx);
if (!CallStreamTransferHelper(aCx, aHelperName, argument, &transferRecord)) {
return false;
}
if (transferRecord.isUndefined()) {
return true;
}
if (!transferRecord.isObject()) {
return false;
}
JS::Rooted<JSObject*> record(aCx, &transferRecord.toObject());
SameThreadStreamTransferData* data =
new SameThreadStreamTransferData(aCx, record);
*aTag = aTransferTag;
*aOwnership = JS::SCTAG_TMO_CUSTOM;
*aContent = data;
*aExtraData = 0;
*aHandled = true;
return true;
}
bool
ReadSameThreadStreamTransfer(JSContext* aCx,
void* aContent,
const char* aHelperName,
JS::MutableHandleObject aReturnObject)
{
MOZ_ASSERT(aContent);
SameThreadStreamTransferData* data =
static_cast<SameThreadStreamTransferData*>(aContent);
JS::Rooted<JS::Value> record(aCx, JS::ObjectValue(*data->mRecord));
JS::Rooted<JS::Value> result(aCx);
if (!CallStreamTransferHelper(aCx, aHelperName, record, &result)) {
return false;
}
if (!result.isObject()) {
return false;
}
aReturnObject.set(&result.toObject());
delete data;
return true;
}
} // anonymous namespace
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
@ -1375,6 +1489,20 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
return true;
}
if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread) {
if (aTag == SCTAG_DOM_TRANSFERRED_WRITABLESTREAM) {
return ReadSameThreadStreamTransfer(aCx, aContent,
"__uxpReceiveWritableStreamTransfer",
aReturnObject);
}
if (aTag == SCTAG_DOM_TRANSFERRED_READABLESTREAM) {
return ReadSameThreadStreamTransfer(aCx, aContent,
"__uxpReceiveReadableStreamTransfer",
aReturnObject);
}
}
return false;
}
@ -1443,6 +1571,31 @@ StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
}
}
if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread) {
bool handled = false;
if (!TryWriteSameThreadStreamTransfer(aCx, obj,
"__uxpTransferWritableStream",
SCTAG_DOM_TRANSFERRED_WRITABLESTREAM,
aTag, aOwnership, aContent,
aExtraData, &handled)) {
return false;
}
if (handled) {
return true;
}
if (!TryWriteSameThreadStreamTransfer(aCx, obj,
"__uxpTransferReadableStream",
SCTAG_DOM_TRANSFERRED_READABLESTREAM,
aTag, aOwnership, aContent,
aExtraData, &handled)) {
return false;
}
if (handled) {
return true;
}
}
return false;
}
@ -1480,6 +1633,16 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
delete data;
return;
}
if ((aTag == SCTAG_DOM_TRANSFERRED_WRITABLESTREAM ||
aTag == SCTAG_DOM_TRANSFERRED_READABLESTREAM) &&
mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread) {
MOZ_ASSERT(aContent);
SameThreadStreamTransferData* data =
static_cast<SameThreadStreamTransferData*>(aContent);
delete data;
return;
}
}
bool