[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / Modules / mediastream / SDPProcessor.cpp
1 /*
2  * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
3  * Copyright (C) 2016 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
34 #if ENABLE(WEB_RTC)
35 #include "SDPProcessor.h"
36
37 #include "Document.h"
38 #include "Frame.h"
39 #include "SDPProcessorScriptResource.h"
40 #include "ScriptController.h"
41 #include "ScriptSourceCode.h"
42 #include "inspector/InspectorValues.h"
43 #include <bindings/ScriptObject.h>
44 #include <wtf/NeverDestroyed.h>
45
46 using namespace Inspector;
47
48 namespace WebCore {
49
50 #define STRING_FUNCTION(name) \
51     static const String& name##String() \
52     { \
53         static NeverDestroyed<const String> name { ASCIILiteral(#name) }; \
54         return name; \
55     }
56
57 STRING_FUNCTION(address)
58 STRING_FUNCTION(apt)
59 STRING_FUNCTION(candidates)
60 STRING_FUNCTION(ccmfir)
61 STRING_FUNCTION(channels)
62 STRING_FUNCTION(clockRate)
63 STRING_FUNCTION(cname)
64 STRING_FUNCTION(componentId)
65 STRING_FUNCTION(dtls)
66 STRING_FUNCTION(encodingName)
67 STRING_FUNCTION(fingerprint)
68 STRING_FUNCTION(fingerprintHashFunction)
69 STRING_FUNCTION(foundation)
70 STRING_FUNCTION(ice)
71 STRING_FUNCTION(mediaDescriptions)
72 STRING_FUNCTION(mediaStreamId)
73 STRING_FUNCTION(mediaStreamTrackId)
74 STRING_FUNCTION(mid)
75 STRING_FUNCTION(mode)
76 STRING_FUNCTION(mux)
77 STRING_FUNCTION(nack)
78 STRING_FUNCTION(nackpli)
79 STRING_FUNCTION(originator)
80 STRING_FUNCTION(packetizationMode)
81 STRING_FUNCTION(parameters)
82 STRING_FUNCTION(password)
83 STRING_FUNCTION(payloads)
84 STRING_FUNCTION(port)
85 STRING_FUNCTION(priority)
86 STRING_FUNCTION(relatedAddress)
87 STRING_FUNCTION(relatedPort)
88 STRING_FUNCTION(rtcp)
89 STRING_FUNCTION(rtcpAddress)
90 STRING_FUNCTION(rtcpPort)
91 STRING_FUNCTION(rtxTime)
92 STRING_FUNCTION(sessionId)
93 STRING_FUNCTION(sessionVersion)
94 STRING_FUNCTION(setup)
95 STRING_FUNCTION(ssrcs)
96 STRING_FUNCTION(tcpType)
97 STRING_FUNCTION(transport)
98 STRING_FUNCTION(type)
99 STRING_FUNCTION(ufrag)
100
101 SDPProcessor::SDPProcessor(ScriptExecutionContext* context)
102     : ContextDestructionObserver(context)
103 {
104 }
105
106 // Note that MediaEndpointSessionConfiguration is a "flatter" structure that the JSON representation. For
107 // example, the JSON representation has an "ice" object which collects a set of properties under a
108 // namespace. MediaEndpointSessionConfiguration has "ice"-prefixes on the corresponding properties.
109
110 static RefPtr<InspectorObject> createCandidateObject(const IceCandidate& candidate)
111 {
112     RefPtr<InspectorObject> candidateObject = InspectorObject::create();
113
114     candidateObject->setString(typeString(), candidate.type);
115     candidateObject->setString(foundationString(), candidate.foundation);
116     candidateObject->setInteger(componentIdString(), candidate.componentId);
117     candidateObject->setString(transportString(), candidate.transport);
118     candidateObject->setInteger(priorityString(), candidate.priority);
119     candidateObject->setString(addressString(), candidate.address);
120     candidateObject->setInteger(portString(), candidate.port);
121     if (!candidate.tcpType.isEmpty())
122         candidateObject->setString(tcpTypeString(), candidate.tcpType);
123     if (candidate.type.convertToASCIIUppercase() != "HOST") {
124         candidateObject->setString(relatedAddressString(), candidate.relatedAddress);
125         candidateObject->setInteger(relatedPortString(), candidate.relatedPort);
126     }
127
128     return candidateObject;
129 }
130
131 static IceCandidate createCandidate(const InspectorObject& candidateObject)
132 {
133     IceCandidate candidate;
134     String stringValue;
135     unsigned intValue;
136
137     if (candidateObject.getString(typeString(), stringValue))
138         candidate.type = stringValue;
139
140     if (candidateObject.getString(foundationString(), stringValue))
141         candidate.foundation = stringValue;
142
143     if (candidateObject.getInteger(componentIdString(), intValue))
144         candidate.componentId = intValue;
145
146     if (candidateObject.getString(transportString(), stringValue))
147         candidate.transport = stringValue;
148
149     if (candidateObject.getInteger(priorityString(), intValue))
150         candidate.priority = intValue;
151
152     if (candidateObject.getString(addressString(), stringValue))
153         candidate.address = stringValue;
154
155     if (candidateObject.getInteger(portString(), intValue))
156         candidate.port = intValue;
157
158     if (candidateObject.getString(tcpTypeString(), stringValue))
159         candidate.tcpType = stringValue;
160
161     if (candidateObject.getString(relatedAddressString(), stringValue))
162         candidate.relatedAddress = stringValue;
163
164     if (candidateObject.getInteger(relatedPortString(), intValue))
165         candidate.relatedPort = intValue;
166
167     return candidate;
168 }
169
170 static RefPtr<MediaEndpointSessionConfiguration> configurationFromJSON(const String& json)
171 {
172     RefPtr<InspectorValue> value;
173     if (!InspectorValue::parseJSON(json, value))
174         return nullptr;
175
176     RefPtr<InspectorObject> object;
177     if (!value->asObject(object))
178         return nullptr;
179
180     RefPtr<MediaEndpointSessionConfiguration> configuration = MediaEndpointSessionConfiguration::create();
181
182     String stringValue;
183     unsigned intValue;
184     unsigned long longValue;
185     bool boolValue;
186
187     RefPtr<InspectorObject> originatorObject = InspectorObject::create();
188     if (object->getObject(originatorString(), originatorObject)) {
189         if (originatorObject->getInteger(sessionIdString(), longValue))
190             configuration->setSessionId(longValue);
191         if (originatorObject->getInteger(sessionVersionString(), intValue))
192             configuration->setSessionVersion(intValue);
193     }
194
195     RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
196     object->getArray(mediaDescriptionsString(), mediaDescriptionsArray);
197
198     for (unsigned i = 0; i < mediaDescriptionsArray->length(); ++i) {
199         RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
200         mediaDescriptionsArray->get(i)->asObject(mediaDescriptionObject);
201
202         PeerMediaDescription mediaDescription;
203
204         if (mediaDescriptionObject->getString(typeString(), stringValue))
205             mediaDescription.type = stringValue;
206
207         if (mediaDescriptionObject->getInteger(portString(), intValue))
208             mediaDescription.port = intValue;
209
210         if (mediaDescriptionObject->getString(addressString(), stringValue))
211             mediaDescription.address = stringValue;
212
213         if (mediaDescriptionObject->getString(modeString(), stringValue))
214             mediaDescription.mode = stringValue;
215
216         if (mediaDescriptionObject->getString(midString(), stringValue))
217             mediaDescription.mid = stringValue;
218
219         RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
220         mediaDescriptionObject->getArray(payloadsString(), payloadsArray);
221
222         for (unsigned j = 0; j < payloadsArray->length(); ++j) {
223             RefPtr<InspectorObject> payloadsObject = InspectorObject::create();
224             payloadsArray->get(j)->asObject(payloadsObject);
225
226             MediaPayload payload;
227
228             if (payloadsObject->getInteger(typeString(), intValue))
229                 payload.type = intValue;
230
231             if (payloadsObject->getString(encodingNameString(), stringValue))
232                 payload.encodingName = stringValue;
233
234             if (payloadsObject->getInteger(clockRateString(), intValue))
235                 payload.clockRate = intValue;
236
237             if (payloadsObject->getInteger(channelsString(), intValue))
238                 payload.channels = intValue;
239
240             if (payloadsObject->getBoolean(ccmfirString(), boolValue))
241                 payload.ccmfir = boolValue;
242
243             if (payloadsObject->getBoolean(nackpliString(), boolValue))
244                 payload.nackpli = boolValue;
245
246             if (payloadsObject->getBoolean(nackString(), boolValue))
247                 payload.nack = boolValue;
248
249             RefPtr<InspectorObject> parametersObject = InspectorObject::create();
250             if (payloadsObject->getObject(parametersString(), parametersObject)) {
251                 if (parametersObject->getInteger(packetizationModeString(), intValue))
252                     payload.addParameter("packetizationMode", intValue);
253
254                 if (parametersObject->getInteger(aptString(), intValue))
255                     payload.addParameter("apt", intValue);
256
257                 if (parametersObject->getInteger(rtxTimeString(), intValue))
258                     payload.addParameter("rtxTime", intValue);
259             }
260
261             mediaDescription.addPayload(WTFMove(payload));
262         }
263
264         RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
265         if (mediaDescriptionObject->getObject(rtcpString(), rtcpObject)) {
266             if (rtcpObject->getBoolean(muxString(), boolValue))
267                 mediaDescription.rtcpMux = boolValue;
268
269             if (rtcpObject->getString(rtcpAddressString(), stringValue))
270                 mediaDescription.rtcpAddress = stringValue;
271
272             if (rtcpObject->getInteger(rtcpPortString(), intValue))
273                 mediaDescription.rtcpPort = intValue;
274         }
275
276         if (mediaDescriptionObject->getString(mediaStreamIdString(), stringValue))
277             mediaDescription.mediaStreamId = stringValue;
278
279         if (mediaDescriptionObject->getString(mediaStreamTrackIdString(), stringValue))
280             mediaDescription.mediaStreamTrackId = stringValue;
281
282         RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
283         if (mediaDescriptionObject->getObject(dtlsString(), dtlsObject)) {
284             if (dtlsObject->getString(setupString(), stringValue))
285                 mediaDescription.dtlsSetup = stringValue;
286
287             if (dtlsObject->getString(fingerprintHashFunctionString(), stringValue))
288                 mediaDescription.dtlsFingerprintHashFunction = stringValue;
289
290             if (dtlsObject->getString(fingerprintString(), stringValue))
291                 mediaDescription.dtlsFingerprint = stringValue;
292         }
293
294         RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
295         mediaDescriptionObject->getArray(ssrcsString(), ssrcsArray);
296
297         for (unsigned j = 0; j < ssrcsArray->length(); ++j) {
298             ssrcsArray->get(j)->asInteger(intValue);
299             mediaDescription.addSsrc(intValue);
300         }
301
302         if (mediaDescriptionObject->getString(cnameString(), stringValue))
303             mediaDescription.cname = stringValue;
304
305         RefPtr<InspectorObject> iceObject = InspectorObject::create();
306         if (mediaDescriptionObject->getObject(iceString(), iceObject)) {
307             if (iceObject->getString(ufragString(), stringValue))
308                 mediaDescription.iceUfrag = stringValue;
309
310             if (iceObject->getString(passwordString(), stringValue))
311                 mediaDescription.icePassword = stringValue;
312
313             RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
314             iceObject->getArray(candidatesString(), candidatesArray);
315
316             for (unsigned j = 0; j < candidatesArray->length(); ++j) {
317                 RefPtr<InspectorObject> candidateObject = InspectorObject::create();
318                 candidatesArray->get(j)->asObject(candidateObject);
319
320                 mediaDescription.addIceCandidate(createCandidate(*candidateObject));
321             }
322         }
323
324         configuration->addMediaDescription(WTFMove(mediaDescription));
325     }
326
327     return configuration;
328 }
329
330 static std::optional<IceCandidate> iceCandidateFromJSON(const String& json)
331 {
332     RefPtr<InspectorValue> value;
333     if (!InspectorValue::parseJSON(json, value))
334         return std::nullopt;
335
336     RefPtr<InspectorObject> candidateObject;
337     if (!value->asObject(candidateObject))
338         return std::nullopt;
339
340     return createCandidate(*candidateObject);
341 }
342
343 static String configurationToJSON(const MediaEndpointSessionConfiguration& configuration)
344 {
345     RefPtr<InspectorObject> object = InspectorObject::create();
346
347     RefPtr<InspectorObject> originatorObject = InspectorObject::create();
348     originatorObject->setDouble(sessionIdString(), configuration.sessionId());
349     originatorObject->setInteger(sessionVersionString(), configuration.sessionVersion());
350     object->setObject(originatorString(), originatorObject);
351
352     RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
353
354     for (const auto& mediaDescription : configuration.mediaDescriptions()) {
355         RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
356
357         mediaDescriptionObject->setString(typeString(), mediaDescription.type);
358         mediaDescriptionObject->setInteger(portString(), mediaDescription.port);
359         mediaDescriptionObject->setString(addressString(), mediaDescription.address);
360         mediaDescriptionObject->setString(modeString(), mediaDescription.mode);
361         mediaDescriptionObject->setString(midString(), mediaDescription.mid);
362
363         RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
364
365         for (auto& payload : mediaDescription.payloads) {
366             RefPtr<InspectorObject> payloadObject = InspectorObject::create();
367
368             payloadObject->setInteger(typeString(), payload.type);
369             payloadObject->setString(encodingNameString(), payload.encodingName);
370             payloadObject->setInteger(clockRateString(), payload.clockRate);
371             payloadObject->setInteger(channelsString(), payload.channels);
372             payloadObject->setBoolean(ccmfirString(), payload.ccmfir);
373             payloadObject->setBoolean(nackpliString(), payload.nackpli);
374             payloadObject->setBoolean(nackString(), payload.nack);
375
376             if (!payload.parameters.isEmpty()) {
377                 RefPtr<InspectorObject> parametersObject = InspectorObject::create();
378
379                 for (auto& name : payload.parameters.keys())
380                     parametersObject->setInteger(name, payload.parameters.get(name));
381
382                 payloadObject->setObject(parametersString(), parametersObject);
383             }
384
385             payloadsArray->pushObject(payloadObject);
386         }
387         mediaDescriptionObject->setArray(payloadsString(), payloadsArray);
388
389         RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
390         rtcpObject->setBoolean(muxString(), mediaDescription.rtcpMux);
391         rtcpObject->setString(addressString(), mediaDescription.rtcpAddress);
392         rtcpObject->setInteger(portString(), mediaDescription.rtcpPort);
393         mediaDescriptionObject->setObject(rtcpString(), rtcpObject);
394
395         mediaDescriptionObject->setString(mediaStreamIdString(), mediaDescription.mediaStreamId);
396         mediaDescriptionObject->setString(mediaStreamTrackIdString(), mediaDescription.mediaStreamTrackId);
397
398         RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
399         dtlsObject->setString(setupString(), mediaDescription.dtlsSetup);
400         dtlsObject->setString(fingerprintHashFunctionString(), mediaDescription.dtlsFingerprintHashFunction);
401         dtlsObject->setString(fingerprintString(), mediaDescription.dtlsFingerprint);
402         mediaDescriptionObject->setObject(dtlsString(), dtlsObject);
403
404         RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
405
406         for (auto ssrc : mediaDescription.ssrcs)
407             ssrcsArray->pushDouble(ssrc);
408         mediaDescriptionObject->setArray(ssrcsString(), ssrcsArray);
409
410         mediaDescriptionObject->setString(cnameString(), mediaDescription.cname);
411
412         RefPtr<InspectorObject> iceObject = InspectorObject::create();
413         iceObject->setString(ufragString(), mediaDescription.iceUfrag);
414         iceObject->setString(passwordString(), mediaDescription.icePassword);
415
416         RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
417
418         for (auto& candidate : mediaDescription.iceCandidates)
419             candidatesArray->pushObject(createCandidateObject(candidate));
420
421         iceObject->setArray(candidatesString(), candidatesArray);
422         mediaDescriptionObject->setObject(iceString(), iceObject);
423
424         mediaDescriptionsArray->pushObject(mediaDescriptionObject);
425     }
426     object->setArray(mediaDescriptionsString(), mediaDescriptionsArray);
427
428     return object->toJSONString();
429 }
430
431 static String iceCandidateToJSON(const IceCandidate& candidate)
432 {
433     return createCandidateObject(candidate)->toJSONString();
434 }
435
436 SDPProcessor::Result SDPProcessor::generate(const MediaEndpointSessionConfiguration& configuration, String& outSdpString) const
437 {
438     String sdpString;
439     if (!callScript("generate", configurationToJSON(configuration), sdpString))
440         return Result::InternalError;
441
442     outSdpString = sdpString;
443     return Result::Success;
444 }
445
446 SDPProcessor::Result SDPProcessor::parse(const String& sdp, RefPtr<MediaEndpointSessionConfiguration>& outConfiguration) const
447 {
448     String scriptOutput;
449     if (!callScript("parse", sdp, scriptOutput))
450         return Result::InternalError;
451
452     if (scriptOutput == "ParseError")
453         return Result::ParseError;
454
455     RefPtr<MediaEndpointSessionConfiguration> configuration = configurationFromJSON(scriptOutput);
456     if (!configuration)
457         return Result::InternalError;
458
459     outConfiguration = configuration;
460     return Result::Success;
461 }
462
463 SDPProcessor::Result SDPProcessor::generateCandidateLine(const IceCandidate& candidate, String& outCandidateLine) const
464 {
465     String candidateLine;
466     if (!callScript("generateCandidateLine", iceCandidateToJSON(candidate), candidateLine))
467         return Result::InternalError;
468
469     outCandidateLine = candidateLine;
470     return Result::Success;
471 }
472
473 SDPProcessor::ParsingResult SDPProcessor::parseCandidateLine(const String& candidateLine) const
474 {
475     String scriptOutput;
476     if (!callScript("parseCandidateLine", candidateLine, scriptOutput))
477         return { Result::InternalError };
478
479     if (scriptOutput == "ParseError")
480         return { Result::ParseError };
481
482     auto candidate = iceCandidateFromJSON(scriptOutput);
483     if (!candidate)
484         return { Result::InternalError };
485     return { WTFMove(candidate.value()) };
486 }
487
488 bool SDPProcessor::callScript(const String& functionName, const String& argument, String& outResult) const
489 {
490     if (!scriptExecutionContext())
491         return false;
492
493     Document* document = downcast<Document>(scriptExecutionContext());
494     if (!document->frame())
495         return false;
496
497     if (!m_isolatedWorld)
498         m_isolatedWorld = DOMWrapperWorld::create(JSDOMWindow::commonVM());
499
500     ScriptController& scriptController = document->frame()->script();
501     JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(*m_isolatedWorld));
502     JSC::VM& vm = globalObject->vm();
503     JSC::JSLockHolder lock(vm);
504     auto scope = DECLARE_CATCH_SCOPE(vm);
505     JSC::ExecState* exec = globalObject->globalExec();
506
507     JSC::JSValue probeFunctionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, "generate"));
508     if (!probeFunctionValue.isFunction()) {
509         URL scriptURL;
510         scriptController.evaluateInWorld(ScriptSourceCode(SDPProcessorScriptResource::scriptString(), scriptURL), *m_isolatedWorld);
511         if (UNLIKELY(scope.exception())) {
512             scope.clearException();
513             return false;
514         }
515     }
516
517     JSC::JSValue functionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, functionName));
518     if (!functionValue.isFunction())
519         return false;
520
521     JSC::JSObject* function = functionValue.toObject(exec);
522     JSC::CallData callData;
523     JSC::CallType callType = function->methodTable()->getCallData(function, callData);
524     if (callType == JSC::CallType::None)
525         return false;
526
527     JSC::MarkedArgumentBuffer argList;
528     argList.append(JSC::jsString(exec, argument));
529
530     JSC::JSValue result = JSC::call(exec, function, callType, callData, globalObject, argList);
531     if (UNLIKELY(scope.exception())) {
532         LOG_ERROR("SDPProcessor script threw in function %s", functionName.ascii().data());
533         scope.clearException();
534         return false;
535     }
536
537     if (!result.isString())
538         return false;
539
540     outResult = result.getString(exec);
541     return true;
542 }
543
544 } // namespace WebCore
545
546 #endif // ENABLE(WEB_RTC)