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