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