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.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
35 #include "RTCPeerConnection.h"
41 #include "EventNames.h"
43 #include "JSRTCPeerConnection.h"
45 #include "MediaEndpointConfiguration.h"
46 #include "MediaStream.h"
47 #include "MediaStreamTrack.h"
49 #include "RTCConfiguration.h"
50 #include "RTCController.h"
51 #include "RTCDataChannel.h"
52 #include "RTCIceCandidate.h"
53 #include "RTCPeerConnectionIceEvent.h"
54 #include "RTCSessionDescription.h"
55 #include "RTCTrackEvent.h"
56 #include <wtf/CryptographicallyRandomNumber.h>
57 #include <wtf/MainThread.h>
59 #include <wtf/text/Base64.h>
63 using namespace PeerConnection;
65 Ref<RTCPeerConnection> RTCPeerConnection::create(ScriptExecutionContext& context)
67 Ref<RTCPeerConnection> peerConnection = adoptRef(*new RTCPeerConnection(context));
68 peerConnection->suspendIfNeeded();
69 // RTCPeerConnection may send events at about any time during its lifetime.
70 // Let's make it uncollectable until the pc is closed by JS or the page stops it.
71 if (!peerConnection->isClosed()) {
72 peerConnection->setPendingActivity(peerConnection.ptr());
73 if (auto* page = downcast<Document>(context).page())
74 peerConnection->registerToController(page->rtcController());
76 return peerConnection;
79 RTCPeerConnection::RTCPeerConnection(ScriptExecutionContext& context)
80 : ActiveDOMObject(&context)
81 #if !RELEASE_LOG_DISABLED
82 , m_logger(downcast<Document>(context).logger())
83 , m_logIdentifier(reinterpret_cast<const void*>(cryptographicallyRandomNumber()))
85 , m_backend(PeerConnectionBackend::create(*this))
87 ALWAYS_LOG(LOGIDENTIFIER);
89 m_connectionState = RTCPeerConnectionState::Closed;
92 RTCPeerConnection::~RTCPeerConnection()
94 ALWAYS_LOG(LOGIDENTIFIER);
95 unregisterFromController();
99 ExceptionOr<void> RTCPeerConnection::initializeWith(Document& document, RTCConfiguration&& configuration)
101 if (!document.frame())
102 return Exception { NotSupportedError };
105 return Exception { NotSupportedError };
107 return initializeConfiguration(WTFMove(configuration));
110 ExceptionOr<Ref<RTCRtpSender>> RTCPeerConnection::addTrack(Ref<MediaStreamTrack>&& track, const Vector<std::reference_wrapper<MediaStream>>& streams)
112 INFO_LOG(LOGIDENTIFIER);
115 return Exception { InvalidStateError };
117 for (RTCRtpSender& sender : m_transceiverSet->senders()) {
118 if (sender.trackId() == track->id())
119 return Exception { InvalidAccessError };
122 Vector<String> mediaStreamIds;
123 for (auto stream : streams)
124 mediaStreamIds.append(stream.get().id());
126 return m_backend->addTrack(track.get(), WTFMove(mediaStreamIds));
129 ExceptionOr<void> RTCPeerConnection::removeTrack(RTCRtpSender& sender)
131 INFO_LOG(LOGIDENTIFIER);
134 return Exception { InvalidStateError, "RTCPeerConnection is closed"_s };
136 if (!sender.isCreatedBy(*m_backend))
137 return Exception { InvalidAccessError, "RTCPeerConnection did not create the given sender"_s };
139 bool shouldAbort = true;
140 RTCRtpTransceiver* senderTransceiver = nullptr;
141 for (auto& transceiver : m_transceiverSet->list()) {
142 if (&sender == &transceiver->sender()) {
143 senderTransceiver = transceiver.get();
144 shouldAbort = sender.isStopped() || !sender.track();
151 sender.setTrackToNull();
152 senderTransceiver->disableSendingDirection();
153 m_backend->removeTrack(sender);
157 ExceptionOr<Ref<RTCRtpTransceiver>> RTCPeerConnection::addTransceiver(AddTransceiverTrackOrKind&& withTrack, const RTCRtpTransceiverInit& init)
159 INFO_LOG(LOGIDENTIFIER);
161 if (WTF::holds_alternative<String>(withTrack)) {
162 const String& kind = WTF::get<String>(withTrack);
163 if (kind != "audio"_s && kind != "video"_s)
164 return Exception { TypeError };
167 return Exception { InvalidStateError };
169 return m_backend->addTransceiver(kind, init);
173 return Exception { InvalidStateError };
175 auto track = WTF::get<RefPtr<MediaStreamTrack>>(withTrack).releaseNonNull();
176 return m_backend->addTransceiver(WTFMove(track), init);
179 void RTCPeerConnection::queuedCreateOffer(RTCOfferOptions&& options, SessionDescriptionPromise&& promise)
181 ALWAYS_LOG(LOGIDENTIFIER);
183 promise.reject(InvalidStateError);
187 m_backend->createOffer(WTFMove(options), WTFMove(promise));
190 void RTCPeerConnection::queuedCreateAnswer(RTCAnswerOptions&& options, SessionDescriptionPromise&& promise)
192 ALWAYS_LOG(LOGIDENTIFIER);
194 promise.reject(InvalidStateError);
198 m_backend->createAnswer(WTFMove(options), WTFMove(promise));
201 void RTCPeerConnection::queuedSetLocalDescription(RTCSessionDescription& description, DOMPromiseDeferred<void>&& promise)
203 ALWAYS_LOG(LOGIDENTIFIER, "Setting local description to:\n", description.sdp());
205 promise.reject(InvalidStateError);
209 m_backend->setLocalDescription(description, WTFMove(promise));
212 RefPtr<RTCSessionDescription> RTCPeerConnection::localDescription() const
214 return m_backend->localDescription();
217 RefPtr<RTCSessionDescription> RTCPeerConnection::currentLocalDescription() const
219 return m_backend->currentLocalDescription();
222 RefPtr<RTCSessionDescription> RTCPeerConnection::pendingLocalDescription() const
224 return m_backend->pendingLocalDescription();
227 void RTCPeerConnection::queuedSetRemoteDescription(RTCSessionDescription& description, DOMPromiseDeferred<void>&& promise)
229 ALWAYS_LOG(LOGIDENTIFIER, "Setting remote description to:\n", description.sdp());
232 promise.reject(InvalidStateError);
235 m_backend->setRemoteDescription(description, WTFMove(promise));
238 RefPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription() const
240 return m_backend->remoteDescription();
243 RefPtr<RTCSessionDescription> RTCPeerConnection::currentRemoteDescription() const
245 return m_backend->currentRemoteDescription();
248 RefPtr<RTCSessionDescription> RTCPeerConnection::pendingRemoteDescription() const
250 return m_backend->pendingRemoteDescription();
253 void RTCPeerConnection::queuedAddIceCandidate(RTCIceCandidate* rtcCandidate, DOMPromiseDeferred<void>&& promise)
255 ALWAYS_LOG(LOGIDENTIFIER, "Received ice candidate:\n", rtcCandidate ? rtcCandidate->candidate() : "null");
258 promise.reject(InvalidStateError);
262 m_backend->addIceCandidate(rtcCandidate, WTFMove(promise));
265 // Implementation of https://w3c.github.io/webrtc-pc/#set-pc-configuration
266 static inline ExceptionOr<Vector<MediaEndpointConfiguration::IceServerInfo>> iceServersFromConfiguration(RTCConfiguration& newConfiguration, const RTCConfiguration* existingConfiguration, bool isLocalDescriptionSet)
268 if (existingConfiguration && newConfiguration.bundlePolicy != existingConfiguration->bundlePolicy)
269 return Exception { InvalidModificationError, "BundlePolicy does not match existing policy" };
271 if (existingConfiguration && newConfiguration.rtcpMuxPolicy != existingConfiguration->rtcpMuxPolicy)
272 return Exception { InvalidModificationError, "RTCPMuxPolicy does not match existing policy" };
274 if (existingConfiguration && newConfiguration.iceCandidatePoolSize != existingConfiguration->iceCandidatePoolSize && isLocalDescriptionSet)
275 return Exception { InvalidModificationError, "IceTransportPolicy pool size does not match existing pool size" };
277 Vector<MediaEndpointConfiguration::IceServerInfo> servers;
278 if (newConfiguration.iceServers) {
279 servers.reserveInitialCapacity(newConfiguration.iceServers->size());
280 for (auto& server : newConfiguration.iceServers.value()) {
281 Vector<URL> serverURLs;
282 WTF::switchOn(server.urls, [&serverURLs] (const String& string) {
283 serverURLs.reserveInitialCapacity(1);
284 serverURLs.uncheckedAppend(URL { URL { }, string });
285 }, [&serverURLs] (const Vector<String>& vector) {
286 serverURLs.reserveInitialCapacity(vector.size());
287 for (auto& string : vector)
288 serverURLs.uncheckedAppend(URL { URL { }, string });
290 for (auto& serverURL : serverURLs) {
291 if (serverURL.isNull())
292 return Exception { TypeError, "Bad ICE server URL" };
293 if (serverURL.protocolIs("turn") || serverURL.protocolIs("turns")) {
294 if (server.credential.isNull() || server.username.isNull())
295 return Exception { InvalidAccessError, "TURN/TURNS server requires both username and credential" };
296 } else if (!serverURL.protocolIs("stun"))
297 return Exception { NotSupportedError, "ICE server protocol not supported" };
299 if (serverURLs.size())
300 servers.uncheckedAppend({ WTFMove(serverURLs), server.credential, server.username });
303 return WTFMove(servers);
306 ExceptionOr<Vector<MediaEndpointConfiguration::CertificatePEM>> RTCPeerConnection::certificatesFromConfiguration(const RTCConfiguration& configuration)
308 auto currentMilliSeconds = WallTime::now().secondsSinceEpoch().milliseconds();
309 auto& origin = downcast<Document>(*scriptExecutionContext()).securityOrigin();
311 Vector<MediaEndpointConfiguration::CertificatePEM> certificates;
312 certificates.reserveInitialCapacity(configuration.certificates.size());
313 for (auto& certificate : configuration.certificates) {
314 if (!originsMatch(origin, certificate->origin()))
315 return Exception { InvalidAccessError, "Certificate does not have a valid origin" };
317 if (currentMilliSeconds > certificate->expires())
318 return Exception { InvalidAccessError, "Certificate has expired"_s };
320 certificates.uncheckedAppend(MediaEndpointConfiguration::CertificatePEM { certificate->pemCertificate(), certificate->pemPrivateKey(), });
322 return WTFMove(certificates);
325 ExceptionOr<void> RTCPeerConnection::initializeConfiguration(RTCConfiguration&& configuration)
327 INFO_LOG(LOGIDENTIFIER);
329 auto servers = iceServersFromConfiguration(configuration, nullptr, false);
330 if (servers.hasException())
331 return servers.releaseException();
333 auto certificates = certificatesFromConfiguration(configuration);
334 if (certificates.hasException())
335 return certificates.releaseException();
337 if (!m_backend->setConfiguration({ servers.releaseReturnValue(), configuration.iceTransportPolicy, configuration.bundlePolicy, configuration.rtcpMuxPolicy, configuration.iceCandidatePoolSize, certificates.releaseReturnValue() }))
338 return Exception { InvalidAccessError, "Bad Configuration Parameters" };
340 m_configuration = WTFMove(configuration);
344 ExceptionOr<void> RTCPeerConnection::setConfiguration(RTCConfiguration&& configuration)
347 return Exception { InvalidStateError };
349 INFO_LOG(LOGIDENTIFIER);
351 auto servers = iceServersFromConfiguration(configuration, &m_configuration, m_backend->isLocalDescriptionSet());
352 if (servers.hasException())
353 return servers.releaseException();
355 if (configuration.certificates.size()) {
356 if (configuration.certificates.size() != m_configuration.certificates.size())
357 return Exception { InvalidModificationError, "Certificates parameters are different" };
359 for (auto& certificate : configuration.certificates) {
360 bool isThere = m_configuration.certificates.findMatching([&certificate](const auto& item) {
361 return item.get() == certificate.get();
364 return Exception { InvalidModificationError, "A certificate given in constructor is not present" };
368 if (!m_backend->setConfiguration({ servers.releaseReturnValue(), configuration.iceTransportPolicy, configuration.bundlePolicy, configuration.rtcpMuxPolicy, configuration.iceCandidatePoolSize, { } }))
369 return Exception { InvalidAccessError, "Bad Configuration Parameters" };
371 m_configuration = WTFMove(configuration);
375 void RTCPeerConnection::getStats(MediaStreamTrack* selector, Ref<DeferredPromise>&& promise)
378 for (auto& transceiver : m_transceiverSet->list()) {
379 if (transceiver->sender().track() == selector) {
380 m_backend->getStats(transceiver->sender(), WTFMove(promise));
383 if (&transceiver->receiver().track() == selector) {
384 m_backend->getStats(transceiver->receiver(), WTFMove(promise));
389 m_backend->getStats(WTFMove(promise));
392 ExceptionOr<Ref<RTCDataChannel>> RTCPeerConnection::createDataChannel(ScriptExecutionContext& context, String&& label, RTCDataChannelInit&& options)
394 ALWAYS_LOG(LOGIDENTIFIER);
397 return Exception { InvalidStateError };
399 if (options.negotiated && !options.negotiated.value() && (label.length() > 65535 || options.protocol.length() > 65535))
400 return Exception { TypeError };
402 if (options.maxPacketLifeTime && options.maxRetransmits)
403 return Exception { TypeError };
405 if (options.id && options.id.value() > 65534)
406 return Exception { TypeError };
408 auto channelHandler = m_backend->createDataChannelHandler(label, options);
410 return Exception { NotSupportedError };
412 return RTCDataChannel::create(context, WTFMove(channelHandler), WTFMove(label), WTFMove(options));
415 bool RTCPeerConnection::doClose()
420 m_connectionState = RTCPeerConnectionState::Closed;
421 m_iceConnectionState = RTCIceConnectionState::Closed;
422 m_signalingState = RTCSignalingState::Closed;
424 for (auto& transceiver : m_transceiverSet->list()) {
426 transceiver->sender().stop();
427 transceiver->receiver().stop();
433 void RTCPeerConnection::close()
438 updateConnectionState();
440 scriptExecutionContext()->postTask([protectedThis = makeRef(*this)](ScriptExecutionContext&) {
441 protectedThis->doStop();
445 void RTCPeerConnection::emulatePlatformEvent(const String& action)
447 m_backend->emulatePlatformEvent(action);
450 void RTCPeerConnection::stop()
458 void RTCPeerConnection::doStop()
467 unsetPendingActivity(this);
470 void RTCPeerConnection::registerToController(RTCController& controller)
472 m_controller = &controller;
473 m_controller->add(*this);
476 void RTCPeerConnection::unregisterFromController()
479 m_controller->remove(*this);
482 const char* RTCPeerConnection::activeDOMObjectName() const
484 return "RTCPeerConnection";
487 bool RTCPeerConnection::canSuspendForDocumentSuspension() const
489 return !hasPendingActivity();
492 bool RTCPeerConnection::hasPendingActivity() const
497 void RTCPeerConnection::addTransceiver(Ref<RTCRtpTransceiver>&& transceiver)
499 INFO_LOG(LOGIDENTIFIER);
500 m_transceiverSet->append(WTFMove(transceiver));
503 void RTCPeerConnection::setSignalingState(RTCSignalingState newState)
505 ALWAYS_LOG(LOGIDENTIFIER, newState);
506 m_signalingState = newState;
509 void RTCPeerConnection::updateIceGatheringState(RTCIceGatheringState newState)
511 ALWAYS_LOG(LOGIDENTIFIER, newState);
513 scriptExecutionContext()->postTask([protectedThis = makeRef(*this), newState](ScriptExecutionContext&) {
514 if (protectedThis->isClosed() || protectedThis->m_iceGatheringState == newState)
517 protectedThis->m_iceGatheringState = newState;
518 protectedThis->dispatchEvent(Event::create(eventNames().icegatheringstatechangeEvent, Event::CanBubble::No, Event::IsCancelable::No));
519 protectedThis->updateConnectionState();
523 void RTCPeerConnection::updateIceConnectionState(RTCIceConnectionState newState)
525 ALWAYS_LOG(LOGIDENTIFIER, newState);
527 scriptExecutionContext()->postTask([protectedThis = makeRef(*this), newState](ScriptExecutionContext&) {
528 if (protectedThis->isClosed() || protectedThis->m_iceConnectionState == newState)
531 protectedThis->m_iceConnectionState = newState;
532 protectedThis->dispatchEvent(Event::create(eventNames().iceconnectionstatechangeEvent, Event::CanBubble::No, Event::IsCancelable::No));
533 protectedThis->updateConnectionState();
537 void RTCPeerConnection::updateConnectionState()
539 RTCPeerConnectionState state;
541 if (m_iceConnectionState == RTCIceConnectionState::Closed)
542 state = RTCPeerConnectionState::Closed;
543 else if (m_iceConnectionState == RTCIceConnectionState::Disconnected)
544 state = RTCPeerConnectionState::Disconnected;
545 else if (m_iceConnectionState == RTCIceConnectionState::Failed)
546 state = RTCPeerConnectionState::Failed;
547 else if (m_iceConnectionState == RTCIceConnectionState::New && m_iceGatheringState == RTCIceGatheringState::New)
548 state = RTCPeerConnectionState::New;
549 else if (m_iceConnectionState == RTCIceConnectionState::Checking || m_iceGatheringState == RTCIceGatheringState::Gathering)
550 state = RTCPeerConnectionState::Connecting;
551 else if ((m_iceConnectionState == RTCIceConnectionState::Completed || m_iceConnectionState == RTCIceConnectionState::Connected) && m_iceGatheringState == RTCIceGatheringState::Complete)
552 state = RTCPeerConnectionState::Connected;
556 if (state == m_connectionState)
559 INFO_LOG(LOGIDENTIFIER, "state changed from: " , m_connectionState, " to ", state);
561 m_connectionState = state;
562 dispatchEvent(Event::create(eventNames().connectionstatechangeEvent, Event::CanBubble::No, Event::IsCancelable::No));
565 void RTCPeerConnection::scheduleNegotiationNeededEvent()
567 scriptExecutionContext()->postTask([protectedThis = makeRef(*this)](ScriptExecutionContext&) {
568 if (protectedThis->isClosed())
570 if (!protectedThis->m_backend->isNegotiationNeeded())
572 protectedThis->m_backend->clearNegotiationNeededState();
573 protectedThis->dispatchEvent(Event::create(eventNames().negotiationneededEvent, Event::CanBubble::No, Event::IsCancelable::No));
577 void RTCPeerConnection::fireEvent(Event& event)
579 dispatchEvent(event);
582 void RTCPeerConnection::dispatchEvent(Event& event)
584 DEBUG_LOG(LOGIDENTIFIER, "dispatching '", event.type(), "'");
585 EventTarget::dispatchEvent(event);
588 static inline ExceptionOr<PeerConnectionBackend::CertificateInformation> certificateTypeFromAlgorithmIdentifier(JSC::ExecState& state, RTCPeerConnection::AlgorithmIdentifier&& algorithmIdentifier)
590 if (WTF::holds_alternative<String>(algorithmIdentifier))
591 return Exception { NotSupportedError, "Algorithm is not supported"_s };
593 auto& value = WTF::get<JSC::Strong<JSC::JSObject>>(algorithmIdentifier);
595 JSC::VM& vm = state.vm();
596 auto scope = DECLARE_CATCH_SCOPE(vm);
598 auto parameters = convertDictionary<RTCPeerConnection::CertificateParameters>(state, value.get());
599 if (UNLIKELY(scope.exception())) {
600 scope.clearException();
601 return Exception { TypeError, "Unable to read certificate parameters"_s };
604 if (parameters.expires && *parameters.expires < 0)
605 return Exception { TypeError, "Expire value is invalid"_s };
607 if (parameters.name == "RSASSA-PKCS1-v1_5"_s) {
608 if (!parameters.hash.isNull() && parameters.hash != "SHA-256"_s)
609 return Exception { NotSupportedError, "Only SHA-256 is supported for RSASSA-PKCS1-v1_5"_s };
611 auto result = PeerConnectionBackend::CertificateInformation::RSASSA_PKCS1_v1_5();
612 if (parameters.modulusLength && parameters.publicExponent) {
613 int publicExponent = 0;
615 for (unsigned counter = 0; counter < parameters.publicExponent->byteLength(); ++counter) {
616 publicExponent += parameters.publicExponent->data()[counter] * value;
620 result.rsaParameters = PeerConnectionBackend::CertificateInformation::RSA { *parameters.modulusLength, publicExponent };
622 result.expires = parameters.expires;
623 return WTFMove(result);
625 if (parameters.name == "ECDSA"_s && parameters.namedCurve == "P-256"_s) {
626 auto result = PeerConnectionBackend::CertificateInformation::ECDSA_P256();
627 result.expires = parameters.expires;
628 return WTFMove(result);
631 return Exception { NotSupportedError, "Algorithm is not supported"_s };
634 void RTCPeerConnection::generateCertificate(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, DOMPromiseDeferred<IDLInterface<RTCCertificate>>&& promise)
636 auto parameters = certificateTypeFromAlgorithmIdentifier(state, WTFMove(algorithmIdentifier));
637 if (parameters.hasException()) {
638 promise.reject(parameters.releaseException());
641 auto& document = downcast<Document>(*JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->scriptExecutionContext());
642 PeerConnectionBackend::generateCertificate(document, parameters.returnValue(), WTFMove(promise));
645 const Vector<std::reference_wrapper<RTCRtpSender>>& RTCPeerConnection::getSenders() const
647 m_backend->collectTransceivers();
648 return m_transceiverSet->senders();
651 const Vector<std::reference_wrapper<RTCRtpReceiver>>& RTCPeerConnection::getReceivers() const
653 m_backend->collectTransceivers();
654 return m_transceiverSet->receivers();
657 const Vector<RefPtr<RTCRtpTransceiver>>& RTCPeerConnection::getTransceivers() const
659 m_backend->collectTransceivers();
660 return m_transceiverSet->list();
663 #if !RELEASE_LOG_DISABLED
664 WTFLogChannel& RTCPeerConnection::logChannel() const
670 } // namespace WebCore
672 #endif // ENABLE(WEB_RTC)