Auto merge of #26752 - ferjm:datachannel, r=Manishearth

WebRTC data channels support

- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #26212

This depends on https://github.com/servo/media/pull/350 and contains the basic pieces to make a  simple test like https://ferjm.github.io/samples/src/content/datachannel/basic/ work
This commit is contained in:
bors-servo 2020-06-30 13:53:32 -04:00 committed by GitHub
commit 4b034ede46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 930 additions and 34 deletions

View file

@ -16,6 +16,7 @@ characteristicvaluechanged
checkbox
click
close
closing
color
complete
compositionend
@ -23,6 +24,7 @@ compositionstart
compositionupdate
controllerchange
cursive
datachannel
date
datetime-local
dir

View file

@ -487,6 +487,10 @@ pub mod raredata;
pub mod readablestream;
pub mod request;
pub mod response;
pub mod rtcdatachannel;
pub mod rtcdatachannelevent;
pub mod rtcerror;
pub mod rtcerrorevent;
pub mod rtcicecandidate;
pub mod rtcpeerconnection;
pub mod rtcpeerconnectioniceevent;

View file

@ -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,
}
}
}

View file

@ -0,0 +1,77 @@
/* 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::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::RTCDataChannelEventBinding::RTCDataChannelEventInit;
use crate::dom::bindings::codegen::Bindings::RTCDataChannelEventBinding::RTCDataChannelEventMethods;
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;
use crate::dom::event::Event;
use crate::dom::globalscope::GlobalScope;
use crate::dom::rtcdatachannel::RTCDataChannel;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_atoms::Atom;
#[dom_struct]
pub struct RTCDataChannelEvent {
event: Event,
channel: Dom<RTCDataChannel>,
}
impl RTCDataChannelEvent {
fn new_inherited(channel: &RTCDataChannel) -> RTCDataChannelEvent {
RTCDataChannelEvent {
event: Event::new_inherited(),
channel: Dom::from_ref(channel),
}
}
pub fn new(
global: &GlobalScope,
type_: Atom,
bubbles: bool,
cancelable: bool,
channel: &RTCDataChannel,
) -> DomRoot<RTCDataChannelEvent> {
let event = reflect_dom_object(
Box::new(RTCDataChannelEvent::new_inherited(&channel)),
global,
);
{
let event = event.upcast::<Event>();
event.init_event(type_, bubbles, cancelable);
}
event
}
#[allow(non_snake_case)]
pub fn Constructor(
window: &Window,
type_: DOMString,
init: &RTCDataChannelEventInit,
) -> DomRoot<RTCDataChannelEvent> {
RTCDataChannelEvent::new(
&window.global(),
Atom::from(type_),
init.parent.bubbles,
init.parent.cancelable,
&init.channel,
)
}
}
impl RTCDataChannelEventMethods for RTCDataChannelEvent {
// https://www.w3.org/TR/webrtc/#dom-datachannelevent-channel
fn Channel(&self) -> DomRoot<RTCDataChannel> {
DomRoot::from_ref(&*self.channel)
}
// https://dom.spec.whatwg.org/#dom-event-istrusted
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
}

View file

@ -0,0 +1,90 @@
/* 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::codegen::Bindings::RTCErrorBinding::RTCErrorDetailType;
use crate::dom::bindings::codegen::Bindings::RTCErrorBinding::RTCErrorInit;
use crate::dom::bindings::codegen::Bindings::RTCErrorBinding::RTCErrorMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::domexception::{DOMErrorName, DOMException};
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use dom_struct::dom_struct;
#[dom_struct]
pub struct RTCError {
exception: Dom<DOMException>,
error_detail: RTCErrorDetailType,
sdp_line_number: Option<i32>,
http_request_status_code: Option<i32>,
sctp_cause_code: Option<i32>,
received_alert: Option<u32>,
sent_alert: Option<u32>,
}
impl RTCError {
fn new_inherited(global: &GlobalScope, init: &RTCErrorInit, message: DOMString) -> RTCError {
RTCError {
exception: Dom::from_ref(&*DOMException::new(
global,
DOMErrorName::from(&message).unwrap(),
)),
error_detail: init.errorDetail,
sdp_line_number: init.sdpLineNumber,
http_request_status_code: init.httpRequestStatusCode,
sctp_cause_code: init.sctpCauseCode,
received_alert: init.receivedAlert,
sent_alert: init.sentAlert,
}
}
pub fn new(global: &GlobalScope, init: &RTCErrorInit, message: DOMString) -> DomRoot<RTCError> {
reflect_dom_object(
Box::new(RTCError::new_inherited(global, init, message)),
global,
)
}
#[allow(non_snake_case)]
pub fn Constructor(
window: &Window,
init: &RTCErrorInit,
message: DOMString,
) -> DomRoot<RTCError> {
RTCError::new(&window.global(), init, message)
}
}
impl RTCErrorMethods for RTCError {
// https://www.w3.org/TR/webrtc/#dom-rtcerror-errordetail
fn ErrorDetail(&self) -> RTCErrorDetailType {
self.error_detail
}
// https://www.w3.org/TR/webrtc/#dom-rtcerror-sdplinenumber
fn GetSdpLineNumber(&self) -> Option<i32> {
self.sdp_line_number
}
// https://www.w3.org/TR/webrtc/#dom-rtcerror
fn GetHttpRequestStatusCode(&self) -> Option<i32> {
self.http_request_status_code
}
// https://www.w3.org/TR/webrtc/#dom-rtcerror-sctpcausecode
fn GetSctpCauseCode(&self) -> Option<i32> {
self.sctp_cause_code
}
// https://www.w3.org/TR/webrtc/#dom-rtcerror-receivedalert
fn GetReceivedAlert(&self) -> Option<u32> {
self.received_alert
}
// https://www.w3.org/TR/webrtc/#dom-rtcerror-sentalert
fn GetSentAlert(&self) -> Option<u32> {
self.sent_alert
}
}

View file

@ -0,0 +1,74 @@
/* 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::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::RTCErrorEventBinding::RTCErrorEventInit;
use crate::dom::bindings::codegen::Bindings::RTCErrorEventBinding::RTCErrorEventMethods;
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;
use crate::dom::event::Event;
use crate::dom::globalscope::GlobalScope;
use crate::dom::rtcerror::RTCError;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_atoms::Atom;
#[dom_struct]
pub struct RTCErrorEvent {
event: Event,
error: Dom<RTCError>,
}
impl RTCErrorEvent {
fn new_inherited(error: &RTCError) -> RTCErrorEvent {
RTCErrorEvent {
event: Event::new_inherited(),
error: Dom::from_ref(error),
}
}
pub fn new(
global: &GlobalScope,
type_: Atom,
bubbles: bool,
cancelable: bool,
error: &RTCError,
) -> DomRoot<RTCErrorEvent> {
let event = reflect_dom_object(Box::new(RTCErrorEvent::new_inherited(&error)), global);
{
let event = event.upcast::<Event>();
event.init_event(type_, bubbles, cancelable);
}
event
}
#[allow(non_snake_case)]
pub fn Constructor(
window: &Window,
type_: DOMString,
init: &RTCErrorEventInit,
) -> DomRoot<RTCErrorEvent> {
RTCErrorEvent::new(
&window.global(),
Atom::from(type_),
init.parent.bubbles,
init.parent.cancelable,
&init.error,
)
}
}
impl RTCErrorEventMethods for RTCErrorEvent {
// https://www.w3.org/TR/webrtc/#dom-rtcerrorevent-error
fn Error(&self) -> DomRoot<RTCError> {
DomRoot::from_ref(&*self.error)
}
// https://dom.spec.whatwg.org/#dom-event-istrusted
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
}

View file

@ -3,6 +3,7 @@
* 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::RTCIceCandidateBinding::RTCIceCandidateInit;
use crate::dom::bindings::codegen::Bindings::RTCPeerConnectionBinding::RTCPeerConnectionMethods;
use crate::dom::bindings::codegen::Bindings::RTCPeerConnectionBinding::{
@ -19,19 +20,22 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::USVString;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::mediastream::MediaStream;
use crate::dom::mediastreamtrack::MediaStreamTrack;
use crate::dom::promise::Promise;
use crate::dom::rtcdatachannel::RTCDataChannel;
use crate::dom::rtcdatachannelevent::RTCDataChannelEvent;
use crate::dom::rtcicecandidate::RTCIceCandidate;
use crate::dom::rtcpeerconnectioniceevent::RTCPeerConnectionIceEvent;
use crate::dom::rtcsessiondescription::RTCSessionDescription;
use crate::dom::rtctrackevent::RTCTrackEvent;
use crate::dom::window::Window;
use crate::realms::InRealm;
use crate::realms::{enter_realm, InRealm};
use crate::task::TaskCanceller;
use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::TaskSource;
@ -40,12 +44,14 @@ use dom_struct::dom_struct;
use servo_media::streams::registry::MediaStreamId;
use servo_media::streams::MediaStreamType;
use servo_media::webrtc::{
BundlePolicy, GatheringState, IceCandidate, IceConnectionState, SdpType, SessionDescription,
SignalingState, WebRtcController, WebRtcSignaller,
BundlePolicy, DataChannelEvent, DataChannelId, DataChannelState, GatheringState, IceCandidate,
IceConnectionState, SdpType, SessionDescription, SignalingState, WebRtcController,
WebRtcSignaller,
};
use servo_media::ServoMedia;
use std::cell::Cell;
use std::collections::HashMap;
use std::rc::Rc;
#[dom_struct]
@ -66,6 +72,8 @@ pub struct RTCPeerConnection {
gathering_state: Cell<RTCIceGatheringState>,
ice_connection_state: Cell<RTCIceConnectionState>,
signaling_state: Cell<RTCSignalingState>,
#[ignore_malloc_size_of = "defined in servo-media"]
data_channels: DomRefCell<HashMap<DataChannelId, Dom<RTCDataChannel>>>,
}
struct RTCSignaller {
@ -142,6 +150,25 @@ impl WebRtcSignaller for RTCSignaller {
);
}
fn on_data_channel_event(
&self,
channel: DataChannelId,
event: DataChannelEvent,
_: &WebRtcController,
) {
// XXX(ferjm) get label and options from channel properties.
let this = self.trusted.clone();
let _ = self.task_source.queue_with_canceller(
task!(on_data_channel_event: move || {
let this = this.root();
let global = this.global();
let _ac = enter_realm(&*global);
this.on_data_channel_event(channel, event);
}),
&self.canceller,
);
}
fn close(&self) {
// do nothing
}
@ -161,6 +188,7 @@ impl RTCPeerConnection {
gathering_state: Cell::new(RTCIceGatheringState::New),
ice_connection_state: Cell::new(RTCIceConnectionState::New),
signaling_state: Cell::new(RTCSignalingState::Stable),
data_channels: DomRefCell::new(HashMap::new()),
}
}
@ -199,6 +227,10 @@ impl RTCPeerConnection {
Ok(RTCPeerConnection::new(&window.global(), config))
}
pub fn get_webrtc_controller(&self) -> &DomRefCell<Option<WebRtcController>> {
&self.controller
}
fn make_signaller(&self) -> Box<dyn WebRtcSignaller> {
let trusted = Trusted::new(self);
let (task_source, canceller) = self
@ -256,6 +288,68 @@ impl RTCPeerConnection {
event.upcast::<Event>().fire(self.upcast());
}
fn on_data_channel_event(&self, channel_id: DataChannelId, event: DataChannelEvent) {
if self.closed.get() {
return;
}
match event {
DataChannelEvent::NewChannel => {
let channel = RTCDataChannel::new(
&self.global(),
&self,
USVString::from("".to_owned()),
&RTCDataChannelInit::empty(),
Some(channel_id),
);
let event = RTCDataChannelEvent::new(
&self.global(),
atom!("datachannel"),
false,
false,
&channel,
);
event.upcast::<Event>().fire(self.upcast());
},
_ => {
let channel = if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
DomRoot::from_ref(&**channel)
} else {
warn!(
"Got an event for an unregistered data channel {:?}",
channel_id
);
return;
};
match event {
DataChannelEvent::Open => channel.on_open(),
DataChannelEvent::Close => channel.on_close(),
DataChannelEvent::Error(error) => channel.on_error(error),
DataChannelEvent::OnMessage(message) => channel.on_message(message),
DataChannelEvent::StateChange(state) => channel.on_state_change(state),
DataChannelEvent::NewChannel => unreachable!(),
}
},
};
}
pub fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
if self
.data_channels
.borrow_mut()
.insert(id, Dom::from_ref(channel))
.is_some()
{
warn!("Data channel already registered {:?}", id);
}
}
pub fn unregister_data_channel(&self, id: &DataChannelId) {
self.data_channels.borrow_mut().remove(&id);
}
/// https://www.w3.org/TR/webrtc/#update-ice-gathering-state
fn update_gathering_state(&self, state: GatheringState) {
// step 1
@ -448,6 +542,9 @@ impl RTCPeerConnectionMethods for RTCPeerConnection {
SetOnsignalingstatechange
);
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ondatachannel
event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
/// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addicecandidate
fn AddIceCandidate(&self, candidate: &RTCIceCandidateInit, comp: InRealm) -> Rc<Promise> {
let p = Promise::new_in_current_realm(&self.global(), comp);
@ -624,8 +721,13 @@ impl RTCPeerConnectionMethods for RTCPeerConnection {
// Step 5 handled by backend
self.controller.borrow_mut().as_ref().unwrap().quit();
// Step 6-10
// (no current support for data channels, transports, etc)
// Step 6
for (_, val) in self.data_channels.borrow().iter() {
val.on_state_change(DataChannelState::Closed);
}
// Step 7-10
// (no current support for transports, etc)
// Step 11
self.ice_connection_state.set(RTCIceConnectionState::Closed);
@ -633,6 +735,15 @@ impl RTCPeerConnectionMethods for RTCPeerConnection {
// Step 11
// (no current support for connection state)
}
/// https://www.w3.org/TR/webrtc/#dom-peerconnection-createdatachannel
fn CreateDataChannel(
&self,
label: USVString,
init: &RTCDataChannelInit,
) -> DomRoot<RTCDataChannel> {
RTCDataChannel::new(&self.global(), &self, label, init, None)
}
}
impl From<SessionDescription> for RTCSessionDescriptionInit {

View file

@ -0,0 +1,49 @@
/* 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/. */
// https://w3c.github.io/webrtc-pc/#dom-rtcdatachannel
[Exposed=Window, Pref="dom.webrtc.enabled"]
interface RTCDataChannel : EventTarget {
readonly attribute USVString label;
readonly attribute boolean ordered;
readonly attribute unsigned short? maxPacketLifeTime;
readonly attribute unsigned short? maxRetransmits;
readonly attribute USVString protocol;
readonly attribute boolean negotiated;
readonly attribute unsigned short? id;
readonly attribute RTCDataChannelState readyState;
//readonly attribute unsigned long bufferedAmount;
//attribute unsigned long bufferedAmountLowThreshold;
attribute EventHandler onopen;
attribute EventHandler onbufferedamountlow;
attribute EventHandler onerror;
attribute EventHandler onclosing;
attribute EventHandler onclose;
void close();
attribute EventHandler onmessage;
[SetterThrows] attribute DOMString binaryType;
[Throws] void send(USVString data);
[Throws] void send(Blob data);
[Throws] void send(ArrayBuffer data);
[Throws] void send(ArrayBufferView data);
};
// https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit
dictionary RTCDataChannelInit {
boolean ordered = true;
unsigned short maxPacketLifeTime;
unsigned short maxRetransmits;
USVString protocol = "";
boolean negotiated = false;
unsigned short id;
};
// https://www.w3.org/TR/webrtc/#dom-rtcdatachannelstate
enum RTCDataChannelState {
"connecting",
"open",
"closing",
"closed"
};

View file

@ -0,0 +1,15 @@
/* 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/. */
// https://w3c.github.io/webrtc-pc/#dom-rtcdatachannelevent
[Exposed=Window, Pref="dom.webrtc.enabled"]
interface RTCDataChannelEvent : Event {
constructor(DOMString type, RTCDataChannelEventInit eventInitDict);
readonly attribute RTCDataChannel channel;
};
dictionary RTCDataChannelEventInit : EventInit {
required RTCDataChannel channel;
};

View file

@ -0,0 +1,35 @@
/* 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/. */
// https://w3c.github.io/webrtc-pc/#dom-rtcerror
[Exposed=Window, Pref="dom.webrtc.enabled"]
interface RTCError : DOMException {
constructor(RTCErrorInit init, optional DOMString message = "");
readonly attribute RTCErrorDetailType errorDetail;
readonly attribute long? sdpLineNumber;
readonly attribute long? httpRequestStatusCode;
readonly attribute long? sctpCauseCode;
readonly attribute unsigned long? receivedAlert;
readonly attribute unsigned long? sentAlert;
};
dictionary RTCErrorInit {
required RTCErrorDetailType errorDetail;
long sdpLineNumber;
long httpRequestStatusCode;
long sctpCauseCode;
unsigned long receivedAlert;
unsigned long sentAlert;
};
enum RTCErrorDetailType {
"data-channel-failure",
"dtls-failure",
"fingerprint-failure",
"sctp-failure",
"sdp-syntax-error",
"hardware-encoder-not-available",
"hardware-encoder-error"
};

View file

@ -0,0 +1,15 @@
/* 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/. */
// https://w3c.github.io/webrtc-pc/#dom-rtcerrorevent
[Exposed=Window, Pref="dom.webrtc.enabled"]
interface RTCErrorEvent : Event {
constructor(DOMString type, RTCErrorEventInit eventInitDict);
[SameObject] readonly attribute RTCError error;
};
dictionary RTCErrorEventInit : EventInit {
required RTCError error;
};

View file

@ -126,3 +126,11 @@ partial interface RTCPeerConnection {
// optional RTCRtpTransceiverInit init);
attribute EventHandler ontrack;
};
// https://www.w3.org/TR/webrtc/#rtcpeerconnection-interface-extensions-0
partial interface RTCPeerConnection {
// readonly attribute RTCSctpTransport? sctp;
RTCDataChannel createDataChannel(USVString label,
optional RTCDataChannelInit dataChannelDict = {});
attribute EventHandler ondatachannel;
};

View file

@ -55,7 +55,7 @@ euclid = "0.20"
gfx = { path = "../gfx" }
gfx_traits = { path = "../gfx_traits" }
gleam = "0.11"
gstreamer = { version = "0.15", optional = true }
gstreamer = { version = "0.15", features = ["v1_16"], optional = true }
ipc-channel = "0.14"
keyboard-types = "0.5"
layout_thread_2013 = { path = "../layout_thread", optional = true }