diff options
Diffstat (limited to 'extensions/broadcast_channel/01_broadcast_channel.js')
-rw-r--r-- | extensions/broadcast_channel/01_broadcast_channel.js | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/extensions/broadcast_channel/01_broadcast_channel.js b/extensions/broadcast_channel/01_broadcast_channel.js new file mode 100644 index 000000000..34f8b9e19 --- /dev/null +++ b/extensions/broadcast_channel/01_broadcast_channel.js @@ -0,0 +1,117 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +"use strict"; + +((window) => { + const core = window.Deno.core; + const webidl = window.__bootstrap.webidl; + + const handlerSymbol = Symbol("eventHandlers"); + function makeWrappedHandler(handler) { + function wrappedHandler(...args) { + if (typeof wrappedHandler.handler !== "function") { + return; + } + return wrappedHandler.handler.call(this, ...args); + } + wrappedHandler.handler = handler; + return wrappedHandler; + } + // TODO(lucacasonato) reuse when we can reuse code between web crates + function defineEventHandler(emitter, name) { + // HTML specification section 8.1.5.1 + Object.defineProperty(emitter, `on${name}`, { + get() { + return this[handlerSymbol]?.get(name)?.handler; + }, + set(value) { + if (!this[handlerSymbol]) { + this[handlerSymbol] = new Map(); + } + let handlerWrapper = this[handlerSymbol]?.get(name); + if (handlerWrapper) { + handlerWrapper.handler = value; + } else { + handlerWrapper = makeWrappedHandler(value); + this.addEventListener(name, handlerWrapper); + } + this[handlerSymbol].set(name, handlerWrapper); + }, + configurable: true, + enumerable: true, + }); + } + + const _name = Symbol("[[name]]"); + const _closed = Symbol("[[closed]]"); + const _rid = Symbol("[[rid]]"); + + class BroadcastChannel extends EventTarget { + [_name]; + [_closed] = false; + [_rid]; + + get name() { + return this[_name]; + } + + constructor(name) { + super(); + + window.location; + + const prefix = "Failed to construct 'broadcastChannel'"; + webidl.requiredArguments(arguments.length, 1, { prefix }); + + this[_name] = webidl.converters["DOMString"](name, { + prefix, + context: "Argument 1", + }); + + this[_rid] = core.opSync("op_broadcast_open", this[_name]); + + this[webidl.brand] = webidl.brand; + + this.#eventLoop(); + } + + postMessage(message) { + webidl.assertBranded(this, BroadcastChannel); + + if (this[_closed]) { + throw new DOMException("Already closed", "InvalidStateError"); + } + + core.opAsync("op_broadcast_send", this[_rid], core.serialize(message)); + } + + close() { + webidl.assertBranded(this, BroadcastChannel); + + this[_closed] = true; + core.close(this[_rid]); + } + + async #eventLoop() { + while (!this[_closed]) { + const message = await core.opAsync( + "op_broadcast_next_event", + this[_rid], + ); + + if (message.length !== 0) { + const event = new MessageEvent("message", { + data: core.deserialize(message), + origin: window.location, + }); + event.target = this; + this.dispatchEvent(event); + } + } + } + } + + defineEventHandler(BroadcastChannel.prototype, "message"); + defineEventHandler(BroadcastChannel.prototype, "messageerror"); + + window.__bootstrap.broadcastChannel = { BroadcastChannel }; +})(this); |