summaryrefslogtreecommitdiff
path: root/ext/web/13_message_port.js
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2021-08-25 13:48:53 +0200
committerGitHub <noreply@github.com>2021-08-25 13:48:53 +0200
commit5d814a4c244d489b4ae51002a0cf1d3c2fe16058 (patch)
tree3597ab7d4c438b96c34dac52f9f537989129001e /ext/web/13_message_port.js
parentf84cd9403db3545c8058a9c28474b3c99d4c2dd4 (diff)
feat: ArrayBuffer in structured clone transfer (#11840)
Diffstat (limited to 'ext/web/13_message_port.js')
-rw-r--r--ext/web/13_message_port.js73
1 files changed, 65 insertions, 8 deletions
diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js
index d0d9e160e..2768c0a92 100644
--- a/ext/web/13_message_port.js
+++ b/ext/web/13_message_port.js
@@ -15,6 +15,8 @@
const { defineEventHandler } = window.__bootstrap.event;
const { DOMException } = window.__bootstrap.domException;
const {
+ ArrayBuffer,
+ ArrayPrototypeFilter,
ArrayPrototypeIncludes,
ArrayPrototypePush,
ObjectSetPrototypeOf,
@@ -22,6 +24,9 @@
SymbolFor,
SymbolToStringTag,
TypeError,
+ WeakSet,
+ WeakSetPrototypeAdd,
+ WeakSetPrototypeHas,
} = window.__bootstrap.primordials;
class MessageChannel {
@@ -139,11 +144,11 @@
this[_id],
);
if (data === null) break;
- let message, transfer;
+ let message, transferables;
try {
const v = deserializeJsMessageData(data);
message = v[0];
- transfer = v[1];
+ transferables = v[1];
} catch (err) {
const event = new MessageEvent("messageerror", { data: err });
this.dispatchEvent(event);
@@ -151,7 +156,10 @@
}
const event = new MessageEvent("message", {
data: message,
- ports: transfer,
+ ports: ArrayPrototypeFilter(
+ transferables,
+ (t) => t instanceof MessagePort,
+ ),
});
this.dispatchEvent(event);
}
@@ -193,12 +201,22 @@
function deserializeJsMessageData(messageData) {
/** @type {object[]} */
const transferables = [];
+ const hostObjects = [];
+ const arrayBufferIdsInTransferables = [];
+ const transferedArrayBuffers = [];
for (const transferable of messageData.transferables) {
switch (transferable.kind) {
case "messagePort": {
const port = createMessagePort(transferable.data);
ArrayPrototypePush(transferables, port);
+ ArrayPrototypePush(hostObjects, port);
+ break;
+ }
+ case "arrayBuffer": {
+ ArrayPrototypePush(transferedArrayBuffers, transferable.data);
+ const i = ArrayPrototypePush(transferables, null);
+ ArrayPrototypePush(arrayBufferIdsInTransferables, i);
break;
}
default:
@@ -207,21 +225,53 @@
}
const data = core.deserialize(messageData.data, {
- hostObjects: transferables,
+ hostObjects,
+ transferedArrayBuffers,
});
+ for (const i in arrayBufferIdsInTransferables) {
+ const id = arrayBufferIdsInTransferables[i];
+ transferables[id] = transferedArrayBuffers[i];
+ }
+
return [data, transferables];
}
+ const detachedArrayBuffers = new WeakSet();
+
/**
* @param {any} data
- * @param {object[]} tranferables
+ * @param {object[]} transferables
* @returns {globalThis.__bootstrap.messagePort.MessageData}
*/
- function serializeJsMessageData(data, tranferables) {
+ function serializeJsMessageData(data, transferables) {
+ const transferedArrayBuffers = ArrayPrototypeFilter(
+ transferables,
+ (a) => a instanceof ArrayBuffer,
+ );
+
+ for (const arrayBuffer of transferedArrayBuffers) {
+ // This is hacky with both false positives and false negatives for
+ // detecting detached array buffers. V8 needs to add a way to tell if a
+ // buffer is detached or not.
+ if (WeakSetPrototypeHas(detachedArrayBuffers, arrayBuffer)) {
+ throw new DOMException(
+ "Can not transfer detached ArrayBuffer",
+ "DataCloneError",
+ );
+ }
+ WeakSetPrototypeAdd(detachedArrayBuffers, arrayBuffer);
+ }
+
let serializedData;
try {
- serializedData = core.serialize(data, { hostObjects: tranferables });
+ serializedData = core.serialize(data, {
+ hostObjects: ArrayPrototypeFilter(
+ transferables,
+ (a) => a instanceof MessagePort,
+ ),
+ transferedArrayBuffers,
+ });
} catch (err) {
throw new DOMException(err.message, "DataCloneError");
}
@@ -229,7 +279,8 @@
/** @type {globalThis.__bootstrap.messagePort.Transferable[]} */
const serializedTransferables = [];
- for (const transferable of tranferables) {
+ let arrayBufferI = 0;
+ for (const transferable of transferables) {
if (transferable instanceof MessagePort) {
webidl.assertBranded(transferable, MessagePort);
const id = transferable[_id];
@@ -244,6 +295,12 @@
kind: "messagePort",
data: id,
});
+ } else if (transferable instanceof ArrayBuffer) {
+ ArrayPrototypePush(serializedTransferables, {
+ kind: "arrayBuffer",
+ data: transferedArrayBuffers[arrayBufferI],
+ });
+ arrayBufferI++;
} else {
throw new DOMException("Value not transferable", "DataCloneError");
}