Skip to main content

script/dom/webrtc/
rtcpeerconnection.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::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::realm::CurrentRealm;
11use js::rust::HandleObject;
12use rustc_hash::FxHashMap;
13use script_bindings::cell::DomRefCell;
14use script_bindings::reflector::reflect_weak_referenceable_dom_object_with_proto;
15use servo_media::ServoMedia;
16use servo_media::streams::MediaStreamType;
17use servo_media::streams::registry::MediaStreamId;
18use servo_media::webrtc::{
19    BundlePolicy, DataChannelEvent, DataChannelId, DataChannelState, GatheringState, IceCandidate,
20    IceConnectionState, SdpType, SessionDescription, SignalingState, WebRtcController,
21    WebRtcSignaller,
22};
23
24use crate::conversions::Convert;
25use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::RTCDataChannelInit;
26use crate::dom::bindings::codegen::Bindings::RTCIceCandidateBinding::RTCIceCandidateInit;
27use crate::dom::bindings::codegen::Bindings::RTCPeerConnectionBinding::{
28    RTCAnswerOptions, RTCBundlePolicy, RTCConfiguration, RTCIceConnectionState,
29    RTCIceGatheringState, RTCOfferOptions, RTCPeerConnectionMethods, RTCRtpTransceiverInit,
30    RTCSignalingState,
31};
32use crate::dom::bindings::codegen::Bindings::RTCSessionDescriptionBinding::{
33    RTCSdpType, RTCSessionDescriptionInit,
34};
35use crate::dom::bindings::codegen::UnionTypes::{MediaStreamTrackOrString, StringOrStringSequence};
36use crate::dom::bindings::error::{Error, Fallible};
37use crate::dom::bindings::inheritance::Castable;
38use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
39use crate::dom::bindings::reflector::DomGlobal;
40use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
41use crate::dom::bindings::str::USVString;
42use crate::dom::event::{Event, EventBubbles, EventCancelable};
43use crate::dom::eventtarget::EventTarget;
44use crate::dom::mediastream::MediaStream;
45use crate::dom::mediastreamtrack::MediaStreamTrack;
46use crate::dom::rtcdatachannel::RTCDataChannel;
47use crate::dom::rtcdatachannelevent::RTCDataChannelEvent;
48use crate::dom::rtcicecandidate::RTCIceCandidate;
49use crate::dom::rtcpeerconnectioniceevent::RTCPeerConnectionIceEvent;
50use crate::dom::rtcrtptransceiver::RTCRtpTransceiver;
51use crate::dom::rtcsessiondescription::RTCSessionDescription;
52use crate::dom::rtctrackevent::RTCTrackEvent;
53use crate::dom::window::Window;
54use crate::dom::{Promise, RootedPromise, TracedPromise};
55use crate::realms::enter_auto_realm;
56use crate::tasks::task_source::SendableTaskSource;
57
58#[dom_struct]
59pub(crate) struct RTCPeerConnection {
60    eventtarget: EventTarget,
61    #[no_trace]
62    controller: DomRefCell<Option<WebRtcController>>,
63    closed: Cell<bool>,
64    // Helps track state changes between the time createOffer/createAnswer
65    // is called and resolved
66    offer_answer_generation: Cell<u32>,
67    offer_promises: DomRefCell<Vec<TracedPromise>>,
68    answer_promises: DomRefCell<Vec<TracedPromise>>,
69    local_description: MutNullableDom<RTCSessionDescription>,
70    remote_description: MutNullableDom<RTCSessionDescription>,
71    gathering_state: Cell<RTCIceGatheringState>,
72    ice_connection_state: Cell<RTCIceConnectionState>,
73    signaling_state: Cell<RTCSignalingState>,
74    data_channels: DomRefCell<FxHashMap<DataChannelId, Dom<RTCDataChannel>>>,
75}
76
77struct RTCSignaller {
78    trusted: Trusted<RTCPeerConnection>,
79    task_source: SendableTaskSource,
80}
81
82impl WebRtcSignaller for RTCSignaller {
83    fn on_ice_candidate(&self, _: &WebRtcController, candidate: IceCandidate) {
84        let this = self.trusted.clone();
85        self.task_source.queue(task!(on_ice_candidate: move |cx| {
86            let this = this.root();
87            this.on_ice_candidate(cx, candidate);
88        }));
89    }
90
91    fn on_negotiation_needed(&self, _: &WebRtcController) {
92        let this = self.trusted.clone();
93        self.task_source
94            .queue(task!(on_negotiation_needed: move |cx| {
95                let this = this.root();
96                this.on_negotiation_needed(cx);
97            }));
98    }
99
100    fn update_gathering_state(&self, state: GatheringState) {
101        let this = self.trusted.clone();
102        self.task_source
103            .queue(task!(update_gathering_state: move |cx| {
104                let this = this.root();
105                this.update_gathering_state(cx, state);
106            }));
107    }
108
109    fn update_ice_connection_state(&self, state: IceConnectionState) {
110        let this = self.trusted.clone();
111        self.task_source
112            .queue(task!(update_ice_connection_state: move |cx| {
113                let this = this.root();
114                this.update_ice_connection_state(cx, state);
115            }));
116    }
117
118    fn update_signaling_state(&self, state: SignalingState) {
119        let this = self.trusted.clone();
120        self.task_source
121            .queue(task!(update_signaling_state: move |cx| {
122                let this = this.root();
123                this.update_signaling_state(cx, state);
124            }));
125    }
126
127    fn on_add_stream(&self, id: &MediaStreamId, ty: MediaStreamType) {
128        let this = self.trusted.clone();
129        let id = *id;
130        self.task_source.queue(task!(on_add_stream: move |cx| {
131            let this = this.root();
132            this.on_add_stream(cx, id, ty, );
133        }));
134    }
135
136    fn on_data_channel_event(
137        &self,
138        channel: DataChannelId,
139        event: DataChannelEvent,
140        _: &WebRtcController,
141    ) {
142        // XXX(ferjm) get label and options from channel properties.
143        let this = self.trusted.clone();
144        self.task_source
145            .queue(task!(on_data_channel_event: move |cx| {
146                let this = this.root();
147                let global = this.global();
148                let mut realm = enter_auto_realm(cx, &*global);
149                this.on_data_channel_event(&mut realm.current_realm(), channel, event);
150            }));
151    }
152
153    fn close(&self) {
154        // do nothing
155    }
156}
157
158impl RTCPeerConnection {
159    pub(crate) fn new_inherited() -> RTCPeerConnection {
160        RTCPeerConnection {
161            eventtarget: EventTarget::new_inherited(),
162            controller: DomRefCell::new(None),
163            closed: Cell::new(false),
164            offer_answer_generation: Cell::new(0),
165            offer_promises: DomRefCell::new(vec![]),
166            answer_promises: DomRefCell::new(vec![]),
167            local_description: Default::default(),
168            remote_description: Default::default(),
169            gathering_state: Cell::new(RTCIceGatheringState::New),
170            ice_connection_state: Cell::new(RTCIceConnectionState::New),
171            signaling_state: Cell::new(RTCSignalingState::Stable),
172            data_channels: DomRefCell::new(FxHashMap::default()),
173        }
174    }
175
176    fn new(
177        cx: &mut JSContext,
178        window: &Window,
179        proto: Option<HandleObject>,
180        config: &RTCConfiguration,
181    ) -> DomRoot<RTCPeerConnection> {
182        let this = reflect_weak_referenceable_dom_object_with_proto(
183            cx,
184            Rc::new(RTCPeerConnection::new_inherited()),
185            window,
186            proto,
187        );
188        let signaller = this.make_signaller();
189        *this.controller.safe_borrow_mut(cx) = Some(ServoMedia::get().create_webrtc(signaller));
190        if let Some(ref servers) = config.iceServers &&
191            let Some(server) = servers.first()
192        {
193            let server = match server.urls {
194                StringOrStringSequence::String(ref s) => Some(s.clone()),
195                StringOrStringSequence::StringSequence(ref s) => s.first().cloned(),
196            };
197            if let Some(server) = server {
198                let policy = match config.bundlePolicy {
199                    RTCBundlePolicy::Balanced => BundlePolicy::Balanced,
200                    RTCBundlePolicy::Max_compat => BundlePolicy::MaxCompat,
201                    RTCBundlePolicy::Max_bundle => BundlePolicy::MaxBundle,
202                };
203                this.controller
204                    .borrow()
205                    .as_ref()
206                    .unwrap()
207                    .configure(String::from(server), policy);
208            }
209        }
210        this
211    }
212
213    pub(crate) fn get_webrtc_controller(&self) -> &DomRefCell<Option<WebRtcController>> {
214        &self.controller
215    }
216
217    fn make_signaller(&self) -> Box<dyn WebRtcSignaller> {
218        let trusted = Trusted::new(self);
219        Box::new(RTCSignaller {
220            trusted,
221            task_source: self.global().task_manager().networking_task_source().into(),
222        })
223    }
224
225    fn on_ice_candidate(&self, cx: &mut JSContext, candidate: IceCandidate) {
226        if self.closed.get() {
227            return;
228        }
229        let candidate = RTCIceCandidate::new(
230            cx,
231            self.global().as_window(),
232            candidate.candidate.into(),
233            None,
234            Some(candidate.sdp_mline_index as u16),
235            None,
236        );
237        let event = RTCPeerConnectionIceEvent::new(
238            cx,
239            self.global().as_window(),
240            atom!("icecandidate"),
241            Some(&candidate),
242            None,
243            true,
244        );
245        event.upcast::<Event>().fire(cx, self.upcast());
246    }
247
248    fn on_negotiation_needed(&self, cx: &mut JSContext) {
249        if self.closed.get() {
250            return;
251        }
252        let event = Event::new(
253            cx,
254            &self.global(),
255            atom!("negotiationneeded"),
256            EventBubbles::DoesNotBubble,
257            EventCancelable::NotCancelable,
258        );
259        event.upcast::<Event>().fire(cx, self.upcast());
260    }
261
262    fn on_add_stream(&self, cx: &mut JSContext, id: MediaStreamId, ty: MediaStreamType) {
263        if self.closed.get() {
264            return;
265        }
266        let track = MediaStreamTrack::new(cx, &self.global(), id, ty);
267        let event = RTCTrackEvent::new(
268            cx,
269            self.global().as_window(),
270            atom!("track"),
271            false,
272            false,
273            &track,
274        );
275        event.upcast::<Event>().fire(cx, self.upcast());
276    }
277
278    fn on_data_channel_event(
279        &self,
280        cx: &mut CurrentRealm,
281        channel_id: DataChannelId,
282        event: DataChannelEvent,
283    ) {
284        if self.closed.get() {
285            return;
286        }
287
288        match event {
289            DataChannelEvent::NewChannel => {
290                let channel = RTCDataChannel::new(
291                    cx,
292                    &self.global(),
293                    self,
294                    USVString::from(String::new()),
295                    &RTCDataChannelInit::empty(),
296                    Some(channel_id),
297                );
298
299                let event = RTCDataChannelEvent::new(
300                    cx,
301                    self.global().as_window(),
302                    atom!("datachannel"),
303                    false,
304                    false,
305                    &channel,
306                );
307                event.upcast::<Event>().fire(cx, self.upcast());
308            },
309            _ => {
310                let channel: DomRoot<RTCDataChannel> =
311                    if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
312                        DomRoot::from_ref(&**channel)
313                    } else {
314                        warn!(
315                            "Got an event for an unregistered data channel {:?}",
316                            channel_id
317                        );
318                        return;
319                    };
320
321                match event {
322                    DataChannelEvent::Open => channel.on_open(cx),
323                    DataChannelEvent::Close => channel.on_close(cx),
324                    DataChannelEvent::Error(error) => channel.on_error(cx, error),
325                    DataChannelEvent::OnMessage(message) => channel.on_message(cx, message),
326                    DataChannelEvent::StateChange(state) => channel.on_state_change(cx, state),
327                    DataChannelEvent::NewChannel => unreachable!(),
328                }
329            },
330        };
331    }
332
333    pub(crate) fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
334        if self
335            .data_channels
336            .borrow_mut()
337            .insert(id, Dom::from_ref(channel))
338            .is_some()
339        {
340            warn!("Data channel already registered {:?}", id);
341        }
342    }
343
344    pub(crate) fn unregister_data_channel(&self, id: &DataChannelId) {
345        self.data_channels.borrow_mut().remove(id);
346    }
347
348    /// <https://www.w3.org/TR/webrtc/#update-ice-gathering-state>
349    fn update_gathering_state(&self, cx: &mut JSContext, state: GatheringState) {
350        // step 1
351        if self.closed.get() {
352            return;
353        }
354
355        // step 2 (state derivation already done by gstreamer)
356        let state: RTCIceGatheringState = state.convert();
357
358        // step 3
359        if state == self.gathering_state.get() {
360            return;
361        }
362
363        // step 4
364        self.gathering_state.set(state);
365
366        // step 5
367        let event = Event::new(
368            cx,
369            &self.global(),
370            atom!("icegatheringstatechange"),
371            EventBubbles::DoesNotBubble,
372            EventCancelable::NotCancelable,
373        );
374        event.upcast::<Event>().fire(cx, self.upcast());
375
376        // step 6
377        if state == RTCIceGatheringState::Complete {
378            let event = RTCPeerConnectionIceEvent::new(
379                cx,
380                self.global().as_window(),
381                atom!("icecandidate"),
382                None,
383                None,
384                true,
385            );
386            event.upcast::<Event>().fire(cx, self.upcast());
387        }
388    }
389
390    /// <https://www.w3.org/TR/webrtc/#update-ice-connection-state>
391    fn update_ice_connection_state(&self, cx: &mut JSContext, state: IceConnectionState) {
392        // step 1
393        if self.closed.get() {
394            return;
395        }
396
397        // step 2 (state derivation already done by gstreamer)
398        let state: RTCIceConnectionState = state.convert();
399
400        // step 3
401        if state == self.ice_connection_state.get() {
402            return;
403        }
404
405        // step 4
406        self.ice_connection_state.set(state);
407
408        // step 5
409        let event = Event::new(
410            cx,
411            &self.global(),
412            atom!("iceconnectionstatechange"),
413            EventBubbles::DoesNotBubble,
414            EventCancelable::NotCancelable,
415        );
416        event.upcast::<Event>().fire(cx, self.upcast());
417    }
418
419    fn update_signaling_state(&self, cx: &mut JSContext, state: SignalingState) {
420        if self.closed.get() {
421            return;
422        }
423
424        let state: RTCSignalingState = state.convert();
425
426        if state == self.signaling_state.get() {
427            return;
428        }
429
430        self.signaling_state.set(state);
431
432        let event = Event::new(
433            cx,
434            &self.global(),
435            atom!("signalingstatechange"),
436            EventBubbles::DoesNotBubble,
437            EventCancelable::NotCancelable,
438        );
439        event.upcast::<Event>().fire(cx, self.upcast());
440    }
441
442    fn create_offer(&self) {
443        let generation = self.offer_answer_generation.get();
444        let task_source = self
445            .global()
446            .task_manager()
447            .networking_task_source()
448            .to_sendable();
449        let this = Trusted::new(self);
450        self.controller
451            .borrow_mut()
452            .as_ref()
453            .unwrap()
454            .create_offer(Box::new(move |desc: SessionDescription| {
455                task_source.queue(task!(offer_created: move |cx| {
456                    let this = this.root();
457                    if this.offer_answer_generation.get() != generation {
458                        // the state has changed since we last created the offer,
459                        // create a fresh one
460                        this.create_offer();
461                    } else {
462                        let init: RTCSessionDescriptionInit = desc.convert();
463                        for promise in this.offer_promises.borrow().iter() {
464                            promise.resolve_native(cx, &init);
465                        }
466                        this.offer_promises.safe_borrow_mut(cx.no_gc()).clear();
467                    }
468                }));
469            }));
470    }
471
472    fn create_answer(&self) {
473        let generation = self.offer_answer_generation.get();
474        let task_source = self
475            .global()
476            .task_manager()
477            .networking_task_source()
478            .to_sendable();
479        let this = Trusted::new(self);
480        self.controller
481            .borrow_mut()
482            .as_ref()
483            .unwrap()
484            .create_answer(Box::new(move |desc: SessionDescription| {
485                task_source.queue(task!(answer_created: move |cx| {
486                    let this = this.root();
487                    if this.offer_answer_generation.get() != generation {
488                        // the state has changed since we last created the offer,
489                        // create a fresh one
490                        this.create_answer();
491                    } else {
492                        let init: RTCSessionDescriptionInit = desc.convert();
493                        for promise in this.answer_promises.borrow().iter() {
494                            promise.resolve_native(cx, &init);
495                        }
496                        this.answer_promises.safe_borrow_mut(cx.no_gc()).clear();
497                    }
498                }));
499            }));
500    }
501}
502
503impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
504    /// <https://w3c.github.io/webrtc-pc/#dom-peerconnection>
505    fn Constructor(
506        cx: &mut JSContext,
507        window: &Window,
508        proto: Option<HandleObject>,
509        config: &RTCConfiguration,
510    ) -> Fallible<DomRoot<RTCPeerConnection>> {
511        Ok(RTCPeerConnection::new(cx, window, proto, config))
512    }
513
514    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icecandidate
515    event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate);
516
517    // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ontrack
518    event_handler!(track, GetOntrack, SetOntrack);
519
520    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-iceconnectionstatechange
521    event_handler!(
522        iceconnectionstatechange,
523        GetOniceconnectionstatechange,
524        SetOniceconnectionstatechange
525    );
526
527    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icegatheringstatechange
528    event_handler!(
529        icegatheringstatechange,
530        GetOnicegatheringstatechange,
531        SetOnicegatheringstatechange
532    );
533
534    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-onnegotiationneeded
535    event_handler!(
536        negotiationneeded,
537        GetOnnegotiationneeded,
538        SetOnnegotiationneeded
539    );
540
541    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-signalingstatechange
542    event_handler!(
543        signalingstatechange,
544        GetOnsignalingstatechange,
545        SetOnsignalingstatechange
546    );
547
548    // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ondatachannel
549    event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
550
551    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addicecandidate>
552    fn AddIceCandidate(
553        &self,
554        current_realm: &mut CurrentRealm,
555        candidate: &RTCIceCandidateInit,
556    ) -> RootedPromise {
557        let p = Promise::new_in_realm(current_realm);
558        if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
559            p.reject_error(
560                current_realm,
561                Error::Type(c"one of sdpMid and sdpMLineIndex must be set".to_owned()),
562            );
563            return p;
564        }
565
566        // XXXManishearth add support for sdpMid
567        if candidate.sdpMLineIndex.is_none() {
568            p.reject_error(
569                current_realm,
570                Error::Type(c"servo only supports sdpMLineIndex right now".to_owned()),
571            );
572            return p;
573        }
574
575        // XXXManishearth this should be enqueued
576        // https://w3c.github.io/webrtc-pc/#enqueue-an-operation
577
578        self.controller
579            .safe_borrow_mut(current_realm.no_gc())
580            .as_ref()
581            .unwrap()
582            .add_ice_candidate(IceCandidate {
583                sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
584                candidate: candidate.candidate.to_string(),
585            });
586
587        // XXXManishearth add_ice_candidate should have a callback
588        p.resolve_native(current_realm, &());
589        p
590    }
591
592    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer>
593    fn CreateOffer(
594        &self,
595        current_realm: &mut CurrentRealm,
596        _options: &RTCOfferOptions,
597    ) -> RootedPromise {
598        let p = Promise::new_in_realm(current_realm);
599        if self.closed.get() {
600            p.reject_error(current_realm, Error::InvalidState(None));
601            return p;
602        }
603        self.offer_promises
604            .safe_borrow_mut(current_realm.no_gc())
605            .push(p.to_traced());
606        self.create_offer();
607        p
608    }
609
610    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer>
611    fn CreateAnswer(
612        &self,
613        current_realm: &mut CurrentRealm,
614        _options: &RTCAnswerOptions,
615    ) -> RootedPromise {
616        let p = Promise::new_in_realm(current_realm);
617        if self.closed.get() {
618            p.reject_error(current_realm, Error::InvalidState(None));
619            return p;
620        }
621        self.answer_promises
622            .safe_borrow_mut(current_realm.no_gc())
623            .push(p.to_traced());
624        self.create_answer();
625        p
626    }
627
628    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-localdescription>
629    fn GetLocalDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
630        self.local_description.get()
631    }
632
633    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-remotedescription>
634    fn GetRemoteDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
635        self.remote_description.get()
636    }
637
638    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-setlocaldescription>
639    fn SetLocalDescription(
640        &self,
641        current_realm: &mut CurrentRealm,
642        desc: &RTCSessionDescriptionInit,
643    ) -> RootedPromise {
644        // XXXManishearth validate the current state
645        let p = Promise::new_in_realm(current_realm);
646        let this = Trusted::new(self);
647        let desc: SessionDescription = desc.convert();
648        let trusted_promise = TrustedPromise::from(&p);
649        let task_source = self
650            .global()
651            .task_manager()
652            .networking_task_source()
653            .to_sendable();
654        self.controller
655            .borrow()
656            .as_ref()
657            .unwrap()
658            .set_local_description(
659                desc.clone(),
660                Box::new(move || {
661                    task_source.queue(task!(local_description_set: move |current_realm| {
662                        // XXXManishearth spec actually asks for an intricate
663                        // dance between pending/current local/remote descriptions
664                        let this = this.root();
665                        let desc = desc.convert();
666                        let desc = RTCSessionDescription::new(
667                            current_realm,
668                            this.global().as_window(),
669                            None,
670                            desc.type_,
671                            desc.sdp,
672                        );
673                        this.local_description.set(Some(&desc));
674                        trusted_promise.root(current_realm).resolve_native(current_realm, &())
675                    }));
676                }),
677            );
678        p
679    }
680
681    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-setremotedescription>
682    fn SetRemoteDescription(
683        &self,
684        current_realm: &mut CurrentRealm,
685        desc: &RTCSessionDescriptionInit,
686    ) -> RootedPromise {
687        // XXXManishearth validate the current state
688        let p = Promise::new_in_realm(current_realm);
689        let this = Trusted::new(self);
690        let desc: SessionDescription = desc.convert();
691        let trusted_promise = TrustedPromise::from(&p);
692        let task_source = self
693            .global()
694            .task_manager()
695            .networking_task_source()
696            .to_sendable();
697        self.controller
698            .borrow()
699            .as_ref()
700            .unwrap()
701            .set_remote_description(
702                desc.clone(),
703                Box::new(move || {
704                    task_source.queue(task!(remote_description_set: move |current_realm| {
705                        // XXXManishearth spec actually asks for an intricate
706                        // dance between pending/current local/remote descriptions
707                        let this = this.root();
708                        let desc = desc.convert();
709                        let desc = RTCSessionDescription::new(
710                            current_realm,
711                            this.global().as_window(),
712                            None,
713                            desc.type_,
714                            desc.sdp,
715                        );
716                        this.remote_description.set(Some(&desc));
717                        trusted_promise.root(current_realm).resolve_native(current_realm, &())
718                    }));
719                }),
720            );
721        p
722    }
723
724    /// <https://w3c.github.io/webrtc-pc/#legacy-interface-extensions>
725    fn AddStream(&self, stream: &MediaStream) {
726        for track in &*stream.get_tracks() {
727            self.controller
728                .borrow()
729                .as_ref()
730                .unwrap()
731                .add_stream(&track.id());
732        }
733    }
734
735    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-icegatheringstate>
736    fn IceGatheringState(&self) -> RTCIceGatheringState {
737        self.gathering_state.get()
738    }
739
740    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-iceconnectionstate>
741    fn IceConnectionState(&self) -> RTCIceConnectionState {
742        self.ice_connection_state.get()
743    }
744
745    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-signalingstate>
746    fn SignalingState(&self) -> RTCSignalingState {
747        self.signaling_state.get()
748    }
749
750    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close>
751    fn Close(&self, cx: &mut JSContext) {
752        // Step 1
753        if self.closed.get() {
754            return;
755        }
756        // Step 2
757        self.closed.set(true);
758
759        // Step 4
760        self.signaling_state.set(RTCSignalingState::Closed);
761
762        // Step 5 handled by backend
763        self.controller
764            .safe_borrow_mut(cx.no_gc())
765            .as_ref()
766            .unwrap()
767            .quit();
768
769        // Step 6
770        for val in self.data_channels.borrow().values() {
771            val.on_state_change(cx, DataChannelState::Closed);
772        }
773
774        // Step 7-10
775        // (no current support for transports, etc)
776
777        // Step 11
778        self.ice_connection_state.set(RTCIceConnectionState::Closed);
779
780        // Step 11
781        // (no current support for connection state)
782    }
783
784    /// <https://www.w3.org/TR/webrtc/#dom-peerconnection-createdatachannel>
785    fn CreateDataChannel(
786        &self,
787        cx: &mut JSContext,
788        label: USVString,
789        init: &RTCDataChannelInit,
790    ) -> DomRoot<RTCDataChannel> {
791        RTCDataChannel::new(cx, &self.global(), self, label, init, None)
792    }
793
794    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver>
795    fn AddTransceiver(
796        &self,
797        cx: &mut JSContext,
798        _track_or_kind: MediaStreamTrackOrString,
799        init: &RTCRtpTransceiverInit,
800    ) -> DomRoot<RTCRtpTransceiver> {
801        RTCRtpTransceiver::new(cx, &self.global(), init.direction)
802    }
803}
804
805impl Convert<RTCSessionDescriptionInit> for SessionDescription {
806    fn convert(self) -> RTCSessionDescriptionInit {
807        let type_ = match self.type_ {
808            SdpType::Answer => RTCSdpType::Answer,
809            SdpType::Offer => RTCSdpType::Offer,
810            SdpType::Pranswer => RTCSdpType::Pranswer,
811            SdpType::Rollback => RTCSdpType::Rollback,
812        };
813        RTCSessionDescriptionInit {
814            type_,
815            sdp: self.sdp.into(),
816        }
817    }
818}
819
820impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
821    fn convert(self) -> SessionDescription {
822        let type_ = match self.type_ {
823            RTCSdpType::Answer => SdpType::Answer,
824            RTCSdpType::Offer => SdpType::Offer,
825            RTCSdpType::Pranswer => SdpType::Pranswer,
826            RTCSdpType::Rollback => SdpType::Rollback,
827        };
828        SessionDescription {
829            type_,
830            sdp: self.sdp.to_string(),
831        }
832    }
833}
834
835impl Convert<RTCIceGatheringState> for GatheringState {
836    fn convert(self) -> RTCIceGatheringState {
837        match self {
838            GatheringState::New => RTCIceGatheringState::New,
839            GatheringState::Gathering => RTCIceGatheringState::Gathering,
840            GatheringState::Complete => RTCIceGatheringState::Complete,
841        }
842    }
843}
844
845impl Convert<RTCIceConnectionState> for IceConnectionState {
846    fn convert(self) -> RTCIceConnectionState {
847        match self {
848            IceConnectionState::New => RTCIceConnectionState::New,
849            IceConnectionState::Checking => RTCIceConnectionState::Checking,
850            IceConnectionState::Connected => RTCIceConnectionState::Connected,
851            IceConnectionState::Completed => RTCIceConnectionState::Completed,
852            IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
853            IceConnectionState::Failed => RTCIceConnectionState::Failed,
854            IceConnectionState::Closed => RTCIceConnectionState::Closed,
855        }
856    }
857}
858
859impl Convert<RTCSignalingState> for SignalingState {
860    fn convert(self) -> RTCSignalingState {
861        match self {
862            SignalingState::Stable => RTCSignalingState::Stable,
863            SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
864            SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
865            SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
866            SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
867            SignalingState::Closed => RTCSignalingState::Closed,
868        }
869    }
870}