/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; use std::ptr; use dom_struct::dom_struct; use js::conversions::ToJSValConvertible; use js::jsapi::{JSAutoRealm, JSObject}; use js::jsval::UndefinedValue; use js::rust::CustomAutoRooterGuard; use js::typedarray::{ArrayBuffer, ArrayBufferView, CreateWith}; use script_traits::serializable::BlobImpl; use servo_media::webrtc::{ DataChannelId, DataChannelInit, DataChannelMessage, DataChannelState, WebRtcError, }; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::{ RTCDataChannelInit, RTCDataChannelMethods, RTCDataChannelState, }; use crate::dom::bindings::codegen::Bindings::RTCErrorBinding::{RTCErrorDetailType, RTCErrorInit}; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::blob::Blob; use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::messageevent::MessageEvent; use crate::dom::rtcerror::RTCError; use crate::dom::rtcerrorevent::RTCErrorEvent; use crate::dom::rtcpeerconnection::RTCPeerConnection; #[dom_struct] pub struct RTCDataChannel { eventtarget: EventTarget, #[ignore_malloc_size_of = "defined in servo-media"] servo_media_id: DataChannelId, peer_connection: Dom, label: USVString, ordered: bool, max_packet_life_time: Option, max_retransmits: Option, protocol: USVString, negotiated: bool, id: Option, ready_state: Cell, binary_type: DomRefCell, } impl RTCDataChannel { #[allow(crown::unrooted_must_root)] pub fn new_inherited( peer_connection: &RTCPeerConnection, label: USVString, options: &RTCDataChannelInit, servo_media_id: Option, ) -> RTCDataChannel { let mut init: DataChannelInit = options.into(); init.label = label.to_string(); let controller = peer_connection.get_webrtc_controller().borrow(); let servo_media_id = servo_media_id.unwrap_or( controller .as_ref() .unwrap() .create_data_channel(init) .expect("Expected data channel id"), ); RTCDataChannel { eventtarget: EventTarget::new_inherited(), servo_media_id, peer_connection: Dom::from_ref(peer_connection), label, ordered: options.ordered, max_packet_life_time: options.maxPacketLifeTime, max_retransmits: options.maxRetransmits, protocol: options.protocol.clone(), negotiated: options.negotiated, id: options.id, ready_state: Cell::new(RTCDataChannelState::Connecting), binary_type: DomRefCell::new(DOMString::from("blob")), } } pub fn new( global: &GlobalScope, peer_connection: &RTCPeerConnection, label: USVString, options: &RTCDataChannelInit, servo_media_id: Option, ) -> DomRoot { let rtc_data_channel = reflect_dom_object( Box::new(RTCDataChannel::new_inherited( peer_connection, label, options, servo_media_id, )), global, ); peer_connection.register_data_channel(rtc_data_channel.servo_media_id, &rtc_data_channel); rtc_data_channel } pub fn on_open(&self) { let event = Event::new( &self.global(), atom!("open"), EventBubbles::DoesNotBubble, EventCancelable::NotCancelable, ); event.upcast::().fire(self.upcast()); } pub fn on_close(&self) { let event = Event::new( &self.global(), atom!("close"), EventBubbles::DoesNotBubble, EventCancelable::NotCancelable, ); event.upcast::().fire(self.upcast()); self.peer_connection .unregister_data_channel(&self.servo_media_id); } pub fn on_error(&self, error: WebRtcError) { let global = self.global(); let cx = GlobalScope::get_cx(); let _ac = JSAutoRealm::new(*cx, self.reflector().get_jsobject().get()); let init = RTCErrorInit { errorDetail: RTCErrorDetailType::Data_channel_failure, httpRequestStatusCode: None, receivedAlert: None, sctpCauseCode: None, sdpLineNumber: None, sentAlert: None, }; let message = match error { WebRtcError::Backend(message) => DOMString::from(message), }; let error = RTCError::new(&global, &init, message); let event = RTCErrorEvent::new(&global, atom!("error"), false, false, &error); event.upcast::().fire(self.upcast()); } #[allow(unsafe_code)] pub fn on_message(&self, channel_message: DataChannelMessage) { unsafe { let global = self.global(); let cx = GlobalScope::get_cx(); let _ac = JSAutoRealm::new(*cx, self.reflector().get_jsobject().get()); rooted!(in(*cx) let mut message = UndefinedValue()); match channel_message { DataChannelMessage::Text(text) => { text.to_jsval(*cx, message.handle_mut()); }, DataChannelMessage::Binary(data) => match &**self.binary_type.borrow() { "blob" => { let blob = Blob::new(&global, BlobImpl::new_from_bytes(data, "".to_owned())); blob.to_jsval(*cx, message.handle_mut()); }, "arraybuffer" => { rooted!(in(*cx) let mut array_buffer = ptr::null_mut::()); assert!(ArrayBuffer::create( *cx, CreateWith::Slice(&data), array_buffer.handle_mut() ) .is_ok()); (*array_buffer).to_jsval(*cx, message.handle_mut()); }, _ => unreachable!(), }, } MessageEvent::dispatch_jsval( self.upcast(), &global, message.handle(), Some(&global.origin().immutable().ascii_serialization()), None, vec![], ); } } pub fn on_state_change(&self, state: DataChannelState) { if let DataChannelState::Closing = state { let event = Event::new( &self.global(), atom!("closing"), EventBubbles::DoesNotBubble, EventCancelable::NotCancelable, ); event.upcast::().fire(self.upcast()); }; self.ready_state.set(state.into()); } fn send(&self, source: &SendSource) -> Fallible<()> { if self.ready_state.get() != RTCDataChannelState::Open { return Err(Error::InvalidState); } let message = match source { SendSource::String(string) => DataChannelMessage::Text(string.0.clone()), SendSource::Blob(blob) => { DataChannelMessage::Binary(blob.get_bytes().unwrap_or(vec![])) }, SendSource::ArrayBuffer(array) => DataChannelMessage::Binary(array.to_vec()), SendSource::ArrayBufferView(array) => DataChannelMessage::Binary(array.to_vec()), }; let controller = self.peer_connection.get_webrtc_controller().borrow(); controller .as_ref() .unwrap() .send_data_channel_message(&self.servo_media_id, message); Ok(()) } } impl Drop for RTCDataChannel { fn drop(&mut self) { self.peer_connection .unregister_data_channel(&self.servo_media_id); } } enum SendSource<'a, 'b> { String(&'a USVString), Blob(&'a Blob), ArrayBuffer(CustomAutoRooterGuard<'b, ArrayBuffer>), ArrayBufferView(CustomAutoRooterGuard<'b, ArrayBufferView>), } impl RTCDataChannelMethods for RTCDataChannel { // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onopen event_handler!(open, GetOnopen, SetOnopen); // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onbufferedamountlow event_handler!( bufferedamountlow, GetOnbufferedamountlow, SetOnbufferedamountlow ); // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onerror event_handler!(error, GetOnerror, SetOnerror); // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onclosing event_handler!(closing, GetOnclosing, SetOnclosing); // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onclose event_handler!(close, GetOnclose, SetOnclose); // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-onmessage event_handler!(message, GetOnmessage, SetOnmessage); // https://www.w3.org/TR/webrtc/#dom-datachannel-label fn Label(&self) -> USVString { self.label.clone() } // https://www.w3.org/TR/webrtc/#dom-datachannel-ordered fn Ordered(&self) -> bool { self.ordered } // https://www.w3.org/TR/webrtc/#dom-datachannel-maxpacketlifetime fn GetMaxPacketLifeTime(&self) -> Option { self.max_packet_life_time } // https://www.w3.org/TR/webrtc/#dom-datachannel-maxretransmits fn GetMaxRetransmits(&self) -> Option { self.max_retransmits } // https://www.w3.org/TR/webrtc/#dom-datachannel-protocol fn Protocol(&self) -> USVString { self.protocol.clone() } // https://www.w3.org/TR/webrtc/#dom-datachannel-negotiated fn Negotiated(&self) -> bool { self.negotiated } // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-id fn GetId(&self) -> Option { self.id } // https://www.w3.org/TR/webrtc/#dom-datachannel-readystate fn ReadyState(&self) -> RTCDataChannelState { self.ready_state.get() } // XXX We need a way to know when the underlying data transport // actually sends data from its queue to decrease buffered amount. // fn BufferedAmount(&self) -> u32; // fn BufferedAmountLowThreshold(&self) -> u32; // fn SetBufferedAmountLowThreshold(&self, value: u32) -> (); // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-close fn Close(&self) { let controller = self.peer_connection.get_webrtc_controller().borrow(); controller .as_ref() .unwrap() .close_data_channel(&self.servo_media_id); } // https://www.w3.org/TR/webrtc/#dom-datachannel-binarytype fn BinaryType(&self) -> DOMString { self.binary_type.borrow().clone() } // https://www.w3.org/TR/webrtc/#dom-datachannel-binarytype fn SetBinaryType(&self, value: DOMString) -> Fallible<()> { if value != "blob" || value != "arraybuffer" { return Err(Error::Syntax); } *self.binary_type.borrow_mut() = value; Ok(()) } // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send fn Send(&self, data: USVString) -> Fallible<()> { self.send(&SendSource::String(&data)) } // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-1 fn Send_(&self, data: &Blob) -> Fallible<()> { self.send(&SendSource::Blob(data)) } // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-2 fn Send__(&self, data: CustomAutoRooterGuard) -> Fallible<()> { self.send(&SendSource::ArrayBuffer(data)) } // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-3 fn Send___(&self, data: CustomAutoRooterGuard) -> Fallible<()> { self.send(&SendSource::ArrayBufferView(data)) } } impl From<&RTCDataChannelInit> for DataChannelInit { fn from(init: &RTCDataChannelInit) -> DataChannelInit { DataChannelInit { label: String::new(), id: init.id, max_packet_life_time: init.maxPacketLifeTime, max_retransmits: init.maxRetransmits, negotiated: init.negotiated, ordered: init.ordered, protocol: init.protocol.to_string(), } } } impl From for RTCDataChannelState { fn from(state: DataChannelState) -> RTCDataChannelState { match state { DataChannelState::Connecting | DataChannelState::__Unknown(_) => { RTCDataChannelState::Connecting }, DataChannelState::Open => RTCDataChannelState::Open, DataChannelState::Closing => RTCDataChannelState::Closing, DataChannelState::Closed => RTCDataChannelState::Closed, } } }