diff options
Diffstat (limited to 'components/script/dom/rtcdatachannel.rs')
-rw-r--r-- | components/script/dom/rtcdatachannel.rs | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/components/script/dom/rtcdatachannel.rs b/components/script/dom/rtcdatachannel.rs new file mode 100644 index 00000000000..052f3f13131 --- /dev/null +++ b/components/script/dom/rtcdatachannel.rs @@ -0,0 +1,387 @@ +/* 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 crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::RTCDataChannelInit; +use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::RTCDataChannelMethods; +use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::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 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 std::cell::Cell; +use std::ptr; + +#[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(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"), + ); + + let channel = 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")), + }; + + channel + } + + 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) { + let event = Event::new( + &self.global(), + atom!("open"), + EventBubbles::DoesNotBubble, + EventCancelable::NotCancelable, + ); + event.upcast::<Event>().fire(self.upcast()); + } + + pub fn on_close(&self) { + let event = Event::new( + &self.global(), + atom!("close"), + EventBubbles::DoesNotBubble, + EventCancelable::NotCancelable, + ); + event.upcast::<Event>().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 = global.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::<Event>().fire(self.upcast()); + } + + #[allow(unsafe_code)] + pub fn on_message(&self, channel_message: DataChannelMessage) { + unsafe { + let global = self.global(); + let cx = global.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::<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![], + ); + } + } + + pub fn on_state_change(&self, state: DataChannelState) { + match state { + DataChannelState::Closing => { + let event = Event::new( + &self.global(), + atom!("closing"), + EventBubbles::DoesNotBubble, + EventCancelable::NotCancelable, + ); + event.upcast::<Event>().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<u16> { + self.max_packet_life_time + } + + // https://www.w3.org/TR/webrtc/#dom-datachannel-maxretransmits + fn GetMaxRetransmits(&self) -> Option<u16> { + 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<u16> { + 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<ArrayBuffer>) -> Fallible<()> { + self.send(&SendSource::ArrayBuffer(data)) + } + + // https://www.w3.org/TR/webrtc/#dom-rtcdatachannel-send!overload-3 + 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::New | + DataChannelState::Connecting | + DataChannelState::__Unknown(_) => RTCDataChannelState::Connecting, + DataChannelState::Open => RTCDataChannelState::Open, + DataChannelState::Closing => RTCDataChannelState::Closing, + DataChannelState::Closed => RTCDataChannelState::Closed, + } + } +} |