773e7e8d8effc4005c2f998c0ed723f67a56b09c
[WebKit-https.git] / Source / WebCore / Modules / mediastream / PeerConnectionBackend.cpp
1 /*
2  * Copyright (C) 2015 Ericsson AB. All rights reserved.
3  * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer
13  *    in the documentation and/or other materials provided with the
14  *    distribution.
15  * 3. Neither the name of Ericsson nor the names of its contributors
16  *    may be used to endorse or promote products derived from this
17  *    software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "PeerConnectionBackend.h"
34
35 #if ENABLE(WEB_RTC)
36
37 #include "EventNames.h"
38 #include "JSRTCSessionDescription.h"
39 #include "Logging.h"
40 #include "RTCIceCandidate.h"
41 #include "RTCPeerConnection.h"
42 #include "RTCPeerConnectionIceEvent.h"
43 #include <wtf/text/StringBuilder.h>
44
45 namespace WebCore {
46
47 void PeerConnectionBackend::createOffer(RTCOfferOptions&& options, PeerConnection::SessionDescriptionPromise&& promise)
48 {
49     ASSERT(!m_offerAnswerPromise);
50     ASSERT(!m_peerConnection.isClosed());
51
52     m_offerAnswerPromise = WTFMove(promise);
53     doCreateOffer(WTFMove(options));
54 }
55
56 void PeerConnectionBackend::createOfferSucceeded(String&& sdp)
57 {
58     ASSERT(isMainThread());
59     RELEASE_LOG(WebRTC, "Creating offer succeeded:\n%{public}s\n", sdp.utf8().data());
60
61     if (m_peerConnection.isClosed())
62         return;
63
64     ASSERT(m_offerAnswerPromise);
65     m_offerAnswerPromise->resolve(RTCSessionDescription::Init { RTCSdpType::Offer, filterSDP(WTFMove(sdp)) });
66     m_offerAnswerPromise = std::nullopt;
67 }
68
69 void PeerConnectionBackend::createOfferFailed(Exception&& exception)
70 {
71     ASSERT(isMainThread());
72     RELEASE_LOG(WebRTC, "Creating offer failed:\n%{public}s\n", exception.message().utf8().data());
73
74     if (m_peerConnection.isClosed())
75         return;
76
77     ASSERT(m_offerAnswerPromise);
78     m_offerAnswerPromise->reject(WTFMove(exception));
79     m_offerAnswerPromise = std::nullopt;
80 }
81
82 void PeerConnectionBackend::createAnswer(RTCAnswerOptions&& options, PeerConnection::SessionDescriptionPromise&& promise)
83 {
84     ASSERT(!m_offerAnswerPromise);
85     ASSERT(!m_peerConnection.isClosed());
86
87     m_offerAnswerPromise = WTFMove(promise);
88     doCreateAnswer(WTFMove(options));
89 }
90
91 void PeerConnectionBackend::createAnswerSucceeded(String&& sdp)
92 {
93     ASSERT(isMainThread());
94     RELEASE_LOG(WebRTC, "Creating answer succeeded:\n%{public}s\n", sdp.utf8().data());
95
96     if (m_peerConnection.isClosed())
97         return;
98
99     ASSERT(m_offerAnswerPromise);
100     m_offerAnswerPromise->resolve(RTCSessionDescription::Init { RTCSdpType::Answer, WTFMove(sdp) });
101     m_offerAnswerPromise = std::nullopt;
102 }
103
104 void PeerConnectionBackend::createAnswerFailed(Exception&& exception)
105 {
106     ASSERT(isMainThread());
107     RELEASE_LOG(WebRTC, "Creating answer failed:\n%{public}s\n", exception.message().utf8().data());
108
109     if (m_peerConnection.isClosed())
110         return;
111
112     ASSERT(m_offerAnswerPromise);
113     m_offerAnswerPromise->reject(WTFMove(exception));
114     m_offerAnswerPromise = std::nullopt;
115 }
116
117 static inline bool isLocalDescriptionTypeValidForState(RTCSdpType type, RTCSignalingState state)
118 {
119     switch (state) {
120     case RTCSignalingState::Stable:
121         return type == RTCSdpType::Offer;
122     case RTCSignalingState::HaveLocalOffer:
123         return type == RTCSdpType::Offer;
124     case RTCSignalingState::HaveRemoteOffer:
125         return type == RTCSdpType::Answer || type == RTCSdpType::Pranswer;
126     case RTCSignalingState::HaveLocalPranswer:
127         return type == RTCSdpType::Answer || type == RTCSdpType::Pranswer;
128     default:
129         return false;
130     };
131
132     ASSERT_NOT_REACHED();
133     return false;
134 }
135
136 void PeerConnectionBackend::setLocalDescription(RTCSessionDescription& sessionDescription, DOMPromiseDeferred<void>&& promise)
137 {
138     ASSERT(!m_peerConnection.isClosed());
139
140     if (!isLocalDescriptionTypeValidForState(sessionDescription.type(), m_peerConnection.signalingState())) {
141         promise.reject(INVALID_STATE_ERR, "Description type incompatible with current signaling state");
142         return;
143     }
144
145     m_setDescriptionPromise = WTFMove(promise);
146     doSetLocalDescription(sessionDescription);
147 }
148
149 void PeerConnectionBackend::setLocalDescriptionSucceeded()
150 {
151     ASSERT(isMainThread());
152     RELEASE_LOG(WebRTC, "Setting local description succeeded\n");
153
154     if (m_peerConnection.isClosed())
155         return;
156
157     ASSERT(m_setDescriptionPromise);
158
159     m_setDescriptionPromise->resolve();
160     m_setDescriptionPromise = std::nullopt;
161 }
162
163 void PeerConnectionBackend::setLocalDescriptionFailed(Exception&& exception)
164 {
165     ASSERT(isMainThread());
166     RELEASE_LOG(WebRTC, "Setting local description failed:\n%{public}s\n", exception.message().utf8().data());
167
168     if (m_peerConnection.isClosed())
169         return;
170
171     ASSERT(m_setDescriptionPromise);
172
173     m_setDescriptionPromise->reject(WTFMove(exception));
174     m_setDescriptionPromise = std::nullopt;
175 }
176
177 static inline bool isRemoteDescriptionTypeValidForState(RTCSdpType type, RTCSignalingState state)
178 {
179     switch (state) {
180     case RTCSignalingState::Stable:
181         return type == RTCSdpType::Offer;
182     case RTCSignalingState::HaveLocalOffer:
183         return type == RTCSdpType::Answer || type == RTCSdpType::Pranswer;
184     case RTCSignalingState::HaveRemoteOffer:
185         return type == RTCSdpType::Offer;
186     case RTCSignalingState::HaveRemotePranswer:
187         return type == RTCSdpType::Answer || type == RTCSdpType::Pranswer;
188     default:
189         return false;
190     };
191
192     ASSERT_NOT_REACHED();
193     return false;
194 }
195
196 void PeerConnectionBackend::setRemoteDescription(RTCSessionDescription& sessionDescription, DOMPromiseDeferred<void>&& promise)
197 {
198     ASSERT(!m_peerConnection.isClosed());
199
200     if (!isRemoteDescriptionTypeValidForState(sessionDescription.type(), m_peerConnection.signalingState())) {
201         promise.reject(INVALID_STATE_ERR, "Description type incompatible with current signaling state");
202         return;
203     }
204
205     m_setDescriptionPromise = WTFMove(promise);
206     doSetRemoteDescription(sessionDescription);
207 }
208
209 void PeerConnectionBackend::setRemoteDescriptionSucceeded()
210 {
211     ASSERT(isMainThread());
212     RELEASE_LOG(WebRTC, "Setting remote description succeeded\n");
213
214     if (m_peerConnection.isClosed())
215         return;
216
217     ASSERT(m_setDescriptionPromise);
218
219     m_setDescriptionPromise->resolve();
220     m_setDescriptionPromise = std::nullopt;
221 }
222
223 void PeerConnectionBackend::setRemoteDescriptionFailed(Exception&& exception)
224 {
225     ASSERT(isMainThread());
226     RELEASE_LOG(WebRTC, "Setting remote description failed:\n%{public}s\n", exception.message().utf8().data());
227
228     if (m_peerConnection.isClosed())
229         return;
230
231     ASSERT(m_setDescriptionPromise);
232
233     m_setDescriptionPromise->reject(WTFMove(exception));
234     m_setDescriptionPromise = std::nullopt;
235 }
236
237 void PeerConnectionBackend::addIceCandidate(RTCIceCandidate* iceCandidate, DOMPromiseDeferred<void>&& promise)
238 {
239     ASSERT(!m_peerConnection.isClosed());
240
241     if (!iceCandidate) {
242         endOfIceCandidates(WTFMove(promise));
243         return;
244     }
245
246     // FIXME: As per https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addicecandidate(), this check should be done before enqueuing the task.
247     if (iceCandidate->sdpMid().isNull() && !iceCandidate->sdpMLineIndex()) {
248         promise.reject(Exception { TypeError, ASCIILiteral("Trying to add a candidate that is missing both sdpMid and sdpMLineIndex") });
249         return;
250     }
251     m_addIceCandidatePromise = WTFMove(promise);
252     doAddIceCandidate(*iceCandidate);
253 }
254
255 void PeerConnectionBackend::addIceCandidateSucceeded()
256 {
257     ASSERT(isMainThread());
258     RELEASE_LOG(WebRTC, "Adding ice candidate succeeded\n");
259
260     if (m_peerConnection.isClosed())
261         return;
262
263     // FIXME: Update remote description and set ICE connection state to checking if not already done so.
264     ASSERT(m_addIceCandidatePromise);
265
266     m_addIceCandidatePromise->resolve();
267     m_addIceCandidatePromise = std::nullopt;
268 }
269
270 void PeerConnectionBackend::addIceCandidateFailed(Exception&& exception)
271 {
272     ASSERT(isMainThread());
273     RELEASE_LOG(WebRTC, "Adding ice candidate failed:\n%{public}s\n", exception.message().utf8().data());
274
275     if (m_peerConnection.isClosed())
276         return;
277
278     ASSERT(m_addIceCandidatePromise);
279
280     m_addIceCandidatePromise->reject(WTFMove(exception));
281     m_addIceCandidatePromise = std::nullopt;
282 }
283
284 void PeerConnectionBackend::fireICECandidateEvent(RefPtr<RTCIceCandidate>&& candidate)
285 {
286     ASSERT(isMainThread());
287
288     m_peerConnection.fireEvent(RTCPeerConnectionIceEvent::create(false, false, WTFMove(candidate)));
289 }
290
291 void PeerConnectionBackend::enableICECandidateFiltering()
292 {
293     m_shouldFilterICECandidates = true;
294 }
295
296 void PeerConnectionBackend::disableICECandidateFiltering()
297 {
298     m_shouldFilterICECandidates = false;
299     for (auto& pendingICECandidate : m_pendingICECandidates)
300         fireICECandidateEvent(RTCIceCandidate::create(WTFMove(pendingICECandidate.sdp), WTFMove(pendingICECandidate.mid), 0));
301     m_pendingICECandidates.clear();
302 }
303
304 static String filterICECandidate(String&& sdp)
305 {
306     ASSERT(!sdp.contains(" host "));
307
308     if (!sdp.contains(" raddr "))
309         return WTFMove(sdp);
310
311     bool skipNextItem = false;
312     bool isFirst = true;
313     StringBuilder filteredSDP;
314     sdp.split(' ', false, [&](StringView item) {
315         if (skipNextItem) {
316             skipNextItem = false;
317             return;
318         }
319         if (item == "raddr" || item == "rport") {
320             skipNextItem = true;
321             return;
322         }
323         if (isFirst)
324             isFirst = false;
325         else
326             filteredSDP.append(' ');
327         filteredSDP.append(item);
328     });
329     return filteredSDP.toString();
330 }
331
332 String PeerConnectionBackend::filterSDP(String&& sdp) const
333 {
334     if (!m_shouldFilterICECandidates)
335         return sdp;
336
337     StringBuilder filteredSDP;
338     sdp.split('\n', false, [&filteredSDP](StringView line) {
339         if (!line.startsWith("a=candidate"))
340             filteredSDP.append(line);
341         else if (line.find(" host ", 11) == notFound)
342             filteredSDP.append(filterICECandidate(line.toString()));
343         else
344             return;
345         filteredSDP.append('\n');
346     });
347     return filteredSDP.toString();
348 }
349
350 void PeerConnectionBackend::newICECandidate(String&& sdp, String&& mid)
351 {
352     RELEASE_LOG(WebRTC, "Gathered ice candidate:\n%{public}s\n", sdp.utf8().data());
353
354     if (!m_shouldFilterICECandidates) {
355         fireICECandidateEvent(RTCIceCandidate::create(WTFMove(sdp), WTFMove(mid), 0));
356         return;
357     }
358     if (sdp.find(" host ", 0) != notFound) {
359         m_pendingICECandidates.append(PendingICECandidate { WTFMove(sdp), WTFMove(mid)});
360         return;
361     }
362     fireICECandidateEvent(RTCIceCandidate::create(filterICECandidate(WTFMove(sdp)), WTFMove(mid), 0));
363 }
364
365 void PeerConnectionBackend::doneGatheringCandidates()
366 {
367     ASSERT(isMainThread());
368     RELEASE_LOG(WebRTC, "Finished ice candidate gathering\n");
369
370     m_peerConnection.fireEvent(RTCPeerConnectionIceEvent::create(false, false, nullptr));
371     m_peerConnection.updateIceGatheringState(RTCIceGatheringState::Complete);
372 }
373
374 void PeerConnectionBackend::updateSignalingState(RTCSignalingState newSignalingState)
375 {
376     ASSERT(isMainThread());
377
378     if (newSignalingState != m_peerConnection.signalingState()) {
379         m_peerConnection.setSignalingState(newSignalingState);
380         m_peerConnection.fireEvent(Event::create(eventNames().signalingstatechangeEvent, false, false));
381     }
382 }
383
384 void PeerConnectionBackend::stop()
385 {
386     m_offerAnswerPromise = std::nullopt;
387     m_setDescriptionPromise = std::nullopt;
388     m_addIceCandidatePromise = std::nullopt;
389
390     doStop();
391 }
392
393 void PeerConnectionBackend::markAsNeedingNegotiation()
394 {
395     if (m_negotiationNeeded)
396         return;
397
398     m_negotiationNeeded = true;
399
400     if (m_peerConnection.signalingState() == RTCSignalingState::Stable)
401         m_peerConnection.scheduleNegotiationNeededEvent();
402 }
403
404 } // namespace WebCore
405
406 #endif // ENABLE(WEB_RTC)