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 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    #[conditional_malloc_size_of]
67    offer_promises: DomRefCell<Vec<Rc<Promise>>>,
68    #[conditional_malloc_size_of]
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<FxHashMap<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::deprecated_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::deprecated_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::deprecated_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::deprecated_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::deprecated_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 |cx| {
133            let this = this.root();
134            this.on_add_stream(cx, id, ty, );
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::deprecated_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(FxHashMap::default()),
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(
265        &self,
266        cx: &mut js::context::JSContext,
267        id: MediaStreamId,
268        ty: MediaStreamType,
269    ) {
270        if self.closed.get() {
271            return;
272        }
273        let track = MediaStreamTrack::new(cx, &self.global(), id, ty);
274        let event = RTCTrackEvent::new(
275            cx,
276            self.global().as_window(),
277            atom!("track"),
278            false,
279            false,
280            &track,
281        );
282        event
283            .upcast::<Event>()
284            .fire(self.upcast(), CanGc::from_cx(cx));
285    }
286
287    fn on_data_channel_event(
288        &self,
289        channel_id: DataChannelId,
290        event: DataChannelEvent,
291        can_gc: CanGc,
292    ) {
293        if self.closed.get() {
294            return;
295        }
296
297        match event {
298            DataChannelEvent::NewChannel => {
299                let channel = RTCDataChannel::new(
300                    &self.global(),
301                    self,
302                    USVString::from("".to_owned()),
303                    &RTCDataChannelInit::empty(),
304                    Some(channel_id),
305                    can_gc,
306                );
307
308                let event = RTCDataChannelEvent::new(
309                    self.global().as_window(),
310                    atom!("datachannel"),
311                    false,
312                    false,
313                    &channel,
314                    can_gc,
315                );
316                event.upcast::<Event>().fire(self.upcast(), can_gc);
317            },
318            _ => {
319                let channel: DomRoot<RTCDataChannel> =
320                    if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
321                        DomRoot::from_ref(&**channel)
322                    } else {
323                        warn!(
324                            "Got an event for an unregistered data channel {:?}",
325                            channel_id
326                        );
327                        return;
328                    };
329
330                match event {
331                    DataChannelEvent::Open => channel.on_open(can_gc),
332                    DataChannelEvent::Close => channel.on_close(can_gc),
333                    DataChannelEvent::Error(error) => channel.on_error(error, can_gc),
334                    DataChannelEvent::OnMessage(message) => channel.on_message(message, can_gc),
335                    DataChannelEvent::StateChange(state) => channel.on_state_change(state, can_gc),
336                    DataChannelEvent::NewChannel => unreachable!(),
337                }
338            },
339        };
340    }
341
342    pub(crate) fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
343        if self
344            .data_channels
345            .borrow_mut()
346            .insert(id, Dom::from_ref(channel))
347            .is_some()
348        {
349            warn!("Data channel already registered {:?}", id);
350        }
351    }
352
353    pub(crate) fn unregister_data_channel(&self, id: &DataChannelId) {
354        self.data_channels.borrow_mut().remove(id);
355    }
356
357    /// <https://www.w3.org/TR/webrtc/#update-ice-gathering-state>
358    fn update_gathering_state(&self, state: GatheringState, can_gc: CanGc) {
359        // step 1
360        if self.closed.get() {
361            return;
362        }
363
364        // step 2 (state derivation already done by gstreamer)
365        let state: RTCIceGatheringState = state.convert();
366
367        // step 3
368        if state == self.gathering_state.get() {
369            return;
370        }
371
372        // step 4
373        self.gathering_state.set(state);
374
375        // step 5
376        let event = Event::new(
377            &self.global(),
378            atom!("icegatheringstatechange"),
379            EventBubbles::DoesNotBubble,
380            EventCancelable::NotCancelable,
381            can_gc,
382        );
383        event.upcast::<Event>().fire(self.upcast(), can_gc);
384
385        // step 6
386        if state == RTCIceGatheringState::Complete {
387            let event = RTCPeerConnectionIceEvent::new(
388                self.global().as_window(),
389                atom!("icecandidate"),
390                None,
391                None,
392                true,
393                can_gc,
394            );
395            event.upcast::<Event>().fire(self.upcast(), can_gc);
396        }
397    }
398
399    /// <https://www.w3.org/TR/webrtc/#update-ice-connection-state>
400    fn update_ice_connection_state(&self, state: IceConnectionState, can_gc: CanGc) {
401        // step 1
402        if self.closed.get() {
403            return;
404        }
405
406        // step 2 (state derivation already done by gstreamer)
407        let state: RTCIceConnectionState = state.convert();
408
409        // step 3
410        if state == self.ice_connection_state.get() {
411            return;
412        }
413
414        // step 4
415        self.ice_connection_state.set(state);
416
417        // step 5
418        let event = Event::new(
419            &self.global(),
420            atom!("iceconnectionstatechange"),
421            EventBubbles::DoesNotBubble,
422            EventCancelable::NotCancelable,
423            can_gc,
424        );
425        event.upcast::<Event>().fire(self.upcast(), can_gc);
426    }
427
428    fn update_signaling_state(&self, state: SignalingState, can_gc: CanGc) {
429        if self.closed.get() {
430            return;
431        }
432
433        let state: RTCSignalingState = state.convert();
434
435        if state == self.signaling_state.get() {
436            return;
437        }
438
439        self.signaling_state.set(state);
440
441        let event = Event::new(
442            &self.global(),
443            atom!("signalingstatechange"),
444            EventBubbles::DoesNotBubble,
445            EventCancelable::NotCancelable,
446            can_gc,
447        );
448        event.upcast::<Event>().fire(self.upcast(), can_gc);
449    }
450
451    fn create_offer(&self) {
452        let generation = self.offer_answer_generation.get();
453        let task_source = self
454            .global()
455            .task_manager()
456            .networking_task_source()
457            .to_sendable();
458        let this = Trusted::new(self);
459        self.controller
460            .borrow_mut()
461            .as_ref()
462            .unwrap()
463            .create_offer(Box::new(move |desc: SessionDescription| {
464                task_source.queue(task!(offer_created: move || {
465                    let this = this.root();
466                    if this.offer_answer_generation.get() != generation {
467                        // the state has changed since we last created the offer,
468                        // create a fresh one
469                        this.create_offer();
470                    } else {
471                        let init: RTCSessionDescriptionInit = desc.convert();
472                        for promise in this.offer_promises.borrow_mut().drain(..) {
473                            promise.resolve_native(&init, CanGc::deprecated_note());
474                        }
475                    }
476                }));
477            }));
478    }
479
480    fn create_answer(&self) {
481        let generation = self.offer_answer_generation.get();
482        let task_source = self
483            .global()
484            .task_manager()
485            .networking_task_source()
486            .to_sendable();
487        let this = Trusted::new(self);
488        self.controller
489            .borrow_mut()
490            .as_ref()
491            .unwrap()
492            .create_answer(Box::new(move |desc: SessionDescription| {
493                task_source.queue(task!(answer_created: move || {
494                    let this = this.root();
495                    if this.offer_answer_generation.get() != generation {
496                        // the state has changed since we last created the offer,
497                        // create a fresh one
498                        this.create_answer();
499                    } else {
500                        let init: RTCSessionDescriptionInit = desc.convert();
501                        for promise in this.answer_promises.borrow_mut().drain(..) {
502                            promise.resolve_native(&init, CanGc::deprecated_note());
503                        }
504                    }
505                }));
506            }));
507    }
508}
509
510impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
511    /// <https://w3c.github.io/webrtc-pc/#dom-peerconnection>
512    fn Constructor(
513        window: &Window,
514        proto: Option<HandleObject>,
515        can_gc: CanGc,
516        config: &RTCConfiguration,
517    ) -> Fallible<DomRoot<RTCPeerConnection>> {
518        Ok(RTCPeerConnection::new(window, proto, config, can_gc))
519    }
520
521    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icecandidate
522    event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate);
523
524    // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ontrack
525    event_handler!(track, GetOntrack, SetOntrack);
526
527    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-iceconnectionstatechange
528    event_handler!(
529        iceconnectionstatechange,
530        GetOniceconnectionstatechange,
531        SetOniceconnectionstatechange
532    );
533
534    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icegatheringstatechange
535    event_handler!(
536        icegatheringstatechange,
537        GetOnicegatheringstatechange,
538        SetOnicegatheringstatechange
539    );
540
541    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-onnegotiationneeded
542    event_handler!(
543        negotiationneeded,
544        GetOnnegotiationneeded,
545        SetOnnegotiationneeded
546    );
547
548    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-signalingstatechange
549    event_handler!(
550        signalingstatechange,
551        GetOnsignalingstatechange,
552        SetOnsignalingstatechange
553    );
554
555    // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ondatachannel
556    event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
557
558    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addicecandidate>
559    fn AddIceCandidate(
560        &self,
561        candidate: &RTCIceCandidateInit,
562        comp: InRealm,
563        can_gc: CanGc,
564    ) -> Rc<Promise> {
565        let p = Promise::new_in_current_realm(comp, can_gc);
566        if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
567            p.reject_error(
568                Error::Type(c"one of sdpMid and sdpMLineIndex must be set".to_owned()),
569                can_gc,
570            );
571            return p;
572        }
573
574        // XXXManishearth add support for sdpMid
575        if candidate.sdpMLineIndex.is_none() {
576            p.reject_error(
577                Error::Type(c"servo only supports sdpMLineIndex right now".to_owned()),
578                can_gc,
579            );
580            return p;
581        }
582
583        // XXXManishearth this should be enqueued
584        // https://w3c.github.io/webrtc-pc/#enqueue-an-operation
585
586        self.controller
587            .borrow_mut()
588            .as_ref()
589            .unwrap()
590            .add_ice_candidate(IceCandidate {
591                sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
592                candidate: candidate.candidate.to_string(),
593            });
594
595        // XXXManishearth add_ice_candidate should have a callback
596        p.resolve_native(&(), can_gc);
597        p
598    }
599
600    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer>
601    fn CreateOffer(&self, _options: &RTCOfferOptions, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
602        let p = Promise::new_in_current_realm(comp, can_gc);
603        if self.closed.get() {
604            p.reject_error(Error::InvalidState(None), can_gc);
605            return p;
606        }
607        self.offer_promises.borrow_mut().push(p.clone());
608        self.create_offer();
609        p
610    }
611
612    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer>
613    fn CreateAnswer(
614        &self,
615        _options: &RTCAnswerOptions,
616        comp: InRealm,
617        can_gc: CanGc,
618    ) -> Rc<Promise> {
619        let p = Promise::new_in_current_realm(comp, can_gc);
620        if self.closed.get() {
621            p.reject_error(Error::InvalidState(None), can_gc);
622            return p;
623        }
624        self.answer_promises.borrow_mut().push(p.clone());
625        self.create_answer();
626        p
627    }
628
629    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-localdescription>
630    fn GetLocalDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
631        self.local_description.get()
632    }
633
634    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-remotedescription>
635    fn GetRemoteDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
636        self.remote_description.get()
637    }
638
639    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-setlocaldescription>
640    fn SetLocalDescription(
641        &self,
642        desc: &RTCSessionDescriptionInit,
643        comp: InRealm,
644        can_gc: CanGc,
645    ) -> Rc<Promise> {
646        // XXXManishearth validate the current state
647        let p = Promise::new_in_current_realm(comp, can_gc);
648        let this = Trusted::new(self);
649        let desc: SessionDescription = desc.convert();
650        let trusted_promise = TrustedPromise::new(p.clone());
651        let task_source = self
652            .global()
653            .task_manager()
654            .networking_task_source()
655            .to_sendable();
656        self.controller
657            .borrow_mut()
658            .as_ref()
659            .unwrap()
660            .set_local_description(
661                desc.clone(),
662                Box::new(move || {
663                    task_source.queue(task!(local_description_set: move || {
664                        // XXXManishearth spec actually asks for an intricate
665                        // dance between pending/current local/remote descriptions
666                        let this = this.root();
667                        let desc = desc.convert();
668                        let desc = RTCSessionDescription::Constructor(
669                            this.global().as_window(),
670                            None,
671                            CanGc::deprecated_note(),
672                            &desc,
673                        ).unwrap();
674                        this.local_description.set(Some(&desc));
675                        trusted_promise.root().resolve_native(&(), CanGc::deprecated_note())
676                    }));
677                }),
678            );
679        p
680    }
681
682    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-setremotedescription>
683    fn SetRemoteDescription(
684        &self,
685        desc: &RTCSessionDescriptionInit,
686        comp: InRealm,
687        can_gc: CanGc,
688    ) -> Rc<Promise> {
689        // XXXManishearth validate the current state
690        let p = Promise::new_in_current_realm(comp, can_gc);
691        let this = Trusted::new(self);
692        let desc: SessionDescription = desc.convert();
693        let trusted_promise = TrustedPromise::new(p.clone());
694        let task_source = self
695            .global()
696            .task_manager()
697            .networking_task_source()
698            .to_sendable();
699        self.controller
700            .borrow_mut()
701            .as_ref()
702            .unwrap()
703            .set_remote_description(
704                desc.clone(),
705                Box::new(move || {
706                    task_source.queue(task!(remote_description_set: move || {
707                        // XXXManishearth spec actually asks for an intricate
708                        // dance between pending/current local/remote descriptions
709                        let this = this.root();
710                        let desc = desc.convert();
711                        let desc = RTCSessionDescription::Constructor(
712                            this.global().as_window(),
713                            None,
714                            CanGc::deprecated_note(),
715                            &desc,
716                        ).unwrap();
717                        this.remote_description.set(Some(&desc));
718                        trusted_promise.root().resolve_native(&(), CanGc::deprecated_note())
719                    }));
720                }),
721            );
722        p
723    }
724
725    /// <https://w3c.github.io/webrtc-pc/#legacy-interface-extensions>
726    fn AddStream(&self, stream: &MediaStream) {
727        for track in &*stream.get_tracks() {
728            self.controller
729                .borrow()
730                .as_ref()
731                .unwrap()
732                .add_stream(&track.id());
733        }
734    }
735
736    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-icegatheringstate>
737    fn IceGatheringState(&self) -> RTCIceGatheringState {
738        self.gathering_state.get()
739    }
740
741    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-iceconnectionstate>
742    fn IceConnectionState(&self) -> RTCIceConnectionState {
743        self.ice_connection_state.get()
744    }
745
746    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-signalingstate>
747    fn SignalingState(&self) -> RTCSignalingState {
748        self.signaling_state.get()
749    }
750
751    /// <https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close>
752    fn Close(&self, can_gc: CanGc) {
753        // Step 1
754        if self.closed.get() {
755            return;
756        }
757        // Step 2
758        self.closed.set(true);
759
760        // Step 4
761        self.signaling_state.set(RTCSignalingState::Closed);
762
763        // Step 5 handled by backend
764        self.controller.borrow_mut().as_ref().unwrap().quit();
765
766        // Step 6
767        for (_, val) in self.data_channels.borrow().iter() {
768            val.on_state_change(DataChannelState::Closed, can_gc);
769        }
770
771        // Step 7-10
772        // (no current support for transports, etc)
773
774        // Step 11
775        self.ice_connection_state.set(RTCIceConnectionState::Closed);
776
777        // Step 11
778        // (no current support for connection state)
779    }
780
781    /// <https://www.w3.org/TR/webrtc/#dom-peerconnection-createdatachannel>
782    fn CreateDataChannel(
783        &self,
784        label: USVString,
785        init: &RTCDataChannelInit,
786    ) -> DomRoot<RTCDataChannel> {
787        RTCDataChannel::new(
788            &self.global(),
789            self,
790            label,
791            init,
792            None,
793            CanGc::deprecated_note(),
794        )
795    }
796
797    /// <https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver>
798    fn AddTransceiver(
799        &self,
800        _track_or_kind: MediaStreamTrackOrString,
801        init: &RTCRtpTransceiverInit,
802    ) -> DomRoot<RTCRtpTransceiver> {
803        RTCRtpTransceiver::new(&self.global(), init.direction, CanGc::deprecated_note())
804    }
805}
806
807impl Convert<RTCSessionDescriptionInit> for SessionDescription {
808    fn convert(self) -> RTCSessionDescriptionInit {
809        let type_ = match self.type_ {
810            SdpType::Answer => RTCSdpType::Answer,
811            SdpType::Offer => RTCSdpType::Offer,
812            SdpType::Pranswer => RTCSdpType::Pranswer,
813            SdpType::Rollback => RTCSdpType::Rollback,
814        };
815        RTCSessionDescriptionInit {
816            type_,
817            sdp: self.sdp.into(),
818        }
819    }
820}
821
822impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
823    fn convert(self) -> SessionDescription {
824        let type_ = match self.type_ {
825            RTCSdpType::Answer => SdpType::Answer,
826            RTCSdpType::Offer => SdpType::Offer,
827            RTCSdpType::Pranswer => SdpType::Pranswer,
828            RTCSdpType::Rollback => SdpType::Rollback,
829        };
830        SessionDescription {
831            type_,
832            sdp: self.sdp.to_string(),
833        }
834    }
835}
836
837impl Convert<RTCIceGatheringState> for GatheringState {
838    fn convert(self) -> RTCIceGatheringState {
839        match self {
840            GatheringState::New => RTCIceGatheringState::New,
841            GatheringState::Gathering => RTCIceGatheringState::Gathering,
842            GatheringState::Complete => RTCIceGatheringState::Complete,
843        }
844    }
845}
846
847impl Convert<RTCIceConnectionState> for IceConnectionState {
848    fn convert(self) -> RTCIceConnectionState {
849        match self {
850            IceConnectionState::New => RTCIceConnectionState::New,
851            IceConnectionState::Checking => RTCIceConnectionState::Checking,
852            IceConnectionState::Connected => RTCIceConnectionState::Connected,
853            IceConnectionState::Completed => RTCIceConnectionState::Completed,
854            IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
855            IceConnectionState::Failed => RTCIceConnectionState::Failed,
856            IceConnectionState::Closed => RTCIceConnectionState::Closed,
857        }
858    }
859}
860
861impl Convert<RTCSignalingState> for SignalingState {
862    fn convert(self) -> RTCSignalingState {
863        match self {
864            SignalingState::Stable => RTCSignalingState::Stable,
865            SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
866            SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
867            SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
868            SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
869            SignalingState::Closed => RTCSignalingState::Closed,
870        }
871    }
872}