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