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