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;
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct RTCDataChannel {
eventtarget: EventTarget,
#[ignore_malloc_size_of = "defined in servo-media"]
servo_media_id: DataChannelId,
peer_connection: Dom<RTCPeerConnection>,
label: USVString,
ordered: bool,
max_packet_life_time: Option<u16>,
max_retransmits: Option<u16>,
protocol: USVString,
negotiated: bool,
id: Option<u16>,
ready_state: Cell<RTCDataChannelState>,
binary_type: DomRefCell<DOMString>,
}
impl RTCDataChannel {
#[allow(crown::unrooted_must_root)]
pub fn new_inherited(
peer_connection: &RTCPeerConnection,
label: USVString,
options: &RTCDataChannelInit,
servo_media_id: Option<DataChannelId>,
) -> 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<DataChannelId>,
) -> DomRoot<RTCDataChannel> {
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, can_gc: CanGc) {
let event = Event::new(
&self.global(),
atom!("open"),
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
can_gc,
);
event.upcast::<Event>().fire(self.upcast(), can_gc);
}
pub fn on_close(&self, can_gc: CanGc) {
let event = Event::new(
&self.global(),
atom!("close"),
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
can_gc,
);
event.upcast::<Event>().fire(self.upcast(), can_gc);
self.peer_connection
.unregister_data_channel(&self.servo_media_id);
}
pub fn on_error(&self, error: WebRtcError, can_gc: CanGc) {
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, can_gc);
let event = RTCErrorEvent::new(&global, atom!("error"), false, false, &error, can_gc);
event.upcast::<Event>().fire(self.upcast(), can_gc);
}
#[allow(unsafe_code)]
pub fn on_message(&self, channel_message: DataChannelMessage, can_gc: CanGc) {
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()),
can_gc,
);
blob.to_jsval(*cx, message.handle_mut());
},
"arraybuffer" => {
rooted!(in(*cx) let mut array_buffer = ptr::null_mut::<JSObject>());
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![],
can_gc,
);
}
}
pub fn on_state_change(&self, state: DataChannelState, can_gc: CanGc) {
if let DataChannelState::Closing = state {
let event = Event::new(
&self.global(),
atom!("closing"),
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
can_gc,
);
event.upcast::<Event>().fire(self.upcast(), can_gc);
};
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 {
event_handler!(open, GetOnopen, SetOnopen);
event_handler!(
bufferedamountlow,
GetOnbufferedamountlow,
SetOnbufferedamountlow
);
event_handler!(error, GetOnerror, SetOnerror);
event_handler!(closing, GetOnclosing, SetOnclosing);
event_handler!(close, GetOnclose, SetOnclose);
event_handler!(message, GetOnmessage, SetOnmessage);
fn Label(&self) -> USVString {
self.label.clone()
}
fn Ordered(&self) -> bool {
self.ordered
}
fn GetMaxPacketLifeTime(&self) -> Option<u16> {
self.max_packet_life_time
}
fn GetMaxRetransmits(&self) -> Option<u16> {
self.max_retransmits
}
fn Protocol(&self) -> USVString {
self.protocol.clone()
}
fn Negotiated(&self) -> bool {
self.negotiated
}
fn GetId(&self) -> Option<u16> {
self.id
}
fn ReadyState(&self) -> RTCDataChannelState {
self.ready_state.get()
}
fn Close(&self) {
let controller = self.peer_connection.get_webrtc_controller().borrow();
controller
.as_ref()
.unwrap()
.close_data_channel(&self.servo_media_id);
}
fn BinaryType(&self) -> DOMString {
self.binary_type.borrow().clone()
}
fn SetBinaryType(&self, value: DOMString) -> Fallible<()> {
if value != "blob" || value != "arraybuffer" {
return Err(Error::Syntax);
}
*self.binary_type.borrow_mut() = value;
Ok(())
}
fn Send(&self, data: USVString) -> Fallible<()> {
self.send(&SendSource::String(&data))
}
fn Send_(&self, data: &Blob) -> Fallible<()> {
self.send(&SendSource::Blob(data))
}
fn Send__(&self, data: CustomAutoRooterGuard<ArrayBuffer>) -> Fallible<()> {
self.send(&SendSource::ArrayBuffer(data))
}
fn Send___(&self, data: CustomAutoRooterGuard<ArrayBufferView>) -> 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<DataChannelState> 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,
}
}
}