1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00
denoland-deno/ext/broadcast_channel/01_broadcast_channel.js
Ricardo Iván Vieitez Parra 98403691d1
fix: call setIsTrusted for generated events (MessageEvent) (#19919)
This addresses issue #19918.

## Issue description

Event messages have the wrong isTrusted value when they are not
triggered by user interaction, which differs from the browser. In
particular, all MessageEvents created by Deno have isTrusted set to
false, even though it should be true.

This is my first ever contribution to Deno, so I might be missing
something.
2023-07-31 23:22:07 +02:00

149 lines
3.8 KiB
JavaScript

// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
/// <reference path="../../core/internal.d.ts" />
const core = globalThis.Deno.core;
const ops = core.ops;
import * as webidl from "ext:deno_webidl/00_webidl.js";
import {
defineEventHandler,
EventTarget,
setIsTrusted,
setTarget,
} from "ext:deno_web/02_event.js";
import DOMException from "ext:deno_web/01_dom_exception.js";
const primordials = globalThis.__bootstrap.primordials;
const {
ArrayPrototypeIndexOf,
ArrayPrototypeSplice,
ArrayPrototypePush,
Symbol,
Uint8Array,
} = primordials;
const _name = Symbol("[[name]]");
const _closed = Symbol("[[closed]]");
const channels = [];
let rid = null;
async function recv() {
while (channels.length > 0) {
const message = await core.opAsync("op_broadcast_recv", rid);
if (message === null) {
break;
}
const { 0: name, 1: data } = message;
dispatch(null, name, new Uint8Array(data));
}
core.close(rid);
rid = null;
}
function dispatch(source, name, data) {
for (let i = 0; i < channels.length; ++i) {
const channel = channels[i];
if (channel === source) continue; // Don't self-send.
if (channel[_name] !== name) continue;
if (channel[_closed]) continue;
const go = () => {
if (channel[_closed]) return;
const event = new MessageEvent("message", {
data: core.deserialize(data), // TODO(bnoordhuis) Cache immutables.
origin: "http://127.0.0.1",
});
setIsTrusted(event, true);
setTarget(event, channel);
channel.dispatchEvent(event);
};
defer(go);
}
}
// Defer to avoid starving the event loop. Not using queueMicrotask()
// for that reason: it lets promises make forward progress but can
// still starve other parts of the event loop.
function defer(go) {
setTimeout(go, 1);
}
class BroadcastChannel extends EventTarget {
[_name];
[_closed] = false;
get name() {
return this[_name];
}
constructor(name) {
super();
const prefix = "Failed to construct 'BroadcastChannel'";
webidl.requiredArguments(arguments.length, 1, prefix);
this[_name] = webidl.converters["DOMString"](name, prefix, "Argument 1");
this[webidl.brand] = webidl.brand;
ArrayPrototypePush(channels, this);
if (rid === null) {
// Create the rid immediately, otherwise there is a time window (and a
// race condition) where messages can get lost, because recv() is async.
rid = ops.op_broadcast_subscribe();
recv();
}
}
postMessage(message) {
webidl.assertBranded(this, BroadcastChannelPrototype);
const prefix = "Failed to execute 'postMessage' on 'BroadcastChannel'";
webidl.requiredArguments(arguments.length, 1, prefix);
if (this[_closed]) {
throw new DOMException("Already closed", "InvalidStateError");
}
if (typeof message === "function" || typeof message === "symbol") {
throw new DOMException("Uncloneable value", "DataCloneError");
}
const data = core.serialize(message);
// Send to other listeners in this VM.
dispatch(this, this[_name], new Uint8Array(data));
// Send to listeners in other VMs.
defer(() => {
if (!this[_closed]) {
core.opAsync("op_broadcast_send", rid, this[_name], data);
}
});
}
close() {
webidl.assertBranded(this, BroadcastChannelPrototype);
this[_closed] = true;
const index = ArrayPrototypeIndexOf(channels, this);
if (index === -1) return;
ArrayPrototypeSplice(channels, index, 1);
if (channels.length === 0) {
ops.op_broadcast_unsubscribe(rid);
}
}
}
defineEventHandler(BroadcastChannel.prototype, "message");
defineEventHandler(BroadcastChannel.prototype, "messageerror");
const BroadcastChannelPrototype = BroadcastChannel.prototype;
export { BroadcastChannel };