851c9c82af4097eed35f610c7acf5d25dbd44b35
[WebKit-https.git] / Source / WebCore / Modules / mediastream / RTCPeerConnection.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
4  * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
5  * Copyright (C) 2017 Apple Inc. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer
15  *    in the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of Google Inc. nor the names of its contributors
18  *    may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "config.h"
35 #include "RTCPeerConnection.h"
36
37 #if ENABLE(WEB_RTC)
38
39 #include "Document.h"
40 #include "Event.h"
41 #include "EventNames.h"
42 #include "Frame.h"
43 #include "Logging.h"
44 #include "MediaEndpointConfiguration.h"
45 #include "MediaStream.h"
46 #include "MediaStreamTrack.h"
47 #include "Page.h"
48 #include "RTCConfiguration.h"
49 #include "RTCController.h"
50 #include "RTCDataChannel.h"
51 #include "RTCIceCandidate.h"
52 #include "RTCPeerConnectionIceEvent.h"
53 #include "RTCSessionDescription.h"
54 #include "RTCTrackEvent.h"
55 #include <wtf/CryptographicallyRandomNumber.h>
56 #include <wtf/MainThread.h>
57 #include <wtf/UUID.h>
58 #include <wtf/text/Base64.h>
59
60 namespace WebCore {
61
62 using namespace PeerConnection;
63
64 Ref<RTCPeerConnection> RTCPeerConnection::create(ScriptExecutionContext& context)
65 {
66     Ref<RTCPeerConnection> peerConnection = adoptRef(*new RTCPeerConnection(context));
67     peerConnection->suspendIfNeeded();
68     // RTCPeerConnection may send events at about any time during its lifetime.
69     // Let's make it uncollectable until the pc is closed by JS or the page stops it.
70     if (!peerConnection->isClosed()) {
71         peerConnection->setPendingActivity(peerConnection.ptr());
72         if (auto* page = downcast<Document>(context).page())
73             peerConnection->registerToController(page->rtcController());
74     }
75     return peerConnection;
76 }
77
78 RTCPeerConnection::RTCPeerConnection(ScriptExecutionContext& context)
79     : ActiveDOMObject(&context)
80 #if !RELEASE_LOG_DISABLED
81     , m_logger(downcast<Document>(context).logger())
82     , m_logIdentifier(reinterpret_cast<const void*>(cryptographicallyRandomNumber()))
83 #endif
84     , m_backend(PeerConnectionBackend::create(*this))
85 {
86     ALWAYS_LOG(LOGIDENTIFIER);
87     if (!m_backend)
88         m_connectionState = RTCPeerConnectionState::Closed;
89 }
90
91 RTCPeerConnection::~RTCPeerConnection()
92 {
93     ALWAYS_LOG(LOGIDENTIFIER);
94     unregisterFromController();
95     stop();
96 }
97
98 ExceptionOr<void> RTCPeerConnection::initializeWith(Document& document, RTCConfiguration&& configuration)
99 {
100     if (!document.frame())
101         return Exception { NotSupportedError };
102
103     if (!m_backend)
104         return Exception { NotSupportedError };
105
106     return initializeConfiguration(WTFMove(configuration));
107 }
108
109 ExceptionOr<Ref<RTCRtpSender>> RTCPeerConnection::addTrack(Ref<MediaStreamTrack>&& track, const Vector<std::reference_wrapper<MediaStream>>& streams)
110 {
111     INFO_LOG(LOGIDENTIFIER);
112
113     if (isClosed())
114         return Exception { InvalidStateError };
115
116     for (RTCRtpSender& sender : m_transceiverSet->senders()) {
117         if (sender.trackId() == track->id())
118             return Exception { InvalidAccessError };
119     }
120
121     Vector<String> mediaStreamIds;
122     for (auto stream : streams)
123         mediaStreamIds.append(stream.get().id());
124
125     RTCRtpSender* sender = nullptr;
126
127     // Reuse an existing sender with the same track kind if it has never been used to send before.
128     for (auto& transceiver : m_transceiverSet->list()) {
129         auto& existingSender = transceiver->sender();
130         if (existingSender.trackKind() == track->kind() && existingSender.trackId().isNull() && !transceiver->hasSendingDirection()) {
131             existingSender.setTrack(WTFMove(track));
132             existingSender.setMediaStreamIds(WTFMove(mediaStreamIds));
133             transceiver->enableSendingDirection();
134             sender = &existingSender;
135             
136             break;
137         }
138     }
139
140     if (!sender) {
141         String transceiverMid = RTCRtpTransceiver::getNextMid();
142         const String& trackKind = track->kind();
143         String trackId = createCanonicalUUIDString();
144
145         auto newSender = RTCRtpSender::create(WTFMove(track), WTFMove(mediaStreamIds), *this);
146         auto receiver = m_backend->createReceiver(transceiverMid, trackKind, trackId);
147         auto transceiver = RTCRtpTransceiver::create(WTFMove(newSender), WTFMove(receiver));
148
149         // This transceiver is not yet associated with an m-line (null mid), but we need a
150         // provisional mid if the transceiver is used to create an offer.
151         transceiver->setProvisionalMid(transceiverMid);
152
153         sender = &transceiver->sender();
154         m_transceiverSet->append(WTFMove(transceiver));
155     }
156
157     m_backend->notifyAddedTrack(*sender);
158     return Ref<RTCRtpSender> { *sender };
159 }
160
161 ExceptionOr<void> RTCPeerConnection::removeTrack(RTCRtpSender& sender)
162 {
163     INFO_LOG(LOGIDENTIFIER);
164
165     if (isClosed())
166         return Exception { InvalidStateError };
167
168     bool shouldAbort = true;
169     for (RTCRtpSender& senderInSet : m_transceiverSet->senders()) {
170         if (&senderInSet == &sender) {
171             shouldAbort = sender.isStopped();
172             break;
173         }
174     }
175     if (shouldAbort)
176         return { };
177
178     sender.stop();
179
180     m_backend->notifyRemovedTrack(sender);
181     return { };
182 }
183
184 ExceptionOr<Ref<RTCRtpTransceiver>> RTCPeerConnection::addTransceiver(AddTransceiverTrackOrKind&& withTrack, const RTCRtpTransceiverInit& init)
185 {
186     INFO_LOG(LOGIDENTIFIER);
187
188     if (WTF::holds_alternative<String>(withTrack)) {
189         const String& kind = WTF::get<String>(withTrack);
190         if (kind != "audio" && kind != "video")
191             return Exception { TypeError };
192
193         auto sender = RTCRtpSender::create(String(kind), Vector<String>(), *this);
194         return completeAddTransceiver(WTFMove(sender), init, createCanonicalUUIDString(), kind);
195     }
196
197     Ref<MediaStreamTrack> track = WTF::get<RefPtr<MediaStreamTrack>>(withTrack).releaseNonNull();
198     const String& trackId = track->id();
199     const String& trackKind = track->kind();
200
201     auto sender = RTCRtpSender::create(WTFMove(track), Vector<String>(), *this);
202     return completeAddTransceiver(WTFMove(sender), init, trackId, trackKind);
203 }
204
205 Ref<RTCRtpTransceiver> RTCPeerConnection::completeAddTransceiver(Ref<RTCRtpSender>&& sender, const RTCRtpTransceiverInit& init, const String& trackId, const String& trackKind)
206 {
207     String transceiverMid = RTCRtpTransceiver::getNextMid();
208     auto transceiver = RTCRtpTransceiver::create(WTFMove(sender), m_backend->createReceiver(transceiverMid, trackKind, trackId));
209
210     transceiver->setProvisionalMid(transceiverMid);
211     transceiver->setDirection(init.direction);
212
213     m_transceiverSet->append(transceiver.copyRef());
214     return transceiver;
215 }
216
217 void RTCPeerConnection::queuedCreateOffer(RTCOfferOptions&& options, SessionDescriptionPromise&& promise)
218 {
219     ALWAYS_LOG(LOGIDENTIFIER);
220     if (isClosed()) {
221         promise.reject(InvalidStateError);
222         return;
223     }
224
225     m_backend->createOffer(WTFMove(options), WTFMove(promise));
226 }
227
228 void RTCPeerConnection::queuedCreateAnswer(RTCAnswerOptions&& options, SessionDescriptionPromise&& promise)
229 {
230     ALWAYS_LOG(LOGIDENTIFIER);
231     if (isClosed()) {
232         promise.reject(InvalidStateError);
233         return;
234     }
235
236     m_backend->createAnswer(WTFMove(options), WTFMove(promise));
237 }
238
239 void RTCPeerConnection::queuedSetLocalDescription(RTCSessionDescription& description, DOMPromiseDeferred<void>&& promise)
240 {
241     ALWAYS_LOG(LOGIDENTIFIER, "Setting local description to:\n", description.sdp());
242     if (isClosed()) {
243         promise.reject(InvalidStateError);
244         return;
245     }
246
247     m_backend->setLocalDescription(description, WTFMove(promise));
248 }
249
250 RefPtr<RTCSessionDescription> RTCPeerConnection::localDescription() const
251 {
252     return m_backend->localDescription();
253 }
254
255 RefPtr<RTCSessionDescription> RTCPeerConnection::currentLocalDescription() const
256 {
257     return m_backend->currentLocalDescription();
258 }
259
260 RefPtr<RTCSessionDescription> RTCPeerConnection::pendingLocalDescription() const
261 {
262     return m_backend->pendingLocalDescription();
263 }
264
265 void RTCPeerConnection::queuedSetRemoteDescription(RTCSessionDescription& description, DOMPromiseDeferred<void>&& promise)
266 {
267     ALWAYS_LOG(LOGIDENTIFIER, "Setting remote description to:\n", description.sdp());
268
269     if (isClosed()) {
270         promise.reject(InvalidStateError);
271         return;
272     }
273     m_backend->setRemoteDescription(description, WTFMove(promise));
274 }
275
276 RefPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription() const
277 {
278     return m_backend->remoteDescription();
279 }
280
281 RefPtr<RTCSessionDescription> RTCPeerConnection::currentRemoteDescription() const
282 {
283     return m_backend->currentRemoteDescription();
284 }
285
286 RefPtr<RTCSessionDescription> RTCPeerConnection::pendingRemoteDescription() const
287 {
288     return m_backend->pendingRemoteDescription();
289 }
290
291 void RTCPeerConnection::queuedAddIceCandidate(RTCIceCandidate* rtcCandidate, DOMPromiseDeferred<void>&& promise)
292 {
293     ALWAYS_LOG(LOGIDENTIFIER, "Received ice candidate:\n", rtcCandidate ? rtcCandidate->candidate() : "null");
294
295     if (isClosed()) {
296         promise.reject(InvalidStateError);
297         return;
298     }
299
300     m_backend->addIceCandidate(rtcCandidate, WTFMove(promise));
301 }
302
303 // Implementation of https://w3c.github.io/webrtc-pc/#set-pc-configuration
304 static inline ExceptionOr<Vector<MediaEndpointConfiguration::IceServerInfo>> iceServersFromConfiguration(RTCConfiguration& newConfiguration, std::optional<const RTCConfiguration&> existingConfiguration, bool isLocalDescriptionSet)
305 {
306     if (existingConfiguration && newConfiguration.bundlePolicy != existingConfiguration->bundlePolicy)
307         return Exception { InvalidModificationError, "IceTransportPolicy does not match existing policy" };
308
309     if (existingConfiguration && newConfiguration.iceCandidatePoolSize != existingConfiguration->iceCandidatePoolSize && isLocalDescriptionSet)
310         return Exception { InvalidModificationError, "IceTransportPolicy pool size does not match existing pool size" };
311
312     Vector<MediaEndpointConfiguration::IceServerInfo> servers;
313     if (newConfiguration.iceServers) {
314         servers.reserveInitialCapacity(newConfiguration.iceServers->size());
315         for (auto& server : newConfiguration.iceServers.value()) {
316             Vector<URL> serverURLs;
317             WTF::switchOn(server.urls, [&serverURLs] (const String& string) {
318                 serverURLs.reserveInitialCapacity(1);
319                 serverURLs.uncheckedAppend(URL { URL { }, string });
320             }, [&serverURLs] (const Vector<String>& vector) {
321                 serverURLs.reserveInitialCapacity(vector.size());
322                 for (auto& string : vector)
323                     serverURLs.uncheckedAppend(URL { URL { }, string });
324             });
325             for (auto& serverURL : serverURLs) {
326                 if (serverURL.isNull())
327                     return Exception { TypeError, "Bad ICE server URL" };
328                 if (serverURL.protocolIs("turn") || serverURL.protocolIs("turns")) {
329                     if (server.credential.isNull() || server.username.isNull())
330                         return Exception { InvalidAccessError, "TURN/TURNS server requires both username and credential" };
331                 } else if (!serverURL.protocolIs("stun"))
332                     return Exception { NotSupportedError, "ICE server protocol not supported" };
333             }
334             if (serverURLs.size())
335                 servers.uncheckedAppend({ WTFMove(serverURLs), server.credential, server.username });
336         }
337     }
338     return WTFMove(servers);
339 }
340
341 ExceptionOr<void> RTCPeerConnection::initializeConfiguration(RTCConfiguration&& configuration)
342 {
343     INFO_LOG(LOGIDENTIFIER);
344
345     auto servers = iceServersFromConfiguration(configuration, std::nullopt, false);
346     if (servers.hasException())
347         return servers.releaseException();
348
349     if (!m_backend->setConfiguration({ servers.releaseReturnValue(), configuration.iceTransportPolicy, configuration.bundlePolicy, configuration.iceCandidatePoolSize }))
350         return Exception { InvalidAccessError, "Bad Configuration Parameters" };
351
352     m_configuration = WTFMove(configuration);
353     return { };
354 }
355
356 ExceptionOr<void> RTCPeerConnection::setConfiguration(RTCConfiguration&& configuration)
357 {
358     if (isClosed())
359         return Exception { InvalidStateError };
360
361     INFO_LOG(LOGIDENTIFIER);
362
363     auto servers = iceServersFromConfiguration(configuration, m_configuration, m_backend->isLocalDescriptionSet());
364     if (servers.hasException())
365         return servers.releaseException();
366
367     if (!m_backend->setConfiguration({ servers.releaseReturnValue(), configuration.iceTransportPolicy, configuration.bundlePolicy, configuration.iceCandidatePoolSize }))
368         return Exception { InvalidAccessError, "Bad Configuration Parameters" };
369
370     m_configuration = WTFMove(configuration);
371     return { };
372 }
373
374 void RTCPeerConnection::getStats(MediaStreamTrack* selector, Ref<DeferredPromise>&& promise)
375 {
376     m_backend->getStats(selector, WTFMove(promise));
377 }
378
379 ExceptionOr<Ref<RTCDataChannel>> RTCPeerConnection::createDataChannel(ScriptExecutionContext& context, String&& label, RTCDataChannelInit&& options)
380 {
381     ALWAYS_LOG(LOGIDENTIFIER);
382
383     if (isClosed())
384         return Exception { InvalidStateError };
385
386     if (options.negotiated && !options.negotiated.value() && (label.length() > 65535 || options.protocol.length() > 65535))
387         return Exception { TypeError };
388
389     if (options.maxPacketLifeTime && options.maxRetransmits)
390         return Exception { TypeError };
391
392     if (options.id && options.id.value() > 65534)
393         return Exception { TypeError };
394     
395     auto channelHandler = m_backend->createDataChannelHandler(label, options);
396     if (!channelHandler)
397         return Exception { NotSupportedError };
398
399     return RTCDataChannel::create(context, WTFMove(channelHandler), WTFMove(label), WTFMove(options));
400 }
401
402 bool RTCPeerConnection::doClose()
403 {
404     if (isClosed())
405         return false;
406
407     m_connectionState = RTCPeerConnectionState::Closed;
408     m_iceConnectionState = RTCIceConnectionState::Closed;
409     m_signalingState = RTCSignalingState::Closed;
410
411     for (auto& transceiver : m_transceiverSet->list()) {
412         transceiver->stop();
413         transceiver->sender().stop();
414         transceiver->receiver().stop();
415     }
416
417     return true;
418 }
419
420 void RTCPeerConnection::close()
421 {
422     if (!doClose())
423         return;
424
425     updateConnectionState();
426     scriptExecutionContext()->postTask([protectedThis = makeRef(*this)](ScriptExecutionContext&) {
427         protectedThis->doStop();
428     });
429 }
430
431 void RTCPeerConnection::emulatePlatformEvent(const String& action)
432 {
433     m_backend->emulatePlatformEvent(action);
434 }
435
436 void RTCPeerConnection::stop()
437 {
438     if (!doClose())
439         return;
440
441     doStop();
442 }
443
444 void RTCPeerConnection::doStop()
445 {
446     if (m_isStopped)
447         return;
448
449     m_isStopped = true;
450
451     m_backend->stop();
452
453     unsetPendingActivity(this);
454 }
455
456 void RTCPeerConnection::registerToController(RTCController& controller)
457 {
458     m_controller = &controller;
459     m_controller->add(*this);
460 }
461
462 void RTCPeerConnection::unregisterFromController()
463 {
464     if (m_controller)
465         m_controller->remove(*this);
466 }
467
468 const char* RTCPeerConnection::activeDOMObjectName() const
469 {
470     return "RTCPeerConnection";
471 }
472
473 bool RTCPeerConnection::canSuspendForDocumentSuspension() const
474 {
475     return !hasPendingActivity();
476 }
477
478 bool RTCPeerConnection::hasPendingActivity() const
479 {
480     return !m_isStopped;
481 }
482
483 void RTCPeerConnection::addTransceiver(Ref<RTCRtpTransceiver>&& transceiver)
484 {
485     INFO_LOG(LOGIDENTIFIER);
486     m_transceiverSet->append(WTFMove(transceiver));
487 }
488
489 void RTCPeerConnection::setSignalingState(RTCSignalingState newState)
490 {
491     ALWAYS_LOG(LOGIDENTIFIER, newState);
492     m_signalingState = newState;
493 }
494
495 void RTCPeerConnection::updateIceGatheringState(RTCIceGatheringState newState)
496 {
497     ALWAYS_LOG(LOGIDENTIFIER, newState);
498
499     scriptExecutionContext()->postTask([protectedThis = makeRef(*this), newState](ScriptExecutionContext&) {
500         if (protectedThis->isClosed() || protectedThis->m_iceGatheringState == newState)
501             return;
502
503         protectedThis->m_iceGatheringState = newState;
504         protectedThis->dispatchEvent(Event::create(eventNames().icegatheringstatechangeEvent, false, false));
505         protectedThis->updateConnectionState();
506     });
507 }
508
509 void RTCPeerConnection::updateIceConnectionState(RTCIceConnectionState newState)
510 {
511     ALWAYS_LOG(LOGIDENTIFIER, newState);
512
513     scriptExecutionContext()->postTask([protectedThis = makeRef(*this), newState](ScriptExecutionContext&) {
514         if (protectedThis->isClosed() || protectedThis->m_iceConnectionState == newState)
515             return;
516
517         protectedThis->m_iceConnectionState = newState;
518         protectedThis->dispatchEvent(Event::create(eventNames().iceconnectionstatechangeEvent, false, false));
519         protectedThis->updateConnectionState();
520     });
521 }
522
523 void RTCPeerConnection::updateConnectionState()
524 {
525     RTCPeerConnectionState state;
526
527     // FIXME: In case m_iceGatheringState is RTCIceGatheringState::Gathering, and m_iceConnectionState is Closed, we should have the connection state be Closed.
528     if (m_iceConnectionState == RTCIceConnectionState::New && m_iceGatheringState == RTCIceGatheringState::New)
529         state = RTCPeerConnectionState::New;
530     else if (m_iceConnectionState == RTCIceConnectionState::Checking || m_iceGatheringState == RTCIceGatheringState::Gathering)
531         state = RTCPeerConnectionState::Connecting;
532     else if ((m_iceConnectionState == RTCIceConnectionState::Completed || m_iceConnectionState == RTCIceConnectionState::Connected) && m_iceGatheringState == RTCIceGatheringState::Complete)
533         state = RTCPeerConnectionState::Connected;
534     else if (m_iceConnectionState == RTCIceConnectionState::Disconnected)
535         state = RTCPeerConnectionState::Disconnected;
536     else if (m_iceConnectionState == RTCIceConnectionState::Failed)
537         state = RTCPeerConnectionState::Failed;
538     else if (m_iceConnectionState == RTCIceConnectionState::Closed)
539         state = RTCPeerConnectionState::Closed;
540     else
541         return;
542
543     if (state == m_connectionState)
544         return;
545
546     INFO_LOG(LOGIDENTIFIER, "state changed from: " , m_connectionState, " to ", state);
547
548     m_connectionState = state;
549     dispatchEvent(Event::create(eventNames().connectionstatechangeEvent, false, false));
550 }
551
552 void RTCPeerConnection::scheduleNegotiationNeededEvent()
553 {
554     scriptExecutionContext()->postTask([protectedThis = makeRef(*this)](ScriptExecutionContext&) {
555         if (protectedThis->isClosed())
556             return;
557         if (!protectedThis->m_backend->isNegotiationNeeded())
558             return;
559         protectedThis->m_backend->clearNegotiationNeededState();
560         protectedThis->dispatchEvent(Event::create(eventNames().negotiationneededEvent, false, false));
561     });
562 }
563
564 void RTCPeerConnection::fireEvent(Event& event)
565 {
566     dispatchEvent(event);
567 }
568
569 void RTCPeerConnection::enqueueReplaceTrackTask(RTCRtpSender& sender, Ref<MediaStreamTrack>&& withTrack, DOMPromiseDeferred<void>&& promise)
570 {
571     scriptExecutionContext()->postTask([protectedThis = makeRef(*this), protectedSender = makeRef(sender), promise = WTFMove(promise), withTrack = WTFMove(withTrack)](ScriptExecutionContext&) mutable {
572         if (protectedThis->isClosed())
573             return;
574         bool hasTrack = protectedSender->track();
575         protectedSender->setTrack(WTFMove(withTrack));
576         if (!hasTrack)
577             protectedThis->m_backend->notifyAddedTrack(protectedSender.get());
578         promise.resolve();
579     });
580 }
581
582 void RTCPeerConnection::replaceTrack(RTCRtpSender& sender, RefPtr<MediaStreamTrack>&& withTrack, DOMPromiseDeferred<void>&& promise)
583 {
584     INFO_LOG(LOGIDENTIFIER);
585
586     if (!withTrack) {
587         scriptExecutionContext()->postTask([protectedSender = makeRef(sender), promise = WTFMove(promise)](ScriptExecutionContext&) mutable {
588             protectedSender->setTrackToNull();
589             promise.resolve();
590         });
591         return;
592     }
593     
594     if (!sender.track()) {
595         enqueueReplaceTrackTask(sender, withTrack.releaseNonNull(), WTFMove(promise));
596         return;
597     }
598
599     m_backend->replaceTrack(sender, withTrack.releaseNonNull(), WTFMove(promise));
600 }
601
602 RTCRtpParameters RTCPeerConnection::getParameters(RTCRtpSender& sender) const
603 {
604     return m_backend->getParameters(sender);
605 }
606
607 void RTCPeerConnection::dispatchEvent(Event& event)
608 {
609     DEBUG_LOG(LOGIDENTIFIER, "dispatching '", event.type(), "'");
610     EventTarget::dispatchEvent(event);
611 }
612
613 #if !RELEASE_LOG_DISABLED
614 WTFLogChannel& RTCPeerConnection::logChannel() const
615 {
616     return LogWebRTC;
617 }
618 #endif
619
620 } // namespace WebCore
621
622 #endif // ENABLE(WEB_RTC)