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