Skip to main content

script/dom/webrtc/
rtcdatachannel.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::ptr;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::conversions::ToJSValConvertible;
11use js::jsapi::JSObject;
12use js::jsval::UndefinedValue;
13use js::realm::CurrentRealm;
14use js::rust::CustomAutoRooterGuard;
15use js::typedarray::{ArrayBuffer, ArrayBufferView, CreateWith};
16use script_bindings::cell::DomRefCell;
17use script_bindings::match_domstring_ascii;
18use script_bindings::reflector::reflect_dom_object;
19use script_bindings::weakref::WeakRef;
20use servo_constellation_traits::BlobImpl;
21use servo_media::webrtc::{
22    DataChannelId, DataChannelInit, DataChannelMessage, DataChannelState, WebRtcError,
23};
24
25use crate::conversions::Convert;
26use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::{
27    RTCDataChannelInit, RTCDataChannelMethods, RTCDataChannelState,
28};
29use crate::dom::bindings::codegen::Bindings::RTCErrorBinding::{RTCErrorDetailType, RTCErrorInit};
30use crate::dom::bindings::error::{Error, Fallible};
31use crate::dom::bindings::inheritance::Castable;
32use crate::dom::bindings::reflector::DomGlobal;
33use crate::dom::bindings::root::{Dom, DomRoot};
34use crate::dom::bindings::str::{DOMString, USVString};
35use crate::dom::blob::Blob;
36use crate::dom::event::{Event, EventBubbles, EventCancelable};
37use crate::dom::eventtarget::EventTarget;
38use crate::dom::globalscope::GlobalScope;
39use crate::dom::messageevent::MessageEvent;
40use crate::dom::rtcerror::RTCError;
41use crate::dom::rtcerrorevent::RTCErrorEvent;
42use crate::dom::rtcpeerconnection::RTCPeerConnection;
43use crate::script_runtime::CanGc;
44
45#[derive(JSTraceable, MallocSizeOf)]
46struct DroppableRTCDataChannel {
47    #[ignore_malloc_size_of = "defined in servo-media"]
48    servo_media_id: DataChannelId,
49    peer_connection: WeakRef<RTCPeerConnection>,
50}
51
52impl DroppableRTCDataChannel {
53    fn new(peer_connection: WeakRef<RTCPeerConnection>, servo_media_id: DataChannelId) -> Self {
54        DroppableRTCDataChannel {
55            servo_media_id,
56            peer_connection,
57        }
58    }
59
60    pub(crate) fn get_servo_media_id(&self) -> DataChannelId {
61        self.servo_media_id
62    }
63}
64
65impl Drop for DroppableRTCDataChannel {
66    fn drop(&mut self) {
67        if let Some(root) = self.peer_connection.root() {
68            root.unregister_data_channel(&self.get_servo_media_id());
69        }
70    }
71}
72
73#[dom_struct]
74pub(crate) struct RTCDataChannel {
75    eventtarget: EventTarget,
76    label: USVString,
77    ordered: bool,
78    max_packet_life_time: Option<u16>,
79    max_retransmits: Option<u16>,
80    protocol: USVString,
81    negotiated: bool,
82    id: Option<u16>,
83    ready_state: Cell<RTCDataChannelState>,
84    binary_type: DomRefCell<DOMString>,
85    peer_connection: Dom<RTCPeerConnection>,
86    droppable: DroppableRTCDataChannel,
87}
88
89impl RTCDataChannel {
90    pub(crate) fn new_inherited(
91        peer_connection: &RTCPeerConnection,
92        label: USVString,
93        options: &RTCDataChannelInit,
94        servo_media_id: Option<DataChannelId>,
95    ) -> RTCDataChannel {
96        let mut init: DataChannelInit = options.convert();
97        init.label = label.0.clone();
98
99        let controller = peer_connection.get_webrtc_controller().borrow();
100        let servo_media_id = servo_media_id.unwrap_or(
101            controller
102                .as_ref()
103                .unwrap()
104                .create_data_channel(init)
105                .expect("Expected data channel id"),
106        );
107
108        RTCDataChannel {
109            eventtarget: EventTarget::new_inherited(),
110            label,
111            ordered: options.ordered,
112            max_packet_life_time: options.maxPacketLifeTime,
113            max_retransmits: options.maxRetransmits,
114            protocol: options.protocol.clone(),
115            negotiated: options.negotiated,
116            id: options.id,
117            ready_state: Cell::new(RTCDataChannelState::Connecting),
118            binary_type: DomRefCell::new(DOMString::from("blob")),
119            peer_connection: Dom::from_ref(peer_connection),
120            droppable: DroppableRTCDataChannel::new(WeakRef::new(peer_connection), servo_media_id),
121        }
122    }
123
124    pub(crate) fn new(
125        global: &GlobalScope,
126        peer_connection: &RTCPeerConnection,
127        label: USVString,
128        options: &RTCDataChannelInit,
129        servo_media_id: Option<DataChannelId>,
130        can_gc: CanGc,
131    ) -> DomRoot<RTCDataChannel> {
132        let rtc_data_channel = reflect_dom_object(
133            Box::new(RTCDataChannel::new_inherited(
134                peer_connection,
135                label,
136                options,
137                servo_media_id,
138            )),
139            global,
140            can_gc,
141        );
142
143        peer_connection
144            .register_data_channel(rtc_data_channel.get_servo_media_id(), &rtc_data_channel);
145
146        rtc_data_channel
147    }
148
149    pub(crate) fn get_servo_media_id(&self) -> DataChannelId {
150        self.droppable.get_servo_media_id()
151    }
152
153    pub(crate) fn on_open(&self, cx: &mut JSContext) {
154        let event = Event::new(
155            &self.global(),
156            atom!("open"),
157            EventBubbles::DoesNotBubble,
158            EventCancelable::NotCancelable,
159            CanGc::from_cx(cx),
160        );
161        event.upcast::<Event>().fire(cx, self.upcast());
162    }
163
164    pub(crate) fn on_close(&self, cx: &mut JSContext) {
165        let event = Event::new(
166            &self.global(),
167            atom!("close"),
168            EventBubbles::DoesNotBubble,
169            EventCancelable::NotCancelable,
170            CanGc::from_cx(cx),
171        );
172        event.upcast::<Event>().fire(cx, self.upcast());
173
174        self.peer_connection
175            .unregister_data_channel(&self.get_servo_media_id());
176    }
177
178    pub(crate) fn on_error(&self, cx: &mut CurrentRealm, error: WebRtcError) {
179        let global = self.global();
180        let window = global.as_window();
181        let init = RTCErrorInit {
182            errorDetail: RTCErrorDetailType::Data_channel_failure,
183            httpRequestStatusCode: None,
184            receivedAlert: None,
185            sctpCauseCode: None,
186            sdpLineNumber: None,
187            sentAlert: None,
188        };
189        let message = match error {
190            WebRtcError::Backend(message) => DOMString::from(message),
191        };
192        let error = RTCError::new(window, &init, message, CanGc::from_cx(cx));
193        let event = RTCErrorEvent::new(
194            window,
195            atom!("error"),
196            false,
197            false,
198            &error,
199            CanGc::from_cx(cx),
200        );
201        event.upcast::<Event>().fire(cx, self.upcast());
202    }
203
204    #[expect(unsafe_code)]
205    pub(crate) fn on_message(&self, cx: &mut CurrentRealm, channel_message: DataChannelMessage) {
206        let global = self.global();
207        rooted!(&in(cx) let mut message = UndefinedValue());
208
209        match channel_message {
210            DataChannelMessage::Text(text) => {
211                text.safe_to_jsval(cx, message.handle_mut());
212            },
213            DataChannelMessage::Binary(data) => {
214                let binary_type = self.binary_type.borrow();
215                match_domstring_ascii!(binary_type,
216                    "blob" => {
217                        let blob = Blob::new(
218                            cx,
219                            &global,
220                            BlobImpl::new_from_bytes(data, "".to_owned()),
221                        );
222                        blob.safe_to_jsval(cx, message.handle_mut());
223                    },
224                    "arraybuffer" => {
225                        rooted!(&in(cx) let mut array_buffer = ptr::null_mut::<JSObject>());
226                        unsafe {
227                            assert!(
228                                ArrayBuffer::create(
229                                    cx.raw_cx(),
230                                    CreateWith::Slice(&data),
231                                    array_buffer.handle_mut()
232                                )
233                                .is_ok()
234                            )
235                        };
236                        (*array_buffer).safe_to_jsval(cx, message.handle_mut());
237                    },
238                    _ => unreachable!(),
239                )
240            },
241        }
242
243        MessageEvent::dispatch_jsval(
244            cx,
245            self.upcast(),
246            &global,
247            message.handle(),
248            Some(&global.origin().immutable().ascii_serialization()),
249            None,
250            vec![],
251        );
252    }
253
254    pub(crate) fn on_state_change(&self, cx: &mut JSContext, state: DataChannelState) {
255        if let DataChannelState::Closing = state {
256            let event = Event::new(
257                &self.global(),
258                atom!("closing"),
259                EventBubbles::DoesNotBubble,
260                EventCancelable::NotCancelable,
261                CanGc::from_cx(cx),
262            );
263            event.upcast::<Event>().fire(cx, self.upcast());
264        };
265        self.ready_state.set(state.convert());
266    }
267
268    fn send(&self, source: &SendSource) -> Fallible<()> {
269        if self.ready_state.get() != RTCDataChannelState::Open {
270            return Err(Error::InvalidState(None));
271        }
272
273        let message = match source {
274            SendSource::String(string) => DataChannelMessage::Text(string.0.clone()),
275            SendSource::Blob(blob) => {
276                DataChannelMessage::Binary(blob.get_bytes().unwrap_or(vec![]))
277            },
278            SendSource::ArrayBuffer(array) => DataChannelMessage::Binary(array.to_vec()),
279            SendSource::ArrayBufferView(array) => DataChannelMessage::Binary(array.to_vec()),
280        };
281
282        let controller = self.peer_connection.get_webrtc_controller().borrow();
283        controller
284            .as_ref()
285            .unwrap()
286            .send_data_channel_message(&self.get_servo_media_id(), message);
287
288        Ok(())
289    }
290}
291
292enum SendSource<'a, 'b> {
293    String(&'a USVString),
294    Blob(&'a Blob),
295    ArrayBuffer(CustomAutoRooterGuard<'b, ArrayBuffer>),
296    ArrayBufferView(CustomAutoRooterGuard<'b, ArrayBufferView>),
297}
298
299impl RTCDataChannelMethods<crate::DomTypeHolder> for RTCDataChannel {
300    // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onopen
301    event_handler!(open, GetOnopen, SetOnopen);
302    // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onbufferedamountlow
303    event_handler!(
304        bufferedamountlow,
305        GetOnbufferedamountlow,
306        SetOnbufferedamountlow
307    );
308    // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onerror
309    event_handler!(error, GetOnerror, SetOnerror);
310    // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onclosing
311    event_handler!(closing, GetOnclosing, SetOnclosing);
312    // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onclose
313    event_handler!(close, GetOnclose, SetOnclose);
314    // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onmessage
315    event_handler!(message, GetOnmessage, SetOnmessage);
316
317    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-label>
318    fn Label(&self) -> USVString {
319        self.label.clone()
320    }
321    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-ordered>
322    fn Ordered(&self) -> bool {
323        self.ordered
324    }
325
326    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-maxpacketlifetime>
327    fn GetMaxPacketLifeTime(&self) -> Option<u16> {
328        self.max_packet_life_time
329    }
330
331    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-maxretransmits>
332    fn GetMaxRetransmits(&self) -> Option<u16> {
333        self.max_retransmits
334    }
335
336    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-protocol>
337    fn Protocol(&self) -> USVString {
338        self.protocol.clone()
339    }
340
341    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-negotiated>
342    fn Negotiated(&self) -> bool {
343        self.negotiated
344    }
345
346    /// <https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-id>
347    fn GetId(&self) -> Option<u16> {
348        self.id
349    }
350
351    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-readystate>
352    fn ReadyState(&self) -> RTCDataChannelState {
353        self.ready_state.get()
354    }
355
356    // XXX We need a way to know when the underlying data transport
357    // actually sends data from its queue to decrease buffered amount.
358
359    //    fn BufferedAmount(&self) -> u32;
360    //    fn BufferedAmountLowThreshold(&self) -> u32;
361    //    fn SetBufferedAmountLowThreshold(&self, value: u32) -> ();
362
363    /// <https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-close>
364    fn Close(&self) {
365        let controller = self.peer_connection.get_webrtc_controller().borrow();
366        controller
367            .as_ref()
368            .unwrap()
369            .close_data_channel(&self.get_servo_media_id());
370    }
371
372    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-binarytype>
373    fn BinaryType(&self) -> DOMString {
374        self.binary_type.borrow().clone()
375    }
376
377    /// <https://www.w3.org/TR/webrtc/#dom-datachannel-binarytype>
378    fn SetBinaryType(&self, value: DOMString) -> Fallible<()> {
379        if value != "blob" || value != "arraybuffer" {
380            return Err(Error::Syntax(None));
381        }
382        *self.binary_type.borrow_mut() = value;
383        Ok(())
384    }
385
386    /// <https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send>
387    fn Send(&self, data: USVString) -> Fallible<()> {
388        self.send(&SendSource::String(&data))
389    }
390
391    /// <https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-1>
392    fn Send_(&self, data: &Blob) -> Fallible<()> {
393        self.send(&SendSource::Blob(data))
394    }
395
396    /// <https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-2>
397    fn Send__(&self, data: CustomAutoRooterGuard<ArrayBuffer>) -> Fallible<()> {
398        self.send(&SendSource::ArrayBuffer(data))
399    }
400
401    /// <https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-3>
402    fn Send___(&self, data: CustomAutoRooterGuard<ArrayBufferView>) -> Fallible<()> {
403        self.send(&SendSource::ArrayBufferView(data))
404    }
405}
406
407impl Convert<DataChannelInit> for &RTCDataChannelInit {
408    fn convert(self) -> DataChannelInit {
409        DataChannelInit {
410            label: String::new(),
411            id: self.id,
412            max_packet_life_time: self.maxPacketLifeTime,
413            max_retransmits: self.maxRetransmits,
414            negotiated: self.negotiated,
415            ordered: self.ordered,
416            protocol: self.protocol.to_string(),
417        }
418    }
419}
420
421impl Convert<RTCDataChannelState> for DataChannelState {
422    fn convert(self) -> RTCDataChannelState {
423        match self {
424            DataChannelState::Connecting | DataChannelState::__Unknown(_) => {
425                RTCDataChannelState::Connecting
426            },
427            DataChannelState::Open => RTCDataChannelState::Open,
428            DataChannelState::Closing => RTCDataChannelState::Closing,
429            DataChannelState::Closed => RTCDataChannelState::Closed,
430        }
431    }
432}