web_rpc/
port.rs

1use std::rc::Rc;
2use wasm_bindgen::JsValue;
3
4/// Port abstracts over the different Javascript types that support sending and
5/// receiving messages. We need to abstract over these types since there is no
6/// trait available to describe this interface. Moreover, some of these interfaces
7/// have slightly different semantics, e.g., we need to call [web_sys::MessagePort::start]
8/// for the [web_sys::MessagePort]. The inner variants are wrapped in an [Rc] so that
9/// we can force a worker to terminate when we drop the last instance. Unfortunately,
10/// the browser does not seem to reliably terminate workers during garbage collection.
11#[derive(Clone)]
12pub enum Port {
13    Worker(Rc<web_sys::Worker>),
14    DedicatedWorkerGlobalScope(Rc<web_sys::DedicatedWorkerGlobalScope>),
15    MessagePort(Rc<web_sys::MessagePort>),
16}
17
18impl Port {
19    /// Dispatch `post_message` for the different implementations
20    pub fn post_message(&self, message: &JsValue, transfer: &JsValue) -> Result<(), JsValue> {
21        match self {
22            Port::Worker(worker) => worker.post_message_with_transfer(message, transfer),
23            Port::DedicatedWorkerGlobalScope(scope) => {
24                scope.post_message_with_transfer(message, transfer)
25            }
26            Port::MessagePort(port) => port.post_message_with_transferable(message, transfer),
27        }
28    }
29
30    pub(crate) fn start(&self) {
31        if let Port::MessagePort(port) = self {
32            port.start()
33        }
34    }
35
36    pub(crate) fn event_target(&self) -> &web_sys::EventTarget {
37        match self {
38            Port::Worker(worker) => worker.as_ref(),
39            Port::DedicatedWorkerGlobalScope(scope) => scope.as_ref(),
40            Port::MessagePort(port) => port.as_ref(),
41        }
42    }
43}
44
45impl From<web_sys::Worker> for Port {
46    fn from(worker: web_sys::Worker) -> Self {
47        Port::Worker(worker.into())
48    }
49}
50
51impl From<web_sys::DedicatedWorkerGlobalScope> for Port {
52    fn from(scope: web_sys::DedicatedWorkerGlobalScope) -> Self {
53        Port::DedicatedWorkerGlobalScope(scope.into())
54    }
55}
56
57impl From<web_sys::MessagePort> for Port {
58    fn from(port: web_sys::MessagePort) -> Self {
59        Port::MessagePort(port.into())
60    }
61}
62
63impl Drop for Port {
64    fn drop(&mut self) {
65        if let Port::Worker(worker) = self {
66            if Rc::strong_count(worker) == 1 {
67                worker.terminate()
68            }
69        }
70    }
71}