Skip to content

Commit

Permalink
feat(extensions): add BroadcastChannel
Browse files Browse the repository at this point in the history
Co-Authored-By: Ben Noordhuis <[email protected]>
Fixes: #10354
  • Loading branch information
bnoordhuis committed May 15, 2021
1 parent 24a2e1e commit 422a9ab
Show file tree
Hide file tree
Showing 16 changed files with 357 additions and 1 deletion.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"extensions/webgpu",
"extensions/webidl",
"extensions/websocket",
"extensions/broadcast_channel",
]
exclude = [
"std/hash/_wasm"
Expand Down
9 changes: 9 additions & 0 deletions cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::JsRuntime;
use deno_core::RuntimeOptions;
use deno_runtime::deno_broadcast_channel;
use deno_runtime::deno_console;
use deno_runtime::deno_crypto;
use deno_runtime::deno_fetch;
Expand Down Expand Up @@ -74,6 +75,10 @@ fn create_compiler_snapshot(
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
op_crate_libs.insert(
"deno.broadcast_channel",
deno_broadcast_channel::get_declaration(),
);

// ensure we invalidate the build properly.
for (_, path) in op_crate_libs.iter() {
Expand Down Expand Up @@ -300,6 +305,10 @@ fn main() {
"cargo:rustc-env=DENO_CRYPTO_LIB_PATH={}",
deno_crypto::get_declaration().display()
);
println!(
"cargo:rustc-env=DENO_BROADCAST_CHANNEL_LIB_PATH={}",
deno_broadcast_channel::get_declaration().display()
);

println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
Expand Down
3 changes: 2 additions & 1 deletion cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ fn print_cache_info(

pub fn get_types(unstable: bool) -> String {
let mut types = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
crate::tsc::DENO_NS_LIB,
crate::tsc::DENO_CONSOLE_LIB,
crate::tsc::DENO_URL_LIB,
Expand All @@ -313,6 +313,7 @@ pub fn get_types(unstable: bool) -> String {
crate::tsc::DENO_WEBSOCKET_LIB,
crate::tsc::DENO_WEBSTORAGE_LIB,
crate::tsc::DENO_CRYPTO_LIB,
crate::tsc::DENO_BROADCAST_CHANNEL_LIB,
crate::tsc::SHARED_GLOBALS_LIB,
crate::tsc::WINDOW_LIB,
);
Expand Down
2 changes: 2 additions & 0 deletions cli/tsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub static DENO_WEBSOCKET_LIB: &str =
pub static DENO_WEBSTORAGE_LIB: &str =
include_str!(env!("DENO_WEBSTORAGE_LIB_PATH"));
pub static DENO_CRYPTO_LIB: &str = include_str!(env!("DENO_CRYPTO_LIB_PATH"));
pub static DENO_BROADCAST_CHANNEL_LIB: &str =
include_str!(env!("DENO_BROADCAST_CHANNEL_LIB_PATH"));
pub static SHARED_GLOBALS_LIB: &str =
include_str!("dts/lib.deno.shared_globals.d.ts");
pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts");
Expand Down
117 changes: 117 additions & 0 deletions extensions/broadcast_channel/01_broadcast_channel.js
Original file line number Diff line number Diff line change
@@ -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);
18 changes: 18 additions & 0 deletions extensions/broadcast_channel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

[package]
name = "deno_broadcast_channel"
version = "0.1.0"
edition = "2018"
description = "Implementation of BroadcastChannel API for Deno"
authors = ["the Deno authors"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/denoland/deno"

[lib]
path = "lib.rs"

[dependencies]
deno_core = { version = "0.87.0", path = "../../core" }
tokio = { version = "1.4.0", features = ["full"] }
5 changes: 5 additions & 0 deletions extensions/broadcast_channel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# deno_broadcast_channel

This crate implements the BroadcastChannel functions of Deno.

Spec: https://html.spec.whatwg.org/multipage/web-messaging.html
55 changes: 55 additions & 0 deletions extensions/broadcast_channel/lib.deno_broadcast_channel.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

// deno-lint-ignore-file no-explicit-any

/// <reference no-default-lib="true" />
/// <reference lib="esnext" />

interface BroadcastChannelEventMap {
"message": MessageEvent;
"messageerror": MessageEvent;
}

interface BroadcastChannel extends EventTarget {
/**
* Returns the channel name (as passed to the constructor).
*/
readonly name: string;
onmessage: ((this: BroadcastChannel, ev: MessageEvent) => any) | null;
onmessageerror: ((this: BroadcastChannel, ev: MessageEvent) => any) | null;
/**
* Closes the BroadcastChannel object, opening it up to garbage collection.
*/
close(): void;
/**
* Sends the given message to other BroadcastChannel objects set up for
* this channel. Messages can be structured objects, e.g. nested objects
* and arrays.
*/
postMessage(message: any): void;
addEventListener<K extends keyof BroadcastChannelEventMap>(
type: K,
listener: (this: BroadcastChannel, ev: BroadcastChannelEventMap[K]) => any,
options?: boolean | AddEventListenerOptions,
): void;
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void;
removeEventListener<K extends keyof BroadcastChannelEventMap>(
type: K,
listener: (this: BroadcastChannel, ev: BroadcastChannelEventMap[K]) => any,
options?: boolean | EventListenerOptions,
): void;
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions,
): void;
}

declare var BroadcastChannel: {
prototype: BroadcastChannel;
new (name: string): BroadcastChannel;
};
Loading

0 comments on commit 422a9ab

Please sign in to comment.