mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
feat(extensions): add BroadcastChannel
Co-Authored-By: Ben Noordhuis <info@bnoordhuis.nl> Fixes: #10354
This commit is contained in:
parent
5f0d91497b
commit
8cf7f966f2
16 changed files with 357 additions and 1 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -591,6 +591,14 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_broadcast_channel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_console"
|
||||
version = "0.7.0"
|
||||
|
@ -691,6 +699,7 @@ version = "0.15.0"
|
|||
dependencies = [
|
||||
"atty",
|
||||
"bytes",
|
||||
"deno_broadcast_channel",
|
||||
"deno_console",
|
||||
"deno_core",
|
||||
"deno_crypto",
|
||||
|
|
|
@ -9,6 +9,7 @@ members = [
|
|||
"serde_v8",
|
||||
"test_plugin",
|
||||
"test_util",
|
||||
"extensions/broadcast_channel",
|
||||
"extensions/console",
|
||||
"extensions/crypto",
|
||||
"extensions/fetch",
|
||||
|
|
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
|
|
|
@ -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,
|
||||
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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");
|
||||
|
|
117
extensions/broadcast_channel/01_broadcast_channel.js
Normal file
117
extensions/broadcast_channel/01_broadcast_channel.js
Normal file
|
@ -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
extensions/broadcast_channel/Cargo.toml
Normal file
18
extensions/broadcast_channel/Cargo.toml
Normal file
|
@ -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.88.0", path = "../../core" }
|
||||
tokio = { version = "1.4.0", features = ["full"] }
|
5
extensions/broadcast_channel/README.md
Normal file
5
extensions/broadcast_channel/README.md
Normal file
|
@ -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
extensions/broadcast_channel/lib.deno_broadcast_channel.d.ts
vendored
Normal file
55
extensions/broadcast_channel/lib.deno_broadcast_channel.d.ts
vendored
Normal file
|
@ -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;
|
||||
};
|
131
extensions/broadcast_channel/lib.rs
Normal file
131
extensions/broadcast_channel/lib.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::bad_resource_id;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::include_js_files;
|
||||
use deno_core::op_async;
|
||||
use deno_core::op_sync;
|
||||
use deno_core::AsyncRefCell;
|
||||
use deno_core::Extension;
|
||||
use deno_core::OpState;
|
||||
use deno_core::RcRef;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
struct BroadcastChannelResource(AsyncRefCell<tokio::fs::File>);
|
||||
|
||||
impl Resource for BroadcastChannelResource {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"broadcastChannel".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_broadcast_open(
|
||||
state: &mut OpState,
|
||||
name: String,
|
||||
_bufs: Option<ZeroCopyBuf>,
|
||||
) -> Result<ResourceId, AnyError> {
|
||||
let path = PathBuf::from("./");
|
||||
std::fs::create_dir_all(&path)?;
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.read(true)
|
||||
.open(path.join(format!("broadcast_{}", name)))?;
|
||||
|
||||
let rid =
|
||||
state
|
||||
.resource_table
|
||||
.add(BroadcastChannelResource(AsyncRefCell::new(
|
||||
tokio::fs::File::from_std(file),
|
||||
)));
|
||||
|
||||
Ok(rid)
|
||||
}
|
||||
|
||||
pub async fn op_broadcast_send(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
rid: ResourceId,
|
||||
buf: Option<ZeroCopyBuf>,
|
||||
) -> Result<(), AnyError> {
|
||||
let state = state.borrow_mut();
|
||||
let resource = state
|
||||
.resource_table
|
||||
.get::<BroadcastChannelResource>(rid)
|
||||
.ok_or_else(bad_resource_id)?;
|
||||
|
||||
let mut file = RcRef::map(&resource, |r| &r.0).borrow_mut().await;
|
||||
|
||||
let buffer_data = buf.unwrap();
|
||||
let mut data = vec![];
|
||||
data.extend_from_slice(&(buffer_data.len() as u64).to_ne_bytes());
|
||||
data.extend_from_slice(&buffer_data);
|
||||
|
||||
file.write_all(&data).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn op_broadcast_next_event(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
rid: ResourceId,
|
||||
_bufs: Option<ZeroCopyBuf>,
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
let resource = {
|
||||
let state = state.borrow_mut();
|
||||
state
|
||||
.resource_table
|
||||
.get::<BroadcastChannelResource>(rid)
|
||||
.ok_or_else(bad_resource_id)?
|
||||
};
|
||||
|
||||
let mut file = RcRef::map(&resource, |r| &r.0).borrow_mut().await;
|
||||
|
||||
let size = match file.read_u64().await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return match e.kind() {
|
||||
deno_core::futures::io::ErrorKind::UnexpectedEof => Ok(vec![]),
|
||||
_ => Err(e.into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut data = vec![0u8; size as usize];
|
||||
match file.read_exact(&mut data).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return match e.kind() {
|
||||
deno_core::futures::io::ErrorKind::UnexpectedEof => Ok(vec![]),
|
||||
_ => Err(e.into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn init() -> Extension {
|
||||
Extension::builder()
|
||||
.js(include_js_files!(
|
||||
prefix "deno:extensions/broadcast_channel",
|
||||
"01_broadcast_channel.js",
|
||||
))
|
||||
.ops(vec![
|
||||
("op_broadcast_open", op_sync(op_broadcast_open)),
|
||||
("op_broadcast_send", op_async(op_broadcast_send)),
|
||||
("op_broadcast_next_event", op_async(op_broadcast_next_event)),
|
||||
])
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn get_declaration() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("lib.deno_broadcast_channel.d.ts")
|
||||
}
|
|
@ -18,6 +18,7 @@ name = "hello_runtime"
|
|||
path = "examples/hello_runtime.rs"
|
||||
|
||||
[build-dependencies]
|
||||
deno_broadcast_channel = { path = "../extensions/broadcast_channel", version = "0.1.0" }
|
||||
deno_console = { version = "0.7.0", path = "../extensions/console" }
|
||||
deno_core = { version = "0.88.0", path = "../core" }
|
||||
deno_crypto = { version = "0.21.0", path = "../extensions/crypto" }
|
||||
|
@ -36,6 +37,7 @@ winres = "0.1.11"
|
|||
winapi = "0.3.9"
|
||||
|
||||
[dependencies]
|
||||
deno_broadcast_channel = { path = "../extensions/broadcast_channel", version = "0.1.0" }
|
||||
deno_console = { version = "0.7.0", path = "../extensions/console" }
|
||||
deno_core = { version = "0.88.0", path = "../core" }
|
||||
deno_crypto = { version = "0.21.0", path = "../extensions/crypto" }
|
||||
|
|
|
@ -52,6 +52,7 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
|
|||
deno_crypto::init(None),
|
||||
deno_webgpu::init(false),
|
||||
deno_timers::init::<deno_timers::NoTimersPermission>(),
|
||||
deno_broadcast_channel::init(),
|
||||
];
|
||||
|
||||
let js_runtime = JsRuntime::new(RuntimeOptions {
|
||||
|
|
|
@ -29,6 +29,7 @@ delete Object.prototype.__proto__;
|
|||
const webgpu = window.__bootstrap.webgpu;
|
||||
const webSocket = window.__bootstrap.webSocket;
|
||||
const webStorage = window.__bootstrap.webStorage;
|
||||
const broadcastChannel = window.__bootstrap.broadcastChannel;
|
||||
const file = window.__bootstrap.file;
|
||||
const formData = window.__bootstrap.formData;
|
||||
const fetch = window.__bootstrap.fetch;
|
||||
|
@ -282,6 +283,7 @@ delete Object.prototype.__proto__;
|
|||
URL: util.nonEnumerable(url.URL),
|
||||
URLSearchParams: util.nonEnumerable(url.URLSearchParams),
|
||||
WebSocket: util.nonEnumerable(webSocket.WebSocket),
|
||||
BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
|
||||
Worker: util.nonEnumerable(worker.Worker),
|
||||
WritableStream: util.nonEnumerable(streams.WritableStream),
|
||||
WritableStreamDefaultWriter: util.nonEnumerable(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
pub use deno_broadcast_channel;
|
||||
pub use deno_console;
|
||||
pub use deno_crypto;
|
||||
pub use deno_fetch;
|
||||
|
|
|
@ -268,6 +268,7 @@ impl WebWorker {
|
|||
options.user_agent.clone(),
|
||||
options.ca_data.clone(),
|
||||
),
|
||||
deno_broadcast_channel::init(),
|
||||
deno_crypto::init(options.seed),
|
||||
deno_webgpu::init(options.unstable),
|
||||
deno_timers::init::<Permissions>(),
|
||||
|
|
|
@ -107,6 +107,7 @@ impl MainWorker {
|
|||
),
|
||||
deno_webstorage::init(options.location_data_dir.clone()),
|
||||
deno_crypto::init(options.seed),
|
||||
deno_broadcast_channel::init(),
|
||||
deno_webgpu::init(options.unstable),
|
||||
deno_timers::init::<Permissions>(),
|
||||
// Metrics
|
||||
|
|
Loading…
Reference in a new issue