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