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